[BUGFIX] Do not cache fallback View paths
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Classes / View / TemplatePaths.php
1 <?php
2 namespace TYPO3\CMS\Fluid\View;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Cache\CacheManager;
18 use TYPO3\CMS\Core\Cache\Frontend\VariableFrontend;
19 use TYPO3\CMS\Core\Utility\ArrayUtility;
20 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Core\Utility\PathUtility;
23 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
24 use TYPO3\CMS\Extbase\Object\ObjectManager;
25
26 /**
27 * Class TemplatePaths
28 *
29 * Custom implementation for template paths resolving, one which differs from the base
30 * implementation in that it is capable of resolving template paths based on TypoScript
31 * configuration when given a package name, and is aware of the Frontend/Backend contexts of TYPO3.
32 */
33 class TemplatePaths extends \TYPO3Fluid\Fluid\View\TemplatePaths
34 {
35 /**
36 * @var array
37 */
38 protected $typoScript = [];
39
40 /**
41 * @var string
42 */
43 protected $templateSource;
44
45 /**
46 * @var string
47 */
48 protected $templatePathAndFilename;
49
50 /**
51 * @param string $extensionKey
52 * @return string|NULL
53 */
54 protected function getExtensionPrivateResourcesPath($extensionKey)
55 {
56 $extensionKey = trim($extensionKey);
57 if ($extensionKey && ExtensionManagementUtility::isLoaded($extensionKey)) {
58 return ExtensionManagementUtility::extPath($extensionKey) . 'Resources/Private/';
59 }
60 return null;
61 }
62
63 /**
64 * @return ConfigurationManagerInterface
65 */
66 protected function getConfigurationManager()
67 {
68 $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
69 $configurationManager = $objectManager->get(ConfigurationManagerInterface::class);
70 return $configurationManager;
71 }
72
73 /**
74 * @param string $extensionKey
75 * @return array
76 */
77 protected function getContextSpecificViewConfiguration($extensionKey)
78 {
79 if (empty($extensionKey)) {
80 return [];
81 }
82 $cache = $this->getRuntimeCache();
83 $cacheIdentifier = 'viewpaths_' . $extensionKey;
84 $configuration = $cache->get($cacheIdentifier);
85 if (!empty($configuration)) {
86 return $configuration;
87 }
88
89 $resources = $this->getExtensionPrivateResourcesPath($extensionKey);
90 $paths = [
91 self::CONFIG_TEMPLATEROOTPATHS => [$resources . 'Templates/'],
92 self::CONFIG_PARTIALROOTPATHS => [$resources . 'Partials/'],
93 self::CONFIG_LAYOUTROOTPATHS => [$resources . 'Layouts/']
94 ];
95
96 $configuredPaths = [];
97 if (!empty($this->templateRootPaths) || !empty($this->partialRootPaths) || !empty($this->layoutRootPaths)) {
98 // The view was configured already
99 $configuredPaths = [
100 self::CONFIG_TEMPLATEROOTPATHS => $this->templateRootPaths,
101 self::CONFIG_PARTIALROOTPATHS => $this->partialRootPaths,
102 self::CONFIG_LAYOUTROOTPATHS => $this->layoutRootPaths,
103 ];
104 } else {
105 if (empty($this->typoScript)) {
106 $this->typoScript = GeneralUtility::removeDotsFromTS(
107 (array)$this->getConfigurationManager()->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT)
108 );
109 }
110 $signature = str_replace('_', '', $extensionKey);
111 if (TYPO3_MODE === 'BE' && isset($this->typoScript['module']['tx_' . $signature]['view'])) {
112 $configuredPaths = (array)$this->typoScript['module']['tx_' . $signature]['view'];
113 } elseif (TYPO3_MODE === 'FE' && isset($this->typoScript['plugin']['tx_' . $signature]['view'])) {
114 $configuredPaths = (array)$this->typoScript['plugin']['tx_' . $signature]['view'];
115 }
116 }
117
118 if (empty($configuredPaths)) {
119 return $paths;
120 }
121
122 foreach ($paths as $name => $defaultPaths) {
123 if (!empty($configuredPaths[$name])) {
124 $paths[$name] = $defaultPaths + (array)$configuredPaths[$name];
125 }
126 }
127
128 $cache->set($cacheIdentifier, $paths);
129
130 return $paths;
131 }
132
133 /**
134 * @return VariableFrontend
135 */
136 protected function getRuntimeCache()
137 {
138 return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
139 }
140
141 /**
142 * @param string|array $path
143 * @return string
144 */
145 protected function sanitizePath($path)
146 {
147 if (is_array($path)) {
148 $paths = array_map([$this, 'sanitizePath'], $path);
149 return array_unique($paths);
150 }
151 $path = $this->ensureAbsolutePath($path);
152 if (is_dir($path)) {
153 $path = $this->ensureSuffixedPath($path);
154 }
155 return $path;
156 }
157
158 /**
159 * Guarantees that $reference is turned into a
160 * correct, absolute path. The input can be a
161 * relative path or a FILE: or EXT: reference
162 * but cannot be a FAL resource identifier.
163 *
164 * @param mixed $reference
165 * @return string
166 */
167 protected function ensureAbsolutePath($reference)
168 {
169 if (false === is_array($reference)) {
170 $filename = PathUtility::isAbsolutePath($reference) ? $reference : GeneralUtility::getFileAbsFileName($reference);
171 } else {
172 foreach ($reference as &$subValue) {
173 $subValue = $this->ensureAbsolutePath($subValue);
174 }
175
176 return $reference;
177 }
178
179 return $filename;
180 }
181
182 /**
183 * Fills the path arrays with defaults, by package name.
184 * Reads those defaults from TypoScript if possible and
185 * if not defined, uses fallback paths by convention.
186 *
187 * @param string $packageName
188 * @return void
189 */
190 public function fillDefaultsByPackageName($packageName)
191 {
192 $this->fillFromConfigurationArray($this->getContextSpecificViewConfiguration($packageName));
193 }
194
195 /**
196 * Overridden setter with enforced sorting behavior
197 *
198 * @param array $templateRootPaths
199 * @return void
200 */
201 public function setTemplateRootPaths(array $templateRootPaths)
202 {
203 parent::setTemplateRootPaths(
204 ArrayUtility::sortArrayWithIntegerKeys($templateRootPaths)
205 );
206 }
207
208 /**
209 * Overridden setter with enforced sorting behavior
210 *
211 * @param array $layoutRootPaths
212 * @return void
213 */
214 public function setLayoutRootPaths(array $layoutRootPaths)
215 {
216 parent::setLayoutRootPaths(
217 ArrayUtility::sortArrayWithIntegerKeys($layoutRootPaths)
218 );
219 }
220
221 /**
222 * Overridden setter with enforced sorting behavior
223 *
224 * @param array $partialRootPaths
225 * @return void
226 */
227 public function setPartialRootPaths(array $partialRootPaths)
228 {
229 parent::setPartialRootPaths(
230 ArrayUtility::sortArrayWithIntegerKeys($partialRootPaths)
231 );
232 }
233
234 /**
235 * Public API for currently protected method. Can be dropped when switching to
236 * Fluid 1.1.0 or above.
237 *
238 * @param string $partialName
239 * @return string
240 */
241 public function getPartialPathAndFilename($partialName)
242 {
243 return parent::getPartialPathAndFilename($partialName);
244 }
245
246 /**
247 * Get absolute path to template file
248 *
249 * @return string Returns the absolute path to a Fluid template file
250 */
251 public function getTemplatePathAndFilename()
252 {
253 return $this->templatePathAndFilename;
254 }
255 }