From 48d47274ab9e5c168f78e153a881fdd84df5fa51 Mon Sep 17 00:00:00 2001 From: Benni Mack <benni@typo3.org> Date: Mon, 7 Aug 2023 14:57:09 +0200 Subject: [PATCH] [!!!][FEATURE] Add UriBuilder->buildUriFromRequest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change adds a new method buildUriFromRequest inside TYPO3's Backend UriBuilder in order to generate links to the current (or any) request with additional parameters. This is quite helpful to link to the same module /route when a request is given, thus reducing code duplication and unifies link generation. In addition, the determineScriptUrl() and getScriptUrl() methods inside LinkBrowsers and ElementBrowsers are removed, and have been removed from the LinkParameterProviderInterface as well. Resolves: #101612 Releases: main Change-Id: Ibaa2d2872178374e4553cf5899ee8ae19168480b Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/80433 Reviewed-by: Stefan B�rk <stefan@buerk.tech> Tested-by: core-ci <typo3@b13.com> Tested-by: Stefan B�rk <stefan@buerk.tech> Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de> Reviewed-by: Oliver Klee <typo3-coding@oliverklee.de> Tested-by: Oliver Klee <typo3-coding@oliverklee.de> --- .../backend/Classes/Clipboard/Clipboard.php | 4 +- .../AbstractLinkBrowserController.php | 31 ++---------- .../Controller/RecordListController.php | 2 +- .../ElementBrowser/AbstractElementBrowser.php | 18 ------- .../ElementBrowser/DatabaseBrowser.php | 21 +------- .../Classes/LinkHandler/PageLinkHandler.php | 23 +-------- .../Classes/LinkHandler/RecordLinkHandler.php | 19 ------- .../Classes/RecordList/DatabaseRecordList.php | 4 +- .../backend/Classes/Routing/UriBuilder.php | 15 ++++++ .../View/LinkParameterProviderInterface.php | 23 ++------- .../Classes/View/FolderUtilityRenderer.php | 17 +++---- .../Functional/Routing/UriBuilderTest.php | 49 ++++++++++++++++++- ...-LinkParameterProviderInterfaceChanged.rst | 49 +++++++++++++++++++ ...-101612-UriBuilder-buildUriFromRequest.rst | 28 +++++++++++ .../AbstractResourceBrowser.php | 36 +++----------- .../ElementBrowser/CreateFolderBrowser.php | 2 +- .../Classes/ElementBrowser/FileBrowser.php | 4 +- .../Classes/ElementBrowser/FolderBrowser.php | 2 +- typo3/sysext/filelist/Classes/FileList.php | 2 +- .../AbstractResourceLinkHandler.php | 33 +++---------- .../Classes/LinkHandler/FileLinkHandler.php | 4 +- .../Classes/LinkHandler/FolderLinkHandler.php | 2 +- .../Php/MethodCallMatcher.php | 14 ++++++ 23 files changed, 195 insertions(+), 207 deletions(-) create mode 100644 typo3/sysext/core/Documentation/Changelog/13.0/Breaking-101612-LinkParameterProviderInterfaceChanged.rst create mode 100644 typo3/sysext/core/Documentation/Changelog/13.0/Feature-101612-UriBuilder-buildUriFromRequest.rst diff --git a/typo3/sysext/backend/Classes/Clipboard/Clipboard.php b/typo3/sysext/backend/Classes/Clipboard/Clipboard.php index 12abf4150f86..745a25bc68c0 100644 --- a/typo3/sysext/backend/Classes/Clipboard/Clipboard.php +++ b/typo3/sysext/backend/Classes/Clipboard/Clipboard.php @@ -788,8 +788,8 @@ class Clipboard 1633604720 ); } - return (string)$this->uriBuilder->buildUriFromRoute( - $this->request->getAttribute('route')->getOption('_identifier'), + return (string)$this->uriBuilder->buildUriFromRequest( + $this->request, array_replace($this->request->getQueryParams(), $parameters) ); } diff --git a/typo3/sysext/backend/Classes/Controller/AbstractLinkBrowserController.php b/typo3/sysext/backend/Classes/Controller/AbstractLinkBrowserController.php index 45c5e4c9ca46..992f19bde672 100644 --- a/typo3/sysext/backend/Classes/Controller/AbstractLinkBrowserController.php +++ b/typo3/sysext/backend/Classes/Controller/AbstractLinkBrowserController.php @@ -36,7 +36,6 @@ use TYPO3\CMS\Core\Localization\LanguageService; use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Core\Service\DependencyOrderingService; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Utility\HttpUtility; use TYPO3\CMS\Core\View\ViewInterface; /** @@ -48,11 +47,6 @@ abstract class AbstractLinkBrowserController { use PageRendererBackendSetupTrait; - /** - * URL of current request - */ - protected string $thisScript = ''; - /** * @var array<string, array> */ @@ -159,12 +153,11 @@ abstract class AbstractLinkBrowserController $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_misc.xlf'); $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_core.xlf'); - $this->determineScriptUrl($request); $this->initVariables($request); $this->loadLinkHandlers(); $this->initCurrentUrl(); - $menuData = $this->buildMenuArray(); + $menuData = $this->buildMenuArray($request); if ($this->displayedLinkHandler instanceof LinkHandlerViewProviderInterface) { $view = $this->displayedLinkHandler->createView($this->backendViewFactory, $request); } else { @@ -215,29 +208,11 @@ abstract class AbstractLinkBrowserController ]; } - public function getScriptUrl(): string - { - return $this->thisScript; - } - public function getParameters(): array { return $this->parameters; } - /** - * Sets the script url depending on being a module or script request. - */ - protected function determineScriptUrl(ServerRequestInterface $request): void - { - if ($route = $request->getAttribute('route')) { - $this->thisScript = (string)$this->uriBuilder->buildUriFromRoute($route->getOption('_identifier')); - } else { - $normalizedParams = $request->getAttribute('normalizedParams'); - $this->thisScript = $normalizedParams->getScriptName(); - } - } - protected function initVariables(ServerRequestInterface $request): void { $queryParams = $request->getQueryParams(); @@ -346,7 +321,7 @@ abstract class AbstractLinkBrowserController * * @return array[] */ - protected function buildMenuArray(): array + protected function buildMenuArray(ServerRequestInterface $request): array { $allowedItems = $this->getAllowedItems(); if ($this->displayedLinkHandlerId && !in_array($this->displayedLinkHandlerId, $allowedItems, true)) { @@ -373,7 +348,7 @@ abstract class AbstractLinkBrowserController $menuDef[$identifier] = [ 'isActive' => $isActive, 'label' => $configuration['label'], - 'url' => $this->thisScript . HttpUtility::buildQueryString($this->getUrlParameters(['act' => $identifier]), '&'), + 'url' => $this->uriBuilder->buildUriFromRequest($request, $this->getUrlParameters(['act' => $identifier])), 'addParams' => $configuration['addParams'] ?? '', 'before' => $configuration['displayBefore'], 'after' => $configuration['displayAfter'], diff --git a/typo3/sysext/backend/Classes/Controller/RecordListController.php b/typo3/sysext/backend/Classes/Controller/RecordListController.php index a4a6289dc02e..747005726720 100644 --- a/typo3/sysext/backend/Classes/Controller/RecordListController.php +++ b/typo3/sysext/backend/Classes/Controller/RecordListController.php @@ -617,7 +617,7 @@ class RecordListController return $value !== null && trim((string)$value) !== ''; }); - return (string)$this->uriBuilder->buildUriFromRoute($request->getAttribute('route')->getOption('_identifier'), $params); + return (string)$this->uriBuilder->buildUriFromRequest($request, $params); } /** diff --git a/typo3/sysext/backend/Classes/ElementBrowser/AbstractElementBrowser.php b/typo3/sysext/backend/Classes/ElementBrowser/AbstractElementBrowser.php index cda8678e00d6..f03fe2895b2f 100644 --- a/typo3/sysext/backend/Classes/ElementBrowser/AbstractElementBrowser.php +++ b/typo3/sysext/backend/Classes/ElementBrowser/AbstractElementBrowser.php @@ -44,13 +44,6 @@ abstract class AbstractElementBrowser */ protected string $identifier = ''; - /** - * URL of current request - * - * @var string - */ - protected $thisScript = ''; - /** * Active with TYPO3 Element Browser: Contains the name of the form field for which this window * opens - thus allows us to make references back to the main window in which the form is. @@ -95,7 +88,6 @@ abstract class AbstractElementBrowser $this->pageRenderer->loadJavaScriptModule('@typo3/backend/viewport/resizable-navigation.js'); $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_misc.xlf'); $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_core.xlf'); - $this->determineScriptUrl(); $this->initVariables($request); } @@ -107,16 +99,6 @@ abstract class AbstractElementBrowser return $this->identifier; } - /** - * Sets the script url depending on being a module or script request - */ - protected function determineScriptUrl() - { - $this->thisScript = (string)$this->uriBuilder->buildUriFromRoute( - $this->getRequest()->getAttribute('route')->getOption('_identifier') - ); - } - protected function initVariables(ServerRequestInterface $request) { $this->bparams = $request->getParsedBody()['bparams'] ?? $request->getQueryParams()['bparams'] ?? ''; diff --git a/typo3/sysext/backend/Classes/ElementBrowser/DatabaseBrowser.php b/typo3/sysext/backend/Classes/ElementBrowser/DatabaseBrowser.php index 34370355e108..2bde02a4a73f 100644 --- a/typo3/sysext/backend/Classes/ElementBrowser/DatabaseBrowser.php +++ b/typo3/sysext/backend/Classes/ElementBrowser/DatabaseBrowser.php @@ -238,7 +238,7 @@ class DatabaseBrowser extends AbstractElementBrowser implements ElementBrowserIn * @param array $values Array of values to include into the parameters * @return string[] Array of parameters which have to be added to URLs */ - public function getUrlParameters(array $values) + public function getUrlParameters(array $values): array { $pid = $values['pid'] ?? $this->expandPage; return [ @@ -247,23 +247,4 @@ class DatabaseBrowser extends AbstractElementBrowser implements ElementBrowserIn 'bparams' => $this->bparams, ]; } - - /** - * @param array $values Values to be checked - * @return bool Returns TRUE if the given values match the currently selected item - */ - public function isCurrentlySelectedItem(array $values) - { - return false; - } - - /** - * Returns the URL of the current script - * - * @return string - */ - public function getScriptUrl() - { - return $this->thisScript; - } } diff --git a/typo3/sysext/backend/Classes/LinkHandler/PageLinkHandler.php b/typo3/sysext/backend/Classes/LinkHandler/PageLinkHandler.php index 168dc5e6cc34..bf2f1aee1936 100644 --- a/typo3/sysext/backend/Classes/LinkHandler/PageLinkHandler.php +++ b/typo3/sysext/backend/Classes/LinkHandler/PageLinkHandler.php @@ -203,10 +203,9 @@ class PageLinkHandler extends AbstractLinkHandler implements LinkHandlerInterfac /** * @param array $values Array of values to include into the parameters or which might influence the parameters - * * @return string[] Array of parameters which have to be added to URLs */ - public function getUrlParameters(array $values) + public function getUrlParameters(array $values): array { $parameters = [ 'expandPage' => isset($values['pid']) ? (int)$values['pid'] : $this->expandPage, @@ -214,26 +213,6 @@ class PageLinkHandler extends AbstractLinkHandler implements LinkHandlerInterfac return array_merge($this->linkBrowser->getUrlParameters($values), $parameters); } - /** - * @param array $values Values to be checked - * - * @return bool Returns TRUE if the given values match the currently selected item - */ - public function isCurrentlySelectedItem(array $values) - { - return !empty($this->linkParts) && (int)$this->linkParts['url']['pageuid'] === (int)$values['pid']; - } - - /** - * Returns the URL of the current script - * - * @return string - */ - public function getScriptUrl() - { - return $this->linkBrowser->getScriptUrl(); - } - /** * @param string[] $fieldDefinitions Array of link attribute field definitions * @return string[] diff --git a/typo3/sysext/backend/Classes/LinkHandler/RecordLinkHandler.php b/typo3/sysext/backend/Classes/LinkHandler/RecordLinkHandler.php index 82f68ae41749..8c438b47b456 100644 --- a/typo3/sysext/backend/Classes/LinkHandler/RecordLinkHandler.php +++ b/typo3/sysext/backend/Classes/LinkHandler/RecordLinkHandler.php @@ -206,25 +206,6 @@ final class RecordLinkHandler extends AbstractLinkHandler implements LinkHandler ); } - /** - * Checks if the submitted page matches the current page. - * - * @param array $values Values to be checked - * @return bool Returns TRUE if the given values match the currently selected item - */ - public function isCurrentlySelectedItem(array $values): bool - { - return !empty($this->linkParts) && (int)$this->linkParts['pid'] === (int)$values['pid']; - } - - /** - * Returns the URL of the current script - */ - public function getScriptUrl(): string - { - return $this->linkBrowser->getScriptUrl(); - } - /** * Render elements of configured table */ diff --git a/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php b/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php index 0dc453d59e38..960b90b02068 100644 --- a/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php +++ b/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php @@ -2678,8 +2678,8 @@ class DatabaseRecordList $urlParameters['sortRev'] = $this->sortRev; } - return (string)$this->uriBuilder->buildUriFromRoute( - $this->request->getAttribute('route')->getOption('_identifier'), + return (string)$this->uriBuilder->buildUriFromRequest( + $this->request, array_replace($urlParameters, $this->overrideUrlParameters) ); } diff --git a/typo3/sysext/backend/Classes/Routing/UriBuilder.php b/typo3/sysext/backend/Classes/Routing/UriBuilder.php index ad1c2df96f36..fb19eb3bb1be 100644 --- a/typo3/sysext/backend/Classes/Routing/UriBuilder.php +++ b/typo3/sysext/backend/Classes/Routing/UriBuilder.php @@ -15,10 +15,12 @@ namespace TYPO3\CMS\Backend\Routing; +use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UriInterface; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route as SymfonyRoute; use TYPO3\CMS\Backend\Routing\Exception\MethodNotAllowedException; use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException; use TYPO3\CMS\Backend\Routing\Exception\RouteTypeNotAllowedException; @@ -123,6 +125,19 @@ class UriBuilder implements SingletonInterface return $this->buildUriFromRoute($name, $parameters, $referenceType); } + /** + * A shorthand function to link to e.g. the same as in the current request, so the internal attribute "route" + * is properly covered and does not need to be exposed all the time. + */ + public function buildUriFromRequest(ServerRequestInterface $request, array $parameters = [], string $referenceType = self::ABSOLUTE_PATH): UriInterface + { + $route = $request->getAttribute('route'); + if (!$route instanceof Route && !$route instanceof SymfonyRoute) { + throw new RouteNotFoundException('No route object was given inside the request object', 1691423325); + } + return $this->buildUriFromRoute($route->getOption('_identifier'), $parameters, $referenceType); + } + /** * Generates a URL or path for a specific route based on the given parameters. * When the route is configured with "access=public" then the token generation is left out. diff --git a/typo3/sysext/backend/Classes/Tree/View/LinkParameterProviderInterface.php b/typo3/sysext/backend/Classes/Tree/View/LinkParameterProviderInterface.php index 3126f0b1fc44..9804ee6d352b 100644 --- a/typo3/sysext/backend/Classes/Tree/View/LinkParameterProviderInterface.php +++ b/typo3/sysext/backend/Classes/Tree/View/LinkParameterProviderInterface.php @@ -1,5 +1,7 @@ <?php +declare(strict_types=1); + /* * This file is part of the TYPO3 CMS project. * @@ -17,30 +19,11 @@ namespace TYPO3\CMS\Backend\Tree\View; interface LinkParameterProviderInterface { - /** - * Returns the URL of the current script - * - * @return string - */ - public function getScriptUrl(); - /** * Provides an array or GET parameters for URL generation * * @param array $values Array of values to include into the parameters or which might influence the parameters - * * @return string[] Array of parameters which have to be added to URLs */ - public function getUrlParameters(array $values); - - /** - * Check if given value is currently the selected item - * - * This method is only used in the page tree. - * - * @param array $values Values to be checked - * - * @return bool Returns TRUE if the given values match the currently selected item - */ - public function isCurrentlySelectedItem(array $values); + public function getUrlParameters(array $values): array; } diff --git a/typo3/sysext/backend/Classes/View/FolderUtilityRenderer.php b/typo3/sysext/backend/Classes/View/FolderUtilityRenderer.php index 35fd4f8f4f7e..13aec52c0b04 100644 --- a/typo3/sysext/backend/Classes/View/FolderUtilityRenderer.php +++ b/typo3/sysext/backend/Classes/View/FolderUtilityRenderer.php @@ -17,6 +17,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Backend\View; +use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Backend\Tree\View\LinkParameterProviderInterface; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; @@ -26,7 +27,6 @@ use TYPO3\CMS\Core\Resource\Folder; use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperRegistry; use TYPO3\CMS\Core\Resource\Security\FileNameValidator; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Utility\HttpUtility; /** * Renders utility forms used in the views for files/folders of Element and Link Browser @@ -54,7 +54,7 @@ class FolderUtilityRenderer * * @return string HTML for the create folder form. */ - public function createFolder(Folder $folderObject) + public function createFolder(ServerRequestInterface $request, Folder $folderObject) { $lang = $this->getLanguageService(); @@ -77,11 +77,11 @@ class FolderUtilityRenderer . htmlspecialchars($folderObject->getCombinedIdentifier()) . '" />'; // Make footer of upload form, including the submit button: - $redirectValue = $this->parameterProvider->getScriptUrl() . HttpUtility::buildQueryString( + $redirectValue = (string)$this->uriBuilder->buildUriFromRequest( + $request, $this->parameterProvider->getUrlParameters( ['identifier' => $folderObject->getCombinedIdentifier()] - ), - '&' + ) ); $markup[] = '<input type="hidden" name="redirect" value="' . htmlspecialchars($redirectValue) . '" />'; @@ -96,7 +96,7 @@ class FolderUtilityRenderer * * @return string HTML for an upload form. */ - public function uploadForm(Folder $folderObject, ?FileExtensionFilter $fileExtensionFilter = null) + public function uploadForm(ServerRequestInterface $request, Folder $folderObject, ?FileExtensionFilter $fileExtensionFilter = null) { if (!$folderObject->checkActionPermission('write')) { return ''; @@ -140,10 +140,7 @@ class FolderUtilityRenderer $formAction = (string)$this->uriBuilder->buildUriFromRoute('tce_file'); $combinedIdentifier = $folderObject->getCombinedIdentifier(); - $redirectValue = $this->parameterProvider->getScriptUrl() . HttpUtility::buildQueryString( - $this->parameterProvider->getUrlParameters(['identifier' => $combinedIdentifier]), - '&' - ); + $redirectValue = (string)$this->uriBuilder->buildUriFromRequest($request, $this->parameterProvider->getUrlParameters(['identifier' => $combinedIdentifier])); $markup[] = '<form class="pt-3 pb-3" action="' . htmlspecialchars($formAction) . '" method="post" name="editform" enctype="multipart/form-data">'; $markup[] = '<input type="hidden" name="data[upload][0][target]" value="' . htmlspecialchars($combinedIdentifier) . '" />'; $markup[] = '<input type="hidden" name="data[upload][0][data]" value="0" />'; diff --git a/typo3/sysext/backend/Tests/Functional/Routing/UriBuilderTest.php b/typo3/sysext/backend/Tests/Functional/Routing/UriBuilderTest.php index 30e77f118dca..7cc6e318b04f 100644 --- a/typo3/sysext/backend/Tests/Functional/Routing/UriBuilderTest.php +++ b/typo3/sysext/backend/Tests/Functional/Routing/UriBuilderTest.php @@ -17,7 +17,10 @@ declare(strict_types=1); namespace TYPO3\CMS\Backend\Tests\Functional\Routing; +use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException; +use TYPO3\CMS\Backend\Routing\Router; use TYPO3\CMS\Backend\Routing\UriBuilder; +use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; @@ -44,7 +47,49 @@ final class UriBuilderTest extends FunctionalTestCase public function buildUriFromRouteResolvesSubModule(): void { $subject = GeneralUtility::makeInstance(UriBuilder::class); - $route = $subject->buildUriFromRoute('site_configuration.edit'); - self::assertStringEndsWith('/module/site/configuration/edit', $route->getPath()); + $uri = $subject->buildUriFromRoute('site_configuration.edit'); + self::assertStringEndsWith('/module/site/configuration/edit', $uri->getPath()); + } + + /** + * @test + */ + public function buildUriFromRequestCanLinkToValidRoute(): void + { + $subject = GeneralUtility::makeInstance(UriBuilder::class); + $route = GeneralUtility::makeInstance(Router::class)->getRoute('site_configuration.edit'); + $route->setOption('_identifier', 'site_configuration.edit'); + $request = new ServerRequest('https://example.com/', 'GET'); + $request = $request->withAttribute('route', $route); + $uri = $subject->buildUriFromRequest($request, ['foo' => 'bar']); + self::assertStringEndsWith('/module/site/configuration/edit', $uri->getPath()); + } + + /** + * @test + */ + public function buildUriFromRequestWithInvalidRouteThrowsException(): void + { + self::expectException(RouteNotFoundException::class); + self::expectExceptionCode(1476050190); + $subject = GeneralUtility::makeInstance(UriBuilder::class); + $route = GeneralUtility::makeInstance(Router::class)->getRoute('site_configuration.edit'); + $request = new ServerRequest('https://example.com/', 'GET'); + // Route is not found in the registry of routes + $route->setOption('_identifier', 'foo.bar'); + $request = $request->withAttribute('route', $route); + $subject->buildUriFromRequest($request, ['foo' => 'bar']); + } + + /** + * @test + */ + public function buildUriFromRequestWithoutRouteThrowsException(): void + { + self::expectException(RouteNotFoundException::class); + self::expectExceptionCode(1691423325); + $subject = GeneralUtility::makeInstance(UriBuilder::class); + $request = new ServerRequest('https://example.com/', 'GET'); + $subject->buildUriFromRequest($request, ['foo' => 'bar']); } } diff --git a/typo3/sysext/core/Documentation/Changelog/13.0/Breaking-101612-LinkParameterProviderInterfaceChanged.rst b/typo3/sysext/core/Documentation/Changelog/13.0/Breaking-101612-LinkParameterProviderInterfaceChanged.rst new file mode 100644 index 000000000000..fd7c3ff60873 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/13.0/Breaking-101612-LinkParameterProviderInterfaceChanged.rst @@ -0,0 +1,49 @@ +.. include:: /Includes.rst.txt + +.. _breaking-101612-1691447955: + +=========================================================== +Breaking: #101612 - LinkParameterProvider Interface changed +=========================================================== + +See :issue:`101612` + +Description +=========== + +The PHP interface :php:`TYPO3\CMS\Backend\Tree\View\LinkParameterProviderInterface` +has changed. The interface is used to generate URLs with query parameters for links +within Element Browsers or Link Browsers in the TYPO3 Backend. + +The methods :php:`getScriptUrl()` and :php:`isCurrentlySelectedItem()` have been removed +from the interface, as the implementing Link Browsers do not need this information anymore +due to simplification in routing. + +The method :php:`getUrlParameters()` now has a native return type :php:`array`, whereas +previously this was only type-hinted. + + +Impact +====== + +When accessing implementing PHP objects, it should be noted that these methods do not +exist anymore. When called this might result in fatal PHP errors. + +When implementing the PHP interface, the implementing code will fail due to missing return +types. + + +Affected installations +====================== + +TYPO3 installations with custom implementations of this interface. + + +Migration +========= + +For extensions implementing the interface, the return type for :php:`getUrlParameters()` +can be added in order to be TYPO3 v12+ compatible. For v13-only compatibility, +it is recommended to remove the superfluous methods. + +.. index:: PHP-API, PartiallyScanned, ext:backend \ No newline at end of file diff --git a/typo3/sysext/core/Documentation/Changelog/13.0/Feature-101612-UriBuilder-buildUriFromRequest.rst b/typo3/sysext/core/Documentation/Changelog/13.0/Feature-101612-UriBuilder-buildUriFromRequest.rst new file mode 100644 index 000000000000..f45e1dfcb707 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/13.0/Feature-101612-UriBuilder-buildUriFromRequest.rst @@ -0,0 +1,28 @@ +.. include:: /Includes.rst.txt + +.. _feature-101612-1691425003: + +================================================== +Feature: #101612 - UriBuilder->buildUriFromRequest +================================================== + +See :issue:`101612` + +Description +=========== + +A new method within :php:`TYPO3\CMS\Backend\Routing\UriBuilder` named `buildUriFromRequest` is added which allows to generate a URL to a backend route of this +request. + + +Impact +====== + +This is typically useful when linking to the current route or module in the TYPO3 Backend for +extension authors to avoid internals with any PSR-7 Request Attribute. + +Usage within a PHP module controller in the TYPO3 Backend Context + +: :php:`$this->uriBuilder->buildUriFromRequest($request, ['id' => $id]);` + +.. index:: Backend, PHP-API, ext:backend \ No newline at end of file diff --git a/typo3/sysext/filelist/Classes/ElementBrowser/AbstractResourceBrowser.php b/typo3/sysext/filelist/Classes/ElementBrowser/AbstractResourceBrowser.php index 003a4be97b2c..6bcdabf26647 100644 --- a/typo3/sysext/filelist/Classes/ElementBrowser/AbstractResourceBrowser.php +++ b/typo3/sysext/filelist/Classes/ElementBrowser/AbstractResourceBrowser.php @@ -18,7 +18,6 @@ namespace TYPO3\CMS\Filelist\ElementBrowser; use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Backend\ElementBrowser\AbstractElementBrowser; use TYPO3\CMS\Backend\ElementBrowser\ElementBrowserInterface; -use TYPO3\CMS\Backend\Routing\Route; use TYPO3\CMS\Backend\Template\Components\Buttons\ButtonInterface; use TYPO3\CMS\Backend\Template\Components\Buttons\DropDown\DropDownDivider; use TYPO3\CMS\Backend\Template\Components\Buttons\DropDown\DropDownItemInterface; @@ -26,13 +25,10 @@ use TYPO3\CMS\Backend\Template\Components\Buttons\DropDown\DropDownRadio; use TYPO3\CMS\Backend\Template\Components\Buttons\DropDown\DropDownToggle; use TYPO3\CMS\Backend\Template\Components\Buttons\DropDownButton; use TYPO3\CMS\Backend\Tree\View\LinkParameterProviderInterface; -use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException; use TYPO3\CMS\Core\Resource\Folder; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Utility\HttpUtility; -use TYPO3\CMS\Core\Utility\PathUtility; use TYPO3\CMS\Filelist\FileList; use TYPO3\CMS\Filelist\Matcher\Matcher; use TYPO3\CMS\Filelist\Type\ViewMode; @@ -153,34 +149,28 @@ abstract class AbstractResourceBrowser extends AbstractElementBrowser implements } /** - * @param array $parameters Array of values to include into the parameters + * @param array $values Array of values to include into the parameters * @return string[] Array of parameters which have to be added to URLs */ - public function getUrlParameters(array $parameters): array + public function getUrlParameters(array $values): array { - $parameters = array_replace_recursive([ + $values = array_replace_recursive([ 'mode' => $this->identifier, - 'expandFolder' => $parameters['identifier'] ?? $this->expandFolder, + 'expandFolder' => $values['identifier'] ?? $this->expandFolder, 'bparams' => $this->bparams, - ], $parameters); + ], $values); - $parameters = array_filter($parameters, static function ($value) { + $values = array_filter($values, static function ($value) { return $value !== null && trim($value) !== ''; }); - return $parameters; + return $values; } protected function createUri(array $parameters = []): string { $parameters = $this->getUrlParameters($parameters); - if (($route = $this->getRequest()->getAttribute('route')) instanceof Route) { - $scriptUrl = (string)$this->uriBuilder->buildUriFromRoute($route->getOption('_identifier'), $parameters); - } else { - $scriptUrl = ($this->thisScript ?: PathUtility::basename(Environment::getCurrentScript())) . HttpUtility::buildQueryString($parameters, '&'); - } - - return $scriptUrl; + return (string)$this->uriBuilder->buildUriFromRequest($this->getRequest(), $parameters); } /** @@ -200,14 +190,4 @@ abstract class AbstractResourceBrowser extends AbstractElementBrowser implements } return [$data, $store]; } - - public function isCurrentlySelectedItem(array $values): bool - { - return false; - } - - public function getScriptUrl(): string - { - return $this->thisScript; - } } diff --git a/typo3/sysext/filelist/Classes/ElementBrowser/CreateFolderBrowser.php b/typo3/sysext/filelist/Classes/ElementBrowser/CreateFolderBrowser.php index 998034b7025c..6ee3d56f0922 100644 --- a/typo3/sysext/filelist/Classes/ElementBrowser/CreateFolderBrowser.php +++ b/typo3/sysext/filelist/Classes/ElementBrowser/CreateFolderBrowser.php @@ -57,7 +57,7 @@ class CreateFolderBrowser extends AbstractResourceBrowser // Build the folder creation form $folderUtilityRenderer = GeneralUtility::makeInstance(FolderUtilityRenderer::class, $this); - $markup[] = $folderUtilityRenderer->createFolder($this->selectedFolder); + $markup[] = $folderUtilityRenderer->createFolder($this->getRequest(), $this->selectedFolder); // Create the filelist header bar $markup[] = '<div class="row justify-content-between mb-2">'; diff --git a/typo3/sysext/filelist/Classes/ElementBrowser/FileBrowser.php b/typo3/sysext/filelist/Classes/ElementBrowser/FileBrowser.php index b38bd3485179..bc3421c57e5b 100644 --- a/typo3/sysext/filelist/Classes/ElementBrowser/FileBrowser.php +++ b/typo3/sysext/filelist/Classes/ElementBrowser/FileBrowser.php @@ -135,8 +135,8 @@ class FileBrowser extends AbstractResourceBrowser // Build the file upload and folder creation form $folderUtilityRenderer = GeneralUtility::makeInstance(FolderUtilityRenderer::class, $this); - $markup[] = $folderUtilityRenderer->uploadForm($this->selectedFolder, $this->fileExtensionFilter); - $markup[] = $folderUtilityRenderer->createFolder($this->selectedFolder); + $markup[] = $folderUtilityRenderer->uploadForm($this->getRequest(), $this->selectedFolder, $this->fileExtensionFilter); + $markup[] = $folderUtilityRenderer->createFolder($this->getRequest(), $this->selectedFolder); $contentHtml = implode('', $markup); } diff --git a/typo3/sysext/filelist/Classes/ElementBrowser/FolderBrowser.php b/typo3/sysext/filelist/Classes/ElementBrowser/FolderBrowser.php index 3fe0fac44f7f..a2ba4caa41ee 100644 --- a/typo3/sysext/filelist/Classes/ElementBrowser/FolderBrowser.php +++ b/typo3/sysext/filelist/Classes/ElementBrowser/FolderBrowser.php @@ -90,7 +90,7 @@ class FolderBrowser extends AbstractResourceBrowser // Build the folder creation form $folderUtilityRenderer = GeneralUtility::makeInstance(FolderUtilityRenderer::class, $this); - $markup[] = $folderUtilityRenderer->createFolder($this->selectedFolder); + $markup[] = $folderUtilityRenderer->createFolder($this->getRequest(), $this->selectedFolder); $contentHtml = implode('', $markup); } diff --git a/typo3/sysext/filelist/Classes/FileList.php b/typo3/sysext/filelist/Classes/FileList.php index 8040a1a1c264..4be12d3b72e8 100644 --- a/typo3/sysext/filelist/Classes/FileList.php +++ b/typo3/sysext/filelist/Classes/FileList.php @@ -1474,7 +1474,7 @@ class FileList return (is_array($value) && $value !== []) || (trim((string)$value) !== ''); }); - return (string)$this->uriBuilder->buildUriFromRoute($route->getOption('_identifier'), $params); + return (string)$this->uriBuilder->buildUriFromRequest($request, $params); } protected function createEditDataUriForResource(ResourceInterface $resource): ?string diff --git a/typo3/sysext/filelist/Classes/LinkHandler/AbstractResourceLinkHandler.php b/typo3/sysext/filelist/Classes/LinkHandler/AbstractResourceLinkHandler.php index 06665c551658..7236d953b0b2 100644 --- a/typo3/sysext/filelist/Classes/LinkHandler/AbstractResourceLinkHandler.php +++ b/typo3/sysext/filelist/Classes/LinkHandler/AbstractResourceLinkHandler.php @@ -20,7 +20,6 @@ use TYPO3\CMS\Backend\Controller\AbstractLinkBrowserController; use TYPO3\CMS\Backend\LinkHandler\LinkHandlerInterface; use TYPO3\CMS\Backend\LinkHandler\LinkHandlerVariableProviderInterface; use TYPO3\CMS\Backend\LinkHandler\LinkHandlerViewProviderInterface; -use TYPO3\CMS\Backend\Routing\Route; use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Backend\Template\Components\Buttons\ButtonInterface; use TYPO3\CMS\Backend\Template\Components\Buttons\DropDown\DropDownDivider; @@ -31,7 +30,6 @@ use TYPO3\CMS\Backend\Template\Components\Buttons\DropDownButton; use TYPO3\CMS\Backend\Tree\View\LinkParameterProviderInterface; use TYPO3\CMS\Backend\View\BackendViewFactory; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; -use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\LinkHandling\LinkService; use TYPO3\CMS\Core\Localization\LanguageService; @@ -42,8 +40,6 @@ use TYPO3\CMS\Core\Resource\File; use TYPO3\CMS\Core\Resource\Folder; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Utility\HttpUtility; -use TYPO3\CMS\Core\Utility\PathUtility; use TYPO3\CMS\Core\View\ViewInterface; use TYPO3\CMS\Filelist\FileList; use TYPO3\CMS\Filelist\Matcher\Matcher; @@ -190,11 +186,6 @@ abstract class AbstractResourceLinkHandler implements LinkHandlerInterface, Link $this->filelist->thumbs = ($GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails'] ?? false) && $this->displayThumbs; } - public function isCurrentlySelectedItem(array $values): bool - { - return false; - } - public function modifyLinkAttributes(array $fieldDefinitions): array { return $fieldDefinitions; @@ -205,11 +196,6 @@ abstract class AbstractResourceLinkHandler implements LinkHandlerInterface, Link return true; } - public function getScriptUrl(): string - { - return $this->linkBrowser->getScriptUrl(); - } - /** * @return string[] Array of body-tag attributes */ @@ -228,14 +214,7 @@ abstract class AbstractResourceLinkHandler implements LinkHandlerInterface, Link protected function createUri(ServerRequestInterface $request, array $parameters = []): string { - $parameters = $this->getUrlParameters($parameters); - if (($route = $request->getAttribute('route')) instanceof Route) { - $scriptUrl = (string)$this->uriBuilder->buildUriFromRoute($route->getOption('_identifier'), $parameters); - } else { - $scriptUrl = ($this->linkBrowser->getScriptUrl() ?: PathUtility::basename(Environment::getCurrentScript())) . HttpUtility::buildQueryString($parameters, '&'); - } - - return $scriptUrl; + return (string)$this->uriBuilder->buildUriFromRequest($request, $this->getUrlParameters($parameters)); } protected function getViewModeButton(ServerRequestInterface $request): ButtonInterface @@ -270,13 +249,13 @@ abstract class AbstractResourceLinkHandler implements LinkHandlerInterface, Link return $viewModeButton; } - public function getUrlParameters(array $parameters): array + public function getUrlParameters(array $values): array { - $parameters = array_replace_recursive([ - 'expandFolder' => $parameters['identifier'] ?? $this->expandFolder, - ], $parameters); + $values = array_replace_recursive([ + 'expandFolder' => $values['identifier'] ?? $this->expandFolder, + ], $values); - return array_merge($this->linkBrowser->getUrlParameters($parameters), $parameters); + return array_merge($this->linkBrowser->getUrlParameters($values), $values); } protected function getLanguageService(): LanguageService diff --git a/typo3/sysext/filelist/Classes/LinkHandler/FileLinkHandler.php b/typo3/sysext/filelist/Classes/LinkHandler/FileLinkHandler.php index 41c1c6a6fbbc..b6ae2d4e957c 100644 --- a/typo3/sysext/filelist/Classes/LinkHandler/FileLinkHandler.php +++ b/typo3/sysext/filelist/Classes/LinkHandler/FileLinkHandler.php @@ -94,8 +94,8 @@ class FileLinkHandler extends AbstractResourceLinkHandler // Build the file upload and folder creation form $folderUtilityRenderer = GeneralUtility::makeInstance(FolderUtilityRenderer::class, $this); - $markup[] = $folderUtilityRenderer->uploadForm($this->selectedFolder); - $markup[] = $folderUtilityRenderer->createFolder($this->selectedFolder); + $markup[] = $folderUtilityRenderer->uploadForm($request, $this->selectedFolder); + $markup[] = $folderUtilityRenderer->createFolder($request, $this->selectedFolder); $contentHtml = implode(PHP_EOL, $markup); } diff --git a/typo3/sysext/filelist/Classes/LinkHandler/FolderLinkHandler.php b/typo3/sysext/filelist/Classes/LinkHandler/FolderLinkHandler.php index a9a346cbd121..cee44a15e083 100644 --- a/typo3/sysext/filelist/Classes/LinkHandler/FolderLinkHandler.php +++ b/typo3/sysext/filelist/Classes/LinkHandler/FolderLinkHandler.php @@ -81,7 +81,7 @@ class FolderLinkHandler extends AbstractResourceLinkHandler // Build the file upload and folder creation form $folderUtilityRenderer = GeneralUtility::makeInstance(FolderUtilityRenderer::class, $this); - $markup[] = $folderUtilityRenderer->createFolder($this->selectedFolder); + $markup[] = $folderUtilityRenderer->createFolder($request, $this->selectedFolder); $contentHtml = implode(PHP_EOL, $markup); } diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php index f097ca6a551c..2e2a8c128e5d 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php @@ -5842,4 +5842,18 @@ return [ 'Deprecation-101475-IconSizeStringConstants.rst', ], ], + 'TYPO3\CMS\Backend\Tree\View\LinkParameterProviderInterface->getScriptUrl' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Breaking-101612-LinkParameterProviderInterfaceChanged.rst', + ], + ], + 'TYPO3\CMS\Backend\Tree\View\LinkParameterProviderInterface->isCurrentlySelectedItem' => [ + 'numberOfMandatoryArguments' => 1, + 'maximumNumberOfArguments' => 1, + 'restFiles' => [ + 'Breaking-101612-LinkParameterProviderInterfaceChanged.rst', + ], + ], ]; -- GitLab