4e06a52b0bfd3f06e18f2936875f37de5892ca88
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Site / Entity / PseudoSite.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Core\Site\Entity;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
20 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
21 use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
22 use TYPO3\CMS\Core\Localization\LanguageService;
23
24 /**
25 * Entity representing a site with legacy configuration (sys_domain) and all available languages in the system (sys_language)
26 */
27 class PseudoSite implements SiteInterface
28 {
29 /**
30 * @var string[]
31 */
32 protected $entryPoints;
33
34 /**
35 * @var int
36 */
37 protected $rootPageId;
38
39 /**
40 * @var SiteLanguage[]
41 */
42 protected $languages;
43
44 /**
45 * attached sys_domain records
46 * @var array
47 */
48 protected $domainRecords = [];
49
50 /**
51 * Sets up a pseudo site object, and its languages and error handlers
52 *
53 * @param int $rootPageId
54 * @param array $configuration
55 */
56 public function __construct(int $rootPageId, array $configuration)
57 {
58 $this->rootPageId = $rootPageId;
59 foreach ($configuration['domains'] ?? [] as $domain) {
60 if (empty($domain['domainName'] ?? false)) {
61 continue;
62 }
63 $this->domainRecords[] = $domain;
64 $this->entryPoints[] = $this->sanitizeBaseUrl($domain['domainName'] ?: '');
65 }
66 if (empty($this->entryPoints)) {
67 $this->entryPoints = ['/'];
68 }
69 $baseEntryPoint = reset($this->entryPoints);
70 foreach ($configuration['languages'] as $languageConfiguration) {
71 $languageUid = (int)$languageConfiguration['languageId'];
72 // Language configuration does not have a base defined
73 // So the main site base is used (usually done for default languages)
74 $base = $this->sanitizeBaseUrl(rtrim($baseEntryPoint, '/') . '/');
75 $this->languages[$languageUid] = new SiteLanguage(
76 $languageUid,
77 $languageConfiguration['locale'] ?? '',
78 $base,
79 $languageConfiguration
80 );
81 }
82 }
83
84 /**
85 * Returns a generic identifier
86 *
87 * @return string
88 */
89 public function getIdentifier(): string
90 {
91 return 'PSEUDO_' . $this->rootPageId;
92 }
93
94 /**
95 * Returns the first base URL of this site, falls back to "/"
96 */
97 public function getBase(): string
98 {
99 return $this->entryPoints[0] ?? '/';
100 }
101
102 /**
103 * Returns the base URLs of this site, if none given, it's always "/"
104 *
105 * @return array
106 */
107 public function getEntryPoints(): array
108 {
109 return $this->entryPoints;
110 }
111
112 /**
113 * Returns the root page ID of this site
114 *
115 * @return int
116 */
117 public function getRootPageId(): int
118 {
119 return $this->rootPageId;
120 }
121
122 /**
123 * Returns all available languages of this site
124 *
125 * @return SiteLanguage[]
126 */
127 public function getLanguages(): array
128 {
129 return $this->languages;
130 }
131
132 /**
133 * Returns a language of this site, given by the sys_language_uid
134 *
135 * @param int $languageId
136 * @return SiteLanguage
137 * @throws \InvalidArgumentException
138 */
139 public function getLanguageById(int $languageId): SiteLanguage
140 {
141 if (isset($this->languages[$languageId])) {
142 return $this->languages[$languageId];
143 }
144 throw new \InvalidArgumentException(
145 'Language ' . $languageId . ' does not exist on site ' . $this->getIdentifier() . '.',
146 1522965188
147 );
148 }
149
150 /**
151 * Fetch the available languages for a specific backend user, used in various places in Backend and Frontend
152 * when a Backend User is authenticated.
153 *
154 * @param BackendUserAuthentication $user
155 * @param int $pageId
156 * @param bool $includeAllLanguagesFlag whether to include "-1" into the list, useful for some backend outputs
157 * @return array
158 */
159 public function getAvailableLanguages(BackendUserAuthentication $user, int $pageId, bool $includeAllLanguagesFlag = false)
160 {
161 $availableLanguages = [];
162
163 // Check if we need to add language "-1"
164 if ($includeAllLanguagesFlag && $user->checkLanguageAccess(-1)) {
165 $availableLanguages[-1] = new SiteLanguage(-1, '', $this->getBase(), [
166 'title' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:multipleLanguages'),
167 'flag' => 'flag-multiple'
168 ]);
169 }
170
171 // Do not add the ones that are not allowed by the user
172 foreach ($this->languages as $language) {
173 if ($user->checkLanguageAccess($language->getLanguageId())) {
174 if ($language->getLanguageId() === 0) {
175 $pageTs = BackendUtility::getPagesTSconfig($pageId);
176 // 0: "Default" language
177 $defaultLanguageLabel = 'LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:defaultLanguage';
178 if (isset($pageTs['mod.']['SHARED.']['defaultLanguageLabel'])) {
179 $defaultLanguageLabel = $pageTs['mod.']['SHARED.']['defaultLanguageLabel'] . ' (' . $this->getLanguageService()->sL($defaultLanguageLabel) . ')';
180 }
181 $defaultLanguageFlag = 'empty-empty';
182 if (isset($pageTs['mod.']['SHARED.']['defaultLanguageFlag'])) {
183 $defaultLanguageFlag = 'flags-' . $pageTs['mod.']['SHARED.']['defaultLanguageFlag'];
184 }
185 $language = new SiteLanguage(0, '', $language->getBase(), [
186 'title' => $this->getLanguageService()->sL($defaultLanguageLabel),
187 'flag' => $defaultLanguageFlag,
188 ]);
189 }
190 $availableLanguages[$language->getLanguageId()] = $language;
191 }
192 }
193
194 return $availableLanguages;
195 }
196
197 /**
198 * Returns a ready-to-use error handler, to be used within the ErrorController
199 *
200 * @param int $statusCode
201 * @return PageErrorHandlerInterface
202 * @throws \RuntimeException
203 */
204 public function getErrorHandler(int $statusCode): PageErrorHandlerInterface
205 {
206 throw new \RuntimeException('No error handler given for the status code "' . $statusCode . '".', 1522495102);
207 }
208
209 /**
210 * If a site base contains "/" or "www.domain.com", it is ensured that
211 * parse_url() can handle this kind of configuration properly.
212 *
213 * @param string $base
214 * @return string
215 */
216 protected function sanitizeBaseUrl(string $base): string
217 {
218 // no protocol ("//") and the first part is no "/" (path), means that this is a domain like
219 // "www.domain.com/blabla", and we want to ensure that this one then gets a "no-scheme agnostic" part
220 if (!empty($base) && strpos($base, '//') === false && $base{0} !== '/') {
221 // either a scheme is added, or no scheme but with domain, or a path which is not absolute
222 // make the base prefixed with a slash, so it is recognized as path, not as domain
223 // treat as path
224 if (strpos($base, '.') === false) {
225 $base = '/' . $base;
226 } else {
227 // treat as domain name
228 $base = '//' . $base;
229 }
230 }
231 return $base;
232 }
233
234 /**
235 * Shorthand functionality for fetching the language service
236 * @return LanguageService
237 */
238 protected function getLanguageService(): LanguageService
239 {
240 return $GLOBALS['LANG'];
241 }
242 }