Skip to content

Commit

Permalink
[BUGFIX] Convert class aliases of switchable controller actions to FQ…
Browse files Browse the repository at this point in the history
…CN's

When the patch https://forge.typo3.org/issues/87550 changed the controller
configuration of plugins and modules to use fully qualified controller
class names instead of just controller aliases, the override mechanism
called switchable controller actions, that can be used via typoscript
and flexforms broke.

This is due to the fact that switchable controller actions are usually
still used with controller aliases rather than fully qualified controller
class names.

To fix this, method overrideSwitchableControllerActions of class
\TYPO3\CMS\Extbase\Configuration\AbstractConfigurationManager has been
adjusted to convert controller aliases to FQCN's before overriding the
controller configuration.

Releases: master
Resolves: #88513
Relates: #87550
Change-Id: Ie2d1eb4b64d03d4e17d08a85aa7f8e548bff92bb
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/60906
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Daniel Goerz <daniel.goerz@posteo.de>
Reviewed-by: Benni Mack <benni@typo3.org>
Reviewed-by: Daniel Goerz <daniel.goerz@posteo.de>
  • Loading branch information
alexanderschnitzler authored and ervaude committed Jun 28, 2019
1 parent 39e8ccd commit e87eb75
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 15 deletions.
Expand Up @@ -222,26 +222,96 @@ 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.
continue;
}
$overriddenNonCacheableActions = array_intersect($nonCacheableActions, $actions);
if (!empty($overriddenNonCacheableActions)) {
$overriddenSwitchableControllerActions[$controllerName]['nonCacheableActions'] = $overriddenNonCacheableActions;
$overriddenSwitchableControllerActions[$controllerClassName]['nonCacheableActions'] = $overriddenNonCacheableActions;
}
}
$frameworkConfiguration['controllerConfiguration'] = $overriddenSwitchableControllerActions;
Expand Down
Expand Up @@ -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']
]
Expand Down Expand Up @@ -506,14 +508,62 @@ public function orderOfActionsCanBeOverriddenForCurrentPlugin(): void
}));
$mergedConfiguration = $this->abstractConfigurationManager->getConfiguration();
$expectedResult = [
'Controller1' => [
'MyExtension\\Controller\\Controller1' => [
'className' => 'MyExtension\\Controller\\Controller1',
'alias' => 'Controller1',
'actions' => ['action2', 'action1', 'action3']
]
];
$actualResult = $mergedConfiguration['controllerConfiguration'];
$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
*/
Expand Down Expand Up @@ -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']
]
];
Expand Down Expand Up @@ -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']
]
Expand Down
Expand Up @@ -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']
]
Expand Down

0 comments on commit e87eb75

Please sign in to comment.