1: <?php
2:
3: /**
4: * This file is part of the Nette Framework (https://nette.org)
5: * Copyright (c) 2004 David Grudl (https://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()->createService('nette.latte'));
97: }
98:
99:
100: /**
101: * Saves the message to template, that can be displayed after redirect.
102: * @param string
103: * @param string
104: * @return \stdClass
105: */
106: public function flashMessage($message, $type = 'info')
107: {
108: $id = $this->getParameterId('flash');
109: $messages = $this->getPresenter()->getFlashSession()->$id;
110: $messages[] = $flash = (object) array(
111: 'message' => $message,
112: 'type' => $type,
113: );
114: $this->getTemplate()->flashes = $messages;
115: $this->getPresenter()->getFlashSession()->$id = $messages;
116: return $flash;
117: }
118:
119:
120: /********************* rendering ****************d*g**/
121:
122:
123: /**
124: * Forces control or its snippet to repaint.
125: * @return void
126: */
127: public function redrawControl($snippet = NULL, $redraw = TRUE)
128: {
129: if ($redraw) {
130: $this->invalidSnippets[$snippet === NULL ? "\0" : $snippet] = TRUE;
131:
132: } elseif ($snippet === NULL) {
133: $this->invalidSnippets = array();
134:
135: } else {
136: unset($this->invalidSnippets[$snippet]);
137: }
138: }
139:
140:
141: /** @deprecated */
142: function invalidateControl($snippet = NULL)
143: {
144: $this->redrawControl($snippet);
145: }
146:
147: /** @deprecated */
148: function validateControl($snippet = NULL)
149: {
150: $this->redrawControl($snippet, FALSE);
151: }
152:
153:
154: /**
155: * Is required to repaint the control or its snippet?
156: * @param string snippet name
157: * @return bool
158: */
159: public function isControlInvalid($snippet = NULL)
160: {
161: if ($snippet === NULL) {
162: if (count($this->invalidSnippets) > 0) {
163: return TRUE;
164:
165: } else {
166: $queue = array($this);
167: do {
168: foreach (array_shift($queue)->getComponents() as $component) {
169: if ($component instanceof IRenderable) {
170: if ($component->isControlInvalid()) {
171: // $this->invalidSnippets['__child'] = TRUE; // as cache
172: return TRUE;
173: }
174:
175: } elseif ($component instanceof Nette\ComponentModel\IContainer) {
176: $queue[] = $component;
177: }
178: }
179: } while ($queue);
180:
181: return FALSE;
182: }
183:
184: } else {
185: return isset($this->invalidSnippets["\0"]) || isset($this->invalidSnippets[$snippet]);
186: }
187: }
188:
189:
190: /**
191: * Returns snippet HTML ID.
192: * @param string snippet name
193: * @return string
194: */
195: public function getSnippetId($name = NULL)
196: {
197: // HTML 4 ID & NAME: [A-Za-z][A-Za-z0-9:_.-]*
198: return 'snippet-' . $this->getUniqueId() . '-' . $name;
199: }
200:
201: }
202: