[TASK] Use FQCN's when registering plugins/modules 66/59566/11
authorAlexander Schnitzler <git@alexanderschnitzler.de>
Thu, 24 Jan 2019 18:02:33 +0000 (19:02 +0100)
committerAnja Leichsenring <aleichsenring@ab-softlab.de>
Fri, 1 Feb 2019 09:30:11 +0000 (10:30 +0100)
The following methods used controller aliases:

- \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin
- \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule

This means, that while configuring plugins or modules, a list
of controllers (and their actions) had to be provided, so they
could be registered as available controllers for said plugin
or module.

The controller name used for the registration, was kind of an
alias for that controller. It was the class name without the
namespace and without the "Controller" suffix. By a certain
convention a proper class name could be derived from that
alias. All this was necessary back then when TYPO3 didn't have
a proper autoloader and when the class location was derived
from the class name. These circumstances do not exist any more
and since it's only important that a controller class exists,
the registration of plugins must now use the fully qualified
controller class name as array key.

The former syntax

   \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
       'TYPO3.CMS.IndexedSearch',
       'Pi2',
       ['Search' => 'form,search,noTypoScript'],
       ['Search' => 'form,search']
   );

has to be converted to

   \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
       'IndexedSearch',
       'Pi2',
       [
           \TYPO3\CMS\IndexedSearch\Controller\SearchController::class
               => 'form,search,noTypoScript'
       ],
       [
           \TYPO3\CMS\IndexedSearch\Controller\SearchController::class
               => 'form,search'
       ]
   );

Please note, that not only the controller configuration changed.
The vendor `TYPO3.CMS` in `TYPO3.CMS.IndexedSearch`, must no longer
be set. Instead, the vendor will be evaluated by looking at the
controller namespace.

Releases: master
Resolves: #87550
Change-Id: If47d087836c4345a8180dae3d4e4b8cd384e684c
Reviewed-on: https://review.typo3.org/59566
Tested-by: TYPO3com <noreply@typo3.com>
Reviewed-by: Jörg Bösche <typo3@joergboesche.de>
Reviewed-by: Mona Muzaffar <mona.muzaffar@gmx.de>
Tested-by: Mona Muzaffar <mona.muzaffar@gmx.de>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
32 files changed:
composer.json
typo3/sysext/belog/ext_tables.php
typo3/sysext/beuser/ext_tables.php
typo3/sysext/core/Documentation/Changelog/master/Deprecation-87550-UseControllerClassesWhenRegisteringPluginsmodules.rst [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/ext_localconf.php
typo3/sysext/extbase/Classes/Core/Bootstrap.php
typo3/sysext/extbase/Classes/Mvc/Controller/AbstractController.php
typo3/sysext/extbase/Classes/Mvc/Request.php
typo3/sysext/extbase/Classes/Mvc/Web/FrontendRequestHandler.php
typo3/sysext/extbase/Classes/Mvc/Web/Request.php
typo3/sysext/extbase/Classes/Mvc/Web/RequestBuilder.php
typo3/sysext/extbase/Classes/Service/ExtensionService.php
typo3/sysext/extbase/Classes/Utility/ExtensionUtility.php
typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_localconf.php
typo3/sysext/extbase/Tests/Unit/Mvc/Controller/AbstractControllerTest.php
typo3/sysext/extbase/Tests/Unit/Mvc/Controller/ActionControllerTest.php
typo3/sysext/extbase/Tests/Unit/Mvc/RequestTest.php
typo3/sysext/extbase/Tests/Unit/Mvc/Web/RequestBuilderTest.php
typo3/sysext/extbase/Tests/Unit/Utility/ExtensionUtilityTest.php
typo3/sysext/extbase/Tests/Unit/Utility/Fixtures/MyExtension/Controller/FirstController.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Utility/Fixtures/MyExtension/Controller/SecondController.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Utility/Fixtures/MyExtension/Controller/ThirdController.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/UnitDeprecated/Utility/ExtensionUtilityTest.php [new file with mode: 0644]
typo3/sysext/extensionmanager/ext_tables.php
typo3/sysext/filelist/ext_tables.php
typo3/sysext/fluid/Tests/Functional/View/TemplatesPathsTest.php
typo3/sysext/form/ext_localconf.php
typo3/sysext/form/ext_tables.php
typo3/sysext/indexed_search/ext_localconf.php
typo3/sysext/indexed_search/ext_tables.php
typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php
typo3/sysext/workspaces/ext_tables.php

index 9602ef1..6fb249c 100644 (file)
                        "TYPO3\\CMS\\Recycler\\Tests\\": "typo3/sysext/recycler/Tests/",
                        "TYPO3\\CMS\\T3editor\\Tests\\": "typo3/sysext/t3editor/Tests/"
                },
-               "classmap": ["typo3/sysext/extbase/Tests/Unit/Object/Container/Fixtures/"]
+               "classmap": [
+                       "typo3/sysext/core/Tests/Functional/Fixtures/",
+                       "typo3/sysext/extbase/Tests/Unit/Object/Container/Fixtures/"
+               ]
        }
 }
index 499b38c..fffe530 100644 (file)
@@ -11,12 +11,12 @@ defined('TYPO3_MODE') or die();
 
 // Module Tools->Log
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-    'TYPO3.CMS.Belog',
+    'Belog',
     'system',
     'log',
     '',
     [
-        'BackendLog' => 'list,deleteMessage',
+        \TYPO3\CMS\Belog\Controller\BackendLogController::class => 'list,deleteMessage',
     ],
     [
         'access' => 'admin',
index 17d442e..ebffebd 100644 (file)
@@ -3,13 +3,13 @@ defined('TYPO3_MODE') or die();
 
 // Module System > Backend Users
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-    'TYPO3.CMS.Beuser',
+    'Beuser',
     'system',
     'tx_Beuser',
     'top',
     [
-        'BackendUser' => 'index, addToCompareList, removeFromCompareList, compare, online, terminateBackendUserSession',
-        'BackendUserGroup' => 'index'
+        \TYPO3\CMS\Beuser\Controller\BackendUserController::class => 'index, addToCompareList, removeFromCompareList, compare, online, terminateBackendUserSession',
+        \TYPO3\CMS\Beuser\Controller\BackendUserGroupController::class => 'index'
     ],
     [
         'access' => 'admin',
@@ -20,12 +20,12 @@ defined('TYPO3_MODE') or die();
 
 // Module System > Access
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-    'TYPO3.CMS.Beuser',
+    'Beuser',
     'system',
     'tx_Permission',
     'top',
     [
-        'Permission' => 'index, edit, update'
+        \TYPO3\CMS\Beuser\Controller\PermissionController::class => 'index, edit, update'
     ],
     [
         'access' => 'admin',
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-87550-UseControllerClassesWhenRegisteringPluginsmodules.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-87550-UseControllerClassesWhenRegisteringPluginsmodules.rst
new file mode 100644 (file)
index 0000000..2c005b4
--- /dev/null
@@ -0,0 +1,88 @@
+.. include:: ../../Includes.txt
+
+=============================================================================
+Deprecation: #87550 - Use controller classes when registering plugins/modules
+=============================================================================
+
+See :issue:`87550`
+
+Description
+===========
+
+Configuring plugins and modules via the following methods has changed in two important ways.
+
+- :php:`\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin`
+- :php:`\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule`
+
+Both methods expect you to provide the argument :php:`$extensionName` and :php:`$controllerActions`.
+:php:`configurePlugin` also allows the argument :php:`$nonCacheableControllerActions`.
+
+The first important change targets the :php:`$extensionName` argument.
+During the switch from underscore class names :php`Tx_Extbase_Foo_Bar` to actual namespaced classes
+:php:`TYPO3\CMS\Extbase\Foo\Bar`, a vendor `TYPO3\CMS` has been introduced which had to be respected
+during the configuration of plugins. To make that possible the argument :php:`$extensionName` has been
+prepended with the vendor name, concatenated with dots.
+
+::
+   <?php
+
+   \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
+       'TYPO3.CMS.Form', // $extensionName
+       'Formframework',
+       ['FormFrontend' => 'render, perform'],
+       ['FormFrontend' => 'perform'],
+       \TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
+   );
+
+Setting the vendor name is now deprecated and must be omitted. Instead, the vendor name will be derived
+from the controller class namespace, which leads to the second important change.
+
+Both arguments :php:`$controllerActions` and `$nonCacheableControllerActions` used controller aliases as
+array keys. The alias was the controller class name without the namespace and without the `Controller`
+suffix. There were a lot of conventions and a quite stupid custom autoloader mechanism before the introduction
+of the composer autoloader, which made it necessary to put controllers in a specific directory and to name
+the controller accordingly. As this is no longer the case, there is no need to guess the controller class name
+any longer. Instead, the configuration/registration is now done with fully qualified controller class names.
+
+::
+   <?php
+
+   \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
+       'Form',
+       'Formframework',
+       [\TYPO3\CMS\Form\Controller\FormFrontendController::class => 'render, perform'],
+       [\TYPO3\CMS\Form\Controller\FormFrontendController::class => 'perform'],
+       \TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
+   );
+
+Conclusion
+==========
+
+The following things are deprecated:
+
+- Prepend the :php:`$extensionName` argument with a vendor name.
+- Using controller aliases as array keys in both arguments :php:`$controllerActions` and :php:`$nonCacheableControllerActions`.
+
+
+Impact
+======
+
+Using the deprecated syntax will log deprecation warnings and will stop working in TYPO3 11.0.
+
+
+Affected Installations
+======================
+
+All installations that use these methods:
+
+- :php:`\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin`
+- :php:`\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule`
+
+
+Migration
+=========
+
+- Omit the vendor name in argument :php:`$extensionName`
+- Use fully qualified class names as array keys in arguments :php:`$controllerActions` and :php:`$nonCacheableControllerActions`
+
+.. index:: PHP-API, PartiallyScanned, ext:extbase
index 3d3cce7..3d25ab5 100644 (file)
@@ -2,13 +2,13 @@
 defined('TYPO3_MODE') or die();
 
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
-    'OliverHader.IrreTutorial',
+    'IrreTutorial',
     'Irre',
     [
-        'Queue' => 'index',
-        'Content' => 'list, show, new, create, edit, update, delete'
+        \OliverHader\IrreTutorial\Controller\QueueController::class => 'index',
+        \OliverHader\IrreTutorial\Controller\ContentController::class => 'list, show, new, create, edit, update, delete'
     ],
     [
-        'Content' => 'create, update, delete'
+        \OliverHader\IrreTutorial\Controller\ContentController::class => 'create, update, delete'
     ]
 );
index b30f71c..1e490a2 100644 (file)
@@ -103,6 +103,12 @@ class Bootstrap implements \TYPO3\CMS\Extbase\Core\BootstrapInterface
         $contentObject = $this->cObj ?? \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class);
         $this->configurationManager->setContentObject($contentObject);
         $this->configurationManager->setConfiguration($configuration);
+        // todo: Shouldn't the configuration manager object – which is a singleton – be stateless?
+        // todo: At this point we give the configuration manager a state, while we could directly pass the
+        // todo: configuration (i.e. controllerName, actionName, vendorName and such), directly to the request
+        // todo: handler, which then creates stateful request objects.
+        // todo: Once this has changed, \TYPO3\CMS\Extbase\Mvc\Web\RequestBuilder::loadDefaultValues does not need
+        // todo: to fetch this configuration from the configuration manager.
     }
 
     /**
index a42db6a..183686a 100644 (file)
@@ -123,6 +123,12 @@ abstract class AbstractController implements ControllerInterface
      */
     public function __construct()
     {
+        // this functionality is actually not needed any more.
+        // the extension name is known during the creation of request objects and if the extension name was
+        // needed, then it were to be fetched by the request object. The controller object shouldn't care about
+        // the extension that uses it.
+        // todo: decide whether to drop this now or along with the property or if to keep that to not break the public api
+
         $className = static::class;
         $classNameParts = explode('\\', $className, 4);
         // Skip vendor and product name for core classes
index deb6d3f..2e45f37 100644 (file)
@@ -24,13 +24,6 @@ class Request implements RequestInterface
     const PATTERN_MATCH_FORMAT = '/^[a-z0-9]{1,5}$/';
 
     /**
-     * Pattern after which the namespaced controller object name is built
-     *
-     * @var string
-     */
-    protected $namespacedControllerObjectNamePattern = '@vendor\@extension\@subpackage\Controller\@controllerController';
-
-    /**
      * @var string Key of the plugin which identifies the plugin. It must be a string containing [a-z0-9]
      */
     protected $pluginName = '';
@@ -53,6 +46,11 @@ class Request implements RequestInterface
     protected $controllerSubpackageKey;
 
     /**
+     * @var string
+     */
+    protected $controllerObjectName;
+
+    /**
      * @var string Object name of the controller which is supposed to handle this request.
      */
     protected $controllerName = 'Standard';
@@ -126,37 +124,19 @@ class Request implements RequestInterface
     }
 
     /**
-     * Returns the object name of the controller defined by the extension name and
-     * controller name
-     *
-     * @return string The controller's Object Name
-     * @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchControllerException if the controller does not exist
-     */
-    public function getControllerObjectName()
-    {
-        $objectName = str_replace(
-            [
-                '@extension',
-                '@subpackage',
-                '@controller',
-                '@vendor',
-                '\\\\'
-            ],
-            [
-                $this->controllerExtensionName,
-                $this->controllerSubpackageKey,
-                $this->controllerName,
-                $this->controllerVendorName,
-                '\\'
-            ],
-            $this->namespacedControllerObjectNamePattern
-        );
-
-        // @todo implement getCaseSensitiveObjectName()
-        if ($objectName === false) {
-            throw new \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchControllerException('The controller object "' . $objectName . '" does not exist.', 1220884009);
-        }
-        return $objectName;
+     * @param string $controllerClassName
+     */
+    public function __construct(string $controllerClassName = '')
+    {
+        $this->controllerObjectName = $controllerClassName;
+    }
+
+    /**
+     * @return string
+     */
+    public function getControllerObjectName(): string
+    {
+        return $this->controllerObjectName;
     }
 
     /**
@@ -255,6 +235,21 @@ class Request implements RequestInterface
     }
 
     /**
+     * @var array
+     */
+    protected $controllerAliasToClassNameMapping = [];
+
+    /**
+     * @param array $controllerAliasToClassNameMapping
+     */
+    public function setControllerAliasToClassNameMapping(array $controllerAliasToClassNameMapping)
+    {
+        // this is only needed as long as forwarded requests are altered and unless there
+        // is no new request object created by the request builder.
+        $this->controllerAliasToClassNameMapping = $controllerAliasToClassNameMapping;
+    }
+
+    /**
      * Sets the name of the controller which is supposed to handle the request.
      * Note: This is not the object name of the controller!
      *
@@ -269,6 +264,8 @@ class Request implements RequestInterface
         }
         if ($controllerName !== null) {
             $this->controllerName = $controllerName;
+            $this->controllerObjectName = $this->controllerAliasToClassNameMapping[$controllerName] ?? '';
+            // There might be no Controller Class, for example for Fluid Templates.
         }
     }
 
@@ -314,6 +311,9 @@ class Request implements RequestInterface
     {
         $controllerObjectName = $this->getControllerObjectName();
         if ($controllerObjectName !== '' && $this->controllerActionName === strtolower($this->controllerActionName)) {
+            // todo: this is nonsense! We can detect a non existing method in
+            // todo: \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin, if necessary.
+            // todo: At this point, we want to have a getter for a fixed value.
             $actionMethodName = $this->controllerActionName . 'Action';
             $classMethods = get_class_methods($controllerObjectName);
             if (is_array($classMethods)) {
index 49d1a97..267b743 100644 (file)
@@ -67,7 +67,7 @@ class FrontendRequestHandler extends AbstractRequestHandler
     public function handleRequest()
     {
         $request = $this->requestBuilder->build();
-        if ($this->extensionService->isActionCacheable(null, null, $request->getControllerName(), $request->getControllerActionName())) {
+        if ($this->extensionService->isActionCacheable(null, null, $request->getControllerObjectName(), $request->getControllerActionName())) {
             $request->setIsCached(true);
         } else {
             $contentObject = $this->configurationManager->getContentObject();
index 1464fda..2f1d057 100644 (file)
@@ -193,6 +193,13 @@ class Request extends \TYPO3\CMS\Extbase\Mvc\Request
                 // This case is kept for compatibility in 7.6 and 6.2, but will be removed in 8
                 $arguments = unserialize(base64_decode($this->hashService->validateAndStripHmac($this->internalArguments['__referrer']['arguments'])));
             }
+            // todo: Creating a referring request object here with a new statement is strange.
+            // todo: As request objects have inject methods and are still meant to be created via object manager,
+            // todo: this creates a partly non functional object. This is ok here as only the arguments matter, but
+            // todo: it should be solved better.
+            // todo: As an alternative, all request objects could be created via RequestBuilder and if there is a
+            // todo: redirect in a controller which is the cause of need for a referring object, said object should be
+            // todo: created before dispatching the new request and not in a getter.
             $referringRequest = new ReferringRequest();
             $referringRequest->setArguments(array_replace_recursive($arguments, $referrerArray));
             return $referringRequest;
index 087d1a3..c71e397 100644 (file)
@@ -54,6 +54,13 @@ class RequestBuilder implements \TYPO3\CMS\Core\SingletonInterface
     protected $extensionName;
 
     /**
+     * The class name of the default controller
+     *
+     * @var string
+     */
+    private $defaultControllerClassName;
+
+    /**
      * The default controller name
      *
      * @var string
@@ -90,6 +97,21 @@ class RequestBuilder implements \TYPO3\CMS\Core\SingletonInterface
     protected $environmentService;
 
     /**
+     * @var array
+     */
+    private $controllerAliasToClassMapping = [];
+
+    /**
+     * @var array
+     */
+    private $controllerClassToAliasMapping = [];
+
+    /**
+     * @var array|string[]
+     */
+    private $allowedControllerAliases = [];
+
+    /**
      * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
      */
     public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
@@ -123,9 +145,12 @@ class RequestBuilder implements \TYPO3\CMS\Core\SingletonInterface
 
     /**
      * @throws MvcException
+     * @see \TYPO3\CMS\Extbase\Core\Bootstrap::initializeConfiguration
      */
     protected function loadDefaultValues()
     {
+        // todo: See comment in \TYPO3\CMS\Extbase\Core\Bootstrap::initializeConfiguration for further explanation
+        // todo: on why we shouldn't use the configuration manager here.
         $configuration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
         if (empty($configuration['extensionName'])) {
             throw new MvcException('"extensionName" is not properly configured. Request can\'t be dispatched!', 1289843275);
@@ -140,15 +165,21 @@ class RequestBuilder implements \TYPO3\CMS\Core\SingletonInterface
         }
         $this->extensionName = $configuration['extensionName'];
         $this->pluginName = $configuration['pluginName'];
-        $this->defaultControllerName = (string)current(array_keys($configuration['controllerConfiguration']));
+        $defaultControllerConfiguration = reset($configuration['controllerConfiguration']) ?? [];
+        $this->defaultControllerClassName = $defaultControllerConfiguration['className'] ?? null;
+        $this->defaultControllerName = $defaultControllerConfiguration['alias'] ?? null;
         $this->allowedControllerActions = [];
-        foreach ($configuration['controllerConfiguration'] as $controllerName => $controllerActions) {
-            $this->allowedControllerActions[$controllerName] = $controllerActions['actions'] ?? null;
+        foreach ($configuration['controllerConfiguration'] as $controllerClassName => $controllerConfiguration) {
+            $this->allowedControllerActions[$controllerClassName] = $controllerConfiguration['actions'] ?? null;
+            $this->controllerAliasToClassMapping[$controllerConfiguration['alias']] = $controllerConfiguration['className'];
+            $this->controllerClassToAliasMapping[$controllerConfiguration['className']] = $controllerConfiguration['alias'];
+            $this->allowedControllerAliases[] = $controllerConfiguration['alias'];
         }
         if (!empty($configuration['format'])) {
             $this->defaultFormat = $configuration['format'];
         }
     }
+
     /**
      * Builds a web request object from the raw HTTP information and the configuration
      *
@@ -179,16 +210,18 @@ class RequestBuilder implements \TYPO3\CMS\Core\SingletonInterface
             $parameters = array_replace_recursive($parameters, $files[$pluginNamespace]);
         }
 
-        $controllerName = $this->resolveControllerName($parameters);
-        $actionName = $this->resolveActionName($controllerName, $parameters);
+        $controllerClassName = $this->resolveControllerClassName($parameters);
+        $actionName = $this->resolveActionName($controllerClassName, $parameters);
         /** @var \TYPO3\CMS\Extbase\Mvc\Web\Request $request */
         $request = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Request::class);
         if ($this->vendorName !== null) {
+            // Still needed for the fucking viewObjectNamePattern in ActionController
             $request->setControllerVendorName($this->vendorName);
         }
         $request->setPluginName($this->pluginName);
         $request->setControllerExtensionName($this->extensionName);
-        $request->setControllerName($controllerName);
+        $request->setControllerAliasToClassNameMapping($this->controllerAliasToClassMapping);
+        $request->setControllerName($this->controllerClassToAliasMapping[$controllerClassName]);
         $request->setControllerActionName($actionName);
         // @todo Use Environment
         $request->setRequestUri(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'));
@@ -216,29 +249,29 @@ class RequestBuilder implements \TYPO3\CMS\Core\SingletonInterface
      * @throws \TYPO3\CMS\Core\Error\Http\PageNotFoundException
      * @return string
      */
-    protected function resolveControllerName(array $parameters)
+    protected function resolveControllerClassName(array $parameters)
     {
         if (!isset($parameters['controller']) || $parameters['controller'] === '') {
-            if (empty($this->defaultControllerName)) {
+            if (empty($this->defaultControllerClassName)) {
                 throw new MvcException('The default controller for extension "' . $this->extensionName . '" and plugin "' . $this->pluginName . '" can not be determined. Please check for TYPO3\\CMS\\Extbase\\Utility\\ExtensionUtility::configurePlugin() in your ext_localconf.php.', 1316104317);
             }
-            return $this->defaultControllerName;
+            return $this->defaultControllerClassName;
         }
-        $allowedControllerNames = array_keys($this->allowedControllerActions);
-        if (!in_array($parameters['controller'], $allowedControllerNames)) {
+        $controllerClassName = $this->controllerAliasToClassMapping[$parameters['controller']] ?? '';
+        if (!in_array($controllerClassName, array_keys($this->allowedControllerActions))) {
             $configuration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
             if (isset($configuration['mvc']['throwPageNotFoundExceptionIfActionCantBeResolved']) && (bool)$configuration['mvc']['throwPageNotFoundExceptionIfActionCantBeResolved']) {
                 throw new \TYPO3\CMS\Core\Error\Http\PageNotFoundException('The requested resource was not found', 1313857897);
             }
             if (isset($configuration['mvc']['callDefaultActionIfActionCantBeResolved']) && (bool)$configuration['mvc']['callDefaultActionIfActionCantBeResolved']) {
-                return $this->defaultControllerName;
+                return $this->defaultControllerClassName;
             }
             throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidControllerNameException(
                 'The controller "' . $parameters['controller'] . '" is not allowed by plugin "' . $this->pluginName . '". Please check for TYPO3\\CMS\\Extbase\\Utility\\ExtensionUtility::configurePlugin() in your ext_localconf.php.',
                 1313855173
             );
         }
-        return filter_var($parameters['controller'], FILTER_SANITIZE_STRING);
+        return filter_var($controllerClassName, FILTER_SANITIZE_STRING);
     }
 
     /**
@@ -246,24 +279,24 @@ class RequestBuilder implements \TYPO3\CMS\Core\SingletonInterface
      * If no action is specified, the defaultActionName will be returned.
      * If that's not available or the specified action is not defined in the current plugin, an exception is thrown.
      *
-     * @param string $controllerName
+     * @param string $controllerClassName
      * @param array $parameters
      * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidActionNameException
      * @throws MvcException
      * @throws \TYPO3\CMS\Core\Error\Http\PageNotFoundException
      * @return string
      */
-    protected function resolveActionName($controllerName, array $parameters)
+    protected function resolveActionName($controllerClassName, array $parameters)
     {
-        $defaultActionName = is_array($this->allowedControllerActions[$controllerName]) ? current($this->allowedControllerActions[$controllerName]) : '';
+        $defaultActionName = is_array($this->allowedControllerActions[$controllerClassName]) ? current($this->allowedControllerActions[$controllerClassName]) : '';
         if (!isset($parameters['action']) || $parameters['action'] === '') {
             if ($defaultActionName === '') {
-                throw new MvcException('The default action can not be determined for controller "' . $controllerName . '". Please check TYPO3\\CMS\\Extbase\\Utility\\ExtensionUtility::configurePlugin() in your ext_localconf.php.', 1295479651);
+                throw new MvcException('The default action can not be determined for controller "' . $controllerClassName . '". Please check TYPO3\\CMS\\Extbase\\Utility\\ExtensionUtility::configurePlugin() in your ext_localconf.php.', 1295479651);
             }
             return $defaultActionName;
         }
         $actionName = $parameters['action'];
-        $allowedActionNames = $this->allowedControllerActions[$controllerName];
+        $allowedActionNames = $this->allowedControllerActions[$controllerClassName];
         if (!in_array($actionName, $allowedActionNames)) {
             $configuration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
             if (isset($configuration['mvc']['throwPageNotFoundExceptionIfActionCantBeResolved']) && (bool)$configuration['mvc']['throwPageNotFoundExceptionIfActionCantBeResolved']) {
@@ -272,7 +305,7 @@ class RequestBuilder implements \TYPO3\CMS\Core\SingletonInterface
             if (isset($configuration['mvc']['callDefaultActionIfActionCantBeResolved']) && (bool)$configuration['mvc']['callDefaultActionIfActionCantBeResolved']) {
                 return $defaultActionName;
             }
-            throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidActionNameException('The action "' . $actionName . '" (controller "' . $controllerName . '") is not allowed by this plugin. Please check TYPO3\\CMS\\Extbase\\Utility\\ExtensionUtility::configurePlugin() in your ext_localconf.php.', 1313855175);
+            throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidActionNameException('The action "' . $actionName . '" (controller "' . $controllerClassName . '") is not allowed by this plugin. Please check TYPO3\\CMS\\Extbase\\Utility\\ExtensionUtility::configurePlugin() in your ext_localconf.php.', 1313855175);
         }
         return filter_var($actionName, FILTER_SANITIZE_STRING);
     }
index 8d5cd39..4f006e0 100644 (file)
@@ -130,17 +130,25 @@ class ExtensionService implements \TYPO3\CMS\Core\SingletonInterface
      *
      * @param string $extensionName Name of the target extension, without underscores
      * @param string $pluginName Name of the target plugin
-     * @param string $controllerName Name of the target controller
+     * @param string $controllerClassName Name of the target controller
      * @param string $actionName Name of the action to be called
      * @return bool TRUE if the specified plugin action is cacheable, otherwise FALSE
      */
-    public function isActionCacheable($extensionName, $pluginName, $controllerName, $actionName)
+    public function isActionCacheable($extensionName, $pluginName, $controllerClassName, $actionName)
     {
-        $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, $extensionName, $pluginName);
-        if (isset($frameworkConfiguration['controllerConfiguration'][$controllerName]) && is_array($frameworkConfiguration['controllerConfiguration'][$controllerName]) && is_array($frameworkConfiguration['controllerConfiguration'][$controllerName]['nonCacheableActions']) && in_array($actionName, $frameworkConfiguration['controllerConfiguration'][$controllerName]['nonCacheableActions'])) {
-            return false;
+        $frameworkConfiguration = $this->configurationManager->getConfiguration(
+            ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK,
+            $extensionName,
+            $pluginName
+        );
+
+        $nonCacheableActions = $frameworkConfiguration['controllerConfiguration'][$controllerClassName]['nonCacheableActions'] ?? null;
+
+        if (!is_array($nonCacheableActions)) {
+            return true;
         }
-        return true;
+
+        return !in_array($actionName, $frameworkConfiguration['controllerConfiguration'][$controllerClassName]['nonCacheableActions'], true);
     }
 
     /**
index 7ccf1c6..3a2afd2 100644 (file)
@@ -49,6 +49,10 @@ class ExtensionUtility
         $vendorName = null;
         $delimiterPosition = strrpos($extensionName, '.');
         if ($delimiterPosition !== false) {
+            trigger_error(
+                'Calling method ' . __METHOD__ . 'with argument $extensionName containing the vendor name is deprecated and will stop working in TYPO3 11.0.',
+                E_USER_DEPRECATED
+            );
             $vendorName = str_replace('.', '\\', substr($extensionName, 0, $delimiterPosition));
             $extensionName = substr($extensionName, $delimiterPosition + 1);
 
@@ -62,10 +66,44 @@ class ExtensionUtility
         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 $controllerName => $actionsList) {
-            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerName] = ['actions' => \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $actionsList)];
-            if (!empty($nonCacheableControllerActions[$controllerName])) {
-                $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerName]['nonCacheableActions'] = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $nonCacheableControllerActions[$controllerName]);
+        foreach ($controllerActions as $controllerClassName => $actionsList) {
+            if (class_exists($controllerClassName)) {
+                $controllerAlias = self::resolveControllerAliasFromControllerClassName($controllerClassName);
+                $vendorName = self::resolveVendorFromExtensionAndControllerClassName($extensionName, $controllerClassName);
+                if (!empty($vendorName)) {
+                    self::checkVendorNameFormat($vendorName, $extensionName);
+                }
+            } else {
+                trigger_error(
+                    'Calling ' . __METHOD__ . ' with controller aliases in argument $controllerActions is deprecated and will stop working in TYPO3 11.0.',
+                    E_USER_DEPRECATED
+                );
+                $controllerAlias = $controllerClassName;
+                $controllerClassName = static::getControllerClassName((string)$vendorName, $extensionName, '', $controllerAlias);
+            }
+
+            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerClassName] = [
+                'className' => $controllerClassName,
+                'alias' => $controllerAlias,
+                'actions' => \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $actionsList)
+            ];
+
+            if (isset($nonCacheableControllerActions[$controllerAlias]) && !empty($nonCacheableControllerActions[$controllerAlias])) {
+                trigger_error(
+                    'Calling ' . __METHOD__ . ' with controller aliases in argument $nonCacheableControllerActions is deprecated and will stop working in TYPO3 11.0.',
+                    E_USER_DEPRECATED
+                );
+                $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerClassName]['nonCacheableActions'] = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(
+                    ',',
+                    $nonCacheableControllerActions[$controllerAlias]
+                );
+            }
+
+            if (isset($nonCacheableControllerActions[$controllerClassName]) && !empty($nonCacheableControllerActions[$controllerClassName])) {
+                $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerClassName]['nonCacheableActions'] = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(
+                    ',',
+                    $nonCacheableControllerActions[$controllerClassName]
+                );
             }
         }
 
@@ -156,6 +194,10 @@ tt_content.' . $pluginSignature . ' {
         // Check if vendor name is prepended to extensionName in the format {vendorName}.{extensionName}
         $vendorName = null;
         if (false !== $delimiterPosition = strrpos($extensionName, '.')) {
+            trigger_error(
+                'Calling method ' . __METHOD__ . 'with argument $extensionName containing the vendor name is deprecated and will stop working in TYPO3 11.0.',
+                E_USER_DEPRECATED
+            );
             $vendorName = str_replace('.', '\\', substr($extensionName, 0, $delimiterPosition));
             $extensionName = substr($extensionName, $delimiterPosition + 1);
 
@@ -187,23 +229,126 @@ tt_content.' . $pluginSignature . ' {
             $moduleSignature .= '_' . $subModuleName;
         }
         $moduleConfiguration['name'] = $moduleSignature;
-        if (null !== $vendorName) {
-            $moduleConfiguration['vendorName'] = $vendorName;
-        }
         $moduleConfiguration['extensionName'] = $extensionName;
         $moduleConfiguration['routeTarget'] = \TYPO3\CMS\Extbase\Core\Bootstrap::class . '::handleBackendRequest';
         if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature] ?? false)) {
             $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature] = [];
         }
-        foreach ($controllerActions as $controllerName => $actions) {
-            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature]['controllers'][$controllerName] = [
-                'actions' => \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $actions)
+        foreach ($controllerActions as $controllerClassName => $actionsList) {
+            if (class_exists($controllerClassName)) {
+                $controllerAlias = self::resolveControllerAliasFromControllerClassName($controllerClassName);
+                $vendorName = self::resolveVendorFromExtensionAndControllerClassName($extensionName, $controllerClassName);
+                if (!empty($vendorName)) {
+                    self::checkVendorNameFormat($vendorName, $extensionName);
+                }
+            } else {
+                trigger_error(
+                    'Calling ' . __METHOD__ . ' with controller aliases is deprecated and will stop working in TYPO3 11.0.',
+                    E_USER_DEPRECATED
+                );
+                $controllerAlias = $controllerClassName;
+                $controllerClassName = static::getControllerClassName($vendorName, $extensionName, '', $controllerAlias);
+            }
+
+            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature]['controllers'][$controllerClassName] = [
+                'className' => $controllerClassName,
+                'alias' => $controllerAlias,
+                'actions' => \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $actionsList)
             ];
         }
+        if (null !== $vendorName) {
+            $moduleConfiguration['vendorName'] = $vendorName;
+        }
         \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addModule($mainModuleName, $subModuleName, $position, null, $moduleConfiguration);
     }
 
     /**
+     * Returns the object name of the controller defined by the extension name and
+     * controller name
+     *
+     * @param string $vendor
+     * @param string $extensionKey
+     * @param string $subPackageKey
+     * @param string $controllerAlias
+     * @return string The controller's Object Name
+     * @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchControllerException if the controller does not exist
+     */
+    public static function getControllerClassName(
+        string $vendor,
+        string $extensionKey,
+        string $subPackageKey,
+        string $controllerAlias
+    ): string {
+        $objectName = str_replace(
+            [
+                '@extension',
+                '@subpackage',
+                '@controller',
+                '@vendor',
+                '\\\\'
+            ],
+            [
+                $extensionKey,
+                $subPackageKey,
+                $controllerAlias,
+                $vendor,
+                '\\'
+            ],
+            '@vendor\@extension\@subpackage\Controller\@controllerController'
+        );
+
+        if ($objectName === false) {
+            throw new \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchControllerException('The controller object "' . $objectName . '" does not exist.', 1220884009);
+        }
+        return trim($objectName, '\\');
+    }
+
+    /**
+     * @param string $controllerClassName
+     * @return string
+     */
+    public static function resolveControllerAliasFromControllerClassName(string $controllerClassName): string
+    {
+        if (strrpos($controllerClassName, 'Controller') === false) {
+            return '';
+        }
+
+        return trim(substr(
+            $controllerClassName,
+            (int)strrpos($controllerClassName, '\\'),
+            -strlen('Controller')
+        ), '\\');
+    }
+
+    /**
+     * @param string $extensionName
+     * @param string $controllerClassName
+     * @return string
+     */
+    public static function resolveVendorFromExtensionAndControllerClassName(string $extensionName, string $controllerClassName): string
+    {
+        if (strpos($controllerClassName, '\\') === false) {
+            // Does not work with non namespaced classes
+            return '';
+        }
+
+        if (false === $extensionNamePosition = strpos($controllerClassName, $extensionName)) {
+            // Does not work for classes that do not include the extension name as namespace part
+            return '';
+        }
+
+        if (--$extensionNamePosition < 0) {
+            return '';
+        }
+
+        return substr(
+            $controllerClassName,
+            0,
+            $extensionNamePosition
+        );
+    }
+
+    /**
      * Register a type converter by class name.
      *
      * @param string $typeConverterClassName
index 0ec79aa..856caff 100644 (file)
@@ -2,18 +2,18 @@
 defined('TYPO3_MODE') or die();
 
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
-    'ExtbaseTeam.BlogExample',
+    'BlogExample',
     'Blogs',
     [
-        'Blog' => 'list,testForm,testForward,testForwardTarget,testRelatedObject',
+        \ExtbaseTeam\BlogExample\Controller\BlogController::class => 'list,testForm,testForward,testForwardTarget,testRelatedObject',
     ],
     []
 );
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
-    'ExtbaseTeam.BlogExample',
+    'BlogExample',
     'Content',
     [
-        'Content' => 'list',
+        \ExtbaseTeam\BlogExample\Controller\ContentController::class => 'list',
     ],
     []
 );
index 24b746f..f9eb282 100644 (file)
@@ -22,20 +22,6 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 class AbstractControllerTest extends UnitTestCase
 {
     /**
-     * @test
-     */
-    public function constructResolvesExtensionnameFromNamespaced()
-    {
-        $className = $this->getUniqueId('DummyController');
-        eval('namespace ' . __NAMESPACE__ . '; class ' . $className . ' extends \\' . \TYPO3\CMS\Extbase\Mvc\Controller\AbstractController::class . ' { function getExtensionName() { return $this->extensionName; } }');
-        $classNameNamespaced = __NAMESPACE__ . '\\' . $className;
-        $mockController = new $classNameNamespaced();
-        $expectedResult = 'Extbase';
-        $actualResult = $mockController->getExtensionName();
-        $this->assertEquals($expectedResult, $actualResult);
-    }
-
-    /**
      * @return array
      */
     public function addFlashMessageDataProvider()
index 2e515d9..fee4dd6 100644 (file)
@@ -520,7 +520,7 @@ class ActionControllerTest extends UnitTestCase
             $pageRendererMock->expects($this->never())->method('addFooterData');
         }
         $requestMock = $this->getMockBuilder(RequestInterface::class)->getMockForAbstractClass();
-        $subject = new ActionController();
+        $subject = new ActionController('');
         $viewProperty = new \ReflectionProperty($subject, 'view');
         $viewProperty->setAccessible(true);
         $viewProperty->setValue($subject, $viewMock);
index 0305070..c187dbc 100644 (file)
@@ -312,29 +312,10 @@ class RequestTest extends UnitTestCase
      * @param string $controllerObjectName
      * @test
      */
-    public function getControllerObjectNameResolvesControllerObjectNameCorrectly($controllerArguments, $controllerObjectName)
-    {
-        /** @var $request \TYPO3\CMS\Extbase\Mvc\Request */
-        $request = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Mvc\Request::class, ['dummy']);
-        $request->_set('controllerVendorName', $controllerArguments['vendorName']);
-        $request->_set('controllerExtensionName', $controllerArguments['extensionName']);
-        $request->_set('controllerSubpackageKey', $controllerArguments['subpackageKey']);
-        $request->_set('controllerName', $controllerArguments['controllerName']);
-
-        $this->assertEquals($controllerObjectName, $request->getControllerObjectName());
-    }
-
-    /**
-     * @dataProvider controllerArgumentsAndExpectedObjectName
-     *
-     * @param array $controllerArguments
-     * @param string $controllerObjectName
-     * @test
-     */
     public function setControllerObjectNameResolvesControllerObjectNameArgumentsCorrectly($controllerArguments, $controllerObjectName)
     {
         /** @var $request \TYPO3\CMS\Extbase\Mvc\Request */
-        $request = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Mvc\Request::class, ['dummy']);
+        $request = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Mvc\Request::class, ['dummy'], [], '', false);
         $request->setControllerObjectName($controllerObjectName);
 
         $actualControllerArguments = [
index 72633a6..8b2551c 100644 (file)
@@ -80,13 +80,19 @@ class RequestBuilderTest extends UnitTestCase
             'controller' => 'TheFirstController',
             'action' => 'show',
             'controllerConfiguration' => [
-                'TheFirstController' => [
+                'MyExtension\Controller\TheFirstControllerController' => [
+                    'className' => 'MyExtension\Controller\TheFirstControllerController',
+                    'alias' => 'TheFirstController',
                     'actions' => ['show', 'index', 'new', 'create', 'delete', 'edit', 'update', 'setup', 'test']
                 ],
-                'TheSecondController' => [
+                'MyExtension\Controller\TheSecondControllerController' => [
+                    'className' => 'MyExtension\Controller\TheSecondControllerController',
+                    'alias' => 'TheSecondController',
                     'actions' => ['show', 'index']
                 ],
-                'TheThirdController' => [
+                'MyExtension\Controller\TheThirdControllerController' => [
+                    'className' => 'MyExtension\Controller\TheThirdControllerController',
+                    'alias' => 'TheThirdController',
                     'actions' => ['delete', 'create', 'onlyInThirdController']
                 ]
             ]
@@ -294,7 +300,10 @@ class RequestBuilderTest extends UnitTestCase
     {
         $this->expectException(Exception::class);
         $this->expectExceptionCode(1295479651);
-        $this->configuration['controllerConfiguration']['TheFirstController'] = [];
+        $this->configuration['controllerConfiguration']['MyExtension\Controller\TheFirstControllerController'] = [
+            'className' => 'MyExtension\Controller\TheFirstControllerController',
+            'alias' => 'TheFirstController',
+        ];
         $this->mockConfigurationManager->expects($this->any())->method('getConfiguration')->will($this->returnValue($this->configuration));
         $this->requestBuilder->_set('configurationManager', $this->mockConfigurationManager);
         $this->requestBuilder->_set('extensionService', $this->mockExtensionService);
@@ -310,6 +319,8 @@ class RequestBuilderTest extends UnitTestCase
         $this->expectExceptionCode(1316104317);
         $this->configuration['controllerConfiguration'] = [
             '' => [
+                'className' => '',
+                'alias' => '',
                 'actions' => ['foo']
             ]
         ];
@@ -349,8 +360,12 @@ class RequestBuilderTest extends UnitTestCase
                 ]
             ]
         ];
-        $this->mockRequest->expects($this->at(8))->method('setArgument')->with('parameter1', 'value1');
-        $this->mockRequest->expects($this->at(9))->method('setArgument')->with(
+
+        // testing at which position something gets called is fishy.
+        // why not make this a functional test and test with an actual requestBuilder instance and check the arguments
+        // later on?
+        $this->mockRequest->expects($this->at(9))->method('setArgument')->with('parameter1', 'value1');
+        $this->mockRequest->expects($this->at(10))->method('setArgument')->with(
             'parameter2',
             ['parameter3' => 'value3', 'parameter4' => 'value4']
         );
@@ -375,7 +390,8 @@ class RequestBuilderTest extends UnitTestCase
                 'format' => 'POST'
             ]
         ];
-        $this->mockRequest->expects($this->at(7))->method('setFormat')->with('POST');
+        // phew! Shitty position tests. Who thought this was a good idea?
+        $this->mockRequest->expects($this->at(8))->method('setFormat')->with('POST');
         $this->requestBuilder->build();
     }
 
@@ -386,7 +402,7 @@ class RequestBuilderTest extends UnitTestCase
     {
         $this->injectDependencies();
         $expectedResult = [
-            'TheFirstController' => [
+            'MyExtension\Controller\TheFirstControllerController' => [
                 'show',
                 'index',
                 'new',
@@ -397,11 +413,11 @@ class RequestBuilderTest extends UnitTestCase
                 'setup',
                 'test'
             ],
-            'TheSecondController' => [
+            'MyExtension\Controller\TheSecondControllerController' => [
                 'show',
                 'index'
             ],
-            'TheThirdController' => [
+            'MyExtension\Controller\TheThirdControllerController' => [
                 'delete',
                 'create',
                 'onlyInThirdController'
index 339f7d6..f9ec1d2 100644 (file)
@@ -15,6 +15,9 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Utility;
  */
 
 use TYPO3\CMS\Extbase\Core\Bootstrap;
+use TYPO3\CMS\Extbase\Tests\Unit\Utility\Fixtures\MyExtension\Controller\FirstController;
+use TYPO3\CMS\Extbase\Tests\Unit\Utility\Fixtures\MyExtension\Controller\SecondController;
+use TYPO3\CMS\Extbase\Tests\Unit\Utility\Fixtures\MyExtension\Controller\ThirdController;
 use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
@@ -64,7 +67,7 @@ class ExtensionUtilityTest extends UnitTestCase
     public function configurePluginWorksForMinimalisticSetup()
     {
         $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
-        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', ['Blog' => 'index']);
+        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [FirstController::class => 'index']);
         $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
         $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
         $this->assertContains('
@@ -81,7 +84,7 @@ class ExtensionUtilityTest extends UnitTestCase
     public function configurePluginCreatesCorrectDefaultTypoScriptSetup()
     {
         $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
-        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', ['Blog' => 'index']);
+        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [FirstController::class => 'index']);
         $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
         $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
     }
@@ -94,7 +97,7 @@ class ExtensionUtilityTest extends UnitTestCase
     {
         $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
         ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
-            'FirstController' => 'index'
+            FirstController::class => 'index'
         ]);
         $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
         $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
@@ -103,7 +106,9 @@ class ExtensionUtilityTest extends UnitTestCase
        pluginName = Pi1', $staticTypoScript);
         $expectedResult = [
             'controllers' => [
-                'FirstController' => [
+                FirstController::class => [
+                    'className' => FirstController::class,
+                    'alias' => 'First',
                     'actions' => ['index']
                 ]
             ],
@@ -146,9 +151,9 @@ class ExtensionUtilityTest extends UnitTestCase
     {
         $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
         ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
-            'FirstController' => 'index,show,new, create,delete,edit,update'
+            FirstController::class => 'index,show,new, create,delete,edit,update'
         ], [
-            'FirstController' => 'index,show'
+            FirstController::class => 'index,show'
         ]);
         $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
         $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
@@ -157,7 +162,9 @@ class ExtensionUtilityTest extends UnitTestCase
        pluginName = Pi1', $staticTypoScript);
         $expectedResult = [
             'controllers' => [
-                'FirstController' => [
+                FirstController::class => [
+                    'className' => FirstController::class,
+                    'alias' => 'First',
                     'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
                     'nonCacheableActions' => ['index', 'show']
                 ]
@@ -175,9 +182,9 @@ class ExtensionUtilityTest extends UnitTestCase
     {
         $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
         ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
-            'FirstController' => 'index,show,new, create,delete,edit,update'
+            FirstController::class => 'index,show,new, create,delete,edit,update'
         ], [
-            'FirstController' => 'new,show'
+            FirstController::class => 'new,show'
         ]);
         $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
         $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
@@ -186,7 +193,9 @@ class ExtensionUtilityTest extends UnitTestCase
        pluginName = Pi1', $staticTypoScript);
         $expectedResult = [
             'controllers' => [
-                'FirstController' => [
+                FirstController::class => [
+                    'className' => FirstController::class,
+                    'alias' => 'First',
                     'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
                     'nonCacheableActions' => ['new', 'show']
                 ]
@@ -204,23 +213,29 @@ class ExtensionUtilityTest extends UnitTestCase
     {
         $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
         ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
-            'FirstController' => 'index,show,new,create,delete,edit,update',
-            'SecondController' => 'index,show,delete',
-            'ThirdController' => 'create'
+            FirstController::class => 'index,show,new,create,delete,edit,update',
+            SecondController::class => 'index,show,delete',
+            ThirdController::class => 'create'
         ], [
-            'FirstController' => 'new,create,edit,update',
-            'ThirdController' => 'create'
+            FirstController::class => 'new,create,edit,update',
+            ThirdController::class => 'create'
         ]);
         $expectedResult = [
             'controllers' => [
-                'FirstController' => [
+                FirstController::class => [
+                    'className' => FirstController::class,
+                    'alias' => 'First',
                     'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
                     'nonCacheableActions' => ['new', 'create', 'edit', 'update']
                 ],
-                'SecondController' => [
+                SecondController::class => [
+                    'className' => SecondController::class,
+                    'alias' => 'Second',
                     'actions' => ['index', 'show', 'delete']
                 ],
-                'ThirdController' => [
+                ThirdController::class => [
+                    'className' => ThirdController::class,
+                    'alias' => 'Third',
                     'actions' => ['create'],
                     'nonCacheableActions' => ['create']
                 ]
@@ -238,25 +253,31 @@ class ExtensionUtilityTest extends UnitTestCase
     {
         $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
         ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
-            'FirstController' => 'index,show,new,create,delete,edit,update',
-            'SecondController' => 'index,show,delete',
-            'ThirdController' => 'create'
+            FirstController::class => 'index,show,new,create,delete,edit,update',
+            SecondController::class => 'index,show,delete',
+            ThirdController::class => 'create'
         ], [
-            'FirstController' => 'index,new,create,edit,update',
-            'SecondController' => 'delete',
-            'ThirdController' => 'create'
+            FirstController::class => 'index,new,create,edit,update',
+            SecondController::class => 'delete',
+            ThirdController::class => 'create'
         ]);
         $expectedResult = [
             'controllers' => [
-                'FirstController' => [
+                FirstController::class => [
+                    'className' => FirstController::class,
+                    'alias' => 'First',
                     'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
                     'nonCacheableActions' => ['index', 'new', 'create', 'edit', 'update']
                 ],
-                'SecondController' => [
+                SecondController::class => [
+                    'className' => SecondController::class,
+                    'alias' => 'Second',
                     'actions' => ['index', 'show', 'delete'],
                     'nonCacheableActions' => ['delete']
                 ],
-                'ThirdController' => [
+                ThirdController::class => [
+                    'className' => ThirdController::class,
+                    'alias' => 'Third',
                     'actions' => ['create'],
                     'nonCacheableActions' => ['create']
                 ]
@@ -348,4 +369,181 @@ class ExtensionUtilityTest extends UnitTestCase
         \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter($typeConverterClassName);
         $this->assertEquals(1, count($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters']));
     }
+
+    /**
+     * DataProvider for explodeObjectControllerName
+     *
+     * @return array
+     */
+    public function controllerArgumentsAndExpectedObjectName()
+    {
+        return [
+            'Vendor TYPO3\CMS, extension, controller given' => [
+                [
+                    'vendorName' => 'TYPO3\\CMS',
+                    'extensionName' => 'Ext',
+                    'subpackageKey' => '',
+                    'controllerName' => 'Foo',
+                ],
+                'TYPO3\\CMS\\Ext\\Controller\\FooController',
+            ],
+            'Vendor TYPO3\CMS, extension, subpackage, controlle given' => [
+                [
+                    'vendorName' => 'TYPO3\\CMS',
+                    'extensionName' => 'Fluid',
+                    'subpackageKey' => 'ViewHelpers\\Widget',
+                    'controllerName' => 'Paginate',
+                ],
+                \TYPO3\CMS\Fluid\ViewHelpers\Widget\Controller\PaginateController::class,
+            ],
+            'Vendor VENDOR, extension, controller given' => [
+                [
+                    'vendorName' => 'VENDOR',
+                    'extensionName' => 'Ext',
+                    'subpackageKey' => '',
+                    'controllerName' => 'Foo',
+                ],
+                'VENDOR\\Ext\\Controller\\FooController',
+            ],
+            'Vendor VENDOR, extension subpackage, controller given' => [
+                [
+                    'vendorName' => 'VENDOR',
+                    'extensionName' => 'Ext',
+                    'subpackageKey' => 'ViewHelpers\\Widget',
+                    'controllerName' => 'Foo',
+                ],
+                'VENDOR\\Ext\\ViewHelpers\\Widget\\Controller\\FooController',
+            ],
+        ];
+    }
+
+    /**
+     * @dataProvider controllerArgumentsAndExpectedObjectName
+     *
+     * @param array $controllerArguments
+     * @param string $controllerObjectName
+     * @test
+     */
+    public function getControllerObjectNameResolvesControllerObjectNameCorrectly($controllerArguments, $controllerObjectName)
+    {
+        $this->assertEquals(
+            $controllerObjectName,
+            ExtensionUtility::getControllerClassName(
+                $controllerArguments['vendorName'],
+                $controllerArguments['extensionName'],
+                $controllerArguments['subpackageKey'],
+                $controllerArguments['controllerName']
+            )
+        );
+    }
+
+    /**
+     * @return array
+     */
+    public function checkResolveControllerAliasFromControllerClassNameDataProvider()
+    {
+        return [
+            'Class in root namespace without controller suffix' => [
+                '',
+                'Foo',
+            ],
+            'Class in root namespace without controller suffix (2)' => [
+                '',
+                'FooBarBazQuxBlaBlub',
+            ],
+            'Controller in root namespace' => [
+                'Foo',
+                'FooController',
+            ],
+            'Controller in root namespace (lowercase)' => [
+                'foo',
+                'fooController',
+            ],
+            'Controller in namespace' => [
+                'Foo',
+                'TYPO3\\CMS\\Ext\\Controller\\FooController',
+            ],
+            'Controller in arbitrary namespace' => [
+                'Foo',
+                'Foo\\Bar\\baz\\qUx\\FooController',
+            ],
+            'Controller with lowercase suffix' => [
+                '',
+                'Foo\\Bar\\baz\\qUx\\Foocontroller',
+            ],
+        ];
+    }
+
+    /**
+     * @dataProvider checkResolveControllerAliasFromControllerClassNameDataProvider
+     *
+     * @param string $expectedControllerAlias
+     * @param string $controllerClassName
+     * @test
+     */
+    public function checkResolveControllerAliasFromControllerClassName(string $expectedControllerAlias, string $controllerClassName)
+    {
+        $this->assertEquals(
+            $expectedControllerAlias,
+            ExtensionUtility::resolveControllerAliasFromControllerClassName(
+                $controllerClassName
+            )
+        );
+    }
+
+    /**
+     * @return array
+     */
+    public function checkResolveVendorFromExtensionAndControllerClassNameDataProvider()
+    {
+        return [
+            'Class in root namespace' => [
+                '',
+                'IndexedSearch',
+                'Foo',
+            ],
+            'Namespaced class without extension name as namespace part' => [
+                '',
+                'IndexedSearch',
+                'Foo\\Bar\\Baz\\Qux',
+            ],
+            'Namespaced class without vendor part before extension name part' => [
+                '',
+                'IndexedSearch',
+                'IndexedSearch\\Controller\\SearchController',
+            ],
+            'Namespaced class with single vendor part' => [
+                'Foo',
+                'IndexedSearch',
+                'Foo\\IndexedSearch\\Controller\\SearchController',
+            ],
+            'Namespaced class with multiple vendor parts' => [
+                'TYPO\\CMS',
+                'IndexedSearch',
+                'TYPO\\CMS\\IndexedSearch\\Controller\\SearchController',
+            ],
+        ];
+    }
+
+    /**
+     * @dataProvider checkResolveVendorFromExtensionAndControllerClassNameDataProvider
+     *
+     * @param string $expectedVendor
+     * @param string $extensionName
+     * @param string $controllerClassName
+     * @test
+     */
+    public function checkResolveVendorFromExtensionAndControllerClassName(
+        string $expectedVendor,
+        string $extensionName,
+        string $controllerClassName
+    ) {
+        $this->assertEquals(
+            $expectedVendor,
+            ExtensionUtility::resolveVendorFromExtensionAndControllerClassName(
+                $extensionName,
+                $controllerClassName
+            )
+        );
+    }
 }
diff --git a/typo3/sysext/extbase/Tests/Unit/Utility/Fixtures/MyExtension/Controller/FirstController.php b/typo3/sysext/extbase/Tests/Unit/Utility/Fixtures/MyExtension/Controller/FirstController.php
new file mode 100644 (file)
index 0000000..b7f6cdf
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Tests\Unit\Utility\Fixtures\MyExtension\Controller;
+
+class FirstController
+{
+}
diff --git a/typo3/sysext/extbase/Tests/Unit/Utility/Fixtures/MyExtension/Controller/SecondController.php b/typo3/sysext/extbase/Tests/Unit/Utility/Fixtures/MyExtension/Controller/SecondController.php
new file mode 100644 (file)
index 0000000..50d1246
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Tests\Unit\Utility\Fixtures\MyExtension\Controller;
+
+class SecondController
+{
+}
diff --git a/typo3/sysext/extbase/Tests/Unit/Utility/Fixtures/MyExtension/Controller/ThirdController.php b/typo3/sysext/extbase/Tests/Unit/Utility/Fixtures/MyExtension/Controller/ThirdController.php
new file mode 100644 (file)
index 0000000..a2aaa6e
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Tests\Unit\Utility\Fixtures\MyExtension\Controller;
+
+class ThirdController
+{
+}
diff --git a/typo3/sysext/extbase/Tests/UnitDeprecated/Utility/ExtensionUtilityTest.php b/typo3/sysext/extbase/Tests/UnitDeprecated/Utility/ExtensionUtilityTest.php
new file mode 100644 (file)
index 0000000..1f946d3
--- /dev/null
@@ -0,0 +1,260 @@
+<?php
+namespace TYPO3\CMS\Extbase\Tests\UnitDeprecated\Utility;
+
+/*
+ * 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 TYPO3\CMS\Extbase\Core\Bootstrap;
+use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+/**
+ * Testcase for class \TYPO3\CMS\Extbase\Utility\ExtensionUtility
+ */
+class ExtensionUtilityTest extends UnitTestCase
+{
+    protected function setUp()
+    {
+        $GLOBALS['TSFE'] = new \stdClass();
+        $GLOBALS['TSFE']->tmpl = new \stdClass();
+        $GLOBALS['TSFE']->tmpl->setup = [];
+        $GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.'] = [
+            '9' => 'CASE',
+            '9.' => [
+                'key.' => [
+                    'field' => 'layout'
+                ],
+                0 => '< plugin.tt_news'
+            ],
+            'extensionname_someplugin' => 'USER',
+            'extensionname_someplugin.' => [
+                'userFunc' => Bootstrap::class . '->run',
+                'extensionName' => 'ExtensionName',
+                'pluginName' => 'SomePlugin'
+            ],
+            'someotherextensionname_secondplugin' => 'USER',
+            'someotherextensionname_secondplugin.' => [
+                'userFunc' => Bootstrap::class . '->run',
+                'extensionName' => 'SomeOtherExtensionName',
+                'pluginName' => 'SecondPlugin'
+            ],
+            'extensionname_thirdplugin' => 'USER',
+            'extensionname_thirdplugin.' => [
+                'userFunc' => Bootstrap::class . '->run',
+                'extensionName' => 'ExtensionName',
+                'pluginName' => 'ThirdPlugin'
+            ]
+        ];
+    }
+
+    /**
+     * @test
+     * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
+     */
+    public function configurePluginWorksForMinimalisticSetup()
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
+        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', ['Blog' => 'index']);
+        $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
+        $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
+        $this->assertContains('
+       userFunc = TYPO3\\CMS\\Extbase\\Core\\Bootstrap->run
+       extensionName = MyExtension
+       pluginName = Pi1', $staticTypoScript);
+        $this->assertNotContains('USER_INT', $staticTypoScript);
+    }
+
+    /**
+     * @test
+     * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
+     */
+    public function configurePluginCreatesCorrectDefaultTypoScriptSetup()
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
+        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', ['Blog' => 'index']);
+        $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
+        $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
+    }
+
+    /**
+     * @test
+     * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
+     */
+    public function configurePluginWorksForASingleControllerAction()
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
+        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
+            'FirstController' => 'index'
+        ]);
+        $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
+        $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
+        $this->assertContains('
+       extensionName = MyExtension
+       pluginName = Pi1', $staticTypoScript);
+        $expectedResult = [
+            'controllers' => [
+                'MyExtension\Controller\FirstControllerController' => [
+                    'className' => 'MyExtension\Controller\FirstControllerController',
+                    'alias' => 'FirstController',
+                    'actions' => ['index']
+                ]
+            ],
+            'pluginType' => 'list_type'
+        ];
+        $this->assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
+    }
+
+    /**
+     * @test
+     * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
+     */
+    public function configurePluginRespectsDefaultActionAsANonCacheableAction()
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
+        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
+            'First' => 'index,show,new, create,delete,edit,update'
+        ], [
+            'First' => 'index,show'
+        ]);
+        $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
+        $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
+        $this->assertContains('
+       extensionName = MyExtension
+       pluginName = Pi1', $staticTypoScript);
+        $expectedResult = [
+            'controllers' => [
+                'MyExtension\Controller\FirstController' => [
+                    'className' => 'MyExtension\Controller\FirstController',
+                    'alias' => 'First',
+                    'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
+                    'nonCacheableActions' => ['index', 'show']
+                ]
+            ],
+            'pluginType' => 'list_type'
+        ];
+        $this->assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
+    }
+
+    /**
+     * @test
+     * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
+     */
+    public function configurePluginRespectsNonDefaultActionAsANonCacheableAction()
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
+        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
+            'First' => 'index,show,new, create,delete,edit,update'
+        ], [
+            'First' => 'new,show'
+        ]);
+        $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
+        $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
+        $this->assertContains('
+       extensionName = MyExtension
+       pluginName = Pi1', $staticTypoScript);
+        $expectedResult = [
+            'controllers' => [
+                'MyExtension\Controller\FirstController' => [
+                    'className' => 'MyExtension\Controller\FirstController',
+                    'alias' => 'First',
+                    'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
+                    'nonCacheableActions' => ['new', 'show']
+                ]
+            ],
+            'pluginType' => 'list_type'
+        ];
+        $this->assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
+    }
+
+    /**
+     * @test
+     * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
+     */
+    public function configurePluginWorksForMultipleControllerActionsWithCacheableActionAsDefault()
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
+        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
+            'First' => 'index,show,new,create,delete,edit,update',
+            'Second' => 'index,show,delete',
+            'Third' => 'create'
+        ], [
+            'First' => 'new,create,edit,update',
+            'Third' => 'create'
+        ]);
+        $expectedResult = [
+            'controllers' => [
+                'MyExtension\Controller\FirstController' => [
+                    'className' => 'MyExtension\Controller\FirstController',
+                    'alias' => 'First',
+                    'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
+                    'nonCacheableActions' => ['new', 'create', 'edit', 'update']
+                ],
+                'MyExtension\Controller\SecondController' => [
+                    'className' => 'MyExtension\Controller\SecondController',
+                    'alias' => 'Second',
+                    'actions' => ['index', 'show', 'delete']
+                ],
+                'MyExtension\Controller\ThirdController' => [
+                    'className' => 'MyExtension\Controller\ThirdController',
+                    'alias' => 'Third',
+                    'actions' => ['create'],
+                    'nonCacheableActions' => ['create']
+                ]
+            ],
+            'pluginType' => 'list_type'
+        ];
+        $this->assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
+    }
+
+    /**
+     * @test
+     * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
+     */
+    public function configurePluginWorksForMultipleControllerActionsWithNonCacheableActionAsDefault()
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
+        ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
+            'First' => 'index,show,new,create,delete,edit,update',
+            'Second' => 'index,show,delete',
+            'Third' => 'create'
+        ], [
+            'First' => 'index,new,create,edit,update',
+            'Second' => 'delete',
+            'Third' => 'create'
+        ]);
+        $expectedResult = [
+            'controllers' => [
+                'MyExtension\Controller\FirstController' => [
+                    'className' => 'MyExtension\Controller\FirstController',
+                    'alias' => 'First',
+                    'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
+                    'nonCacheableActions' => ['index', 'new', 'create', 'edit', 'update']
+                ],
+                'MyExtension\Controller\SecondController' => [
+                    'className' => 'MyExtension\Controller\SecondController',
+                    'alias' => 'Second',
+                    'actions' => ['index', 'show', 'delete'],
+                    'nonCacheableActions' => ['delete']
+                ],
+                'MyExtension\Controller\ThirdController' => [
+                    'className' => 'MyExtension\Controller\ThirdController',
+                    'alias' => 'Third',
+                    'actions' => ['create'],
+                    'nonCacheableActions' => ['create']
+                ]
+            ],
+            'pluginType' => 'list_type'
+        ];
+        $this->assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
+    }
+}
index 3ef9cc1..61931f0 100644 (file)
@@ -2,19 +2,18 @@
 defined('TYPO3_MODE') or die();
 
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-    'TYPO3.CMS.Extensionmanager',
+    'Extensionmanager',
     'tools',
     'extensionmanager',
     '',
     [
-        'List' => 'index,unresolvedDependencies,ter,showAllVersions,distributions',
-        'Action' => 'toggleExtensionInstallationState,installExtensionWithoutSystemDependencyCheck,removeExtension,downloadExtensionZip,reloadExtensionData',
-        'Configuration' => 'showConfigurationForm,save,saveAndClose',
-        'Download' => 'checkDependencies,installFromTer,installExtensionWithoutSystemDependencyCheck,installDistribution,updateExtension,updateCommentForUpdatableVersions',
-        'UpdateScript' => 'show',
-        'UpdateFromTer' => 'updateExtensionListFromTer',
-        'UploadExtensionFile' => 'form,extract',
-        'Distribution' => 'show'
+        \TYPO3\CMS\Extensionmanager\Controller\ListController::class => 'index,unresolvedDependencies,ter,showAllVersions,distributions',
+        \TYPO3\CMS\Extensionmanager\Controller\ActionController::class => 'toggleExtensionInstallationState,installExtensionWithoutSystemDependencyCheck,removeExtension,downloadExtensionZip,reloadExtensionData',
+        \TYPO3\CMS\Extensionmanager\Controller\DownloadController::class => 'checkDependencies,installFromTer,installExtensionWithoutSystemDependencyCheck,installDistribution,updateExtension,updateCommentForUpdatableVersions',
+        \TYPO3\CMS\Extensionmanager\Controller\UpdateScriptController::class => 'show',
+        \TYPO3\CMS\Extensionmanager\Controller\UpdateFromTerController::class => 'updateExtensionListFromTer',
+        \TYPO3\CMS\Extensionmanager\Controller\UploadExtensionFileController::class => 'form,extract',
+        \TYPO3\CMS\Extensionmanager\Controller\DistributionController::class => 'show'
     ],
     [
         'access' => 'systemMaintainer',
index 19cb5dc..ab764f0 100644 (file)
@@ -2,12 +2,12 @@
 defined('TYPO3_MODE') or die();
 
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-    'TYPO3.CMS.Filelist',
+    'Filelist',
     'file',
     'list',
     '',
     [
-        'FileList' => 'index, search',
+        \TYPO3\CMS\Filelist\Controller\FileListController::class => 'index, search',
     ],
     [
         'access' => 'user,group',
index d8e766d..cdf7abc 100644 (file)
@@ -44,7 +44,9 @@ class TemplatesPathsTest extends FunctionalTestCase
                         'plugins' => [
                             'Pi' => [
                                 'controllers' => [
-                                    'Template' => [
+                                    \TYPO3Fluid\FluidTest\Controller\TemplateController::class => [
+                                        'className' => \TYPO3Fluid\FluidTest\Controller\TemplateController::class,
+                                        'alias' => 'Template',
                                         'actions' => [
                                             'baseTemplate',
                                         ],
index 888e262..706d3c6 100644 (file)
@@ -78,10 +78,10 @@ call_user_func(function () {
 
     // Register FE plugin
     \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
-        'TYPO3.CMS.Form',
+        'Form',
         'Formframework',
-        ['FormFrontend' => 'render, perform'],
-        ['FormFrontend' => 'perform'],
+        [\TYPO3\CMS\Form\Controller\FormFrontendController::class => 'render, perform'],
+        [\TYPO3\CMS\Form\Controller\FormFrontendController::class => 'perform'],
         \TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
     );
 
index c36388f..5c1cb44 100644 (file)
@@ -3,13 +3,13 @@ defined('TYPO3_MODE') or die();
 
 // Register the backend module Web->Forms
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-    'TYPO3.CMS.Form',
+    'Form',
     'web',
     'formbuilder',
     '',
     [
-        'FormManager' => 'index, show, create, duplicate, references, delete',
-        'FormEditor' => 'index, saveForm, renderFormPage, renderRenderableOptions',
+        \TYPO3\CMS\Form\Controller\FormManagerController::class => 'index, show, create, duplicate, references, delete',
+        \TYPO3\CMS\Form\Controller\FormEditorController::class => 'index, saveForm, renderFormPage, renderRenderableOptions',
     ],
     [
         'access' => 'user,group',
index fa9bfba..8b393c2 100644 (file)
@@ -2,7 +2,12 @@
 defined('TYPO3_MODE') or die();
 
 // register extbase plugin
-\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin('TYPO3.CMS.IndexedSearch', 'Pi2', ['Search' => 'form,search,noTypoScript'], ['Search' => 'form,search']);
+\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
+    'IndexedSearch',
+    'Pi2',
+    [\TYPO3\CMS\IndexedSearch\Controller\SearchController::class => 'form,search,noTypoScript'],
+    [\TYPO3\CMS\IndexedSearch\Controller\SearchController::class => 'form,search']
+);
 
 // Attach to hooks:
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageIndexing'][] = \TYPO3\CMS\IndexedSearch\Indexer::class;
index 971ad4e..c9887c3 100644 (file)
@@ -2,12 +2,12 @@
 defined('TYPO3_MODE') or die();
 
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-    'TYPO3.CMS.IndexedSearch',
+    'IndexedSearch',
     'web',
     'isearch',
     '',
     [
-        'Administration' => 'index,pages,externalDocuments,statistic,statisticDetails,deleteIndexedItem,saveStopwordsKeywords,wordDetail',
+        \TYPO3\CMS\IndexedSearch\Controller\AdministrationController::class => 'index,pages,externalDocuments,statistic,statisticDetails,deleteIndexedItem,saveStopwordsKeywords,wordDetail',
     ],
     [
         'access' => 'admin',
index 2bfb21d..fb54759 100644 (file)
@@ -840,4 +840,18 @@ return [
             'Breaking-87193-DeprecatedFunctionalityRemoved.rst',
         ],
     ],
+    'TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin' => [
+        'numberOfMandatoryArguments' => 3,
+        'maximumNumberOfArguments' => 5,
+        'restFiles' => [
+            'Deprecation-87550-UseControllerClassesWhenRegisteringPluginsmodules.rst',
+        ],
+    ],
+    'TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 6,
+        'restFiles' => [
+            'Deprecation-87550-UseControllerClassesWhenRegisteringPluginsmodules.rst',
+        ],
+    ],
 ];
index ac93ad4..b4994a1 100644 (file)
@@ -3,13 +3,13 @@ defined('TYPO3_MODE') or die();
 
 // Registers the workspaces Backend Module
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-    'TYPO3.CMS.Workspaces',
+    'Workspaces',
     'web',
     'workspaces',
     'before:info',
     [
         // An array holding the controller-action-combinations that are accessible
-        'Review' => 'index,fullIndex,singleIndex'
+        \TYPO3\CMS\Workspaces\Controller\ReviewController::class => 'index,fullIndex,singleIndex'
     ],
     [
         'access' => 'user,group',