Blogexample:
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / View / TX_EXTMVC_View_TemplateView.php
1 <?php
2 declare(ENCODING = 'utf-8');
3
4 /* *
5 * This script belongs to the FLOW3 framework. *
6 * *
7 * It is free software; you can redistribute it and/or modify it under *
8 * the terms of the GNU Lesser General Public License as published by the *
9 * Free Software Foundation, either version 3 of the License, or (at your *
10 * option) any later version. *
11 * *
12 * This script is distributed in the hope that it will be useful, but *
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
14 * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
15 * General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public *
18 * License along with the script. *
19 * If not, see http://www.gnu.org/licenses/lgpl.html *
20 * *
21 * The TYPO3 project - inspiring people to share! *
22 * */
23
24 require_once(PATH_t3lib . 'class.t3lib_parsehtml.php');
25 require_once(t3lib_extMgm::extPath('extmvc') . 'Classes/Utility/TX_EXTMVC_Utility_Strings.php');
26
27 /**
28 * A basic Template View
29 *
30 * @version $Id:$
31 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
32 * @scope prototype
33 */
34 class TX_EXTMVC_View_TemplateView extends TX_EXTMVC_View_AbstractView {
35
36 /**
37 * Pattern for fetching information from controller object name
38 * @var string
39 */
40 const PATTERN_CONTROLLER = '/^TX_\w*_Controller_(?P<ControllerName>\w*)Controller$/sm';
41
42 const SCAN_PATTERN_SUBPARTS = '/<!--\s*###(?P<SubpartName>[^#]*)###.*?-->(?P<SubpartTemplateSource>.*?)<!--\s*###(?P=SubpartName)###.*?-->/sm';
43 const SCAN_PATTERN_MARKER = '/###(?P<MarkerName>.*?)###/sm';
44
45 const SPLIT_PATTERN_MARKER = '/^(?:(?P<ViewHelperName>[a-zA-Z0-9_]+):)?(?P<ContextVariable>(?:\s*[a-zA-Z0-9_]+)(?=(\s|$)))?(?P<ObjectAndProperty>(?:\s*[a-zA-Z0-9_]+\.(?:[a-zA-Z0-9_]+)(?=(\s|$))))?(?P<Attributes>(?:\s*[a-zA-Z0-9_]+=(?:"(?:[^"])*"|\'(?:[^\'])*\'|[a-zA-Z0-9_\.]+)\s*)*)\s*$/';
46 const SPLIT_PATTERN_ARGUMENTS = '/(?P<ArgumentKey>[a-zA-Z][a-zA-Z0-9_]*)=(?:(?:"(?P<ValueDoubleQuoted>[^"\s]+)")|(?:\'(?P<ValueSingleQuoted>[^\'\s]+)\')|(?:(?P<ValueUnquoted>[^"\'\s]*)))/';
47
48 /**
49 * File pattern for resolving the template file
50 * @var string
51 */
52 protected $templateFilePattern = 'Resources/Template/@controller/@action.xhtml';
53
54 /**
55 * @var array Marker uids and their replacement content
56 */
57 protected $markers = array();
58
59 /**
60 * @var array Subparts
61 */
62 protected $subparts = array();
63
64 /**
65 * @var array Wrapped subparts
66 */
67 protected $wrappedSubparts = array();
68
69 /**
70 * Context variables
71 * @var array of context variables
72 */
73 protected $contextVariables = array();
74
75 /**
76 * Template file path. If set, overrides the templateFilePattern
77 * @var string
78 */
79 protected $templateFile = NULL;
80
81 /**
82 * @var string
83 */
84 protected $templateSource = '';
85
86 /**
87 * Name of current action to render
88 * @var string
89 */
90 protected $actionName;
91
92 /**
93 * The content object
94 *
95 * @var tslib_cObj
96 **/
97 private $cObj;
98
99 public function __construct() {
100 $this->initializeView();
101 }
102
103 /**
104 * Initialize view
105 *
106 * @return void
107 */
108 protected function initializeView() {
109 $this->cObj = t3lib_div::makeInstance('tslib_cObj');
110 }
111
112 /**
113 * Sets the template file. Effectively overrides the dynamic resolving of a template file.
114 *
115 * @param string $templateFile Template file path
116 * @return void
117 */
118 public function setTemplateFile($templateFile) {
119 $this->templateFile = $templateFile;
120 }
121
122 /**
123 * Sets the text source which contains the markers of this template view
124 * is going to fill in.
125 *
126 * @param string $templateSource The template source
127 * @return void
128 */
129 public function setTemplateSource($templateSource) {
130 $this->templateSource = $templateSource;
131 }
132
133 /**
134 * Resolve the template file path, based on $this->templateFilePath and $this->templatePathPattern.
135 * In case a template has been set with $this->setTemplateFile, it just uses the given template file.
136 * Otherwise, it resolves the $this->templatePathPattern
137 *
138 * @param string $action Name of action. Optional. Defaults to current action.
139 * @return string File name of template file
140 */
141 protected function resolveTemplateFile() {
142 if ($this->templateFile) {
143 return $this->templateFile;
144 } else {
145 $action = ($this->actionName ? $this->actionName : $this->request->getControllerActionName());
146 preg_match(self::PATTERN_CONTROLLER, $this->request->getControllerObjectName(), $matches);
147 $controllerName = $matches['ControllerName'];
148 $templateFile = $this->templateFilePattern;
149 $templateFile = str_replace('@controller', $controllerName, $templateFile);
150 $templateFile = str_replace('@action', strtolower($action), $templateFile);
151 return $templateFile;
152 }
153 }
154
155 /**
156 * Load the given template file.
157 *
158 * @param string $templateFilePath Full path to template file to load
159 * @return string the contents of the template file
160 */
161 protected function loadTemplateFile($templateFilePath) {
162 $templateSource = file_get_contents(t3lib_extMgm::extPath(strtolower($this->request->getControllerExtensionKey())) . $templateFilePath, FILE_TEXT);
163 if (!$templateSource) throw new RuntimeException('The template file "' . $templateFilePath . '" was not found.', 1225709595); // TODO Specific exception
164 return $templateSource;
165 }
166
167 /**
168 * Find the XHTML template according to $this->templatePathPattern and render the template.
169 *
170 * @return string Rendered Template
171 */
172 public function render() {
173 if ($this->templateSource == '') {
174 $this->actionName = $action;
175 $templateFileName = $this->resolveTemplateFile();
176 $templateSource = $this->loadTemplateFile($templateFileName);
177 } else {
178 $templateSource = $this->templateSource;
179 }
180 // TODO exception if a template was not defined
181 $content = $this->renderTemplate($templateSource, $this->contextVariables);
182 // $this->removeUnfilledMarkers($content);
183 return $content;
184 }
185
186 /**
187 * Recursive rendering of a given template source.
188 *
189 * @param string $templateSource The template source
190 * @return void
191 */
192 protected function renderTemplate($templateSource, $variables) {
193 $subpartArray = array();
194 $subparts = $this->getSubparts($templateSource);
195 foreach ($subparts as $subpartMarker => $subpartSource) {
196 $subpartArray['###' . $subpartMarker . '###'] = $this->getMarkerContent($subpartMarker, $variables, $subpartSource);
197 }
198 // $content = $this->cObj->substituteMarkerArrayCached($templateSource, $markerArray, $subpartArray, $wrappedSubpartArray);
199 $markerArray = array();
200 $markers = $this->getMarkers($templateSource);
201 foreach ($markers as $marker => $foo) {
202 $markerArray['###' . $marker . '###'] = $this->getMarkerContent($marker, $variables);
203 }
204 $content = $this->cObj->substituteMarkerArrayCached($templateSource, $markerArray, $subpartArray, $wrappedSubpartArray);
205
206 return $content;
207 }
208
209 public function getMarkerArray($templateSource, $value = NULL) {
210 $markers = $this->getMarkers($templateSource);
211 $markerArray = array();
212 foreach ($markers as $marker => $foo) {
213 $markerArray['###' . $marker . '###'] = $this->getMarkerContent($marker, $value);
214 }
215 return $markerArray;
216 }
217
218 protected function getMarkerContent($marker, $variables = NULL, $templateSource = NULL) {
219 preg_match(self::SPLIT_PATTERN_MARKER, $marker, $explodedMarker);
220 $viewHelperName = TX_EXTMVC_Utility_Strings::underscoredToUpperCamelCase($explodedMarker['ViewHelperName']);
221 $contextVariable = TX_EXTMVC_Utility_Strings::underscoredToUpperCamelCase($explodedMarker['ContextVariable']);
222 $explodedObjectAndProperty = explode('.', $explodedMarker['ObjectAndProperty']);
223 $objectName = TX_EXTMVC_Utility_Strings::underscoredToUpperCamelCase($explodedObjectAndProperty[0]);
224 $property = TX_EXTMVC_Utility_Strings::underscoredToLowerCamelCase($explodedObjectAndProperty[1]);
225 if (!empty($explodedMarker['Attributes'])) {
226 $arguments = $this->getArguments($explodedMarker['Attributes'], $variables);
227 }
228 if ($variables[$objectName] instanceof TX_EXTMVC_DomainObject_AbstractDomainObject) {
229 $object = $variables[$objectName];
230 $possibleMethodName = 'get' . $property;
231 if (method_exists($object, $possibleMethodName)) {
232 $content = $object->$possibleMethodName();
233 }
234 }
235
236 if ($viewHelperName === 'Convert') {
237 if (!empty($arguments['format'])) {
238 $format = $arguments['format'];
239 } else {
240 $format = NULL;
241 }
242 }
243
244 if ($viewHelperName === 'For') {
245 if (is_array($arguments['each'])) {
246 foreach ($arguments['each'] as $singleElement) {
247 $variables[TX_EXTMVC_Utility_Strings::underscoredToUpperCamelCase($arguments['as'])] = $singleElement; // FIXME strtolower
248 $content .= $this->renderTemplate($templateSource, $variables);
249 }
250 }
251 }
252 return $this->convertValue($content, $format);
253 }
254
255 protected function getArguments($attributes, $variables) {
256 preg_match_all(self::SPLIT_PATTERN_ARGUMENTS, $attributes, $explodedAttributes, PREG_SET_ORDER);
257 $arguments = array();
258 foreach ($explodedAttributes as $explodedAttribute) {
259 if (!empty($explodedAttribute['ValueDoubleQuoted'])) {
260 $argumentValue = $explodedAttribute['ValueDoubleQuoted'];
261 } elseif (!empty($explodedAttribute['ValueSingleQuoted'])) {
262 $argumentValue = $explodedAttribute['ValueSingleQuoted'];
263 } elseif (!empty($explodedAttribute['ValueUnquoted'])) {
264 $explodedValue = explode('.', $explodedAttribute['ValueUnquoted']);
265 if (count($explodedValue) > 1) {
266 $possibleMethodName = 'get' . TX_EXTMVC_Utility_Strings::underscoredToUpperCamelCase($explodedValue[1]);
267 $argumentValueObject = $variables[TX_EXTMVC_Utility_Strings::underscoredToUpperCamelCase($explodedValue[0])];
268 if (method_exists($argumentValueObject, $possibleMethodName)) {
269 $argumentValue = $argumentValueObject->$possibleMethodName();
270 }
271 } else {
272 $argumentValue = $variables[$explodedValue[0]];
273 }
274 } else {
275 $argumentValue = NULL;
276 }
277 $arguments[TX_EXTMVC_Utility_Strings::underscoredToLowerCamelCase($explodedAttribute['ArgumentKey'])] = $argumentValue;
278 }
279 return $arguments;
280 }
281
282 protected function getSubpartArray($templateSource) {
283 $subpartArray = array();
284 if (count($subparts) > 0) {
285 foreach ($subparts as $subpartMarker => $subpartTemplateSource) {
286 $value = $this->getMarkerContent($subpartMarker);
287 $subpartArray['###' . $subpartMarker . '###'] .= $this->renderTemplate($subpartTemplateSource, $value);
288 }
289 }
290 return $subpartArray;
291 }
292
293 protected function getSubparts($templateSource) {
294 preg_match_all(self::SCAN_PATTERN_SUBPARTS, $templateSource, $matches, PREG_SET_ORDER);
295 $subparts = array();
296 if (is_array($matches)) {
297 foreach ($matches as $key => $match) {
298 $subparts[$match['SubpartName']] = $match['SubpartTemplateSource'];
299 }
300 }
301 return $subparts;
302 }
303
304 protected function getMarkers($templateSource) {
305 preg_match_all(self::SCAN_PATTERN_MARKER, $templateSource, $matches, PREG_SET_ORDER);
306 $markers = array();
307 if (is_array($matches)) {
308 foreach ($matches as $key => $match) {
309 $markers[$match['MarkerName']] = NULL;
310 }
311 }
312 return $markers;
313 }
314
315 /**
316 * Resolve a view helper.
317 *
318 * @param string $namespaceIdentifier Namespace identifier for the view helper.
319 * @param string $methodIdentifier Method identifier, might be hierarchical like "link.url"
320 * @return array An Array where the first argument is the object to call the method on, and the second argument is the method name
321 */
322 protected function resolveViewHelperClassName($viewHelperName) {
323 $className = '';
324 $className = ucfirst($explodedViewHelperName[0]);
325 $className .= 'ViewHelper';
326
327 $name = 'TX_Blogexample_View_' . $className;
328
329 return $name;
330 }
331
332
333
334 protected function convertValue($value, $format = NULL) {
335 if (!is_string($format)) ; // TODO Throw exception?
336 if ($value instanceof DateTime) {
337 if ($format === NULL) {
338 $value = $value->format('Y-m-d G:i'); // TODO Date time format from extension settings
339 } else {
340 $value = $value->format($format);
341 }
342 } else {
343 }
344 return $value;
345 }
346
347 protected function removeUnfilledMarkers(&$content) {
348 // TODO remove also comments
349 $content = preg_replace('/###.*###|<!--[^>]*###.*###[^<]*-->(.*)/msU', '', $content);
350 }
351
352 /**
353 * Assigns domain models (single objects or aggregates) or values to the view
354 *
355 * @param string $valueName The name of the value
356 * @param mixed $value the value to assign
357 * @return void
358 */
359 public function assign($key, $value) {
360 $this->contextVariables[$key] = $value;
361 }
362 }
363 ?>