From 9369ea39f03abf65a16451046f1636843c48a739 Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Fri, 25 May 2018 11:15:29 +0200
Subject: [PATCH] [TASK] Deprecate Backend Module Routing methods

Since TYPO3 v9.0, all routing in backend is handled unified, thus, the old
methods from historical reasons can be deprecated.

- BackendUtility::getModuleUrl()
- UriBuilder->buildUriFromModule()

Instead of guessing the name to a module route, an "id" for the route (like the path)
can be explictly given.

Resolves: #85113
Releases: master
Change-Id: I8cf2b9dc3a938dd7227dcc972e0782619ede4213
Reviewed-on: https://review.typo3.org/57048
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
---
 .../backend/Classes/Routing/UriBuilder.php    |  2 +
 .../Classes/Utility/BackendUtility.php        |  3 +-
 .../Utility/ExtensionManagementUtility.php    | 18 +++------
 ...5113-LegacyBackendModuleRoutingMethods.rst | 38 +++++++++++++++++++
 .../Classes/Mvc/Web/Routing/UriBuilder.php    | 25 +++++++-----
 .../Unit/Mvc/Web/Routing/UriBuilderTest.php   |  9 +++--
 .../Php/MethodCallMatcher.php                 |  7 ++++
 .../Php/MethodCallStaticMatcher.php           |  7 ++++
 .../Classes/RecordList/DatabaseRecordList.php |  2 +-
 9 files changed, 83 insertions(+), 28 deletions(-)
 create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Deprecation-85113-LegacyBackendModuleRoutingMethods.rst

diff --git a/typo3/sysext/backend/Classes/Routing/UriBuilder.php b/typo3/sysext/backend/Classes/Routing/UriBuilder.php
index 6086927ac16a..10e998d8bff0 100644
--- a/typo3/sysext/backend/Classes/Routing/UriBuilder.php
+++ b/typo3/sysext/backend/Classes/Routing/UriBuilder.php
@@ -121,9 +121,11 @@ class UriBuilder
      * @param string $referenceType The type of reference to be generated (one of the constants)
      *
      * @return Uri The generated Uri
+     * @deprecated since TYPO3 v9.3, will be removed in TYPO3 v10. Use buildUriFromRoute() instead.
      */
     public function buildUriFromModule($moduleName, $parameters = [], $referenceType = self::ABSOLUTE_PATH)
     {
+        trigger_error('UriBuilder->buildUriFromModule() will be removed in TYPO3 v10, use buildUriFromRoute() instead.', E_USER_DEPRECATED);
         $parameters = [
             'route' => $moduleName,
             'token' => FormProtectionFactory::get('backend')->generateToken('route', $moduleName)
diff --git a/typo3/sysext/backend/Classes/Utility/BackendUtility.php b/typo3/sysext/backend/Classes/Utility/BackendUtility.php
index b6476d9f8819..5f33c73ab414 100644
--- a/typo3/sysext/backend/Classes/Utility/BackendUtility.php
+++ b/typo3/sysext/backend/Classes/Utility/BackendUtility.php
@@ -3205,10 +3205,11 @@ class BackendUtility
      * @param string $moduleName Name of the module
      * @param array $urlParameters URL parameters that should be added as key value pairs
      * @return string Calculated URL
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10. Use UriBuilder instead.
      */
     public static function getModuleUrl($moduleName, $urlParameters = [])
     {
-        /** @var UriBuilder $uriBuilder */
+        trigger_error('BackendUtility::getModuleUrl() will be removed in TYPO3 v10, use UriBuilder->buildUriFromRoute() instead.', E_USER_DEPRECATED);
         $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
         try {
             $uri = $uriBuilder->buildUriFromRoute($moduleName, $urlParameters);
diff --git a/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php b/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php
index f588433d68ce..7e647b832e37 100644
--- a/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php
+++ b/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php
@@ -15,6 +15,8 @@ namespace TYPO3\CMS\Core\Utility;
  */
 
 use Symfony\Component\Finder\Finder;
+use TYPO3\CMS\Backend\Routing\Route;
+use TYPO3\CMS\Backend\Routing\Router;
 use TYPO3\CMS\Core\Category\CategoryRegistry;
 use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Imaging\IconRegistry;
@@ -904,13 +906,9 @@ class ExtensionManagementUtility
         }
 
         // Also register the module as regular route
+        $routeName = $moduleConfiguration['id'] ?? $fullModuleSignature;
         // Build Route objects from the data
-        $name = $fullModuleSignature;
-        if (isset($moduleConfiguration['path'])) {
-            $path = $moduleConfiguration['path'];
-        } else {
-            $path = str_replace('_', '/', $name);
-        }
+        $path = $moduleConfiguration['path'] ?? str_replace('_', '/', $fullModuleSignature);
         $path = '/' . trim($path, '/') . '/';
 
         $options = [
@@ -922,12 +920,8 @@ class ExtensionManagementUtility
             $options['target'] = $moduleConfiguration['routeTarget'];
         }
 
-        $router = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Router::class);
-        $router->addRoute(
-            $name,
-            // @todo: see if we should do a "module route"
-            GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Route::class, $path, $options)
-        );
+        $router = GeneralUtility::makeInstance(Router::class);
+        $router->addRoute($routeName, GeneralUtility::makeInstance(Route::class, $path, $options));
     }
 
     /**
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-85113-LegacyBackendModuleRoutingMethods.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-85113-LegacyBackendModuleRoutingMethods.rst
new file mode 100644
index 000000000000..7bb6cb6e0f9c
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-85113-LegacyBackendModuleRoutingMethods.rst
@@ -0,0 +1,38 @@
+.. include:: ../../Includes.txt
+
+===========================================================
+Deprecation: #85113 - Legacy Backend Module Routing methods
+===========================================================
+
+See :issue:`85113`
+
+Description
+===========
+
+In TYPO3 v9, Backend routing was unified to be handled via the "route" query string parameter. Backend modules
+are now automatically registered to be a backend route.
+
+The following methods are thus deprecated in favor of using the method above.
+
+* :php:`BackendUtility::getModuleUrl()`
+* :php:`UriBuilder->buildUriFromModule()`
+
+
+Impact
+======
+
+Calling one of the deprecated methods above will trigger a deprecation message.
+
+
+Affected Installations
+======================
+
+Any TYPO3 installation with a custom backend-related extension using one of the methods directly in a PHP context.
+
+
+Migration
+=========
+
+Use :php:`UriBuilder->buildUriFromRoute($moduleIdentifier)` instead.
+
+.. index:: Backend, PHP-API, FullyScanned
diff --git a/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php b/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php
index 9d8dbd1dcbae..ea6b929bb555 100644
--- a/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php
+++ b/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Extbase\Mvc\Web\Routing;
  */
 
 use TYPO3\CMS\Backend\Routing\Exception\ResourceNotFoundException;
+use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Mvc\Request;
@@ -667,31 +668,35 @@ class UriBuilder
             $id = GeneralUtility::_GP('id');
             // backwards compatibility: check for M parameter
             // @deprecated since TYPO3 CMS 9, will be removed in TYPO3 CMS 10.
-            $module = GeneralUtility::_GP('route') ?: GeneralUtility::_GP('M');
+            $route = GeneralUtility::_GP('route') ?: GeneralUtility::_GP('M');
             if ($id !== null) {
                 $arguments['id'] = $id;
             }
-            if ($module !== null) {
-                $arguments['route'] = $module;
+            if ($route !== null) {
+                $arguments['route'] = $route;
             }
         }
         ArrayUtility::mergeRecursiveWithOverrule($arguments, $this->arguments);
         $arguments = $this->convertDomainObjectsToIdentityArrays($arguments);
         $this->lastArguments = $arguments;
-        $moduleName = $arguments['route'] ?? null;
+        $routeName = $arguments['route'] ?? null;
         unset($arguments['route'], $arguments['token']);
         $backendUriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
         try {
             if ($this->request instanceof WebRequest && $this->createAbsoluteUri) {
-                $uri = (string)$backendUriBuilder->buildUriFromRoutePath($moduleName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
+                $uri = (string)$backendUriBuilder->buildUriFromRoutePath($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
             } else {
-                $uri = (string)$backendUriBuilder->buildUriFromRoutePath($moduleName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
+                $uri = (string)$backendUriBuilder->buildUriFromRoutePath($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
             }
         } catch (ResourceNotFoundException $e) {
-            if ($this->request instanceof WebRequest && $this->createAbsoluteUri) {
-                $uri = (string)$backendUriBuilder->buildUriFromModule($moduleName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
-            } else {
-                $uri = (string)$backendUriBuilder->buildUriFromModule($moduleName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
+            try {
+                if ($this->request instanceof WebRequest && $this->createAbsoluteUri) {
+                    $uri = (string)$backendUriBuilder->buildUriFromRoute($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
+                } else {
+                    $uri = (string)$backendUriBuilder->buildUriFromRoute($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
+                }
+            } catch (RouteNotFoundException $e) {
+                $uri = '';
             }
         }
         if ($this->section !== '') {
diff --git a/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php b/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php
index 5cf4d0ba5d88..f84b16007f88 100644
--- a/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php
+++ b/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php
@@ -83,6 +83,7 @@ class UriBuilderTest extends UnitTestCase
         $router = GeneralUtility::makeInstance(Router::class);
         $router->addRoute('module_key', new Route('/test/Path', []));
         $router->addRoute('module_key2', new Route('/test/Path2', []));
+        $router->addRoute('', new Route('', []));
         // Mocking backend user is required for backend URI generation as BackendUtility::getModuleUrl() is called
         $backendUserMock = $this->createMock(BackendUserAuthentication::class);
         $backendUserMock->expects($this->any())->method('check')->will($this->returnValue(true));
@@ -258,7 +259,7 @@ class UriBuilderTest extends UnitTestCase
                     'route',
                     'id'
                 ],
-                '/typo3/index.php?route=&token=dummyToken&foo=bar&foo2=bar2'
+                '/typo3/index.php?route=%2F&token=dummyToken&foo=bar&foo2=bar2'
             ],
             'Arguments to be excluded in the end' => [
                 [
@@ -273,7 +274,7 @@ class UriBuilderTest extends UnitTestCase
                     'route',
                     'id'
                 ],
-                '/typo3/index.php?route=&token=dummyToken&foo=bar&foo2=bar2'
+                '/typo3/index.php?route=%2F&token=dummyToken&foo=bar&foo2=bar2'
             ],
             'Arguments in nested array to be excluded' => [
                 [
@@ -428,7 +429,7 @@ class UriBuilderTest extends UnitTestCase
         ];
         $this->uriBuilder->setAddQueryString(true);
         $this->uriBuilder->setAddQueryStringMethod('POST,GET');
-        $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('/typo3/index.php?route=&token=dummyToken&key1=POST1&key2=GET2&key3[key31]=POST31&key3[key32]=GET32&key3[key33][key331]=GET331&key3[key33][key332]=POST332');
+        $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('/typo3/index.php?route=%2F&token=dummyToken&key1=POST1&key2=GET2&key3[key31]=POST31&key3[key32]=GET32&key3[key33][key331]=GET331&key3[key33][key332]=POST332');
         $actualResult = $this->uriBuilder->buildBackendUri();
         $this->assertEquals($expectedResult, $actualResult);
     }
@@ -461,7 +462,7 @@ class UriBuilderTest extends UnitTestCase
         ];
         $this->uriBuilder->setAddQueryString(true);
         $this->uriBuilder->setAddQueryStringMethod('GET,POST');
-        $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('/typo3/index.php?route=&token=dummyToken&key1=GET1&key2=POST2&key3[key31]=GET31&key3[key32]=POST32&key3[key33][key331]=POST331&key3[key33][key332]=GET332');
+        $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('/typo3/index.php?route=%2F&token=dummyToken&key1=GET1&key2=POST2&key3[key31]=GET31&key3[key32]=POST32&key3[key33][key331]=POST331&key3[key33][key332]=GET332');
         $actualResult = $this->uriBuilder->buildBackendUri();
         $this->assertEquals($expectedResult, $actualResult);
     }
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
index 3dfd88579845..60cd80afcebc 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
@@ -2317,4 +2317,11 @@ return [
             'Deprecation-85125-UsagesOfCharsetConverterInCore.rst'
         ],
     ],
+    'TYPO3\CMS\Backend\Routing\UriBuilder->buildUriFromModule' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-85113-LegacyBackendModuleRoutingMethods.rst',
+        ]
+    ],
 ];
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php
index a47d5faae27d..08b1ec360850 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php
@@ -596,4 +596,11 @@ return [
             'Deprecation-85102-PhpOptionsUtility.rst',
         ],
     ],
+    'TYPO3\CMS\Backend\Utility\BackendUtility::getModuleUrl' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 2,
+        'restFiles' => [
+            'Deprecation-85113-LegacyBackendModuleRoutingMethods.rst',
+        ],
+    ],
 ];
diff --git a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
index b1d5d64abcd0..0acccfe93d1c 100644
--- a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
+++ b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
@@ -2002,7 +2002,7 @@ class DatabaseRecordList
         if ($this->isEditable($table)) {
             // "Revert" link (history/undo)
             if ((bool)\trim($userTsConfig['options.']['showHistory.'][$table] ?? $userTsConfig['options.']['showHistory'] ?? '1')) {
-                $moduleUrl = BackendUtility::getModuleUrl('record_history', ['element' => $table . ':' . $row['uid']]);
+                $moduleUrl = (string)$uriBuilder->buildUriFromRoute('record_history', ['element' => $table . ':' . $row['uid']]);
                 $onClick = 'return jumpExt(' . GeneralUtility::quoteJSvalue($moduleUrl) . ',\'#latest\');';
                 $historyAction = '<a class="btn btn-default" href="#" onclick="' . htmlspecialchars($onClick) . '" title="'
                     . htmlspecialchars($this->getLanguageService()->getLL('history')) . '">'
-- 
GitLab