From 0a056901cb19eba686a91ab050150ef4f46f6b71 Mon Sep 17 00:00:00 2001 From: Mario Lubenka Date: Tue, 11 Jun 2024 13:43:31 +0200 Subject: [PATCH] [FEATURE] Disallow content elements in all containers A new method "Registry::addDisallowedCType" is added that allows excluding content elements from all containers per default (if Ext:content_defender is installed). However, content elements excluded in such a way can still be explicitly allowed via "allowed" setting in column configuration. Note: In order to take effect on the whole TYPO3 system, the method needs to be called in ext_localconf.php. Resolves: #507 --- Classes/Tca/Registry.php | 35 ++++++++++++++ Tests/Functional/Tca/RegistryTest.php | 67 +++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/Classes/Tca/Registry.php b/Classes/Tca/Registry.php index acbfe667..b0bfaa8a 100644 --- a/Classes/Tca/Registry.php +++ b/Classes/Tca/Registry.php @@ -23,6 +23,13 @@ class Registry implements SingletonInterface { + /** + * This list is populated by "addDisallowedCType" and allows defining CTypes that are disallowed + * in all containers, when they are not explicitly allowed by configuration. + * Note that the disallow functionality requires Ext:content_defender. + */ + protected array $disallowedCTypes = []; + /** * @param ContainerConfiguration $containerConfiguration */ @@ -103,6 +110,23 @@ public function configureContainer(ContainerConfiguration $containerConfiguratio $GLOBALS['TCA']['tt_content']['containerConfiguration'][$containerConfiguration->getCType()] = $containerConfiguration->toArray(); } + /** + * Add a CType and exclude it from all containers per default. + * It may be explicitly enabled using the "allowed" configuration. + * + * NOTE: In order for this to correctly take effect, call this method + * in your extension's ext_localconf.php, not in TCA. + * + * @see getContentDefenderConfiguration + */ + public function addDisallowedCType(string $cType): void + { + if (in_array($cType, $this->disallowedCTypes)) { + return; + } + $this->disallowedCTypes[] = $cType; + } + public function getContentDefenderConfiguration(string $cType, int $colPos): array { $contentDefenderConfiguration = []; @@ -113,9 +137,20 @@ public function getContentDefenderConfiguration(string $cType, int $colPos): arr $contentDefenderConfiguration['allowed.'] = $column['allowed'] ?? []; $contentDefenderConfiguration['disallowed.'] = $column['disallowed'] ?? []; $contentDefenderConfiguration['maxitems'] = $column['maxitems'] ?? 0; + break 2; } } } + // Add globally disallowed CTypes when they are not explicitly allowed and not already disallowed + $addDisallowedCTypes = array_diff( + $this->disallowedCTypes, + GeneralUtility::trimExplode(',', $contentDefenderConfiguration['allowed.']['CType'] ?? '', true) + ); + if ($addDisallowedCTypes !== []) { + $allDisallowedCTypes = GeneralUtility::trimExplode(',', $contentDefenderConfiguration['disallowed.']['CType'] ?? '', true); + array_push($allDisallowedCTypes, ...$addDisallowedCTypes); + $contentDefenderConfiguration['disallowed.']['CType'] = implode(',', $allDisallowedCTypes); + } return $contentDefenderConfiguration; } diff --git a/Tests/Functional/Tca/RegistryTest.php b/Tests/Functional/Tca/RegistryTest.php index 472d5ee0..e188f5c8 100644 --- a/Tests/Functional/Tca/RegistryTest.php +++ b/Tests/Functional/Tca/RegistryTest.php @@ -113,4 +113,71 @@ public function originalPageTsIsNotOverriden(): void $expected = 'LLL:EXT:backend/Resources/Private/Language/locallang_db_new_content_el.xlf:special'; self::assertSame($expected, $specialHeader); } + + /** + * @test + * @dataProvider contentDefenderData + */ + public function getContentDefenderConfiguration(int $colPos, array $disallowedCTypes, array $expectedConfiguration) + { + $registry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(Registry::class); + $registry->configureContainer( + ( + new ContainerConfiguration( + 'b13-container', // CType + 'foo', // label + 'bar', // description + [ + [ + ['name' => 'foo', 'colPos' => 1], + ['name' => 'foo2', 'colPos' => 2, 'allowed' => ['CType' => 'ce1'], 'disallowed' => ['CType' => 'ce2']], + ['name' => 'foo3', 'colPos' => 3, 'maxitems' => 3], + ['name' => 'foo4', 'colPos' => 4, 'allowed' => ['CType' => 'ce1']], + ], + ] // grid configuration + ) + )->setGroup('special') + ); + if ($disallowedCTypes !== []) { + foreach ($disallowedCTypes as $cType) { + $registry->addDisallowedCType($cType); + } + } + $configuration = $registry->getContentDefenderConfiguration('b13-container', $colPos); + self::assertEquals($expectedConfiguration, $configuration); + } + + public static function contentDefenderData(): \Traversable + { + yield 'no column' => [ + 'colPos' => 999, + 'disallowedCTypes' => [], + 'expectedConfiguration' => [], + ]; + yield 'default configuration' => [ + 'colPos' => 1, + 'disallowedCTypes' => [], + 'expectedConfiguration' => ['allowed.' => [], 'disallowed.' => [], 'maxitems' => 0], + ]; + yield 'maxitems' => [ + 'colPos' => 3, + 'disallowedCTypes' => [], + 'expectedConfiguration' => ['allowed.' => [], 'disallowed.' => [], 'maxitems' => 3], + ]; + yield 'allowed and disallowed' => [ + 'colPos' => 2, + 'disallowedCTypes' => [], + 'expectedConfiguration' => ['allowed.' => ['CType' => 'ce1'], 'disallowed.' => ['CType' => 'ce2'], 'maxitems' => 0], + ]; + yield 'globally disallowed CType' => [ + 'colPos' => 4, + 'disallowedCTypes' => ['ce3'], + 'expectedConfiguration' => ['allowed.' => ['CType' => 'ce1'], 'disallowed.' => ['CType' => 'ce3'], 'maxitems' => 0], + ]; + yield 'globally disallowed CType explicity allowed' => [ + 'colPos' => 4, + 'disallowedCTypes' => ['ce1'], + 'expectedConfiguration' => ['allowed.' => ['CType' => 'ce1'], 'disallowed.' => [], 'maxitems' => 0], + ]; + } }