From 115bff0a09fbd74e7e50c99465e5a8fcedf4fc78 Mon Sep 17 00:00:00 2001
From: Alexander Schnitzler <git@alexanderschnitzler.de>
Date: Thu, 26 Mar 2020 12:34:33 +0100
Subject: [PATCH] [BUGFIX] Fix several methods of ExtensionService

Due to a missing functional test and a legacy extension
configuration format in a unit test several issues with
methods in the ExtensionService class have not been
spotted.

Although the registration of plugins changed in that
manner that FQCN's are used instead of controller class
names, the internal handling in Extbase still relies on
controller names, which are internally named controller
aliases. The internal Extbase mechanics must still work
like before, passing the controller name around as this
is unique in the context of a plugin. Therefore it's not
necessary to expose or use the FQCN.

Releases: master
Resolves: #90822
Change-Id: Idd574bcbcddaa209337f73ad8526b2423ad9c786
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/63933
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Toben Schmidt <t4ss5t@posteo.de>
Tested-by: Daniel Goerz <daniel.goerz@posteo.de>
Reviewed-by: Toben Schmidt <t4ss5t@posteo.de>
Reviewed-by: Daniel Goerz <daniel.goerz@posteo.de>
---
 .../Classes/Service/ExtensionService.php      | 25 +++----
 .../Service/ExtensionServiceTest.php          | 66 +++++++++++++++++++
 .../Unit/Service/ExtensionServiceTest.php     | 12 ++--
 3 files changed, 88 insertions(+), 15 deletions(-)
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Service/ExtensionServiceTest.php

diff --git a/typo3/sysext/extbase/Classes/Service/ExtensionService.php b/typo3/sysext/extbase/Classes/Service/ExtensionService.php
index a14769166cd4..2f332816c87d 100644
--- a/typo3/sysext/extbase/Classes/Service/ExtensionService.php
+++ b/typo3/sysext/extbase/Classes/Service/ExtensionService.php
@@ -123,11 +123,14 @@ class ExtensionService implements \TYPO3\CMS\Core\SingletonInterface
         }
         $pluginNames = [];
         foreach ($plugins as $pluginName => $pluginConfiguration) {
-            foreach ($pluginConfiguration['controllers'] ?? [] as $pluginControllerName => $pluginControllerActions) {
+            $controllers = $pluginConfiguration['controllers'] ?? [];
+            $controllerAliases = array_column($controllers, 'actions', 'alias');
+
+            foreach ($controllerAliases as $pluginControllerName => $pluginControllerActions) {
                 if (strtolower($pluginControllerName) !== strtolower($controllerName)) {
                     continue;
                 }
-                if (in_array($actionName, $pluginControllerActions['actions'], true)) {
+                if (in_array($actionName, $pluginControllerActions, true)) {
                     $pluginNames[] = $pluginName;
                 }
             }
@@ -230,10 +233,10 @@ class ExtensionService implements \TYPO3\CMS\Core\SingletonInterface
      */
     public function getDefaultControllerNameByPlugin(string $extensionName, string $pluginName): ?string
     {
-        // todo: using false as a default is fishy.
-        // todo: rather make sure that $controllers is an array and then return the (string) key or null
-        $controllers = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'] ?? false;
-        return $controllers ? key($controllers) : null;
+        $controllers = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'] ?? [];
+        $controllerAliases = array_column($controllers, 'alias');
+        $defaultControllerName = (string)($controllerAliases[0] ?? '');
+        return $defaultControllerName !== '' ? $defaultControllerName : null;
     }
 
     /**
@@ -246,11 +249,11 @@ class ExtensionService implements \TYPO3\CMS\Core\SingletonInterface
      */
     public function getDefaultActionNameByPluginAndController(string $extensionName, string $pluginName, string $controllerName): ?string
     {
-        // todo: using false as a default is fishy.
-        // todo: rather make sure that $actions is an array and then return the (string) key or null
-        // todo: also use reset(), rather than current, as array pointer might have been moved.
-        $actions = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerName]['actions'] ?? false;
-        return $actions ? current($actions) : null;
+        $controllers = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'] ?? [];
+        $controllerActionsByAlias = array_column($controllers, 'actions', 'alias');
+        $actions = $controllerActionsByAlias[$controllerName] ?? [];
+        $defaultActionName = (string)($actions[0] ?? '');
+        return $defaultActionName !== '' ? $defaultActionName : null;
     }
 
     /**
diff --git a/typo3/sysext/extbase/Tests/Functional/Service/ExtensionServiceTest.php b/typo3/sysext/extbase/Tests/Functional/Service/ExtensionServiceTest.php
new file mode 100644
index 000000000000..c5f65f6c84a6
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Service/ExtensionServiceTest.php
@@ -0,0 +1,66 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Extbase\Tests\Functional\Service;
+
+/*
+ * 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!
+ */
+
+use Prophecy\Argument;
+use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
+use TYPO3\CMS\Extbase\Configuration\FrontendConfigurationManager;
+use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
+use TYPO3\CMS\Extbase\Service\EnvironmentService;
+use TYPO3\CMS\Extbase\Service\ExtensionService;
+use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
+
+class ExtensionServiceTest extends FunctionalTestCase
+{
+    /**
+     * @var array
+     */
+    protected $testExtensionsToLoad = ['typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example'];
+
+    /**
+     * @var array
+     */
+    protected $coreExtensionsToLoad = ['extbase', 'fluid'];
+
+    /**
+     * @test
+     */
+    public function getPluginNameByActionDetectsPluginNameFromGlobalExtensionConfigurationArray()
+    {
+        $environmentService = $this->prophesize(EnvironmentService::class);
+        $environmentService->isEnvironmentInFrontendMode()->willReturn(true);
+        $environmentService->isEnvironmentInBackendMode()->willReturn(false);
+        $environmentService = $environmentService->reveal();
+
+        $frontendConfigurationManager = $this->prophesize(FrontendConfigurationManager::class);
+        $frontendConfigurationManager->getConfiguration(Argument::cetera())->willReturn([]);
+
+        $objectManager = $this->prophesize(ObjectManagerInterface::class);
+        $objectManager->get(Argument::exact(FrontendConfigurationManager::class))->willReturn($frontendConfigurationManager->reveal());
+
+        $configurationManager = new ConfigurationManager(
+            $objectManager->reveal(),
+            $environmentService
+        );
+
+        $extensionService = new ExtensionService();
+        $extensionService->injectConfigurationManager($configurationManager);
+
+        $pluginName = $extensionService->getPluginNameByAction('BlogExample', 'Blog', 'testForm');
+
+        self::assertSame('Blogs', $pluginName);
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Unit/Service/ExtensionServiceTest.php b/typo3/sysext/extbase/Tests/Unit/Service/ExtensionServiceTest.php
index ea054535f17f..c508a0f509dd 100644
--- a/typo3/sysext/extbase/Tests/Unit/Service/ExtensionServiceTest.php
+++ b/typo3/sysext/extbase/Tests/Unit/Service/ExtensionServiceTest.php
@@ -61,14 +61,16 @@ class ExtensionServiceTest extends UnitTestCase
                 'plugins' => [
                     'SomePlugin' => [
                         'controllers' => [
-                            'ControllerName' => [
+                            'Fully\\Qualified\\ControllerName' => [
+                                'alias' => 'ControllerName',
                                 'actions' => ['index', 'otherAction']
                             ]
                         ]
                     ],
                     'ThirdPlugin' => [
                         'controllers' => [
-                            'ControllerName' => [
+                            'Fully\\Qualified\\ControllerName' => [
+                                'alias' => 'ControllerName',
                                 'actions' => ['otherAction', 'thirdAction']
                             ]
                         ]
@@ -79,11 +81,13 @@ class ExtensionServiceTest extends UnitTestCase
                 'plugins' => [
                     'SecondPlugin' => [
                         'controllers' => [
-                            'ControllerName' => [
+                            'Fully\\Qualified\\ControllerName' => [
+                                'alias' => 'ControllerName',
                                 'actions' => ['index', 'otherAction']
                             ],
-                            'SecondControllerName' => [
+                            'Fully\\Qualified\\SecondControllerName' => [
                                 'actions' => ['someAction', 'someOtherAction'],
+                                'alias' => 'SecondControllerName',
                                 'nonCacheableActions' => ['someOtherAction']
                             ]
                         ]
-- 
GitLab