From e4105acedf6265f5979dee13d428eeb8667290d7 Mon Sep 17 00:00:00 2001
From: Nikita Hovratov <nikita.h@live.de>
Date: Sat, 9 Dec 2023 21:55:25 +0100
Subject: [PATCH] [TASK] Extract registration of controller actions into
 separate method

EU::configurePlugin tightly couples registration of controller actions
and TypoScript generation for a frontend rendering definition.
Furthermore, lib.contentElement is only defined in fluid_styled_content,
which makes it an indirect requirement.

This patch extracts the part for the controller action registration, so
it can be used independently of fluid_styled_content. In addition, this
new method expects the controller actions to have an array shape
already. A converter method ensures compatibility for both string and
array syntax.

A concrete use-case for internal usage would be Content Blocks, as it
would be possible to separately define a Content Block of type "Plugin"
and in addition register controller actions for it.

Resolves: #102643
Releases: main, 12.4
Change-Id: I74d84f54bdd399934b57b3e49e2209f62b5fda68
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/82181
Reviewed-by: Oliver Bartsch <bo@cedev.de>
Tested-by: Oliver Bartsch <bo@cedev.de>
Tested-by: core-ci <typo3@b13.com>
---
 .../Classes/Utility/ExtensionUtility.php      | 61 ++++++++++++++-----
 .../Unit/Utility/ExtensionUtilityTest.php     | 39 ++++++++++++
 2 files changed, 84 insertions(+), 16 deletions(-)

diff --git a/typo3/sysext/extbase/Classes/Utility/ExtensionUtility.php b/typo3/sysext/extbase/Classes/Utility/ExtensionUtility.php
index 9fc7beabd11b..aba43e713d57 100644
--- a/typo3/sysext/extbase/Classes/Utility/ExtensionUtility.php
+++ b/typo3/sysext/extbase/Classes/Utility/ExtensionUtility.php
@@ -52,23 +52,10 @@ class ExtensionUtility
         $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
 
         $pluginSignature = strtolower($extensionName . '_' . $pluginName);
-        if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName] ?? false)) {
-            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName] = [];
-        }
-        foreach ($controllerActions as $controllerClassName => $actionsList) {
-            $controllerAlias = self::resolveControllerAliasFromControllerClassName($controllerClassName);
 
-            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerClassName] = [
-                'className' => $controllerClassName,
-                'alias' => $controllerAlias,
-                'actions' => GeneralUtility::trimExplode(',', (string)$actionsList),
-            ];
-
-            if (!empty($nonCacheableControllerActions[$controllerClassName])) {
-                $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerClassName]['nonCacheableActions']
-                    = GeneralUtility::trimExplode(',', (string)$nonCacheableControllerActions[$controllerClassName]);
-            }
-        }
+        $controllerActions = self::actionCommaListToArray($controllerActions);
+        $nonCacheableControllerActions = self::actionCommaListToArray($nonCacheableControllerActions);
+        self::registerControllerActions($extensionName, $pluginName, $controllerActions, $nonCacheableControllerActions);
 
         switch ($pluginType) {
             case self::PLUGIN_TYPE_PLUGIN:
@@ -100,6 +87,32 @@ tt_content.' . $pluginSignature . ' {
 ' . $pluginContent, 'defaultContentRendering');
     }
 
+    /**
+     * @param array<string, string[]> $controllerActions
+     * @param array<string, string[]> $nonCacheableControllerActions
+     * @internal
+     */
+    public static function registerControllerActions(string $extensionName, string $pluginName, array $controllerActions, array $nonCacheableControllerActions): void
+    {
+        if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName] ?? false)) {
+            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName] = [];
+        }
+        foreach ($controllerActions as $controllerClassName => $actionsList) {
+            $controllerAlias = self::resolveControllerAliasFromControllerClassName($controllerClassName);
+
+            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerClassName] = [
+                'className' => $controllerClassName,
+                'alias' => $controllerAlias,
+                'actions' => $actionsList,
+            ];
+
+            if (!empty($nonCacheableControllerActions[$controllerClassName])) {
+                $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerClassName]['nonCacheableActions']
+                    = $nonCacheableControllerActions[$controllerClassName];
+            }
+        }
+    }
+
     /**
      * Register an Extbase PlugIn into backend's list of plugins
      * FOR USE IN Configuration/TCA/Overrides/tt_content.php
@@ -199,6 +212,22 @@ tt_content.' . $pluginSignature . ' {
         );
     }
 
+    /**
+     * @param array<string, string|string[]> $controllerActions
+     * @return array<string, string[]>
+     */
+    protected static function actionCommaListToArray(array $controllerActions): array
+    {
+        foreach ($controllerActions as $controllerClassName => $actionsList) {
+            if (is_array($actionsList)) {
+                continue;
+            }
+            $actionsListArray = GeneralUtility::trimExplode(',', (string)$actionsList);
+            $controllerActions[$controllerClassName] = $actionsListArray;
+        }
+        return $controllerActions;
+    }
+
     /**
      * Register a type converter by class name.
      *
diff --git a/typo3/sysext/extbase/Tests/Unit/Utility/ExtensionUtilityTest.php b/typo3/sysext/extbase/Tests/Unit/Utility/ExtensionUtilityTest.php
index 6115f3d9fb23..0eda20b5b74f 100644
--- a/typo3/sysext/extbase/Tests/Unit/Utility/ExtensionUtilityTest.php
+++ b/typo3/sysext/extbase/Tests/Unit/Utility/ExtensionUtilityTest.php
@@ -202,6 +202,45 @@ final class ExtensionUtilityTest extends UnitTestCase
         self::assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
     }
 
+    /**
+     * @test
+     */
+    public function configurePluginWorksForMultipleControllerActionsAsArrayWithCacheableActionAsDefault(): void
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
+        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
+            FirstController::class => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
+            SecondController::class => ['index', 'show', 'delete'],
+            ThirdController::class => ['create'],
+        ], [
+            FirstController::class => ['new', 'create', 'edit', 'update'],
+            ThirdController::class => ['create'],
+        ]);
+        $expectedResult = [
+            'controllers' => [
+                FirstController::class => [
+                    'className' => FirstController::class,
+                    'alias' => 'First',
+                    'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
+                    'nonCacheableActions' => ['new', 'create', 'edit', 'update'],
+                ],
+                SecondController::class => [
+                    'className' => SecondController::class,
+                    'alias' => 'Second',
+                    'actions' => ['index', 'show', 'delete'],
+                ],
+                ThirdController::class => [
+                    'className' => ThirdController::class,
+                    'alias' => 'Third',
+                    'actions' => ['create'],
+                    'nonCacheableActions' => ['create'],
+                ],
+            ],
+            'pluginType' => 'list_type',
+        ];
+        self::assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
+    }
+
     /**
      * @test
      */
-- 
GitLab