TYPO3 CMS  TYPO3_8-7
MenuProcessor.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
21 
59 {
60  const LINK_PLACEHOLDER = '###LINKPLACEHOLDER###';
61  const TARGET_PLACEHOLDER = '###TARGETPLACEHOLDER###';
62 
68  public $cObj;
69 
76 
84  'cache_period',
85  'entryLevel',
86  'entryLevel.',
87  'special',
88  'special.',
89  'minItems',
90  'minItems.',
91  'maxItems',
92  'maxItems.',
93  'begin',
94  'begin.',
95  'alternativeSortingField',
96  'alternativeSortingField.',
97  'showAccessRestrictedPages',
98  'showAccessRestrictedPages.',
99  'excludeUidList',
100  'excludeUidList.',
101  'excludeDoktypes',
102  'includeNotInMenu',
103  'alwaysActivePIDlist',
104  'alwaysActivePIDlist.',
105  'protectLvar',
106  'addQueryString',
107  'addQueryString.',
108  'if',
109  'if.',
110  'levels',
111  'levels.',
112  'expandAll',
113  'expandAll.',
114  'includeSpacer',
115  'includeSpacer.',
116  'as',
117  'titleField',
118  'titleField.',
119  'dataProcessing',
120  'dataProcessing.'
121  ];
122 
130  'levels',
131  'levels.',
132  'expandAll',
133  'expandAll.',
134  'includeSpacer',
135  'includeSpacer.',
136  'as',
137  'titleField',
138  'titleField.',
139  'dataProcessing',
140  'dataProcessing.'
141  ];
142 
146  protected $menuConfig = [
147  'wrap' => '[|]'
148  ];
149 
153  protected $menuLevelConfig = [
154  'doNotLinkIt' => '1',
155  'wrapItemAndSub' => '{|}, |*| {|}, |*| {|}',
156  'stdWrap.' => [
157  'cObject' => 'COA',
158  'cObject.' => [
159  '10' => 'USER',
160  '10.' => [
161  'userFunc' => 'TYPO3\CMS\Frontend\DataProcessing\MenuProcessor->getDataAsJson',
162  'stdWrap.' => [
163  'wrap' => '"data":|'
164  ]
165  ],
166  '20' => 'TEXT',
167  '20.' => [
168  'field' => 'nav_title // title',
169  'trim' => '1',
170  'wrap' => ',"title":|',
171  'preUserFunc' => 'TYPO3\CMS\Frontend\DataProcessing\MenuProcessor->jsonEncodeUserFunc'
172  ],
173  '21' => 'TEXT',
174  '21.' => [
175  'value' => self::LINK_PLACEHOLDER,
176  'wrap' => ',"link":|',
177  ],
178  '22' => 'TEXT',
179  '22.' => [
180  'value' => self::TARGET_PLACEHOLDER,
181  'wrap' => ',"target":|',
182  ],
183  '30' => 'TEXT',
184  '30.' => [
185  'value' => '0',
186  'wrap' => ',"active":|'
187  ],
188  '40' => 'TEXT',
189  '40.' => [
190  'value' => '0',
191  'wrap' => ',"current":|'
192  ],
193  '50' => 'TEXT',
194  '50.' => [
195  'value' => '0',
196  'wrap' => ',"spacer":|'
197  ]
198  ]
199  ]
200  ];
201 
205  public $menuDefaults = [
206  'levels' => 1,
207  'expandAll' => 1,
208  'includeSpacer' => 0,
209  'as' => 'menu',
210  'titleField' => 'nav_title // title'
211  ];
212 
216  protected $menuLevels;
217 
221  protected $menuExpandAll;
222 
227 
231  protected $menuTitleField;
232 
237 
242 
247 
251  public function __construct()
252  {
253  $this->contentDataProcessor = GeneralUtility::makeInstance(ContentDataProcessor::class);
254  }
255 
262  protected function getConfigurationValue($key)
263  {
264  return $this->cObj->stdWrapValue($key, $this->processorConfiguration, $this->menuDefaults[$key]);
265  }
266 
272  public function validateConfiguration()
273  {
274  $invalidArguments = [];
275  foreach ($this->processorConfiguration as $key => $value) {
276  if (!in_array($key, $this->allowedConfigurationKeys)) {
277  $invalidArguments[str_replace('.', '', $key)] = $key;
278  }
279  }
280  if (!empty($invalidArguments)) {
281  throw new \InvalidArgumentException('MenuProcessor Configuration contains invalid Arguments: ' . implode(', ', $invalidArguments), 1478806566);
282  }
283  }
284 
288  public function prepareConfiguration()
289  {
290  $this->menuConfig += $this->processorConfiguration;
291  // Filter configuration
292  foreach ($this->menuConfig as $key => $value) {
293  if (in_array($key, $this->removeConfigurationKeysForHmenu)) {
294  unset($this->menuConfig[$key]);
295  }
296  }
297  // Process special value
298  if (isset($this->menuConfig['special.']['value.'])) {
299  $this->menuConfig['special.']['value'] = $this->cObj->stdWrap($this->menuConfig['special.']['value'], $this->menuConfig['special.']['value.']);
300  unset($this->menuConfig['special.']['value.']);
301  }
302  }
303 
307  public function prepareLevelConfiguration()
308  {
309  $this->menuLevelConfig['stdWrap.']['cObject.'] = array_replace_recursive(
310  $this->menuLevelConfig['stdWrap.']['cObject.'],
311  [
312  '20.' => [
313  'field' => $this->menuTitleField,
314  ]
315  ]
316  );
317  }
318 
323  {
324  if ($this->menuConfig['special'] === 'language') {
325  $this->menuLevelConfig['stdWrap.']['cObject.'] = array_replace_recursive(
326  $this->menuLevelConfig['stdWrap.']['cObject.'],
327  [
328  '60' => 'TEXT',
329  '60.' => [
330  'value' => '1',
331  'wrap' => ',"available":|'
332  ],
333  '70' => 'TEXT',
334  '70.' => [
335  'value' => $this->menuConfig['special.']['value'],
336  'listNum.' => [
337  'stdWrap.' => [
338  'data' => 'register:count_HMENU_MENUOBJ',
339  'wrap' => '|-1'
340  ],
341  'splitChar' => ','
342  ],
343  'wrap' => ',"languageUid":"|"'
344  ]
345  ]
346  );
347  }
348  }
349 
353  public function buildConfiguration()
354  {
355  for ($i = 1; $i <= $this->menuLevels; $i++) {
356  $this->menuConfig[$i] = 'TMENU';
357  $this->menuConfig[$i . '.']['IProcFunc'] = 'TYPO3\CMS\Frontend\DataProcessing\MenuProcessor->replacePlaceholderInRenderedMenuItem';
358  if ($i > 1) {
359  $this->menuConfig[$i . '.']['stdWrap.']['wrap'] = ',"children": [|]';
360  }
361  if (array_key_exists('showAccessRestrictedPages', $this->menuConfig)) {
362  $this->menuConfig[$i . '.']['showAccessRestrictedPages'] = $this->menuConfig['showAccessRestrictedPages'];
363  if (array_key_exists('showAccessRestrictedPages.', $this->menuConfig)
364  && is_array($this->menuConfig['showAccessRestrictedPages.'])) {
365  $this->menuConfig[$i . '.']['showAccessRestrictedPages.'] = $this->menuConfig['showAccessRestrictedPages.'];
366  }
367  }
368  $this->menuConfig[$i . '.']['expAll'] = $this->menuExpandAll;
369  $this->menuConfig[$i . '.']['alternativeSortingField'] = $this->menuAlternativeSortingField;
370  $this->menuConfig[$i . '.']['NO'] = '1';
371  $this->menuConfig[$i . '.']['NO.'] = $this->menuLevelConfig;
372  if ($this->menuIncludeSpacer) {
373  $this->menuConfig[$i . '.']['SPC'] = '1';
374  $this->menuConfig[$i . '.']['SPC.'] = $this->menuConfig[$i . '.']['NO.'];
375  $this->menuConfig[$i . '.']['SPC.']['stdWrap.']['cObject.']['50.']['value'] = '1';
376  }
377  $this->menuConfig[$i . '.']['IFSUB'] = '1';
378  $this->menuConfig[$i . '.']['IFSUB.'] = $this->menuConfig[$i . '.']['NO.'];
379  $this->menuConfig[$i . '.']['ACT'] = '1';
380  $this->menuConfig[$i . '.']['ACT.'] = $this->menuConfig[$i . '.']['NO.'];
381  $this->menuConfig[$i . '.']['ACT.']['stdWrap.']['cObject.']['30.']['value'] = '1';
382  $this->menuConfig[$i . '.']['ACTIFSUB'] = '1';
383  $this->menuConfig[$i . '.']['ACTIFSUB.'] = $this->menuConfig[$i . '.']['ACT.'];
384  $this->menuConfig[$i . '.']['CUR'] = '1';
385  $this->menuConfig[$i . '.']['CUR.'] = $this->menuConfig[$i . '.']['ACT.'];
386  $this->menuConfig[$i . '.']['CUR.']['stdWrap.']['cObject.']['40.']['value'] = '1';
387  $this->menuConfig[$i . '.']['CURIFSUB'] = '1';
388  $this->menuConfig[$i . '.']['CURIFSUB.'] = $this->menuConfig[$i . '.']['CUR.'];
389  if ($this->menuConfig['special'] === 'language') {
390  $this->menuConfig[$i . '.']['USERDEF1'] = $this->menuConfig[$i . '.']['NO'];
391  $this->menuConfig[$i . '.']['USERDEF1.'] = $this->menuConfig[$i . '.']['NO.'];
392  $this->menuConfig[$i . '.']['USERDEF1.']['stdWrap.']['cObject.']['60.']['value'] = '0';
393  $this->menuConfig[$i . '.']['USERDEF2'] = $this->menuConfig[$i . '.']['ACT'];
394  $this->menuConfig[$i . '.']['USERDEF2.'] = $this->menuConfig[$i . '.']['ACT.'];
395  $this->menuConfig[$i . '.']['USERDEF2.']['stdWrap.']['cObject.']['60.']['value'] = '0';
396  }
397  }
398  }
399 
407  public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData)
408  {
409  $this->cObj = $cObj;
410  $this->processorConfiguration = $processorConfiguration;
411 
412  // Get Configuration
413  $this->menuLevels = (int)$this->getConfigurationValue('levels') ?: 1;
414  $this->menuExpandAll = (int)$this->getConfigurationValue('expandAll');
415  $this->menuIncludeSpacer = (int)$this->getConfigurationValue('includeSpacer');
416  $this->menuTargetVariableName = $this->getConfigurationValue('as');
417  $this->menuTitleField = $this->getConfigurationValue('titleField');
418  $this->menuAlternativeSortingField = $this->getConfigurationValue('alternativeSortingField');
419 
420  // Validate Configuration
421  $this->validateConfiguration();
422 
423  // Build Configuration
424  $this->prepareConfiguration();
425  $this->prepareLevelConfiguration();
427  $this->buildConfiguration();
428 
429  // Process Configuration
430  $menuContentObject = $cObj->getContentObject('HMENU');
431  $renderedMenu = $menuContentObject->render($this->menuConfig);
432  if (!$renderedMenu) {
433  return $processedData;
434  }
435 
436  // Process menu
437  $menu = json_decode($renderedMenu, true);
438  $processedMenu = [];
439 
440  foreach ($menu as $key => $page) {
441  $processedMenu[$key] = $this->processAdditionalDataProcessors($page, $processorConfiguration);
442  }
443 
444  // Return processed data
445  $processedData[$this->menuTargetVariableName] = $processedMenu;
446  return $processedData;
447  }
448 
455  protected function processAdditionalDataProcessors($page, $processorConfiguration)
456  {
457  if (is_array($page['children'])) {
458  foreach ($page['children'] as $key => $item) {
459  $page['children'][$key] = $this->processAdditionalDataProcessors($item, $processorConfiguration);
460  }
461  }
463  $recordContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class);
464  $recordContentObjectRenderer->start($page['data'], 'pages');
465  $processedPage = $this->contentDataProcessor->process($recordContentObjectRenderer, $processorConfiguration, $page);
466  return $processedPage;
467  }
468 
474  public function getDataAsJson()
475  {
476  return $this->jsonEncode($this->cObj->data);
477  }
478 
486  public function jsonEncodeUserFunc($content, $conf)
487  {
488  $content = $this->jsonEncode($content);
489  return $content;
490  }
491 
498  public function jsonEncode($value)
499  {
500  return json_encode($value, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
501  }
502 
509  public function replacePlaceholderInRenderedMenuItem($menuItem, $conf)
510  {
511  $link = $this->jsonEncode($menuItem['linkHREF']['HREF']);
512  $target = $this->jsonEncode($menuItem['linkHREF']['TARGET']);
513 
514  $menuItem['parts']['title'] = str_replace(self::LINK_PLACEHOLDER, $link, $menuItem['parts']['title']);
515  $menuItem['parts']['title'] = str_replace(self::TARGET_PLACEHOLDER, $target, $menuItem['parts']['title']);
516 
517  return $menuItem;
518  }
519 }
process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData)
static makeInstance($className,... $constructorArguments)