[FEATURE] Show only sys_languages in Backend which are available
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Site / Entity / Site.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\Core\Authentication\BackendUserAuthentication;
20 use TYPO3\CMS\Core\Error\PageErrorHandler\FluidPageErrorHandler;
21 use TYPO3\CMS\Core\Error\PageErrorHandler\InvalidPageErrorHandlerException;
22 use TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler;
23 use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
24 use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerNotConfiguredException;
25 use TYPO3\CMS\Core\Localization\LanguageService;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27
28 /**
29 * Entity representing a single site with available languages
30 */
31 class Site implements SiteInterface
32 {
33 protected const ERRORHANDLER_TYPE_PAGE = 'Page';
34 protected const ERRORHANDLER_TYPE_FLUID = 'Fluid';
35 protected const ERRORHANDLER_TYPE_PHP = 'PHP';
36
37 /**
38 * @var string
39 */
40 protected $identifier;
41
42 /**
43 * @var string
44 */
45 protected $base;
46
47 /**
48 * @var int
49 */
50 protected $rootPageId;
51
52 /**
53 * Any attributes for this site
54 * @var array
55 */
56 protected $configuration;
57
58 /**
59 * @var SiteLanguage[]
60 */
61 protected $languages;
62
63 /**
64 * @var array
65 */
66 protected $errorHandlers;
67
68 /**
69 * Sets up a site object, and its languages and error handlers
70 *
71 * @param string $identifier
72 * @param int $rootPageId
73 * @param array $configuration
74 */
75 public function __construct(string $identifier, int $rootPageId, array $configuration)
76 {
77 $this->identifier = $identifier;
78 $this->rootPageId = $rootPageId;
79 $this->configuration = $configuration;
80 $configuration['languages'] = $configuration['languages'] ?: [
81 0 => [
82 'languageId' => 0,
83 'title' => 'Default',
84 'navigationTitle' => '',
85 'typo3Language' => 'default',
86 'flag' => 'us',
87 'locale' => 'en_US.UTF-8',
88 'iso-639-1' => 'en',
89 'hreflang' => 'en-US',
90 'direction' => '',
91 ]
92 ];
93 $this->base = $this->sanitizeBaseUrl($configuration['base'] ?? '');
94 foreach ($configuration['languages'] as $languageConfiguration) {
95 $languageUid = (int)$languageConfiguration['languageId'];
96 // site language has defined its own base, this is the case most of the time.
97 if (!empty($languageConfiguration['base'])) {
98 $base = $languageConfiguration['base'];
99 $base = $this->sanitizeBaseUrl($base);
100 $baseParts = parse_url($base);
101 // no host given by the language-specific base, so lets prefix the main site base
102 if (empty($baseParts['scheme']) && empty($baseParts['host'])) {
103 $base = rtrim($this->base, '/') . '/' . ltrim($base, '/');
104 $base = $this->sanitizeBaseUrl($base);
105 }
106 } else {
107 // Language configuration does not have a base defined
108 // So the main site base is used (usually done for default languages)
109 $base = $this->sanitizeBaseUrl(rtrim($this->base, '/') . '/');
110 }
111 if (!empty($languageConfiguration['flag'])) {
112 if ($languageConfiguration['flag'] === 'global') {
113 $languageConfiguration['flag'] = 'flag-multiple';
114 } elseif ($languageConfiguration['flag'] !== 'empty-empty') {
115 $languageConfiguration['flag'] = 'flags-' . $languageConfiguration['flag'];
116 }
117 }
118 $this->languages[$languageUid] = new SiteLanguage(
119 $languageUid,
120 $languageConfiguration['locale'],
121 $base,
122 $languageConfiguration
123 );
124 }
125 foreach ($configuration['errorHandling'] ?? [] as $errorHandlingConfiguration) {
126 $code = $errorHandlingConfiguration['errorCode'];
127 unset($errorHandlingConfiguration['errorCode']);
128 $this->errorHandlers[(int)$code] = $errorHandlingConfiguration;
129 }
130 }
131
132 /**
133 * Gets the identifier of this site,
134 * mainly used when maintaining / configuring sites.
135 *
136 * @return string
137 */
138 public function getIdentifier(): string
139 {
140 return $this->identifier;
141 }
142
143 /**
144 * Returns the base URL of this site
145 *
146 * @return string
147 */
148 public function getBase(): string
149 {
150 return $this->base;
151 }
152
153 /**
154 * Returns the root page ID of this site
155 *
156 * @return int
157 */
158 public function getRootPageId(): int
159 {
160 return $this->rootPageId;
161 }
162
163 /**
164 * Returns all available languages of this site
165 *
166 * @return SiteLanguage[]
167 */
168 public function getLanguages(): array
169 {
170 $languages = [];
171 foreach ($this->languages as $languageId => $language) {
172 if ($language->enabled()) {
173 $languages[$languageId] = $language;
174 }
175 }
176 return $languages;
177 }
178
179 /**
180 * Returns all available languages of this site, even the ones disabled for frontend usages
181 *
182 * @return SiteLanguage[]
183 */
184 public function getAllLanguages(): array
185 {
186 return $this->languages;
187 }
188
189 /**
190 * Returns a language of this site, given by the sys_language_uid
191 *
192 * @param int $languageId
193 * @return SiteLanguage
194 * @throws \InvalidArgumentException
195 */
196 public function getLanguageById(int $languageId): SiteLanguage
197 {
198 if (isset($this->languages[$languageId])) {
199 return $this->languages[$languageId];
200 }
201 throw new \InvalidArgumentException(
202 'Language ' . $languageId . ' does not exist on site ' . $this->identifier . '.',
203 1522960188
204 );
205 }
206
207 /**
208 * @inheritdoc
209 */
210 public function getDefaultLanguage(): SiteLanguage
211 {
212 return reset($this->languages);
213 }
214
215 /**
216 * @inheritdoc
217 */
218 public function getAvailableLanguages(BackendUserAuthentication $user, bool $includeAllLanguagesFlag = false, int $pageId = null): array
219 {
220 $availableLanguages = [];
221
222 // Check if we need to add language "-1"
223 if ($includeAllLanguagesFlag && $user->checkLanguageAccess(-1)) {
224 $availableLanguages[-1] = new SiteLanguage(-1, '', $this->getBase(), [
225 'title' => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:multipleLanguages'),
226 'flag' => 'flag-multiple'
227 ]);
228 }
229
230 // Do not add the ones that are not allowed by the user
231 foreach ($this->languages as $language) {
232 if ($user->checkLanguageAccess($language->getLanguageId())) {
233 $availableLanguages[$language->getLanguageId()] = $language;
234 }
235 }
236
237 return $availableLanguages;
238 }
239
240 /**
241 * Returns a ready-to-use error handler, to be used within the ErrorController
242 *
243 * @param int $statusCode
244 * @return PageErrorHandlerInterface
245 * @throws PageErrorHandlerNotConfiguredException
246 * @throws InvalidPageErrorHandlerException
247 */
248 public function getErrorHandler(int $statusCode): PageErrorHandlerInterface
249 {
250 $errorHandlerConfiguration = $this->errorHandlers[$statusCode] ?? null;
251 switch ($errorHandlerConfiguration['errorHandler']) {
252 case self::ERRORHANDLER_TYPE_FLUID:
253 return GeneralUtility::makeInstance(FluidPageErrorHandler::class, $statusCode, $errorHandlerConfiguration);
254 case self::ERRORHANDLER_TYPE_PAGE:
255 return GeneralUtility::makeInstance(PageContentErrorHandler::class, $statusCode, $errorHandlerConfiguration);
256 case self::ERRORHANDLER_TYPE_PHP:
257 $handler = GeneralUtility::makeInstance($errorHandlerConfiguration['errorPhpClassFQCN'], $statusCode, $errorHandlerConfiguration);
258 // Check if the interface is implemented
259 if (!($handler instanceof PageErrorHandlerInterface)) {
260 throw new InvalidPageErrorHandlerException('The configured error handler "' . (string)$errorHandlerConfiguration['errorPhpClassFQCN'] . '" for status code ' . $statusCode . ' must implement the PageErrorHandlerInterface.', 1527432330);
261 }
262 return $handler;
263 }
264 throw new PageErrorHandlerNotConfiguredException('No error handler given for the status code "' . $statusCode . '".', 1522495914);
265 }
266
267 /**
268 * Returns the whole configuration for this site
269 *
270 * @return array
271 */
272 public function getConfiguration(): array
273 {
274 return $this->configuration;
275 }
276
277 /**
278 * Returns a single configuration attribute
279 *
280 * @param string $attributeName
281 * @return mixed
282 * @throws \InvalidArgumentException
283 */
284 public function getAttribute(string $attributeName)
285 {
286 if (isset($this->configuration[$attributeName])) {
287 return $this->configuration[$attributeName];
288 }
289 throw new \InvalidArgumentException(
290 'Attribute ' . $attributeName . ' does not exist on site ' . $this->identifier . '.',
291 1522495954
292 );
293 }
294
295 /**
296 * If a site base contains "/" or "www.domain.com", it is ensured that
297 * parse_url() can handle this kind of configuration properly.
298 *
299 * @param string $base
300 * @return string
301 */
302 protected function sanitizeBaseUrl(string $base): string
303 {
304 // no protocol ("//") and the first part is no "/" (path), means that this is a domain like
305 // "www.domain.com/blabla", and we want to ensure that this one then gets a "no-scheme agnostic" part
306 if (!empty($base) && strpos($base, '//') === false && $base{0} !== '/') {
307 // either a scheme is added, or no scheme but with domain, or a path which is not absolute
308 // make the base prefixed with a slash, so it is recognized as path, not as domain
309 // treat as path
310 if (strpos($base, '.') === false) {
311 $base = '/' . $base;
312 } else {
313 // treat as domain name
314 $base = '//' . $base;
315 }
316 }
317 return $base;
318 }
319
320 /**
321 * Shorthand functionality for fetching the language service
322 * @return LanguageService
323 */
324 protected function getLanguageService(): LanguageService
325 {
326 return $GLOBALS['LANG'];
327 }
328 }