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