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