‪TYPO3CMS  ‪main
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 
24 
62 {
63  public const ‪LINK_PLACEHOLDER = '###LINKPLACEHOLDER###';
64  public const ‪TARGET_PLACEHOLDER = '###TARGETPLACEHOLDER###';
65 
70 
77 
85  'cache',
86  'cache.',
87  'cache_period',
88  'entryLevel',
89  'entryLevel.',
90  'special',
91  'special.',
92  'minItems',
93  'minItems.',
94  'maxItems',
95  'maxItems.',
96  'begin',
97  'begin.',
98  'alternativeSortingField',
99  'alternativeSortingField.',
100  'showAccessRestrictedPages',
101  'showAccessRestrictedPages.',
102  'excludeUidList',
103  'excludeUidList.',
104  'excludeDoktypes',
105  'includeNotInMenu',
106  'includeNotInMenu.',
107  'alwaysActivePIDlist',
108  'alwaysActivePIDlist.',
109  'protectLvar',
110  'addQueryString',
111  'addQueryString.',
112  'if',
113  'if.',
114  'levels',
115  'levels.',
116  'expandAll',
117  'expandAll.',
118  'includeSpacer',
119  'includeSpacer.',
120  'as',
121  'titleField',
122  'titleField.',
123  'dataProcessing',
124  'dataProcessing.',
125  ];
126 
134  'levels',
135  'levels.',
136  'expandAll',
137  'expandAll.',
138  'includeSpacer',
139  'includeSpacer.',
140  'as',
141  'titleField',
142  'titleField.',
143  'dataProcessing',
144  'dataProcessing.',
145  ];
146 
150  protected ‪$menuConfig = [
151  'wrap' => '[|]',
152  ];
153 
157  protected ‪$menuLevelConfig = [
158  'doNotLinkIt' => '1',
159  'wrapItemAndSub' => '{|}, |*| {|}, |*| {|}',
160  'stdWrap.' => [
161  'cObject' => 'COA',
162  'cObject.' => [
163  '10' => 'USER',
164  '10.' => [
165  'userFunc' => 'TYPO3\CMS\Frontend\DataProcessing\MenuProcessor->getDataAsJson',
166  'stdWrap.' => [
167  'wrap' => '"data":|',
168  ],
169  ],
170  '20' => 'TEXT',
171  '20.' => [
172  'field' => 'nav_title // title',
173  'trim' => '1',
174  'wrap' => ',"title":|',
175  'preUserFunc' => 'TYPO3\CMS\Frontend\DataProcessing\MenuProcessor->jsonEncodeUserFunc',
176  ],
177  '21' => 'TEXT',
178  '21.' => [
179  'value' => ‪self::LINK_PLACEHOLDER,
180  'wrap' => ',"link":|',
181  ],
182  '22' => 'TEXT',
183  '22.' => [
184  'value' => ‪self::TARGET_PLACEHOLDER,
185  'wrap' => ',"target":|',
186  ],
187  '30' => 'TEXT',
188  '30.' => [
189  'value' => '0',
190  'wrap' => ',"active":|',
191  ],
192  '40' => 'TEXT',
193  '40.' => [
194  'value' => '0',
195  'wrap' => ',"current":|',
196  ],
197  '50' => 'TEXT',
198  '50.' => [
199  'value' => '0',
200  'wrap' => ',"spacer":|',
201  ],
202  '60' => 'TEXT',
203  '60.' => [
204  'value' => '0',
205  'wrap' => ',"hasSubpages":|',
206  ],
207  ],
208  ],
209  ];
210 
214  public ‪$menuDefaults = [
215  'levels' => 1,
216  'expandAll' => 1,
217  'includeSpacer' => 0,
218  'as' => 'menu',
219  'titleField' => 'nav_title // title',
220  ];
221 
225  protected ‪$menuLevels;
226 
230  protected ‪$menuExpandAll;
231 
235  protected ‪$menuIncludeSpacer;
236 
241 
246 
250  protected ‪$menuTargetVariableName;
251 
252  public function ‪__construct(
253  protected ‪ContentDataProcessor $contentDataProcessor,
254  protected ‪ContentObjectFactory $contentObjectFactory
255  ) {}
256 
262  {
263  $this->cObj = ‪$cObj;
264  }
265 
272  protected function ‪getConfigurationValue($key)
273  {
274  return $this->cObj->stdWrapValue($key, $this->processorConfiguration, $this->menuDefaults[$key] ?? '');
275  }
276 
282  public function ‪validateConfiguration()
283  {
284  $invalidArguments = [];
285  foreach ($this->processorConfiguration as $key => $value) {
286  if (!in_array($key, $this->allowedConfigurationKeys)) {
287  $invalidArguments[str_replace('.', '', $key)] = $key;
288  }
289  }
290  if (!empty($invalidArguments)) {
291  throw new \InvalidArgumentException('MenuProcessor Configuration contains invalid Arguments: ' . implode(', ', $invalidArguments), 1478806566);
292  }
293  }
294 
298  public function ‪prepareConfiguration()
299  {
300  $this->menuConfig += ‪$this->processorConfiguration;
301  // Filter configuration
302  foreach ($this->menuConfig as $key => $value) {
303  if (in_array($key, $this->removeConfigurationKeysForHmenu)) {
304  unset($this->menuConfig[$key]);
305  }
306  }
307  // Process special value
308  if (isset($this->menuConfig['special.']['value.'])) {
309  $this->menuConfig['special.']['value'] = $this->cObj->stdWrapValue('value', $this->menuConfig['special.']);
310  unset($this->menuConfig['special.']['value.']);
311  }
312  }
313 
317  public function ‪prepareLevelConfiguration()
318  {
319  $this->menuLevelConfig['stdWrap.']['cObject.'] = array_replace_recursive(
320  $this->menuLevelConfig['stdWrap.']['cObject.'],
321  [
322  '20.' => [
323  'field' => $this->menuTitleField,
324  ],
325  ]
326  );
327  }
328 
332  public function ‪prepareLevelLanguageConfiguration()
333  {
334  if (($this->menuConfig['special'] ?? '') === 'language') {
335  $languageUids = $this->menuConfig['special.']['value'];
336  if ($this->menuConfig['special.']['value'] === 'auto') {
337  $site = $this->cObj->getRequest()->getAttribute('site');
338  $languageUids = implode(',', array_keys($site->getLanguages()));
339  }
340  $this->menuLevelConfig['stdWrap.']['cObject.'] = array_replace_recursive(
341  $this->menuLevelConfig['stdWrap.']['cObject.'],
342  [
343  '60' => 'TEXT',
344  '60.' => [
345  'value' => '1',
346  'wrap' => ',"available":|',
347  ],
348  '70' => 'TEXT',
349  '70.' => [
350  'value' => $languageUids,
351  'listNum.' => [
352  'stdWrap.' => [
353  'data' => 'register:count_HMENU_MENUOBJ',
354  'wrap' => '|-1',
355  ],
356  'splitChar' => ',',
357  ],
358  'wrap' => ',"languageUid":"|"',
359  ],
360  ]
361  );
362  }
363  }
364 
368  public function ‪buildConfiguration()
369  {
370  for ($i = 1; $i <= ‪$this->menuLevels; $i++) {
371  $this->menuConfig[$i] = 'TMENU';
372  $this->menuConfig[$i . '.']['IProcFunc'] = 'TYPO3\CMS\Frontend\DataProcessing\MenuProcessor->replacePlaceholderInRenderedMenuItem';
373  if ($i > 1) {
374  $this->menuConfig[$i . '.']['stdWrap.']['wrap'] = ',"children": [|]';
375  }
376  if (array_key_exists('showAccessRestrictedPages', $this->menuConfig)) {
377  $this->menuConfig[$i . '.']['showAccessRestrictedPages'] = $this->menuConfig['showAccessRestrictedPages'];
378  if (array_key_exists('showAccessRestrictedPages.', $this->menuConfig)
379  && is_array($this->menuConfig['showAccessRestrictedPages.'])) {
380  $this->menuConfig[$i . '.']['showAccessRestrictedPages.'] = $this->menuConfig['showAccessRestrictedPages.'];
381  }
382  }
383  $this->menuConfig[$i . '.']['expAll'] = ‪$this->menuExpandAll;
384  $this->menuConfig[$i . '.']['alternativeSortingField'] = ‪$this->menuAlternativeSortingField;
385  $this->menuConfig[$i . '.']['NO'] = '1';
386  $this->menuConfig[$i . '.']['NO.'] = ‪$this->menuLevelConfig;
387  if ($this->menuIncludeSpacer) {
388  $this->menuConfig[$i . '.']['SPC'] = '1';
389  $this->menuConfig[$i . '.']['SPC.'] = $this->menuConfig[$i . '.']['NO.'];
390  $this->menuConfig[$i . '.']['SPC.']['stdWrap.']['cObject.']['50.']['value'] = '1';
391  }
392  $this->menuConfig[$i . '.']['IFSUB'] = '1';
393  $this->menuConfig[$i . '.']['IFSUB.'] = $this->menuConfig[$i . '.']['NO.'];
394  $this->menuConfig[$i . '.']['IFSUB.']['stdWrap.']['cObject.']['60.']['value'] = '1';
395  $this->menuConfig[$i . '.']['ACT'] = '1';
396  $this->menuConfig[$i . '.']['ACT.'] = $this->menuConfig[$i . '.']['NO.'];
397  $this->menuConfig[$i . '.']['ACT.']['stdWrap.']['cObject.']['30.']['value'] = '1';
398  $this->menuConfig[$i . '.']['ACTIFSUB'] = '1';
399  $this->menuConfig[$i . '.']['ACTIFSUB.'] = $this->menuConfig[$i . '.']['ACT.'];
400  $this->menuConfig[$i . '.']['ACTIFSUB.']['stdWrap.']['cObject.']['60.']['value'] = '1';
401  $this->menuConfig[$i . '.']['CUR'] = '1';
402  $this->menuConfig[$i . '.']['CUR.'] = $this->menuConfig[$i . '.']['ACT.'];
403  $this->menuConfig[$i . '.']['CUR.']['stdWrap.']['cObject.']['40.']['value'] = '1';
404  $this->menuConfig[$i . '.']['CURIFSUB'] = '1';
405  $this->menuConfig[$i . '.']['CURIFSUB.'] = $this->menuConfig[$i . '.']['CUR.'];
406  $this->menuConfig[$i . '.']['CURIFSUB.']['stdWrap.']['cObject.']['60.']['value'] = '1';
407  if (($this->menuConfig['special'] ?? '') === 'language') {
408  $this->menuConfig[$i . '.']['USERDEF1'] = $this->menuConfig[$i . '.']['NO'];
409  $this->menuConfig[$i . '.']['USERDEF1.'] = $this->menuConfig[$i . '.']['NO.'];
410  $this->menuConfig[$i . '.']['USERDEF1.']['stdWrap.']['cObject.']['60.']['value'] = '0';
411  $this->menuConfig[$i . '.']['USERDEF2'] = $this->menuConfig[$i . '.']['ACT'];
412  $this->menuConfig[$i . '.']['USERDEF2.'] = $this->menuConfig[$i . '.']['ACT.'];
413  $this->menuConfig[$i . '.']['USERDEF2.']['stdWrap.']['cObject.']['60.']['value'] = '0';
414  }
415  }
416  }
417 
425  public function ‪process(ContentObjectRenderer ‪$cObj, array $contentObjectConfiguration, array ‪$processorConfiguration, array $processedData)
426  {
427  $this->cObj = ‪$cObj;
428  $this->processorConfiguration = ‪$processorConfiguration;
429 
430  // Get Configuration
431  $this->menuLevels = (int)$this->‪getConfigurationValue('levels') ?: 1;
432  $this->menuExpandAll = (int)$this->‪getConfigurationValue('expandAll');
433  $this->menuIncludeSpacer = (int)$this->‪getConfigurationValue('includeSpacer');
434  $this->menuTargetVariableName = $this->‪getConfigurationValue('as');
435  $this->menuTitleField = $this->‪getConfigurationValue('titleField');
436  $this->menuAlternativeSortingField = $this->‪getConfigurationValue('alternativeSortingField');
437 
438  // Validate Configuration
439  $this->‪validateConfiguration();
440 
441  // Build Configuration
442  $this->‪prepareConfiguration();
445  $this->‪buildConfiguration();
446 
447  // Process Configuration
448  $menuContentObject = $this->contentObjectFactory->getContentObject('HMENU', ‪$cObj->‪getRequest(), ‪$cObj);
449  $renderedMenu = $menuContentObject?->render($this->menuConfig);
450  if (!$renderedMenu) {
451  return $processedData;
452  }
453 
454  // Process menu
455  $menu = json_decode($renderedMenu, true);
456  $processedMenu = [];
457  if (is_iterable($menu)) {
458  foreach ($menu as $key => $page) {
459  $processedMenu[$key] = $this->‪processAdditionalDataProcessors($page, ‪$processorConfiguration);
460  }
461  }
462 
463  // Return processed data
464  $processedData[‪$this->menuTargetVariableName] = $processedMenu;
465  return $processedData;
466  }
467 
476  {
477  if (is_array($page['children'] ?? false)) {
478  foreach ($page['children'] as $key => $item) {
479  $page['children'][$key] = $this->‪processAdditionalDataProcessors($item, ‪$processorConfiguration);
480  }
481  }
482  $request = $this->cObj->getRequest();
483  $recordContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class);
484  $recordContentObjectRenderer->setRequest($request);
485  $recordContentObjectRenderer->start($page['data'] ?? [], 'pages');
486 
487  return $this->contentDataProcessor->process($recordContentObjectRenderer, ‪$processorConfiguration, $page);
488  }
489 
495  public function ‪getDataAsJson()
496  {
497  return $this->‪jsonEncode($this->cObj->data);
498  }
499 
507  public function ‪jsonEncodeUserFunc($content, $conf)
508  {
509  $content = $this->‪jsonEncode($content);
510  return $content;
511  }
512 
519  public function ‪jsonEncode($value)
520  {
521  return json_encode($value, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
522  }
523 
531  public function ‪replacePlaceholderInRenderedMenuItem($menuItem, $conf)
532  {
533  $link = $this->‪jsonEncode($menuItem['linkHREF'] instanceof ‪LinkResultInterface ? $menuItem['linkHREF']->getUrl() : '');
534  $target = $this->‪jsonEncode($menuItem['linkHREF'] instanceof ‪LinkResultInterface ? $menuItem['linkHREF']->getTarget() : '');
535 
536  $menuItem['parts']['title'] = str_replace(self::LINK_PLACEHOLDER, $link, $menuItem['parts']['title']);
537  $menuItem['parts']['title'] = str_replace(self::TARGET_PLACEHOLDER, $target, $menuItem['parts']['title']);
538 
539  return $menuItem;
540  }
541 }
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$removeConfigurationKeysForHmenu
‪array $removeConfigurationKeysForHmenu
Definition: MenuProcessor.php:130
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuExpandAll
‪int $menuExpandAll
Definition: MenuProcessor.php:222
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$cObj
‪ContentObjectRenderer $cObj
Definition: MenuProcessor.php:69
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\setContentObjectRenderer
‪setContentObjectRenderer(ContentObjectRenderer $cObj)
Definition: MenuProcessor.php:249
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\jsonEncode
‪string jsonEncode($value)
Definition: MenuProcessor.php:507
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\validateConfiguration
‪validateConfiguration()
Definition: MenuProcessor.php:270
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\prepareLevelConfiguration
‪prepareLevelConfiguration()
Definition: MenuProcessor.php:305
‪TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor
Definition: ContentDataProcessor.php:27
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuLevelConfig
‪array $menuLevelConfig
Definition: MenuProcessor.php:152
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\buildConfiguration
‪buildConfiguration()
Definition: MenuProcessor.php:356
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$allowedConfigurationKeys
‪array $allowedConfigurationKeys
Definition: MenuProcessor.php:82
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\prepareLevelLanguageConfiguration
‪prepareLevelLanguageConfiguration()
Definition: MenuProcessor.php:320
‪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:495
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuConfig
‪array $menuConfig
Definition: MenuProcessor.php:146
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
Definition: MenuProcessor.php:62
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuTargetVariableName
‪string $menuTargetVariableName
Definition: MenuProcessor.php:238
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\prepareConfiguration
‪prepareConfiguration()
Definition: MenuProcessor.php:286
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$processorConfiguration
‪array $processorConfiguration
Definition: MenuProcessor.php:75
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuLevels
‪int $menuLevels
Definition: MenuProcessor.php:218
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\getDataAsJson
‪string getDataAsJson()
Definition: MenuProcessor.php:483
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\getConfigurationValue
‪string getConfigurationValue($key)
Definition: MenuProcessor.php:260
‪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:226
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getRequest
‪getRequest()
Definition: ContentObjectRenderer.php:5430
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuAlternativeSortingField
‪string $menuAlternativeSortingField
Definition: MenuProcessor.php:234
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectFactory
Definition: ContentObjectFactory.php:29
‪TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface
Definition: DataProcessorInterface.php:23
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuDefaults
‪array $menuDefaults
Definition: MenuProcessor.php:208
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\process
‪array process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData)
Definition: MenuProcessor.php:413
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\$menuTitleField
‪string $menuTitleField
Definition: MenuProcessor.php:230
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\replacePlaceholderInRenderedMenuItem
‪array replacePlaceholderInRenderedMenuItem($menuItem, $conf)
Definition: MenuProcessor.php:519
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\__construct
‪__construct(protected ContentDataProcessor $contentDataProcessor, protected ContentObjectFactory $contentObjectFactory)
Definition: MenuProcessor.php:240
‪TYPO3\CMS\Frontend\DataProcessing\MenuProcessor\processAdditionalDataProcessors
‪array processAdditionalDataProcessors($page, $processorConfiguration)
Definition: MenuProcessor.php:463