From 29f3d83f2e06f8f914cc74846cf0f6ec9aa68e45 Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Mon, 7 Nov 2022 22:40:17 +0100
Subject: [PATCH] [BUGFIX] Keep existing baseVariants for site languages
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The backend UI does not support handling of baseVariants
for languages, however it is possible to define them
in the config.yaml of a site configuration.

However, when editing and saving a site, the baseVariants
manually defined were removed. This has now been changed,
as the baseVariants are kept when persisting
the site configuratino via the UI.

Resolves: #93719
Releases: main, 11.5
Change-Id: I8a0b3be72ddfab46da59b3f8a69ad16121c7246b
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/76460
Tested-by: core-ci <typo3@b13.com>
Tested-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: Oliver Bartsch <bo@cedev.de>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
Reviewed-by: Benni Mack <benni@typo3.org>
---
 .../SiteConfigurationController.php           | 38 ++++++++++---
 .../SiteConfigurationControllerTest.php       | 53 +++++++++++++++++++
 2 files changed, 85 insertions(+), 6 deletions(-)

diff --git a/typo3/sysext/backend/Classes/Controller/SiteConfigurationController.php b/typo3/sysext/backend/Classes/Controller/SiteConfigurationController.php
index 9e150da61e1e..53f563c47058 100644
--- a/typo3/sysext/backend/Classes/Controller/SiteConfigurationController.php
+++ b/typo3/sysext/backend/Classes/Controller/SiteConfigurationController.php
@@ -390,12 +390,9 @@ class SiteConfigurationController
                 }
             }
 
-            // keep root config objects not given via GUI
-            // this way extension authors are able to use their own objects on root level
-            // that are not configurable via GUI
-            // however: we overwrite the full subset of any GUI object to make sure we have a clean state
-            $newSysSiteData = array_merge($currentSiteConfiguration, $newSysSiteData);
-            $newSiteConfiguration = $this->validateFullStructure($newSysSiteData);
+            $newSiteConfiguration = $this->validateFullStructure(
+                $this->getMergeSiteData($currentSiteConfiguration, $newSysSiteData)
+            );
 
             // Persist the configuration
             $siteConfigurationManager = GeneralUtility::makeInstance(SiteConfiguration::class);
@@ -808,6 +805,35 @@ class SiteConfigurationController
         return !empty($value) || $value === '0';
     }
 
+    /**
+     * Method keeps root config objects, which are not given via GUI. This way,
+     * extension authors are able to use their own objects on root level that are
+     * not configurable via GUI. However: We overwrite the full subset of any GUI
+     * object to make sure we have a clean state.
+     *
+     * Additionally, we also keep the baseVariants of languages, since they
+     * can't be modified via the GUI, but are part of the public API.
+     */
+    protected function getMergeSiteData(array $currentSiteConfiguration, array $newSysSiteData): array
+    {
+        $newSysSiteData = array_merge($currentSiteConfiguration, $newSysSiteData);
+
+        // @todo: this should go away, once base variants for languages are managable via the GUI.
+        $existingLanguageConfigurationsWithBaseVariants = [];
+        foreach ($currentSiteConfiguration['languages'] ?? [] as $languageConfiguration) {
+            if (isset($languageConfiguration['baseVariants'])) {
+                $existingLanguageConfigurationsWithBaseVariants[$languageConfiguration['languageId']] = $languageConfiguration['baseVariants'];
+            }
+        }
+        foreach ($newSysSiteData['languages'] ?? [] as $key => $languageConfiguration) {
+            if (isset($existingLanguageConfigurationsWithBaseVariants[$languageConfiguration['languageId']])) {
+                $newSysSiteData['languages'][$key]['baseVariants'] = $existingLanguageConfigurationsWithBaseVariants[$languageConfiguration['languageId']];
+            }
+        }
+
+        return $newSysSiteData;
+    }
+
     protected function getLanguageService(): LanguageService
     {
         return $GLOBALS['LANG'];
diff --git a/typo3/sysext/backend/Tests/Unit/Controller/SiteConfigurationControllerTest.php b/typo3/sysext/backend/Tests/Unit/Controller/SiteConfigurationControllerTest.php
index a653f9dfd4e8..25792f1eafb1 100644
--- a/typo3/sysext/backend/Tests/Unit/Controller/SiteConfigurationControllerTest.php
+++ b/typo3/sysext/backend/Tests/Unit/Controller/SiteConfigurationControllerTest.php
@@ -126,4 +126,57 @@ class SiteConfigurationControllerTest extends UnitTestCase
 
         self::assertEquals($expected, $mockedSiteConfigurationController->_call('getDuplicatedEntryPoints', $sites, $rootPages));
     }
+
+    /**
+     * @test
+     */
+    public function languageBaseVariantsAreKept(): void
+    {
+        $mockedSiteConfigurationController = $this->getAccessibleMock(SiteConfigurationController::class, ['dummy'], [], '', false);
+
+        $currentSiteConfig = [
+            'base' => '//domain1.tld/',
+            'websiteTitle' => 'domain1',
+            'languages' => [
+                0 => [
+                    'languageId' => 0,
+                    'title' => 'English',
+                    'base' => '/',
+                    'baseVariants' => [
+                        [
+                            'base' => '/en',
+                        ],
+                    ],
+                ],
+            ],
+        ];
+
+        $newSiteConfig = $currentSiteConfig;
+        $newSiteConfig['rootPageId'] = 123;
+        $newSiteConfig['websiteTitle'] = 'domain1 renamed';
+        unset($newSiteConfig['languages'][0]['baseVariants']);
+
+        $expected = [
+            'base' => '//domain1.tld/',
+            'rootPageId' => 123,
+            'websiteTitle' => 'domain1 renamed',
+            'languages' => [
+                0 => [
+                    'languageId' => 0,
+                    'title' => 'English',
+                    'base' => '/',
+                    'baseVariants' => [
+                        [
+                            'base' => '/en',
+                        ],
+                    ],
+                ],
+            ],
+        ];
+
+        self::assertEquals(
+            $expected,
+            $mockedSiteConfigurationController->_call('getMergeSiteData', $currentSiteConfig, $newSiteConfig)
+        );
+    }
 }
-- 
GitLab