[BUGFIX] Make TemplatePaths->fillDefaultsByPackageName sort paths 38/52938/3
authorClaus Due <claus@namelesscoder.net>
Wed, 24 May 2017 15:41:14 +0000 (17:41 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Sun, 4 Jun 2017 09:38:02 +0000 (11:38 +0200)
Due to the sequence of methods being called from this method,
it is necessary to sort TS-configured paths right when they are
resolved instead of passing the unsorted array further down.

Patch also sorts methods protected below public; true diff is
one line. Toggle this line between before/after and run the test
to see the problem demonstrated.

See issue description for a more detailed reasoning.

Change-Id: I0317c4ee683c2fac2c7def7c6d79f947afc4b078
Resolves: #81340
Releases: master, 8.7
Reviewed-on: https://review.typo3.org/52938
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Markus Sommer <markussom@posteo.de>
Tested-by: Markus Sommer <markussom@posteo.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/fluid/Classes/View/TemplatePaths.php
typo3/sysext/fluid/Tests/Unit/View/TemplatePathsTest.php

index ebd8d46..3dd19a1 100644 (file)
@@ -108,9 +108,9 @@ class TemplatePaths extends \TYPO3Fluid\Fluid\View\TemplatePaths
                 );
             }
             $signature = str_replace('_', '', $extensionKey);
-            if (TYPO3_MODE === 'BE' && isset($this->typoScript['module']['tx_' . $signature]['view'])) {
+            if ($this->isBackendMode() && isset($this->typoScript['module']['tx_' . $signature]['view'])) {
                 $configuredPaths = (array)$this->typoScript['module']['tx_' . $signature]['view'];
-            } elseif (TYPO3_MODE === 'FE' && isset($this->typoScript['plugin']['tx_' . $signature]['view'])) {
+            } elseif (isset($this->typoScript['plugin']['tx_' . $signature]['view'])) {
                 $configuredPaths = (array)$this->typoScript['plugin']['tx_' . $signature]['view'];
             }
         }
@@ -121,7 +121,7 @@ class TemplatePaths extends \TYPO3Fluid\Fluid\View\TemplatePaths
 
         foreach ($paths as $name => $defaultPaths) {
             if (!empty($configuredPaths[$name])) {
-                $paths[$name] = array_merge($defaultPaths, (array)$configuredPaths[$name]);
+                $paths[$name] = array_merge($defaultPaths, ArrayUtility::sortArrayWithIntegerKeys((array)$configuredPaths[$name]));
             }
         }
 
@@ -131,38 +131,6 @@ class TemplatePaths extends \TYPO3Fluid\Fluid\View\TemplatePaths
     }
 
     /**
-     * @return VariableFrontend
-     */
-    protected function getRuntimeCache()
-    {
-        return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
-    }
-
-    /**
-     * Guarantees that $reference is turned into a
-     * correct, absolute path. The input can be a
-     * relative path or a FILE: or EXT: reference
-     * but cannot be a FAL resource identifier.
-     *
-     * @param mixed $reference
-     * @return string
-     */
-    protected function ensureAbsolutePath($reference)
-    {
-        if (false === is_array($reference)) {
-            $filename = PathUtility::isAbsolutePath($reference) ? $reference : GeneralUtility::getFileAbsFileName($reference);
-        } else {
-            foreach ($reference as &$subValue) {
-                $subValue = $this->ensureAbsolutePath($subValue);
-            }
-
-            return $reference;
-        }
-
-        return $filename;
-    }
-
-    /**
      * Fills the path arrays with defaults, by package name.
      * Reads those defaults from TypoScript if possible and
      * if not defined, uses fallback paths by convention.
@@ -231,4 +199,40 @@ class TemplatePaths extends \TYPO3Fluid\Fluid\View\TemplatePaths
     {
         return $this->templatePathAndFilename;
     }
+
+    /**
+     * Guarantees that $reference is turned into a
+     * correct, absolute path. The input can be a
+     * relative path or a FILE: or EXT: reference
+     * but cannot be a FAL resource identifier.
+     *
+     * @param mixed $reference
+     * @return string
+     */
+    protected function ensureAbsolutePath($reference)
+    {
+        if (!is_array($reference)) {
+            return PathUtility::isAbsolutePath($reference) ? $reference : GeneralUtility::getFileAbsFileName($reference);
+        }
+        foreach ($reference as &$subValue) {
+            $subValue = $this->ensureAbsolutePath($subValue);
+        }
+        return $reference;
+    }
+
+    /**
+     * @return bool
+     */
+    protected function isBackendMode()
+    {
+        return TYPO3_MODE === 'BE';
+    }
+
+    /**
+     * @return VariableFrontend
+     */
+    protected function getRuntimeCache()
+    {
+        return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
+    }
 }
index f6a598a..5c11046 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Fluid\Tests\Unit\View;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Cache\Frontend\VariableFrontend;
+use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
 use TYPO3\CMS\Fluid\View\TemplatePaths;
 
 /**
@@ -98,4 +100,64 @@ class TemplatePathsTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
         $subject->$setter($paths);
         $this->assertAttributeSame($expected, $method, $subject);
     }
+
+    /**
+     * @test
+     */
+    public function getContextSpecificViewConfigurationSortsTypoScriptConfiguredPathsCorrectly()
+    {
+        $configurationManager = $this->getMockBuilder(ConfigurationManagerInterface::class)->getMockForAbstractClass();
+        $configurationManager->expects($this->once())->method('getConfiguration')->willReturn([
+            'plugin.' => [
+                'tx_test.' => [
+                    'view.' => [
+                        'templateRootPaths.' => [
+                            '30' => 'third',
+                            '10' => 'first',
+                            '20' => 'second'
+                        ],
+                        'partialRootPaths.' => [
+                            '20' => '2',
+                            '30' => '3',
+                            '10' => '1'
+                        ],
+                        'layoutRootPaths.' => [
+                            '130' => '3.',
+                            '10' => '1.',
+                            '120' => '2.'
+                        ],
+                    ]
+                ]
+            ]
+        ]);
+        $cache = $this->getMockBuilder(VariableFrontend::class)->setMethods(['get', 'set'])->disableOriginalConstructor()->getMock();
+        $cache->expects($this->once())->method('get')->willReturn(false);
+        $cache->expects($this->once())->method('set');
+        $subject = $this->getMockBuilder(TemplatePaths::class)->setMethods(['getConfigurationManager', 'getExtensionPrivateResourcesPath', 'getRuntimeCache', 'isBackendMode'])->getMock();
+        $subject->expects($this->once())->method('getExtensionPrivateResourcesPath')->with('test')->willReturn('test/');
+        $subject->expects($this->once())->method('getConfigurationManager')->willReturn($configurationManager);
+        $subject->expects($this->once())->method('getRuntimeCache')->willReturn($cache);
+        $subject->expects($this->once())->method('isBackendMode')->willReturn(false);
+        $result = $this->callInaccessibleMethod($subject, 'getContextSpecificViewConfiguration', 'test');
+        $this->assertSame([
+            'templateRootPaths' => [
+                'test/Templates/',
+                'first',
+                'second',
+                'third'
+            ],
+            'partialRootPaths' => [
+                'test/Partials/',
+                '1',
+                '2',
+                '3'
+            ],
+            'layoutRootPaths' => [
+                'test/Layouts/',
+                '1.',
+                '2.',
+                '3.'
+            ]
+        ], $result);
+    }
 }