[CLEANUP] Replace FLOW3 with TYPO3 Flow
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Classes / View / TemplateView.php
1 <?php
2 namespace TYPO3\CMS\Fluid\View;
3
4 /* *
5 * This script is backported from the TYPO3 Flow package "TYPO3.Fluid". *
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, either version 3 *
9 * of the License, or (at your option) any later version. *
10 * *
11 * The TYPO3 project - inspiring people to share! *
12 * */
13 /**
14 * The main template view. Should be used as view if you want Fluid Templating
15 *
16 * @api
17 */
18 class TemplateView extends \TYPO3\CMS\Fluid\View\AbstractTemplateView {
19
20 /**
21 * Pattern to be resolved for "@templateRoot" in the other patterns.
22 *
23 * @var string
24 */
25 protected $templateRootPathPattern = '@packageResourcesPath/Private/Templates';
26
27 /**
28 * Pattern to be resolved for "@partialRoot" in the other patterns.
29 *
30 * @var string
31 */
32 protected $partialRootPathPattern = '@packageResourcesPath/Private/Partials';
33
34 /**
35 * Pattern to be resolved for "@layoutRoot" in the other patterns.
36 *
37 * @var string
38 */
39 protected $layoutRootPathPattern = '@packageResourcesPath/Private/Layouts';
40
41 /**
42 * Path to the template root. If NULL, then $this->templateRootPathPattern will be used.
43 *
44 * @var string
45 */
46 protected $templateRootPath = NULL;
47
48 /**
49 * Path to the partial root. If NULL, then $this->partialRootPathPattern will be used.
50 *
51 * @var string
52 */
53 protected $partialRootPath = NULL;
54
55 /**
56 * Path to the layout root. If NULL, then $this->layoutRootPathPattern will be used.
57 *
58 * @var string
59 */
60 protected $layoutRootPath = NULL;
61
62 /**
63 * File pattern for resolving the template file
64 *
65 * @var string
66 */
67 protected $templatePathAndFilenamePattern = '@templateRoot/@subpackage/@controller/@action.@format';
68
69 /**
70 * Directory pattern for global partials. Not part of the public API, should not be changed for now.
71 *
72 * @var string
73 */
74 private $partialPathAndFilenamePattern = '@partialRoot/@subpackage/@partial.@format';
75
76 /**
77 * File pattern for resolving the layout
78 *
79 * @var string
80 */
81 protected $layoutPathAndFilenamePattern = '@layoutRoot/@layout.@format';
82
83 /**
84 * Path and filename of the template file. If set, overrides the templatePathAndFilenamePattern
85 *
86 * @var string
87 */
88 protected $templatePathAndFilename = NULL;
89
90 /**
91 * Path and filename of the layout file. If set, overrides the layoutPathAndFilenamePattern
92 *
93 * @var string
94 */
95 protected $layoutPathAndFilename = NULL;
96
97 public function __construct() {
98 $this->injectTemplateParser(\TYPO3\CMS\Fluid\Compatibility\TemplateParserBuilder::build());
99 $this->injectObjectManager(\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager'));
100 $this->setRenderingContext($this->objectManager->create('TYPO3\\CMS\\Fluid\\Core\\Rendering\\RenderingContextInterface'));
101 }
102
103 public function initializeView() {
104
105 }
106
107 // Here, the backporter can insert a constructor method, which is needed for Fluid v4.
108 /**
109 * Sets the path and name of of the template file. Effectively overrides the
110 * dynamic resolving of a template file.
111 *
112 * @param string $templatePathAndFilename Template file path
113 * @return void
114 * @api
115 */
116 public function setTemplatePathAndFilename($templatePathAndFilename) {
117 $this->templatePathAndFilename = $templatePathAndFilename;
118 }
119
120 /**
121 * Sets the path and name of the layout file. Overrides the dynamic resolving of the layout file.
122 *
123 * @param string $layoutPathAndFilename Path and filename of the layout file
124 * @return void
125 * @api
126 */
127 public function setLayoutPathAndFilename($layoutPathAndFilename) {
128 $this->layoutPathAndFilename = $layoutPathAndFilename;
129 }
130
131 /**
132 * Checks whether a template can be resolved for the current request context.
133 *
134 * @param \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext Controller context which is available inside the view
135 * @return boolean
136 * @api
137 */
138 public function canRender(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext) {
139 $this->setControllerContext($controllerContext);
140 try {
141 $this->getTemplateSource();
142 return TRUE;
143 } catch (\TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException $e) {
144 return FALSE;
145 }
146 }
147
148 /**
149 * Set the root path to the templates.
150 * If set, overrides the one determined from $this->templateRootPathPattern
151 *
152 * @param string $templateRootPath Root path to the templates. If set, overrides the one determined from $this->templateRootPathPattern
153 * @return void
154 * @api
155 */
156 public function setTemplateRootPath($templateRootPath) {
157 $this->templateRootPath = $templateRootPath;
158 }
159
160 /**
161 * Returns a unique identifier for the resolved template file
162 * This identifier is based on the template path and last modification date
163 *
164 * @param string $actionName Name of the action. If NULL, will be taken from request.
165 * @return string template identifier
166 */
167 protected function getTemplateIdentifier($actionName = NULL) {
168 $templatePathAndFilename = $this->getTemplatePathAndFilename($actionName);
169 if ($actionName === NULL) {
170 $actionName = $this->controllerContext->getRequest()->getControllerActionName();
171 }
172 $prefix = 'action_' . $actionName;
173 return $this->createIdentifierForFile($templatePathAndFilename, $prefix);
174 }
175
176 /**
177 * Resolve the template path and filename for the given action. If $actionName
178 * is NULL, looks into the current request.
179 *
180 * @param string $actionName Name of the action. If NULL, will be taken from request.
181 * @return string Full path to template
182 * @throws \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException
183 */
184 protected function getTemplateSource($actionName = NULL) {
185 $templatePathAndFilename = $this->getTemplatePathAndFilename($actionName);
186 $templateSource = file_get_contents($templatePathAndFilename);
187 if ($templateSource === FALSE) {
188 throw new \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException('"' . $templatePathAndFilename . '" is not a valid template resource URI.', 1257246929);
189 }
190 return $templateSource;
191 }
192
193 /**
194 * Resolve the template path and filename for the given action. If $actionName
195 * is NULL, looks into the current request.
196 *
197 * @param string $actionName Name of the action. If NULL, will be taken from request.
198 * @return string Full path to template
199 * @throws \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException
200 */
201 protected function getTemplatePathAndFilename($actionName = NULL) {
202 if ($this->templatePathAndFilename !== NULL) {
203 return $this->templatePathAndFilename;
204 }
205 if ($actionName === NULL) {
206 $actionName = $this->controllerContext->getRequest()->getControllerActionName();
207 }
208 $actionName = ucfirst($actionName);
209 $paths = $this->expandGenericPathPattern($this->templatePathAndFilenamePattern, FALSE, FALSE);
210 foreach ($paths as &$templatePathAndFilename) {
211 // These tokens are replaced by the Backporter for the graceful fallback in version 4.
212 $fallbackPath = str_replace('@action', lcfirst($actionName), $templatePathAndFilename);
213 $templatePathAndFilename = str_replace('@action', $actionName, $templatePathAndFilename);
214 if (file_exists($templatePathAndFilename)) {
215 // additional check for deprecated template filename for case insensitive file systems (Windows)
216 $realFileName = basename(realpath($templatePathAndFilename));
217 if ($realFileName !== ucfirst($realFileName)) {
218 \TYPO3\CMS\Core\Utility\GeneralUtility::deprecationLog('the template filename "' . \TYPO3\CMS\Core\Utility\GeneralUtility::fixWindowsFilePath(realpath($templatePathAndFilename)) . '" is lowercase. This is deprecated since TYPO3 4.4. Please rename the template to "' . basename($templatePathAndFilename) . '"');
219 }
220 return $templatePathAndFilename;
221 } elseif (file_exists($fallbackPath)) {
222 \TYPO3\CMS\Core\Utility\GeneralUtility::deprecationLog('the template filename "' . $fallbackPath . '" is lowercase. This is deprecated since TYPO3 4.4. Please rename the template to "' . basename($templatePathAndFilename) . '"');
223 return $fallbackPath;
224 }
225 }
226 throw new \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException('Template could not be loaded. I tried "' . implode('", "', $paths) . '"', 1225709595);
227 }
228
229 /**
230 * Returns a unique identifier for the resolved layout file.
231 * This identifier is based on the template path and last modification date
232 *
233 * @param string $layoutName The name of the layout
234 * @return string layout identifier
235 */
236 protected function getLayoutIdentifier($layoutName = 'Default') {
237 $layoutPathAndFilename = $this->getLayoutPathAndFilename($layoutName);
238 $prefix = 'layout_' . $layoutName;
239 return $this->createIdentifierForFile($layoutPathAndFilename, $prefix);
240 }
241
242 /**
243 * Resolve the path and file name of the layout file, based on
244 * $this->layoutPathAndFilename and $this->layoutPathAndFilenamePattern.
245 *
246 * In case a layout has already been set with setLayoutPathAndFilename(),
247 * this method returns that path, otherwise a path and filename will be
248 * resolved using the layoutPathAndFilenamePattern.
249 *
250 * @param string $layoutName Name of the layout to use. If none given, use "Default
251 * @return string contents of the layout template
252 * @throws \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException
253 */
254 protected function getLayoutSource($layoutName = 'Default') {
255 $layoutPathAndFilename = $this->getLayoutPathAndFilename($layoutName);
256 $layoutSource = file_get_contents($layoutPathAndFilename);
257 if ($layoutSource === FALSE) {
258 throw new \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException('"' . $layoutPathAndFilename . '" is not a valid template resource URI.', 1257246929);
259 }
260 return $layoutSource;
261 }
262
263 /**
264 * Resolve the path and file name of the layout file, based on
265 * $this->layoutPathAndFilename and $this->layoutPathAndFilenamePattern.
266 *
267 * In case a layout has already been set with setLayoutPathAndFilename(),
268 * this method returns that path, otherwise a path and filename will be
269 * resolved using the layoutPathAndFilenamePattern.
270 *
271 * @param string $layoutName Name of the layout to use. If none given, use "Default
272 * @return string Path and filename of layout files
273 * @throws \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException
274 */
275 protected function getLayoutPathAndFilename($layoutName = 'Default') {
276 if ($this->layoutPathAndFilename !== NULL) {
277 return $this->layoutPathAndFilename;
278 }
279 $paths = $this->expandGenericPathPattern($this->layoutPathAndFilenamePattern, TRUE, TRUE);
280 $layoutName = ucfirst($layoutName);
281 foreach ($paths as &$layoutPathAndFilename) {
282 // These tokens are replaced by the Backporter for the graceful fallback in version 4.
283 $fallbackPath = str_replace('@layout', lcfirst($layoutName), $layoutPathAndFilename);
284 $layoutPathAndFilename = str_replace('@layout', $layoutName, $layoutPathAndFilename);
285 if (file_exists($layoutPathAndFilename)) {
286 return $layoutPathAndFilename;
287 } elseif (file_exists($fallbackPath)) {
288 \TYPO3\CMS\Core\Utility\GeneralUtility::deprecationLog('the layout filename "' . $fallbackPath . '" is lowercase. This is deprecated since TYPO3 4.6. Please rename the layout to "' . basename($layoutPathAndFilename) . '"');
289 return $fallbackPath;
290 }
291 }
292 throw new \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException('The template files "' . implode('", "', $paths) . '" could not be loaded.', 1225709595);
293 }
294
295 /**
296 * Returns a unique identifier for the resolved partial file.
297 * This identifier is based on the template path and last modification date
298 *
299 * @param string $partialName The name of the partial
300 * @return string partial identifier
301 */
302 protected function getPartialIdentifier($partialName) {
303 $partialPathAndFilename = $this->getPartialPathAndFilename($partialName);
304 $prefix = 'partial_' . $partialName;
305 return $this->createIdentifierForFile($partialPathAndFilename, $prefix);
306 }
307
308 /**
309 * Figures out which partial to use.
310 *
311 * @param string $partialName The name of the partial
312 * @return string contents of the partial template
313 * @throws \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException
314 */
315 protected function getPartialSource($partialName) {
316 $partialPathAndFilename = $this->getPartialPathAndFilename($partialName);
317 $partialSource = file_get_contents($partialPathAndFilename);
318 if ($partialSource === FALSE) {
319 throw new \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException('"' . $partialPathAndFilename . '" is not a valid template resource URI.', 1257246929);
320 }
321 return $partialSource;
322 }
323
324 /**
325 * Resolve the partial path and filename based on $this->partialPathAndFilenamePattern.
326 *
327 * @param string $partialName The name of the partial
328 * @return string the full path which should be used. The path definitely exists.
329 * @throws \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException
330 */
331 protected function getPartialPathAndFilename($partialName) {
332 $paths = $this->expandGenericPathPattern($this->partialPathAndFilenamePattern, TRUE, TRUE);
333 foreach ($paths as &$partialPathAndFilename) {
334 $partialPathAndFilename = str_replace('@partial', $partialName, $partialPathAndFilename);
335 if (file_exists($partialPathAndFilename)) {
336 return $partialPathAndFilename;
337 }
338 }
339 throw new \TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException('The template files "' . implode('", "', $paths) . '" could not be loaded.', 1225709595);
340 }
341
342 /**
343 * Resolves the template root to be used inside other paths.
344 *
345 * @return string Path to template root directory
346 */
347 protected function getTemplateRootPath() {
348 if ($this->templateRootPath !== NULL) {
349 return $this->templateRootPath;
350 } else {
351 return str_replace('@packageResourcesPath', \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($this->controllerContext->getRequest()->getControllerExtensionKey()) . 'Resources/', $this->templateRootPathPattern);
352 }
353 }
354
355 /**
356 * Set the root path to the partials.
357 * If set, overrides the one determined from $this->partialRootPathPattern
358 *
359 * @param string $partialRootPath Root path to the partials. If set, overrides the one determined from $this->partialRootPathPattern
360 * @return void
361 * @api
362 */
363 public function setPartialRootPath($partialRootPath) {
364 $this->partialRootPath = $partialRootPath;
365 }
366
367 /**
368 * Resolves the partial root to be used inside other paths.
369 *
370 * @return string Path to partial root directory
371 */
372 protected function getPartialRootPath() {
373 if ($this->partialRootPath !== NULL) {
374 return $this->partialRootPath;
375 } else {
376 return str_replace('@packageResourcesPath', \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($this->controllerContext->getRequest()->getControllerExtensionKey()) . 'Resources/', $this->partialRootPathPattern);
377 }
378 }
379
380 /**
381 * Set the root path to the layouts.
382 * If set, overrides the one determined from $this->layoutRootPathPattern
383 *
384 * @param string $layoutRootPath Root path to the layouts. If set, overrides the one determined from $this->layoutRootPathPattern
385 * @return void
386 * @api
387 */
388 public function setLayoutRootPath($layoutRootPath) {
389 $this->layoutRootPath = $layoutRootPath;
390 }
391
392 /**
393 * Resolves the layout root to be used inside other paths.
394 *
395 * @return string Path to layout root directory
396 */
397 protected function getLayoutRootPath() {
398 if ($this->layoutRootPath !== NULL) {
399 return $this->layoutRootPath;
400 } else {
401 return str_replace('@packageResourcesPath', \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($this->controllerContext->getRequest()->getControllerExtensionKey()) . 'Resources/', $this->layoutRootPathPattern);
402 }
403 }
404
405 /**
406 * Processes "@templateRoot", "@subpackage", "@controller", and "@format" placeholders inside $pattern.
407 * This method is used to generate "fallback chains" for file system locations where a certain Partial can reside.
408 *
409 * If $bubbleControllerAndSubpackage is FALSE and $formatIsOptional is FALSE, then the resulting array will only have one element
410 * with all the above placeholders replaced.
411 *
412 * If you set $bubbleControllerAndSubpackage to TRUE, then you will get an array with potentially many elements:
413 * The first element of the array is like above. The second element has the @ controller part set to "" (the empty string)
414 * The third element now has the @ controller part again stripped off, and has the last subpackage part stripped off as well.
415 * This continues until both "@subpackage" and "@controller" are empty.
416 *
417 * Example for $bubbleControllerAndSubpackage is TRUE, we have the Tx_MyExtension_MySubPackage_Controller_MyController
418 * as Controller Object Name and the current format is "html"
419 *
420 * If pattern is "@templateRoot/@subpackage/@controller/@action.@format", then the resulting array is:
421 * - "Resources/Private/Templates/MySubPackage/My/@action.html"
422 * - "Resources/Private/Templates/MySubPackage/@action.html"
423 * - "Resources/Private/Templates/@action.html"
424 *
425 * If you set $formatIsOptional to TRUE, then for any of the above arrays, every element will be duplicated - once with "@format"
426 * replaced by the current request format, and once with ."@format" stripped off.
427 *
428 * @param string $pattern Pattern to be resolved
429 * @param boolean $bubbleControllerAndSubpackage if TRUE, then we successively split off parts from "@controller" and "@subpackage" until both are empty.
430 * @param boolean $formatIsOptional if TRUE, then half of the resulting strings will have ."@format" stripped off, and the other half will have it.
431 * @return array unix style path
432 */
433 protected function expandGenericPathPattern($pattern, $bubbleControllerAndSubpackage, $formatIsOptional) {
434 $pattern = str_replace('@templateRoot', $this->getTemplateRootPath(), $pattern);
435 $pattern = str_replace('@partialRoot', $this->getPartialRootPath(), $pattern);
436 $pattern = str_replace('@layoutRoot', $this->getLayoutRootPath(), $pattern);
437 $subpackageKey = $this->controllerContext->getRequest()->getControllerSubpackageKey();
438 $controllerName = $this->controllerContext->getRequest()->getControllerName();
439 if ($subpackageKey !== NULL) {
440 if (strpos($subpackageKey, \TYPO3\CMS\Fluid\Fluid::NAMESPACE_SEPARATOR) !== FALSE) {
441 $namespaceSeparator = \TYPO3\CMS\Fluid\Fluid::NAMESPACE_SEPARATOR;
442 } else {
443 $namespaceSeparator = \TYPO3\CMS\Fluid\Fluid::LEGACY_NAMESPACE_SEPARATOR;
444 }
445 $subpackageParts = explode($namespaceSeparator, $subpackageKey);
446 } else {
447 $subpackageParts = array();
448 }
449 $results = array();
450 $i = $controllerName === NULL ? 0 : -1;
451 do {
452 $temporaryPattern = $pattern;
453 if ($i < 0) {
454 $temporaryPattern = str_replace('@controller', $controllerName, $temporaryPattern);
455 } else {
456 $temporaryPattern = str_replace('//', '/', str_replace('@controller', '', $temporaryPattern));
457 }
458 $temporaryPattern = str_replace('@subpackage', implode('/', $i < 0 ? $subpackageParts : array_slice($subpackageParts, $i)), $temporaryPattern);
459 $results[] = \TYPO3\CMS\Core\Utility\GeneralUtility::fixWindowsFilePath(str_replace('@format', $this->controllerContext->getRequest()->getFormat(), $temporaryPattern));
460 if ($formatIsOptional) {
461 $results[] = \TYPO3\CMS\Core\Utility\GeneralUtility::fixWindowsFilePath(str_replace('.@format', '', $temporaryPattern));
462 }
463 } while ($i++ < count($subpackageParts) && $bubbleControllerAndSubpackage);
464 return $results;
465 }
466
467 /**
468 * Returns a unique identifier for the given file in the format
469 * <PackageKey>_<SubPackageKey>_<ControllerName>_<prefix>_<SHA1>
470 * The SH1 hash is a checksum that is based on the file path and last modification date
471 *
472 * @param string $pathAndFilename
473 * @param string $prefix
474 * @return string
475 */
476 protected function createIdentifierForFile($pathAndFilename, $prefix) {
477 $request = $this->controllerContext->getRequest();
478 $extensionName = $request->getControllerExtensionName();
479 $subPackageKey = $request->getControllerSubpackageKey();
480 if ($subPackageKey !== NULL) {
481 $extensionName .= '_' . $subPackageKey;
482 }
483 $controllerName = $request->getControllerName();
484 $templateModifiedTimestamp = filemtime($pathAndFilename);
485 $templateIdentifier = sprintf('%s_%s_%s_%s', $extensionName, $controllerName, $prefix, sha1($pathAndFilename . '|' . $templateModifiedTimestamp));
486 return $templateIdentifier;
487 }
488
489 }
490
491
492 ?>