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: /********************* template factory ****************d*g**/
37:
38:
39: /**
40: * @return ITemplate
41: */
42: final public function getTemplate()
43: {
44: if ($this->template === NULL) {
45: $value = $this->createTemplate();
46: if (!$value instanceof ITemplate && $value !== NULL) {
47: $class2 = get_class($value); $class = get_class($this);
48: throw new UnexpectedValueException("Object returned by $class::createTemplate() must be instance of ITemplate, '$class2' given.");
49: }
50: $this->template = $value;
51: }
52: return $this->template;
53: }
54:
55:
56: /**
57: * @param string|NULL
58: * @return ITemplate
59: */
60: protected function createTemplate($class = NULL)
61: {
62: $template = $class ? new $class : new NFileTemplate;
63: $presenter = $this->getPresenter(FALSE);
64: $template->onPrepareFilters[] = $this->templatePrepareFilters;
65: $template->registerHelperLoader('NTemplateHelpers::loader');
66:
67: // default parameters
68: $template->control = $template->_control = $this;
69: $template->presenter = $template->_presenter = $presenter;
70: if ($presenter instanceof NPresenter) {
71: $template->setCacheStorage($presenter->getContext()->getService('nette.templateCacheStorage'));
72: $template->user = $presenter->getUser();
73: $template->netteHttpResponse = $presenter->getHttpResponse();
74: $template->netteCacheStorage = $presenter->getContext()->getByType('ICacheStorage');
75: $template->baseUri = $template->baseUrl = rtrim($presenter->getHttpRequest()->getUrl()->getBaseUrl(), '/');
76: $template->basePath = preg_replace('#https?://[^/]+#A', '', $template->baseUrl);
77:
78: // flash message
79: if ($presenter->hasFlashSession()) {
80: $id = $this->getParameterId('flash');
81: $template->flashes = $presenter->getFlashSession()->$id;
82: }
83: }
84: if (!isset($template->flashes) || !is_array($template->flashes)) {
85: $template->flashes = array();
86: }
87:
88: return $template;
89: }
90:
91:
92: /**
93: * Descendant can override this method to customize template compile-time filters.
94: * @param NTemplate
95: * @return void
96: */
97: public function templatePrepareFilters($template)
98: {
99: $template->registerFilter($this->getPresenter()->getContext()->nette->createLatte());
100: }
101:
102:
103: /**
104: * Returns widget component specified by name.
105: * @param string
106: * @return IComponent
107: */
108: public function getWidget($name)
109: {
110: trigger_error(__METHOD__ . '() is deprecated, use getComponent() instead.', E_USER_WARNING);
111: return $this->getComponent($name);
112: }
113:
114:
115: /**
116: * Saves the message to template, that can be displayed after redirect.
117: * @param string
118: * @param string
119: * @return \stdClass
120: */
121: public function flashMessage($message, $type = 'info')
122: {
123: $id = $this->getParameterId('flash');
124: $messages = $this->getPresenter()->getFlashSession()->$id;
125: $messages[] = $flash = (object) array(
126: 'message' => $message,
127: 'type' => $type,
128: );
129: $this->getTemplate()->flashes = $messages;
130: $this->getPresenter()->getFlashSession()->$id = $messages;
131: return $flash;
132: }
133:
134:
135: /********************* rendering ****************d*g**/
136:
137:
138: /**
139: * Forces control or its snippet to repaint.
140: * @param string
141: * @return void
142: */
143: public function invalidateControl($snippet = NULL)
144: {
145: $this->invalidSnippets[$snippet] = TRUE;
146: }
147:
148:
149: /**
150: * Allows control or its snippet to not repaint.
151: * @param string
152: * @return void
153: */
154: public function validateControl($snippet = NULL)
155: {
156: if ($snippet === NULL) {
157: $this->invalidSnippets = array();
158:
159: } else {
160: unset($this->invalidSnippets[$snippet]);
161: }
162: }
163:
164:
165: /**
166: * Is required to repaint the control or its snippet?
167: * @param string snippet name
168: * @return bool
169: */
170: public function isControlInvalid($snippet = NULL)
171: {
172: if ($snippet === NULL) {
173: if (count($this->invalidSnippets) > 0) {
174: return TRUE;
175:
176: } else {
177: $queue = array($this);
178: do {
179: foreach (array_shift($queue)->getComponents() as $component) {
180: if ($component instanceof IRenderable) {
181: if ($component->isControlInvalid()) {
182: // $this->invalidSnippets['__child'] = TRUE; // as cache
183: return TRUE;
184: }
185:
186: } elseif ($component instanceof IComponentContainer) {
187: $queue[] = $component;
188: }
189: }
190: } while ($queue);
191:
192: return FALSE;
193: }
194:
195: } else {
196: return isset($this->invalidSnippets[NULL]) || isset($this->invalidSnippets[$snippet]);
197: }
198: }
199:
200:
201: /**
202: * Returns snippet HTML ID.
203: * @param string snippet name
204: * @return string
205: */
206: public function getSnippetId($name = NULL)
207: {
208: // HTML 4 ID & NAME: [A-Za-z][A-Za-z0-9:_.-]*
209: return 'snippet-' . $this->getUniqueId() . '-' . $name;
210: }
211:
212: }
213: