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