diff --git a/typo3/sysext/extbase/Classes/Configuration/AbstractConfigurationManager.php b/typo3/sysext/extbase/Classes/Configuration/AbstractConfigurationManager.php index 01a91bea9bf9..d729231d88a6 100644 --- a/typo3/sysext/extbase/Classes/Configuration/AbstractConfigurationManager.php +++ b/typo3/sysext/extbase/Classes/Configuration/AbstractConfigurationManager.php @@ -222,18 +222,88 @@ public function getDefaultBackendStoragePid() } /** + * This method possibly overrides the controller configuration with an alternative configuration passed along via + * $frameworkConfiguration. + * + * If called by \TYPO3\CMS\Extbase\Configuration\AbstractConfigurationManager::getConfiguration, + * $switchableControllerActions may contain an alternative controller configuration defined via typoscript: + * + * Example: + * tt_content.list.20.indexedsearch_pi2.switchableControllerActions { + * Search { + * 0 = search + * } + * } + * + * If called by \TYPO3\CMS\Extbase\Configuration\FrontendConfigurationManager::overrideSwitchableControllerActionsFromFlexForm, + * $switchableControllerActions may contain an alternative controller configuration defined via plugin flexform. + * * @param array &$frameworkConfiguration * @param array $switchableControllerActions */ protected function overrideSwitchableControllerActions(array &$frameworkConfiguration, array $switchableControllerActions) { + $controllerAliasToClass = []; + foreach ($frameworkConfiguration['controllerConfiguration'] as $controllerClass => $controllerConfiguration) { + $controllerAliasToClass[$controllerConfiguration['alias']] = $controllerClass; + } + $overriddenSwitchableControllerActions = []; foreach ($switchableControllerActions as $controllerName => $actions) { - if (!isset($frameworkConfiguration['controllerConfiguration'][$controllerName])) { + // Trim leading backslashes if a fully qualified controller class name with leading slashes is used. + $controllerName = ltrim($controllerName, '\\'); + + $controllerIsConfigured = false; + if (array_key_exists($controllerName, $controllerAliasToClass)) { + /* + * If $controllerName can be found in the keys of $controllerAliasToClass, $controllerName is a + * controller alias and not a FQCN. In this case switchable controller actions have been defined with + * controller aliases as such: + * + * tt_content.list.20.indexedsearch_pi2.switchableControllerActions { + * Search { + * 0 = search + * } + * } + */ + $controllerIsConfigured = true; + $controllerClassName = $controllerAliasToClass[$controllerName]; + $controllerAlias = $controllerName; + } + + if (in_array($controllerName, $controllerAliasToClass, true)) { + /* + * If $controllerName can be found in the values of $controllerAliasToClass, $controllerName is a + * FQCN. In this case switchable controller actions have been defined with fully qualified controller + * class names as such: + * + * tt_content.list.20.indexedsearch_pi2.switchableControllerActions { + * TYPO3\CMS\IndexedSearch\Controller\SearchController { + * 0 = search + * } + * } + */ + $controllerIsConfigured = true; + $controllerClassName = $controllerName; + $controllerAlias = $frameworkConfiguration['controllerConfiguration'][$controllerName]['alias']; + } + + if (!$controllerIsConfigured) { continue; } - $overriddenSwitchableControllerActions[$controllerName] = ['actions' => $actions]; - $nonCacheableActions = $frameworkConfiguration['controllerConfiguration'][$controllerName]['nonCacheableActions'] ?? null; + + if (!isset($overriddenSwitchableControllerActions[$controllerClassName])) { + $overriddenSwitchableControllerActions[$controllerClassName] = [ + 'alias' => $controllerAlias, + 'className' => $controllerClassName, + 'actions' => [] + ]; + } + $overriddenSwitchableControllerActions[$controllerClassName]['actions'] = array_merge( + $overriddenSwitchableControllerActions[$controllerClassName]['actions'], + $actions + ); + $nonCacheableActions = $frameworkConfiguration['controllerConfiguration'][$controllerClassName]['nonCacheableActions'] ?? null; if (!is_array($nonCacheableActions)) { // There are no non-cacheable actions, thus we can directly continue // with the next controller name. @@ -241,7 +311,7 @@ protected function overrideSwitchableControllerActions(array &$frameworkConfigur } $overriddenNonCacheableActions = array_intersect($nonCacheableActions, $actions); if (!empty($overriddenNonCacheableActions)) { - $overriddenSwitchableControllerActions[$controllerName]['nonCacheableActions'] = $overriddenNonCacheableActions; + $overriddenSwitchableControllerActions[$controllerClassName]['nonCacheableActions'] = $overriddenNonCacheableActions; } } $frameworkConfiguration['controllerConfiguration'] = $overriddenSwitchableControllerActions; diff --git a/typo3/sysext/extbase/Tests/Unit/Configuration/AbstractConfigurationManagerTest.php b/typo3/sysext/extbase/Tests/Unit/Configuration/AbstractConfigurationManagerTest.php index 23ff0a3e38a7..833ece29d470 100644 --- a/typo3/sysext/extbase/Tests/Unit/Configuration/AbstractConfigurationManagerTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Configuration/AbstractConfigurationManagerTest.php @@ -106,10 +106,12 @@ class AbstractConfigurationManagerTest extends UnitTestCase * @var array */ protected $testSwitchableControllerActions = [ - 'Controller1' => [ + 'MyExtension\\Controller\\Controller1' => [ + 'alias' => 'Controller1', 'actions' => ['action1', 'action2', 'action3'] ], - 'Controller2' => [ + 'MyExtension\\Controller\\Controller2' => [ + 'alias' => 'Controller2', 'actions' => ['action4', 'action5', 'action6'], 'nonCacheableActions' => ['action4', 'action6'] ] @@ -506,7 +508,9 @@ public function orderOfActionsCanBeOverriddenForCurrentPlugin(): void })); $mergedConfiguration = $this->abstractConfigurationManager->getConfiguration(); $expectedResult = [ - 'Controller1' => [ + 'MyExtension\\Controller\\Controller1' => [ + 'className' => 'MyExtension\\Controller\\Controller1', + 'alias' => 'Controller1', 'actions' => ['action2', 'action1', 'action3'] ] ]; @@ -514,6 +518,52 @@ public function orderOfActionsCanBeOverriddenForCurrentPlugin(): void $this->assertEquals($expectedResult, $actualResult); } + /** + * @test + */ + public function controllerOfSwitchableControllerActionsCanBeAFullyQualifiedClassName(): void + { + $configuration = [ + 'extensionName' => 'CurrentExtensionName', + 'pluginName' => 'CurrentPluginName', + 'switchableControllerActions' => [ + 'MyExtension\\Controller\\Controller1' => ['action2', 'action1', 'action3'], + '\\MyExtension\\Controller\\Controller2' => ['newAction2', 'action4', 'action5'] + ] + ]; + $this->mockTypoScriptService->expects($this->any())->method('convertTypoScriptArrayToPlainArray')->with($configuration)->will($this->returnValue($configuration)); + $this->abstractConfigurationManager->setConfiguration($configuration); + $this->abstractConfigurationManager->expects($this->once())->method('getPluginConfiguration')->with( + 'CurrentExtensionName', + 'CurrentPluginName' + )->will($this->returnValue($this->testPluginConfiguration)); + $this->abstractConfigurationManager->expects($this->once())->method('getSwitchableControllerActions')->with( + 'CurrentExtensionName', + 'CurrentPluginName' + )->will($this->returnValue($this->testSwitchableControllerActions)); + $this->abstractConfigurationManager->expects($this->once())->method('getContextSpecificFrameworkConfiguration')->will($this->returnCallback(function ( + $a + ) { + return $a; + })); + $mergedConfiguration = $this->abstractConfigurationManager->getConfiguration(); + $expectedResult = [ + 'MyExtension\\Controller\\Controller1' => [ + 'className' => 'MyExtension\\Controller\\Controller1', + 'alias' => 'Controller1', + 'actions' => ['action2', 'action1', 'action3'] + ], + 'MyExtension\\Controller\\Controller2' => [ + 'className' => 'MyExtension\\Controller\\Controller2', + 'alias' => 'Controller2', + 'actions' => ['newAction2', 'action4', 'action5'], + 'nonCacheableActions' => ['action4'] + ] + ]; + $actualResult = $mergedConfiguration['controllerConfiguration']; + $this->assertEquals($expectedResult, $actualResult); + } + /** * @test */ @@ -543,7 +593,9 @@ public function newActionsCanBeAddedForCurrentPlugin(): void })); $mergedConfiguration = $this->abstractConfigurationManager->getConfiguration(); $expectedResult = [ - 'Controller1' => [ + 'MyExtension\\Controller\\Controller1' => [ + 'className' => 'MyExtension\\Controller\\Controller1', + 'alias' => 'Controller1', 'actions' => ['action2', 'action1', 'action3', 'newAction'] ] ]; @@ -614,10 +666,14 @@ public function cachingOfActionsCanNotBeChanged(): void })); $mergedConfiguration = $this->abstractConfigurationManager->getConfiguration(); $expectedResult = [ - 'Controller1' => [ + 'MyExtension\\Controller\\Controller1' => [ + 'className' => 'MyExtension\\Controller\\Controller1', + 'alias' => 'Controller1', 'actions' => ['newAction', 'action1'] ], - 'Controller2' => [ + 'MyExtension\\Controller\\Controller2' => [ + 'className' => 'MyExtension\\Controller\\Controller2', + 'alias' => 'Controller2', 'actions' => ['newAction2', 'action4', 'action5'], 'nonCacheableActions' => ['action4'] ] diff --git a/typo3/sysext/extbase/Tests/Unit/Configuration/FrontendConfigurationManagerTest.php b/typo3/sysext/extbase/Tests/Unit/Configuration/FrontendConfigurationManagerTest.php index 87ef21f60bb6..88be283b90cc 100644 --- a/typo3/sysext/extbase/Tests/Unit/Configuration/FrontendConfigurationManagerTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Configuration/FrontendConfigurationManagerTest.php @@ -305,26 +305,32 @@ public function overrideSwitchableControllerActionsFromFlexFormMergesNonCacheabl 'pluginName' => 'Pi1', 'extensionName' => 'SomeExtension', 'controllerConfiguration' => [ - 'Controller1' => [ + 'MyExtension\\Controller\\Controller1' => [ + 'alias' => 'Controller1', 'actions' => ['action1 , action2'] ], - 'Controller2' => [ + 'MyExtension\\Controller\\Controller2' => [ + 'alias' => 'Controller2', 'actions' => ['action2', 'action1', 'action3'], 'nonCacheableActions' => ['action2', 'action3'] ] ] ]; $flexFormConfiguration = [ - 'switchableControllerActions' => 'Controller1 -> action2;Controller2->action3; Controller2->action1' + 'switchableControllerActions' => 'Controller1 -> action2;\\MyExtension\\Controller\\Controller2->action3; Controller2->action1' ]; $expectedResult = [ 'pluginName' => 'Pi1', 'extensionName' => 'SomeExtension', 'controllerConfiguration' => [ - 'Controller1' => [ + 'MyExtension\\Controller\\Controller1' => [ + 'className' => 'MyExtension\\Controller\\Controller1', + 'alias' => 'Controller1', 'actions' => ['action2'] ], - 'Controller2' => [ + 'MyExtension\\Controller\\Controller2' => [ + 'className' => 'MyExtension\\Controller\\Controller2', + 'alias' => 'Controller2', 'actions' => ['action3', 'action1'], 'nonCacheableActions' => [1 => 'action3'] ]