1: <?php
2:
3: /**
4: * This file is part of the Nette Framework (http://nette.org)
5: *
6: * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
7: *
8: * For the full copyright and license information, please view
9: * the file license.txt that was distributed with this source code.
10: * @package Nette\Application\UI
11: */
12:
13:
14:
15: /**
16: * Control is renderable Presenter component.
17: *
18: * @author David Grudl
19: *
20: * @property-read ITemplate $template
21: * @property-read string $snippetId
22: * @package Nette\Application\UI
23: */
24: abstract class NControl extends NPresenterComponent implements IRenderable
25: {
26: /** @var ITemplate */
27: private $template;
28:
29: /** @var array */
30: private $invalidSnippets = array();
31:
32: /** @var bool */
33: public $snippetMode;
34:
35:
36:
37: /********************* template factory ****************d*g**/
38:
39:
40:
41: /**
42: * @return ITemplate
43: */
44: final public function getTemplate()
45: {
46: if ($this->template === NULL) {
47: $value = $this->createTemplate();
48: if (!$value instanceof ITemplate && $value !== NULL) {
49: $class2 = get_class($value); $class = get_class($this);
50: throw new UnexpectedValueException("Object returned by $class::createTemplate() must be instance of ITemplate, '$class2' given.");
51: }
52: $this->template = $value;
53: }
54: return $this->template;
55: }
56:
57:
58:
59: /**
60: * @param string|NULL
61: * @return ITemplate
62: */
63: protected function createTemplate($class = NULL)
64: {
65: $template = $class ? new $class : new NFileTemplate;
66: $presenter = $this->getPresenter(FALSE);
67: $template->onPrepareFilters[] = $this->templatePrepareFilters;
68: $template->registerHelperLoader('NTemplateHelpers::loader');
69:
70: // default parameters
71: $template->control = $template->_control = $this;
72: $template->presenter = $template->_presenter = $presenter;
73: if ($presenter instanceof NPresenter) {
74: $template->setCacheStorage($presenter->getContext()->nette->templateCacheStorage);
75: $template->user = $presenter->getUser();
76: $template->netteHttpResponse = $presenter->getHttpResponse();
77: $template->netteCacheStorage = $presenter->getContext()->getByType('ICacheStorage');
78: $template->baseUri = $template->baseUrl = rtrim($presenter->getHttpRequest()->getUrl()->getBaseUrl(), '/');
79: $template->basePath = preg_replace('#https?://[^/]+#A', '', $template->baseUrl);
80:
81: // flash message
82: if ($presenter->hasFlashSession()) {
83: $id = $this->getParameterId('flash');
84: $template->flashes = $presenter->getFlashSession()->$id;
85: }
86: }
87: if (!isset($template->flashes) || !is_array($template->flashes)) {
88: $template->flashes = array();
89: }
90:
91: return $template;
92: }
93:
94:
95:
96: /**
97: * Descendant can override this method to customize template compile-time filters.
98: * @param NTemplate
99: * @return void
100: */
101: public function templatePrepareFilters($template)
102: {
103: $template->registerFilter($this->getPresenter()->getContext()->nette->createLatte());
104: }
105:
106:
107:
108: /**
109: * Returns widget component specified by name.
110: * @param string
111: * @return IComponent
112: */
113: public function getWidget($name)
114: {
115: trigger_error(__METHOD__ . '() is deprecated, use getComponent() instead.', E_USER_WARNING);
116: return $this->getComponent($name);
117: }
118:
119:
120:
121: /**
122: * Saves the message to template, that can be displayed after redirect.
123: * @param string
124: * @param string
125: * @return \stdClass
126: */
127: public function flashMessage($message, $type = 'info')
128: {
129: $id = $this->getParameterId('flash');
130: $messages = $this->getPresenter()->getFlashSession()->$id;
131: $messages[] = $flash = (object) array(
132: 'message' => $message,
133: 'type' => $type,
134: );
135: $this->getTemplate()->flashes = $messages;
136: $this->getPresenter()->getFlashSession()->$id = $messages;
137: return $flash;
138: }
139:
140:
141:
142: /********************* rendering ****************d*g**/
143:
144:
145:
146: /**
147: * Forces control or its snippet to repaint.
148: * @param string
149: * @return void
150: */
151: public function invalidateControl($snippet = NULL)
152: {
153: $this->invalidSnippets[$snippet] = TRUE;
154: }
155:
156:
157:
158: /**
159: * Allows control or its snippet to not repaint.
160: * @param string
161: * @return void
162: */
163: public function validateControl($snippet = NULL)
164: {
165: if ($snippet === NULL) {
166: $this->invalidSnippets = array();
167:
168: } else {
169: unset($this->invalidSnippets[$snippet]);
170: }
171: }
172:
173:
174:
175: /**
176: * Is required to repaint the control or its snippet?
177: * @param string snippet name
178: * @return bool
179: */
180: public function isControlInvalid($snippet = NULL)
181: {
182: if ($snippet === NULL) {
183: if (count($this->invalidSnippets) > 0) {
184: return TRUE;
185:
186: } else {
187: $queue = array($this);
188: do {
189: foreach (array_shift($queue)->getComponents() as $component) {
190: if ($component instanceof IRenderable) {
191: if ($component->isControlInvalid()) {
192: // $this->invalidSnippets['__child'] = TRUE; // as cache
193: return TRUE;
194: }
195:
196: } elseif ($component instanceof IComponentContainer) {
197: $queue[] = $component;
198: }
199: }
200: } while ($queue);
201:
202: return FALSE;
203: }
204:
205: } else {
206: return isset($this->invalidSnippets[NULL]) || isset($this->invalidSnippets[$snippet]);
207: }
208: }
209:
210:
211:
212: /**
213: * Returns snippet HTML ID.
214: * @param string snippet name
215: * @return string
216: */
217: public function getSnippetId($name = NULL)
218: {
219: // HTML 4 ID & NAME: [A-Za-z][A-Za-z0-9:_.-]*
220: return 'snippet-' . $this->getUniqueId() . '-' . $name;
221: }
222:
223: }
224: