From e9907829d7a44e498d0fc4a875754a79f66f740d Mon Sep 17 00:00:00 2001
From: Nikita Hovratov <nikita.h@live.de>
Date: Fri, 1 Sep 2023 14:33:14 +0200
Subject: [PATCH] [FEATURE] Add BeforeLoadedPageTsConfigEvent
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

With the recent deprecation of adding page TSconfig via ext_localconf
as a string, the need for a new PSR-14 event arose. This event can be
used to add global static page TSconfig before the global page.tsconfig
files are loaded. This allows extensions to add basic config, which
still can be overridden with page.tsconfig.

Resolves: #101818
Releases: main
Change-Id: Ic6fc0bff5e19c6c097c123386452b0e2bba0e17e
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/80814
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Stefan Bürk <stefan@buerk.tech>
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: core-ci <typo3@b13.com>
---
 composer.json                                 |  1 +
 .../Event/BeforeLoadedPageTsConfigEvent.php   | 47 +++++++++++++++++
 .../IncludeTree/TsConfigTreeBuilder.php       |  3 ++
 ...e-101818-BeforeLoadedPageTsConfigEvent.rst | 50 +++++++++++++++++++
 .../EventListener/AddGlobalPageTsConfig.php   | 30 +++++++++++
 .../Configuration/Services.yaml               |  8 +++
 .../test_tsconfig_event/composer.json         | 19 +++++++
 .../test_tsconfig_event/ext_emconf.php        | 21 ++++++++
 .../BeforeLoadedPageTsConfigEventTest.php     | 41 +++++++++++++++
 9 files changed, 220 insertions(+)
 create mode 100644 typo3/sysext/core/Classes/TypoScript/IncludeTree/Event/BeforeLoadedPageTsConfigEvent.php
 create mode 100644 typo3/sysext/core/Documentation/Changelog/13.0/Feature-101818-BeforeLoadedPageTsConfigEvent.rst
 create mode 100644 typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/Classes/EventListener/AddGlobalPageTsConfig.php
 create mode 100644 typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/Configuration/Services.yaml
 create mode 100644 typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/composer.json
 create mode 100644 typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/ext_emconf.php
 create mode 100644 typo3/sysext/core/Tests/Functional/TypoScript/IncludeTree/Event/BeforeLoadedPageTsConfigEventTest.php

diff --git a/composer.json b/composer.json
index 89be150d1bd3..b005717236ed 100644
--- a/composer.json
+++ b/composer.json
@@ -312,6 +312,7 @@
 			"TYPO3Tests\\TestMeta\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Classes/",
 			"TYPO3Tests\\TestTyposcriptAstFunctionEvent\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Classes/",
 			"TYPO3Tests\\TestTyposcriptPagetsconfigfactory\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_pagetsconfigfactory/Classes/",
+			"TYPO3Tests\\TestTsconfigEvent\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/Classes/",
 			"TYPO3Tests\\TypeConverterTest\\": "typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/type_converter_test/Classes/",
 			"TYPO3Tests\\ViewhelperLibrary\\": "typo3/sysext/fluid/Tests/Functional/Fixtures/Libraries/viewhelper_library/src/"
 		}
diff --git a/typo3/sysext/core/Classes/TypoScript/IncludeTree/Event/BeforeLoadedPageTsConfigEvent.php b/typo3/sysext/core/Classes/TypoScript/IncludeTree/Event/BeforeLoadedPageTsConfigEvent.php
new file mode 100644
index 000000000000..99b5decc0da5
--- /dev/null
+++ b/typo3/sysext/core/Classes/TypoScript/IncludeTree/Event/BeforeLoadedPageTsConfigEvent.php
@@ -0,0 +1,47 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\TypoScript\IncludeTree\Event;
+
+/**
+ * Extensions can add global page TSconfig right before they are loaded from other sources
+ * like the global page.tsconfig file.
+ *
+ * Note: The added config should not depend on runtime / request. This is considered static
+ *       config and thus should be identical on every request.
+ */
+final class BeforeLoadedPageTsConfigEvent
+{
+    public function __construct(private array $tsConfig = [])
+    {
+    }
+
+    public function getTsConfig(): array
+    {
+        return $this->tsConfig;
+    }
+
+    public function addTsConfig(string $tsConfig): void
+    {
+        $this->tsConfig[] = $tsConfig;
+    }
+
+    public function setTsConfig(array $tsConfig): void
+    {
+        $this->tsConfig = $tsConfig;
+    }
+}
diff --git a/typo3/sysext/core/Classes/TypoScript/IncludeTree/TsConfigTreeBuilder.php b/typo3/sysext/core/Classes/TypoScript/IncludeTree/TsConfigTreeBuilder.php
index 2270b150ed00..e7de781fc6b8 100644
--- a/typo3/sysext/core/Classes/TypoScript/IncludeTree/TsConfigTreeBuilder.php
+++ b/typo3/sysext/core/Classes/TypoScript/IncludeTree/TsConfigTreeBuilder.php
@@ -21,6 +21,7 @@ use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
 use TYPO3\CMS\Core\EventDispatcher\EventDispatcher;
 use TYPO3\CMS\Core\Package\PackageManager;
+use TYPO3\CMS\Core\TypoScript\IncludeTree\Event\BeforeLoadedPageTsConfigEvent;
 use TYPO3\CMS\Core\TypoScript\IncludeTree\Event\ModifyLoadedPageTsConfigEvent;
 use TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode\RootInclude;
 use TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode\TsConfigInclude;
@@ -115,6 +116,8 @@ final class TsConfigTreeBuilder
             }
         }
         if (!$gotPackagesPagesTsConfigFromCache) {
+            $event = $this->eventDispatcher->dispatch(new BeforeLoadedPageTsConfigEvent());
+            $collectedPagesTsConfigArray = $event->getTsConfig();
             foreach ($this->packageManager->getActivePackages() as $package) {
                 $packagePath = $package->getPackagePath();
                 $tsConfigFile = null;
diff --git a/typo3/sysext/core/Documentation/Changelog/13.0/Feature-101818-BeforeLoadedPageTsConfigEvent.rst b/typo3/sysext/core/Documentation/Changelog/13.0/Feature-101818-BeforeLoadedPageTsConfigEvent.rst
new file mode 100644
index 000000000000..00ee3defc00f
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/13.0/Feature-101818-BeforeLoadedPageTsConfigEvent.rst
@@ -0,0 +1,50 @@
+.. include:: /Includes.rst.txt
+
+.. _feature-101818-1693570608:
+
+================================================
+Feature: #101818 - BeforeLoadedPageTsConfigEvent
+================================================
+
+See :issue:`101818`
+
+Description
+===========
+
+The PSR-14 event :php:`\TYPO3\CMS\Core\TypoScript\IncludeTree\Event\BeforeLoadedPageTsConfigEvent`
+can be used to add global static page TSconfig before anything else is loaded.
+This is especially useful, if page TSconfig is generated automatically as a
+string from a PHP function.
+
+It is important to understand that this config is considered static and thus
+should not depend on runtime / request.
+
+Example
+-------
+
+.. code-block:: php
+
+    <?php
+
+    namespace Vendor\MyExtension\EventListener;
+
+    use TYPO3\CMS\Core\Attribute\AsEventListener;
+    use TYPO3\CMS\Core\TypoScript\IncludeTree\Event\BeforeLoadedPageTsConfigEvent;
+
+    #[AsEventListener(identifier: 'vendor/my-extension/global-pagetsconfig')]
+    final class AddGlobalPageTsConfig
+    {
+        public function __invoke(BeforeLoadedPageTsConfigEvent $event): void
+        {
+            $event->addTsConfig('global = a global setting');
+        }
+    }
+
+
+Impact
+======
+
+Developers are able to define an event listener which is dispatched before any
+other page TSconfig is loaded.
+
+.. index:: Backend, PHP-API, ext:core
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/Classes/EventListener/AddGlobalPageTsConfig.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/Classes/EventListener/AddGlobalPageTsConfig.php
new file mode 100644
index 000000000000..cc2da7a89d05
--- /dev/null
+++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/Classes/EventListener/AddGlobalPageTsConfig.php
@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3Tests\TestTsconfigEvent\EventListener;
+
+use TYPO3\CMS\Core\Attribute\AsEventListener;
+use TYPO3\CMS\Core\TypoScript\IncludeTree\Event\BeforeLoadedPageTsConfigEvent;
+
+#[AsEventListener(identifier: 'typo3tests/test-tsconfig-event/add-global')]
+final class AddGlobalPageTsConfig
+{
+    public function __invoke(BeforeLoadedPageTsConfigEvent $event): void
+    {
+        $event->addTsConfig('number = one');
+    }
+}
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/Configuration/Services.yaml b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/Configuration/Services.yaml
new file mode 100644
index 000000000000..099f1b10ce39
--- /dev/null
+++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/Configuration/Services.yaml
@@ -0,0 +1,8 @@
+services:
+  _defaults:
+    autowire: true
+    autoconfigure: true
+    public: false
+
+  TYPO3Tests\TestTsconfigEvent\:
+    resource: '../Classes/*'
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/composer.json b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/composer.json
new file mode 100644
index 000000000000..78995cd5297d
--- /dev/null
+++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/composer.json
@@ -0,0 +1,19 @@
+{
+	"name": "typo3tests/test-tsconfig-event",
+	"type": "typo3-cms-extension",
+	"description": "This extension defines event listeners for page TSConfig modification.",
+	"license": "GPL-2.0-or-later",
+	"require": {
+		"typo3/cms-core": "13.0.*@dev"
+	},
+	"extra": {
+		"typo3/cms": {
+			"extension-key": "test_tsconfig_event"
+		}
+	},
+	"autoload": {
+		"psr-4": {
+			"TYPO3Tests\\TestTsconfigEvent\\": "Classes/"
+		}
+	}
+}
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/ext_emconf.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/ext_emconf.php
new file mode 100644
index 000000000000..eee1548bc458
--- /dev/null
+++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event/ext_emconf.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+$EM_CONF[$_EXTKEY] = [
+    'title' => 'This extension defines event listeners for page TSConfig modification.',
+    'description' => 'This extension defines event listeners for page TSConfig modification.',
+    'category' => 'example',
+    'version' => '13.0.0',
+    'state' => 'beta',
+    'author' => 'Nikita Hovratov',
+    'author_email' => 'info@nikita-hovratov.de',
+    'author_company' => '',
+    'constraints' => [
+        'depends' => [
+            'typo3' => '13.0.0',
+        ],
+        'conflicts' => [],
+        'suggests' => [],
+    ],
+];
diff --git a/typo3/sysext/core/Tests/Functional/TypoScript/IncludeTree/Event/BeforeLoadedPageTsConfigEventTest.php b/typo3/sysext/core/Tests/Functional/TypoScript/IncludeTree/Event/BeforeLoadedPageTsConfigEventTest.php
new file mode 100644
index 000000000000..43bc45d9b31f
--- /dev/null
+++ b/typo3/sysext/core/Tests/Functional/TypoScript/IncludeTree/Event/BeforeLoadedPageTsConfigEventTest.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\Tests\Functional\TypoScript\IncludeTree\Event;
+
+use TYPO3\CMS\Core\Site\Entity\NullSite;
+use TYPO3\CMS\Core\TypoScript\PageTsConfigFactory;
+use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
+
+final class BeforeLoadedPageTsConfigEventTest extends FunctionalTestCase
+{
+    protected array $testExtensionsToLoad = [
+        'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tsconfig_event',
+    ];
+
+    /**
+     * @test
+     */
+    public function globalPageTsconfigIsAddedByEvent(): void
+    {
+        $subject = $this->get(PageTsConfigFactory::class);
+
+        $pageTsConfig = $subject->create([], new NullSite());
+
+        self::assertSame('one', $pageTsConfig->getPageTsConfigArray()['number']);
+    }
+}
-- 
GitLab