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