ffeb00f406b928c761125a69de2f7bc043957123
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Classes / View / StandaloneView.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 * *
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 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Extbase\Utility\ArrayUtility;
25 use TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException;
26 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
27
28 /**
29 * A standalone template view.
30 * Should be used as view if you want to use Fluid without Extbase extensions
31 *
32 * @api
33 */
34 class StandaloneView extends AbstractTemplateView {
35
36 /**
37 * Source code of the Fluid template
38 *
39 * @var string
40 */
41 protected $templateSource = NULL;
42
43 /**
44 * absolute path of the Fluid template
45 *
46 * @var string
47 */
48 protected $templatePathAndFilename = NULL;
49
50 /**
51 * Path(s) to the partial root
52 *
53 * @var array
54 */
55 protected $partialRootPaths = NULL;
56
57 /**
58 * Path(s) to the layout root
59 *
60 * @var array
61 */
62 protected $layoutRootPaths = NULL;
63
64 /**
65 * Constructor
66 *
67 * @param ContentObjectRenderer $contentObject The current cObject. If NULL a new instance will be created
68 * @throws \InvalidArgumentException
69 * @throws \UnexpectedValueException
70 */
71 public function __construct(ContentObjectRenderer $contentObject = NULL) {
72 $this->objectManager = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
73 /** @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager */
74 $configurationManager = $this->objectManager->get(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::class);
75 if ($contentObject === NULL) {
76 /** @var ContentObjectRenderer $contentObject */
77 $contentObject = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class);
78 }
79 $configurationManager->setContentObject($contentObject);
80 $this->templateParser = $this->objectManager->get(\TYPO3\CMS\Fluid\Core\Parser\TemplateParser::class);
81 $this->setRenderingContext($this->objectManager->get(\TYPO3\CMS\Fluid\Core\Rendering\RenderingContext::class));
82 /** @var \TYPO3\CMS\Extbase\Mvc\Web\Request $request */
83 $request = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Request::class);
84 $request->setRequestURI(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'));
85 $request->setBaseURI(GeneralUtility::getIndpEnv('TYPO3_SITE_URL'));
86 /** @var \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder $uriBuilder */
87 $uriBuilder = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder::class);
88 $uriBuilder->setRequest($request);
89 /** @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext */
90 $controllerContext = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext::class);
91 $controllerContext->setRequest($request);
92 $controllerContext->setUriBuilder($uriBuilder);
93 $this->setControllerContext($controllerContext);
94 $this->templateCompiler = $this->objectManager->get(\TYPO3\CMS\Fluid\Core\Compiler\TemplateCompiler::class);
95 // singleton
96 $this->templateCompiler->setTemplateCache(GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('fluid_template'));
97 }
98
99 /**
100 * Sets the format of the current request (default format is "html")
101 *
102 * @param string $format
103 * @return void
104 * @api
105 */
106 public function setFormat($format) {
107 $this->getRequest()->setFormat($format);
108 }
109
110 /**
111 * Returns the format of the current request (defaults is "html")
112 *
113 * @return string $format
114 * @api
115 */
116 public function getFormat() {
117 return $this->getRequest()->getFormat();
118 }
119
120 /**
121 * Returns the current request object
122 *
123 * @return \TYPO3\CMS\Extbase\Mvc\Web\Request
124 */
125 public function getRequest() {
126 return $this->controllerContext->getRequest();
127 }
128
129 /**
130 * Sets the absolute path to a Fluid template file
131 *
132 * @param string $templatePathAndFilename Fluid template path
133 * @return void
134 * @api
135 */
136 public function setTemplatePathAndFilename($templatePathAndFilename) {
137 $this->templatePathAndFilename = $templatePathAndFilename;
138 }
139
140 /**
141 * Returns the absolute path to a Fluid template file if it was specified with setTemplatePathAndFilename() before
142 *
143 * @return string Fluid template path
144 * @api
145 */
146 public function getTemplatePathAndFilename() {
147 return $this->templatePathAndFilename;
148 }
149
150 /**
151 * Sets the Fluid template source
152 * You can use setTemplatePathAndFilename() alternatively if you only want to specify the template path
153 *
154 * @param string $templateSource Fluid template source code
155 * @return void
156 * @api
157 */
158 public function setTemplateSource($templateSource) {
159 $this->templateSource = $templateSource;
160 }
161
162 /**
163 * Set the root path to the layouts.
164 *
165 * @param string $layoutRootPath Root path to the layouts.
166 * @return void
167 * @api
168 * @see setLayoutRootPaths()
169 * @deprecated since Fluid 7; Use setLayoutRootPaths() instead
170 */
171 public function setLayoutRootPath($layoutRootPath) {
172 GeneralUtility::logDeprecatedFunction();
173 $this->setLayoutRootPaths(array($layoutRootPath));
174 }
175
176 /**
177 * Set the root path(s) to the layouts.
178 *
179 * @param array $layoutRootPaths Root path to the layouts
180 * @return void
181 * @api
182 */
183 public function setLayoutRootPaths(array $layoutRootPaths) {
184 $this->layoutRootPaths = $layoutRootPaths;
185 }
186
187 /**
188 * Returns the first found entry in $this->layoutRootPaths.
189 * Don't use, this might not be the desired result.
190 *
191 * @throws InvalidTemplateResourceException
192 * @return string Path to layout root directory
193 * @deprecated since Fluid 7; Use getLayoutRootPaths() instead
194 */
195 public function getLayoutRootPath() {
196 GeneralUtility::logDeprecatedFunction();
197 $layoutRootPaths = $this->getLayoutRootPaths();
198 return array_shift($layoutRootPaths);
199 }
200
201 /**
202 * Resolves the layout root to be used inside other paths.
203 *
204 * @return string Fluid layout root path
205 * @throws InvalidTemplateResourceException
206 * @api
207 */
208 public function getLayoutRootPaths() {
209 if ($this->layoutRootPaths === NULL && $this->templatePathAndFilename === NULL) {
210 throw new InvalidTemplateResourceException('No layout root path has been specified. Use setLayoutRootPaths().', 1288091419);
211 }
212 if ($this->layoutRootPaths === NULL) {
213 $this->layoutRootPaths = array(dirname($this->templatePathAndFilename) . '/Layouts');
214 }
215 return $this->layoutRootPaths;
216 }
217
218 /**
219 * Set the root path to the partials.
220 * If set, overrides the one determined from $this->partialRootPathPattern
221 *
222 * @param string $partialRootPath Root path to the partials. If set, overrides the one determined from $this->partialRootPathPattern
223 * @return void
224 * @api
225 * @see setPartialRootPaths()
226 * @deprecated since Fluid 7; Use setPartialRootPaths() instead
227 */
228 public function setPartialRootPath($partialRootPath) {
229 GeneralUtility::logDeprecatedFunction();
230 $this->setPartialRootPaths(array($partialRootPath));
231 }
232
233 /**
234 * Returns the first found entry in $this->partialRootPaths
235 * Don't use, this might not be the desired result.
236 *
237 * @throws InvalidTemplateResourceException
238 * @return string Path to partial root directory
239 * @deprecated since Fluid 7; Use getPartialRootPaths() instead
240 */
241 public function getPartialRootPath() {
242 GeneralUtility::logDeprecatedFunction();
243 $partialRootPaths = $this->getPartialRootPaths();
244 return array_shift($partialRootPaths);
245 }
246
247 /**
248 * Set the root path(s) to the partials.
249 * If set, overrides the one determined from $this->partialRootPathPattern
250 *
251 * @param array $partialRootPaths Root paths to the partials. If set, overrides the one determined from $this->partialRootPathPattern
252 * @return void
253 * @api
254 */
255 public function setPartialRootPaths(array $partialRootPaths) {
256 $this->partialRootPaths = $partialRootPaths;
257 }
258
259 /**
260 * Returns the absolute path to the folder that contains Fluid partial files
261 *
262 * @return string Fluid partial root path
263 * @throws InvalidTemplateResourceException
264 * @api
265 */
266 public function getPartialRootPaths() {
267 if ($this->partialRootPaths === NULL && $this->templatePathAndFilename === NULL) {
268 throw new InvalidTemplateResourceException('No partial root path has been specified. Use setPartialRootPaths().', 1288094511);
269 }
270 if ($this->partialRootPaths === NULL) {
271 $this->partialRootPaths = array(dirname($this->templatePathAndFilename) . '/Partials');
272 }
273 return $this->partialRootPaths;
274 }
275
276 /**
277 * Checks whether a template can be resolved for the current request
278 *
279 * @return bool
280 * @api
281 */
282 public function hasTemplate() {
283 try {
284 $this->getTemplateSource();
285 return TRUE;
286 } catch (InvalidTemplateResourceException $e) {
287 return FALSE;
288 }
289 }
290
291 /**
292 * Returns a unique identifier for the resolved template file
293 * This identifier is based on the template path and last modification date
294 *
295 * @param string $actionName Name of the action. This argument is not used in this view!
296 * @return string template identifier
297 * @throws InvalidTemplateResourceException
298 */
299 protected function getTemplateIdentifier($actionName = NULL) {
300 if ($this->templateSource === NULL) {
301 $templatePathAndFilename = $this->getTemplatePathAndFilename();
302 $templatePathAndFilenameInfo = pathinfo($templatePathAndFilename);
303 $templateFilenameWithoutExtension = basename($templatePathAndFilename, '.' . $templatePathAndFilenameInfo['extension']);
304 $prefix = sprintf('template_file_%s', $templateFilenameWithoutExtension);
305 return $this->createIdentifierForFile($templatePathAndFilename, $prefix);
306 } else {
307 $templateSource = $this->getTemplateSource();
308 $prefix = 'template_source';
309 $templateIdentifier = sprintf('Standalone_%s_%s', $prefix, sha1($templateSource));
310 return $templateIdentifier;
311 }
312 }
313
314 /**
315 * Returns the Fluid template source code
316 *
317 * @param string $actionName Name of the action. This argument is not used in this view!
318 * @return string Fluid template source
319 * @throws InvalidTemplateResourceException
320 */
321 protected function getTemplateSource($actionName = NULL) {
322 if ($this->templateSource === NULL && $this->templatePathAndFilename === NULL) {
323 throw new InvalidTemplateResourceException('No template has been specified. Use either setTemplateSource() or setTemplatePathAndFilename().', 1288085266);
324 }
325 if ($this->templateSource === NULL) {
326 if (!is_file($this->templatePathAndFilename)) {
327 throw new InvalidTemplateResourceException('Template could not be found at "' . $this->templatePathAndFilename . '".', 1288087061);
328 }
329 $this->templateSource = file_get_contents($this->templatePathAndFilename);
330 }
331 return $this->templateSource;
332 }
333
334 /**
335 * Returns a unique identifier for the resolved layout file.
336 * This identifier is based on the template path and last modification date
337 *
338 * @param string $layoutName The name of the layout
339 * @return string layout identifier
340 * @throws InvalidTemplateResourceException
341 */
342 protected function getLayoutIdentifier($layoutName = 'Default') {
343 $layoutPathAndFilename = $this->getLayoutPathAndFilename($layoutName);
344 $prefix = 'layout_' . $layoutName;
345 return $this->createIdentifierForFile($layoutPathAndFilename, $prefix);
346 }
347
348 /**
349 * Resolves the path and file name of the layout file, based on
350 * $this->getLayoutRootPaths() and request format and returns the file contents
351 *
352 * @param string $layoutName Name of the layout to use. If none given, use "Default"
353 * @return string contents of the layout file if it was found
354 * @throws InvalidTemplateResourceException
355 */
356 protected function getLayoutSource($layoutName = 'Default') {
357 $layoutPathAndFilename = $this->getLayoutPathAndFilename($layoutName);
358 $layoutSource = file_get_contents($layoutPathAndFilename);
359 if ($layoutSource === FALSE) {
360 throw new InvalidTemplateResourceException('"' . $layoutPathAndFilename . '" is not a valid template resource URI.', 1312215888);
361 }
362 return $layoutSource;
363 }
364
365 /**
366 * Resolve the path and file name of the layout file, based on
367 * $this->getLayoutRootPaths() and request format
368 *
369 * In case a layout has already been set with setLayoutPathAndFilename(),
370 * this method returns that path, otherwise a path and filename will be
371 * resolved using the layoutPathAndFilenamePattern.
372 *
373 * @param string $layoutName Name of the layout to use. If none given, use "Default"
374 * @return string Path and filename of layout files
375 * @throws InvalidTemplateResourceException
376 */
377 protected function getLayoutPathAndFilename($layoutName = 'Default') {
378 $layoutName = ucfirst($layoutName);
379 $possibleLayoutPaths = array();
380 $paths = ArrayUtility::sortArrayWithIntegerKeys($this->getLayoutRootPaths());
381 $paths = array_reverse($paths, TRUE);
382 foreach ($paths as $layoutRootPath) {
383 $possibleLayoutPaths[] = GeneralUtility::fixWindowsFilePath($layoutRootPath . '/' . $layoutName . '.' . $this->getRequest()->getFormat());
384 $possibleLayoutPaths[] = GeneralUtility::fixWindowsFilePath($layoutRootPath . '/' . $layoutName);
385 }
386 foreach ($possibleLayoutPaths as $layoutPathAndFilename) {
387 if ($this->testFileExistence($layoutPathAndFilename)) {
388 return $layoutPathAndFilename;
389 }
390 }
391
392 throw new InvalidTemplateResourceException('Could not load layout file. Tried following paths: "' . implode('", "', $possibleLayoutPaths) . '".', 1288092555);
393 }
394
395 /**
396 * Wrapper method for is_file function for testing reasons
397 *
398 * @param string $filePath
399 * @return bool
400 */
401 protected function testFileExistence($filePath) {
402 return is_file($filePath);
403 }
404
405 /**
406 * Returns a unique identifier for the resolved partial file.
407 * This identifier is based on the template path and last modification date
408 *
409 * @param string $partialName The name of the partial
410 * @return string partial identifier
411 * @throws InvalidTemplateResourceException
412 */
413 protected function getPartialIdentifier($partialName) {
414 $partialPathAndFilename = $this->getPartialPathAndFilename($partialName);
415 $prefix = 'partial_' . $partialName;
416 return $this->createIdentifierForFile($partialPathAndFilename, $prefix);
417 }
418
419 /**
420 * Resolves the path and file name of the partial file, based on
421 * $this->getPartialRootPath() and request format and returns the file contents
422 *
423 * @param string $partialName The name of the partial
424 * @return string contents of the layout file if it was found
425 * @throws InvalidTemplateResourceException
426 */
427 protected function getPartialSource($partialName) {
428 $partialPathAndFilename = $this->getPartialPathAndFilename($partialName);
429 $partialSource = file_get_contents($partialPathAndFilename);
430 if ($partialSource === FALSE) {
431 throw new InvalidTemplateResourceException('"' . $partialPathAndFilename . '" is not a valid template resource URI.', 1257246932);
432 }
433 return $partialSource;
434 }
435
436 /**
437 * Resolve the partial path and filename based on $this->getPartialRootPaths() and request format
438 *
439 * @param string $partialName The name of the partial
440 * @return string The full path which should be used. The path definitely exists.
441 * @throws InvalidTemplateResourceException
442 */
443 protected function getPartialPathAndFilename($partialName) {
444 $partialName = ucfirst($partialName);
445 $paths = ArrayUtility::sortArrayWithIntegerKeys($this->getPartialRootPaths());
446 $paths = array_reverse($paths, TRUE);
447 $possiblePartialPaths = array();
448 foreach ($paths as $partialRootPath) {
449 $possiblePartialPaths[] = GeneralUtility::fixWindowsFilePath($partialRootPath . '/' . $partialName . '.' . $this->getRequest()->getFormat());
450 $possiblePartialPaths[] = GeneralUtility::fixWindowsFilePath($partialRootPath . '/' . $partialName);
451 }
452 foreach ($possiblePartialPaths as $partialPathAndFilename) {
453 if ($this->testFileExistence($partialPathAndFilename)) {
454 return $partialPathAndFilename;
455 }
456 }
457 throw new InvalidTemplateResourceException('Could not load partial file. Tried following paths: "' . implode('", "', $possiblePartialPaths) . '".', 1288092556);
458 }
459
460 /**
461 * Returns a unique identifier for the given file in the format
462 * Standalone_<prefix>_<SHA1>
463 * The SH1 hash is a checksum that is based on the file path and last modification date
464 *
465 * @param string $pathAndFilename
466 * @param string $prefix
467 * @return string
468 */
469 protected function createIdentifierForFile($pathAndFilename, $prefix) {
470 $templateModifiedTimestamp = filemtime($pathAndFilename);
471 $templateIdentifier = sprintf('Standalone_%s_%s', $prefix, sha1($pathAndFilename . '|' . $templateModifiedTimestamp));
472 $templateIdentifier = str_replace('/', '_', str_replace('.', '_', $templateIdentifier));
473 return $templateIdentifier;
474 }
475 }