From 141d185ea970157dcc1b42fd06476294250e4616 Mon Sep 17 00:00:00 2001
From: Christian Kuhn <lolli@schwarzbu.ch>
Date: Sat, 15 Apr 2023 10:58:36 +0200
Subject: [PATCH] [BUGFIX] Extbase BackendConfigurationManager fakes
 sys_template
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The TypoScript parser has to load TypoScript from globals
($GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_')
and TypoScript from extension static files
(ext_typoscript_*.typoscript) at some point.

It usually adds this before the first "root" sys_template
row. This is fine in frontend.

Extbase backend modules rely on frontend TypoScript for
configuration as well. This decision from the dark ages
hurts us plenty, but is very hard to get rid of.

The extbase BackendConfigurationManager does a lot of
guesswork to calculate such frontend TypoScript: There
are various special cases, especially when an extbase
backend module is not loaded within page context.

The ext:form extension triggers such a special case:
When there is only a single page marked as "is_siteroot"
without a sys_template record, configuration from
globals is not included. The configuration is then
incomplete and the module fails.

The old TypoScript parser had a dedicated hack and
toggle for this, which forced it to still include
global configuration. The new TypoScript parser
does not contain this hack. Instead, the extbase
BackendConfigurationManager "fakes" a sys_template
row to trigger the global inclusion. This works if
there is no "is_siteroot" page at all, but is not
triggered when a page could be determined.

The patch slightly changes the extbase logic to also
add the fake row if a page could be determined, but
no sys_template record exists.

Additionally, the ext:form backend controllers now
throw a dedicated exception if configuration is
incomplete.

Resolves: #99458
Related: #97816
Releases: main
Change-Id: Ie48cd5c97704c9c4a0ad7a5e555f49f181f88006
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/78650
Tested-by: Stefan Bürk <stefan@buerk.tech>
Reviewed-by: Kevin Appelt <kevin.appelt@icloud.com>
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: Kevin Appelt <kevin.appelt@icloud.com>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
---
 .../BackendConfigurationManager.php           | 42 ++++++++++---------
 .../Controller/AbstractBackendController.php  |  9 +++-
 2 files changed, 30 insertions(+), 21 deletions(-)

diff --git a/typo3/sysext/extbase/Classes/Configuration/BackendConfigurationManager.php b/typo3/sysext/extbase/Classes/Configuration/BackendConfigurationManager.php
index 8dcaac1a1ea6..7a24fcf32d8a 100644
--- a/typo3/sysext/extbase/Classes/Configuration/BackendConfigurationManager.php
+++ b/typo3/sysext/extbase/Classes/Configuration/BackendConfigurationManager.php
@@ -215,29 +215,33 @@ class BackendConfigurationManager implements SingletonInterface
         $site = $request?->getAttribute('site');
 
         $rootLine = [];
+        $sysTemplateFakeRow = [
+            'uid' => 0,
+            'pid' => 0,
+            'title' => 'Fake sys_template row to force extension statics loading',
+            'root' => 1,
+            'clear' => 3,
+            'include_static_file' => '',
+            'basedOn' => '',
+            'includeStaticAfterBasedOn' => 0,
+            'static_file_mode' => false,
+            'constants' => '',
+            'config' => '',
+            'deleted' => 0,
+            'hidden' => 0,
+            'starttime' => 0,
+            'endtime' => 0,
+            'sorting' => 0,
+        ];
         if ($pageId > 0) {
             $rootLine = GeneralUtility::makeInstance(RootlineUtility::class, $pageId)->get();
             $sysTemplateRows = $this->sysTemplateRepository->getSysTemplateRowsByRootline($rootLine, $request);
             ksort($rootLine);
-        } else {
-            $sysTemplateRows[] = [
-                'uid' => 0,
-                'pid' => 0,
-                'title' => 'Fake sys_template row to force extension statics loading',
-                'root' => 1,
-                'clear' => 3,
-                'include_static_file' => '',
-                'basedOn' => '',
-                'includeStaticAfterBasedOn' => 0,
-                'static_file_mode' => false,
-                'constants' => '',
-                'config' => '',
-                'deleted' => 0,
-                'hidden' => 0,
-                'starttime' => 0,
-                'endtime' => 0,
-                'sorting' => 0,
-            ];
+        }
+        if (empty($sysTemplateRows)) {
+            // If there is no page (pid 0 only), or if the first 'is_siteroot' site has no sys_template record,
+            // then we "fake" a sys_template row: This triggers inclusion of 'global' and 'extension static' TypoScript.
+            $sysTemplateRows[] = $sysTemplateFakeRow;
         }
 
         // We do cache tree and tokens, but don't cache full ast in this backend context for now:
diff --git a/typo3/sysext/form/Classes/Controller/AbstractBackendController.php b/typo3/sysext/form/Classes/Controller/AbstractBackendController.php
index 8953c8be8269..cc771f62c0f7 100644
--- a/typo3/sysext/form/Classes/Controller/AbstractBackendController.php
+++ b/typo3/sysext/form/Classes/Controller/AbstractBackendController.php
@@ -41,8 +41,13 @@ abstract class AbstractBackendController extends ActionController
 
     public function initializeObject()
     {
-        $this->formSettings = GeneralUtility::makeInstance(ConfigurationManagerInterface::class)
-            ->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_YAML_SETTINGS, 'form');
+        $configurationManager = GeneralUtility::makeInstance(ConfigurationManagerInterface::class);
+        $this->formSettings = $configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_YAML_SETTINGS, 'form');
+        if (!isset($this->formSettings['formManager'])) {
+            // Config sub array formManager is crucial and should always exist. If it does
+            // not, this indicates an issue in config loading logic. Except in this case.
+            throw new \LogicException('Configuration could not be loaded', 1681549038);
+        }
     }
 
     /**
-- 
GitLab