From 8eb66df379911b1fbf0aa516d6384c8fc011d24f Mon Sep 17 00:00:00 2001 From: Nikita Hovratov <nikita.h@live.de> Date: Fri, 29 Sep 2023 13:18:15 +0200 Subject: [PATCH] [FEATURE] Introduce PSR-14 BeforeTcaOverridesEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This event can be used to dynamically generate TCA and add it as additional base TCA. This is especially useful for "TCA generator" extensions, which add TCA based on another resource, while still enabling users to override TCA via TCA overrides as usual. Resolves: #102067 Releases: main Change-Id: I6d96e18b94f2a53693037b46e3b23d3b7f657154 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/81297 Tested-by: Stefan Bürk <stefan@buerk.tech> Tested-by: Oliver Bartsch <bo@cedev.de> Reviewed-by: Stefan Bürk <stefan@buerk.tech> Reviewed-by: Oliver Bartsch <bo@cedev.de> Tested-by: core-ci <typo3@b13.com> --- composer.json | 1 + .../Event/BeforeTcaOverridesEvent.php | 41 +++++++++++++ .../Utility/ExtensionManagementUtility.php | 11 ++++ ...Feature-102067-BeforeTcaOverridesEvent.rst | 59 +++++++++++++++++++ .../AddTcaAfterTcaCompilation.php | 32 ++++++++++ .../AddTcaBeforeTcaOverrides.php | 33 +++++++++++ .../Configuration/Services.yaml | 8 +++ .../Configuration/TCA/Overrides/fruit.php | 6 ++ .../Configuration/TCA/fruit.php | 21 +++++++ .../Extensions/test_tca_event/composer.json | 19 ++++++ .../Extensions/test_tca_event/ext_emconf.php | 21 +++++++ .../Tca/AfterTcaCompilationEventTest.php | 35 +++++++++++ .../Tca/BeforeTcaOverridesEventTest.php | 43 ++++++++++++++ ...ensionManagementUtilityAccessibleProxy.php | 4 ++ 14 files changed, 334 insertions(+) create mode 100644 typo3/sysext/core/Classes/Configuration/Event/BeforeTcaOverridesEvent.php create mode 100644 typo3/sysext/core/Documentation/Changelog/13.0/Feature-102067-BeforeTcaOverridesEvent.rst create mode 100644 typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Classes/EventListener/AddTcaAfterTcaCompilation.php create mode 100644 typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Classes/EventListener/AddTcaBeforeTcaOverrides.php create mode 100644 typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/Services.yaml create mode 100644 typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/TCA/Overrides/fruit.php create mode 100644 typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/TCA/fruit.php create mode 100644 typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/composer.json create mode 100644 typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/ext_emconf.php create mode 100644 typo3/sysext/core/Tests/Functional/Tca/AfterTcaCompilationEventTest.php create mode 100644 typo3/sysext/core/Tests/Functional/Tca/BeforeTcaOverridesEventTest.php diff --git a/composer.json b/composer.json index d6e9010cf6c1..0abdb839279a 100644 --- a/composer.json +++ b/composer.json @@ -311,6 +311,7 @@ "TYPO3Tests\\TestIrreForeignfield\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_irre_foreignfield/Classes/", "TYPO3Tests\\TestLogger\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_logger/Classes/", "TYPO3Tests\\TestMeta\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_meta/Classes/", + "TYPO3Tests\\TestTcaEvent\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/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/", diff --git a/typo3/sysext/core/Classes/Configuration/Event/BeforeTcaOverridesEvent.php b/typo3/sysext/core/Classes/Configuration/Event/BeforeTcaOverridesEvent.php new file mode 100644 index 000000000000..db15c6e0ea42 --- /dev/null +++ b/typo3/sysext/core/Classes/Configuration/Event/BeforeTcaOverridesEvent.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\Configuration\Event; + +/** + * Event before $GLOBALS['TCA'] is overridden by TCA/Overrides to allow to manipulate $tca, before overrides are merged. + * + * Side note: It is possible to check against the original TCA as this is stored within $GLOBALS['TCA'] + * before this event is fired. + */ +final class BeforeTcaOverridesEvent +{ + public function __construct(private array $tca) + { + } + + public function getTca(): array + { + return $this->tca; + } + + public function setTca(array $tca): void + { + $this->tca = $tca; + } +} diff --git a/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php b/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php index 4ea383ef7c5a..3d2de19c1897 100644 --- a/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php +++ b/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php @@ -22,6 +22,7 @@ use Symfony\Component\Finder\Finder; use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; use TYPO3\CMS\Core\Configuration\Event\AfterTcaCompilationEvent; +use TYPO3\CMS\Core\Configuration\Event\BeforeTcaOverridesEvent; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\DataHandling\PageDoktypeRegistry; use TYPO3\CMS\Core\Migrations\TcaMigration; @@ -1318,6 +1319,8 @@ tt_content.' . $key . $suffix . ' { } } + static::dispatchBaseTcaIsBeingBuiltEvent($GLOBALS['TCA']); + // To require TCA Overrides in a safe scoped environment avoiding local variable clashes. // @see TYPO3\CMS\Core\Tests\Functional\Utility\ExtensionManagementUtility\ExtensionManagementUtilityTcaOverrideRequireTest $scopedRequire = static function (string $filename): void { @@ -1355,6 +1358,14 @@ tt_content.' . $key . $suffix . ' { static::dispatchTcaIsBeingBuiltEvent($GLOBALS['TCA']); } + /** + * Triggers an event for manipulating the TCA before overrides are applied. + */ + protected static function dispatchBaseTcaIsBeingBuiltEvent(array $tca): void + { + $GLOBALS['TCA'] = static::$eventDispatcher->dispatch(new BeforeTcaOverridesEvent($tca))->getTca(); + } + /** * Triggers an event for manipulating the final TCA */ diff --git a/typo3/sysext/core/Documentation/Changelog/13.0/Feature-102067-BeforeTcaOverridesEvent.rst b/typo3/sysext/core/Documentation/Changelog/13.0/Feature-102067-BeforeTcaOverridesEvent.rst new file mode 100644 index 000000000000..18666109a7bd --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/13.0/Feature-102067-BeforeTcaOverridesEvent.rst @@ -0,0 +1,59 @@ +.. include:: /Includes.rst.txt + +.. _feature-102067-1695985288: + +================================================= +Feature: #102067 - PSR-14 BeforeTcaOverridesEvent +================================================= + +See :issue:`102067` + +Description +=========== + +A new PSR-14 :php:`\TYPO3\CMS\Core\Configuration\Event\BeforeTcaOverridesEvent` +has been introduced, enabling developers to listen to the state between loaded +base TCA and merging of TCA overrides. + +Example +------- + +.. code-block:: php + + <?php + + declare(strict_types=1); + + namespace Vendor\MyExtension\EventListener; + + use TYPO3\CMS\Core\Attribute\AsEventListener; + use TYPO3\CMS\Core\Configuration\Event\BeforeTcaOverridesEvent; + + final class AddTcaBeforeTcaOverrides + { + #[AsEventListener('vendor/my-extension/before-tca-overrides')] + public function __invoke(BeforeTcaOverridesEvent $event): void + { + $tca = $event->getTca(); + $tca['tt_content']['columns']['header']['config']['max'] = 100; + $event->setTca($tca); + } + } + + +Impact +====== + +The new PSR-14 can be used to dynamically generate TCA and add it as additional +base TCA. This is especially useful for "TCA generator" extensions, which add +TCA based on another resource, while still enabling users to override TCA via +the known TCA overrides API. + +.. note:: + + Please note that TCA is always "runtime cached". This means that dynamic + additions must never depend on runtime state, e.g. the current PSR-7 + request or similar, because such information might not even exist when + the first call is e.g. done from CLI. + +.. index:: Backend, PHP-API, TCA, ext:core diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Classes/EventListener/AddTcaAfterTcaCompilation.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Classes/EventListener/AddTcaAfterTcaCompilation.php new file mode 100644 index 000000000000..cfdfe1cc02f6 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Classes/EventListener/AddTcaAfterTcaCompilation.php @@ -0,0 +1,32 @@ +<?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\TestTcaEvent\EventListener; + +use TYPO3\CMS\Core\Attribute\AsEventListener; +use TYPO3\CMS\Core\Configuration\Event\AfterTcaCompilationEvent; + +#[AsEventListener(identifier: 'typo3tests/test-tca-event/after-tca-compilation')] +final class AddTcaAfterTcaCompilation +{ + public function __invoke(AfterTcaCompilationEvent $event): void + { + $tca = $event->getTca(); + $tca['fruit']['ctrl']['title'] = 'Vegetable'; + $event->setTca($tca); + } +} diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Classes/EventListener/AddTcaBeforeTcaOverrides.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Classes/EventListener/AddTcaBeforeTcaOverrides.php new file mode 100644 index 000000000000..482d5a3a7d13 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Classes/EventListener/AddTcaBeforeTcaOverrides.php @@ -0,0 +1,33 @@ +<?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\TestTcaEvent\EventListener; + +use TYPO3\CMS\Core\Attribute\AsEventListener; +use TYPO3\CMS\Core\Configuration\Event\BeforeTcaOverridesEvent; + +#[AsEventListener(identifier: 'typo3tests/test-tca-event/before-tca-overrides')] +final class AddTcaBeforeTcaOverrides +{ + public function __invoke(BeforeTcaOverridesEvent $event): void + { + $tca = $event->getTca(); + $tca['fruit']['columns']['name']['config']['type'] = 'number'; + $tca['fruit']['columns']['name']['config']['label'] = 'Monstera'; + $event->setTca($tca); + } +} diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/Services.yaml b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/Services.yaml new file mode 100644 index 000000000000..c0a548a7bf09 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/Services.yaml @@ -0,0 +1,8 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + TYPO3Tests\TestTcaEvent\: + resource: '../Classes/*' diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/TCA/Overrides/fruit.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/TCA/Overrides/fruit.php new file mode 100644 index 000000000000..4e3b2c2346d2 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/TCA/Overrides/fruit.php @@ -0,0 +1,6 @@ +<?php + +declare(strict_types=1); + +$GLOBALS['TCA']['fruit']['ctrl']['title'] = 'Plant'; +$GLOBALS['TCA']['fruit']['columns']['name']['config']['type'] = 'text'; diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/TCA/fruit.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/TCA/fruit.php new file mode 100644 index 000000000000..fb8c81fe04e4 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/Configuration/TCA/fruit.php @@ -0,0 +1,21 @@ +<?php + +return [ + 'ctrl' => [ + 'title' => 'Fruit', + 'label' => 'name', + ], + 'types' => [ + '1' => [ + 'showitem' => 'name', + ], + ], + 'columns' => [ + 'name' => [ + 'label' => 'Name', + 'config' => [ + 'type' => 'input', + ], + ], + ], +]; diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/composer.json b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/composer.json new file mode 100644 index 000000000000..494520e0475d --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/composer.json @@ -0,0 +1,19 @@ +{ + "name": "typo3tests/test-tca-event", + "type": "typo3-cms-extension", + "description": "This extension defines event listeners for TCA modification.", + "license": "GPL-2.0-or-later", + "require": { + "typo3/cms-core": "13.0.*@dev" + }, + "extra": { + "typo3/cms": { + "extension-key": "test_tca_event" + } + }, + "autoload": { + "psr-4": { + "TYPO3Tests\\TestTcaEvent\\": "Classes/" + } + } +} diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/ext_emconf.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/ext_emconf.php new file mode 100644 index 000000000000..9602fb464fa1 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event/ext_emconf.php @@ -0,0 +1,21 @@ +<?php + +declare(strict_types=1); + +$EM_CONF[$_EXTKEY] = [ + 'title' => 'This extension defines event listeners for TCA modification.', + 'description' => 'This extension defines event listeners for TCA 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/Tca/AfterTcaCompilationEventTest.php b/typo3/sysext/core/Tests/Functional/Tca/AfterTcaCompilationEventTest.php new file mode 100644 index 000000000000..d1d384621d54 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Tca/AfterTcaCompilationEventTest.php @@ -0,0 +1,35 @@ +<?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\Tca; + +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; + +final class AfterTcaCompilationEventTest extends FunctionalTestCase +{ + protected array $testExtensionsToLoad = [ + 'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event', + ]; + + /** + * @test + */ + public function addedTcaOverridesAnythingElse(): void + { + self::assertSame('Vegetable', $GLOBALS['TCA']['fruit']['ctrl']['title']); + } +} diff --git a/typo3/sysext/core/Tests/Functional/Tca/BeforeTcaOverridesEventTest.php b/typo3/sysext/core/Tests/Functional/Tca/BeforeTcaOverridesEventTest.php new file mode 100644 index 000000000000..484f4977422a --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Tca/BeforeTcaOverridesEventTest.php @@ -0,0 +1,43 @@ +<?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\Tca; + +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; + +final class BeforeTcaOverridesEventTest extends FunctionalTestCase +{ + protected array $testExtensionsToLoad = [ + 'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_tca_event', + ]; + + /** + * @test + */ + public function cannotOverrideTcaOverrides(): void + { + self::assertSame('text', $GLOBALS['TCA']['fruit']['columns']['name']['config']['type']); + } + + /** + * @test + */ + public function canOverrideBaseTca(): void + { + self::assertSame('Monstera', $GLOBALS['TCA']['fruit']['columns']['name']['config']['label']); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Utility/AccessibleProxies/ExtensionManagementUtilityAccessibleProxy.php b/typo3/sysext/core/Tests/Unit/Utility/AccessibleProxies/ExtensionManagementUtilityAccessibleProxy.php index 991c87099bb7..070e0fde1818 100644 --- a/typo3/sysext/core/Tests/Unit/Utility/AccessibleProxies/ExtensionManagementUtilityAccessibleProxy.php +++ b/typo3/sysext/core/Tests/Unit/Utility/AccessibleProxies/ExtensionManagementUtilityAccessibleProxy.php @@ -77,6 +77,10 @@ class ExtensionManagementUtilityAccessibleProxy extends ExtensionManagementUtili $GLOBALS['TCA'] = []; } + public static function dispatchBaseTcaIsBeingBuiltEvent(array $tca): void + { + } + public static function dispatchTcaIsBeingBuiltEvent(array $tca): void { } -- GitLab