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