[!!!][TASK] Remove deprecated frontend-related hooks and include scripts
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Middleware / PageResolver.php
index 6fd6a8b..f99baf7 100644 (file)
@@ -23,9 +23,21 @@ use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Context\UserAspect;
 use TYPO3\CMS\Core\Context\WorkspaceAspect;
 use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Context\UserAspect;
 use TYPO3\CMS\Core\Context\WorkspaceAspect;
+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\Routing\PageArguments;
+use TYPO3\CMS\Core\Routing\RouteNotFoundException;
+use TYPO3\CMS\Core\Routing\SiteRouteResult;
+use TYPO3\CMS\Core\Site\Entity\Site;
+use TYPO3\CMS\Core\Site\Entity\SiteInterface;
+use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\MathUtility;
+use TYPO3\CMS\Frontend\Controller\ErrorController;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
+use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;
 
 /**
  * Process the ID, type and other parameters.
 
 /**
  * Process the ID, type and other parameters.
@@ -36,6 +48,16 @@ use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
  */
 class PageResolver implements MiddlewareInterface
 {
  */
 class PageResolver implements MiddlewareInterface
 {
+    /**
+     * @var TypoScriptFrontendController
+     */
+    protected $controller;
+
+    public function __construct(TypoScriptFrontendController $controller = null)
+    {
+        $this->controller = $controller ?? $GLOBALS['TSFE'];
+    }
+
     /**
      * Resolve the page ID
      *
     /**
      * Resolve the page ID
      *
@@ -45,41 +67,131 @@ class PageResolver implements MiddlewareInterface
      */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
      */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
-        $GLOBALS['TSFE']->siteScript = $request->getAttribute('normalizedParams')->getSiteScript();
-        $this->checkAlternativeIdMethods($GLOBALS['TSFE']);
-        $GLOBALS['TSFE']->determineId();
+        // First, resolve the root page of the site, the Page ID of the current domain
+        if (($site = $request->getAttribute('site', null)) instanceof SiteInterface) {
+            $this->controller->domainStartPage = $site->getRootPageId();
+        }
+        $language = $request->getAttribute('language', null);
+
+        $hasSiteConfiguration = $language instanceof SiteLanguage && $site instanceof Site;
+
+        // Resolve the page ID based on TYPO3's native routing functionality
+        if ($hasSiteConfiguration) {
+            /** @var SiteRouteResult $previousResult */
+            $previousResult = $request->getAttribute('routing', null);
+            if (!$previousResult) {
+                return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
+                    $request,
+                    'The requested page does not exist',
+                    ['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
+                );
+            }
+
+            $requestId = (string)($request->getQueryParams()['id'] ?? '');
+            if (!empty($requestId) && !empty($page = $this->resolvePageId($requestId))) {
+                // Legacy URIs (?id=12345) takes precedence, not matter if a route is given
+                $pageArguments = new PageArguments(
+                    (int)($page['l10n_parent'] ?: $page['uid']),
+                    (string)($request->getQueryParams()['type'] ?? '0'),
+                    [],
+                    [],
+                    $request->getQueryParams()
+                );
+            } else {
+                // Check for the route
+                try {
+                    $pageArguments = $site->getRouter()->matchRequest($request, $previousResult);
+                } catch (RouteNotFoundException $e) {
+                    return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
+                        $request,
+                        'The requested page does not exist',
+                        ['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
+                    );
+                }
+            }
+            if (!$pageArguments->getPageId()) {
+                return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
+                    $request,
+                    'The requested page does not exist',
+                    ['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
+                );
+            }
+
+            $this->controller->id = $pageArguments->getPageId();
+            $this->controller->type = $pageArguments->getPageType() ?? $this->controller->type;
+            $request = $request->withAttribute('routing', $pageArguments);
+            // stop in case arguments are dirty (=defined twice in route and GET query parameters)
+            if ($pageArguments->areDirty()) {
+                return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
+                    $request,
+                    'The requested URL is not distinct',
+                    ['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
+                );
+            }
+
+            // merge the PageArguments with the request query parameters
+            $queryParams = array_replace_recursive($request->getQueryParams(), $pageArguments->getArguments());
+            $request = $request->withQueryParams($queryParams);
+            $this->controller->setPageArguments($pageArguments);
+
+            // At this point, we later get further route modifiers
+            // for bw-compat we update $GLOBALS[TYPO3_REQUEST] to be used later in TSFE.
+            $GLOBALS['TYPO3_REQUEST'] = $request;
+        } else {
+            // old-school page resolving for realurl, cooluri etc.
+            $this->controller->siteScript = $request->getAttribute('normalizedParams')->getSiteScript();
+        }
+
+        $this->controller->determineId();
 
         // No access? Then remove user & Re-evaluate the page-id
 
         // No access? Then remove user & Re-evaluate the page-id
-        if ($GLOBALS['TSFE']->isBackendUserLoggedIn() && !$GLOBALS['BE_USER']->doesUserHaveAccess($GLOBALS['TSFE']->page, Permission::PAGE_SHOW)) {
+        if ($this->controller->isBackendUserLoggedIn() && !$GLOBALS['BE_USER']->doesUserHaveAccess($this->controller->page, Permission::PAGE_SHOW)) {
             unset($GLOBALS['BE_USER']);
             // Register an empty backend user as aspect
             $this->setBackendUserAspect(GeneralUtility::makeInstance(Context::class), null);
             unset($GLOBALS['BE_USER']);
             // Register an empty backend user as aspect
             $this->setBackendUserAspect(GeneralUtility::makeInstance(Context::class), null);
-            $this->checkAlternativeIdMethods($GLOBALS['TSFE']);
-            $GLOBALS['TSFE']->determineId();
+            $this->controller->determineId();
         }
 
         }
 
-        // Evaluate the cache hash parameter
-        $GLOBALS['TSFE']->makeCacheHash();
-
         return $handler->handle($request);
     }
 
     /**
         return $handler->handle($request);
     }
 
     /**
-     * Provides ways to bypass the '?id=[xxx]&type=[xx]' format, using either PATH_INFO or Server Rewrites
-     *
-     * Two options:
-     * 1) Use PATH_INFO (also Apache) to extract id and type from that var. Does not require any special modules compiled with apache. (less typical)
-     * 2) Using hook which enables features like those provided from "realurl" extension (AKA "Speaking URLs")
-     *
-     * @param TypoScriptFrontendController $tsfe
+     * @param string $pageId
+     * @return array|null
      */
      */
-    protected function checkAlternativeIdMethods(TypoScriptFrontendController $tsfe)
+    protected function resolvePageId(string $pageId): ?array
     {
     {
-        // Call post processing function for custom URL methods.
-        $_params = ['pObj' => &$tsfe];
-        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc'] ?? [] as $_funcRef) {
-            GeneralUtility::callUserFunction($_funcRef, $_params, $tsfe);
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('pages');
+        $queryBuilder
+            ->getRestrictions()
+            ->removeAll()
+            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
+            ->add(GeneralUtility::makeInstance(FrontendWorkspaceRestriction::class));
+
+        if (MathUtility::canBeInterpretedAsInteger($pageId)) {
+            $constraint = $queryBuilder->expr()->eq(
+                'uid',
+                $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)
+            );
+        } else {
+            $constraint = $queryBuilder->expr()->eq(
+                'alias',
+                $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_STR)
+            );
+        }
+
+        $statement = $queryBuilder
+            ->select('uid', 'l10n_parent', 'pid')
+            ->from('pages')
+            ->where($constraint)
+            ->execute();
+
+        $page = $statement->fetch();
+        if (empty($page)) {
+            return null;
         }
         }
+        return $page;
     }
 
     /**
     }
 
     /**