From d9a97457e00c7b88582031a8cc3136c6e0a5487c Mon Sep 17 00:00:00 2001 From: Bernd Sengupta Date: Thu, 25 Sep 2025 16:37:28 +0200 Subject: [PATCH 1/3] [TASK] Make the plugin a `CType` Fixes #1332 --- Build/composer-unused/composer-unused.php | 3 +- Build/phpstan/phpstan.neon | 4 + .../AbstractListTypeToCTypeUpdate.php | 287 ++++++++++++++++++ Classes/Upgrades/ListTypeToCTypeUpdate.php | 30 ++ Configuration/Services.php | 3 +- Configuration/TCA/Overrides/tt_content.php | 88 +++--- .../Private/Language/de.locallang_db.xlf | 14 +- Resources/Private/Language/locallang_db.xlf | 11 +- .../ContentElement.csv | 26 +- .../TeaController/ContentElements.csv | 6 +- .../Upgrades/Fixtures/PluginAsCType.csv | 4 + .../Upgrades/Fixtures/PluginAsListType.csv | 4 + .../Upgrades/ListTypeToCTypeUpdateTest.php | 75 +++++ composer.json | 3 +- ext_localconf.php | 12 +- 15 files changed, 507 insertions(+), 63 deletions(-) create mode 100644 Classes/Upgrades/AbstractListTypeToCTypeUpdate.php create mode 100644 Classes/Upgrades/ListTypeToCTypeUpdate.php create mode 100644 Tests/Functional/Upgrades/Fixtures/PluginAsCType.csv create mode 100644 Tests/Functional/Upgrades/Fixtures/PluginAsListType.csv create mode 100644 Tests/Functional/Upgrades/ListTypeToCTypeUpdateTest.php diff --git a/Build/composer-unused/composer-unused.php b/Build/composer-unused/composer-unused.php index b223e9bbb..df912a7af 100644 --- a/Build/composer-unused/composer-unused.php +++ b/Build/composer-unused/composer-unused.php @@ -3,9 +3,8 @@ declare(strict_types=1); use ComposerUnused\ComposerUnused\Configuration\Configuration; -use ComposerUnused\ComposerUnused\Configuration\NamedFilter; + return static function (Configuration $config): Configuration { - $config->addNamedFilter(NamedFilter::fromString('typo3/cms-fluid')); return $config; }; diff --git a/Build/phpstan/phpstan.neon b/Build/phpstan/phpstan.neon index 594ff22df..7f336ba4c 100644 --- a/Build/phpstan/phpstan.neon +++ b/Build/phpstan/phpstan.neon @@ -62,6 +62,10 @@ parameters: - '$_FILES' - '$_SERVER' message: 'Use PSR-7 API instead' + + excludePaths: + # We need this Class only for V12. So we exclude it here + - ../../Classes/Upgrades/AbstractListTypeToCTypeUpdate.php ignoreErrors: - diff --git a/Classes/Upgrades/AbstractListTypeToCTypeUpdate.php b/Classes/Upgrades/AbstractListTypeToCTypeUpdate.php new file mode 100644 index 000000000..c56f6c1e5 --- /dev/null +++ b/Classes/Upgrades/AbstractListTypeToCTypeUpdate.php @@ -0,0 +1,287 @@ +validateRequirements(); + } + + /** + * This must return an array containing the "list_type" to "CType" mapping + * + * Example: + * + * [ + * 'pi_plugin1' => 'pi_plugin1', + * 'pi_plugin2' => 'new_content_element', + * ] + * + * @return array + */ + abstract protected function getListTypeToCTypeMapping(): array; + + abstract public function getTitle(): string; + + abstract public function getDescription(): string; + + public function getPrerequisites(): array + { + return [ + DatabaseUpdatedPrerequisite::class, + ]; + } + + public function updateNecessary(): bool + { + return ( + $this->getListTypeToCTypeMapping() !== [] && + $this->columnsExistInContentTable() && + $this->hasContentElementsToUpdate() + ) + || ( + $this->getListTypeToCTypeMapping() !== [] && + $this->columnsExistInBackendUserGroupsTable() + && $this->hasNoLegacyBackendGroupsExplicitAllowDenyConfiguration() + && $this->hasBackendUserGroupsToUpdate() + ); + } + + public function executeUpdate(): bool + { + if ($this->getListTypeToCTypeMapping() !== [] && + $this->columnsExistInContentTable() && + $this->hasContentElementsToUpdate() + ) { + $this->updateContentElements(); + } + if ($this->getListTypeToCTypeMapping() !== [] && + $this->columnsExistInBackendUserGroupsTable() + && $this->hasNoLegacyBackendGroupsExplicitAllowDenyConfiguration() + && $this->hasBackendUserGroupsToUpdate() + ) { + $this->updateBackendUserGroups(); + } + + return true; + } + + protected function columnsExistInContentTable(): bool + { + $schemaManager = $this->connectionPool + ->getConnectionForTable(self::TABLE_CONTENT) + ->createSchemaManager(); + + $tableColumnNames = array_flip( + array_map( + static fn(Column $column) => $column->getName(), + $schemaManager->listTableColumns(self::TABLE_CONTENT), + ), + ); + + foreach (['CType', 'list_type'] as $column) { + if (!isset($tableColumnNames[$column])) { + return false; + } + } + + return true; + } + + protected function columnsExistInBackendUserGroupsTable(): bool + { + $schemaManager = $this->connectionPool + ->getConnectionForTable(self::TABLE_BACKEND_USER_GROUPS) + ->createSchemaManager(); + + return isset($schemaManager->listTableColumns(self::TABLE_BACKEND_USER_GROUPS)['explicit_allowdeny']); + } + + protected function hasContentElementsToUpdate(): bool + { + $listTypesToUpdate = array_keys($this->getListTypeToCTypeMapping()); + + $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_CONTENT); + $queryBuilder->getRestrictions()->removeAll(); + $queryBuilder + ->count('uid') + ->from(self::TABLE_CONTENT) + ->where( + $queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('list')), + $queryBuilder->expr()->in( + 'list_type', + $queryBuilder->createNamedParameter($listTypesToUpdate, Connection::PARAM_STR_ARRAY), + ), + ); + + return (bool)$queryBuilder->executeQuery()->fetchOne(); + } + + protected function hasBackendUserGroupsToUpdate(): bool + { + $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_BACKEND_USER_GROUPS); + $queryBuilder->getRestrictions()->removeAll(); + + $searchConstraints = []; + foreach ($this->getListTypeToCTypeMapping() as $listTyp) { + $searchConstraints[] = $queryBuilder->expr()->like( + 'explicit_allowdeny', + $queryBuilder->createNamedParameter( + '%' . $queryBuilder->escapeLikeWildcards('tt_content:list_type:' . $listTyp) . '%', + ), + ); + } + + $queryBuilder + ->count('uid') + ->from(self::TABLE_BACKEND_USER_GROUPS) + ->where( + $queryBuilder->expr()->or(...$searchConstraints), + ); + + return (bool)$queryBuilder->executeQuery()->fetchOne(); + } + + /** + * Returns true, if no legacy explicit_allowdeny be_groups configuration is found. Note, that we can not rely + * BackendGroupsExplicitAllowDenyMigration status here, since the update must also be executed for new + * TYPO3 v13+ installations, where BackendGroupsExplicitAllowDenyMigration is not required. + */ + protected function hasNoLegacyBackendGroupsExplicitAllowDenyConfiguration(): bool + { + $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_BACKEND_USER_GROUPS); + $queryBuilder->getRestrictions()->removeAll(); + $queryBuilder + ->count('uid') + ->from(self::TABLE_BACKEND_USER_GROUPS) + ->where( + $queryBuilder->expr()->like( + 'explicit_allowdeny', + $queryBuilder->createNamedParameter( + '%ALLOW%', + ), + ), + ); + /** @phpstan-ignore cast.int */ + return (int)$queryBuilder->executeQuery()->fetchOne() === 0; + } + /** @phpstan-ignore missingType.iterableValue */ + protected function getContentElementsToUpdate(string $listType): array + { + $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_CONTENT); + $queryBuilder->getRestrictions()->removeAll(); + $queryBuilder + ->select('uid') + ->from(self::TABLE_CONTENT) + ->where( + $queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('list')), + $queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter($listType)), + ); + + return $queryBuilder->executeQuery()->fetchAllAssociative(); + } + /** @phpstan-ignore missingType.iterableValue */ + protected function getBackendUserGroupsToUpdate(string $listType): array + { + $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_BACKEND_USER_GROUPS); + $queryBuilder->getRestrictions()->removeAll(); + $queryBuilder + ->select('uid', 'explicit_allowdeny') + ->from(self::TABLE_BACKEND_USER_GROUPS) + ->where( + $queryBuilder->expr()->like( + 'explicit_allowdeny', + $queryBuilder->createNamedParameter( + '%' . $queryBuilder->escapeLikeWildcards('tt_content:list_type:' . $listType) . '%', + ), + ), + ); + return $queryBuilder->executeQuery()->fetchAllAssociative(); + } + + protected function updateContentElements(): void + { + $connection = $this->connectionPool->getConnectionForTable(self::TABLE_CONTENT); + + foreach ($this->getListTypeToCTypeMapping() as $listType => $contentType) { + foreach ($this->getContentElementsToUpdate($listType) as $record) { + $connection->update( + self::TABLE_CONTENT, + [ + 'CType' => $contentType, + 'list_type' => '', + ], + ['uid' => (int)$record['uid']], + ); + } + } + } + + protected function updateBackendUserGroups(): void + { + $connection = $this->connectionPool->getConnectionForTable(self::TABLE_BACKEND_USER_GROUPS); + + foreach ($this->getListTypeToCTypeMapping() as $listType => $contentType) { + foreach ($this->getBackendUserGroupsToUpdate($listType) as $record) { + $fields = GeneralUtility::trimExplode(',', $record['explicit_allowdeny'], true); + foreach ($fields as $key => $field) { + if ($field === 'tt_content:list_type:' . $listType) { + unset($fields[$key]); + $fields[] = 'tt_content:CType:' . $contentType; + } + } + + $connection->update( + self::TABLE_BACKEND_USER_GROUPS, + [ + 'explicit_allowdeny' => implode(',', array_unique($fields)), + ], + ['uid' => (int)$record['uid']], + ); + } + } + } + + private function validateRequirements(): void + { + if ($this->getTitle() === '') { + throw new \RuntimeException('The update class "' . static::class . '" must provide a title by extending "getTitle()"', 1727605675); + } + if ($this->getDescription() === '') { + throw new \RuntimeException('The update class "' . static::class . '" must provide a description by extending "getDescription()"', 1727605676); + } + if ($this->getListTypeToCTypeMapping() === []) { + throw new \RuntimeException('The update class "' . static::class . '" does not provide a "list_type" to "CType" migration mapping', 1727605677); + } + + foreach ($this->getListTypeToCTypeMapping() as $listType => $contentElement) { + if (!is_string($listType) || $listType === '') { + throw new \RuntimeException('Invalid mapping item "' . $listType . '" in class "' . static::class, 1727605678); + } + if (!is_string($contentElement) || $contentElement === '') { + throw new \RuntimeException('Invalid mapping item "' . $contentElement . '" in class "' . static::class, 1727605679); + } + } + } +} diff --git a/Classes/Upgrades/ListTypeToCTypeUpdate.php b/Classes/Upgrades/ListTypeToCTypeUpdate.php new file mode 100644 index 000000000..42e0305f4 --- /dev/null +++ b/Classes/Upgrades/ListTypeToCTypeUpdate.php @@ -0,0 +1,30 @@ + 'tea_teaindex', + 'tea_teashow' => 'tea_teashow', + 'tea_teafront_end_editor' => 'tea_teafront_end_editor', + ]; + } + + public function getTitle(): string + { + return 'Migrates tea extension plugins'; + } + + public function getDescription(): string + { + return 'Migrates tea_index, tea_show, tea_front_end_editor from list_type to CType.'; + } +} diff --git a/Configuration/Services.php b/Configuration/Services.php index 1e95f19d4..fb637ffe2 100644 --- a/Configuration/Services.php +++ b/Configuration/Services.php @@ -11,7 +11,8 @@ ->services() ->defaults() ->autowire() - ->autoconfigure(); + ->autoconfigure() + ->public(); $services ->load('TTN\\Tea\\', '../Classes/*') diff --git a/Configuration/TCA/Overrides/tt_content.php b/Configuration/TCA/Overrides/tt_content.php index 8a35b8d8c..93a3bb219 100644 --- a/Configuration/TCA/Overrides/tt_content.php +++ b/Configuration/TCA/Overrides/tt_content.php @@ -1,56 +1,68 @@ Visible Sichtbar - + + Tea plugins + Tea + + + Tea Index + Teeliste + + + Tea plugin to show the listview. + Tea Plugin für eine Listenansicht. + + diff --git a/Resources/Private/Language/locallang_db.xlf b/Resources/Private/Language/locallang_db.xlf index 2cb6e432f..585e47683 100644 --- a/Resources/Private/Language/locallang_db.xlf +++ b/Resources/Private/Language/locallang_db.xlf @@ -27,6 +27,15 @@ Visible - + + Tea + + + Tea Index + + + Tea plugin to show the listview. + + diff --git a/Tests/Functional/Controller/Fixtures/Database/FrontEndEditorController/ContentElement.csv b/Tests/Functional/Controller/Fixtures/Database/FrontEndEditorController/ContentElement.csv index 9420a7c45..a5d8501f4 100644 --- a/Tests/Functional/Controller/Fixtures/Database/FrontEndEditorController/ContentElement.csv +++ b/Tests/Functional/Controller/Fixtures/Database/FrontEndEditorController/ContentElement.csv @@ -1,14 +1,14 @@ "tt_content" -,"uid","pid","CType","header","list_type","pi_flexform" -,1,1,"list","Tea FrontEnd Editor","tea_teafrontendeditor"," - - - - - - 2 - - - - - " +,"uid","pid","CType","header","pi_flexform" +,1,1,"tea_teafrontendeditor","Tea FrontEnd Editor"," + + + + + + 2 + + + + + " diff --git a/Tests/Functional/Controller/Fixtures/Database/TeaController/ContentElements.csv b/Tests/Functional/Controller/Fixtures/Database/TeaController/ContentElements.csv index c4f2e6125..61a86d7e0 100644 --- a/Tests/Functional/Controller/Fixtures/Database/TeaController/ContentElements.csv +++ b/Tests/Functional/Controller/Fixtures/Database/TeaController/ContentElements.csv @@ -1,6 +1,6 @@ "tt_content" -,"uid","pid","CType","header","list_type","pi_flexform" -,1,1,"list","Tea index","tea_teaindex"," +,"uid","pid","CType","header","pi_flexform" +,1,1,"tea_teaindex","Tea index"," @@ -18,4 +18,4 @@ " -,2,3,"list","Tea show","tea_teashow","" +,2,3,"tea_teashow","Tea show","" diff --git a/Tests/Functional/Upgrades/Fixtures/PluginAsCType.csv b/Tests/Functional/Upgrades/Fixtures/PluginAsCType.csv new file mode 100644 index 000000000..a7ccef90e --- /dev/null +++ b/Tests/Functional/Upgrades/Fixtures/PluginAsCType.csv @@ -0,0 +1,4 @@ +"tt_content" +,"uid","pid","CType","list_type" +,1,2,"tea_teaindex","" +,2,4,"tea_teashow","" diff --git a/Tests/Functional/Upgrades/Fixtures/PluginAsListType.csv b/Tests/Functional/Upgrades/Fixtures/PluginAsListType.csv new file mode 100644 index 000000000..614003a60 --- /dev/null +++ b/Tests/Functional/Upgrades/Fixtures/PluginAsListType.csv @@ -0,0 +1,4 @@ +"tt_content" +,"uid","pid","CType","list_type" +,1,2,"list","tea_teaindex" +,2,4,"list","tea_teashow" diff --git a/Tests/Functional/Upgrades/ListTypeToCTypeUpdateTest.php b/Tests/Functional/Upgrades/ListTypeToCTypeUpdateTest.php new file mode 100644 index 000000000..85a7752e4 --- /dev/null +++ b/Tests/Functional/Upgrades/ListTypeToCTypeUpdateTest.php @@ -0,0 +1,75 @@ +subject = $this->get(ListTypeToCTypeUpdate::class); + } + + #[Test] + public function isUpgradeWizard(): void + { + self::assertInstanceOf(AbstractListTypeToCTypeUpdate::class, $this->subject); + } + + #[Test] + public function hasDescription(): void + { + $expected = 'Migrates tea_index, tea_show, tea_front_end_editor from list_type to CType.'; + self::assertSame($expected, $this->subject->getDescription()); + } + + #[Test] + public function hasTitle(): void + { + $expected = 'Migrates tea extension plugins'; + self::assertSame($expected, $this->subject->getTitle()); + } + + #[Test] + public function hasListTypeToCTypeMapping(): void + { + + $expected = [ + 'tea_teaindex' => 'tea_teaindex', + 'tea_teashow' => 'tea_teashow', + 'tea_teafront_end_editor' => 'tea_teafront_end_editor', + ]; + self::assertSame($expected, $this->subject->getListTypeToCTypeMapping()); + } + + #[Test] + public function executeUpdate(): void + { + $this->importCSVDataSet( + self::FIXTURES_PREFIX . 'PluginAsListType.csv' + ); + $result = $this->subject->executeUpdate(); + $this->assertCSVDataSet( + self::FIXTURES_PREFIX . 'PluginAsCType.csv' + ); + } + +} diff --git a/composer.json b/composer.json index 7d19062e9..f95000a40 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,8 @@ "typo3/cms-core": "^12.4.41", "typo3/cms-extbase": "^12.4.41 || ^13.4", "typo3/cms-fluid": "^12.4.41 || ^13.4", - "typo3/cms-frontend": "^12.4.41 || ^13.4" + "typo3/cms-frontend": "^12.4.41 || ^13.4", + "typo3/cms-install": "^12.4.31 || ^13.4" }, "require-dev": { "ergebnis/composer-normalize": "2.49.0", diff --git a/ext_localconf.php b/ext_localconf.php index 76fb8c796..9296f0f69 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -4,6 +4,7 @@ use TTN\Tea\Controller\FrontEndEditorController; use TTN\Tea\Controller\TeaController; +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Extbase\Utility\ExtensionUtility; defined('TYPO3') or die('Access denied.'); @@ -21,7 +22,8 @@ // non-cacheable actions [ TeaController::class => '', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); ExtensionUtility::configurePlugin( 'Tea', @@ -31,7 +33,8 @@ ], [ TeaController::class => '', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); // This makes the plugin available for front-end rendering. @@ -49,5 +52,8 @@ // All actions need to be non-cacheable because they either contain dynamic data, // or because they are specific to the logged-in FE user (while FE content is cached by FE groups). FrontEndEditorController::class => 'index, edit, update, create, new, delete', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); + +ExtensionManagementUtility::addPageTSConfig('@import \'EXT:tea/Configuration/TSconfig/Page/ContentElementWizard.tsconfig\''); From 158f380114f76a9345108faf2571d518f65b1d0f Mon Sep 17 00:00:00 2001 From: Eike Starkmann Date: Mon, 2 Feb 2026 16:31:26 +0100 Subject: [PATCH 2/3] [TASK] remove upgrade wizard and tests This will be an extra pr Related: #1332 --- .../AbstractListTypeToCTypeUpdate.php | 287 ------------------ Classes/Upgrades/ListTypeToCTypeUpdate.php | 30 -- .../Upgrades/Fixtures/PluginAsCType.csv | 4 - .../Upgrades/Fixtures/PluginAsListType.csv | 4 - .../Upgrades/ListTypeToCTypeUpdateTest.php | 75 ----- 5 files changed, 400 deletions(-) delete mode 100644 Classes/Upgrades/AbstractListTypeToCTypeUpdate.php delete mode 100644 Classes/Upgrades/ListTypeToCTypeUpdate.php delete mode 100644 Tests/Functional/Upgrades/Fixtures/PluginAsCType.csv delete mode 100644 Tests/Functional/Upgrades/Fixtures/PluginAsListType.csv delete mode 100644 Tests/Functional/Upgrades/ListTypeToCTypeUpdateTest.php diff --git a/Classes/Upgrades/AbstractListTypeToCTypeUpdate.php b/Classes/Upgrades/AbstractListTypeToCTypeUpdate.php deleted file mode 100644 index c56f6c1e5..000000000 --- a/Classes/Upgrades/AbstractListTypeToCTypeUpdate.php +++ /dev/null @@ -1,287 +0,0 @@ -validateRequirements(); - } - - /** - * This must return an array containing the "list_type" to "CType" mapping - * - * Example: - * - * [ - * 'pi_plugin1' => 'pi_plugin1', - * 'pi_plugin2' => 'new_content_element', - * ] - * - * @return array - */ - abstract protected function getListTypeToCTypeMapping(): array; - - abstract public function getTitle(): string; - - abstract public function getDescription(): string; - - public function getPrerequisites(): array - { - return [ - DatabaseUpdatedPrerequisite::class, - ]; - } - - public function updateNecessary(): bool - { - return ( - $this->getListTypeToCTypeMapping() !== [] && - $this->columnsExistInContentTable() && - $this->hasContentElementsToUpdate() - ) - || ( - $this->getListTypeToCTypeMapping() !== [] && - $this->columnsExistInBackendUserGroupsTable() - && $this->hasNoLegacyBackendGroupsExplicitAllowDenyConfiguration() - && $this->hasBackendUserGroupsToUpdate() - ); - } - - public function executeUpdate(): bool - { - if ($this->getListTypeToCTypeMapping() !== [] && - $this->columnsExistInContentTable() && - $this->hasContentElementsToUpdate() - ) { - $this->updateContentElements(); - } - if ($this->getListTypeToCTypeMapping() !== [] && - $this->columnsExistInBackendUserGroupsTable() - && $this->hasNoLegacyBackendGroupsExplicitAllowDenyConfiguration() - && $this->hasBackendUserGroupsToUpdate() - ) { - $this->updateBackendUserGroups(); - } - - return true; - } - - protected function columnsExistInContentTable(): bool - { - $schemaManager = $this->connectionPool - ->getConnectionForTable(self::TABLE_CONTENT) - ->createSchemaManager(); - - $tableColumnNames = array_flip( - array_map( - static fn(Column $column) => $column->getName(), - $schemaManager->listTableColumns(self::TABLE_CONTENT), - ), - ); - - foreach (['CType', 'list_type'] as $column) { - if (!isset($tableColumnNames[$column])) { - return false; - } - } - - return true; - } - - protected function columnsExistInBackendUserGroupsTable(): bool - { - $schemaManager = $this->connectionPool - ->getConnectionForTable(self::TABLE_BACKEND_USER_GROUPS) - ->createSchemaManager(); - - return isset($schemaManager->listTableColumns(self::TABLE_BACKEND_USER_GROUPS)['explicit_allowdeny']); - } - - protected function hasContentElementsToUpdate(): bool - { - $listTypesToUpdate = array_keys($this->getListTypeToCTypeMapping()); - - $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_CONTENT); - $queryBuilder->getRestrictions()->removeAll(); - $queryBuilder - ->count('uid') - ->from(self::TABLE_CONTENT) - ->where( - $queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('list')), - $queryBuilder->expr()->in( - 'list_type', - $queryBuilder->createNamedParameter($listTypesToUpdate, Connection::PARAM_STR_ARRAY), - ), - ); - - return (bool)$queryBuilder->executeQuery()->fetchOne(); - } - - protected function hasBackendUserGroupsToUpdate(): bool - { - $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_BACKEND_USER_GROUPS); - $queryBuilder->getRestrictions()->removeAll(); - - $searchConstraints = []; - foreach ($this->getListTypeToCTypeMapping() as $listTyp) { - $searchConstraints[] = $queryBuilder->expr()->like( - 'explicit_allowdeny', - $queryBuilder->createNamedParameter( - '%' . $queryBuilder->escapeLikeWildcards('tt_content:list_type:' . $listTyp) . '%', - ), - ); - } - - $queryBuilder - ->count('uid') - ->from(self::TABLE_BACKEND_USER_GROUPS) - ->where( - $queryBuilder->expr()->or(...$searchConstraints), - ); - - return (bool)$queryBuilder->executeQuery()->fetchOne(); - } - - /** - * Returns true, if no legacy explicit_allowdeny be_groups configuration is found. Note, that we can not rely - * BackendGroupsExplicitAllowDenyMigration status here, since the update must also be executed for new - * TYPO3 v13+ installations, where BackendGroupsExplicitAllowDenyMigration is not required. - */ - protected function hasNoLegacyBackendGroupsExplicitAllowDenyConfiguration(): bool - { - $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_BACKEND_USER_GROUPS); - $queryBuilder->getRestrictions()->removeAll(); - $queryBuilder - ->count('uid') - ->from(self::TABLE_BACKEND_USER_GROUPS) - ->where( - $queryBuilder->expr()->like( - 'explicit_allowdeny', - $queryBuilder->createNamedParameter( - '%ALLOW%', - ), - ), - ); - /** @phpstan-ignore cast.int */ - return (int)$queryBuilder->executeQuery()->fetchOne() === 0; - } - /** @phpstan-ignore missingType.iterableValue */ - protected function getContentElementsToUpdate(string $listType): array - { - $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_CONTENT); - $queryBuilder->getRestrictions()->removeAll(); - $queryBuilder - ->select('uid') - ->from(self::TABLE_CONTENT) - ->where( - $queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('list')), - $queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter($listType)), - ); - - return $queryBuilder->executeQuery()->fetchAllAssociative(); - } - /** @phpstan-ignore missingType.iterableValue */ - protected function getBackendUserGroupsToUpdate(string $listType): array - { - $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_BACKEND_USER_GROUPS); - $queryBuilder->getRestrictions()->removeAll(); - $queryBuilder - ->select('uid', 'explicit_allowdeny') - ->from(self::TABLE_BACKEND_USER_GROUPS) - ->where( - $queryBuilder->expr()->like( - 'explicit_allowdeny', - $queryBuilder->createNamedParameter( - '%' . $queryBuilder->escapeLikeWildcards('tt_content:list_type:' . $listType) . '%', - ), - ), - ); - return $queryBuilder->executeQuery()->fetchAllAssociative(); - } - - protected function updateContentElements(): void - { - $connection = $this->connectionPool->getConnectionForTable(self::TABLE_CONTENT); - - foreach ($this->getListTypeToCTypeMapping() as $listType => $contentType) { - foreach ($this->getContentElementsToUpdate($listType) as $record) { - $connection->update( - self::TABLE_CONTENT, - [ - 'CType' => $contentType, - 'list_type' => '', - ], - ['uid' => (int)$record['uid']], - ); - } - } - } - - protected function updateBackendUserGroups(): void - { - $connection = $this->connectionPool->getConnectionForTable(self::TABLE_BACKEND_USER_GROUPS); - - foreach ($this->getListTypeToCTypeMapping() as $listType => $contentType) { - foreach ($this->getBackendUserGroupsToUpdate($listType) as $record) { - $fields = GeneralUtility::trimExplode(',', $record['explicit_allowdeny'], true); - foreach ($fields as $key => $field) { - if ($field === 'tt_content:list_type:' . $listType) { - unset($fields[$key]); - $fields[] = 'tt_content:CType:' . $contentType; - } - } - - $connection->update( - self::TABLE_BACKEND_USER_GROUPS, - [ - 'explicit_allowdeny' => implode(',', array_unique($fields)), - ], - ['uid' => (int)$record['uid']], - ); - } - } - } - - private function validateRequirements(): void - { - if ($this->getTitle() === '') { - throw new \RuntimeException('The update class "' . static::class . '" must provide a title by extending "getTitle()"', 1727605675); - } - if ($this->getDescription() === '') { - throw new \RuntimeException('The update class "' . static::class . '" must provide a description by extending "getDescription()"', 1727605676); - } - if ($this->getListTypeToCTypeMapping() === []) { - throw new \RuntimeException('The update class "' . static::class . '" does not provide a "list_type" to "CType" migration mapping', 1727605677); - } - - foreach ($this->getListTypeToCTypeMapping() as $listType => $contentElement) { - if (!is_string($listType) || $listType === '') { - throw new \RuntimeException('Invalid mapping item "' . $listType . '" in class "' . static::class, 1727605678); - } - if (!is_string($contentElement) || $contentElement === '') { - throw new \RuntimeException('Invalid mapping item "' . $contentElement . '" in class "' . static::class, 1727605679); - } - } - } -} diff --git a/Classes/Upgrades/ListTypeToCTypeUpdate.php b/Classes/Upgrades/ListTypeToCTypeUpdate.php deleted file mode 100644 index 42e0305f4..000000000 --- a/Classes/Upgrades/ListTypeToCTypeUpdate.php +++ /dev/null @@ -1,30 +0,0 @@ - 'tea_teaindex', - 'tea_teashow' => 'tea_teashow', - 'tea_teafront_end_editor' => 'tea_teafront_end_editor', - ]; - } - - public function getTitle(): string - { - return 'Migrates tea extension plugins'; - } - - public function getDescription(): string - { - return 'Migrates tea_index, tea_show, tea_front_end_editor from list_type to CType.'; - } -} diff --git a/Tests/Functional/Upgrades/Fixtures/PluginAsCType.csv b/Tests/Functional/Upgrades/Fixtures/PluginAsCType.csv deleted file mode 100644 index a7ccef90e..000000000 --- a/Tests/Functional/Upgrades/Fixtures/PluginAsCType.csv +++ /dev/null @@ -1,4 +0,0 @@ -"tt_content" -,"uid","pid","CType","list_type" -,1,2,"tea_teaindex","" -,2,4,"tea_teashow","" diff --git a/Tests/Functional/Upgrades/Fixtures/PluginAsListType.csv b/Tests/Functional/Upgrades/Fixtures/PluginAsListType.csv deleted file mode 100644 index 614003a60..000000000 --- a/Tests/Functional/Upgrades/Fixtures/PluginAsListType.csv +++ /dev/null @@ -1,4 +0,0 @@ -"tt_content" -,"uid","pid","CType","list_type" -,1,2,"list","tea_teaindex" -,2,4,"list","tea_teashow" diff --git a/Tests/Functional/Upgrades/ListTypeToCTypeUpdateTest.php b/Tests/Functional/Upgrades/ListTypeToCTypeUpdateTest.php deleted file mode 100644 index 85a7752e4..000000000 --- a/Tests/Functional/Upgrades/ListTypeToCTypeUpdateTest.php +++ /dev/null @@ -1,75 +0,0 @@ -subject = $this->get(ListTypeToCTypeUpdate::class); - } - - #[Test] - public function isUpgradeWizard(): void - { - self::assertInstanceOf(AbstractListTypeToCTypeUpdate::class, $this->subject); - } - - #[Test] - public function hasDescription(): void - { - $expected = 'Migrates tea_index, tea_show, tea_front_end_editor from list_type to CType.'; - self::assertSame($expected, $this->subject->getDescription()); - } - - #[Test] - public function hasTitle(): void - { - $expected = 'Migrates tea extension plugins'; - self::assertSame($expected, $this->subject->getTitle()); - } - - #[Test] - public function hasListTypeToCTypeMapping(): void - { - - $expected = [ - 'tea_teaindex' => 'tea_teaindex', - 'tea_teashow' => 'tea_teashow', - 'tea_teafront_end_editor' => 'tea_teafront_end_editor', - ]; - self::assertSame($expected, $this->subject->getListTypeToCTypeMapping()); - } - - #[Test] - public function executeUpdate(): void - { - $this->importCSVDataSet( - self::FIXTURES_PREFIX . 'PluginAsListType.csv' - ); - $result = $this->subject->executeUpdate(); - $this->assertCSVDataSet( - self::FIXTURES_PREFIX . 'PluginAsCType.csv' - ); - } - -} From 45941cd41327a0a533569dd97dff4d261b45112a Mon Sep 17 00:00:00 2001 From: Eike Starkmann Date: Mon, 2 Feb 2026 16:41:06 +0100 Subject: [PATCH 3/3] [TASK] remove unused dependencies Related: #1332 --- composer.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/composer.json b/composer.json index f95000a40..3d91fd17b 100644 --- a/composer.json +++ b/composer.json @@ -43,9 +43,7 @@ "psr/http-message": "^1.0.1", "typo3/cms-core": "^12.4.41", "typo3/cms-extbase": "^12.4.41 || ^13.4", - "typo3/cms-fluid": "^12.4.41 || ^13.4", - "typo3/cms-frontend": "^12.4.41 || ^13.4", - "typo3/cms-install": "^12.4.31 || ^13.4" + "typo3/cms-frontend": "^12.4.41 || ^13.4" }, "require-dev": { "ergebnis/composer-normalize": "2.49.0",