‪TYPO3CMS  11.5
MenuProcessor.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
21 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
24 
62 {
63  public const ‪LINK_PLACEHOLDER = '###LINKPLACEHOLDER###';
64  public const ‪TARGET_PLACEHOLDER = '###TARGETPLACEHOLDER###';
65 
71  protected ?ContentObjectRenderer ‪$cObj = null;
72 
79 
87  'cache',
88  'cache.',
89  'cache_period',
90  'entryLevel',
91  'entryLevel.',
92  'special',
93  'special.',
94  'minItems',
95  'minItems.',
96  'maxItems',
97  'maxItems.',
98  'begin',
99  'begin.',
100  'alternativeSortingField',
101  'alternativeSortingField.',
102  'showAccessRestrictedPages',
103  'showAccessRestrictedPages.',
104  'excludeUidList',
105  'excludeUidList.',
106  'excludeDoktypes',
107  'includeNotInMenu',
108  'includeNotInMenu.',
109  'alwaysActivePIDlist',
110  'alwaysActivePIDlist.',
111  'protectLvar',
112  'addQueryString',
113  'addQueryString.',
114  'if',
115  'if.',
116  'levels',
117  'levels.',
118  'expandAll',
119  'expandAll.',
120  'includeSpacer',
121  'includeSpacer.',
122  'as',
123  'titleField',
124  'titleField.',
125  'dataProcessing',
126  'dataProcessing.',
127  ];
128 
136  'levels',
137  'levels.',
138  'expandAll',
139  'expandAll.',
140  'includeSpacer',
141  'includeSpacer.',
142  'as',
143  'titleField',
144  'titleField.',
145  'dataProcessing',
146  'dataProcessing.',
147  ];
148 
152  protected ‪$menuConfig = [
153  'wrap' => '[|]',
154  ];
155 
159  protected ‪$menuLevelConfig = [
160  'doNotLinkIt' => '1',
161  'wrapItemAndSub' => '{|}, |*| {|}, |*| {|}',
162  'stdWrap.' => [
163  'cObject' => 'COA',
164  'cObject.' => [
165  '10' => 'USER',
166  '10.' => [
167  'userFunc' => 'TYPO3\CMS\Frontend\DataProcessing\MenuProcessor->getDataAsJson',
168  'stdWrap.' => [
169  'wrap' => '"data":|',
170  ],
171  ],
172  '20' => 'TEXT',
173  '20.' => [
174  'field' => 'nav_title // title',
175  'trim' => '1',
176  'wrap' => ',"title":|',
177  'preUserFunc' => 'TYPO3\CMS\Frontend\DataProcessing\MenuProcessor->jsonEncodeUserFunc',
178  ],
179  '21' => 'TEXT',
180  '21.' => [
181  'value' => ‪self::LINK_PLACEHOLDER,
182  'wrap' => ',"link":|',
183  ],
184  '22' => 'TEXT',
185  '22.' => [
186  'value' => ‪self::TARGET_PLACEHOLDER,
187  'wrap' => ',"target":|',
188  ],
189  '30' => 'TEXT',
190  '30.' => [
191  'value' => '0',
192  'wrap' => ',"active":|',
193  ],
194  '40' => 'TEXT',
195  '40.' => [
196  'value' => '0',
197  'wrap' => ',"current":|',
198  ],
199  '50' => 'TEXT',
200  '50.' => [
201  'value' => '0',
202  'wrap' => ',"spacer":|',
203  ],
204  '60' => 'TEXT',
205  '60.' => [
206  'value' => '0',
207  'wrap' => ',"hasSubpages":|',
208  ],
209  ],
210  ],
211  ];
212 
216  public ‪$menuDefaults = [
217  'levels' => 1,
218  'expandAll' => 1,
219  'includeSpacer' => 0,
220  'as' => 'menu',
221  'titleField' => 'nav_title // title',
222  ];
223 
227  protected ‪$menuLevels;
228 
232  protected ‪$menuExpandAll;
233 
237  protected ‪$menuIncludeSpacer;
238 
242  protected ‪$menuTitleField;
243 
248 
252  protected ‪$menuTargetVariableName;
253 
257  protected ‪$contentDataProcessor;
258 
262  public function ‪__construct()
263  {
264  $this->contentDataProcessor = GeneralUtility::makeInstance(ContentDataProcessor::class);
265  }
266 
273  public function ‪setContentObjectRenderer(ContentObjectRenderer ‪$cObj): void
274  {
275  $this->cObj = ‪$cObj;
276  }
277 
284  protected function ‪getConfigurationValue($key)
285  {
286  return $this->cObj->stdWrapValue($key, $this->processorConfiguration, $this->menuDefaults[$key] ?? '');
287  }
288 
294  public function ‪validateConfiguration()
295  {
296  $invalidArguments = [];
297  foreach ($this->processorConfiguration as $key => $value) {
298  if (!in_array($key, $this->allowedConfigurationKeys)) {
299  $invalidArguments[str_replace('.', '', $key)] = $key;
300  }
301  }
302  if (!empty($invalidArguments)) {
303  throw new \InvalidArgumentException('MenuProcessor Configuration contains invalid Arguments: ' . implode(', ', $invalidArguments), 1478806566);
304  }
305  }
306 
310  public function ‪prepareConfiguration()
311  {
312  $this->menuConfig += ‪$this->processorConfiguration;
313  // Filter configuration
314  foreach ($this->menuConfig as $key => $value) {
315  if (in_array($key, $this->removeConfigurationKeysForHmenu)) {
316  unset($this->menuConfig[$key]);
317  }
318  }
319  // Process special value
320  if (isset($this->menuConfig['special.']['value.'])) {
321  $this->menuConfig['special.']['value'] = $this->cObj->stdWrapValue('value', $this->menuConfig['special.']);
322  unset($this->menuConfig['special.']['value.']);
323  }
324  }
325 
329  public function ‪prepareLevelConfiguration()
330  {
331  $this->menuLevelConfig['stdWrap.']['cObject.'] = array_replace_recursive(
332  $this->menuLevelConfig['stdWrap.']['cObject.'],
333  [
334  '20.' => [
335  'field' => $this->menuTitleField,
336  ],
337  ]
338  );
339  }
340 
344  public function ‪prepareLevelLanguageConfiguration()
345  {
346  if (($this->menuConfig['special'] ?? '') === 'language') {
347  $languageUids = $this->menuConfig['special.']['value'];
348  if ($this->menuConfig['special.']['value'] === 'auto') {
349  $site = $this->‪getCurrentSite();
350  $languageUids = implode(',', array_keys($site->getLanguages()));
351  }
352  $this->menuLevelConfig['stdWrap.']['cObject.'] = array_replace_recursive(
353  $this->menuLevelConfig['stdWrap.']['cObject.'],
354  [
355  '60' => 'TEXT',
356  '60.' => [
357  'value' => '1',
358  'wrap' => ',"available":|',
359  ],
360  '70' => 'TEXT',
361  '70.' => [
362  'value' => $languageUids,
363  'listNum.' => [
364  'stdWrap.' => [
365  'data' => 'register:count_HMENU_MENUOBJ',
366  'wrap' => '|-1',
367  ],
368  'splitChar' => ',',
369  ],
370  'wrap' => ',"languageUid":"|"',
371  ],
372  ]
373  );
374  }
375  }
376 
380  public function ‪buildConfiguration()
381  {
382  for ($i = 1; $i <= ‪$this->menuLevels; $i++) {
383  $this->menuConfig[$i] = 'TMENU';
384  $this->menuConfig[$i . '.']['IProcFunc'] = 'TYPO3\CMS\Frontend\DataProcessing\MenuProcessor->replacePlaceholderInRenderedMenuItem';
385  if ($i > 1) {
386  $this->menuConfig[$i . '.']['stdWrap.']['wrap'] = ',"children": [|]';
387  }
388  if (array_key_exists('showAccessRestrictedPages', $this->menuConfig)) {
389  $this->menuConfig[$i . '.']['showAccessRestrictedPages'] = $this->menuConfig['showAccessRestrictedPages'];
390  if (array_key_exists('showAccessRestrictedPages.', $this->menuConfig)
391  && is_array($this->menuConfig['showAccessRestrictedPages.'])) {
392  $this->menuConfig[$i . '.']['showAccessRestrictedPages.'] = $this->menuConfig['showAccessRestrictedPages.'];
393  }
394  }
395  $this->menuConfig[$i . '.']['expAll'] = ‪$this->menuExpandAll;
396  $this->menuConfig[$i . '.']['alternativeSortingField'] = ‪$this->menuAlternativeSortingField;
397  $this->menuConfig[$i . '.']['NO'] = '1';
398  $this->menuConfig[$i . '.']['NO.'] = ‪$this->menuLevelConfig;
399  if ($this->menuIncludeSpacer) {
400  $this->menuConfig[$i . '.']['SPC'] = '1';
401  $this->menuConfig[$i . '.']['SPC.'] = $this->menuConfig[$i . '.']['NO.'];
402  $this->menuConfig[$i . '.']['SPC.']['stdWrap.']['cObject.']['50.']['value'] = '1';
403  }
404  $this->menuConfig[$i . '.']['IFSUB'] = '1';
405  $this->menuConfig[$i . '.']['IFSUB.'] = $this->menuConfig[$i . '.']['NO.'];
406  $this->menuConfig[$i . '.']['IFSUB.']['stdWrap.']['cObject.']['60.']['value'] = '1';
407  $this->menuConfig[$i . '.']['ACT'] = '1';
408  $this->menuConfig[$i . '.']['ACT.'] = $this->menuConfig[$i . '.']['NO.'];
409  $this->menuConfig[$i . '.']['ACT.']['stdWrap.']['cObject.']['30.']['value'] = '1';
410  $this->menuConfig[$i . '.']['ACTIFSUB'] = '1';
411  $this->menuConfig[$i . '.']['ACTIFSUB.'] = $this->menuConfig[$i . '.']['ACT.'];
412  $this->menuConfig[$i . '.']['ACTIFSUB.']['stdWrap.']['cObject.']['60.']['value'] = '1';
413  $this->menuConfig[$i . '.']['CUR'] = '1';
414  $this->menuConfig[$i . '.']['CUR.'] = $this->menuConfig[$i . '.']['ACT.'];
415  $this->menuConfig[$i . '.']['CUR.']['stdWrap.']['cObject.']['40.']['value'] = '1';
416  $this->menuConfig[$i . '.']['CURIFSUB'] = '1';
417  $this->menuConfig[$i . '.']['CURIFSUB.'] = $this->menuConfig[$i . '.']['CUR.'];
418  $this->menuConfig[$i . '.']['CURIFSUB.']['stdWrap.']['cObject.']['60.']['value'] = '1';
419  if (($this->menuConfig['special'] ?? '') === 'language') {
420  $this->menuConfig[$i . '.']['USERDEF1'] = $this->menuConfig[$i . '.']['NO'];
421  $this->menuConfig[$i . '.']['USERDEF1.'] = $this->menuConfig[$i . '.']['NO.'];
422  $this->menuConfig[$i . '.']['USERDEF1.']['stdWrap.']['cObject.']['60.']['value'] = '0';
423  $this->menuConfig[$i . '.']['USERDEF2'] = $this->menuConfig[$i . '.']['ACT'];
424  $this->menuConfig[$i . '.']['USERDEF2.'] = $this->menuConfig[$i . '.']['ACT.'];
425  $this->menuConfig[$i . '.']['USERDEF2.']['stdWrap.']['cObject.']['60.']['value'] = '0';
426  }
427  }
428  }
429 
437  public function ‪process(ContentObjectRenderer ‪$cObj, array $contentObjectConfiguration, array ‪$processorConfiguration, array $processedData)
438  {
439  $this->cObj = ‪$cObj;
440  $this->processorConfiguration = ‪$processorConfiguration;
441 
442  // Get Configuration
443  $this->menuLevels = (int)$this->‪getConfigurationValue('levels') ?: 1;
444  $this->menuExpandAll = (int)$this->‪getConfigurationValue('expandAll');
445  $this->menuIncludeSpacer = (int)$this->‪getConfigurationValue('includeSpacer');
446  $this->menuTargetVariableName = $this->‪getConfigurationValue('as');
447  $this->menuTitleField = $this->‪getConfigurationValue('titleField');
448  $this->menuAlternativeSortingField = $this->‪getConfigurationValue('alternativeSortingField');
449 
450  // Validate Configuration
451  $this->‪validateConfiguration();
452 
453  // Build Configuration
454  $this->‪prepareConfiguration();
457  $this->‪buildConfiguration();
458 
459  // Process Configuration
460  if (($menuContentObject = ‪$cObj->getContentObject('HMENU')) === null
461  || !($renderedMenu = $menuContentObject->render($this->menuConfig))
462  ) {
463  return $processedData;
464  }
465 
466  // Process menu
467  $menu = json_decode($renderedMenu, true);
468  $processedMenu = [];
469  if (is_iterable($menu)) {
470  foreach ($menu as $key => $page) {
471  $processedMenu[$key] = $this->‪processAdditionalDataProcessors($page, ‪$processorConfiguration);
472  }
473  }
474 
475  // Return processed data
476  $processedData[‪$this->menuTargetVariableName] = $processedMenu;
477  return $processedData;
478  }
479 
488  {
489  if (is_array($page['children'] ?? false)) {
490  foreach ($page['children'] as $key => $item) {
491  $page['children'][$key] = $this->‪processAdditionalDataProcessors($item, ‪$processorConfiguration);
492  }
493  }
494  $request = $this->cObj->getRequest();
495  $recordContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class);
496  $recordContentObjectRenderer->start($page['data'] ?? [], 'pages', $request);
497 
498  return $this->contentDataProcessor->process($recordContentObjectRenderer, ‪$processorConfiguration, $page);
499  }
500 
506  public function ‪getDataAsJson()
507  {
508  return $this->‪jsonEncode($this->cObj->data);
509  }
510 
518  public function ‪jsonEncodeUserFunc($content, $conf)
519  {
520  $content = $this->‪jsonEncode($content);
521  return $content;
522  }
523 
530  public function ‪jsonEncode($value)
531  {
532  return json_encode($value, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
533  }
534 
542  public function ‪replacePlaceholderInRenderedMenuItem($menuItem, $conf)
543  {
544  $link = $this->‪jsonEncode($menuItem['linkHREF']['HREF'] ?? '');
545  $target = $this->‪jsonEncode($menuItem['linkHREF']['TARGET'] ?? '');
546 
547  $menuItem['parts']['title'] = str_replace(self::LINK_PLACEHOLDER, $link, $menuItem['parts']['title']);
548  $menuItem['parts']['title'] = str_replace(self::TARGET_PLACEHOLDER, $target, $menuItem['parts']['title']);
549 
550  return $menuItem;
551  }
552 
558  protected function ‪getCurrentSite(): ‪SiteInterface
559  {
561  }
562 
566  protected function ‪getTypoScriptFrontendController()
567  {
568  return ‪$GLOBALS['TSFE'];
569  }
570 }
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$removeConfigurationKeysForHmenu
‪array $removeConfigurationKeysForHmenu
Definition: MenuProcessor.php:132
‪TYPO3\CMS\Core\Site\Entity\SiteInterface
Definition: SiteInterface.php:26
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuExpandAll
‪int $menuExpandAll
Definition: MenuProcessor.php:224
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\getTypoScriptFrontendController
‪TypoScriptFrontendController getTypoScriptFrontendController()
Definition: MenuProcessor.php:553
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$cObj
‪ContentObjectRenderer $cObj
Definition: MenuProcessor.php:71
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\setContentObjectRenderer
‪setContentObjectRenderer(ContentObjectRenderer $cObj)
Definition: MenuProcessor.php:260
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\jsonEncode
‪string jsonEncode($value)
Definition: MenuProcessor.php:517
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$contentDataProcessor
‪ContentDataProcessor $contentDataProcessor
Definition: MenuProcessor.php:244
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\validateConfiguration
‪validateConfiguration()
Definition: MenuProcessor.php:281
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\prepareLevelConfiguration
‪prepareLevelConfiguration()
Definition: MenuProcessor.php:316
‪TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor
Definition: ContentDataProcessor.php:26
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuLevelConfig
‪array $menuLevelConfig
Definition: MenuProcessor.php:154
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\getSite
‪getSite()
Definition: TypoScriptFrontendController.php:3596
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\buildConfiguration
‪buildConfiguration()
Definition: MenuProcessor.php:367
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$allowedConfigurationKeys
‪array $allowedConfigurationKeys
Definition: MenuProcessor.php:84
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\prepareLevelLanguageConfiguration
‪prepareLevelLanguageConfiguration()
Definition: MenuProcessor.php:331
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\TARGET_PLACEHOLDER
‪const TARGET_PLACEHOLDER
Definition: MenuProcessor.php:64
‪TYPO3\CMS\Frontend\DataProcessing
Definition: CommaSeparatedValueProcessor.php:16
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\jsonEncodeUserFunc
‪string jsonEncodeUserFunc($content, $conf)
Definition: MenuProcessor.php:505
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuConfig
‪array $menuConfig
Definition: MenuProcessor.php:148
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
Definition: MenuProcessor.php:62
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuTargetVariableName
‪string $menuTargetVariableName
Definition: MenuProcessor.php:240
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\prepareConfiguration
‪prepareConfiguration()
Definition: MenuProcessor.php:297
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$processorConfiguration
‪array $processorConfiguration
Definition: MenuProcessor.php:77
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuLevels
‪int $menuLevels
Definition: MenuProcessor.php:220
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\getDataAsJson
‪string getDataAsJson()
Definition: MenuProcessor.php:493
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\getConfigurationValue
‪string getConfigurationValue($key)
Definition: MenuProcessor.php:271
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\LINK_PLACEHOLDER
‪const LINK_PLACEHOLDER
Definition: MenuProcessor.php:63
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuIncludeSpacer
‪int $menuIncludeSpacer
Definition: MenuProcessor.php:228
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuAlternativeSortingField
‪string $menuAlternativeSortingField
Definition: MenuProcessor.php:236
‪TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface
Definition: DataProcessorInterface.php:23
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:104
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuDefaults
‪array $menuDefaults
Definition: MenuProcessor.php:210
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\process
‪array process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData)
Definition: MenuProcessor.php:424
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\__construct
‪__construct()
Definition: MenuProcessor.php:249
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\getCurrentSite
‪SiteInterface getCurrentSite()
Definition: MenuProcessor.php:545
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuTitleField
‪string $menuTitleField
Definition: MenuProcessor.php:232
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\replacePlaceholderInRenderedMenuItem
‪array replacePlaceholderInRenderedMenuItem($menuItem, $conf)
Definition: MenuProcessor.php:529
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\processAdditionalDataProcessors
‪array processAdditionalDataProcessors($page, $processorConfiguration)
Definition: MenuProcessor.php:474