‪TYPO3CMS  ‪main
LanguageMenuProcessor.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
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 
19 
20 use Psr\Http\Message\ServerRequestInterface;
29 
47 {
48  protected const ‪LINK_PLACEHOLDER = '###LINKPLACEHOLDER###';
50  protected array ‪$processorConfiguration;
51 
56  protected array ‪$allowedConfigurationKeys = [
57  'if',
58  'if.',
59  'languages',
60  'languages.',
61  'as',
62  'addQueryString',
63  'addQueryString.',
64  ];
65 
71  'languages',
72  'languages.',
73  'as',
74  ];
75 
76  protected array ‪$menuConfig = [
77  'special' => 'language',
78  'addQueryString' => 1,
79  'wrap' => '[|]',
80  ];
81 
82  protected array ‪$menuLevelConfig = [
83  'doNotLinkIt' => '1',
84  'wrapItemAndSub' => '{|}, |*| {|}, |*| {|}',
85  'stdWrap.' => [
86  'cObject' => 'COA',
87  'cObject.' => [
88  '1' => 'LOAD_REGISTER',
89  '1.' => [
90  'languageId.' => [
91  'cObject' => 'TEXT',
92  'cObject.' => [
93  'value.' => [
94  'data' => 'register:languages_HMENU',
95  ],
96  'listNum.' => [
97  'stdWrap.' => [
98  'data' => 'register:count_HMENU_MENUOBJ',
99  'wrap' => '|-1',
100  ],
101  'splitChar' => ',',
102  ],
103  ],
104  ],
105  ],
106  '10' => 'TEXT',
107  '10.' => [
108  'stdWrap.' => [
109  'data' => 'register:languageId',
110  ],
111  'wrap' => '"languageId":|',
112  ],
113  '11' => 'USER',
114  '11.' => [
115  'userFunc' => 'TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor->getFieldAsJson',
116  'language.' => [
117  'data' => 'register:languageId',
118  ],
119  'field' => 'locale',
120  'stdWrap.' => [
121  'wrap' => ',"locale":|',
122  ],
123  ],
124  '20' => 'USER',
125  '20.' => [
126  'userFunc' => 'TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor->getFieldAsJson',
127  'language.' => [
128  'data' => 'register:languageId',
129  ],
130  'field' => 'title',
131  'stdWrap.' => [
132  'wrap' => ',"title":|',
133  ],
134  ],
135  '21' => 'USER',
136  '21.' => [
137  'userFunc' => 'TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor->getFieldAsJson',
138  'language.' => [
139  'data' => 'register:languageId',
140  ],
141  'field' => 'navigationTitle',
142  'stdWrap.' => [
143  'wrap' => ',"navigationTitle":|',
144  ],
145  ],
146  '22' => 'USER',
147  '22.' => [
148  'userFunc' => 'TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor->getFieldAsJson',
149  'language.' => [
150  'data' => 'register:languageId',
151  ],
152  'field' => 'locale:languageCode',
153  'stdWrap.' => [
154  'wrap' => ',"twoLetterIsoCode":|',
155  ],
156  ],
157  '23' => 'USER',
158  '23.' => [
159  'userFunc' => 'TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor->getFieldAsJson',
160  'language.' => [
161  'data' => 'register:languageId',
162  ],
163  'field' => 'hreflang',
164  'stdWrap.' => [
165  'wrap' => ',"hreflang":|',
166  ],
167  ],
168  '24' => 'USER',
169  '24.' => [
170  'userFunc' => 'TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor->getFieldAsJson',
171  'language.' => [
172  'data' => 'register:languageId',
173  ],
174  'field' => 'direction',
175  'stdWrap.' => [
176  'wrap' => ',"direction":|',
177  ],
178  ],
179  '25' => 'USER',
180  '25.' => [
181  'userFunc' => 'TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor->getFieldAsJson',
182  'language.' => [
183  'data' => 'register:languageId',
184  ],
185  'field' => 'flag',
186  'stdWrap.' => [
187  'wrap' => ',"flag":|',
188  ],
189  ],
190  '90' => 'TEXT',
191  '90.' => [
192  'value' => ‪self::LINK_PLACEHOLDER,
193  'wrap' => ',"link":|',
194  ],
195  '91' => 'TEXT',
196  '91.' => [
197  'value' => '0',
198  'wrap' => ',"active":|',
199  ],
200  '92' => 'TEXT',
201  '92.' => [
202  'value' => '0',
203  'wrap' => ',"current":|',
204  ],
205  '93' => 'TEXT',
206  '93.' => [
207  'value' => '1',
208  'wrap' => ',"available":|',
209  ],
210  '99' => 'RESTORE_REGISTER',
211  ],
212  ],
213  ];
214 
215  protected array ‪$menuDefaults = [
216  'as' => 'languagemenu',
217  ];
218 
219  public function ‪__construct(
220  protected readonly ‪ContentDataProcessor $contentDataProcessor,
221  protected readonly ‪ContentObjectFactory $contentObjectFactory,
222  ) {}
223 
229  {
230  $this->cObj = ‪$cObj;
231  }
232 
236  protected function ‪getConfigurationValue(string $key): string
237  {
238  return $this->cObj->stdWrapValue($key, $this->processorConfiguration, $this->menuDefaults[$key]);
239  }
240 
241  protected function ‪getRequest(): ServerRequestInterface
242  {
243  return $this->cObj->getRequest();
244  }
245 
249  protected function ‪getCurrentSite(): ‪Site
250  {
251  return $this->‪getRequest()->getAttribute('site');
252  }
253 
259  protected function ‪jsonEncode($value): string
260  {
261  return json_encode($value, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
262  }
263 
267  protected function ‪validateConfiguration()
268  {
269  $invalidArguments = [];
270  foreach ($this->processorConfiguration as $key => $value) {
271  if (!in_array($key, $this->allowedConfigurationKeys)) {
272  $invalidArguments[str_replace('.', '', $key)] = $key;
273  }
274  }
275  if (!empty($invalidArguments)) {
276  throw new \InvalidArgumentException('LanguageMenuProcessor configuration contains invalid arguments: ' . implode(', ', $invalidArguments), 1522959188);
277  }
278  }
279 
283  protected function ‪prepareConfiguration(): void
284  {
285  $this->menuConfig = array_merge($this->menuConfig, $this->processorConfiguration);
286 
287  // Process languages
288  $this->menuConfig['special.']['value'] = $this->cObj->stdWrapValue('languages', $this->menuConfig, 'auto');
289 
290  // Filter configuration
291  foreach ($this->menuConfig as $key => $value) {
292  if (in_array($key, $this->removeConfigurationKeysForHmenu, true)) {
293  unset($this->menuConfig[$key]);
294  }
295  }
296 
298  $this->‪getRequest()->getAttribute('frontend.page.information')->getId(),
299  (array)‪$GLOBALS['TYPO3_CONF_VARS']['FE']['additionalCanonicalizedUrlParameters']
300  );
301 
302  $this->menuConfig['addQueryString.']['exclude'] = implode(
303  ',',
304  array_merge(
305  ‪GeneralUtility::trimExplode(',', $this->menuConfig['addQueryString.']['exclude'] ?? '', true),
306  $paramsToExclude
307  )
308  );
309  }
310 
314  protected function ‪buildConfiguration(): void
315  {
316  $this->menuConfig['1'] = 'TMENU';
317  $this->menuConfig['1.']['IProcFunc'] = LanguageMenuProcessor::class . '->replacePlaceholderInRenderedMenuItem';
318  $this->menuConfig['1.']['NO'] = '1';
319  $this->menuConfig['1.']['NO.'] = ‪$this->menuLevelConfig;
320  $this->menuConfig['1.']['ACT'] = $this->menuConfig['1.']['NO'];
321  $this->menuConfig['1.']['ACT.'] = $this->menuConfig['1.']['NO.'];
322  $this->menuConfig['1.']['ACT.']['stdWrap.']['cObject.']['91.']['value'] = '1';
323  $this->menuConfig['1.']['CUR'] = $this->menuConfig['1.']['ACT'];
324  $this->menuConfig['1.']['CUR.'] = $this->menuConfig['1.']['ACT.'];
325  $this->menuConfig['1.']['CUR.']['stdWrap.']['cObject.']['92.']['value'] = '1';
326  $this->menuConfig['1.']['USERDEF1'] = $this->menuConfig['1.']['NO'];
327  $this->menuConfig['1.']['USERDEF1.'] = $this->menuConfig['1.']['NO.'];
328  $this->menuConfig['1.']['USERDEF1.']['stdWrap.']['cObject.']['93.']['value'] = '0';
329  $this->menuConfig['1.']['USERDEF2'] = $this->menuConfig['1.']['ACT'];
330  $this->menuConfig['1.']['USERDEF2.'] = $this->menuConfig['1.']['ACT.'];
331  $this->menuConfig['1.']['USERDEF2.']['stdWrap.']['cObject.']['93.']['value'] = '0';
332  }
333 
337  protected function ‪validateAndBuildConfiguration(): void
338  {
339  // Validate Configuration
340  $this->‪validateConfiguration();
341 
342  // Build Configuration
343  $this->‪prepareConfiguration();
344  $this->‪buildConfiguration();
345  }
346 
354  public function ‪process(‪ContentObjectRenderer ‪$cObj, array $contentObjectConfiguration, array ‪$processorConfiguration, array $processedData): array
355  {
356  $this->cObj = ‪$cObj;
357  $this->processorConfiguration = ‪$processorConfiguration;
358 
359  // Validate and Build Configuration
361 
362  // Process Configuration
363  $menuContentObject = $this->contentObjectFactory->getContentObject('HMENU', ‪$cObj->‪getRequest(), ‪$cObj);
364  $renderedMenu = $menuContentObject?->render($this->menuConfig);
365  if (!$renderedMenu) {
366  return $processedData;
367  }
368 
369  // Process menu
370  $menu = json_decode($renderedMenu, true);
371  $processedMenu = [];
372  if (is_iterable($menu)) {
373  foreach ($menu as $key => $language) {
374  $processedMenu[$key] = $language;
375  }
376  }
377 
378  // Return processed data
379  $processedData[$this->‪getConfigurationValue('as')] = $processedMenu;
380  return $processedData;
381  }
382 
386  public function ‪replacePlaceholderInRenderedMenuItem(array $menuItem): array
387  {
388  $link = $this->‪jsonEncode($menuItem['linkHREF'] instanceof ‪LinkResultInterface ? $menuItem['linkHREF']->getUrl() : '');
389 
390  $menuItem['parts']['title'] = str_replace(self::LINK_PLACEHOLDER, $link, $menuItem['parts']['title']);
391 
392  return $menuItem;
393  }
394 
404  public function ‪getFieldAsJson(string $content, array $conf): string
405  {
406  // Support of stdWrap for parameters
407  if (isset($conf['language.'])) {
408  $conf['language'] = $this->cObj->stdWrapValue('language', $conf);
409  unset($conf['language.']);
410  }
411  if (isset($conf['field.'])) {
412  $conf['field'] = $this->cObj->stdWrapValue('field', $conf);
413  unset($conf['field.']);
414  }
415 
416  // Check required fields
417  if ($conf['language'] === '') {
418  throw new \InvalidArgumentException('Argument \'language\' must be supplied.', 1522959186);
419  }
420  if ($conf['field'] === '') {
421  throw new \InvalidArgumentException('Argument \'field\' must be supplied.', 1522959187);
422  }
423  $fieldValue = $conf['field'];
424 
425  // Get and check current site
426  $site = $this->‪getCurrentSite();
427 
428  // Throws InvalidArgumentException in case language is not found which is fine
429  $languageObject = $site->getLanguageById((int)$conf['language']);
430  if ($languageObject->enabled()) {
431  $language = $languageObject->toArray();
432  // Harmonizing the namings from the site configuration value with the TypoScript setting
433  $language['flag'] = $language['flagIdentifier'];
434  } else {
435  return $this->‪jsonEncode(null);
436  }
437 
438  if (str_starts_with($fieldValue, 'locale:')) {
439  $keyParts = explode(':', $fieldValue, 2);
440  $localeObject = $languageObject->getLocale();
441  switch ($keyParts[1] ?? '') {
442  case 'languageCode':
443  $contents = $localeObject->getLanguageCode();
444  break;
445  case 'countryCode':
446  $contents = $localeObject->getCountryCode();
447  break;
448  case 'full':
449  default:
450  $contents = $localeObject->getName();
451  }
452  } elseif ($fieldValue === 'direction') {
453  $contents = $languageObject->getLocale()->isRightToLeftLanguageDirection() ? 'rtl' : 'ltr';
454  } elseif (isset($language[$fieldValue])) {
455  $contents = $language[$fieldValue];
456  } else {
457  // Check field for return exists
458  throw new \InvalidArgumentException('Invalid value \'' . $fieldValue . '\' for argument \'field\' supplied.', 1524063160);
459  }
460 
461  return $this->‪jsonEncode($contents);
462  }
463 }
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\jsonEncode
‪jsonEncode($value)
Definition: LanguageMenuProcessor.php:259
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\setContentObjectRenderer
‪setContentObjectRenderer(ContentObjectRenderer $cObj)
Definition: LanguageMenuProcessor.php:228
‪TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor
Definition: ContentDataProcessor.php:27
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\prepareConfiguration
‪prepareConfiguration()
Definition: LanguageMenuProcessor.php:283
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\getFieldAsJson
‪string getFieldAsJson(string $content, array $conf)
Definition: LanguageMenuProcessor.php:404
‪TYPO3\CMS\Frontend\DataProcessing
Definition: CommaSeparatedValueProcessor.php:16
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\$menuConfig
‪array $menuConfig
Definition: LanguageMenuProcessor.php:76
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\replacePlaceholderInRenderedMenuItem
‪replacePlaceholderInRenderedMenuItem(array $menuItem)
Definition: LanguageMenuProcessor.php:386
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\$removeConfigurationKeysForHmenu
‪array $removeConfigurationKeysForHmenu
Definition: LanguageMenuProcessor.php:70
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor
Definition: LanguageMenuProcessor.php:47
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:42
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\getCurrentSite
‪getCurrentSite()
Definition: LanguageMenuProcessor.php:249
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\$menuLevelConfig
‪array $menuLevelConfig
Definition: LanguageMenuProcessor.php:82
‪TYPO3\CMS\Frontend\Utility\CanonicalizationUtility\getParamsToExcludeForCanonicalizedUrl
‪static getParamsToExcludeForCanonicalizedUrl(int $pageId, array $additionalCanonicalizedUrlParameters=[])
Definition: CanonicalizationUtility.php:40
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\getRequest
‪getRequest()
Definition: LanguageMenuProcessor.php:241
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\getConfigurationValue
‪getConfigurationValue(string $key)
Definition: LanguageMenuProcessor.php:236
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\$cObj
‪ContentObjectRenderer $cObj
Definition: LanguageMenuProcessor.php:49
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getRequest
‪getRequest()
Definition: ContentObjectRenderer.php:5430
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\$menuDefaults
‪array $menuDefaults
Definition: LanguageMenuProcessor.php:215
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\buildConfiguration
‪buildConfiguration()
Definition: LanguageMenuProcessor.php:314
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectFactory
Definition: ContentObjectFactory.php:29
‪TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface
Definition: DataProcessorInterface.php:23
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\validateAndBuildConfiguration
‪validateAndBuildConfiguration()
Definition: LanguageMenuProcessor.php:337
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\LINK_PLACEHOLDER
‪const LINK_PLACEHOLDER
Definition: LanguageMenuProcessor.php:48
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\validateConfiguration
‪validateConfiguration()
Definition: LanguageMenuProcessor.php:267
‪TYPO3\CMS\Frontend\Utility\CanonicalizationUtility
Definition: CanonicalizationUtility.php:26
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\$allowedConfigurationKeys
‪array $allowedConfigurationKeys
Definition: LanguageMenuProcessor.php:56
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\__construct
‪__construct(protected readonly ContentDataProcessor $contentDataProcessor, protected readonly ContentObjectFactory $contentObjectFactory,)
Definition: LanguageMenuProcessor.php:219
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\$processorConfiguration
‪array $processorConfiguration
Definition: LanguageMenuProcessor.php:50
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822
‪TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor\process
‪array process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData)
Definition: LanguageMenuProcessor.php:354