‪TYPO3CMS  ‪main
AbstractMenuContentObject.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 
18 use Psr\EventDispatcher\EventDispatcherInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use Psr\Log\LogLevel;
44 
53 {
57  protected int ‪$menuNumber = 1;
58 
64  protected ‪$entryLevel = 0;
65 
72 
76  protected ‪$alwaysActivePIDlist = [];
77 
83  public ‪$parent_cObj;
84 
90  protected ‪$MP_array = [];
91 
97  protected ‪$conf = [];
98 
104  protected ‪$mconf = [];
105 
109  protected ‪$sys_page;
110 
116  protected ‪$id;
117 
124  protected ‪$nextActive;
125 
131  protected ‪$menuArr;
132 
136  protected ‪$hash;
137 
141  protected ‪$result = [];
142 
149  protected ‪$rL_uidRegister;
150 
154  protected ‪$I;
155 
156  protected ServerRequestInterface ‪$request;
157 
161  protected array ‪$alternativeMenuTempArray = [];
162 
168  protected ‪$parentMenuArrItemKey;
169 
173  protected ‪$parentMenuArr;
174 
175  protected bool ‪$disableGroupAccessCheck = false;
176 
177  protected const ‪customItemStates = [
178  // IFSUB is TRUE if there exist submenu items to the current item
179  'IFSUB',
180  'ACT',
181  // ACTIFSUB is TRUE if there exist submenu items to the current item and the current item is active
182  'ACTIFSUB',
183  // CUR is TRUE if the current page equals the item here!
184  'CUR',
185  // CURIFSUB is TRUE if there exist submenu items to the current item and the current page equals the item here!
186  'CURIFSUB',
187  'USR',
188  'SPC',
189  'USERDEF1',
190  'USERDEF2',
191  ];
192 
205  public function ‪start($_, ‪$sys_page, ‪$id, ‪$conf, int ‪$menuNumber, string $objSuffix, ServerRequestInterface ‪$request): bool
206  {
207  $this->conf = (array)‪$conf;
208  $this->menuNumber = ‪$menuNumber;
209  $this->mconf = (array)‪$conf[$this->menuNumber . $objSuffix . '.'];
210  $this->request = ‪$request;
211  // Sets the internal vars. $sys_page MUST be the PageRepository object
212  if ($this->conf[$this->menuNumber . $objSuffix] && is_object(‪$sys_page)) {
213  $localRootLine = ‪$request->getAttribute('frontend.page.information')->getLocalRootLine();
214  $this->sys_page = ‪$sys_page;
215  // alwaysActivePIDlist initialized:
216  $this->conf['alwaysActivePIDlist'] = (string)$this->parent_cObj->stdWrapValue('alwaysActivePIDlist', $this->conf);
217  if (trim($this->conf['alwaysActivePIDlist'])) {
218  $this->alwaysActivePIDlist = ‪GeneralUtility::intExplode(',', $this->conf['alwaysActivePIDlist']);
219  }
220  // includeNotInMenu initialized:
221  $this->conf['includeNotInMenu'] = $this->parent_cObj->stdWrapValue('includeNotInMenu', $this->conf, false);
222  // exclude doktypes that should not be shown in menu (e.g. backend user section)
223  if ($this->conf['excludeDoktypes'] ?? false) {
224  $this->excludedDoktypes = ‪GeneralUtility::intExplode(',', (string)($this->conf['excludeDoktypes']));
225  }
226  // EntryLevel
227  $this->entryLevel = $this->parent_cObj->getKey(
228  $this->parent_cObj->stdWrapValue('entryLevel', $this->conf),
229  $localRootLine
230  );
231  // Set parent page: If $id not stated with start() then the base-id will be found from rootLine[$this->entryLevel]
232  // Called as the next level in a menu. It is assumed that $this->MP_array is set from parent menu.
233  if (‪$id) {
234  $this->id = (int)‪$id;
235  } else {
236  // This is a BRAND NEW menu, first level. So we take ID from rootline and also find MP_array (mount points)
237  $this->id = (int)($localRootLine[$this->entryLevel]['uid'] ?? 0);
238 
239  // Traverse rootline to build MP_array of pages BEFORE the entryLevel
240  // (MP var for ->id is picked up in the next part of the code...)
241  foreach ($localRootLine as ‪$entryLevel => $levelRec) {
242  // For overlaid mount points, set the variable right now:
243  if (($levelRec['_MP_PARAM'] ?? false) && ($levelRec['_MOUNT_OL'] ?? false)) {
244  $this->MP_array[] = $levelRec['_MP_PARAM'];
245  }
246 
247  // Break when entry level is reached:
248  if (‪$entryLevel >= $this->entryLevel) {
249  break;
250  }
251 
252  // For normal mount points, set the variable for next level.
253  if (!empty($levelRec['_MP_PARAM']) && empty($levelRec['_MOUNT_OL'])) {
254  $this->MP_array[] = $levelRec['_MP_PARAM'];
255  }
256  }
257  }
258  // Return FALSE if no page ID was set (thus no menu of subpages can be made).
259  if ($this->id <= 0) {
260  return false;
261  }
262  // Check if page is a mount point, and if so set id and MP_array
263  // (basically this is ONLY for non-overlay mode, but in overlay mode an ID with a mount point should never reach this point anyways, so no harm done...)
264  $mount_info = $this->sys_page->getMountPointInfo($this->id);
265  if (is_array($mount_info)) {
266  $this->MP_array[] = $mount_info['MPvar'];
267  $this->id = $mount_info['mount_pid'];
268  }
269  // Gather list of page uids in root line (for "isActive" evaluation). Also adds the MP params in the path so Mount Points are respected.
270  // (List is specific for this rootline, so it may be supplied from parent menus for speed...)
271  if ($this->rL_uidRegister === null) {
272  $this->rL_uidRegister = [];
273  $rl_MParray = [];
274  foreach ($localRootLine as $v_rl) {
275  // For overlaid mount points, set the variable right now:
276  if (($v_rl['_MP_PARAM'] ?? false) && ($v_rl['_MOUNT_OL'] ?? false)) {
277  $rl_MParray[] = $v_rl['_MP_PARAM'];
278  }
279  // Add to register:
280  $this->rL_uidRegister[] = 'ITEM:' . $v_rl['uid'] .
281  (
282  !empty($rl_MParray)
283  ? ':' . implode(',', $rl_MParray)
284  : ''
285  );
286  // For normal mount points, set the variable for next level.
287  if (($v_rl['_MP_PARAM'] ?? false) && !($v_rl['_MOUNT_OL'] ?? false)) {
288  $rl_MParray[] = $v_rl['_MP_PARAM'];
289  }
290  }
291  }
292  // Set $directoryLevel so the following evaluation of the nextActive will not return
293  // an invalid value if .special=directory was set
294  $directoryLevel = 0;
295  if (($this->conf['special'] ?? '') === 'directory') {
296  $value = $this->parent_cObj->stdWrapValue('value', $this->conf['special.'] ?? [], null);
297  if ($value === '') {
298  $value = $this->request->getAttribute('frontend.page.information')->getId();
299  }
300  $directoryLevel = $this->‪getRootlineLevel($localRootLine, (string)$value);
301  }
302  // Setting "nextActive": This is the page uid + MPvar of the NEXT page in rootline. Used to expand the menu if we are in the right branch of the tree
303  // Notice: The automatic expansion of a menu is designed to work only when no "special" modes (except "directory") are used.
304  $startLevel = $directoryLevel ?: ‪$this->entryLevel;
305  $currentLevel = $startLevel + ‪$this->menuNumber;
306  if (is_array($localRootLine[$currentLevel] ?? null)) {
307  $nextMParray = ‪$this->MP_array;
308  if (empty($nextMParray) && !($localRootLine[$currentLevel]['_MOUNT_OL'] ?? false) && $currentLevel > 0) {
309  // Make sure to slide-down any mount point information (_MP_PARAM) to children records in the rootline
310  // otherwise automatic expansion will not work
311  $parentRecord = $localRootLine[$currentLevel - 1] ?? [];
312  if (isset($parentRecord['_MP_PARAM'])) {
313  $nextMParray[] = $parentRecord['_MP_PARAM'];
314  }
315  }
316  // In overlay mode, add next level MPvars as well:
317  if ($localRootLine[$currentLevel]['_MOUNT_OL'] ?? false) {
318  $nextMParray[] = $localRootLine[$currentLevel]['_MP_PARAM'] ?? [];
319  }
320  $this->nextActive = ($localRootLine[$currentLevel]['uid'] ?? 0) .
321  (
322  !empty($nextMParray)
323  ? ':' . implode(',', $nextMParray)
324  : ''
325  );
326  } else {
327  $this->nextActive = '';
328  }
329  return true;
330  }
331  $this->‪getTimeTracker()->setTSlogMessage('ERROR in menu', LogLevel::ERROR);
332  return false;
333  }
334 
341  public function ‪makeMenu()
342  {
343  if (!$this->id) {
344  return;
345  }
346 
347  // Initializing showAccessRestrictedPages
348  if ($this->mconf['showAccessRestrictedPages'] ?? false) {
349  $this->disableGroupAccessCheck = true;
350  }
351 
352  $menuItems = $this->‪prepareMenuItems();
353 
354  $c = 0;
355  $c_b = 0;
356 
357  $minItems = (int)(($this->mconf['minItems'] ?? 0) ?: ($this->conf['minItems'] ?? 0));
358  $maxItems = (int)(($this->mconf['maxItems'] ?? 0) ?: ($this->conf['maxItems'] ?? 0));
359  $begin = $this->parent_cObj->calc(($this->mconf['begin'] ?? 0) ?: ($this->conf['begin'] ?? 0));
360  $minItemsConf = $this->mconf['minItems.'] ?? $this->conf['minItems.'] ?? null;
361  $minItems = is_array($minItemsConf) ? $this->parent_cObj->stdWrap((string)$minItems, $minItemsConf) : $minItems;
362  $maxItemsConf = $this->mconf['maxItems.'] ?? $this->conf['maxItems.'] ?? null;
363  $maxItems = is_array($maxItemsConf) ? $this->parent_cObj->stdWrap((string)$maxItems, $maxItemsConf) : $maxItems;
364  $beginConf = $this->mconf['begin.'] ?? $this->conf['begin.'] ?? null;
365  $begin = is_array($beginConf) ? $this->parent_cObj->stdWrap((string)$begin, $beginConf) : $begin;
366  $this->menuArr = [];
367  foreach ($menuItems as &$data) {
368  $data['isSpacer'] = ($data['isSpacer'] ?? false) || (int)($data['doktype'] ?? 0) === ‪PageRepository::DOKTYPE_SPACER || ($data['ITEM_STATE'] ?? '') === 'SPC';
369  }
370  $menuItems = $this->‪removeInaccessiblePages($menuItems);
371  // Fill in the menuArr with elements that should go into the menu
372  foreach ($menuItems as $menuItem) {
373  $c_b++;
374  // If the beginning item has been reached, add the items.
375  if ($begin <= $c_b) {
376  $this->menuArr[$c] = $menuItem;
377  $c++;
378  if ($maxItems && $c >= $maxItems) {
379  break;
380  }
381  }
382  }
383  // Fill in fake items, if min-items is set.
384  if ($minItems) {
385  while ($c < $minItems) {
386  $this->menuArr[$c] = [
387  'title' => '...',
388  'uid' => $this->request->getAttribute('frontend.page.information')->getId(),
389  ];
390  $c++;
391  }
392  }
393  // Passing the menuArr through a user defined function:
394  if ($this->mconf['itemArrayProcFunc'] ?? false) {
395  $this->menuArr = $this->‪userProcess('itemArrayProcFunc', $this->menuArr);
396  }
397  // Setting number of menu items
398  $frontendController = $this->‪getTypoScriptFrontendController();
399  $frontendController->register['count_menuItems'] = count($this->menuArr);
400  $this->‪generate();
401  // End showAccessRestrictedPages
402  if ($this->mconf['showAccessRestrictedPages'] ?? false) {
403  $this->disableGroupAccessCheck = false;
404  }
405  }
406 
413  public function ‪generate()
414  {
415  $itemConfiguration = [];
416  $splitCount = count($this->menuArr);
417  if ($splitCount) {
418  $itemConfiguration = $this->‪processItemStates($splitCount);
419  }
420  $this->result = $itemConfiguration;
421  }
422 
426  public function ‪writeMenu()
427  {
428  return '';
429  }
430 
434  protected function ‪removeInaccessiblePages(array $pages): array
435  {
436  $banned = $this->‪getBannedUids();
437  $filteredPages = [];
438  foreach ($pages as $aPage) {
439  $isSpacerPage = ((int)($aPage['doktype'] ?? 0) === ‪PageRepository::DOKTYPE_SPACER) || ($aPage['isSpacer'] ?? false);
440  if ($this->‪filterMenuPages($aPage, $banned, $isSpacerPage)) {
441  $filteredPages[] = $aPage;
442  }
443  }
444  $event = new FilterMenuItemsEvent(
445  $pages,
446  $filteredPages,
447  $this->mconf,
448  $this->conf,
449  $banned,
450  $this->excludedDoktypes,
451  $this->‪getCurrentSite(),
452  GeneralUtility::makeInstance(Context::class),
453  $this->request->getAttribute('frontend.page.information')->getPageRecord()
454  );
455  $event = GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch($event);
456  return $event->getFilteredMenuItems();
457  }
458 
464  protected function ‪prepareMenuItems()
465  {
466  $menuItems = [];
467  $alternativeSortingField = trim($this->mconf['alternativeSortingField'] ?? '') ?: 'sorting';
468 
469  // Additional where clause, usually starts with AND (as usual with all additionalWhere functionality in TS)
470  $additionalWhere = $this->parent_cObj->stdWrapValue('additionalWhere', $this->mconf);
471  $additionalWhere .= $this->‪getDoktypeExcludeWhere();
472 
473  // ... only for the FIRST level of a HMENU
474  if ($this->menuNumber == 1 && ($this->conf['special'] ?? false)) {
475  $value = (string)$this->parent_cObj->stdWrapValue('value', $this->conf['special.'] ?? [], null);
476  switch ($this->conf['special']) {
477  case 'userfunction':
478  $menuItems = $this->‪prepareMenuItemsForUserSpecificMenu($value, $alternativeSortingField);
479  break;
480  case 'language':
481  $menuItems = $this->‪prepareMenuItemsForLanguageMenu($value);
482  break;
483  case 'directory':
484  $menuItems = $this->‪prepareMenuItemsForDirectoryMenu($value, $alternativeSortingField);
485  break;
486  case 'list':
487  $menuItems = $this->‪prepareMenuItemsForListMenu($value);
488  break;
489  case 'updated':
490  $menuItems = $this->‪prepareMenuItemsForUpdatedMenu(
491  $value,
492  $this->mconf['alternativeSortingField'] ?? ''
493  );
494  break;
495  case 'keywords':
496  $menuItems = $this->‪prepareMenuItemsForKeywordsMenu(
497  $value,
498  $this->mconf['alternativeSortingField'] ?? ''
499  );
500  break;
501  case 'categories':
502  $categoryMenuUtility = GeneralUtility::makeInstance(CategoryMenuUtility::class);
503  $menuItems = $categoryMenuUtility->collectPages($value, $this->conf['special.'], $this);
504  break;
505  case 'rootline':
506  $menuItems = $this->‪prepareMenuItemsForRootlineMenu();
507  break;
508  case 'browse':
509  $menuItems = $this->‪prepareMenuItemsForBrowseMenu($value, $alternativeSortingField, $additionalWhere);
510  break;
511  }
512  if ($this->mconf['sectionIndex'] ?? false) {
513  $sectionIndexes = [];
514  foreach ($menuItems as $page) {
515  $sectionIndexes = $sectionIndexes + $this->‪sectionIndex($alternativeSortingField, $page['uid']);
516  }
517  $menuItems = $sectionIndexes;
518  }
519  } elseif ($this->alternativeMenuTempArray !== []) {
520  // Setting $menuItems array if not level 1.
522  } elseif ($this->mconf['sectionIndex'] ?? false) {
523  $menuItems = $this->‪sectionIndex($alternativeSortingField);
524  } else {
525  // Default: Gets a hierarchical menu based on subpages of $this->id
526  $subMenuDecision = $this->‪getRuntimeCache()->get($this->‪getCacheIdentifierForSubMenuDecision($this->id));
527  if (!isset($subMenuDecision['result']) || $subMenuDecision['result'] === true) {
528  $menuItems = $this->sys_page->getMenu($this->id, '*', $alternativeSortingField, $additionalWhere, true, $this->disableGroupAccessCheck);
529  }
530  }
531  return $menuItems;
532  }
533 
541  protected function ‪prepareMenuItemsForUserSpecificMenu($specialValue, $sortingField)
542  {
543  $menuItems = $this->parent_cObj->callUserFunction(
544  $this->conf['special.']['userFunc'],
545  array_merge($this->conf['special.'], ['value' => $specialValue, '_altSortField' => $sortingField]),
546  ''
547  );
548  return is_array($menuItems) ? $menuItems : [];
549  }
550 
557  protected function ‪prepareMenuItemsForLanguageMenu($specialValue)
558  {
559  $menuItems = [];
560  // Getting current page record NOT overlaid by any translation:
561  $pageRecord = $this->request->getAttribute('frontend.page.information')->getPageRecord();
562  $currentPageWithNoOverlay = ($pageRecord['_TRANSLATION_SOURCE'] ?? null)?->toArray(true) ?? $pageRecord;
563 
564  ‪$languages = $this->‪getCurrentSite()->getLanguages();
565  if ($specialValue === 'auto') {
566  $languageItems = array_keys(‪$languages);
567  } else {
568  $languageItems = ‪GeneralUtility::intExplode(',', $specialValue);
569  }
570 
571  $tsfe = $this->‪getTypoScriptFrontendController();
572  $tsfe->register['languages_HMENU'] = implode(',', $languageItems);
573 
574  $currentLanguageId = $this->‪getCurrentLanguageAspect()->getId();
575 
576  // @todo Fetch all language overlays in a single query
577  foreach ($languageItems as $sUid) {
578  // Find overlay record:
579  if ($sUid) {
581  $pageRepository = $this->‪buildPageRepository($languageAspect);
582  $lRecs = $pageRepository->getPageOverlay($currentPageWithNoOverlay, $languageAspect);
583  // getPageOverlay() might return the original record again, if so this is emptied
584  // this should be fixed in PageRepository in the future.
585  if (!empty($lRecs) && !isset($lRecs['_LOCALIZED_UID'])) {
586  $lRecs = [];
587  }
588  } else {
589  $lRecs = [];
590  }
591  // Checking if the "disabled" state should be set.
592  $pageTranslationVisibility = new PageTranslationVisibility((int)($currentPageWithNoOverlay['l18n_cfg'] ?? 0));
593  if ($pageTranslationVisibility->shouldHideTranslationIfNoTranslatedRecordExists() && $sUid &&
594  empty($lRecs) || $pageTranslationVisibility->shouldBeHiddenInDefaultLanguage() &&
595  (!$sUid || empty($lRecs)) ||
596  !($this->conf['special.']['normalWhenNoLanguage'] ?? false) && $sUid && empty($lRecs)
597  ) {
598  $iState = $currentLanguageId === $sUid ? 'USERDEF2' : 'USERDEF1';
599  } else {
600  $iState = $currentLanguageId === $sUid ? 'ACT' : 'NO';
601  }
602  // Adding menu item:
603  $menuItems[] = array_merge(
604  array_merge($currentPageWithNoOverlay, $lRecs),
605  [
606  '_REQUESTED_OVERLAY_LANGUAGE' => $sUid,
607  'ITEM_STATE' => $iState,
608  '_ADD_GETVARS' => $this->conf['addQueryString'] ?? false,
609  '_SAFE' => true,
610  ]
611  );
612  }
613  return $menuItems;
614  }
615 
620  protected function ‪buildPageRepository(‪LanguageAspect $languageAspect = null): ‪PageRepository
621  {
622  // clone global context object (singleton)
623  $context = clone GeneralUtility::makeInstance(Context::class);
624  $context->setAspect(
625  'language',
626  $languageAspect ?? GeneralUtility::makeInstance(LanguageAspect::class)
627  );
628  return GeneralUtility::makeInstance(
629  PageRepository::class,
630  $context
631  );
632  }
633 
641  protected function ‪prepareMenuItemsForDirectoryMenu($specialValue, $sortingField)
642  {
643  $menuItems = [];
644  if ($specialValue == '') {
645  $specialValue = $this->request->getAttribute('frontend.page.information')->getId();
646  }
647  $items = ‪GeneralUtility::intExplode(',', (string)$specialValue);
648  $pageLinkBuilder = GeneralUtility::makeInstance(PageLinkBuilder::class, $this->parent_cObj);
649  foreach ($items as ‪$id) {
650  $MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps(‪$id);
651  // Checking if a page is a mount page and if so, change the ID and set the MP var properly.
652  $mount_info = $this->sys_page->getMountPointInfo(‪$id);
653  if (is_array($mount_info)) {
654  if ($mount_info['overlay']) {
655  // Overlays should already have their full MPvars calculated:
656  $MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps((int)$mount_info['mount_pid']);
657  $MP = $MP ?: $mount_info['MPvar'];
658  } else {
659  $MP = ($MP ? $MP . ',' : '') . $mount_info['MPvar'];
660  }
661  ‪$id = $mount_info['mount_pid'];
662  }
663  $subPages = $this->sys_page->getMenu(‪$id, '*', $sortingField, '', true, $this->disableGroupAccessCheck);
664  foreach ($subPages as $row) {
665  // Add external MP params
666  if ($MP) {
667  $row['_MP_PARAM'] = $MP . (($row['_MP_PARAM'] ?? '') ? ',' . $row['_MP_PARAM'] : '');
668  }
669  $menuItems[] = $row;
670  }
671  }
672 
673  return $menuItems;
674  }
675 
682  protected function ‪prepareMenuItemsForListMenu($specialValue)
683  {
684  $menuItems = [];
685  if ($specialValue == '') {
686  $specialValue = ‪$this->id;
687  }
688  $pageIds = ‪GeneralUtility::intExplode(',', (string)$specialValue);
689  ‪$disableGroupAccessCheck = !empty($this->mconf['showAccessRestrictedPages']);
690  $pageRecords = $this->sys_page->getMenuForPages($pageIds, '*', 'sorting', '', true, ‪$disableGroupAccessCheck);
691  // After fetching the page records, restore the initial order by using the page id list as arrays keys and
692  // replace them with the resolved page records. The id list is cleaned up first, since ids might be invalid.
693  $pageRecords = array_replace(
694  array_flip(array_intersect(array_values($pageIds), array_keys($pageRecords))),
695  $pageRecords
696  );
697  $pageLinkBuilder = GeneralUtility::makeInstance(PageLinkBuilder::class, $this->parent_cObj);
698  foreach ($pageRecords as $row) {
699  $pageId = (int)$row['uid'];
700  $MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps($pageId);
701  // Keep mount point?
702  $mount_info = $this->sys_page->getMountPointInfo($pageId, $row);
703  // $pageId is a valid mount point
704  if (is_array($mount_info) && $mount_info['overlay']) {
705  $mountedPageId = (int)$mount_info['mount_pid'];
706  // Using "getPage" is OK since we need the check for enableFields
707  // AND for type 2 of mount pids we DO require a doktype < 200!
708  $mountedPageRow = $this->sys_page->getPage($mountedPageId, ‪$disableGroupAccessCheck);
709  if (empty($mountedPageRow)) {
710  // If the mount point could not be fetched with respect to
711  // enableFields, the page should not become a part of the menu!
712  continue;
713  }
714  $row = $mountedPageRow;
715  $row['_MP_PARAM'] = $mount_info['MPvar'];
716  // Overlays should already have their full MPvars calculated, that's why we unset the
717  // existing $row['_MP_PARAM'], as the full $MP will be added again below
718  $MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps($mountedPageId);
719  if ($MP) {
720  unset($row['_MP_PARAM']);
721  }
722  }
723  if ($MP) {
724  $row['_MP_PARAM'] = $MP . ($row['_MP_PARAM'] ? ',' . $row['_MP_PARAM'] : '');
725  }
726  $menuItems[] = $row;
727  }
728  return $menuItems;
729  }
730 
738  protected function ‪prepareMenuItemsForUpdatedMenu($specialValue, $sortingField)
739  {
740  $menuItems = [];
741  if ($specialValue == '') {
742  $specialValue = $this->request->getAttribute('frontend.page.information')->getId();
743  }
744  $items = ‪GeneralUtility::intExplode(',', (string)$specialValue);
745  if (‪MathUtility::canBeInterpretedAsInteger($this->conf['special.']['depth'] ?? null)) {
746  $depth = ‪MathUtility::forceIntegerInRange($this->conf['special.']['depth'], 1, 20);
747  } else {
748  $depth = 20;
749  }
750  // Max number of items
751  $limit = ‪MathUtility::forceIntegerInRange(($this->conf['special.']['limit'] ?? 0), 0, 100);
752  $maxAge = (int)($this->parent_cObj->calc($this->conf['special.']['maxAge'] ?? 0));
753  if (!$limit) {
754  $limit = 10;
755  }
756  // 'auto', 'manual', 'tstamp'
757  $mode = $this->conf['special.']['mode'] ?? '';
758  // Get id's
759  $beginAtLevel = ‪MathUtility::forceIntegerInRange(($this->conf['special.']['beginAtLevel'] ?? 0), 0, 100);
760  $pageIds = [];
761  foreach ($items as ‪$id) {
762  // Exclude the current ID if beginAtLevel is > 0
763  if ($beginAtLevel > 0) {
764  $pageIds = array_merge($pageIds, $this->sys_page->getDescendantPageIdsRecursive(‪$id, $depth - 1 + $beginAtLevel, $beginAtLevel - 1));
765  } else {
766  $pageIds = array_merge($pageIds, [‪$id], $this->sys_page->getDescendantPageIdsRecursive(‪$id, $depth - 1 + $beginAtLevel, $beginAtLevel - 1));
767  }
768  }
769  // Get sortField (mode)
770  $sortField = $this->‪getMode($mode);
771 
772  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
773  $extraWhere = ($this->conf['includeNotInMenu'] ? '' : ' AND pages.nav_hide=0') . $this->‪getDoktypeExcludeWhere();
774  if ($this->conf['special.']['excludeNoSearchPages'] ?? false) {
775  $extraWhere .= sprintf(' AND %s=%s', $connection->quoteIdentifier('pages.no_search'), $connection->quote('0'));
776  }
777  if ($maxAge > 0) {
778  $extraWhere .= sprintf(' AND %s>%s', $connection->quoteIdentifier($sortField), $connection->quote((string)(‪$GLOBALS['SIM_ACCESS_TIME'] - $maxAge)));
779  }
780  $extraWhere = sprintf('%s>=%s', $connection->quoteIdentifier($sortField), $connection->quote('0')) . $extraWhere;
781 
782  $i = 0;
783  $pageRecords = $this->sys_page->getMenuForPages($pageIds, '*', $sortingField ?: $sortField . ' DESC', $extraWhere, true, $this->disableGroupAccessCheck);
784  foreach ($pageRecords as $row) {
785  // Build a custom LIMIT clause as "getMenuForPages()" does not support this
786  if (++$i > $limit) {
787  continue;
788  }
789  $menuItems[$row['uid']] = $row;
790  }
791 
792  return $menuItems;
793  }
794 
802  protected function ‪prepareMenuItemsForKeywordsMenu($specialValue, $sortingField)
803  {
804  $menuItems = [];
805  [$specialValue] = ‪GeneralUtility::intExplode(',', $specialValue);
806  if (!$specialValue) {
807  $specialValue = $this->request->getAttribute('frontend.page.information')->getId();
808  }
809  if (($this->conf['special.']['setKeywords'] ?? false) || ($this->conf['special.']['setKeywords.'] ?? false)) {
810  $kw = (string)$this->parent_cObj->stdWrapValue('setKeywords', $this->conf['special.'] ?? []);
811  } else {
812  // The page record of the 'value'.
813  $value_rec = $this->sys_page->getPage((int)$specialValue);
814  $kfieldSrc = ($this->conf['special.']['keywordsField.']['sourceField'] ?? false) ? $this->conf['special.']['keywordsField.']['sourceField'] : 'keywords';
815  // keywords.
816  $kw = trim($this->parent_cObj->keywords($value_rec[$kfieldSrc] ?? ''));
817  }
818  // *'auto', 'manual', 'tstamp'
819  $mode = $this->conf['special.']['mode'] ?? '';
820  $sortField = $this->‪getMode($mode);
821  // Depth, limit, extra where
822  if (‪MathUtility::canBeInterpretedAsInteger($this->conf['special.']['depth'] ?? null)) {
823  $depth = ‪MathUtility::forceIntegerInRange($this->conf['special.']['depth'], 0, 20);
824  } else {
825  $depth = 20;
826  }
827  // Max number of items
828  $limit = ‪MathUtility::forceIntegerInRange(($this->conf['special.']['limit'] ?? 0), 0, 100);
829  // Start point
830  $localRootLine = $this->request->getAttribute('frontend.page.information')->getLocalRootLine();
831  $eLevel = $this->parent_cObj->getKey(
832  $this->parent_cObj->stdWrapValue('entryLevel', $this->conf['special.'] ?? []),
833  $localRootLine
834  );
835  $startUid = (int)($localRootLine[$eLevel]['uid'] ?? 0);
836  // Which field is for keywords
837  $kfield = 'keywords';
838  if ($this->conf['special.']['keywordsField'] ?? false) {
839  [$kfield] = explode(' ', trim($this->conf['special.']['keywordsField']));
840  }
841  // If there are keywords and the startUid is present
842  if ($kw && $startUid) {
843  $bA = ‪MathUtility::forceIntegerInRange(($this->conf['special.']['beginAtLevel'] ?? 0), 0, 100);
844  $id_list = $this->sys_page->getDescendantPageIdsRecursive($startUid, $depth - 1 + $bA, $bA - 1);
845  $id_list = array_merge([(int)$startUid], $id_list);
846  $kwArr = ‪GeneralUtility::trimExplode(',', $kw, true);
847  $keyWordsWhereArr = [];
848  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
849  foreach ($kwArr as $word) {
850  $keyWordsWhereArr[] = $queryBuilder->expr()->like(
851  $kfield,
852  $queryBuilder->createNamedParameter(
853  '%' . $queryBuilder->escapeLikeWildcards($word) . '%'
854  )
855  );
856  }
857  $queryBuilder
858  ->select('*')
859  ->from('pages')
860  ->where(
861  $queryBuilder->expr()->in(
862  'uid',
863  $id_list
864  ),
865  $queryBuilder->expr()->neq(
866  'uid',
867  $queryBuilder->createNamedParameter($specialValue, ‪Connection::PARAM_INT)
868  )
869  );
870 
871  if (!empty($keyWordsWhereArr)) {
872  $queryBuilder->andWhere($queryBuilder->expr()->or(...$keyWordsWhereArr));
873  }
874 
875  if (!empty($this->excludedDoktypes)) {
876  $queryBuilder->andWhere(
877  $queryBuilder->expr()->notIn(
878  'pages.doktype',
879  $this->excludedDoktypes
880  )
881  );
882  }
883 
884  if (!$this->conf['includeNotInMenu']) {
885  $queryBuilder->andWhere($queryBuilder->expr()->eq('pages.nav_hide', 0));
886  }
887 
888  if ($this->conf['special.']['excludeNoSearchPages'] ?? false) {
889  $queryBuilder->andWhere($queryBuilder->expr()->eq('pages.no_search', 0));
890  }
891 
892  if ($limit > 0) {
893  $queryBuilder->setMaxResults($limit);
894  }
895 
896  if ($sortingField) {
897  $queryBuilder->orderBy($sortingField);
898  } else {
899  $queryBuilder->orderBy($sortField, 'desc');
900  }
901 
902  ‪$result = $queryBuilder->executeQuery();
903  while ($row = ‪$result->fetchAssociative()) {
904  $this->sys_page->versionOL('pages', $row, true);
905  if (is_array($row)) {
906  $menuItems[$row['uid']] = $this->sys_page->getPageOverlay($row);
907  }
908  }
909  }
910 
911  return $menuItems;
912  }
913 
919  protected function ‪prepareMenuItemsForRootlineMenu()
920  {
921  $menuItems = [];
922  $range = (string)$this->parent_cObj->stdWrapValue('range', $this->conf['special.'] ?? []);
923  $begin_end = explode('|', $range);
924  $begin_end[0] = (int)$begin_end[0];
925  if (!‪MathUtility::canBeInterpretedAsInteger($begin_end[1] ?? '')) {
926  $begin_end[1] = -1;
927  }
928  $localRootLine = $this->request->getAttribute('frontend.page.information')->getLocalRootLine();
929  $beginKey = $this->parent_cObj->getKey($begin_end[0], $localRootLine ?? []);
930  $endKey = $this->parent_cObj->getKey($begin_end[1], $localRootLine ?? []);
931  if ($endKey < $beginKey) {
932  $endKey = $beginKey;
933  }
934  $rl_MParray = [];
935  foreach ($localRootLine as $k_rl => $v_rl) {
936  // For overlaid mount points, set the variable right now:
937  if (($v_rl['_MP_PARAM'] ?? false) && ($v_rl['_MOUNT_OL'] ?? false)) {
938  $rl_MParray[] = $v_rl['_MP_PARAM'];
939  }
940  // Traverse rootline:
941  if ($k_rl >= $beginKey && $k_rl <= $endKey) {
942  $temp_key = $k_rl;
943  $menuItems[$temp_key] = $this->sys_page->getPage((int)$v_rl['uid']);
944  if (!empty($menuItems[$temp_key])) {
945  // If there are no specific target for the page, put the level specific target on.
946  if (!$menuItems[$temp_key]['target']) {
947  $menuItems[$temp_key]['target'] = $this->conf['special.']['targets.'][$k_rl] ?? '';
948  $menuItems[$temp_key]['_MP_PARAM'] = implode(',', $rl_MParray);
949  }
950  } else {
951  unset($menuItems[$temp_key]);
952  }
953  }
954  // For normal mount points, set the variable for next level.
955  if (($v_rl['_MP_PARAM'] ?? false) && !($v_rl['_MOUNT_OL'] ?? false)) {
956  $rl_MParray[] = $v_rl['_MP_PARAM'];
957  }
958  }
959  // Reverse order of elements (e.g. "1,2,3,4" gets "4,3,2,1"):
960  if (isset($this->conf['special.']['reverseOrder']) && $this->conf['special.']['reverseOrder']) {
961  $menuItems = array_reverse($menuItems);
962  }
963  return $menuItems;
964  }
965 
974  protected function ‪prepareMenuItemsForBrowseMenu($specialValue, $sortingField, $additionalWhere)
975  {
976  $menuItems = [];
977  [$specialValue] = ‪GeneralUtility::intExplode(',', $specialValue);
978  if (!$specialValue) {
979  $specialValue = $this->request->getAttribute('frontend.page.information')->getPageRecord()['uid'];
980  }
981  $localRootLine = $this->request->getAttribute('frontend.page.information')->getLocalRootLine();
982  // Will not work out of rootline
983  if ($specialValue != ($localRootLine[0]['uid'] ?? null)) {
984  $recArr = [];
985  // The page record of the 'value'.
986  $value_rec = $this->sys_page->getPage((int)$specialValue, $this->disableGroupAccessCheck);
987  // 'up' page cannot be outside rootline
988  if ($value_rec['pid']) {
989  // The page record of 'up'.
990  $recArr['up'] = $this->sys_page->getPage((int)$value_rec['pid'], $this->disableGroupAccessCheck);
991  }
992  // If the 'up' item was NOT level 0 in rootline...
993  if (($recArr['up']['pid'] ?? 0) && $value_rec['pid'] != ($localRootLine[0]['uid'] ?? null)) {
994  // The page record of "index".
995  $recArr['index'] = $this->sys_page->getPage((int)$recArr['up']['pid']);
996  }
997  // check if certain pages should be excluded
998  $additionalWhere .= ($this->conf['includeNotInMenu'] ? '' : ' AND pages.nav_hide=0') . $this->‪getDoktypeExcludeWhere();
999  if ($this->conf['special.']['excludeNoSearchPages'] ?? false) {
1000  $additionalWhere .= ' AND pages.no_search=0';
1001  }
1002  // prev / next is found
1003  $prevnext_menu = $this->‪removeInaccessiblePages($this->sys_page->getMenu($value_rec['pid'], '*', $sortingField, $additionalWhere, true, $this->disableGroupAccessCheck));
1004  ‪$nextActive = false;
1005  foreach ($prevnext_menu as $k_b => $v_b) {
1006  if (‪$nextActive) {
1007  $recArr['next'] = $v_b;
1008  ‪$nextActive = false;
1009  }
1010  if ($v_b['uid'] == $specialValue) {
1011  if (isset($lastKey)) {
1012  $recArr['prev'] = $prevnext_menu[$lastKey];
1013  }
1014  ‪$nextActive = true;
1015  }
1016  $lastKey = $k_b;
1017  }
1018  unset($lastKey);
1019 
1020  $recArr['first'] = reset($prevnext_menu);
1021  $recArr['last'] = end($prevnext_menu);
1022  // prevsection / nextsection is found
1023  // You can only do this, if there is a valid page two levels up!
1024  if (!empty($recArr['index']['uid'])) {
1025  $prevnextsection_menu = $this->‪removeInaccessiblePages($this->sys_page->getMenu($recArr['index']['uid'], '*', $sortingField, $additionalWhere, true, $this->disableGroupAccessCheck));
1026  ‪$nextActive = false;
1027  foreach ($prevnextsection_menu as $k_b => $v_b) {
1028  if (‪$nextActive) {
1029  $sectionRec_temp = $this->‪removeInaccessiblePages($this->sys_page->getMenu($v_b['uid'], '*', $sortingField, $additionalWhere, true, $this->disableGroupAccessCheck));
1030  if (!empty($sectionRec_temp)) {
1031  $recArr['nextsection'] = reset($sectionRec_temp);
1032  $recArr['nextsection_last'] = end($sectionRec_temp);
1033  ‪$nextActive = false;
1034  }
1035  }
1036  if ($v_b['uid'] == $value_rec['pid']) {
1037  if (isset($lastKey)) {
1038  $sectionRec_temp = $this->‪removeInaccessiblePages($this->sys_page->getMenu($prevnextsection_menu[$lastKey]['uid'], '*', $sortingField, $additionalWhere, true, $this->disableGroupAccessCheck));
1039  if (!empty($sectionRec_temp)) {
1040  $recArr['prevsection'] = reset($sectionRec_temp);
1041  $recArr['prevsection_last'] = end($sectionRec_temp);
1042  }
1043  }
1044  ‪$nextActive = true;
1045  }
1046  $lastKey = $k_b;
1047  }
1048  unset($lastKey);
1049  }
1050  if ($this->conf['special.']['items.']['prevnextToSection'] ?? false) {
1051  if (!is_array($recArr['prev'] ?? false) && is_array($recArr['prevsection_last'] ?? false)) {
1052  $recArr['prev'] = $recArr['prevsection_last'];
1053  }
1054  if (!is_array($recArr['next'] ?? false) && is_array($recArr['nextsection'] ?? false)) {
1055  $recArr['next'] = $recArr['nextsection'];
1056  }
1057  }
1058  $items = explode('|', $this->conf['special.']['items']);
1059  $c = 0;
1060  foreach ($items as $k_b => $v_b) {
1061  $v_b = strtolower(trim($v_b));
1062  if ((int)($this->conf['special.'][$v_b . '.']['uid'] ?? false)) {
1063  $recArr[$v_b] = $this->sys_page->getPage((int)$this->conf['special.'][$v_b . '.']['uid'], $this->disableGroupAccessCheck);
1064  }
1065  if (is_array($recArr[$v_b] ?? false)) {
1066  $menuItems[$c] = $recArr[$v_b];
1067  if ($this->conf['special.'][$v_b . '.']['target'] ?? false) {
1068  $menuItems[$c]['target'] = $this->conf['special.'][$v_b . '.']['target'];
1069  }
1070  foreach ((array)($this->conf['special.'][$v_b . '.']['fields.'] ?? []) as $fk => $val) {
1071  $menuItems[$c][$fk] = $val;
1072  }
1073  $c++;
1074  }
1075  }
1076  }
1077  return $menuItems;
1078  }
1079 
1091  public function ‪filterMenuPages(&$data, $banUidArray, $isSpacerPage)
1092  {
1093  if ($data['_SAFE'] ?? false) {
1094  return true;
1095  }
1096  // If the spacer-function is not enabled, spacers will not enter the $menuArr
1097  if (!($this->mconf['SPC'] ?? false) && $isSpacerPage) {
1098  return false;
1099  }
1100  // Page may not be a 'Backend User Section' or any other excluded doktype
1101  if (in_array((int)($data['doktype'] ?? 0), $this->excludedDoktypes, true)) {
1102  return false;
1103  }
1104  // PageID should not be banned (check for default language and translated IDs)
1105  if (($data['_LOCALIZED_UID'] ?? 0) > 0 && in_array((int)$data['_LOCALIZED_UID'], $banUidArray, true)) {
1106  return false;
1107  }
1108  if (in_array((int)($data['uid'] ?? 0), $banUidArray, true)) {
1109  return false;
1110  }
1111  // If the page is hide in menu, but the menu does not include them do not show the page
1112  if (($data['nav_hide'] ?? false) && !($this->conf['includeNotInMenu'] ?? false)) {
1113  return false;
1114  }
1115  // Checking if a page should be shown in the menu depending on whether a translation exists or if the default language is disabled
1116  if (!$this->sys_page->isPageSuitableForLanguage($data, $this->getCurrentLanguageAspect())) {
1117  return false;
1118  }
1119  $languageAspect = $this->‪getCurrentLanguageAspect();
1120  // Checking if the link should point to the default language so links to non-accessible pages will not happen
1121  if ($languageAspect->getId() > 0 && !empty($this->conf['protectLvar'])) {
1122  $pageTranslationVisibility = new PageTranslationVisibility((int)($data['l18n_cfg'] ?? 0));
1123  if ($this->conf['protectLvar'] === 'all' || $pageTranslationVisibility->shouldHideTranslationIfNoTranslatedRecordExists()) {
1124  $olRec = $this->sys_page->getPageOverlay((int)($data['uid'] ?? 0), $languageAspect);
1125  if (empty($olRec)) {
1126  // If no page translation record then page can NOT be accessed in
1127  // the language pointed to, therefore we protect the link by linking to the default language
1128  $data['_REQUESTED_OVERLAY_LANGUAGE'] = '0';
1129  }
1130  }
1131  }
1132  return true;
1133  }
1134 
1148  protected function ‪processItemStates($splitCount)
1149  {
1150  // Prepare normal settings
1151  if (!is_array($this->mconf['NO.'] ?? null) && $this->mconf['NO']) {
1152  // Setting a blank array if NO=1 and there are no properties.
1153  $this->mconf['NO.'] = [];
1154  }
1155  $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
1156  $NOconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['NO.'], $splitCount);
1157 
1158  // Prepare custom states settings, overriding normal settings
1159  foreach (self::customItemStates as $state) {
1160  if (empty($this->mconf[$state])) {
1161  continue;
1162  }
1163  $customConfiguration = null;
1164  foreach ($NOconf as $key => $val) {
1165  if ($this->‪isItemState($state, $key)) {
1166  // if this is the first element of type $state, we must generate the custom configuration.
1167  if ($customConfiguration === null) {
1168  $customConfiguration = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf[$state . '.'], $splitCount);
1169  }
1170  // Substitute normal with the custom (e.g. IFSUB)
1171  if (isset($customConfiguration[$key])) {
1172  $NOconf[$key] = $customConfiguration[$key];
1173  }
1174  }
1175  }
1176  }
1177 
1178  return $NOconf;
1179  }
1180 
1189  protected function ‪link($key, $altTarget, $typeOverride)
1190  {
1191  $runtimeCache = $this->‪getRuntimeCache();
1192  $MP_var = $this->‪getMPvar($key);
1193  $cacheId = 'menu-generated-links-' . md5(
1194  $key
1195  . ($altTarget ?: ($this->mconf['target'] ?? '') . (isset($this->mconf['target.']) ? json_encode($this->mconf['target.']) : ''))
1196  . $typeOverride
1197  . $MP_var
1198  . ($this->mconf['addParams'] ?? '')
1199  . ($this->I['val']['additionalParams'] ?? '')
1200  . ((string)($this->mconf['showAccessRestrictedPages'] ?? '_'))
1201  . (isset($this->mconf['showAccessRestrictedPages.']) ? json_encode($this->mconf['showAccessRestrictedPages.']) : '')
1202  . json_encode($this->menuArr[$key])
1203  . ($this->I['val']['ATagParams'] ?? '')
1204  . (isset($this->I['val']['ATagParams.']) ? json_encode($this->I['val']['ATagParams.']) : '')
1205  );
1206  $runtimeCachedLink = $runtimeCache->get($cacheId);
1207  if ($runtimeCachedLink !== false) {
1208  return $runtimeCachedLink;
1209  }
1210 
1211  $typoScript = $this->request->getAttribute('frontend.typoscript');
1212  $backupSetupConfigArray = $hackedSetupConfigArray = $typoScript->getConfigArray();
1213 
1214  // links to a specific page
1215  if ($this->mconf['showAccessRestrictedPages'] ?? false) {
1216  // @todo: Resetting config is a hack. This needs to be resolved differently. Consumed in PageLinkBuilder.
1217  $hackedSetupConfigArray['typolinkLinkAccessRestrictedPages'] = $this->mconf['showAccessRestrictedPages'];
1218  $hackedSetupConfigArray['typolinkLinkAccessRestrictedPages_addParams'] = $this->mconf['showAccessRestrictedPages.']['addParams'] ?? '';
1219  $hackedSetupConfigArray['typolinkLinkAccessRestrictedPages.']['ATagParams'] = $this->mconf['showAccessRestrictedPages.']['ATagParams'] ?? '';
1220  $typoScript->setConfigArray($hackedSetupConfigArray);
1221  }
1222  // If a user script returned the value overrideId in the menu array we use that as page id
1223  if (($this->mconf['overrideId'] ?? false) || ($this->menuArr[$key]['overrideId'] ?? false)) {
1224  $overrideId = (int)($this->mconf['overrideId'] ?: $this->menuArr[$key]['overrideId']);
1225  $overrideId = $overrideId > 0 ? $overrideId : null;
1226  // Clear MP parameters since ID was changed.
1227  $MP_params = '';
1228  } else {
1229  $overrideId = null;
1230  // Mount points:
1231  $MP_params = $MP_var ? '&MP=' . rawurlencode($MP_var) : '';
1232  }
1233  // Setting main target
1234  $mainTarget = $altTarget ?: (string)$this->parent_cObj->stdWrapValue('target', $this->mconf);
1235  // Creating link
1236  $addParams = ($this->mconf['addParams'] ?? '') . ($this->I['val']['additionalParams'] ?? '') . $MP_params;
1237  try {
1238  $linkResult = $this->‪menuTypoLink($this->menuArr[$key], $mainTarget, $addParams, $typeOverride, $overrideId);
1239  // Overriding URL / Target if set to do so:
1240  if ($this->menuArr[$key]['_OVERRIDE_HREF'] ?? false) {
1241  $linkResult = $linkResult->withAttribute('href', $this->menuArr[$key]['_OVERRIDE_HREF']);
1242  if ($this->menuArr[$key]['_OVERRIDE_TARGET'] ?? false) {
1243  $linkResult = $linkResult->withAttribute('target', $this->menuArr[$key]['_OVERRIDE_TARGET']);
1244  }
1245  }
1246  } catch (UnableToLinkException $e) {
1247  $linkResult = null;
1248  }
1249  $runtimeCache->set($cacheId, $linkResult);
1250 
1251  // End showAccessRestrictedPages
1252  if ($this->mconf['showAccessRestrictedPages'] ?? false) {
1253  $typoScript->setConfigArray($backupSetupConfigArray);
1254  }
1255 
1256  return $linkResult;
1257  }
1258 
1266  protected function ‪subMenu(int ‪$uid, string $objSuffix, int $menuItemKey)
1267  {
1268  // Setting alternative menu item array if _SUB_MENU has been defined in the current ->menuArr
1269  $altArray = [];
1270  if (is_array($this->menuArr[$menuItemKey]['_SUB_MENU'] ?? null) && !empty($this->menuArr[$menuItemKey]['_SUB_MENU'])) {
1271  $altArray = $this->menuArr[$menuItemKey]['_SUB_MENU'];
1272  }
1273  // Make submenu if the page is the next active
1274  $menuType = $this->conf[($this->menuNumber + 1) . $objSuffix] ?? '';
1275  // stdWrap for expAll
1276  $this->mconf['expAll'] = $this->parent_cObj->stdWrapValue('expAll', $this->mconf);
1277  if (($this->mconf['expAll'] || $this->‪isNext($uid, $this->‪getMPvar($menuItemKey)) || $altArray !== []) && !($this->mconf['sectionIndex'] ?? false)) {
1278  try {
1279  $menuObjectFactory = GeneralUtility::makeInstance(MenuContentObjectFactory::class);
1281  $submenu = $menuObjectFactory->getMenuObjectByType($menuType);
1282  $submenu->entryLevel = $this->entryLevel + 1;
1283  $submenu->rL_uidRegister = ‪$this->rL_uidRegister;
1284  $submenu->MP_array = ‪$this->MP_array;
1285  if ($this->menuArr[$menuItemKey]['_MP_PARAM'] ?? false) {
1286  $submenu->MP_array[] = $this->menuArr[$menuItemKey]['_MP_PARAM'];
1287  }
1288  // Especially scripts that build the submenu needs the parent data
1289  $submenu->parent_cObj = ‪$this->parent_cObj;
1290  $submenu->setParentMenu($this->menuArr, $menuItemKey);
1291  // Setting alternativeMenuTempArray (will be effective only if an array and not empty)
1292  if ($altArray !== []) {
1293  $submenu->alternativeMenuTempArray = $altArray;
1294  }
1295  if ($submenu->start(null, $this->sys_page, ‪$uid, $this->conf, $this->menuNumber + 1, $objSuffix, $this->request)) {
1296  $submenu->makeMenu();
1297  // Memorize the current menu item count
1298  $tsfe = $this->‪getTypoScriptFrontendController();
1299  $tempCountMenuObj = $tsfe->register['count_MENUOBJ'];
1300  // Reset the menu item count for the submenu
1301  $tsfe->register['count_MENUOBJ'] = 0;
1302  $content = $submenu->writeMenu();
1303  // Restore the item count now that the submenu has been handled
1304  $tsfe->register['count_MENUOBJ'] = $tempCountMenuObj;
1305  $tsfe->register['count_menuItems'] = count($this->menuArr);
1306  return $content;
1307  }
1308  } catch (‪NoSuchMenuTypeException $e) {
1309  }
1310  }
1311  return '';
1312  }
1313 
1322  protected function ‪isNext(‪$uid, $MPvar)
1323  {
1324  // Check for always active PIDs:
1325  if (in_array((int)‪$uid, $this->alwaysActivePIDlist, true)) {
1326  return true;
1327  }
1328  $testUid = ‪$uid . ($MPvar ? ':' . $MPvar : '');
1329  if (‪$uid && $testUid == $this->nextActive) {
1330  return true;
1331  }
1332  return false;
1333  }
1334 
1342  protected function ‪isActive(array $page, $MPvar)
1343  {
1344  // Check for always active PIDs
1345  ‪$uid = (int)($page['uid'] ?? 0);
1346  if (in_array(‪$uid, $this->alwaysActivePIDlist, true)) {
1347  return true;
1348  }
1349  $testUid = ‪$uid . ($MPvar ? ':' . $MPvar : '');
1350  if (‪$uid && in_array('ITEM:' . $testUid, $this->rL_uidRegister, true)) {
1351  return true;
1352  }
1353  try {
1354  $page = $this->sys_page->resolveShortcutPage($page, $this->disableGroupAccessCheck);
1355  $shortcutPage = (int)($page['_SHORTCUT_ORIGINAL_PAGE_UID'] ?? 0);
1356  if ($shortcutPage) {
1357  if (in_array($shortcutPage, $this->alwaysActivePIDlist, true)) {
1358  return true;
1359  }
1360  $testUid = $shortcutPage . ($MPvar ? ':' . $MPvar : '');
1361  if (in_array('ITEM:' . $testUid, $this->rL_uidRegister, true)) {
1362  return true;
1363  }
1364  }
1365  } catch (\‪Exception $e) {
1366  // Shortcut could not be resolved
1367  return false;
1368  }
1369  return false;
1370  }
1371 
1379  protected function ‪isCurrent(array $page, $MPvar)
1380  {
1381  $testUid = ($page['uid'] ?? 0) . ($MPvar ? ':' . $MPvar : '');
1382  if (($page['uid'] ?? 0) && end($this->rL_uidRegister) === 'ITEM:' . $testUid) {
1383  return true;
1384  }
1385  try {
1386  $page = $this->sys_page->resolveShortcutPage($page);
1387  $shortcutPage = (int)($page['_SHORTCUT_ORIGINAL_PAGE_UID'] ?? 0);
1388  if ($shortcutPage) {
1389  $testUid = $shortcutPage . ($MPvar ? ':' . $MPvar : '');
1390  if (end($this->rL_uidRegister) === 'ITEM:' . $testUid) {
1391  return true;
1392  }
1393  }
1394  } catch (\‪Exception $e) {
1395  // Shortcut could not be resolved
1396  return false;
1397  }
1398  return false;
1399  }
1400 
1408  protected function ‪isSubMenu(‪$uid)
1409  {
1410  $cacheId = $this->‪getCacheIdentifierForSubMenuDecision($uid);
1411  $runtimeCache = $this->‪getRuntimeCache();
1412  $cachedDecision = $runtimeCache->get($cacheId);
1413  if (isset($cachedDecision['result'])) {
1414  return $cachedDecision['result'];
1415  }
1416  // Looking for a mount-pid for this UID since if that
1417  // exists we should look for a subpages THERE and not in the input $uid;
1418  $mount_info = $this->sys_page->getMountPointInfo(‪$uid);
1419  if (is_array($mount_info)) {
1420  ‪$uid = $mount_info['mount_pid'];
1421  }
1422 
1423  // Collect subpages for all pages on current level
1424  $pageIdsOnSameLevel = array_column($this->menuArr, 'uid');
1425  $cacheIdentifierPagesNextLevel = 'menucontentobject-is-submenu-pages-next-level-' . $this->menuNumber . '-' . sha1(json_encode($pageIdsOnSameLevel));
1426  $cachePagesNextLevel = $runtimeCache->get($cacheIdentifierPagesNextLevel);
1427  if (!is_array($cachePagesNextLevel)) {
1428  $cachePagesNextLevel = $this->sys_page->getMenu($pageIdsOnSameLevel, 'uid,pid,doktype,mount_pid,mount_pid_ol,nav_hide,shortcut,shortcut_mode,l18n_cfg');
1429  $runtimeCache->set($cacheIdentifierPagesNextLevel, $cachePagesNextLevel);
1430  }
1431 
1432  $recs = array_filter($cachePagesNextLevel, static fn(array $item) => (int)$item['pid'] === (int)‪$uid);
1433 
1434  $hasSubPages = false;
1435  $bannedUids = $this->‪getBannedUids();
1436  $languageId = $this->‪getCurrentLanguageAspect()->getId();
1437  foreach ($recs as $theRec) {
1438  // no valid subpage if the document type is excluded from the menu
1439  if (in_array((int)($theRec['doktype'] ?? 0), $this->excludedDoktypes, true)) {
1440  continue;
1441  }
1442  // No valid subpage if the page is hidden inside menus and
1443  // it wasn't forced to show such entries
1444  if (isset($theRec['nav_hide']) && $theRec['nav_hide']
1445  && (!isset($this->conf['includeNotInMenu']) || !$this->conf['includeNotInMenu'])
1446  ) {
1447  continue;
1448  }
1449  // No valid subpage if the default language should be shown and the page settings
1450  // are excluding the visibility of the default language
1451  $pageTranslationVisibility = new PageTranslationVisibility((int)($theRec['l18n_cfg'] ?? 0));
1452  if (!$languageId && $pageTranslationVisibility->shouldBeHiddenInDefaultLanguage()) {
1453  continue;
1454  }
1455  // No valid subpage if the alternative language should be shown and the page settings
1456  // are requiring a valid overlay, but it doesn't exist
1457  if ($pageTranslationVisibility->shouldHideTranslationIfNoTranslatedRecordExists() && $languageId > 0 && !isset($theRec['_LOCALIZED_UID'])) {
1458  continue;
1459  }
1460  // No valid subpage if the subpage is banned by excludeUidList (check for default language pages as well)
1461  if (isset($theRec['_LOCALIZED_UID']) && in_array($theRec['_LOCALIZED_UID'], $bannedUids, true)) {
1462  continue;
1463  }
1464  if (in_array((int)($theRec['uid'] ?? 0), $bannedUids, true)) {
1465  continue;
1466  }
1467  $hasSubPages = true;
1468  break;
1469  }
1470  $runtimeCache->set($cacheId, ['result' => $hasSubPages]);
1471  return $hasSubPages;
1472  }
1473 
1474  protected function ‪getCacheIdentifierForSubMenuDecision(‪$uid): string
1475  {
1476  return 'menucontentobject-is-submenu-decision-' . ‪$uid . '-' . (int)($this->conf['includeNotInMenu'] ?? 0);
1477  }
1478 
1487  protected function ‪isItemState($kind, $key)
1488  {
1489  $natVal = false;
1490  // If any value is set for ITEM_STATE the normal evaluation is discarded
1491  if ($this->menuArr[$key]['ITEM_STATE'] ?? false) {
1492  if ((string)$this->menuArr[$key]['ITEM_STATE'] === (string)$kind) {
1493  $natVal = true;
1494  }
1495  } else {
1496  switch ($kind) {
1497  case 'SPC':
1498  $natVal = (bool)$this->menuArr[$key]['isSpacer'];
1499  break;
1500  case 'IFSUB':
1501  $natVal = $this->‪isSubMenu($this->menuArr[$key]['uid'] ?? 0);
1502  break;
1503  case 'ACT':
1504  $natVal = $this->‪isActive(($this->menuArr[$key] ?? []), $this->‪getMPvar($key));
1505  break;
1506  case 'ACTIFSUB':
1507  $natVal = $this->‪isActive(($this->menuArr[$key] ?? []), $this->‪getMPvar($key)) && $this->‪isSubMenu($this->menuArr[$key]['uid']);
1508  break;
1509  case 'CUR':
1510  $natVal = $this->‪isCurrent(($this->menuArr[$key] ?? []), $this->‪getMPvar($key));
1511  break;
1512  case 'CURIFSUB':
1513  $natVal = $this->‪isCurrent(($this->menuArr[$key] ?? []), $this->‪getMPvar($key)) && $this->‪isSubMenu($this->menuArr[$key]['uid']);
1514  break;
1515  case 'USR':
1516  $natVal = (bool)$this->menuArr[$key]['fe_group'];
1517  break;
1518  }
1519  }
1520  return $natVal;
1521  }
1522 
1531  protected function ‪userProcess($mConfKey, $passVar)
1532  {
1533  if ($this->mconf[$mConfKey]) {
1534  $funcConf = (array)($this->mconf[$mConfKey . '.'] ?? []);
1535  $funcConf['parentObj'] = $this;
1536  $passVar = $this->parent_cObj->callUserFunction($this->mconf[$mConfKey], $funcConf, $passVar);
1537  }
1538  return $passVar;
1539  }
1544  protected function ‪setATagParts(?‪LinkResultInterface $linkResult)
1545  {
1546  $this->I['A1'] = $linkResult ? '<a ' . GeneralUtility::implodeAttributes($linkResult->‪getAttributes(), true) . '>' : '';
1547  $this->I['A2'] = $linkResult ? '</a>' : '';
1548  }
1549 
1557  protected function ‪getPageTitle($title, $nav_title)
1558  {
1559  return trim($nav_title) !== '' ? $nav_title : $title;
1560  }
1561 
1569  protected function ‪getMPvar($key)
1570  {
1571  if (‪$GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) {
1572  $localMP_array = ‪$this->MP_array;
1573  // NOTICE: "_MP_PARAM" is allowed to be a commalist of PID pairs!
1574  if ($this->menuArr[$key]['_MP_PARAM'] ?? false) {
1575  $localMP_array[] = $this->menuArr[$key]['_MP_PARAM'];
1576  }
1577  return !empty($localMP_array) ? implode(',', $localMP_array) : '';
1578  }
1579  return '';
1580  }
1581 
1587  protected function ‪getDoktypeExcludeWhere()
1588  {
1589  return !empty($this->excludedDoktypes) ? ' AND pages.doktype NOT IN (' . implode(',', $this->excludedDoktypes) . ')' : '';
1590  }
1591 
1597  protected function ‪getBannedUids()
1598  {
1599  $excludeUidList = (string)$this->parent_cObj->stdWrapValue('excludeUidList', $this->conf);
1600  if (!trim($excludeUidList)) {
1601  return [];
1602  }
1603  $currentPageUid = $this->request->getAttribute('frontend.page.information')->getPageRecord()['uid'] ?? '';
1604  $banUidList = str_replace('current', (string)($currentPageUid), $excludeUidList);
1605  return ‪GeneralUtility::intExplode(',', $banUidList);
1606  }
1607 
1617  protected function ‪menuTypoLink(array $page, string $oTarget, $addParams, $typeOverride, ?int $overridePageId = null): ‪LinkResultInterface
1618  {
1619  ‪$conf = [
1620  'parameter' => $overridePageId ?? $page['uid'] ?? 0,
1621  ];
1622  if (‪MathUtility::canBeInterpretedAsInteger($typeOverride)) {
1623  ‪$conf['parameter'] .= ',' . (int)$typeOverride;
1624  }
1625  if ($addParams) {
1626  ‪$conf['additionalParams'] = $addParams;
1627  }
1628  // Used only for special=language
1629  if ($page['_ADD_GETVARS'] ?? false) {
1630  ‪$conf['addQueryString'] = $page['_ADD_GETVARS'];
1631  ‪$conf['addQueryString.'] = $this->conf['addQueryString.'] ?? [];
1632  }
1633 
1634  // Ensure that the typolink gets an info which language was actually requested. The $page record could be the record
1635  // from page translation language=1 as fallback but page translation language=2 was requested. Search for
1636  // "_REQUESTED_OVERLAY_LANGUAGE" for more details
1637  if (isset($page['_REQUESTED_OVERLAY_LANGUAGE'])) {
1638  ‪$conf['language'] = $page['_REQUESTED_OVERLAY_LANGUAGE'];
1639  }
1640  if ($oTarget) {
1641  ‪$conf['target'] = $oTarget;
1642  }
1643  // $this->I['val'] contains the configuration of the ItemState (e.g. NO / SPC) etc, which should be handed in
1644  // to this method instead of accessed directly in the future.
1645  if (isset($this->I['val']['ATagParams']) || isset($this->I['val']['ATagParams.'])) {
1646  ‪$conf['ATagParams'] = $this->I['val']['ATagParams'] ?? '';
1647  ‪$conf['ATagParams.'] = $this->I['val']['ATagParams.'] ?? [];
1648  }
1649  if ($page['sectionIndex_uid'] ?? false) {
1650  ‪$conf['section'] = $page['sectionIndex_uid'];
1651  }
1652  ‪$conf['page'] = new Page($page);
1653 
1654  $backupData = $this->parent_cObj->data;
1655  $this->parent_cObj->data = $page;
1656  $link = $this->parent_cObj->createLink('|', ‪$conf);
1657  $this->parent_cObj->data = $backupData;
1658 
1659  return $link;
1660  }
1661 
1673  protected function ‪sectionIndex($altSortField, $pid = null)
1674  {
1675  $pid = (int)($pid ?: $this->id);
1676  $basePageRow = $this->sys_page->getPage($pid);
1677  if ($basePageRow === []) {
1678  return [];
1679  }
1680  $useColPos = (int)$this->parent_cObj->stdWrapValue('useColPos', $this->mconf['sectionIndex.'] ?? [], 0);
1681  $selectSetup = [
1682  'pidInList' => $pid,
1683  'orderBy' => $altSortField,
1684  'languageField' => 'sys_language_uid',
1685  'where' => '',
1686  ];
1687 
1688  if ($useColPos >= 0) {
1689  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1690  ->getConnectionForTable('tt_content')
1691  ->getExpressionBuilder();
1692  $selectSetup['where'] = $expressionBuilder->eq('colPos', $useColPos);
1693  }
1694 
1695  if ($basePageRow['content_from_pid'] ?? false) {
1696  // If the page is configured to show content from a referenced page the sectionIndex contains only contents of
1697  // the referenced page
1698  $selectSetup['pidInList'] = $basePageRow['content_from_pid'];
1699  }
1700  $statement = $this->parent_cObj->exec_getQuery('tt_content', $selectSetup);
1701  if (!$statement) {
1702  $message = 'SectionIndex: Query to fetch the content elements failed!';
1703  throw new \UnexpectedValueException($message, 1337334849);
1704  }
1705  ‪$result = [];
1706  while ($row = $statement->fetchAssociative()) {
1707  $this->sys_page->versionOL('tt_content', $row);
1708  if ($this->‪getCurrentLanguageAspect()->doOverlays() && $basePageRow['sys_language_uid'] > 0) {
1709  $languageAspect = new LanguageAspect($basePageRow['sys_language_uid'], $basePageRow['sys_language_uid'], $this->‪getCurrentLanguageAspect()->getOverlayType());
1710  $row = $this->sys_page->getLanguageOverlay(
1711  'tt_content',
1712  $row,
1713  $languageAspect
1714  );
1715  }
1716  if (is_array($row)) {
1717  $sectionIndexType = $this->mconf['sectionIndex.']['type'] ?? '';
1718  if ($sectionIndexType !== 'all') {
1719  $doIncludeInSectionIndex = $row['sectionIndex'] >= 1;
1720  $doHeaderCheck = $sectionIndexType === 'header';
1721  $isValidHeader = ((int)$row['header_layout'] !== 100 || !empty($this->mconf['sectionIndex.']['includeHiddenHeaders'])) && trim($row['header']) !== '';
1722  if (!$doIncludeInSectionIndex || ($doHeaderCheck && !$isValidHeader)) {
1723  continue;
1724  }
1725  }
1726  ‪$uid = $row['uid'] ?? null;
1727  ‪$result[‪$uid] = $basePageRow;
1728  ‪$result[‪$uid]['title'] = $row['header'];
1729  ‪$result[‪$uid]['nav_title'] = $row['header'];
1730  // Prevent false exclusion in filterMenuPages, thus: Always show tt_content records
1731  ‪$result[‪$uid]['nav_hide'] = 0;
1732  ‪$result[‪$uid]['subtitle'] = $row['subheader'] ?? '';
1733  ‪$result[‪$uid]['starttime'] = $row['starttime'] ?? '';
1734  ‪$result[‪$uid]['endtime'] = $row['endtime'] ?? '';
1735  ‪$result[‪$uid]['fe_group'] = $row['fe_group'] ?? '';
1736  ‪$result[‪$uid]['media'] = $row['media'] ?? '';
1737  ‪$result[‪$uid]['header_layout'] = $row['header_layout'] ?? '';
1738  ‪$result[‪$uid]['bodytext'] = $row['bodytext'] ?? '';
1739  ‪$result[‪$uid]['image'] = $row['image'] ?? '';
1740  ‪$result[‪$uid]['sectionIndex_uid'] = ‪$uid;
1741  }
1742  }
1743 
1744  return ‪$result;
1745  }
1746 
1752  public function ‪getSysPage()
1753  {
1754  return ‪$this->sys_page;
1755  }
1756 
1762  public function ‪getParentContentObject()
1763  {
1764  return ‪$this->parent_cObj;
1765  }
1766 
1771  {
1772  $frontendController = $this->parent_cObj->getTypoScriptFrontendController();
1773  if (!$frontendController instanceof ‪TypoScriptFrontendController) {
1774  throw new ‪ContentRenderingException('TypoScriptFrontendController is not available.', 1655725105);
1775  }
1776  return $frontendController;
1777  }
1778 
1779  protected function ‪getCurrentLanguageAspect(): ‪LanguageAspect
1780  {
1781  return GeneralUtility::makeInstance(Context::class)->getAspect('language');
1782  }
1783 
1784  protected function ‪getTimeTracker(): ‪TimeTracker
1785  {
1786  return GeneralUtility::makeInstance(TimeTracker::class);
1787  }
1788 
1789  protected function ‪getCache(): FrontendInterface
1790  {
1791  return GeneralUtility::makeInstance(CacheManager::class)->getCache('hash');
1792  }
1793 
1795  {
1796  return GeneralUtility::makeInstance(CacheManager::class)->getCache('runtime');
1797  }
1798 
1799  protected function ‪getCurrentSite(): ‪Site
1800  {
1801  return $this->request->‪getAttribute('site');
1802  }
1803 
1811  public function ‪setParentMenu(array ‪$menuArr, $menuItemKey)
1812  {
1813  // check if menuArr is a valid array and that menuItemKey matches an existing menuItem in menuArr
1814  if (is_array(‪$menuArr)
1815  && (is_int($menuItemKey) && $menuItemKey >= 0 && isset(‪$menuArr[$menuItemKey]))
1816  ) {
1817  $this->parentMenuArr = ‪$menuArr;
1818  $this->parentMenuArrItemKey = $menuItemKey;
1819  }
1820  }
1821 
1827  protected function ‪hasParentMenuArr()
1828  {
1829  return
1830  $this->menuNumber > 1
1831  && is_array($this->parentMenuArr)
1832  && !empty($this->parentMenuArr)
1833  ;
1834  }
1835 
1839  protected function ‪hasParentMenuItemKey()
1840  {
1841  return $this->parentMenuArrItemKey !== null;
1842  }
1843 
1847  protected function ‪hasParentMenuItem()
1848  {
1849  return
1850  $this->‪hasParentMenuArr()
1851  && $this->‪hasParentMenuItemKey()
1852  && isset($this->‪getParentMenuArr()[$this->parentMenuArrItemKey])
1853  ;
1854  }
1855 
1861  public function ‪getParentMenuArr()
1862  {
1863  return $this->‪hasParentMenuArr() ? $this->parentMenuArr : [];
1864  }
1865 
1871  public function ‪getParentMenuItem()
1872  {
1873  // check if we have a parentMenuItem and if it is an array
1874  if ($this->‪hasParentMenuItem()
1875  && is_array($this->‪getParentMenuArr()[$this->parentMenuArrItemKey])
1876  ) {
1878  }
1879 
1880  return null;
1881  }
1882 
1883  private function ‪getMode(string $mode = ''): string
1884  {
1885  switch ($mode) {
1886  case 'starttime':
1887  $sortField = 'starttime';
1888  break;
1889  case 'lastUpdated':
1890  case 'manual':
1891  $sortField = 'lastUpdated';
1892  break;
1893  case 'tstamp':
1894  $sortField = 'tstamp';
1895  break;
1896  case 'crdate':
1897  $sortField = 'crdate';
1898  break;
1899  default:
1900  $sortField = 'SYS_LASTCHANGED';
1901  }
1902 
1903  return $sortField;
1904  }
1905 
1912  private function ‪getRootlineLevel(array $rootLine, string $list): int
1913  {
1914  $idx = 0;
1915  foreach ($rootLine as $page) {
1916  if (‪GeneralUtility::inList($list, $page['uid'])) {
1917  return $idx;
1918  }
1919  $idx++;
1920  }
1921  return 0;
1922  }
1923 }
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForListMenu
‪array prepareMenuItemsForListMenu($specialValue)
Definition: AbstractMenuContentObject.php:665
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getBannedUids
‪array getBannedUids()
Definition: AbstractMenuContentObject.php:1580
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getParentMenuItem
‪array null getParentMenuItem()
Definition: AbstractMenuContentObject.php:1854
‪TYPO3\CMS\Core\Domain\Page
Definition: Page.php:24
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\menuTypoLink
‪menuTypoLink(array $page, string $oTarget, $addParams, $typeOverride, ?int $overridePageId=null)
Definition: AbstractMenuContentObject.php:1600
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$MP_array
‪string[] $MP_array
Definition: AbstractMenuContentObject.php:85
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$parentMenuArrItemKey
‪int null $parentMenuArrItemKey
Definition: AbstractMenuContentObject.php:152
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\hasParentMenuArr
‪bool hasParentMenuArr()
Definition: AbstractMenuContentObject.php:1810
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getPageTitle
‪string getPageTitle($title, $nav_title)
Definition: AbstractMenuContentObject.php:1540
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\isNext
‪bool isNext($uid, $MPvar)
Definition: AbstractMenuContentObject.php:1305
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\writeMenu
‪string writeMenu()
Definition: AbstractMenuContentObject.php:409
‪TYPO3\CMS\Core\Context\LanguageAspectFactory
Definition: LanguageAspectFactory.php:27
‪TYPO3\CMS\Frontend\ContentObject\Menu\Exception\NoSuchMenuTypeException
Definition: NoSuchMenuTypeException.php:23
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getMode
‪getMode(string $mode='')
Definition: AbstractMenuContentObject.php:1866
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForRootlineMenu
‪array prepareMenuItemsForRootlineMenu()
Definition: AbstractMenuContentObject.php:902
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$parentMenuArr
‪array $parentMenuArr
Definition: AbstractMenuContentObject.php:156
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\hasParentMenuItem
‪hasParentMenuItem()
Definition: AbstractMenuContentObject.php:1830
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$nextActive
‪string $nextActive
Definition: AbstractMenuContentObject.php:114
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getCache
‪getCache()
Definition: AbstractMenuContentObject.php:1772
‪$languages
‪$languages
Definition: updateIsoDatabase.php:104
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$conf
‪array $conf
Definition: AbstractMenuContentObject.php:91
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItems
‪array prepareMenuItems()
Definition: AbstractMenuContentObject.php:447
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$alternativeMenuTempArray
‪array $alternativeMenuTempArray
Definition: AbstractMenuContentObject.php:146
‪TYPO3\CMS\Core\Context\LanguageAspectFactory\createFromSiteLanguage
‪static createFromSiteLanguage(SiteLanguage $language)
Definition: LanguageAspectFactory.php:31
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\sectionIndex
‪array sectionIndex($altSortField, $pid=null)
Definition: AbstractMenuContentObject.php:1656
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForUpdatedMenu
‪array prepareMenuItemsForUpdatedMenu($specialValue, $sortingField)
Definition: AbstractMenuContentObject.php:721
‪TYPO3\CMS\Core\Site\Entity\Site\getAttribute
‪mixed getAttribute(string $attributeName)
Definition: Site.php:353
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\link
‪LinkResultInterface null link($key, $altTarget, $typeOverride)
Definition: AbstractMenuContentObject.php:1172
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForLanguageMenu
‪array prepareMenuItemsForLanguageMenu($specialValue)
Definition: AbstractMenuContentObject.php:540
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$disableGroupAccessCheck
‪bool $disableGroupAccessCheck
Definition: AbstractMenuContentObject.php:158
‪TYPO3\CMS\Core\Type\Bitmask\PageTranslationVisibility
Definition: PageTranslationVisibility.php:30
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\hasParentMenuItemKey
‪hasParentMenuItemKey()
Definition: AbstractMenuContentObject.php:1822
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject
Definition: AbstractMenuContentObject.php:53
‪TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException
Definition: ContentRenderingException.php:24
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForUserSpecificMenu
‪array prepareMenuItemsForUserSpecificMenu($specialValue, $sortingField)
Definition: AbstractMenuContentObject.php:524
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\filterMenuPages
‪bool filterMenuPages(&$data, $banUidArray, $isSpacerPage)
Definition: AbstractMenuContentObject.php:1074
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$result
‪array $result
Definition: AbstractMenuContentObject.php:128
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\userProcess
‪mixed userProcess($mConfKey, $passVar)
Definition: AbstractMenuContentObject.php:1514
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:42
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getDoktypeExcludeWhere
‪string getDoktypeExcludeWhere()
Definition: AbstractMenuContentObject.php:1570
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$sys_page
‪PageRepository $sys_page
Definition: AbstractMenuContentObject.php:101
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$I
‪mixed[] $I
Definition: AbstractMenuContentObject.php:139
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$menuArr
‪array[] $menuArr
Definition: AbstractMenuContentObject.php:120
‪TYPO3\CMS\Frontend\Exception
Definition: Exception.php:23
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\generate
‪generate()
Definition: AbstractMenuContentObject.php:396
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\isCurrent
‪bool isCurrent(array $page, $MPvar)
Definition: AbstractMenuContentObject.php:1362
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getCacheIdentifierForSubMenuDecision
‪getCacheIdentifierForSubMenuDecision($uid)
Definition: AbstractMenuContentObject.php:1457
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getRootlineLevel
‪int getRootlineLevel(array $rootLine, string $list)
Definition: AbstractMenuContentObject.php:1895
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$parent_cObj
‪ContentObjectRenderer $parent_cObj
Definition: AbstractMenuContentObject.php:79
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getRuntimeCache
‪getRuntimeCache()
Definition: AbstractMenuContentObject.php:1777
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\subMenu
‪string subMenu(int $uid, string $objSuffix, int $menuItemKey)
Definition: AbstractMenuContentObject.php:1249
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SPACER
‪const DOKTYPE_SPACER
Definition: PageRepository.php:103
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getTypoScriptFrontendController
‪getTypoScriptFrontendController()
Definition: AbstractMenuContentObject.php:1753
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_BE_USER_SECTION
‪const DOKTYPE_BE_USER_SECTION
Definition: PageRepository.php:101
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$id
‪int $id
Definition: AbstractMenuContentObject.php:107
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SYSFOLDER
‪const DOKTYPE_SYSFOLDER
Definition: PageRepository.php:104
‪TYPO3\CMS\Core\Context\LanguageAspect
Definition: LanguageAspect.php:57
‪TYPO3\CMS\Frontend\Event\FilterMenuItemsEvent
Definition: FilterMenuItemsEvent.php:27
‪TYPO3\CMS\Frontend\ContentObject\Menu
Definition: AbstractMenuContentObject.php:16
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForBrowseMenu
‪array prepareMenuItemsForBrowseMenu($specialValue, $sortingField, $additionalWhere)
Definition: AbstractMenuContentObject.php:957
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\makeMenu
‪makeMenu()
Definition: AbstractMenuContentObject.php:324
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\setATagParts
‪setATagParts(?LinkResultInterface $linkResult)
Definition: AbstractMenuContentObject.php:1527
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\customItemStates
‪const customItemStates
Definition: AbstractMenuContentObject.php:160
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$entryLevel
‪int $entryLevel
Definition: AbstractMenuContentObject.php:63
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\setParentMenu
‪setParentMenu(array $menuArr, $menuItemKey)
Definition: AbstractMenuContentObject.php:1794
‪TYPO3\CMS\Core\TypoScript\TypoScriptService
Definition: TypoScriptService.php:27
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getCurrentLanguageAspect
‪getCurrentLanguageAspect()
Definition: AbstractMenuContentObject.php:1762
‪TYPO3\CMS\Core\Resource\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\isActive
‪bool isActive(array $page, $MPvar)
Definition: AbstractMenuContentObject.php:1325
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$rL_uidRegister
‪array $rL_uidRegister
Definition: AbstractMenuContentObject.php:135
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:58
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getTimeTracker
‪getTimeTracker()
Definition: AbstractMenuContentObject.php:1767
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getCurrentSite
‪getCurrentSite()
Definition: AbstractMenuContentObject.php:1782
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$menuNumber
‪int $menuNumber
Definition: AbstractMenuContentObject.php:57
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForKeywordsMenu
‪array prepareMenuItemsForKeywordsMenu($specialValue, $sortingField)
Definition: AbstractMenuContentObject.php:785
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Core\Utility\GeneralUtility\inList
‪static bool inList($list, $item)
Definition: GeneralUtility.php:422
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getParentContentObject
‪ContentObjectRenderer getParentContentObject()
Definition: AbstractMenuContentObject.php:1745
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$hash
‪string $hash
Definition: AbstractMenuContentObject.php:124
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\processItemStates
‪array processItemStates($splitCount)
Definition: AbstractMenuContentObject.php:1131
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$excludedDoktypes
‪int[] $excludedDoktypes
Definition: AbstractMenuContentObject.php:69
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:69
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$mconf
‪array $mconf
Definition: AbstractMenuContentObject.php:97
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getSysPage
‪PageRepository getSysPage()
Definition: AbstractMenuContentObject.php:1735
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$alwaysActivePIDlist
‪int[] $alwaysActivePIDlist
Definition: AbstractMenuContentObject.php:73
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange(mixed $theInt, int $min, int $max=2000000000, int $defaultValue=0)
Definition: MathUtility.php:34
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\start
‪bool start($_, $sys_page, $id, $conf, int $menuNumber, string $objSuffix, ServerRequestInterface $request)
Definition: AbstractMenuContentObject.php:188
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\removeInaccessiblePages
‪removeInaccessiblePages(array $pages)
Definition: AbstractMenuContentObject.php:417
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getParentMenuArr
‪array getParentMenuArr()
Definition: AbstractMenuContentObject.php:1844
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static list< int > intExplode(string $delimiter, string $string, bool $removeEmptyValues=false)
Definition: GeneralUtility.php:756
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:34
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$request
‪ServerRequestInterface $request
Definition: AbstractMenuContentObject.php:141
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForDirectoryMenu
‪array prepareMenuItemsForDirectoryMenu($specialValue, $sortingField)
Definition: AbstractMenuContentObject.php:624
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getMPvar
‪string getMPvar($key)
Definition: AbstractMenuContentObject.php:1552
‪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\ContentObject\Menu\AbstractMenuContentObject\isItemState
‪bool isItemState($kind, $key)
Definition: AbstractMenuContentObject.php:1470
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\buildPageRepository
‪buildPageRepository(LanguageAspect $languageAspect=null)
Definition: AbstractMenuContentObject.php:603
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\isSubMenu
‪bool isSubMenu($uid)
Definition: AbstractMenuContentObject.php:1391