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