From 2fc2326606e0d35467a89c917bb3b3b88acab3ef Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Mon, 7 Nov 2022 17:31:17 +0100
Subject: [PATCH] [BUGFIX] Resolve shortcut to a different page in a localized
 page

When a page translation of type=shortcut (doktype=4)
contains a different value than its original
page, the proper target URL is now resolved correctly
upon link creation and link resolving in TSFE.

This additionally fixes flaws in the behavior of first subpage in cases where the first subpage differs between languages.


Resolves: #98565
Resolves: #98566
Resolves: #87815
Releases: main, 12.4
Change-Id: If44033affc1cde1f59d5ccab97d75cffd01d2ad0
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/79751
Tested-by: core-ci <typo3@b13.com>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Benni Mack <benni@typo3.org>
---
 .../TypoScriptFrontendController.php          |  1 +
 .../Classes/Typolink/PageLinkBuilder.php      |  8 +++++
 .../SiteHandling/Fixtures/SlugScenario.yaml   |  2 ++
 .../SiteHandling/SlugLinkGeneratorTest.php    | 16 ++++++++-
 .../SiteHandling/SlugSiteRequestTest.php      | 36 +++++++++++++------
 5 files changed, 51 insertions(+), 12 deletions(-)

diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
index 09b3b0a3ae67..37e64b4acc16 100644
--- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
+++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
@@ -905,6 +905,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
             // about languages - whether we took the correct shortcut or
             // whether a translation of the page overwrites the shortcut
             // target and we need to follow the new target
+            $this->settingLanguage($request);
             $this->originalShortcutPage = $this->page;
             $this->page = $this->sys_page->resolveShortcutPage($this->page, true);
             $this->id = (int)$this->page['uid'];
diff --git a/typo3/sysext/frontend/Classes/Typolink/PageLinkBuilder.php b/typo3/sysext/frontend/Classes/Typolink/PageLinkBuilder.php
index d2c82fb3f2bd..01a5b27ef27a 100644
--- a/typo3/sysext/frontend/Classes/Typolink/PageLinkBuilder.php
+++ b/typo3/sysext/frontend/Classes/Typolink/PageLinkBuilder.php
@@ -408,6 +408,14 @@ class PageLinkBuilder extends AbstractTypolinkBuilder
         if (empty($page) || !is_array($page)) {
             return [];
         }
+
+        // If the page repository (= current page) does actually link to a different page
+        // It is needed to also resolve the page translation now, as it might have a different shortcut
+        // page
+        if (isset($configuration['language']) && $configuration['language'] !== 'current') {
+            $page = $pageRepository->getLanguageOverlay('pages', $page, new LanguageAspect($configuration['language'], $configuration['language']));
+        }
+
         $page = $this->resolveShortcutPage($page, $pageRepository, $disableGroupAccessCheck);
 
         $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'] ?? null;
diff --git a/typo3/sysext/frontend/Tests/Functional/SiteHandling/Fixtures/SlugScenario.yaml b/typo3/sysext/frontend/Tests/Functional/SiteHandling/Fixtures/SlugScenario.yaml
index bb23dd44db59..3e392b91f552 100644
--- a/typo3/sysext/frontend/Tests/Functional/SiteHandling/Fixtures/SlugScenario.yaml
+++ b/typo3/sysext/frontend/Tests/Functional/SiteHandling/Fixtures/SlugScenario.yaml
@@ -141,6 +141,8 @@ entities:
             - self: {id: 2021, title: 'FEGroups Restricted', visitorGroups: 30, slug: '/sysfolder-restricted'}
             - self: {id: 2022, title: 'FEGroups Restricted', hidden: 1, visitorGroups: 30, slug: '/sysfolder-restricted-hidden'}
         - self: {id: 2030, title: 'Cross Site Shortcut', slug: '/cross-site-shortcut', type: *pageShortcut, shortcut: 2100}
+          languageVariants:
+            - self: {id: 2031, title: 'Shortcut to Research - shows a different page', language: 1, type: *pageShortcut, shortcut: 1400, slug: '/other-cross-site-shortcut', l10n_state: '{"starttime":"parent","endtime":"parent","nav_hide":"parent","url":"parent","lastUpdated":"parent","newUntil":"parent","no_search":"parent","shortcut":"custom","shortcut_mode":"parent","content_from_pid":"parent","author":"parent","author_email":"parent","media":"parent","og_image":"parent","twitter_image":"parent"}' }
     - self: {id: 2000, title: 'ACME Blog', type: *pageShortcut, shortcut: 'first', root: true, slug: '/'}
       children:
         - self: {id: 2100, title: 'Authors', slug: '/authors'}
diff --git a/typo3/sysext/frontend/Tests/Functional/SiteHandling/SlugLinkGeneratorTest.php b/typo3/sysext/frontend/Tests/Functional/SiteHandling/SlugLinkGeneratorTest.php
index 93b0891ee784..16418efd09cb 100644
--- a/typo3/sysext/frontend/Tests/Functional/SiteHandling/SlugLinkGeneratorTest.php
+++ b/typo3/sysext/frontend/Tests/Functional/SiteHandling/SlugLinkGeneratorTest.php
@@ -830,6 +830,20 @@ final class SlugLinkGeneratorTest extends AbstractTestCase
                     ],
                 ],
             ],
+            'resolved shortcut in translation' => [
+                'https://acme.fr/',
+                1100,
+                '2030',
+                [
+                    [
+                        'title' => 'Shortcut to Research - shows a different page',
+                        'link' => '/acme-dans-votre-region',
+                        'active' => 0,
+                        'current' => 0,
+                    ],
+                ],
+            ],
+
         ];
     }
 
@@ -837,7 +851,7 @@ final class SlugLinkGeneratorTest extends AbstractTestCase
      * @test
      * @dataProvider hierarchicalMenuSetsActiveStateProperlyDataProvider
      */
-    public function hierarchicalMenuSetsActiveStateProperly(string $hostPrefix, int $sourcePageId, string $menuPageIds, array $expectation): void
+    public function hierarchicalMenuSetsActiveStateProperly(string $hostPrefix, int $sourcePageId, string $menuPageIds, array $expectation, int $languageId = 0): void
     {
         $response = $this->executeFrontendSubRequest(
             (new InternalRequest($hostPrefix))
diff --git a/typo3/sysext/frontend/Tests/Functional/SiteHandling/SlugSiteRequestTest.php b/typo3/sysext/frontend/Tests/Functional/SiteHandling/SlugSiteRequestTest.php
index 3770327c0b11..5bb44f007416 100644
--- a/typo3/sysext/frontend/Tests/Functional/SiteHandling/SlugSiteRequestTest.php
+++ b/typo3/sysext/frontend/Tests/Functional/SiteHandling/SlugSiteRequestTest.php
@@ -1220,16 +1220,22 @@ final class SlugSiteRequestTest extends AbstractTestCase
         );
     }
 
-    public static function crossSiteShortcutsAreRedirectedDataProvider(): array
+    public static function crossSiteShortcutsAreRedirectedDataProvider(): \Generator
     {
-        return [
-            'shortcut is redirected' => [
-                'https://website.local/cross-site-shortcut',
-                307,
-                [
-                    'X-Redirect-By' => ['TYPO3 Shortcut/Mountpoint'],
-                    'location' => ['https://blog.local/authors'],
-                ],
+        yield 'shortcut is redirected' => [
+            'https://website.local/cross-site-shortcut',
+            307,
+            [
+                'X-Redirect-By' => ['TYPO3 Shortcut/Mountpoint'],
+                'location' => ['https://blog.local/authors'],
+            ],
+        ];
+        yield 'shortcut of translated page is redirected to a different page than the original page' => [
+            'https://website.local/fr/other-cross-site-shortcut',
+            307,
+            [
+                'X-Redirect-By' => ['TYPO3 Shortcut/Mountpoint'],
+                'location' => ['https://website.local/fr/acme-dans-votre-region'],
             ],
         ];
     }
@@ -1242,11 +1248,19 @@ final class SlugSiteRequestTest extends AbstractTestCase
     {
         $this->writeSiteConfiguration(
             'website-local',
-            $this->buildSiteConfiguration(1000, 'https://website.local/')
+            $this->buildSiteConfiguration(1000, 'https://website.local/'),
+            [
+                $this->buildDefaultLanguageConfiguration('EN', '/'),
+                $this->buildLanguageConfiguration('FR', '/fr/', ['EN']),
+            ]
         );
         $this->writeSiteConfiguration(
             'blog-local',
-            $this->buildSiteConfiguration(2000, 'https://blog.local/')
+            $this->buildSiteConfiguration(2000, 'https://blog.local/'),
+            [
+                $this->buildDefaultLanguageConfiguration('EN', '/'),
+                $this->buildLanguageConfiguration('FR', '/fr/', ['EN']),
+            ]
         );
         $this->setUpFrontendRootPage(
             2000,
-- 
GitLab