[TASK] Clean up Routing API 49/58149/9
authorOliver Hader <oliver@typo3.org>
Tue, 25 Sep 2018 07:51:52 +0000 (09:51 +0200)
committerOliver Hader <oliver.hader@typo3.org>
Wed, 26 Sep 2018 16:20:07 +0000 (18:20 +0200)
The PageUriBuilder is gone, as all is centralized in the
PageRouter now, which acts as the centralized place to resolve
Requests ("matchRequest") and to create URLs ("generateUri")
for pages that have a site configuration.

RouterInterface is the abstract interface which is intended to be
used within Backend as well in the future, and provides a good basis
for both cases (that's why page ID and SiteInterface is not hardcoded
in the API).

RouteResultInterface is introduced to allow further Result objects
like page-specific results, useful for future routing improvements.

Since PageUriBuilder was only used in cases where there was a
site, the Router is now bound to a site (see constructor).

When generating a URL, the PageRouter can receive a special
argument called "_language" to hand over a SiteLanguage object.

Resolves: #86388
Releases: master
Change-Id: Ib090d3373a88cb7c534557ef21b46dce646078b5
Reviewed-on: https://review.typo3.org/58149
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
20 files changed:
typo3/sysext/backend/Classes/Utility/BackendUtility.php
typo3/sysext/core/Classes/Error/PageErrorHandler/PageContentErrorHandler.php
typo3/sysext/core/Classes/Routing/PageRouter.php
typo3/sysext/core/Classes/Routing/PageUriBuilder.php [deleted file]
typo3/sysext/core/Classes/Routing/RouteResult.php
typo3/sysext/core/Classes/Routing/RouteResultInterface.php [new file with mode: 0644]
typo3/sysext/core/Classes/Routing/RouterInterface.php [new file with mode: 0644]
typo3/sysext/core/Classes/Routing/SiteMatcher.php
typo3/sysext/core/Classes/Site/Entity/Site.php
typo3/sysext/core/Migrations/Code/ClassAliasMap.php
typo3/sysext/core/Tests/Unit/Routing/PageRouterTest.php
typo3/sysext/frontend/Classes/Middleware/PageResolver.php
typo3/sysext/frontend/Classes/Middleware/StaticRouteResolver.php
typo3/sysext/frontend/Classes/Typolink/PageLinkBuilder.php
typo3/sysext/frontend/Tests/Unit/Middleware/PageResolverTest.php
typo3/sysext/seo/Tests/Functional/Canonical/CanonicalGeneratorTest.php
typo3/sysext/seo/Tests/Functional/Fixtures/pages-canonical.xml
typo3/sysext/seo/Tests/Functional/Fixtures/pages-sitemap.xml
typo3/sysext/seo/Tests/Functional/XmlSitemap/XmlSitemapIndexTest.php
typo3/sysext/viewpage/Classes/Controller/ViewModuleController.php

index cdd8f51..0ae30bc 100644 (file)
@@ -41,9 +41,10 @@ use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
 use TYPO3\CMS\Core\Resource\File;
 use TYPO3\CMS\Core\Resource\ProcessedFile;
 use TYPO3\CMS\Core\Resource\ResourceFactory;
-use TYPO3\CMS\Core\Routing\PageUriBuilder;
+use TYPO3\CMS\Core\Routing\RouterInterface;
 use TYPO3\CMS\Core\Routing\SiteMatcher;
 use TYPO3\CMS\Core\Site\Entity\PseudoSite;
+use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Site\SiteFinder;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
@@ -2665,12 +2666,17 @@ class BackendUtility
             // Check if the page (= its rootline) has a site attached, otherwise just keep the URL as is
             $rootLine = $rootLine ?? BackendUtility::BEgetRootLine($pageUid);
             try {
+                /** @var Site $site */
                 $site = $siteFinder->getSiteByPageId((int)$pageUid, $rootLine);
                 // Create a multi-dimensional array out of the additional get vars
                 $additionalQueryParams = [];
                 parse_str($additionalGetVars, $additionalQueryParams);
-                $uriBuilder = GeneralUtility::makeInstance(PageUriBuilder::class);
-                $previewUrl = (string)$uriBuilder->buildUri($pageUid, $additionalQueryParams, $anchorSection, ['site' => $site, 'rootLine' => $rootLine], $uriBuilder::ABSOLUTE_URL);
+                $previewUrl = (string)$site->getRouter()->generateUri(
+                    $pageUid,
+                    $additionalQueryParams,
+                    $anchorSection,
+                    RouterInterface::ABSOLUTE_URL
+                );
             } catch (SiteNotFoundException $e) {
                 $previewUrl = self::createPreviewUrl($pageUid, $rootLine, $anchorSection, $additionalGetVars, $viewScript);
             }
index 1cfc13f..0c50c56 100644 (file)
@@ -20,7 +20,8 @@ use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\LinkHandling\LinkService;
-use TYPO3\CMS\Core\Routing\PageUriBuilder;
+use TYPO3\CMS\Core\Site\Entity\Site;
+use TYPO3\CMS\Core\Site\SiteFinder;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -86,14 +87,14 @@ class PageContentErrorHandler implements PageErrorHandlerInterface
             return $urlParams['url'];
         }
 
+        $site = $request->getAttribute('site', null);
+        if (!$site instanceof Site) {
+            $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId((int)$urlParams['pageuid']);
+        }
         // Build Url
-        $uriBuilder = GeneralUtility::makeInstance(PageUriBuilder::class);
-        return (string)$uriBuilder->buildUri(
+        return (string)$site->getRouter()->generateUri(
             (int)$urlParams['pageuid'],
-            [],
-            null,
-            ['language' => $request->getAttribute('language', null)],
-            PageUriBuilder::ABSOLUTE_URL
+            ['_language' => $request->getAttribute('language', null)]
         );
     }
 }
index 3a634ef..1647195 100644 (file)
@@ -18,22 +18,26 @@ namespace TYPO3\CMS\Core\Routing;
 
 use Doctrine\DBAL\Connection;
 use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\UriInterface;
 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
 use Symfony\Component\Routing\Matcher\UrlMatcher;
 use Symfony\Component\Routing\RequestContext;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
+use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Database\Query\Restriction\FrontendWorkspaceRestriction;
-use TYPO3\CMS\Core\Site\Entity\SiteInterface;
+use TYPO3\CMS\Core\Http\Uri;
+use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
- * Page Router looking up the slug of the page path.
+ * Page Router - responsible for a page based on a request, by looking up the slug of the page path.
+ * Is also used for generating URLs for pages.
  *
- * This is done via the "Route Candidate" pattern.
+ * Resolving is done via the "Route Candidate" pattern.
  *
  * Example:
  * - /about-us/team/management/
@@ -51,27 +55,49 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
  * Please note: PageRouter does not restrict the HTTP method or is bound to any domain constraints,
  * as the SiteMatcher has done that already.
  *
- * The concept of the PageRouter is to *resolve*, and not build URIs. On top, it is a facade to hide the
+ * The concept of the PageRouter is to *resolve*, and to build URIs. On top, it is a facade to hide the
  * dependency to symfony and to not expose its logic.
-
- * @internal This API is not public yet and might change in the future, until TYPO3 v9 or TYPO3 v10.
  */
-class PageRouter
+class PageRouter implements RouterInterface
 {
     /**
+     * @var Site
+     */
+    protected $site;
+
+    /**
+     * @var array
+     */
+    protected $configuration;
+
+    /**
+     * A page router is always bound to a specific site.
+     *
+     * @param Site $site
+     * @param array $configuration
+     */
+    public function __construct(Site $site, array $configuration)
+    {
+        $this->site = $site;
+        $this->configuration = $configuration;
+    }
+
+    /**
+     * Finds a RouteResult based on the given request.
+     *
      * @param ServerRequestInterface $request
-     * @param string $routePathTail
-     * @param SiteInterface $site
-     * @param SiteLanguage $language
-     * @return RouteResult|null
+     * @param RouteResultInterface|RouteResult|null $previousResult
+     * @return RouteResult
      */
-    public function matchRoute(ServerRequestInterface $request, string $routePathTail, SiteInterface $site, SiteLanguage $language): ?RouteResult
+    public function matchRequest(ServerRequestInterface $request, RouteResultInterface $previousResult = null): ?RouteResultInterface
     {
+        $routePathTail = $previousResult ? $previousResult->getTail() : '';
+        $language = $previousResult ? $previousResult->getLanguage() : null;
         $slugCandidates = $this->getCandidateSlugsFromRoutePath($routePathTail);
         if (empty($slugCandidates)) {
             return null;
         }
-        $pageCandidates = $this->getPagesFromDatabaseForCandidates($slugCandidates, $site, $language->getLanguageId());
+        $pageCandidates = $this->getPagesFromDatabaseForCandidates($slugCandidates, $language->getLanguageId());
         // Stop if there are no candidates
         if (empty($pageCandidates)) {
             return null;
@@ -94,22 +120,77 @@ class PageRouter
         try {
             $result = $matcher->match('/' . ltrim($routePathTail, '/'));
             unset($result['_route']);
-            return new RouteResult($request->getUri(), $site, $language, $result['tail'], $result);
+            return new RouteResult($request->getUri(), $this->site, $language, $result['tail'], $result);
         } catch (ResourceNotFoundException $e) {
             // do nothing
         }
-        return new RouteResult($request->getUri(), $site, $language);
+        return new RouteResult($request->getUri(), $this->site, $language);
+    }
+
+    /**
+     * API for generating a page where the $route parameter is typically an array (page record) or the page ID
+     *
+     * @param array|string $route
+     * @param array $parameters an array of query parameters which can be built into the URI path, also consider the special handling of "_language"
+     * @param string $fragment additional #my-fragment part
+     * @param string $type see the RouterInterface for possible types
+     * @return UriInterface
+     */
+    public function generateUri($route, array $parameters = [], string $fragment = '', string $type = ''): UriInterface
+    {
+        // Resolve site
+        $siteLanguage = null;
+        $languageOption = $parameters['_language'] ?? null;
+        if ($languageOption instanceof SiteLanguage) {
+            $siteLanguage = $languageOption;
+            unset($parameters['_language']);
+        }
+        if ($siteLanguage === null) {
+            $siteLanguage = $this->site->getDefaultLanguage();
+        }
+
+        $pageId = 0;
+        if (is_array($route)) {
+            $pageId = (int)$route['uid'];
+        } elseif (is_scalar($route)) {
+            $pageId = (int)$route;
+        }
+        $pageRecord = BackendUtility::getRecord('pages', $pageId);
+        if ($siteLanguage->getLanguageId() > 0) {
+            $pageLocalizations = BackendUtility::getRecordLocalization('pages', $pageId, $siteLanguage->getLanguageId());
+            $pageRecord = $pageLocalizations[0] ?? $pageRecord;
+        }
+        $prefix = (string)$siteLanguage->getBase();
+        $prefix = rtrim($prefix, '/') . '/' . ltrim($pageRecord['slug'] ?? '', '/');
+
+        // Add the query parameters as string
+        $queryString = http_build_query($parameters, '', '&', PHP_QUERY_RFC3986);
+        $prefix = rtrim($prefix, '?');
+        if (!empty($queryString)) {
+            if (strpos($prefix, '?') === false) {
+                $prefix .= '?';
+            } else {
+                $prefix .= '&';
+            }
+        }
+        $uri = new Uri($prefix . $queryString);
+        if ($fragment) {
+            $uri = $uri->withFragment($fragment);
+        }
+        if ($type === self::ABSOLUTE_PATH) {
+            $uri = $uri->withScheme('')->withHost('')->withPort(null);
+        }
+        return $uri;
     }
 
     /**
      * Check for records in the database which matches one of the slug candidates.
      *
      * @param array $slugCandidates
-     * @param SiteInterface $site
      * @param int $languageId
      * @return array
      */
-    protected function getPagesFromDatabaseForCandidates(array $slugCandidates, SiteInterface $site, int $languageId): array
+    protected function getPagesFromDatabaseForCandidates(array $slugCandidates, int $languageId): array
     {
         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
             ->getQueryBuilderForTable('pages');
@@ -143,7 +224,7 @@ class PageRouter
         $siteMatcher = GeneralUtility::makeInstance(SiteMatcher::class);
         while ($row = $statement->fetch()) {
             $pageIdInDefaultLanguage = (int)($languageId > 0 ? $row['l10n_parent'] : $row['uid']);
-            if ($siteMatcher->matchByPageId($pageIdInDefaultLanguage)->getRootPageId() === $site->getRootPageId()) {
+            if ($siteMatcher->matchByPageId($pageIdInDefaultLanguage)->getRootPageId() === $this->site->getRootPageId()) {
                 $pages[] = $row;
             }
         }
@@ -152,7 +233,8 @@ class PageRouter
 
     /**
      * Returns possible URL parts for a string like /home/about-us/offices/
-     * to return
+     * to return.
+     *
      * /home/about-us/offices/
      * /home/about-us/offices
      * /home/about-us/
diff --git a/typo3/sysext/core/Classes/Routing/PageUriBuilder.php b/typo3/sysext/core/Classes/Routing/PageUriBuilder.php
deleted file mode 100644 (file)
index 9a379a9..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-<?php
-declare(strict_types = 1);
-
-namespace TYPO3\CMS\Core\Routing;
-
-/*
- * 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 Psr\Http\Message\UriInterface;
-use TYPO3\CMS\Backend\Utility\BackendUtility;
-use TYPO3\CMS\Core\Exception\SiteNotFoundException;
-use TYPO3\CMS\Core\Http\Uri;
-use TYPO3\CMS\Core\SingletonInterface;
-use TYPO3\CMS\Core\Site\Entity\Site;
-use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
-use TYPO3\CMS\Core\Site\SiteFinder;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-
-/**
- * Responsible for generates URLs to pages which are NOT bound to any permissions or frontend restrictions.
- *
- * If a page is built with a site in the root line, the base of the site (+ language) is used
- * and the &L parameter is then dropped explicitly.
- *
- * @internal as this might change until TYPO3 v9 LTS
- * @todo: check handling of MP parameter.
- */
-class PageUriBuilder implements SingletonInterface
-{
-    /**
-     * Generates an absolute URL
-     */
-    public const ABSOLUTE_URL = 'url';
-
-    /**
-     * Generates an absolute path
-     */
-    public const ABSOLUTE_PATH = 'path';
-
-    /**
-     * @var SiteFinder
-     */
-    protected $siteFinder;
-
-    /**
-     * PageUriBuilder constructor.
-     */
-    public function __construct()
-    {
-        $this->siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
-    }
-
-    /**
-     * Main entrypoint for generating an Uri for a page.
-     *
-     * @param int $pageId
-     * @param array $queryParameters
-     * @param string $fragment
-     * @param array $options ['language' => 123, 'rootLine' => etc.]
-     * @param string $referenceType
-     * @return UriInterface
-     */
-    public function buildUri(int $pageId, array $queryParameters = [], string $fragment = null, array $options = [], string $referenceType = self::ABSOLUTE_PATH): UriInterface
-    {
-        // Resolve site
-        $site = $options['site'] ?? null;
-        $siteLanguage = null;
-        $languageOption = $options['language'] ?? null;
-        $languageQueryParameter = isset($queryParameters['L']) ? (int)$queryParameters['L'] : null;
-
-        if (!($site instanceof Site)) {
-            try {
-                $site = $this->siteFinder->getSiteByPageId($pageId, $options['rootLine'] ?? null);
-            } catch (SiteNotFoundException $e) {
-                // no site found, must be an pseudo site site
-            }
-        }
-        if ($languageOption) {
-            if ($languageOption instanceof SiteLanguage) {
-                $siteLanguage = $languageOption;
-                $languageOption = $languageOption->getLanguageId();
-            } else {
-                $languageOption = (int)$languageOption;
-            }
-        }
-        $languageId = $languageOption ?? $languageQueryParameter ?? null;
-
-        // Resolve language (based on the options / query parameters, and remove it
-        // from GET variables, as the language is determined by the language path
-        if ($site) {
-            if ($languageId !== null) {
-                try {
-                    $siteLanguage = $site->getLanguageById($languageId);
-                    unset($queryParameters['L']);
-                } catch (\InvalidArgumentException $e) {
-                    // No Language found, so do fallback linking
-                }
-            } else {
-                $siteLanguage = $site->getDefaultLanguage();
-                unset($queryParameters['L']);
-            }
-        }
-
-        // If something is found, use /en/?id=123&additionalParams
-        // Only if a language is configured for the site, build a URL with a site prefix / base
-        if ($site && $siteLanguage) {
-            // Ensure to fetch the path segment / slug if it exists
-            $pageRecord = BackendUtility::getRecord('pages', $pageId);
-            if ($siteLanguage->getLanguageId() > 0) {
-                $pageLocalizations = BackendUtility::getRecordLocalization('pages', $pageId, $siteLanguage->getLanguageId());
-                $pageRecord = $pageLocalizations[0] ?? $pageRecord;
-            }
-            $prefix = (string)$siteLanguage->getBase();
-            if (!empty($pageRecord['slug'] ?? '')) {
-                $prefix = rtrim($prefix, '/') . '/' . ltrim($pageRecord['slug'], '/');
-            } else {
-                $prefix .= '?id=' . $pageId;
-            }
-        } else {
-            // If nothing is found, use index.php?id=123&additionalParams
-            // This usually kicks in with "PseudoSites" where no language object can be determined.
-            $prefix = $referenceType === self::ABSOLUTE_URL ? GeneralUtility::getIndpEnv('TYPO3_SITE_URL') : '';
-            $prefix .= 'index.php?id=' . $pageId;
-            if ($languageId !== null) {
-                $queryParameters['L'] = $languageId;
-            }
-        }
-
-        // Add the query parameters as string
-        $queryString = http_build_query($queryParameters, '', '&', PHP_QUERY_RFC3986);
-        $prefix = rtrim($prefix, '?');
-        if (!empty($queryString)) {
-            if (strpos($prefix, '?') === false) {
-                $prefix .= '?';
-            } else {
-                $prefix .= '&';
-            }
-        }
-        $uri = new Uri($prefix . $queryString);
-        if ($fragment) {
-            $uri = $uri->withFragment($fragment);
-        }
-        if ($referenceType === self::ABSOLUTE_PATH) {
-            $uri = $uri->withScheme('')->withHost('')->withPort(null);
-        }
-        return $uri;
-    }
-}
index 69037ec..202c313 100644 (file)
@@ -26,7 +26,7 @@ use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
  *
  * @internal this API might change until 9 LTS.
  */
-class RouteResult implements \ArrayAccess
+class RouteResult implements RouteResultInterface
 {
     /**
      * @var array
diff --git a/typo3/sysext/core/Classes/Routing/RouteResultInterface.php b/typo3/sysext/core/Classes/Routing/RouteResultInterface.php
new file mode 100644 (file)
index 0000000..303a724
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Core\Routing;
+
+/*
+ * 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!
+ */
+
+/**
+ * An object that is usually returned by a Router to contain all results.
+ */
+interface RouteResultInterface extends \ArrayAccess
+{
+}
diff --git a/typo3/sysext/core/Classes/Routing/RouterInterface.php b/typo3/sysext/core/Classes/Routing/RouterInterface.php
new file mode 100644 (file)
index 0000000..73b07ee
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Core\Routing;
+
+/*
+ * 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 Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Base Router to be used all over the TYPO3 Core. Its base lies around PSR-7 requests + URIs, and special "RouteResult"
+ * objects.
+ */
+interface RouterInterface
+{
+    /**
+     * Generates an absolute URL
+     */
+    const ABSOLUTE_URL = 'url';
+
+    /**
+     * Generates an absolute path
+     */
+    const ABSOLUTE_PATH = 'absolute';
+
+    /**
+     * @param ServerRequestInterface $request
+     * @param RouteResultInterface|null $previousResult
+     * @return RouteResultInterface|null if no route has matched, an empty RouteResult is returned.
+     */
+    public function matchRequest(ServerRequestInterface $request, RouteResultInterface $previousResult = null): ?RouteResultInterface;
+
+    /**
+     * Builds a URI based on the $route and the given parameters.
+     *
+     * @param string|array $route either the route name, or for pages it is usually the array of a page record, or the page ID
+     * @param array $parameters query parameters, specially reserved parameters are usually prefixed with "_"
+     * @param string $fragment the section/fragment www.example.com/page/#fragment, WITHOUT the hash
+     * @param string $type see the constants above.
+     * @return UriInterface
+     */
+    public function generateUri($route, array $parameters = [], string $fragment = '', string $type = self::ABSOLUTE_URL): UriInterface;
+}
index 78c402d..f76611a 100644 (file)
@@ -80,9 +80,9 @@ class SiteMatcher implements SingletonInterface
      * a sys_domain record, and match against them.
      *
      * @param ServerRequestInterface $request
-     * @return RouteResult
+     * @return RouteResultInterface
      */
-    public function matchRequest(ServerRequestInterface $request): RouteResult
+    public function matchRequest(ServerRequestInterface $request): RouteResultInterface
     {
         $site = null;
         $language = null;
index 9c5e3c6..a0feab8 100644 (file)
@@ -337,15 +337,13 @@ class Site implements SiteInterface
     }
 
     /**
-     * Returns applicable routers for this site
+     * Returns the applicable router for this site. This might be configurable in the future.
      *
-     * @return RouterInterface[]
+     * @return RouterInterface
      */
-    public function getRouters(): array
+    public function getRouter(): RouterInterface
     {
-        return [
-            new PageRouter()
-        ];
+        return new PageRouter($this, $this->configuration['routing'] ?? []);
     }
 
     /**
index 1cc94b1..e1e8752 100644 (file)
@@ -8,7 +8,6 @@ return [
     'TYPO3\\CMS\\Core\\IO\\PharStreamWrapperException' => \TYPO3\PharStreamWrapper\Exception::class,
     'TYPO3\\CMS\\Core\\Tree\\TableConfiguration\\ExtJsArrayTreeRenderer' => \TYPO3\CMS\Core\Tree\TableConfiguration\ArrayTreeRenderer::class,
     'TYPO3\\CMS\\Core\\History\\RecordHistory' => \TYPO3\CMS\Core\DataHandling\History\RecordHistoryStore::class,
-    'TYPO3\\CMS\\Backend\\Routing\\PageUriBuilder' => \TYPO3\CMS\Core\Routing\PageUriBuilder::class,
     'TYPO3\\CMS\\Saltedpasswords\\Salt\\AbstractSalt' => \TYPO3\CMS\Core\Crypto\PasswordHashing\AbstractComposedSalt::class,
     'TYPO3\\CMS\\Saltedpasswords\\Salt\\AbstractComposedSalt' => \TYPO3\CMS\Core\Crypto\PasswordHashing\AbstractComposedSalt::class,
     'TYPO3\\CMS\\Saltedpasswords\\Salt\\Argon2iSalt' => \TYPO3\CMS\Core\Crypto\PasswordHashing\Argon2iPasswordHash::class,
index 9b8bc22..edcd537 100644 (file)
@@ -30,6 +30,7 @@ class PageRouterTest extends UnitTestCase
     public function properSiteConfigurationFindsRoute()
     {
         $incomingUrl = 'https://king.com/lotus-flower/en/mr-magpie/bloom';
+        $slugCandidates = ['/mr-magpie/bloom/', '/mr-magpie/bloom'];
         $pageRecord = ['uid' => 13, 'l10n_parent' => 0, 'slug' => '/mr-magpie/bloom/'];
         $site = new Site('lotus-flower', 13, [
             'base' => '/lotus-flower/',
@@ -44,9 +45,11 @@ class PageRouterTest extends UnitTestCase
         $language = $site->getDefaultLanguage();
 
         $request = new ServerRequest($incomingUrl, 'GET');
-        $subject = $this->getAccessibleMock(PageRouter::class, ['getPagesFromDatabaseForCandidates']);
+        $previousResult = new RouteResult($request->getUri(), $site, $language, '/mr-magpie/bloom');
+        $subject = $this->getAccessibleMock(PageRouter::class, ['getCandidateSlugsFromRoutePath', 'getPagesFromDatabaseForCandidates'], [$site, []]);
+        $subject->expects($this->once())->method('getCandidateSlugsFromRoutePath')->willReturn($slugCandidates);
         $subject->expects($this->once())->method('getPagesFromDatabaseForCandidates')->willReturn([$pageRecord]);
-        $routeResult = $subject->matchRoute($request, '/mr-magpie/bloom', $site, $language);
+        $routeResult = $subject->matchRequest($request, $previousResult);
 
         $expectedRouteResult = new RouteResult($request->getUri(), $site, $language, '', ['page' => $pageRecord, 'tail' => '']);
         $this->assertEquals($expectedRouteResult, $routeResult);
@@ -59,6 +62,7 @@ class PageRouterTest extends UnitTestCase
     public function properSiteConfigurationWithoutTrailingSlashFindsRoute()
     {
         $incomingUrl = 'https://king.com/lotus-flower/en/mr-magpie/bloom/unknown-code/';
+        $slugCandidates = ['/mr-magpie/bloom/unknown-code/', '/mr-magpie/bloom/unknown-code'];
         $pageRecord = ['uid' => 13, 'l10n_parent' => 0, 'slug' => '/mr-magpie/bloom/'];
         $site = new Site('lotus-flower', 13, [
             'base' => '/lotus-flower/',
@@ -71,11 +75,12 @@ class PageRouterTest extends UnitTestCase
             ]
         ]);
         $language = $site->getDefaultLanguage();
-
         $request = new ServerRequest($incomingUrl, 'GET');
-        $subject = $this->getAccessibleMock(PageRouter::class, ['getPagesFromDatabaseForCandidates']);
+        $previousResult = new RouteResult($request->getUri(), $site, $language, '/mr-magpie/bloom/unknown-code/');
+        $subject = $this->getAccessibleMock(PageRouter::class, ['getCandidateSlugsFromRoutePath', 'getPagesFromDatabaseForCandidates'], [$site, []]);
+        $subject->expects($this->once())->method('getCandidateSlugsFromRoutePath')->willReturn($slugCandidates);
         $subject->expects($this->once())->method('getPagesFromDatabaseForCandidates')->willReturn([$pageRecord]);
-        $routeResult = $subject->matchRoute($request, '/mr-magpie/bloom/unknown-code/', $site, $language);
+        $routeResult = $subject->matchRequest($request, $previousResult);
 
         $expectedRouteResult = new RouteResult($request->getUri(), $site, $language, 'unknown-code/', ['page' => $pageRecord, 'tail' => 'unknown-code/']);
         $this->assertEquals($expectedRouteResult, $routeResult);
index d0f4dfa..e191a29 100644 (file)
@@ -73,11 +73,13 @@ class PageResolver implements MiddlewareInterface
 
         // Resolve the page ID based on TYPO3's native routing functionality
         if ($hasSiteConfiguration) {
+            /** @var PageRouter $router */
+            $router = $site->getRouter();
             /** @var RouteResult $previousResult */
             $previousResult = $request->getAttribute('routing', new RouteResult($request->getUri(), $site, $language));
             if (!empty($previousResult->getTail())) {
                 // Check for the route
-                $routeResult = $this->getPageRouter()->matchRoute($request, $previousResult->getTail(), $site, $language);
+                $routeResult = $router->matchRequest($request, $previousResult);
                 $request = $request->withAttribute('routing', $routeResult);
                 if (is_array($routeResult['page'])) {
                     $page = $routeResult['page'];
@@ -141,14 +143,6 @@ class PageResolver implements MiddlewareInterface
     }
 
     /**
-     * @return PageRouter
-     */
-    protected function getPageRouter(): PageRouter
-    {
-        return GeneralUtility::makeInstance(PageRouter::class);
-    }
-
-    /**
      * Provides ways to bypass the '?id=[xxx]&type=[xx]' format, using either PATH_INFO or Server Rewrites
      *
      * Two options:
index f4b48e0..4f6ee32 100644 (file)
@@ -24,7 +24,7 @@ use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Http\RequestFactory;
 use TYPO3\CMS\Core\LinkHandling\LinkService;
 use TYPO3\CMS\Core\Resource\File;
-use TYPO3\CMS\Core\Routing\PageUriBuilder;
+use TYPO3\CMS\Core\Routing\RouterInterface;
 use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -45,7 +45,7 @@ class StaticRouteResolver implements MiddlewareInterface
             if (in_array($path, $routeNames, true)) {
                 $key = array_search($path, $routeNames, true);
                 $routeConfig = $configuration[$key];
-                [$content, $contentType] = $this->resolveByType($request, $routeConfig['type'], $routeConfig);
+                [$content, $contentType] = $this->resolveByType($request, $site, $routeConfig['type'], $routeConfig);
                 return new HtmlResponse($content, 200, ['Content-Type' => $contentType]);
             }
         }
@@ -73,20 +73,18 @@ class StaticRouteResolver implements MiddlewareInterface
         return [$content, $contentType];
     }
 
-    private function getPageUri(ServerRequestInterface $request, array $urlParams): string
+    private function getPageUri(ServerRequestInterface $request, Site $site, array $urlParams): string
     {
-        $uriBuilder = GeneralUtility::makeInstance(PageUriBuilder::class);
-        $uri = (string)$uriBuilder->buildUri(
+        $uri = $site->getRouter()->generateUri(
             (int)$urlParams['pageuid'],
-            ['type' => $urlParams['pagetype'] ?? 0],
-            null,
-            ['language' => $request->getAttribute('language', null)],
-            PageUriBuilder::ABSOLUTE_URL
+            ['type' => $urlParams['pagetype'] ?? 0, '_language' => $request->getAttribute('language', null)],
+            '',
+            RouterInterface::ABSOLUTE_URL
         );
-        return $uri;
+        return (string)$uri;
     }
 
-    private function resolveByType(ServerRequestInterface $request, string $type, array $routeConfig): array
+    private function resolveByType(ServerRequestInterface $request, Site $site, string $type, array $routeConfig): array
     {
         switch ($type) {
             case 'staticText':
@@ -97,7 +95,7 @@ class StaticRouteResolver implements MiddlewareInterface
                 $linkService = GeneralUtility::makeInstance(LinkService::class);
                 $urlParams = $linkService->resolve($routeConfig['source']);
                 if ($urlParams['type'] === 'url' || $urlParams['type'] === 'page') {
-                    $uri = $urlParams['url'] ?? $this->getPageUri($request, $urlParams);
+                    $uri = $urlParams['url'] ?? $this->getPageUri($request, $site, $urlParams);
                     [$content, $contentType] = $this->getFromUri($uri);
                 } elseif ($urlParams['type'] === 'file') {
                     [$content, $contentType] = $this->getFromFile($urlParams['file']);
index 83be0eb..f440829 100644 (file)
@@ -24,7 +24,7 @@ use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Exception\Page\RootLineException;
 use TYPO3\CMS\Core\Exception\SiteNotFoundException;
-use TYPO3\CMS\Core\Routing\PageUriBuilder;
+use TYPO3\CMS\Core\Routing\RouterInterface;
 use TYPO3\CMS\Core\Routing\SiteMatcher;
 use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Site\Entity\SiteInterface;
@@ -359,13 +359,12 @@ class PageLinkBuilder extends AbstractTypolinkBuilder
         }
 
         $targetPageId = (int)($page['l10n_parent'] > 0 ? $page['l10n_parent'] : $page['uid']);
-
-        $uri = GeneralUtility::makeInstance(PageUriBuilder::class)->buildUri(
+        $queryParameters['_language'] = $siteLanguageOfTargetPage;
+        $uri = $siteOfTargetPage->getRouter()->generateUri(
             $targetPageId,
             $queryParameters,
             $fragment,
-            ['site' => $siteOfTargetPage, 'language' => $siteLanguageOfTargetPage],
-            $useAbsoluteUrl ? PageUriBuilder::ABSOLUTE_URL : PageUriBuilder::ABSOLUTE_PATH
+            $useAbsoluteUrl ? RouterInterface::ABSOLUTE_URL : RouterInterface::ABSOLUTE_PATH
         );
         // Override scheme, but only if the site does not define a scheme yet AND the site defines a domain/host
         if ($useAbsoluteUrl && !$uri->getScheme() && $uri->getHost()) {
index 7cf6ac1..665c18b 100644 (file)
@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Frontend\Tests\Unit\Middleware;
  * The TYPO3 project - inspiring people to share!
  */
 
+use PHPUnit\Framework\MockObject\MockObject;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\RequestHandlerInterface;
@@ -84,29 +85,32 @@ class PageResolverTest extends UnitTestCase
     {
         $incomingUrl = 'https://king.com/lotus-flower/en/mr-magpie/bloom';
         $pageRecord = ['uid' => 13, 'l10n_parent' => 0, 'slug' => '/mr-magpie/bloom'];
-        $site = new Site('lotus-flower', 13, [
-            'base' => '/lotus-flower/',
-            'languages' => [
-                0 => [
-                    'languageId' => 0,
-                    'locale' => 'en_US.UTF-8',
-                    'base' => '/en/'
-                ],
+        /** @var MockObject|Site $site */
+        $site = $this->getMockBuilder(Site::class)->setConstructorArgs([
+            'lotus-flower', 13, [
+                'base' => '/lotus-flower/',
+                'languages' => [
+                    0 => [
+                        'languageId' => 0,
+                        'locale' => 'en_US.UTF-8',
+                        'base' => '/en/'
+                    ],
+                ]
             ]
-        ]);
+        ])->setMethods(['getRouter'])->getMock();
         $language = $site->getDefaultLanguage();
 
         $request = new ServerRequest($incomingUrl, 'GET');
         $request = $request->withAttribute('site', $site);
         $request = $request->withAttribute('language', $language);
         $request = $request->withAttribute('routing', new RouteResult($request->getUri(), $site, $language, 'mr-magpie/bloom'));
-
         $expectedRouteResult = new RouteResult($request->getUri(), $site, $language, '', ['page' => $pageRecord]);
-        $pageRouterMock = $this->getMockBuilder(PageRouter::class)->disableOriginalConstructor()->setMethods(['matchRoute'])->getMock();
-        $pageRouterMock->expects($this->once())->method('matchRoute')->willReturn($expectedRouteResult);
 
-        $subject = $this->getAccessibleMock(PageResolver::class, ['getPageRouter'], [$this->controller], '', true);
-        $subject->expects($this->any())->method('getPageRouter')->willReturn($pageRouterMock);
+        $pageRouterMock = $this->getMockBuilder(PageRouter::class)->disableOriginalConstructor()->setMethods(['matchRequest'])->getMock();
+        $pageRouterMock->expects($this->once())->method('matchRequest')->willReturn($expectedRouteResult);
+        $site->expects($this->any())->method('getRouter')->willReturn($pageRouterMock);
+
+        $subject = new PageResolver($this->controller);
         $response = $subject->process($request, $this->responseOutputHandler);
         $result = $response->getBody()->getContents();
         $result = json_decode($result, true);
@@ -123,16 +127,19 @@ class PageResolverTest extends UnitTestCase
     {
         $incomingUrl = 'https://king.com/lotus-flower/en/mr-magpie/bloom/';
         $pageRecord = ['uid' => 13, 'l10n_parent' => 0, 'slug' => '/mr-magpie/bloom'];
-        $site = new Site('lotus-flower', 13, [
-            'base' => '/lotus-flower/',
-            'languages' => [
-                0 => [
-                    'languageId' => 0,
-                    'locale' => 'en_US.UTF-8',
-                    'base' => '/en/'
-                ],
+        /** @var MockObject|Site $site */
+        $site = $this->getMockBuilder(Site::class)->setConstructorArgs([
+            'lotus-flower', 13, [
+                'base' => '/lotus-flower/',
+                'languages' => [
+                    0 => [
+                        'languageId' => 0,
+                        'locale' => 'en_US.UTF-8',
+                        'base' => '/en/'
+                    ],
+                ]
             ]
-        ]);
+        ])->setMethods(['getRouter'])->getMock();
         $language = $site->getDefaultLanguage();
 
         $request = new ServerRequest($incomingUrl, 'GET');
@@ -141,11 +148,11 @@ class PageResolverTest extends UnitTestCase
         $request = $request->withAttribute('routing', new RouteResult($request->getUri(), $site, $language, 'mr-magpie/bloom/'));
 
         $expectedRouteResult = new RouteResult($request->getUri(), $site, $language, '/', ['page' => $pageRecord]);
-        $pageRouterMock = $this->getMockBuilder(PageRouter::class)->disableOriginalConstructor()->setMethods(['matchRoute'])->getMock();
-        $pageRouterMock->expects($this->once())->method('matchRoute')->willReturn($expectedRouteResult);
+        $pageRouterMock = $this->getMockBuilder(PageRouter::class)->disableOriginalConstructor()->setMethods(['matchRequest'])->getMock();
+        $pageRouterMock->expects($this->once())->method('matchRequest')->willReturn($expectedRouteResult);
+        $site->expects($this->any())->method('getRouter')->willReturn($pageRouterMock);
 
-        $subject = $this->getAccessibleMock(PageResolver::class, ['getPageRouter'], [$this->controller], '', true);
-        $subject->expects($this->any())->method('getPageRouter')->willReturn($pageRouterMock);
+        $subject = new PageResolver($this->controller);
         $response = $subject->process($request, $this->responseOutputHandler);
         $this->assertEquals(307, $response->getStatusCode());
         $this->assertEquals('https://king.com/lotus-flower/en/mr-magpie/bloom', $response->getHeader('Location')[0]);
@@ -160,16 +167,19 @@ class PageResolverTest extends UnitTestCase
     {
         $incomingUrl = 'https://king.com/lotus-flower/en/mr-magpie/bloom';
         $pageRecord = ['uid' => 13, 'l10n_parent' => 0, 'slug' => '/mr-magpie/bloom/'];
-        $site = new Site('lotus-flower', 13, [
-            'base' => '/lotus-flower/',
-            'languages' => [
-                0 => [
-                    'languageId' => 0,
-                    'locale' => 'en_US.UTF-8',
-                    'base' => '/en/'
-                ],
+        /** @var MockObject|Site $site */
+        $site = $this->getMockBuilder(Site::class)->setConstructorArgs([
+            'lotus-flower', 13, [
+                'base' => '/lotus-flower/',
+                'languages' => [
+                    0 => [
+                        'languageId' => 0,
+                        'locale' => 'en_US.UTF-8',
+                        'base' => '/en/'
+                    ],
+                ]
             ]
-        ]);
+        ])->setMethods(['getRouter'])->getMock();
         $language = $site->getDefaultLanguage();
 
         $request = new ServerRequest($incomingUrl, 'GET');
@@ -178,11 +188,11 @@ class PageResolverTest extends UnitTestCase
         $request = $request->withAttribute('routing', new RouteResult($request->getUri(), $site, $language, 'mr-magpie/bloom/'));
 
         $expectedRouteResult = new RouteResult($request->getUri(), $site, $language, '', ['page' => $pageRecord]);
-        $pageRouterMock = $this->getMockBuilder(PageRouter::class)->disableOriginalConstructor()->setMethods(['matchRoute'])->getMock();
-        $pageRouterMock->expects($this->once())->method('matchRoute')->willReturn($expectedRouteResult);
+        $pageRouterMock = $this->getMockBuilder(PageRouter::class)->disableOriginalConstructor()->setMethods(['matchRequest'])->getMock();
+        $pageRouterMock->expects($this->once())->method('matchRequest')->willReturn($expectedRouteResult);
+        $site->expects($this->any())->method('getRouter')->willReturn($pageRouterMock);
 
-        $subject = $this->getAccessibleMock(PageResolver::class, ['getPageRouter'], [$this->controller], '', true);
-        $subject->expects($this->any())->method('getPageRouter')->willReturn($pageRouterMock);
+        $subject = new PageResolver($this->controller);
         $response = $subject->process($request, $this->responseOutputHandler);
         $this->assertEquals(307, $response->getStatusCode());
         $this->assertEquals('https://king.com/lotus-flower/en/mr-magpie/bloom/', $response->getHeader('Location')[0]);
index d0a9cb7..ea38695 100644 (file)
@@ -66,9 +66,9 @@ class CanonicalGeneratorTest extends AbstractTestCase
             'uid: 2 with canonical_link' => [2, '<link rel="canonical" href="http://localhost/dummy-1-2"/>' . LF],
             'uid: 3 with canonical_link AND content_from_pid = 2' => [3, '<link rel="canonical" href="http://localhost/dummy-1-2"/>' . LF],
             'uid: 4 without canonical_link AND content_from_pid = 2' => [4, '<link rel="canonical" href="http://localhost/dummy-1-2"/>' . LF],
-            'uid: 5 without canonical_link AND without content_from_pid set' => [5, '<link rel="canonical" href="http://localhost/?id=5"/>' . LF],
-            'uid: 6 without canonical_link AND content_from_pid = 7 (but target page is deleted)' => [6, '<link rel="canonical" href="http://localhost/?id=6"/>' . LF],
-            'uid: 8 without canonical_link AND content_from_pid = 9 (but target page is hidden)' => [8, '<link rel="canonical" href="http://localhost/?id=8"/>' . LF],
+            'uid: 5 without canonical_link AND without content_from_pid set' => [5, '<link rel="canonical" href="http://localhost/dummy-1-2-5"/>' . LF],
+            'uid: 6 without canonical_link AND content_from_pid = 7 (but target page is deleted)' => [6, '<link rel="canonical" href="http://localhost/dummy-1-2-6"/>' . LF],
+            'uid: 8 without canonical_link AND content_from_pid = 9 (but target page is hidden)' => [8, '<link rel="canonical" href="http://localhost/dummy-1-2-8"/>' . LF],
         ];
     }
 
index 787cb01..7499c01 100644 (file)
@@ -6,6 +6,7 @@
         <title>Root 1</title>
         <is_siteroot>1</is_siteroot>
         <canonical_link>http://localhost/</canonical_link>
+        <slug>/</slug>
         <content_from_pid>0</content_from_pid>
         <deleted>0</deleted>
     </pages>
@@ -15,6 +16,7 @@
         <title>Dummy 1-2</title>
         <tstamp>1491811200</tstamp>
         <canonical_link>http://localhost/dummy-1-2</canonical_link>
+        <slug>/dummy-1-2</slug>
         <content_from_pid>0</content_from_pid>
         <deleted>0</deleted>
     </pages>
@@ -24,6 +26,7 @@
         <title>Dummy 1-3</title>
         <SYS_LASTCHANGED>1535657401</SYS_LASTCHANGED>
         <canonical_link>http://localhost/dummy-1-3</canonical_link>
+        <slug>/dummy-1-3</slug>
         <content_from_pid>2</content_from_pid>
         <deleted>0</deleted>
     </pages>
@@ -32,6 +35,7 @@
         <pid>1</pid>
         <title>Dummy 1-4</title>
         <canonical_link></canonical_link>
+        <slug>/dummy-1-4</slug>
         <content_from_pid>2</content_from_pid>
         <deleted>0</deleted>
     </pages>
@@ -40,6 +44,7 @@
         <pid>2</pid>
         <title>Dummy 1-2-5</title>
         <canonical_link></canonical_link>
+        <slug>/dummy-1-2-5</slug>
         <content_from_pid>0</content_from_pid>
         <deleted>0</deleted>
     </pages>
@@ -47,6 +52,7 @@
         <uid>6</uid>
         <pid>2</pid>
         <title>Dummy</title>
+        <slug>/dummy-1-2-6</slug>
         <canonical_link></canonical_link>
         <content_from_pid>7</content_from_pid>
         <deleted>0</deleted>
@@ -56,6 +62,7 @@
         <pid>2</pid>
         <title>Dummy</title>
         <canonical_link></canonical_link>
+        <slug>/dummy-1-2-7</slug>
         <content_from_pid>0</content_from_pid>
         <deleted>1</deleted>
     </pages>
@@ -63,6 +70,7 @@
         <uid>8</uid>
         <pid>2</pid>
         <title>Dummy</title>
+        <slug>/dummy-1-2-8</slug>
         <canonical_link></canonical_link>
         <content_from_pid>9</content_from_pid>
         <deleted>0</deleted>
@@ -72,6 +80,7 @@
         <pid>2</pid>
         <title>Dummy</title>
         <canonical_link></canonical_link>
+        <slug>/dummy-1-2-9</slug>
         <content_from_pid>0</content_from_pid>
         <deleted>0</deleted>
         <hidden>1</hidden>
index 476a68f..f61c853 100644 (file)
@@ -5,6 +5,7 @@
         <pid>0</pid>
         <title>Root 1</title>
         <is_siteroot>1</is_siteroot>
+        <slug>/</slug>
         <deleted>0</deleted>
     </pages>
     <pages>
@@ -12,6 +13,7 @@
         <pid>1</pid>
         <title>Dummy 1-2</title>
         <tstamp>1491811200</tstamp>
+        <slug>/dummy-1-2</slug>
         <deleted>0</deleted>
     </pages>
     <pages>
         <pid>1</pid>
         <title>Dummy 1-3</title>
         <SYS_LASTCHANGED>1535657401</SYS_LASTCHANGED>
+        <slug>/dummy-1-3</slug>
         <deleted>0</deleted>
     </pages>
     <pages>
         <uid>4</uid>
         <pid>1</pid>
         <title>Dummy 1-4</title>
+        <slug>/dummy-1-4</slug>
         <deleted>0</deleted>
     </pages>
     <pages>
         <uid>5</uid>
         <pid>2</pid>
         <title>Dummy 1-2-5</title>
+        <slug>/dummy-1-2-5</slug>
         <deleted>0</deleted>
     </pages>
     <pages>
         <uid>6</uid>
         <pid>2</pid>
         <title>Dummy 1-2-6</title>
+        <slug>/dummy-1-2-6</slug>
         <deleted>0</deleted>
     </pages>
     <pages>
         <uid>7</uid>
         <pid>2</pid>
         <title>Dummy 1-2-7</title>
+        <slug>/dummy-1-2-7</slug>
         <deleted>0</deleted>
     </pages>
     <pages>
         <uid>8</uid>
         <pid>3</pid>
         <title>Dummy 1-3-8</title>
+        <slug>/dummy-1-3-8</slug>
         <deleted>0</deleted>
     </pages>
     <pages>
         <uid>9</uid>
         <pid>3</pid>
         <title>Dummy 1-3-9</title>
+        <slug>/dummy-1-3-9</slug>
         <deleted>0</deleted>
     </pages>
     <pages>
         <uid>10</uid>
         <pid>4</pid>
         <title>Dummy 1-4-10</title>
+        <slug>/dummy-1-4-10</slug>
         <deleted>0</deleted>
     </pages>
 </dataset>
index bb39ea5..dc22dba 100644 (file)
@@ -51,22 +51,23 @@ class XmlSitemapIndexTest extends AbstractTestCase
     {
         $this->writeSiteConfiguration(
             'website-local',
-            $this->buildSiteConfiguration(1, 'http://localhost/')
+            $this->buildSiteConfiguration(1, 'http://localhost/'),
+            [
+                $this->buildDefaultLanguageConfiguration('EN', '/')
+            ]
         );
 
         $response = $this->executeFrontendRequest(
-            (new InternalRequest())->withQueryParameters([
+            (new InternalRequest('http://localhost/'))->withQueryParameters([
                 'id' => 1,
                 'type' => 1533906435
             ])
         );
 
-        $expectedHeaders = [
-            'Content-Length' => [0 => '462']
-        ];
-        $expectedBody = '#<loc>http://localhost/\?id=1&amp;type=1533906435&amp;sitemap=pages&amp;page=0</loc>#';
+        $expectedContent = '<loc>http://localhost/?type=1533906435&amp;sitemap=pages&amp;page=0</loc>';
         $this->assertEquals(200, $response->getStatusCode());
-        $this->assertEquals($expectedHeaders, $response->getHeaders());
-        $this->assertRegExp($expectedBody, (string)$response->getBody());
+        $this->assertArrayHasKey('Content-Length', $response->getHeaders());
+        $this->assertGreaterThan(0, $response->getHeader('Content-Length')[0]);
+        $this->assertContains($expectedContent, (string)$response->getBody());
     }
 }
index c5d2454..42e261b 100644 (file)
@@ -29,8 +29,8 @@ use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Page\PageRenderer;
-use TYPO3\CMS\Core\Routing\PageUriBuilder;
 use TYPO3\CMS\Core\Routing\SiteMatcher;
+use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
@@ -218,21 +218,16 @@ class ViewModuleController
                 $additionalGetVars .= '&MP=' . $mountPointInformation['MPvar'];
             }
             $additionalGetVars .= $this->getTypeParameterIfSet($finalPageIdToShow);
-            $additionalQueryParams = [];
-            parse_str($additionalGetVars, $additionalQueryParams);
-            $options = [
-                'site' => $site,
-                'rootLine' => $rootLine,
-                'language' => $languageId,
-            ];
-            $uriBuilder = GeneralUtility::makeInstance(PageUriBuilder::class);
-            return (string)$uriBuilder->buildUri(
-                $finalPageIdToShow,
-                $additionalQueryParams,
-                '',
-                $options,
-                $uriBuilder::ABSOLUTE_URL
-            );
+            /** @todo */
+            if ($site instanceof Site) {
+                $additionalQueryParams = [];
+                parse_str($additionalGetVars, $additionalQueryParams);
+                $additionalQueryParams['_language'] = $site->getLanguageById($languageId);
+                $uri = (string)$site->getRouter()->generateUri($finalPageIdToShow, $additionalQueryParams);
+            } else {
+                $uri = BackendUtility::getPreviewUrl($finalPageIdToShow, '', $rootLine, '', '', $additionalGetVars);
+            }
+            return $uri;
         }
         return '#';
     }