[FEATURE] Add "Pseudo" Site functionality
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Middleware / SiteResolver.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Frontend\Middleware;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use Psr\Http\Server\MiddlewareInterface;
21 use Psr\Http\Server\RequestHandlerInterface;
22 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
23 use TYPO3\CMS\Core\Routing\SiteMatcher;
24 use TYPO3\CMS\Core\Site\Entity\PseudoSite;
25 use TYPO3\CMS\Core\Site\Entity\Site;
26 use TYPO3\CMS\Core\Site\Entity\SiteInterface;
27 use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
28 use TYPO3\CMS\Core\Site\SiteFinder;
29 use TYPO3\CMS\Core\Utility\GeneralUtility;
30 use TYPO3\CMS\Frontend\Controller\ErrorController;
31 use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;
32
33 /**
34 * Identifies if a site is configured for the request, based on "id" and "L" GET/POST parameters, or the requested
35 * string.
36 *
37 * If a site is found, the request is populated with the found language+site objects. If none is found, the main magic
38 * is handled by the PageResolver middleware.
39 *
40 * In addition to that, TSFE gets the $domainStartPage information resolved and added.
41 */
42 class SiteResolver implements MiddlewareInterface
43 {
44 /**
45 * @var SiteMatcher
46 */
47 protected $matcher;
48
49 public function __construct(SiteMatcher $matcher = null)
50 {
51 $this->matcher = $matcher ?? GeneralUtility::makeInstance(
52 SiteMatcher::class,
53 GeneralUtility::makeInstance(SiteFinder::class)
54 );
55 }
56
57 /**
58 * Resolve the site/language information by checking the page ID or the URL.
59 *
60 * @param ServerRequestInterface $request
61 * @param RequestHandlerInterface $handler
62 * @return ResponseInterface
63 */
64 public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
65 {
66 $routeResult = $this->matcher->matchRequest($request);
67 $site = $routeResult['site'] ?? null;
68 $language = $routeResult['language'] ?? null;
69
70 // language is found, and hidden but also not visible to the BE user, this needs to fail
71 if ($language instanceof SiteLanguage && !$this->isLanguageEnabled($language, $GLOBALS['BE_USER'] ?? null)) {
72 $request = $request->withAttribute('site', $site);
73 return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
74 $request,
75 'Page is not available in the requested language.',
76 ['code' => PageAccessFailureReasons::LANGUAGE_NOT_AVAILABLE]
77 );
78 }
79
80 // Add language+site information to the PSR-7 request object.
81 if ($language instanceof SiteLanguage && $site instanceof Site) {
82 $request = $request->withAttribute('site', $site);
83 $request = $request->withAttribute('language', $language);
84 $queryParams = $request->getQueryParams();
85 // necessary to calculate the proper hash base
86 $queryParams['L'] = $language->getLanguageId();
87 $request = $request->withQueryParams($queryParams);
88 $_GET['L'] = $queryParams['L'];
89 // At this point, we later get further route modifiers
90 // for bw-compat we update $GLOBALS[TYPO3_REQUEST] to be used later in TSFE.
91 $GLOBALS['TYPO3_REQUEST'] = $request;
92 } elseif ($site instanceof PseudoSite) {
93 $request = $request->withAttribute('site', $site);
94 // At this point, we later get further route modifiers
95 // for bw-compat we update $GLOBALS[TYPO3_REQUEST] to be used later in TSFE.
96 $GLOBALS['TYPO3_REQUEST'] = $request;
97 }
98
99 // Now resolve the root page of the site, the page_id of the current domain
100 if ($site instanceof SiteInterface) {
101 $GLOBALS['TSFE']->domainStartPage = $site->getRootPageId();
102 }
103
104 return $handler->handle($request);
105 }
106
107 /**
108 * Checks if the language is allowed in Frontend, if not, check if there is valid BE user
109 *
110 * @param SiteLanguage|null $language
111 * @param BackendUserAuthentication|null $user
112 * @return bool
113 */
114 protected function isLanguageEnabled(SiteLanguage $language, BackendUserAuthentication $user = null): bool
115 {
116 // language is hidden, check if a possible backend user is allowed to access the language
117 if ($language->enabled() || ($user instanceof BackendUserAuthentication && $user->checkLanguageAccess($language->getLanguageId()))) {
118 return true;
119 }
120 return false;
121 }
122 }