[BUGFIX] Avoid double slashes in slug generation 03/58503/2
authorOliver Hader <oliver@typo3.org>
Sun, 30 Sep 2018 15:34:58 +0000 (17:34 +0200)
committerWouter Wolters <typo3@wouterwolters.nl>
Sun, 30 Sep 2018 16:09:43 +0000 (18:09 +0200)
When generating slugs it might happen, that double slashes
are generated. This basically results from both prepending
and appending slashes at the same time.

Resolves: #86479
Releases: master
Change-Id: I441fee333cd0b3fbad3dde1a130eb08356bd4c2d
Reviewed-on: https://review.typo3.org/58503
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
typo3/sysext/core/Classes/DataHandling/SlugHelper.php
typo3/sysext/core/Tests/Unit/DataHandling/SlugHelperTest.php

index 6b313a7..397063a 100644 (file)
@@ -128,7 +128,7 @@ class SlugHelper
         // Remove trailing and beginning slashes, except if the trailing slash was added, then we'll re-add it
         $appendTrailingSlash = $extractedSlug !== '' && substr($slug, -1) === '/';
         $slug = $extractedSlug . ($appendTrailingSlash ? '/' : '');
-        if ($this->prependSlashInSlug) {
+        if ($this->prependSlashInSlug && ($slug{0} ?? '') !== '/') {
             $slug = '/' . $slug;
         }
         return $slug;
@@ -162,8 +162,9 @@ class SlugHelper
         }
         $prefix = '';
         if ($this->configuration['generatorOptions']['prefixParentPageSlug'] ?? false) {
-            $languageId = (int)$recordData[$GLOBALS['TCA'][$this->tableName]['ctrl']['languageField']];
-            $rootLine = BackendUtility::BEgetRootLine($pid, '', true, ['nav_title']);
+            $languageFieldName = $GLOBALS['TCA'][$this->tableName]['ctrl']['languageField'] ?? null;
+            $languageId = (int)($recordData[$languageFieldName] ?? 0);
+            $rootLine = $this->resolveRootLine($pid);
             $parentPageRecord = reset($rootLine);
             if ($languageId > 0) {
                 $localizedParentPageRecord = BackendUtility::getRecordLocalization('pages', $parentPageRecord['uid'], $languageId);
@@ -198,7 +199,7 @@ class SlugHelper
         if ($slug === '' || $slug === '/') {
             $slug = 'default-' . GeneralUtility::shortMD5(json_encode($recordData));
         }
-        if ($this->prependSlashInSlug) {
+        if ($this->prependSlashInSlug && ($slug{0} ?? '') !== '/') {
             $slug = '/' . $slug;
         }
         if (!empty($prefix)) {
@@ -532,4 +533,13 @@ class SlugHelper
             )
         );
     }
+
+    /**
+     * @param int $pid
+     * @return array
+     */
+    protected function resolveRootLine(int $pid): array
+    {
+        return BackendUtility::BEgetRootLine($pid, '', true, ['nav_title']);
+    }
 }
index b2c6635..e72b4d6 100644 (file)
@@ -344,4 +344,69 @@ class SlugHelperTest extends UnitTestCase
             $subject->generate(['title' => $input, 'uid' => 13], 13)
         );
     }
+
+    /**
+     * @return array
+     */
+    public function generatePrependsSlugsForPagesDataProvider(): array
+    {
+        return [
+            'simple title' => [
+                'Products',
+                '/parent-page/products'
+            ],
+            'title with spaces' => [
+                'Product Cow',
+                '/parent-page/product-cow'
+            ],
+            'title with invalid characters' => [
+                'Products - Cows',
+                '/parent-page/products-cows'
+            ],
+            'title with only invalid characters' => [
+                '!!!',
+                '/parent-page/default-51cf35392c'
+            ],
+        ];
+    }
+
+    /**
+     * @dataProvider generatePrependsSlugsForPagesDataProvider
+     * @param string $input
+     * @param string $expected
+     * @test
+     */
+    public function generatePrependsSlugsForPages(string $input, string $expected)
+    {
+        $GLOBALS['dummyTable']['ctrl'] = [];
+        $rootLine = [
+            [
+                'uid' => '13',
+                'pid' => '10',
+                'title' => 'Parent Page',
+            ]
+        ];
+        $subject = $this->getAccessibleMock(
+            SlugHelper::class,
+            ['resolveRootLine'],
+            [
+                'pages',
+                'slug',
+                [
+                    'generatorOptions' => [
+                        'fields' => ['title'],
+                        'prefixParentPageSlug' => true,
+                    ],
+                ]
+            ]
+        );
+        $subject->expects(static::at(0))
+            ->method('resolveRootLine')->with(13)->willReturn($rootLine);
+        $subject->expects(static::at(1))
+            ->method('resolveRootLine')->with(10)->willReturn([]);
+        static::assertEquals(
+            $expected,
+            $subject->generate(['title' => $input, 'uid' => 13], 13)
+        );
+    }
 }