diff --git a/packages/typo3-fractor/config/typo3-13.php b/packages/typo3-fractor/config/typo3-13.php index cdb7b27f..cc3232a4 100644 --- a/packages/typo3-fractor/config/typo3-13.php +++ b/packages/typo3-fractor/config/typo3-13.php @@ -4,6 +4,7 @@ use a9f\Typo3Fractor\TYPO3v13\TypoScript\MigrateIncludeTypoScriptSyntaxFractor; use a9f\Typo3Fractor\TYPO3v13\TypoScript\RemovePageDoktypeRecyclerFromUserTsConfigFractor; +use a9f\Typo3Fractor\TYPO3v13\TypoScript\MigratePluginContentElementAndPluginSubtypesFractor; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { @@ -14,4 +15,5 @@ $services->set(MigrateIncludeTypoScriptSyntaxFractor::class); $services->set(RemovePageDoktypeRecyclerFromUserTsConfigFractor::class); + $services->set(MigratePluginContentElementAndPluginSubtypesFractor::class); }; diff --git a/packages/typo3-fractor/docs/typo3-fractor-rules.md b/packages/typo3-fractor/docs/typo3-fractor-rules.md index d953bf25..133b9c00 100644 --- a/packages/typo3-fractor/docs/typo3-fractor-rules.md +++ b/packages/typo3-fractor/docs/typo3-fractor-rules.md @@ -316,6 +316,122 @@ Migrate password and salted password to password type
+## MigratePluginContentElementAndPluginSubtypesFractorChatGPT + +Migrate plugin content element and plugin subtypes (list_type) + +- class: [`a9f\Typo3Fractor\TYPO3v13\TypoScript\MigratePluginContentElementAndPluginSubtypesFractorChatGPT`](../rules/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractorChatGPT.php) + +```diff +-tt_content.list.20.examples_pi1 = USER +-tt_content.list.20.examples_pi1 { +- userFunc = MyVendor\Examples\Controller\ExampleController->example ++tt_content.examples_pi1 =< lib.contentElement ++tt_content.examples_pi1 { ++ 20 = USER ++ 20 { ++ userFunc = MyVendor\Examples\Controller\ExampleController->example ++ } ++ templateName = Generic + } + +-tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1 ++tt_content.examples_pi1.20 < plugin.tx_examples_pi1 +``` + +
+ +```diff +-tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1 ++tt_content.examples_pi1.20 < plugin.tx_examples_pi1 +``` + +
+ +## MigratePluginContentElementAndPluginSubtypesFractorGeminiV1 + +Migrate plugin content element and plugin subtypes (list_type) + +- class: [`a9f\Typo3Fractor\TYPO3v13\TypoScript\MigratePluginContentElementAndPluginSubtypesFractorGeminiV1`](../rules/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractorGeminiV1.php) + +```diff +-tt_content.list.20.examples_pi1 = USER +-tt_content.list.20.examples_pi1 { +- userFunc = MyVendor\Examples\Controller\ExampleController->example ++tt_content.examples_pi1 =< lib.contentElement ++tt_content.examples_pi1 { ++ 20 = USER ++ 20 { ++ userFunc = MyVendor\Examples\Controller\ExampleController->example ++ } ++ templateName = Generic + } + +-tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1 ++tt_content.examples_pi1.20 < plugin.tx_examples_pi1 +``` + +
+ +```diff +-tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1 ++tt_content.examples_pi1.20 < plugin.tx_examples_pi1 +``` + +
+ +## MigratePluginContentElementAndPluginSubtypesFractorGeminiV2 + +Migrate plugin content element and plugin subtypes (list_type) + +- class: [`a9f\Typo3Fractor\TYPO3v13\TypoScript\MigratePluginContentElementAndPluginSubtypesFractorGeminiV2`](../rules/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractorGeminiV2.php) + +```diff +-tt_content.list.20.examples_pi1 = USER +-tt_content.list.20.examples_pi1 { +- userFunc = MyVendor\Examples\Controller\ExampleController->example ++tt_content.examples_pi1 =< lib.contentElement ++tt_content.examples_pi1 { ++ 20 = USER ++ 20 { ++ userFunc = MyVendor\Examples\Controller\ExampleController->example ++ } ++ templateName = Generic + } + +-tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1 ++tt_content.examples_pi1.20 < plugin.tx_examples_pi1 +``` + +
+ +```diff +-tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1 ++tt_content.examples_pi1.20 < plugin.tx_examples_pi1 +``` + +
+ +## MigratePluginContentElementAndPluginSubtypesFractor + +Migrate plugin content element and plugin subtypes (list_type) + +- class: [`a9f\Typo3Fractor\TYPO3v13\TypoScript\MigratePluginContentElementAndPluginSubtypesFractor`](../rules/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor.php) + +```diff +-tt_content.list.20.examples_pi1 = USER +-tt_content.list.20.examples_pi1 { ++tt_content.examples_pi1 = USER ++tt_content.examples_pi1 { + userFunc = MyVendor\Examples\Controller\ExampleController->example + } + +-tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1 ++tt_content.examples_pi1 < plugin.tx_examples_pi1 +``` + +
+ ## MigrateRenderTypeColorpickerToTypeColorFlexFormFractor Migrate renderType colorpicker to type color diff --git a/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture.typoscript.fixture b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture.typoscript.fixture new file mode 100644 index 00000000..d92a46ae --- /dev/null +++ b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture.typoscript.fixture @@ -0,0 +1,13 @@ +tt_content.list.20.examples_pi1 = USER +tt_content.list.20.examples_pi1 { + userFunc = MyVendor\Examples\Controller\ExampleController->example +} +----- +tt_content.examples_pi1 =< lib.contentElement +tt_content.examples_pi1 { + 20 = USER + 20 { + userFunc = MyVendor\Examples\Controller\ExampleController->example + } + templateName = Generic +} diff --git a/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_copy_tt_content.typoscript.fixture b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_copy_tt_content.typoscript.fixture new file mode 100644 index 00000000..bc21a5dd --- /dev/null +++ b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_copy_tt_content.typoscript.fixture @@ -0,0 +1,3 @@ +temp.example < tt_content.list.20.examples_pi1 +----- +temp.example < tt_content.examples_pi1.20 diff --git a/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_mixed_nested.typoscript.fixture b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_mixed_nested.typoscript.fixture new file mode 100644 index 00000000..e767bd61 --- /dev/null +++ b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_mixed_nested.typoscript.fixture @@ -0,0 +1,15 @@ +tt_content { + list.20 { + examples_pi1 { + userFunc = MyVendor\Examples\Controller\ExampleController->example + } + } +} +----- +tt_content { + examples_pi1 { + 20 { + userFunc = MyVendor\Examples\Controller\ExampleController->example + } + } +} diff --git a/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_nested.typoscript.fixture b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_nested.typoscript.fixture new file mode 100644 index 00000000..1346f289 --- /dev/null +++ b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_nested.typoscript.fixture @@ -0,0 +1,17 @@ +tt_content { + list { + 20 { + examples_pi1 { + userFunc = MyVendor\Examples\Controller\ExampleController->example + } + } + } +} +----- +tt_content { + examples_pi1 { + 20 { + userFunc = MyVendor\Examples\Controller\ExampleController->example + } + } +} diff --git a/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_plugin_top_level.typoscript.fixture b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_plugin_top_level.typoscript.fixture new file mode 100644 index 00000000..81f350b8 --- /dev/null +++ b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_plugin_top_level.typoscript.fixture @@ -0,0 +1,5 @@ +tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1 +----- +tt_content.examples_pi1 =< lib.contentElement +tt_content.examples_pi1.20 < plugin.tx_examples_pi1 +tt_content.examples_pi1.templateName = Generic diff --git a/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_plugin_top_level_reference.typoscript.fixture b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_plugin_top_level_reference.typoscript.fixture new file mode 100644 index 00000000..c6849beb --- /dev/null +++ b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_plugin_top_level_reference.typoscript.fixture @@ -0,0 +1,5 @@ +tt_content.list.20.examples_pi1 =< plugin.tx_examples_pi1 +----- +tt_content.examples_pi1 =< lib.contentElement +tt_content.examples_pi1.20 =< plugin.tx_examples_pi1 +tt_content.examples_pi1.templateName = Generic diff --git a/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_reference_tt_content.typoscript.fixture b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_reference_tt_content.typoscript.fixture new file mode 100644 index 00000000..fbd1a32f --- /dev/null +++ b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/Fixtures/fixture_reference_tt_content.typoscript.fixture @@ -0,0 +1,3 @@ +temp.example =< tt_content.list.20.examples_pi1 +----- +temp.example =< tt_content.examples_pi1.20 diff --git a/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/MigratePluginContentElementAndPluginSubtypesFractorTest.php b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/MigratePluginContentElementAndPluginSubtypesFractorTest.php new file mode 100644 index 00000000..038aee38 --- /dev/null +++ b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/MigratePluginContentElementAndPluginSubtypesFractorTest.php @@ -0,0 +1,27 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixtures', '*.typoscript.fixture'); + } + + public function provideConfigFilePath(): ?string + { + return __DIR__ . '/config/fractor.php'; + } +} diff --git a/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/config/fractor.php b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/config/fractor.php new file mode 100644 index 00000000..39d846b1 --- /dev/null +++ b/packages/typo3-fractor/rules-tests/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor/config/fractor.php @@ -0,0 +1,15 @@ +withOptions([ + XmlProcessorOption::INDENT_CHARACTER => Indent::STYLE_TAB, + XmlProcessorOption::INDENT_SIZE => 1, + ]) + ->withRules([MigratePluginContentElementAndPluginSubtypesFractor::class]); diff --git a/packages/typo3-fractor/rules/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor.php b/packages/typo3-fractor/rules/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor.php new file mode 100644 index 00000000..5a8c0a69 --- /dev/null +++ b/packages/typo3-fractor/rules/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor.php @@ -0,0 +1,166 @@ +example +} + +tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1 +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +tt_content.examples_pi1 =< lib.contentElement +tt_content.examples_pi1 { + 20 = USER + 20 { + userFunc = MyVendor\Examples\Controller\ExampleController->example + } + templateName = Generic +} + +tt_content.examples_pi1.20 < plugin.tx_examples_pi1 +CODE_SAMPLE + ), new CodeSample( + <<<'CODE_SAMPLE' +tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1 +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +tt_content.examples_pi1.20 < plugin.tx_examples_pi1 +CODE_SAMPLE + )]); + } + + public function refactor(Statement $statement): null|Statement|int + { + if ($this->shouldSkip($statement)) { + return null; + } + + if ($statement instanceof NestedAssignment) { + $rootLine = ['tt_content', 'list', '20', '#']; + /** @var NestedAssignment|null $pluginStatement */ + $pluginStatement = $this->findPluginStatement($statement, $rootLine); + if ($pluginStatement === null) { + return null; + } + + $pluginStatement->object->absoluteName = str_replace( + 'list.20.', + '', + $pluginStatement->object->absoluteName + ); + $pluginStatement->object->relativeName = str_replace( + 'list.20.', + '', + $pluginStatement->object->relativeName + ); + + if ($pluginStatement->object->absoluteName === $pluginStatement->object->relativeName) { + return $pluginStatement; + } + $statement->statements[0] = $pluginStatement; + } elseif ($statement instanceof Assignment) { + $statement->object->absoluteName = str_replace('list.20.', '', $statement->object->absoluteName); + $statement->object->relativeName = str_replace('list.20.', '', $statement->object->relativeName); + + $objectPathLeft = $this->builder->path( + str_replace('list.20.', '', $statement->object->absoluteName), + str_replace('list.20.', '', $statement->object->relativeName) + ); + $objectPathRight = $this->builder->path('tt_content.lib.contentElement', 'lib.contentElement'); + + $reference = new Reference($objectPathLeft, $objectPathRight, $statement->sourceLine); + + } elseif ($statement instanceof Copy || $statement instanceof Reference) { + $statement->object->absoluteName = str_replace('list.20.', '', $statement->object->absoluteName); + $statement->object->relativeName = str_replace('list.20.', '', $statement->object->relativeName); + + $statement->target->absoluteName = str_replace('list.20.', '', $statement->target->absoluteName); + $statement->target->relativeName = str_replace('list.20.', '', $statement->target->relativeName); + } + + return $statement; + } + + private function shouldSkip(Statement $statement): bool + { + if (! $statement instanceof NestedAssignment + && ! $statement instanceof Assignment + && ! $statement instanceof Copy + && ! $statement instanceof Reference + ) { + return true; + } + + if (($statement instanceof Copy || $statement instanceof Reference)) { + return ! str_starts_with($statement->target->relativeName, 'tt_content') + && ! str_starts_with($statement->object->absoluteName, 'tt_content'); + } + + if (! str_starts_with($statement->object->absoluteName, 'tt_content')) { + return true; + } + + return false; + } + + /** + * @param array $rootLine + */ + private function findPluginStatement(Statement $statement, array $rootLine): ?Statement + { + if (! $statement instanceof NestedAssignment) { + return null; + } + + if (! isset($rootLine[0])) { + return $statement; + } + + $objectPath = $statement->object->relativeName; + $parts = explode('.', $objectPath); + foreach ($parts as $part) { + $firstRootLineItem = $rootLine[0]; + if ($firstRootLineItem === $part || $firstRootLineItem === '#') { + array_shift($rootLine); + } + } + + if ($rootLine === []) { + // we found it! + return $statement; + } + + return $this->findPluginStatement($statement->statements[0], $rootLine); + } +}