‪TYPO3CMS  9.5
AbstractMenuContentObject.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 
36 
45 {
48 
49  protected ‪$deprecatedPublicProperties = [
50  'menuNumber' => 'Using $menuNumber of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
51  'entryLevel' => 'Using $entryLevel of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
52  'spacerIDList' => 'Using $spacerIDList of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
53  'doktypeExcludeList' => 'Using $doktypeExcludeList of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
54  'alwaysActivePIDlist' => 'Using $alwaysActivePIDlist of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
55  'imgNamePrefix' => 'Using $imgNamePrefix of cObject HMENU is discouraged, as all graphical-related functionality will be removed in TYPO3 v10.0.',
56  'imgNameNotRandom' => 'Using $imgNameNotRandom of cObject HMENU is discouraged, as all graphical-related functionality will be removed in TYPO3 v10.0.',
57  'debug' => 'Using $debug of cObject HMENU from the outside is discouraged, as this variable is not in use anymore and will be removed in TYPO3 v10.0.',
58  'GMENU_fixKey' => 'Using $GMENU_fixKey of cObject HMENU is discouraged, as all graphical-related functionality will be removed in TYPO3 v10.0.',
59  'MP_array' => 'Using $MP_array of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
60  'conf' => 'Using $conf of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
61  'mconf' => 'Using $mconf of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
62  'tmpl' => 'Using $tmpl of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
63  'sys_page' => 'Using $sys_page of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
64  'id' => 'Using $id of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
65  'nextActive' => 'Using $nextActive of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
66  'menuArr' => 'Using $menuArr of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
67  'hash' => 'Using $hash of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
68  'result' => 'Using $result of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
69  'rL_uidRegister' => 'Using $rL_uidRegister of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
70  'INPfixMD5' => 'Using $INPfixMD5 of cObject HMENU is discouraged, as all graphical-related functionality will be removed in TYPO3 v10.0.',
71  'I' => 'Using $I of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
72  'WMresult' => 'Using $WMresult of cObject HMENU is discouraged, as all graphical-related functionality will be removed in TYPO3 v10.0.',
73  'WMfreezePrefix' => 'Using $WMfreezePrefix of cObject HMENU is discouraged, as all graphical-related functionality will be removed in TYPO3 v10.0.',
74  'WMmenuItems' => 'Using $WMmenuItems of cObject HMENU is discouraged, as all graphical-related functionality will be removed in TYPO3 v10.0.',
75  'WMsubmenuObjSuffixes' => 'Using $WMsubmenuObjSuffixes of cObject HMENU is discouraged, as all graphical-related functionality will be removed in TYPO3 v10.0.',
76  'WMextraScript' => 'Using $WMextraScript of cObject HMENU is discouraged, as all graphical-related functionality will be removed in TYPO3 v10.0.',
77  'WMcObj' => 'Using $WMcObj of cObject HMENU is discouraged, as all graphical-related functionality will be removed in TYPO3 v10.0.',
78  'alternativeMenuTempArray' => 'Using $alternativeMenuTempArray of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
79  'nameAttribute' => 'Using $nameAttribute of cObject HMENU from the outside is discouraged, as this variable is only used for internal storage.',
80  ];
81 
82  protected ‪$deprecatedPublicMethods = [
83  'subMenu' => 'Using subMenu() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
84  'link' => 'Using link() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
85  'procesItemStates' => 'Using procesItemStates() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
86  'changeLinksForAccessRestrictedPages' => 'Using changeLinksForAccessRestrictedPages() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
87  'isNext' => 'Using isNext() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
88  'isActive' => 'Using isActive() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
89  'isCurrent' => 'Using isCurrent() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
90  'isSubMenu' => 'Using isSubMenu() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
91  'isItemState' => 'Using isItemState() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
92  'accessKey' => 'Using accessKey() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
93  'userProcess' => 'Using userProcess() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
94  'setATagParts' => 'Using setATagParts() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
95  'getPageTitle' => 'Using getPageTitle() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
96  'getMPvar' => 'Using getMPvar() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
97  'getDoktypeExcludeWhere' => 'Using getDoktypeExcludeWhere() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
98  'getBannedUids' => 'Using getBannedUids() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
99  'menuTypoLink' => 'Using menuTypoLink() within HMENU is discouraged, as this is internal functionality that should not be exposed to the public.',
100  'extProc_RO' => 'Using extProc_RO() within HMENU extensions is discouraged, as rollover functionality will be removed in TYPO3 v10.0.',
101  'extProc_init' => 'Using extProc_init() within HMENU extensions is discouraged, as extending HMENU should only happens via userFunc options.',
102  'extProc_beforeLinking' => 'Using extProc_beforeLinking() within HMENU extensions is discouraged, as extending HMENU should only happens via userFunc options.',
103  'extProc_afterLinking' => 'Using extProc_afterLinking() within HMENU extensions is discouraged, as extending HMENU should only happens via userFunc options.',
104  'extProc_beforeAllWrap' => 'Using extProc_beforeAllWrap() within HMENU extensions is discouraged, as extending HMENU should only happens via userFunc options.',
105  'extProc_finish' => 'Using extProc_finish() within HMENU extensions is discouraged, as extending HMENU should only happens via userFunc options.',
106  'getBeforeAfter' => 'Using getBeforeAfter() within HMENU extensions is discouraged, as extending HMENU should only happens via userFunc options.',
107  ];
108 
114  protected ‪$menuNumber = 1;
115 
121  protected ‪$entryLevel = 0;
122 
128  protected ‪$spacerIDList = '199';
129 
135  protected ‪$doktypeExcludeList = '6';
136 
140  protected ‪$alwaysActivePIDlist = [];
141 
145  protected ‪$imgNamePrefix = 'img';
146 
150  protected ‪$imgNameNotRandom = 0;
151 
155  protected ‪$debug = false;
156 
162  public ‪$parent_cObj;
163 
167  protected ‪$GMENU_fixKey = 'gmenu';
168 
174  protected ‪$MP_array = [];
175 
181  protected ‪$conf = [];
182 
188  protected ‪$mconf = [];
189 
193  protected ‪$tmpl;
194 
198  protected ‪$sys_page;
199 
205  protected ‪$id;
206 
213  protected ‪$nextActive;
214 
220  protected ‪$menuArr;
221 
225  protected ‪$hash;
226 
230  protected ‪$result = [];
231 
238  protected ‪$rL_uidRegister;
239 
243  protected ‪$INPfixMD5;
244 
248  protected ‪$I;
249 
253  protected ‪$WMresult;
254 
258  protected ‪$WMfreezePrefix;
259 
263  protected ‪$WMmenuItems;
264 
268  protected ‪$WMsubmenuObjSuffixes;
269 
273  protected ‪$WMextraScript;
274 
278  protected ‪$WMcObj;
279 
285  protected ‪$alternativeMenuTempArray = '';
286 
292  protected ‪$nameAttribute = 'name';
293 
300  protected ‪$useCacheHash = false;
301 
307  protected ‪$parentMenuArrItemKey;
308 
312  protected ‪$parentMenuArr;
313 
326  public function ‪start(‪$tmpl, ‪$sys_page, ‪$id, ‪$conf, ‪$menuNumber, $objSuffix = '')
327  {
328  $tsfe = $this->‪getTypoScriptFrontendController();
329  // Init:
330  $this->conf = ‪$conf;
331  $this->menuNumber = ‪$menuNumber;
332  $this->mconf = ‪$conf[$this->menuNumber . $objSuffix . '.'];
333  $this->‪debug = !empty($tsfe->config['config']['debug']);
334  $this->WMcObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
335  // In XHTML and HTML5 there is no "name" attribute anymore
336  switch ($tsfe->xhtmlDoctype) {
337  case 'xhtml_strict':
338  // intended fall-through
339  case 'xhtml_11':
340  // intended fall-through
341  case 'html5':
342  // intended fall-through
343  case '':
344  // empty means that it's HTML5 by default
345  $this->nameAttribute = 'id';
346  break;
347  default:
348  $this->nameAttribute = 'name';
349  }
350  // Sets the internal vars. $tmpl MUST be the template-object. $sys_page MUST be the sys_page object
351  if ($this->conf[$this->menuNumber . $objSuffix] && is_object(‪$tmpl) && is_object(‪$sys_page)) {
352  $this->tmpl = ‪$tmpl;
353  $this->sys_page = ‪$sys_page;
354  // alwaysActivePIDlist initialized:
355  if (trim($this->conf['alwaysActivePIDlist']) || isset($this->conf['alwaysActivePIDlist.'])) {
356  if (isset($this->conf['alwaysActivePIDlist.'])) {
357  $this->conf['alwaysActivePIDlist'] = $this->parent_cObj->stdWrap(
358  $this->conf['alwaysActivePIDlist'],
359  $this->conf['alwaysActivePIDlist.']
360  );
361  }
362  $this->alwaysActivePIDlist = GeneralUtility::intExplode(',', $this->conf['alwaysActivePIDlist']);
363  }
364  // includeNotInMenu initialized:
365  $includeNotInMenu = $this->conf['includeNotInMenu'];
366  $includeNotInMenuConf = $this->conf['includeNotInMenu.'] ?? null;
367  $this->conf['includeNotInMenu'] = is_array($includeNotInMenuConf)
368  ? $this->parent_cObj->stdWrap($includeNotInMenu, $includeNotInMenuConf)
369  : $includeNotInMenu;
370  // 'not in menu' doktypes
371  if ($this->conf['excludeDoktypes']) {
372  $this->doktypeExcludeList = implode(',', GeneralUtility::intExplode(',', $this->conf['excludeDoktypes']));
373  }
374  // EntryLevel
375  $this->entryLevel = $this->parent_cObj->getKey(
376  isset(‪$conf['entryLevel.']) ? $this->parent_cObj->stdWrap(
377  ‪$conf['entryLevel'],
378  ‪$conf['entryLevel.']
379  ) : ‪$conf['entryLevel'],
380  $this->tmpl->rootLine
381  );
382  // Set parent page: If $id not stated with start() then the base-id will be found from rootLine[$this->entryLevel]
383  // Called as the next level in a menu. It is assumed that $this->MP_array is set from parent menu.
384  if (‪$id) {
385  $this->id = (int)‪$id;
386  } else {
387  // This is a BRAND NEW menu, first level. So we take ID from rootline and also find MP_array (mount points)
388  $this->id = (int)$this->tmpl->rootLine[$this->entryLevel]['uid'];
389  // Traverse rootline to build MP_array of pages BEFORE the entryLevel
390  // (MP var for ->id is picked up in the next part of the code...)
391  foreach ($this->tmpl->rootLine as ‪$entryLevel => $levelRec) {
392  // For overlaid mount points, set the variable right now:
393  if ($levelRec['_MP_PARAM'] && $levelRec['_MOUNT_OL']) {
394  $this->MP_array[] = $levelRec['_MP_PARAM'];
395  }
396  // Break when entry level is reached:
397  if (‪$entryLevel >= $this->entryLevel) {
398  break;
399  }
400  // For normal mount points, set the variable for next level.
401  if ($levelRec['_MP_PARAM'] && !$levelRec['_MOUNT_OL']) {
402  $this->MP_array[] = $levelRec['_MP_PARAM'];
403  }
404  }
405  }
406  // Return FALSE if no page ID was set (thus no menu of subpages can be made).
407  if ($this->id <= 0) {
408  return false;
409  }
410  // Check if page is a mount point, and if so set id and MP_array
411  // (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...)
412  $mount_info = $this->sys_page->getMountPointInfo($this->id);
413  if (is_array($mount_info)) {
414  $this->MP_array[] = $mount_info['MPvar'];
415  $this->id = $mount_info['mount_pid'];
416  }
417  // Gather list of page uids in root line (for "isActive" evaluation). Also adds the MP params in the path so Mount Points are respected.
418  // (List is specific for this rootline, so it may be supplied from parent menus for speed...)
419  if ($this->rL_uidRegister === null) {
420  $this->rL_uidRegister = [];
421  $rl_MParray = [];
422  foreach ($this->tmpl->rootLine as $v_rl) {
423  // For overlaid mount points, set the variable right now:
424  if ($v_rl['_MP_PARAM'] && $v_rl['_MOUNT_OL']) {
425  $rl_MParray[] = $v_rl['_MP_PARAM'];
426  }
427  // Add to register:
428  $this->rL_uidRegister[] = 'ITEM:' . $v_rl['uid'] .
429  (
430  !empty($rl_MParray)
431  ? ':' . implode(',', $rl_MParray)
432  : ''
433  );
434  // For normal mount points, set the variable for next level.
435  if ($v_rl['_MP_PARAM'] && !$v_rl['_MOUNT_OL']) {
436  $rl_MParray[] = $v_rl['_MP_PARAM'];
437  }
438  }
439  }
440  // Set $directoryLevel so the following evalution of the nextActive will not return
441  // an invalid value if .special=directory was set
442  $directoryLevel = 0;
443  if ($this->conf['special'] === 'directory') {
444  $value = isset($this->conf['special.']['value.']) ? $this->parent_cObj->stdWrap(
445  $this->conf['special.']['value'],
446  $this->conf['special.']['value.']
447  ) : $this->conf['special.']['value'];
448  if ($value === '') {
449  $value = $tsfe->page['uid'];
450  }
451  $directoryLevel = (int)$tsfe->tmpl->getRootlineLevel($value);
452  }
453  // 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
454  // Notice: The automatic expansion of a menu is designed to work only when no "special" modes (except "directory") are used.
455  $startLevel = $directoryLevel ?: ‪$this->entryLevel;
456  $currentLevel = $startLevel + ‪$this->menuNumber;
457  if (is_array($this->tmpl->rootLine[$currentLevel])) {
458  $nextMParray = ‪$this->MP_array;
459  if (empty($nextMParray) && !$this->tmpl->rootLine[$currentLevel]['_MOUNT_OL'] && $currentLevel > 0) {
460  // Make sure to slide-down any mount point information (_MP_PARAM) to children records in the rootline
461  // otherwise automatic expansion will not work
462  $parentRecord = $this->tmpl->rootLine[$currentLevel - 1];
463  if (isset($parentRecord['_MP_PARAM'])) {
464  $nextMParray[] = $parentRecord['_MP_PARAM'];
465  }
466  }
467  // In overlay mode, add next level MPvars as well:
468  if ($this->tmpl->rootLine[$currentLevel]['_MOUNT_OL']) {
469  $nextMParray[] = $this->tmpl->rootLine[$currentLevel]['_MP_PARAM'];
470  }
471  $this->nextActive = $this->tmpl->rootLine[$currentLevel]['uid'] .
472  (
473  !empty($nextMParray)
474  ? ':' . implode(',', $nextMParray)
475  : ''
476  );
477  } else {
478  $this->nextActive = '';
479  }
480  // imgNamePrefix
481  if ($this->mconf['imgNamePrefix']) {
482  $this->imgNamePrefix = $this->mconf['imgNamePrefix'];
483  }
484  $this->imgNameNotRandom = $this->mconf['imgNameNotRandom'];
485  $retVal = true;
486  } else {
487  $this->‪getTimeTracker()->‪setTSlogMessage('ERROR in menu', 3);
488  $retVal = false;
489  }
490  return $retVal;
491  }
492 
499  public function ‪makeMenu()
500  {
501  if (!$this->id) {
502  return;
503  }
504 
505  $this->useCacheHash = false;
506 
507  // Initializing showAccessRestrictedPages
508  $SAVED_where_groupAccess = '';
509  if ($this->mconf['showAccessRestrictedPages']) {
510  // SAVING where_groupAccess
511  $SAVED_where_groupAccess = $this->sys_page->where_groupAccess;
512  // Temporarily removing fe_group checking!
513  $this->sys_page->where_groupAccess = '';
514  }
515 
516  $menuItems = $this->‪prepareMenuItems();
517 
518  $c = 0;
519  $c_b = 0;
520  $minItems = (int)($this->mconf['minItems'] ?: $this->conf['minItems']);
521  $maxItems = (int)($this->mconf['maxItems'] ?: $this->conf['maxItems']);
522  $begin = $this->parent_cObj->calc($this->mconf['begin'] ? $this->mconf['begin'] : $this->conf['begin']);
523  $minItemsConf = $this->mconf['minItems.'] ?? $this->conf['minItems.'] ?? null;
524  $minItems = is_array($minItemsConf) ? $this->parent_cObj->stdWrap($minItems, $minItemsConf) : $minItems;
525  $maxItemsConf = $this->mconf['maxItems.'] ?? $this->conf['maxItems.'] ?? null;
526  $maxItems = is_array($maxItemsConf) ? $this->parent_cObj->stdWrap($maxItems, $maxItemsConf) : $maxItems;
527  $beginConf = $this->mconf['begin.'] ?? $this->conf['begin.'] ?? null;
528  $begin = is_array($beginConf) ? $this->parent_cObj->stdWrap($begin, $beginConf) : $begin;
529  $banUidArray = $this->‪getBannedUids();
530  // Fill in the menuArr with elements that should go into the menu:
531  $this->menuArr = [];
532  foreach ($menuItems as $data) {
533  $spacer = GeneralUtility::inList($this->spacerIDList, $data['doktype']) || $data['ITEM_STATE'] === 'SPC';
534  // if item is a spacer, $spacer is set
535  if ($this->‪filterMenuPages($data, $banUidArray, $spacer)) {
536  $c_b++;
537  // If the beginning item has been reached.
538  if ($begin <= $c_b) {
539  $this->menuArr[$c] = $this->‪determineOriginalShortcutPage($data);
540  $this->menuArr[$c]['isSpacer'] = $spacer;
541  $c++;
542  if ($maxItems && $c >= $maxItems) {
543  break;
544  }
545  }
546  }
547  }
548  // Fill in fake items, if min-items is set.
549  if ($minItems) {
550  while ($c < $minItems) {
551  $this->menuArr[$c] = [
552  'title' => '...',
553  'uid' => $this->‪getTypoScriptFrontendController()->id
554  ];
555  $c++;
556  }
557  }
558  // Passing the menuArr through a user defined function:
559  if ($this->mconf['itemArrayProcFunc']) {
560  $this->menuArr = $this->‪userProcess('itemArrayProcFunc', $this->menuArr);
561  }
562  // Setting number of menu items
563  $this->‪getTypoScriptFrontendController()->register['count_menuItems'] = count($this->menuArr);
564  $this->hash = md5(
565  serialize($this->menuArr) .
566  serialize($this->mconf) .
567  serialize($this->tmpl->rootLine) .
568  serialize($this->MP_array)
569  );
570  // Get the cache timeout:
571  if ($this->conf['cache_period']) {
572  $cacheTimeout = $this->conf['cache_period'];
573  } else {
574  $cacheTimeout = $this->‪getTypoScriptFrontendController()->‪get_cache_timeout();
575  }
576  $cache = $this->‪getCache();
577  $cachedData = $cache->get($this->hash);
578  if (!is_array($cachedData)) {
579  $this->‪generate();
580  $cache->set($this->hash, $this->result, ['ident_MENUDATA'], (int)$cacheTimeout);
581  } else {
582  $this->result = $cachedData;
583  }
584  // End showAccessRestrictedPages
585  if ($this->mconf['showAccessRestrictedPages']) {
586  // RESTORING where_groupAccess
587  $this->sys_page->where_groupAccess = $SAVED_where_groupAccess;
588  }
589  }
590 
596  public function ‪generate()
597  {
598  }
599 
603  public function ‪writeMenu()
604  {
605  return '';
606  }
607 
614  protected function ‪removeInaccessiblePages(array $pages)
615  {
616  $banned = $this->‪getBannedUids();
617  $filteredPages = [];
618  foreach ($pages as $aPage) {
619  if ($this->‪filterMenuPages($aPage, $banned, $aPage['doktype'] === ‪PageRepository::DOKTYPE_SPACER)) {
620  $filteredPages[$aPage['uid']] = $aPage;
621  }
622  }
623  return $filteredPages;
624  }
625 
631  protected function ‪prepareMenuItems()
632  {
633  $menuItems = [];
634  $alternativeSortingField = trim($this->mconf['alternativeSortingField']) ?: 'sorting';
635 
636  // Additional where clause, usually starts with AND (as usual with all additionalWhere functionality in TS)
637  $additionalWhere = $this->mconf['additionalWhere'] ?? '';
638  if (isset($this->mconf['additionalWhere.'])) {
639  $additionalWhere = $this->parent_cObj->stdWrap($additionalWhere, $this->mconf['additionalWhere.']);
640  }
641 
642  // ... only for the FIRST level of a HMENU
643  if ($this->menuNumber == 1 && $this->conf['special']) {
644  $value = isset($this->conf['special.']['value.'])
645  ? $this->parent_cObj->stdWrap($this->conf['special.']['value'], $this->conf['special.']['value.'])
646  : $this->conf['special.']['value'];
647  switch ($this->conf['special']) {
648  case 'userfunction':
649  $menuItems = $this->‪prepareMenuItemsForUserSpecificMenu($value, $alternativeSortingField);
650  break;
651  case 'language':
652  $menuItems = $this->‪prepareMenuItemsForLanguageMenu($value);
653  break;
654  case 'directory':
655  $menuItems = $this->‪prepareMenuItemsForDirectoryMenu($value, $alternativeSortingField);
656  break;
657  case 'list':
658  $menuItems = $this->prepareMenuItemsForListMenu($value);
659  break;
660  case 'updated':
661  $menuItems = $this->‪prepareMenuItemsForUpdatedMenu(
662  $value,
663  $this->mconf['alternativeSortingField'] ?: false
664  );
665  break;
666  case 'keywords':
667  $menuItems = $this->‪prepareMenuItemsForKeywordsMenu(
668  $value,
669  $this->mconf['alternativeSortingField'] ?: false
670  );
671  break;
672  case 'categories':
674  $categoryMenuUtility = GeneralUtility::makeInstance(CategoryMenuUtility::class);
675  $menuItems = $categoryMenuUtility->collectPages($value, $this->conf['special.'], $this);
676  break;
677  case 'rootline':
678  $menuItems = $this->‪prepareMenuItemsForRootlineMenu();
679  break;
680  case 'browse':
681  $menuItems = $this->‪prepareMenuItemsForBrowseMenu($value, $alternativeSortingField, $additionalWhere);
682  break;
683  }
684  if ($this->mconf['sectionIndex']) {
685  $sectionIndexes = [];
686  foreach ($menuItems as $page) {
687  $sectionIndexes = $sectionIndexes + $this->‪sectionIndex($alternativeSortingField, $page['uid']);
688  }
689  $menuItems = $sectionIndexes;
690  }
691  } elseif (is_array($this->alternativeMenuTempArray)) {
692  // Setting $menuItems array if not level 1.
694  } elseif ($this->mconf['sectionIndex']) {
695  $menuItems = $this->‪sectionIndex($alternativeSortingField);
696  } else {
697  // Default: Gets a hierarchical menu based on subpages of $this->id
698  $menuItems = $this->sys_page->getMenu($this->id, '*', $alternativeSortingField, $additionalWhere);
699  }
700  return $menuItems;
701  }
702 
710  protected function ‪prepareMenuItemsForUserSpecificMenu($specialValue, $sortingField)
711  {
712  $menuItems = $this->parent_cObj->callUserFunction(
713  $this->conf['special.']['userFunc'],
714  array_merge($this->conf['special.'], ['value' => $specialValue, '_altSortField' => $sortingField]),
715  ''
716  );
717  if (!is_array($menuItems)) {
718  $menuItems = [];
719  }
720  return $menuItems;
721  }
722 
729  protected function ‪prepareMenuItemsForLanguageMenu($specialValue)
730  {
731  $menuItems = [];
732  // Getting current page record NOT overlaid by any translation:
733  $tsfe = $this->‪getTypoScriptFrontendController();
734  $currentPageWithNoOverlay = $this->sys_page->getRawRecord('pages', $tsfe->page['uid']);
735 
736  if ($specialValue === 'auto') {
737  $site = $this->‪getCurrentSite();
738  $languages = $site->getLanguages();
739  ‪$languageItems = array_keys($languages);
740  } else {
741  ‪$languageItems = GeneralUtility::intExplode(',', $specialValue);
742  }
743 
744  $tsfe->register['languages_HMENU'] = implode(',', ‪$languageItems);
745 
746  $currentLanguageId = $this->‪getCurrentLanguageAspect()->getId();
747 
748  foreach (‪$languageItems as $sUid) {
749  // Find overlay record:
750  if ($sUid) {
751  $lRecs = $this->sys_page->getPageOverlay($tsfe->page['uid'], $sUid);
752  } else {
753  $lRecs = [];
754  }
755  // Checking if the "disabled" state should be set.
756  if (GeneralUtility::hideIfNotTranslated($tsfe->page['l18n_cfg']) && $sUid &&
757  empty($lRecs) || GeneralUtility::hideIfDefaultLanguage($tsfe->page['l18n_cfg']) &&
758  (!$sUid || empty($lRecs)) ||
759  !$this->conf['special.']['normalWhenNoLanguage'] && $sUid && empty($lRecs)
760  ) {
761  $iState = $currentLanguageId == $sUid ? 'USERDEF2' : 'USERDEF1';
762  } else {
763  $iState = $currentLanguageId == $sUid ? 'ACT' : 'NO';
764  }
765  if ($this->conf['addQueryString']) {
766  $getVars = $this->parent_cObj->getQueryArguments(
767  $this->conf['addQueryString.'],
768  ['L' => $sUid],
769  true
770  );
771  $this->‪analyzeCacheHashRequirements($getVars);
772  } else {
773  $getVars = '&L=' . $sUid;
774  }
775  // Adding menu item:
776  $menuItems[] = array_merge(
777  array_merge($currentPageWithNoOverlay, $lRecs),
778  [
779  'ITEM_STATE' => $iState,
780  '_ADD_GETVARS' => $getVars,
781  '_SAFE' => true
782  ]
783  );
784  }
785  return $menuItems;
786  }
787 
795  protected function ‪prepareMenuItemsForDirectoryMenu($specialValue, $sortingField)
796  {
797  $tsfe = $this->‪getTypoScriptFrontendController();
798  $menuItems = [];
799  if ($specialValue == '') {
800  $specialValue = $tsfe->page['uid'];
801  }
802  $items = GeneralUtility::intExplode(',', $specialValue);
803  $pageLinkBuilder = GeneralUtility::makeInstance(PageLinkBuilder::class, $this->parent_cObj);
804  foreach ($items as ‪$id) {
805  $MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps(‪$id);
806  // Checking if a page is a mount page and if so, change the ID and set the MP var properly.
807  $mount_info = $this->sys_page->getMountPointInfo(‪$id);
808  if (is_array($mount_info)) {
809  if ($mount_info['overlay']) {
810  // Overlays should already have their full MPvars calculated:
811  $MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps((int)$mount_info['mount_pid']);
812  $MP = $MP ? $MP : $mount_info['MPvar'];
813  } else {
814  $MP = ($MP ? $MP . ',' : '') . $mount_info['MPvar'];
815  }
816  ‪$id = $mount_info['mount_pid'];
817  }
818  // Get sub-pages:
819  $statement = $this->parent_cObj->exec_getQuery('pages', ['pidInList' => ‪$id, 'orderBy' => $sortingField]);
820  while ($row = $statement->fetch()) {
821  // When the site language configuration is in "free" mode, then the page without overlay is fetched
822  // (which is kind-of strange for pages, but this is what exec_getQuery() is doing)
823  // this means, that $row is a translated page, but hasn't been overlaid. For this reason, we fetch
824  // the default translation page again, (which does a ->getPageOverlay() again - doing this on a
825  // translated page would result in no record at all)
826  if ($row['l10n_parent'] > 0 && !isset($row['_PAGES_OVERLAY'])) {
827  $row = $this->sys_page->getPage($row['l10n_parent'], true);
828  }
829  $tsfe->sys_page->versionOL('pages', $row, true);
830  if (!empty($row)) {
831  // Keep mount point?
832  $mount_info = $this->sys_page->getMountPointInfo($row['uid'], $row);
833  // There is a valid mount point.
834  if (is_array($mount_info) && $mount_info['overlay']) {
835  // Using "getPage" is OK since we need the check for enableFields
836  // AND for type 2 of mount pids we DO require a doktype < 200!
837  $mp_row = $this->sys_page->getPage($mount_info['mount_pid']);
838  if (!empty($mp_row)) {
839  $row = $mp_row;
840  $row['_MP_PARAM'] = $mount_info['MPvar'];
841  } else {
842  // If the mount point could not be fetched with respect
843  // to enableFields, unset the row so it does not become a part of the menu!
844  unset($row);
845  }
846  }
847  // Add external MP params, then the row:
848  if (!empty($row)) {
849  if ($MP) {
850  $row['_MP_PARAM'] = $MP . ($row['_MP_PARAM'] ? ',' . $row['_MP_PARAM'] : '');
851  }
852  $menuItems[] = $this->sys_page->getPageOverlay($row);
853  }
854  }
855  }
856  }
857 
858  return $menuItems;
859  }
860 
867  protected function prepareMenuItemsForListMenu($specialValue)
868  {
869  $menuItems = [];
870  if ($specialValue == '') {
871  $specialValue = ‪$this->id;
872  }
873  $skippedEnableFields = [];
874  if (!empty($this->mconf['showAccessRestrictedPages'])) {
875  $skippedEnableFields = ['fe_group' => 1];
876  }
878  $loadDB = GeneralUtility::makeInstance(RelationHandler::class);
879  $loadDB->setFetchAllFields(true);
880  $loadDB->start($specialValue, 'pages');
881  $loadDB->additionalWhere['pages'] = $this->sys_page->enableFields('pages', -1, $skippedEnableFields);
882  $loadDB->getFromDB();
883  $pageLinkBuilder = GeneralUtility::makeInstance(PageLinkBuilder::class, $this->parent_cObj);
884  foreach ($loadDB->itemArray as $val) {
885  $MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps((int)$val['id']);
886  // Keep mount point?
887  $mount_info = $this->sys_page->getMountPointInfo($val['id']);
888  // There is a valid mount point.
889  if (is_array($mount_info) && $mount_info['overlay']) {
890  // Using "getPage" is OK since we need the check for enableFields
891  // AND for type 2 of mount pids we DO require a doktype < 200!
892  $mp_row = $this->sys_page->getPage($mount_info['mount_pid']);
893  if (!empty($mp_row)) {
894  $row = $mp_row;
895  $row['_MP_PARAM'] = $mount_info['MPvar'];
896  // Overlays should already have their full MPvars calculated
897  if ($mount_info['overlay']) {
898  $MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps((int)$mount_info['mount_pid']);
899  if ($MP) {
900  unset($row['_MP_PARAM']);
901  }
902  }
903  } else {
904  // If the mount point could not be fetched with respect to
905  // enableFields, unset the row so it does not become a part of the menu!
906  unset($row);
907  }
908  } else {
909  $row = $loadDB->results['pages'][$val['id']];
910  }
911  // Add versioning overlay for current page (to respect workspaces)
912  if (isset($row) && is_array($row)) {
913  $this->sys_page->versionOL('pages', $row, true);
914  }
915  // Add external MP params, then the row:
916  if (isset($row) && is_array($row)) {
917  if ($MP) {
918  $row['_MP_PARAM'] = $MP . ($row['_MP_PARAM'] ? ',' . $row['_MP_PARAM'] : '');
919  }
920  $menuItems[] = $this->sys_page->getPageOverlay($row);
921  }
922  }
923  return $menuItems;
924  }
925 
933  protected function ‪prepareMenuItemsForUpdatedMenu($specialValue, $sortingField)
934  {
935  $tsfe = $this->‪getTypoScriptFrontendController();
936  $menuItems = [];
937  if ($specialValue == '') {
938  $specialValue = $tsfe->page['uid'];
939  }
940  $items = GeneralUtility::intExplode(',', $specialValue);
941  if (‪MathUtility::canBeInterpretedAsInteger($this->conf['special.']['depth'])) {
942  $depth = ‪MathUtility::forceIntegerInRange($this->conf['special.']['depth'], 1, 20);
943  } else {
944  $depth = 20;
945  }
946  // Max number of items
947  $limit = ‪MathUtility::forceIntegerInRange($this->conf['special.']['limit'], 0, 100);
948  $maxAge = (int)$this->parent_cObj->calc($this->conf['special.']['maxAge']);
949  if (!$limit) {
950  $limit = 10;
951  }
952  // *'auto', 'manual', 'tstamp'
953  $mode = $this->conf['special.']['mode'];
954  // Get id's
955  $beginAtLevel = ‪MathUtility::forceIntegerInRange($this->conf['special.']['beginAtLevel'], 0, 100);
956  $id_list_arr = [];
957  foreach ($items as ‪$id) {
958  // Exclude the current ID if beginAtLevel is > 0
959  if ($beginAtLevel > 0) {
960  $id_list_arr[] = $this->parent_cObj->getTreeList(‪$id, $depth - 1 + $beginAtLevel, $beginAtLevel - 1);
961  } else {
962  $id_list_arr[] = $this->parent_cObj->getTreeList(-1 * ‪$id, $depth - 1 + $beginAtLevel, $beginAtLevel - 1);
963  }
964  }
965  $id_list = implode(',', $id_list_arr);
966  // Get sortField (mode)
967  switch ($mode) {
968  case 'starttime':
969  $sortField = 'starttime';
970  break;
971  case 'lastUpdated':
972  case 'manual':
973  $sortField = 'lastUpdated';
974  break;
975  case 'tstamp':
976  $sortField = 'tstamp';
977  break;
978  case 'crdate':
979  $sortField = 'crdate';
980  break;
981  default:
982  $sortField = 'SYS_LASTCHANGED';
983  }
984  $extraWhere = ($this->conf['includeNotInMenu'] ? '' : ' AND pages.nav_hide=0') . $this->‪getDoktypeExcludeWhere();
985  if ($this->conf['special.']['excludeNoSearchPages']) {
986  $extraWhere .= ' AND pages.no_search=0';
987  }
988  if ($maxAge > 0) {
989  $extraWhere .= ' AND ' . $sortField . '>' . (‪$GLOBALS['SIM_ACCESS_TIME'] - $maxAge);
990  }
991  $statement = $this->parent_cObj->exec_getQuery('pages', [
992  'pidInList' => '0',
993  'uidInList' => $id_list,
994  'where' => $sortField . '>=0' . $extraWhere,
995  'orderBy' => $sortingField ?: $sortField . ' DESC',
996  'max' => $limit
997  ]);
998  while ($row = $statement->fetch()) {
999  // When the site language configuration is in "free" mode, then the page without overlay is fetched
1000  // (which is kind-of strange for pages, but this is what exec_getQuery() is doing)
1001  // this means, that $row is a translated page, but hasn't been overlaid. For this reason, we fetch
1002  // the default translation page again, (which does a ->getPageOverlay() again - doing this on a
1003  // translated page would result in no record at all)
1004  if ($row['l10n_parent'] > 0 && !isset($row['_PAGES_OVERLAY'])) {
1005  $row = $this->sys_page->getPage($row['l10n_parent'], true);
1006  }
1007  $tsfe->sys_page->versionOL('pages', $row, true);
1008  if (is_array($row)) {
1009  $menuItems[$row['uid']] = $this->sys_page->getPageOverlay($row);
1010  }
1011  }
1012 
1013  return $menuItems;
1014  }
1015 
1023  protected function ‪prepareMenuItemsForKeywordsMenu($specialValue, $sortingField)
1024  {
1025  $tsfe = $this->‪getTypoScriptFrontendController();
1026  $menuItems = [];
1027  [$specialValue] = GeneralUtility::intExplode(',', $specialValue);
1028  if (!$specialValue) {
1029  $specialValue = $tsfe->page['uid'];
1030  }
1031  if ($this->conf['special.']['setKeywords'] || $this->conf['special.']['setKeywords.']) {
1032  $kw = isset($this->conf['special.']['setKeywords.']) ? $this->parent_cObj->stdWrap($this->conf['special.']['setKeywords'], $this->conf['special.']['setKeywords.']) : $this->conf['special.']['setKeywords'];
1033  } else {
1034  // The page record of the 'value'.
1035  $value_rec = $this->sys_page->getPage($specialValue);
1036  $kfieldSrc = $this->conf['special.']['keywordsField.']['sourceField'] ? $this->conf['special.']['keywordsField.']['sourceField'] : 'keywords';
1037  // keywords.
1038  $kw = trim($this->parent_cObj->keywords($value_rec[$kfieldSrc]));
1039  }
1040  // *'auto', 'manual', 'tstamp'
1041  $mode = $this->conf['special.']['mode'];
1042  switch ($mode) {
1043  case 'starttime':
1044  $sortField = 'starttime';
1045  break;
1046  case 'lastUpdated':
1047  case 'manual':
1048  $sortField = 'lastUpdated';
1049  break;
1050  case 'tstamp':
1051  $sortField = 'tstamp';
1052  break;
1053  case 'crdate':
1054  $sortField = 'crdate';
1055  break;
1056  default:
1057  $sortField = 'SYS_LASTCHANGED';
1058  }
1059  // Depth, limit, extra where
1060  if (‪MathUtility::canBeInterpretedAsInteger($this->conf['special.']['depth'])) {
1061  $depth = ‪MathUtility::forceIntegerInRange($this->conf['special.']['depth'], 0, 20);
1062  } else {
1063  $depth = 20;
1064  }
1065  // Max number of items
1066  $limit = ‪MathUtility::forceIntegerInRange($this->conf['special.']['limit'], 0, 100);
1067  // Start point
1068  $eLevel = $this->parent_cObj->getKey(
1069  isset($this->conf['special.']['entryLevel.'])
1070  ? $this->parent_cObj->stdWrap($this->conf['special.']['entryLevel'], $this->conf['special.']['entryLevel.'])
1071  : $this->conf['special.']['entryLevel'],
1072  $this->tmpl->rootLine
1073  );
1074  $startUid = (int)$this->tmpl->rootLine[$eLevel]['uid'];
1075  // Which field is for keywords
1076  $kfield = 'keywords';
1077  if ($this->conf['special.']['keywordsField']) {
1078  [$kfield] = explode(' ', trim($this->conf['special.']['keywordsField']));
1079  }
1080  // If there are keywords and the startuid is present
1081  if ($kw && $startUid) {
1082  $bA = ‪MathUtility::forceIntegerInRange($this->conf['special.']['beginAtLevel'], 0, 100);
1083  $id_list = $this->parent_cObj->getTreeList(-1 * $startUid, $depth - 1 + $bA, $bA - 1);
1084  $kwArr = GeneralUtility::trimExplode(',', $kw, true);
1085  $keyWordsWhereArr = [];
1086  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1087  foreach ($kwArr as $word) {
1088  $keyWordsWhereArr[] = $queryBuilder->expr()->like(
1089  $kfield,
1090  $queryBuilder->createNamedParameter(
1091  '%' . $queryBuilder->escapeLikeWildcards($word) . '%',
1092  \PDO::PARAM_STR
1093  )
1094  );
1095  }
1096  $queryBuilder
1097  ->select('*')
1098  ->from('pages')
1099  ->where(
1100  $queryBuilder->expr()->in(
1101  'uid',
1102  GeneralUtility::intExplode(',', $id_list, true)
1103  ),
1104  $queryBuilder->expr()->neq(
1105  'uid',
1106  $queryBuilder->createNamedParameter($specialValue, \PDO::PARAM_INT)
1107  )
1108  );
1109 
1110  if (count($keyWordsWhereArr) !== 0) {
1111  $queryBuilder->andWhere($queryBuilder->expr()->orX(...$keyWordsWhereArr));
1112  }
1113 
1114  if ($this->doktypeExcludeList) {
1115  $queryBuilder->andWhere(
1116  $queryBuilder->expr()->notIn(
1117  'pages.doktype',
1118  GeneralUtility::intExplode(',', $this->doktypeExcludeList, true)
1119  )
1120  );
1121  }
1123  if (!$this->conf['includeNotInMenu']) {
1124  $queryBuilder->andWhere($queryBuilder->expr()->eq('pages.nav_hide', 0));
1125  }
1126 
1127  if ($this->conf['special.']['excludeNoSearchPages']) {
1128  $queryBuilder->andWhere($queryBuilder->expr()->eq('pages.no_search', 0));
1129  }
1130 
1131  if ($limit > 0) {
1132  $queryBuilder->setMaxResults($limit);
1133  }
1134 
1135  if ($sortingField) {
1136  $queryBuilder->orderBy($sortingField);
1137  } else {
1138  $queryBuilder->orderBy($sortField, 'desc');
1139  }
1140 
1141  ‪$result = $queryBuilder->execute();
1142  while ($row = ‪$result->fetch()) {
1143  $tsfe->sys_page->versionOL('pages', $row, true);
1144  if (is_array($row)) {
1145  $menuItems[$row['uid']] = $this->sys_page->getPageOverlay($row);
1146  }
1147  }
1148  }
1149 
1150  return $menuItems;
1151  }
1152 
1158  protected function ‪prepareMenuItemsForRootlineMenu()
1159  {
1160  $menuItems = [];
1161  $range = isset($this->conf['special.']['range.'])
1162  ? $this->parent_cObj->stdWrap($this->conf['special.']['range'], $this->conf['special.']['range.'])
1163  : $this->conf['special.']['range'];
1164  $begin_end = explode('|', $range);
1165  $begin_end[0] = (int)$begin_end[0];
1166  if (!‪MathUtility::canBeInterpretedAsInteger($begin_end[1])) {
1167  $begin_end[1] = -1;
1168  }
1169  $beginKey = $this->parent_cObj->getKey($begin_end[0], $this->tmpl->rootLine);
1170  $endKey = $this->parent_cObj->getKey($begin_end[1], $this->tmpl->rootLine);
1171  if ($endKey < $beginKey) {
1172  $endKey = $beginKey;
1173  }
1174  $rl_MParray = [];
1175  foreach ($this->tmpl->rootLine as $k_rl => $v_rl) {
1176  // For overlaid mount points, set the variable right now:
1177  if ($v_rl['_MP_PARAM'] && $v_rl['_MOUNT_OL']) {
1178  $rl_MParray[] = $v_rl['_MP_PARAM'];
1179  }
1180  // Traverse rootline:
1181  if ($k_rl >= $beginKey && $k_rl <= $endKey) {
1182  $temp_key = $k_rl;
1183  $menuItems[$temp_key] = $this->sys_page->getPage($v_rl['uid']);
1184  if (!empty($menuItems[$temp_key])) {
1185  // If there are no specific target for the page, put the level specific target on.
1186  if (!$menuItems[$temp_key]['target']) {
1187  $menuItems[$temp_key]['target'] = $this->conf['special.']['targets.'][$k_rl];
1188  $menuItems[$temp_key]['_MP_PARAM'] = implode(',', $rl_MParray);
1189  }
1190  } else {
1191  unset($menuItems[$temp_key]);
1192  }
1193  }
1194  // For normal mount points, set the variable for next level.
1195  if ($v_rl['_MP_PARAM'] && !$v_rl['_MOUNT_OL']) {
1196  $rl_MParray[] = $v_rl['_MP_PARAM'];
1197  }
1198  }
1199  // Reverse order of elements (e.g. "1,2,3,4" gets "4,3,2,1"):
1200  if (isset($this->conf['special.']['reverseOrder']) && $this->conf['special.']['reverseOrder']) {
1201  $menuItems = array_reverse($menuItems);
1202  }
1203  return $menuItems;
1204  }
1205 
1214  protected function ‪prepareMenuItemsForBrowseMenu($specialValue, $sortingField, $additionalWhere)
1215  {
1216  $menuItems = [];
1217  [$specialValue] = GeneralUtility::intExplode(',', $specialValue);
1218  if (!$specialValue) {
1219  $specialValue = $this->‪getTypoScriptFrontendController()->page['uid'];
1220  }
1221  // Will not work out of rootline
1222  if ($specialValue != $this->tmpl->rootLine[0]['uid']) {
1223  $recArr = [];
1224  // The page record of the 'value'.
1225  $value_rec = $this->sys_page->getPage($specialValue);
1226  // 'up' page cannot be outside rootline
1227  if ($value_rec['pid']) {
1228  // The page record of 'up'.
1229  $recArr['up'] = $this->sys_page->getPage($value_rec['pid']);
1230  }
1231  // If the 'up' item was NOT level 0 in rootline...
1232  if ($recArr['up']['pid'] && $value_rec['pid'] != $this->tmpl->rootLine[0]['uid']) {
1233  // The page record of "index".
1234  $recArr['index'] = $this->sys_page->getPage($recArr['up']['pid']);
1235  }
1236  // check if certain pages should be excluded
1237  $additionalWhere .= ($this->conf['includeNotInMenu'] ? '' : ' AND pages.nav_hide=0') . $this->‪getDoktypeExcludeWhere();
1238  if ($this->conf['special.']['excludeNoSearchPages']) {
1239  $additionalWhere .= ' AND pages.no_search=0';
1240  }
1241  // prev / next is found
1242  $prevnext_menu = $this->‪removeInaccessiblePages($this->sys_page->getMenu($value_rec['pid'], '*', $sortingField, $additionalWhere));
1243  $lastKey = 0;
1244  ‪$nextActive = 0;
1245  foreach ($prevnext_menu as $k_b => $v_b) {
1246  if (‪$nextActive) {
1247  $recArr['next'] = $v_b;
1248  ‪$nextActive = 0;
1249  }
1250  if ($v_b['uid'] == $specialValue) {
1251  if ($lastKey) {
1252  $recArr['prev'] = $prevnext_menu[$lastKey];
1253  }
1254  ‪$nextActive = 1;
1255  }
1256  $lastKey = $k_b;
1257  }
1258 
1259  $recArr['first'] = reset($prevnext_menu);
1260  $recArr['last'] = end($prevnext_menu);
1261  // prevsection / nextsection is found
1262  // You can only do this, if there is a valid page two levels up!
1263  if (!empty($recArr['index']['uid'])) {
1264  $prevnextsection_menu = $this->‪removeInaccessiblePages($this->sys_page->getMenu($recArr['index']['uid'], '*', $sortingField, $additionalWhere));
1265  $lastKey = 0;
1266  ‪$nextActive = 0;
1267  foreach ($prevnextsection_menu as $k_b => $v_b) {
1268  if (‪$nextActive) {
1269  $sectionRec_temp = $this->‪removeInaccessiblePages($this->sys_page->getMenu($v_b['uid'], '*', $sortingField, $additionalWhere));
1270  if (!empty($sectionRec_temp)) {
1271  $recArr['nextsection'] = reset($sectionRec_temp);
1272  $recArr['nextsection_last'] = end($sectionRec_temp);
1273  ‪$nextActive = 0;
1274  }
1275  }
1276  if ($v_b['uid'] == $value_rec['pid']) {
1277  if ($lastKey) {
1278  $sectionRec_temp = $this->‪removeInaccessiblePages($this->sys_page->getMenu($prevnextsection_menu[$lastKey]['uid'], '*', $sortingField, $additionalWhere));
1279  if (!empty($sectionRec_temp)) {
1280  $recArr['prevsection'] = reset($sectionRec_temp);
1281  $recArr['prevsection_last'] = end($sectionRec_temp);
1282  }
1283  }
1284  ‪$nextActive = 1;
1285  }
1286  $lastKey = $k_b;
1287  }
1288  }
1289  if ($this->conf['special.']['items.']['prevnextToSection']) {
1290  if (!is_array($recArr['prev']) && is_array($recArr['prevsection_last'])) {
1291  $recArr['prev'] = $recArr['prevsection_last'];
1292  }
1293  if (!is_array($recArr['next']) && is_array($recArr['nextsection'])) {
1294  $recArr['next'] = $recArr['nextsection'];
1295  }
1296  }
1297  $items = explode('|', $this->conf['special.']['items']);
1298  $c = 0;
1299  foreach ($items as $k_b => $v_b) {
1300  $v_b = strtolower(trim($v_b));
1301  if ((int)$this->conf['special.'][$v_b . '.']['uid']) {
1302  $recArr[$v_b] = $this->sys_page->getPage((int)$this->conf['special.'][$v_b . '.']['uid']);
1303  }
1304  if (is_array($recArr[$v_b])) {
1305  $menuItems[$c] = $recArr[$v_b];
1306  if ($this->conf['special.'][$v_b . '.']['target']) {
1307  $menuItems[$c]['target'] = $this->conf['special.'][$v_b . '.']['target'];
1308  }
1309  $tmpSpecialFields = $this->conf['special.'][$v_b . '.']['fields.'];
1310  if (is_array($tmpSpecialFields)) {
1311  foreach ($tmpSpecialFields as $fk => $val) {
1312  $menuItems[$c][$fk] = $val;
1313  }
1314  }
1315  $c++;
1316  }
1317  }
1318  }
1319  return $menuItems;
1320  }
1321 
1327  protected function ‪analyzeCacheHashRequirements($queryString)
1328  {
1329  $parameters = GeneralUtility::explodeUrl2Array($queryString);
1330  if (!empty($parameters)) {
1331  if (!isset($parameters['id'])) {
1332  $queryString .= '&id=' . $this->‪getTypoScriptFrontendController()->id;
1333  }
1335  $cacheHashCalculator = GeneralUtility::makeInstance(CacheHashCalculator::class);
1336  $cHashParameters = $cacheHashCalculator->getRelevantParameters($queryString);
1337  if (count($cHashParameters) > 1) {
1338  $this->useCacheHash = (
1339  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['disableNoCacheParameter'] ||
1340  !isset($parameters['no_cache']) ||
1341  !$parameters['no_cache']
1342  );
1343  }
1344  }
1345  }
1346 
1358  public function ‪filterMenuPages(&$data, $banUidArray, $spacer)
1359  {
1360  $includePage = true;
1361  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/tslib/class.tslib_menu.php']['filterMenuPages'] ?? [] as $className) {
1362  $hookObject = GeneralUtility::makeInstance($className);
1363  if (!$hookObject instanceof AbstractMenuFilterPagesHookInterface) {
1364  throw new \UnexpectedValueException($className . ' must implement interface ' . AbstractMenuFilterPagesHookInterface::class, 1269877402);
1365  }
1366  $includePage = $includePage && $hookObject->processFilter($data, $banUidArray, $spacer, $this);
1367  }
1368  if (!$includePage) {
1369  return false;
1370  }
1371  if ($data['_SAFE']) {
1372  return true;
1373  }
1374 
1375  if (
1376  ($this->mconf['SPC'] || !$spacer) // If the spacer-function is not enabled, spacers will not enter the $menuArr
1377  && (!$data['nav_hide'] || $this->conf['includeNotInMenu']) // Not hidden in navigation
1378  && !GeneralUtility::inList($this->doktypeExcludeList, $data['doktype']) // Page may not be 'not_in_menu' or 'Backend User Section'
1379  && !in_array($data['uid'], $banUidArray, false) // not in banned uid's
1380  ) {
1381  // Checking if a page should be shown in the menu depending on whether a translation exists or if the default language is disabled
1382  if ($this->sys_page->isPageSuitableForLanguage($data, $this->getCurrentLanguageAspect())) {
1383  // Checking if "&L" should be modified so links to non-accessible pages will not happen.
1384  if ($this->‪getCurrentLanguageAspect()->getId() > 0 && $this->conf['protectLvar']) {
1385  if ($this->conf['protectLvar'] === 'all' || GeneralUtility::hideIfNotTranslated($data['l18n_cfg'])) {
1386  $olRec = $this->sys_page->getPageOverlay($data['uid'], $this->‪getCurrentLanguageAspect()->getId());
1387  if (empty($olRec)) {
1388  // If no page translation record then page can NOT be accessed in
1389  // the language pointed to by "&L" and therefore we protect the link by setting "&L=0"
1390  $data['_ADD_GETVARS'] .= '&L=0';
1391  }
1392  }
1393  }
1394  return true;
1395  }
1396  }
1397  return false;
1398  }
1399 
1413  protected function ‪procesItemStates($splitCount)
1414  {
1415  // Prepare normal settings
1416  if (!is_array($this->mconf['NO.']) && $this->mconf['NO']) {
1417  // Setting a blank array if NO=1 and there are no properties.
1418  $this->mconf['NO.'] = [];
1419  }
1420  $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
1421  $NOconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['NO.'], $splitCount);
1422  // Prepare rollOver settings, overriding normal settings
1423  $ROconf = [];
1424  if ($this->mconf['RO']) {
1425  $ROconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['RO.'], $splitCount);
1426  }
1427  // Prepare IFSUB settings, overriding normal settings
1428  // IFSUB is TRUE if there exist submenu items to the current item
1429  if (!empty($this->mconf['IFSUB'])) {
1430  $IFSUBconf = null;
1431  $IFSUBROconf = null;
1432  foreach ($NOconf as $key => $val) {
1433  if ($this->‪isItemState('IFSUB', $key)) {
1434  // if this is the first IFSUB element, we must generate IFSUB.
1435  if ($IFSUBconf === null) {
1436  $IFSUBconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['IFSUB.'], $splitCount);
1437  if (!empty($this->mconf['IFSUBRO'])) {
1438  $IFSUBROconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['IFSUBRO.'], $splitCount);
1439  }
1440  }
1441  // Substitute normal with ifsub
1442  if (isset($IFSUBconf[$key])) {
1443  $NOconf[$key] = $IFSUBconf[$key];
1444  }
1445  // If rollOver on normal, we must apply a state for rollOver on the active
1446  if ($ROconf) {
1447  // If RollOver on active then apply this
1448  $ROconf[$key] = !empty($IFSUBROconf[$key]) ? $IFSUBROconf[$key] : $IFSUBconf[$key];
1449  }
1450  }
1451  }
1452  }
1453  // Prepare active settings, overriding normal settings
1454  if (!empty($this->mconf['ACT'])) {
1455  $ACTconf = null;
1456  $ACTROconf = null;
1457  // Find active
1458  foreach ($NOconf as $key => $val) {
1459  if ($this->‪isItemState('ACT', $key)) {
1460  // If this is the first 'active', we must generate ACT.
1461  if ($ACTconf === null) {
1462  $ACTconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['ACT.'], $splitCount);
1463  // Prepare active rollOver settings, overriding normal active settings
1464  if (!empty($this->mconf['ACTRO'])) {
1465  $ACTROconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['ACTRO.'], $splitCount);
1466  }
1467  }
1468  // Substitute normal with active
1469  if (isset($ACTconf[$key])) {
1470  $NOconf[$key] = $ACTconf[$key];
1471  }
1472  // If rollOver on normal, we must apply a state for rollOver on the active
1473  if ($ROconf) {
1474  // If RollOver on active then apply this
1475  $ROconf[$key] = !empty($ACTROconf[$key]) ? $ACTROconf[$key] : $ACTconf[$key];
1476  }
1477  }
1478  }
1479  }
1480  // Prepare ACT (active)/IFSUB settings, overriding normal settings
1481  // ACTIFSUB is TRUE if there exist submenu items to the current item and the current item is active
1482  if (!empty($this->mconf['ACTIFSUB'])) {
1483  $ACTIFSUBconf = null;
1484  $ACTIFSUBROconf = null;
1485  // Find active
1486  foreach ($NOconf as $key => $val) {
1487  if ($this->‪isItemState('ACTIFSUB', $key)) {
1488  // If this is the first 'active', we must generate ACTIFSUB.
1489  if ($ACTIFSUBconf === null) {
1490  $ACTIFSUBconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['ACTIFSUB.'], $splitCount);
1491  // Prepare active rollOver settings, overriding normal active settings
1492  if (!empty($this->mconf['ACTIFSUBRO'])) {
1493  $ACTIFSUBROconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['ACTIFSUBRO.'], $splitCount);
1494  }
1495  }
1496  // Substitute normal with active
1497  if (isset($ACTIFSUBconf[$key])) {
1498  $NOconf[$key] = $ACTIFSUBconf[$key];
1499  }
1500  // If rollOver on normal, we must apply a state for rollOver on the active
1501  if ($ROconf) {
1502  // If RollOver on active then apply this
1503  $ROconf[$key] = !empty($ACTIFSUBROconf[$key]) ? $ACTIFSUBROconf[$key] : $ACTIFSUBconf[$key];
1504  }
1505  }
1506  }
1507  }
1508  // Prepare CUR (current) settings, overriding normal settings
1509  // CUR is TRUE if the current page equals the item here!
1510  if (!empty($this->mconf['CUR'])) {
1511  $CURconf = null;
1512  $CURROconf = null;
1513  foreach ($NOconf as $key => $val) {
1514  if ($this->‪isItemState('CUR', $key)) {
1515  // if this is the first 'current', we must generate CUR. Basically this control is just inherited
1516  // from the other implementations as current would only exist one time and that's it
1517  // (unless you use special-features of HMENU)
1518  if ($CURconf === null) {
1519  $CURconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['CUR.'], $splitCount);
1520  if (!empty($this->mconf['CURRO'])) {
1521  $CURROconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['CURRO.'], $splitCount);
1522  }
1523  }
1524  // Substitute normal with current
1525  if (isset($CURconf[$key])) {
1526  $NOconf[$key] = $CURconf[$key];
1527  }
1528  // If rollOver on normal, we must apply a state for rollOver on the active
1529  if ($ROconf) {
1530  // If RollOver on active then apply this
1531  $ROconf[$key] = !empty($CURROconf[$key]) ? $CURROconf[$key] : $CURconf[$key];
1532  }
1533  }
1534  }
1535  }
1536  // Prepare CUR (current)/IFSUB settings, overriding normal settings
1537  // CURIFSUB is TRUE if there exist submenu items to the current item and the current page equals the item here!
1538  if (!empty($this->mconf['CURIFSUB'])) {
1539  $CURIFSUBconf = null;
1540  $CURIFSUBROconf = null;
1541  foreach ($NOconf as $key => $val) {
1542  if ($this->‪isItemState('CURIFSUB', $key)) {
1543  // If this is the first 'current', we must generate CURIFSUB.
1544  if ($CURIFSUBconf === null) {
1545  $CURIFSUBconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['CURIFSUB.'], $splitCount);
1546  // Prepare current rollOver settings, overriding normal current settings
1547  if (!empty($this->mconf['CURIFSUBRO'])) {
1548  $CURIFSUBROconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['CURIFSUBRO.'], $splitCount);
1549  }
1550  }
1551  // Substitute normal with active
1552  if ($CURIFSUBconf[$key]) {
1553  $NOconf[$key] = $CURIFSUBconf[$key];
1554  }
1555  // If rollOver on normal, we must apply a state for rollOver on the current
1556  if ($ROconf) {
1557  // If RollOver on current then apply this
1558  $ROconf[$key] = !empty($CURIFSUBROconf[$key]) ? $CURIFSUBROconf[$key] : $CURIFSUBconf[$key];
1559  }
1560  }
1561  }
1562  }
1563  // Prepare active settings, overriding normal settings
1564  if (!empty($this->mconf['USR'])) {
1565  $USRconf = null;
1566  $USRROconf = null;
1567  // Find active
1568  foreach ($NOconf as $key => $val) {
1569  if ($this->‪isItemState('USR', $key)) {
1570  // if this is the first active, we must generate USR.
1571  if ($USRconf === null) {
1572  $USRconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['USR.'], $splitCount);
1573  // Prepare active rollOver settings, overriding normal active settings
1574  if (!empty($this->mconf['USRRO'])) {
1575  $USRROconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['USRRO.'], $splitCount);
1576  }
1577  }
1578  // Substitute normal with active
1579  if ($USRconf[$key]) {
1580  $NOconf[$key] = $USRconf[$key];
1581  }
1582  // If rollOver on normal, we must apply a state for rollOver on the active
1583  if ($ROconf) {
1584  // If RollOver on active then apply this
1585  $ROconf[$key] = !empty($USRROconf[$key]) ? $USRROconf[$key] : $USRconf[$key];
1586  }
1587  }
1588  }
1589  }
1590  // Prepare spacer settings, overriding normal settings
1591  if (!empty($this->mconf['SPC'])) {
1592  $SPCconf = null;
1593  // Find spacers
1594  foreach ($NOconf as $key => $val) {
1595  if ($this->‪isItemState('SPC', $key)) {
1596  // If this is the first spacer, we must generate SPC.
1597  if ($SPCconf === null) {
1598  $SPCconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['SPC.'], $splitCount);
1599  }
1600  // Substitute normal with spacer
1601  if (isset($SPCconf[$key])) {
1602  $NOconf[$key] = $SPCconf[$key];
1603  }
1604  }
1605  }
1606  }
1607  // Prepare Userdefined settings
1608  if (!empty($this->mconf['USERDEF1'])) {
1609  $USERDEF1conf = null;
1610  $USERDEF1ROconf = null;
1611  // Find active
1612  foreach ($NOconf as $key => $val) {
1613  if ($this->‪isItemState('USERDEF1', $key)) {
1614  // If this is the first active, we must generate USERDEF1.
1615  if ($USERDEF1conf === null) {
1616  $USERDEF1conf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['USERDEF1.'], $splitCount);
1617  // Prepare active rollOver settings, overriding normal active settings
1618  if (!empty($this->mconf['USERDEF1RO'])) {
1619  $USERDEF1ROconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['USERDEF1RO.'], $splitCount);
1620  }
1621  }
1622  // Substitute normal with active
1623  if (isset($USERDEF1conf[$key])) {
1624  $NOconf[$key] = $USERDEF1conf[$key];
1625  }
1626  // If rollOver on normal, we must apply a state for rollOver on the active
1627  if ($ROconf) {
1628  // If RollOver on active then apply this
1629  $ROconf[$key] = !empty($USERDEF1ROconf[$key]) ? $USERDEF1ROconf[$key] : $USERDEF1conf[$key];
1630  }
1631  }
1632  }
1633  }
1634  // Prepare Userdefined settings
1635  if (!empty($this->mconf['USERDEF2'])) {
1636  $USERDEF2conf = null;
1637  $USERDEF2ROconf = null;
1638  // Find active
1639  foreach ($NOconf as $key => $val) {
1640  if ($this->‪isItemState('USERDEF2', $key)) {
1641  // If this is the first active, we must generate USERDEF2.
1642  if ($USERDEF2conf === null) {
1643  $USERDEF2conf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['USERDEF2.'], $splitCount);
1644  // Prepare active rollOver settings, overriding normal active settings
1645  if (!empty($this->mconf['USERDEF2RO'])) {
1646  $USERDEF2ROconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['USERDEF2RO.'], $splitCount);
1647  }
1648  }
1649  // Substitute normal with active
1650  if (isset($USERDEF2conf[$key])) {
1651  $NOconf[$key] = $USERDEF2conf[$key];
1652  }
1653  // If rollOver on normal, we must apply a state for rollOver on the active
1654  if ($ROconf) {
1655  // If RollOver on active then apply this
1656  $ROconf[$key] = !empty($USERDEF2ROconf[$key]) ? $USERDEF2ROconf[$key] : $USERDEF2conf[$key];
1657  }
1658  }
1659  }
1660  }
1661  return [$NOconf, $ROconf];
1662  }
1663 
1673  protected function ‪link($key, $altTarget = '', $typeOverride = '')
1674  {
1675  $runtimeCache = $this->‪getRuntimeCache();
1676  $MP_var = $this->‪getMPvar($key);
1677  $cacheId = 'menu-generated-links-' . md5($key . $altTarget . $typeOverride . $MP_var . serialize($this->menuArr[$key]));
1678  $runtimeCachedLink = $runtimeCache->get($cacheId);
1679  if ($runtimeCachedLink !== false) {
1680  return $runtimeCachedLink;
1681  }
1682 
1683  // Mount points:
1684  $MP_params = $MP_var ? '&MP=' . rawurlencode($MP_var) : '';
1685  // Setting override ID
1686  if ($this->mconf['overrideId'] || $this->menuArr[$key]['overrideId']) {
1687  $overrideArray = [];
1688  // If a user script returned the value overrideId in the menu array we use that as page id
1689  $overrideArray['uid'] = $this->mconf['overrideId'] ?: $this->menuArr[$key]['overrideId'];
1690  $overrideArray['alias'] = '';
1691  // Clear MP parameters since ID was changed.
1692  $MP_params = '';
1693  } else {
1694  $overrideArray = '';
1695  }
1696  // Setting main target:
1697  if ($altTarget) {
1698  $mainTarget = $altTarget;
1699  } elseif ($this->mconf['target.']) {
1700  $mainTarget = $this->parent_cObj->stdWrap($this->mconf['target'], $this->mconf['target.']);
1701  } else {
1702  $mainTarget = $this->mconf['target'];
1703  }
1704  // Creating link:
1705  $addParams = $this->mconf['addParams'] . $MP_params;
1706  if ($this->mconf['collapse'] && $this->‪isActive($this->menuArr[$key]['uid'], $this->‪getMPvar($key))) {
1707  $thePage = $this->sys_page->getPage($this->menuArr[$key]['pid']);
1708  $addParams .= $this->menuArr[$key]['_ADD_GETVARS'];
1709  $LD = $this->‪menuTypoLink($thePage, $mainTarget, '', '', $overrideArray, $addParams, $typeOverride);
1710  } else {
1711  $addParams .= $this->I['val']['additionalParams'] . $this->menuArr[$key]['_ADD_GETVARS'];
1712  $LD = $this->‪menuTypoLink($this->menuArr[$key], $mainTarget, '', '', $overrideArray, $addParams, $typeOverride);
1713  }
1714  // Override default target configuration if option is set
1715  if ($this->menuArr[$key]['target']) {
1716  $LD['target'] = $this->menuArr[$key]['target'];
1717  }
1718  // Override URL if using "External URL"
1719  if ($this->menuArr[$key]['doktype'] == ‪PageRepository::DOKTYPE_LINK) {
1720  $externalUrl = $this->‪getSysPage()->‪getExtURL($this->menuArr[$key]);
1721  // Create link using typolink (concerning spamProtectEmailAddresses) for email links
1722  $LD['totalURL'] = $this->parent_cObj->typoLink_URL(['parameter' => $externalUrl]);
1723  // Links to emails should not have any target
1724  if (stripos($externalUrl, 'mailto:') === 0) {
1725  $LD['target'] = '';
1726  // use external target for the URL
1727  } elseif (empty($LD['target']) && !empty($this->‪getTypoScriptFrontendController()->extTarget)) {
1728  $LD['target'] = $this->‪getTypoScriptFrontendController()->extTarget;
1729  }
1730  }
1731 
1732  $tsfe = $this->‪getTypoScriptFrontendController();
1733 
1734  // Override url if current page is a shortcut
1735  $shortcut = null;
1736  if ($this->menuArr[$key]['doktype'] == ‪PageRepository::DOKTYPE_SHORTCUT && $this->menuArr[$key]['shortcut_mode'] != ‪PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
1737  $menuItem = $this->menuArr[$key];
1738  try {
1739  $shortcut = $tsfe->sys_page->getPageShortcut(
1740  $menuItem['shortcut'],
1741  $menuItem['shortcut_mode'],
1742  $menuItem['uid'],
1743  20,
1744  [],
1745  true
1746  );
1747  } catch (\Exception $ex) {
1748  }
1749  if (!is_array($shortcut)) {
1750  $runtimeCache->set($cacheId, []);
1751  return [];
1752  }
1753  // Only setting url, not target
1754  $LD['totalURL'] = $this->parent_cObj->typoLink_URL([
1755  'parameter' => $shortcut['uid'],
1756  'language' => 'current',
1757  'additionalParams' => $addParams . $this->I['val']['additionalParams'] . $menuItem['_ADD_GETVARS'],
1758  'linkAccessRestrictedPages' => !empty($this->mconf['showAccessRestrictedPages'])
1759  ]);
1760  }
1761  if ($shortcut) {
1762  $pageData = $shortcut;
1763  $pageData['_SHORTCUT_PAGE_UID'] = $this->menuArr[$key]['uid'];
1764  } else {
1765  $pageData = $this->menuArr[$key];
1766  }
1767  // Manipulation in case of access restricted pages:
1768  $this->‪changeLinksForAccessRestrictedPages($LD, $pageData, $mainTarget, $typeOverride);
1769  // Overriding URL / Target if set to do so:
1770  if ($this->menuArr[$key]['_OVERRIDE_HREF']) {
1771  $LD['totalURL'] = $this->menuArr[$key]['_OVERRIDE_HREF'];
1772  if ($this->menuArr[$key]['_OVERRIDE_TARGET']) {
1773  $LD['target'] = $this->menuArr[$key]['_OVERRIDE_TARGET'];
1774  }
1775  }
1776  // OnClick open in windows.
1777  $onClick = '';
1778  if ($this->mconf['JSWindow']) {
1779  ‪$conf = $this->mconf['JSWindow.'];
1780  $url = $LD['totalURL'];
1781  $LD['totalURL'] = '#';
1782  $onClick = 'openPic('
1783  . GeneralUtility::quoteJSvalue($tsfe->baseUrlWrap($url)) . ','
1784  . '\'' . (‪$conf['newWindow'] ? md5($url) : 'theNewPage') . '\','
1785  . GeneralUtility::quoteJSvalue(‪$conf['params']) . '); return false;';
1786  $tsfe->setJS('openPic');
1787  }
1788  // look for type and popup
1789  // following settings are valid in field target:
1790  // 230 will add type=230 to the link
1791  // 230 500x600 will add type=230 to the link and open in popup window with 500x600 pixels
1792  // 230 _blank will add type=230 to the link and open with target "_blank"
1793  // 230x450:resizable=0,location=1 will open in popup window with 500x600 pixels with settings "resizable=0,location=1"
1794  $matches = [];
1795  $targetIsType = $LD['target'] && ‪MathUtility::canBeInterpretedAsInteger($LD['target']) ? (int)$LD['target'] : false;
1796  if (preg_match('/([0-9]+[\\s])?(([0-9]+)x([0-9]+))?(:.+)?/s', $LD['target'], $matches) || $targetIsType) {
1797  // has type?
1798  if ((int)$matches[1] || $targetIsType) {
1799  $LD['totalURL'] .= (strpos($LD['totalURL'], '?') === false ? '?' : '&') . 'type=' . ($targetIsType ?: (int)$matches[1]);
1800  $LD['target'] = $targetIsType ? '' : trim(substr($LD['target'], strlen($matches[1]) + 1));
1801  }
1802  // Open in popup window?
1803  if ($matches[3] && $matches[4]) {
1804  $JSparamWH = 'width=' . $matches[3] . ',height=' . $matches[4] . ($matches[5] ? ',' . substr($matches[5], 1) : '');
1805  $onClick = 'openPic('
1806  . GeneralUtility::quoteJSvalue($tsfe->baseUrlWrap($LD['totalURL']))
1807  . ',\'FEopenLink\',' . GeneralUtility::quoteJSvalue($JSparamWH) . ');return false;';
1808  $tsfe->setJS('openPic');
1809  $LD['target'] = '';
1810  }
1811  }
1812  // out:
1813  $list = [];
1814  // Added this check: What it does is to enter the baseUrl (if set, which it should for "realurl" based sites)
1815  // as URL if the calculated value is empty. The problem is that no link is generated with a blank URL
1816  // and blank URLs might appear when the realurl encoding is used and a link to the frontpage is generated.
1817  $list['HREF'] = (string)$LD['totalURL'] !== '' ? $LD['totalURL'] : $tsfe->baseUrl;
1818  $list['TARGET'] = $LD['target'];
1819  $list['onClick'] = $onClick;
1820  $runtimeCache->set($cacheId, $list);
1821  return $list;
1822  }
1823 
1833  protected function ‪determineOriginalShortcutPage(array $page)
1834  {
1835  // Check if modification is required
1836  if (
1837  $this->‪getCurrentLanguageAspect()->getId() > 0
1838  && empty($page['shortcut'])
1839  && !empty($page['uid'])
1840  && !empty($page['_PAGES_OVERLAY'])
1841  && !empty($page['_PAGES_OVERLAY_UID'])
1842  ) {
1843  // Using raw record since the record was overlaid and is correct already:
1844  $originalPage = $this->sys_page->getRawRecord('pages', $page['uid']);
1845 
1846  if ($originalPage['shortcut_mode'] === $page['shortcut_mode'] && !empty($originalPage['shortcut'])) {
1847  $page['shortcut'] = $originalPage['shortcut'];
1848  }
1849  }
1850 
1851  return $page;
1852  }
1862  protected function ‪changeLinksForAccessRestrictedPages(&$LD, $page, $mainTarget, $typeOverride)
1863  {
1864  // If access restricted pages should be shown in menus, change the link of such pages to link to a redirection page:
1865  if ($this->mconf['showAccessRestrictedPages'] && $this->mconf['showAccessRestrictedPages'] !== 'NONE' && !$this->‪getTypoScriptFrontendController()->checkPageGroupAccess($page)) {
1866  $thePage = $this->sys_page->getPage($this->mconf['showAccessRestrictedPages']);
1867  $addParams = str_replace(
1868  [
1869  '###RETURN_URL###',
1870  '###PAGE_ID###'
1871  ],
1872  [
1873  rawurlencode($LD['totalURL']),
1874  $page['_SHORTCUT_PAGE_UID'] ?? $page['uid']
1875  ],
1876  $this->mconf['showAccessRestrictedPages.']['addParams']
1877  );
1878  $LD = $this->‪menuTypoLink($thePage, $mainTarget, '', '', '', $addParams, $typeOverride);
1879  }
1880  }
1881 
1889  protected function ‪subMenu($uid, $objSuffix = '')
1890  {
1891  // Setting alternative menu item array if _SUB_MENU has been defined in the current ->menuArr
1892  $altArray = '';
1893  if (is_array($this->menuArr[$this->I['key']]['_SUB_MENU']) && !empty($this->menuArr[$this->I['key']]['_SUB_MENU'])) {
1894  $altArray = $this->menuArr[$this->I['key']]['_SUB_MENU'];
1895  }
1896  // Make submenu if the page is the next active
1897  $menuType = $this->conf[($this->menuNumber + 1) . $objSuffix];
1898  // stdWrap for expAll
1899  if (isset($this->mconf['expAll.'])) {
1900  $this->mconf['expAll'] = $this->parent_cObj->stdWrap($this->mconf['expAll'], $this->mconf['expAll.']);
1901  }
1902  if (($this->mconf['expAll'] || $this->‪isNext($uid, $this->‪getMPvar($this->I['key'])) || is_array($altArray)) && !$this->mconf['sectionIndex']) {
1903  try {
1904  $menuObjectFactory = GeneralUtility::makeInstance(MenuContentObjectFactory::class);
1906  $submenu = $menuObjectFactory->getMenuObjectByType($menuType);
1907  $submenu->entryLevel = $this->entryLevel + 1;
1908  $submenu->rL_uidRegister = ‪$this->rL_uidRegister;
1909  $submenu->MP_array = ‪$this->MP_array;
1910  if ($this->menuArr[$this->I['key']]['_MP_PARAM']) {
1911  $submenu->MP_array[] = $this->menuArr[$this->I['key']]['_MP_PARAM'];
1912  }
1913  // Especially scripts that build the submenu needs the parent data
1914  $submenu->parent_cObj = ‪$this->parent_cObj;
1915  $submenu->setParentMenu($this->menuArr, $this->I['key']);
1916  // Setting alternativeMenuTempArray (will be effective only if an array)
1917  if (is_array($altArray)) {
1918  $submenu->alternativeMenuTempArray = $altArray;
1919  }
1920  if ($submenu->start($this->tmpl, $this->sys_page, $uid, $this->conf, $this->menuNumber + 1, $objSuffix)) {
1921  $submenu->makeMenu();
1922  // Memorize the current menu item count
1923  $tsfe = $this->‪getTypoScriptFrontendController();
1924  $tempCountMenuObj = $tsfe->register['count_MENUOBJ'];
1925  // Reset the menu item count for the submenu
1926  $tsfe->register['count_MENUOBJ'] = 0;
1927  $content = $submenu->writeMenu();
1928  // Restore the item count now that the submenu has been handled
1929  $tsfe->register['count_MENUOBJ'] = $tempCountMenuObj;
1930  $tsfe->register['count_menuItems'] = count($this->menuArr);
1931  return $content;
1932  }
1933  } catch (‪Exception\NoSuchMenuTypeException $e) {
1934  }
1935  }
1936  return '';
1937  }
1938 
1947  protected function ‪isNext($uid, $MPvar = '')
1948  {
1949  // Check for always active PIDs:
1950  if (!empty($this->alwaysActivePIDlist) && in_array((int)$uid, $this->alwaysActivePIDlist, true)) {
1951  return true;
1952  }
1953  $testUid = $uid . ($MPvar ? ':' . $MPvar : '');
1954  if ($uid && $testUid == $this->nextActive) {
1955  return true;
1956  }
1957  return false;
1958  }
1959 
1967  protected function ‪isActive($uid, $MPvar = '')
1968  {
1969  // Check for always active PIDs:
1970  if (!empty($this->alwaysActivePIDlist) && in_array((int)$uid, $this->alwaysActivePIDlist, true)) {
1971  return true;
1972  }
1973  $testUid = $uid . ($MPvar ? ':' . $MPvar : '');
1974  if ($uid && in_array('ITEM:' . $testUid, $this->rL_uidRegister, true)) {
1975  return true;
1976  }
1977  return false;
1978  }
1979 
1987  protected function ‪isCurrent($uid, $MPvar = '')
1988  {
1989  $testUid = $uid . ($MPvar ? ':' . $MPvar : '');
1990  return $uid && end($this->rL_uidRegister) === 'ITEM:' . $testUid;
1991  }
1992 
2000  protected function ‪isSubMenu($uid)
2001  {
2002  $cacheId = 'menucontentobject-is-submenu-decision-' . $uid;
2003  $runtimeCache = $this->‪getRuntimeCache();
2004  $cachedDecision = $runtimeCache->get($cacheId);
2005  if (isset($cachedDecision['result'])) {
2006  return $cachedDecision['result'];
2007  }
2008  // Looking for a mount-pid for this UID since if that
2009  // exists we should look for a subpages THERE and not in the input $uid;
2010  $mount_info = $this->sys_page->getMountPointInfo($uid);
2011  if (is_array($mount_info)) {
2012  $uid = $mount_info['mount_pid'];
2013  }
2014  $recs = $this->sys_page->getMenu($uid, 'uid,pid,doktype,mount_pid,mount_pid_ol,nav_hide,shortcut,shortcut_mode,l18n_cfg');
2015  $hasSubPages = false;
2016  $bannedUids = $this->‪getBannedUids();
2017  $languageId = $this->‪getCurrentLanguageAspect()->getId();
2018  foreach ($recs as $theRec) {
2019  // no valid subpage if the document type is excluded from the menu
2020  if (GeneralUtility::inList($this->doktypeExcludeList, $theRec['doktype'] ?? '')) {
2021  continue;
2022  }
2023  // No valid subpage if the page is hidden inside menus and
2024  // it wasn't forced to show such entries
2025  if (isset($theRec['nav_hide']) && $theRec['nav_hide']
2026  && (!isset($this->conf['includeNotInMenu']) || !$this->conf['includeNotInMenu'])
2027  ) {
2028  continue;
2029  }
2030  // No valid subpage if the default language should be shown and the page settings
2031  // are excluding the visibility of the default language
2032  if (!$languageId && GeneralUtility::hideIfDefaultLanguage($theRec['l18n_cfg'] ?? 0)) {
2033  continue;
2034  }
2035  // No valid subpage if the alternative language should be shown and the page settings
2036  // are requiring a valid overlay but it doesn't exists
2037  $hideIfNotTranslated = GeneralUtility::hideIfNotTranslated($theRec['l18n_cfg'] ?? null);
2038  if ($languageId && $hideIfNotTranslated && !$theRec['_PAGES_OVERLAY']) {
2039  continue;
2040  }
2041  // No valid subpage if the subpage is banned by excludeUidList
2042  if (in_array($theRec['uid'], $bannedUids)) {
2043  continue;
2044  }
2045  $hasSubPages = true;
2046  break;
2047  }
2048  $runtimeCache->set($cacheId, ['result' => $hasSubPages]);
2049  return $hasSubPages;
2050  }
2051 
2060  protected function ‪isItemState($kind, $key)
2061  {
2062  $natVal = false;
2063  // If any value is set for ITEM_STATE the normal evaluation is discarded
2064  if ($this->menuArr[$key]['ITEM_STATE'] ?? false) {
2065  if ((string)$this->menuArr[$key]['ITEM_STATE'] === (string)$kind) {
2066  $natVal = true;
2067  }
2068  } else {
2069  switch ($kind) {
2070  case 'SPC':
2071  $natVal = (bool)$this->menuArr[$key]['isSpacer'];
2072  break;
2073  case 'IFSUB':
2074  $natVal = $this->‪isSubMenu($this->menuArr[$key]['uid']);
2075  break;
2076  case 'ACT':
2077  $natVal = $this->‪isActive($this->menuArr[$key]['uid'], $this->‪getMPvar($key));
2078  break;
2079  case 'ACTIFSUB':
2080  $natVal = $this->‪isActive($this->menuArr[$key]['uid'], $this->‪getMPvar($key)) && $this->‪isSubMenu($this->menuArr[$key]['uid']);
2081  break;
2082  case 'CUR':
2083  $natVal = $this->‪isCurrent($this->menuArr[$key]['uid'], $this->‪getMPvar($key));
2084  break;
2085  case 'CURIFSUB':
2086  $natVal = $this->‪isCurrent($this->menuArr[$key]['uid'], $this->‪getMPvar($key)) && $this->‪isSubMenu($this->menuArr[$key]['uid']);
2087  break;
2088  case 'USR':
2089  $natVal = (bool)$this->menuArr[$key]['fe_group'];
2090  break;
2091  }
2092  }
2093  return $natVal;
2094  }
2095 
2102  protected function ‪accessKey($title)
2103  {
2104  $tsfe = $this->‪getTypoScriptFrontendController();
2105  // The global array ACCESSKEY is used to globally control if letters are already used!!
2106  $result = [];
2107  $title = trim(strip_tags($title));
2108  $titleLen = strlen($title);
2109  for ($a = 0; $a < $titleLen; $a++) {
2110  $key = strtoupper(substr($title, $a, 1));
2111  if (preg_match('/[A-Z]/', $key) && !isset($tsfe->accessKey[$key])) {
2112  $tsfe->accessKey[$key] = 1;
2113  ‪$result['code'] = ' accesskey="' . $key . '"';
2114  ‪$result['alt'] = ' (ALT+' . $key . ')';
2115  ‪$result['key'] = $key;
2116  break;
2117  }
2118  }
2119  return ‪$result;
2120  }
2121 
2130  protected function ‪userProcess($mConfKey, $passVar)
2131  {
2132  if ($this->mconf[$mConfKey]) {
2133  $funcConf = $this->mconf[$mConfKey . '.'];
2134  $funcConf['parentObj'] = $this;
2135  $passVar = $this->parent_cObj->callUserFunction($this->mconf[$mConfKey], $funcConf, $passVar);
2136  }
2137  return $passVar;
2138  }
2139 
2143  protected function ‪setATagParts()
2144  {
2145  $params = trim($this->I['val']['ATagParams']) . $this->I['accessKey']['code'];
2146  $params = $params !== '' ? ' ' . $params : '';
2147  $this->I['A1'] = '<a ' . GeneralUtility::implodeAttributes($this->I['linkHREF'], true) . $params . '>';
2148  $this->I['A2'] = '</a>';
2149  }
2150 
2158  protected function ‪getPageTitle($title, $nav_title)
2159  {
2160  return trim($nav_title) !== '' ? $nav_title : $title;
2161  }
2170  protected function ‪getMPvar($key)
2171  {
2172  if (‪$GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) {
2173  $localMP_array = ‪$this->MP_array;
2174  // NOTICE: "_MP_PARAM" is allowed to be a commalist of PID pairs!
2175  if ($this->menuArr[$key]['_MP_PARAM']) {
2176  $localMP_array[] = $this->menuArr[$key]['_MP_PARAM'];
2177  }
2178  return !empty($localMP_array) ? implode(',', $localMP_array) : '';
2179  }
2180  return '';
2181  }
2182 
2188  protected function ‪getDoktypeExcludeWhere()
2189  {
2190  return $this->doktypeExcludeList ? ' AND pages.doktype NOT IN (' . $this->doktypeExcludeList . ')' : '';
2191  }
2192 
2198  protected function ‪getBannedUids()
2199  {
2200  $excludeUidList = isset($this->conf['excludeUidList.'])
2201  ? $this->parent_cObj->stdWrap($this->conf['excludeUidList'], $this->conf['excludeUidList.'])
2202  : $this->conf['excludeUidList'];
2203 
2204  if (!trim($excludeUidList)) {
2205  return [];
2206  }
2207 
2208  $banUidList = str_replace('current', $this->‪getTypoScriptFrontendController()->page['uid'] ?? null, $excludeUidList);
2209  return GeneralUtility::intExplode(',', $banUidList);
2210  }
2211 
2224  protected function ‪menuTypoLink($page, $oTarget, $no_cache, $script, $overrideArray = '', $addParams = '', $typeOverride = '')
2225  {
2226  ‪$conf = [
2227  'parameter' => is_array($overrideArray) && $overrideArray['uid'] ? $overrideArray['uid'] : $page['uid']
2228  ];
2229  if (‪MathUtility::canBeInterpretedAsInteger($typeOverride)) {
2230  ‪$conf['parameter'] .= ',' . (int)$typeOverride;
2231  }
2232  if ($addParams) {
2233  ‪$conf['additionalParams'] = $addParams;
2234  }
2236  // Ensure that the typolink gets an info which language was actually requested. The $page record could be the record
2237  // from page translation language=1 as fallback but page translation language=2 was requested. Search for
2238  // "_PAGES_OVERLAY_REQUESTEDLANGUAGE" for more details
2239  if ($page['_PAGES_OVERLAY_REQUESTEDLANGUAGE'] ?? 0) {
2240  ‪$conf['language'] = $page['_PAGES_OVERLAY_REQUESTEDLANGUAGE'];
2241  }
2242  if ($no_cache) {
2243  ‪$conf['no_cache'] = true;
2244  } elseif ($this->useCacheHash) {
2245  ‪$conf['useCacheHash'] = true;
2246  }
2247  if ($oTarget) {
2248  ‪$conf['target'] = $oTarget;
2249  }
2250  if ($page['sectionIndex_uid'] ?? false) {
2251  ‪$conf['section'] = $page['sectionIndex_uid'];
2252  }
2253  ‪$conf['linkAccessRestrictedPages'] = !empty($this->mconf['showAccessRestrictedPages']);
2254  $this->parent_cObj->typoLink('|', ‪$conf);
2255  $LD = $this->parent_cObj->lastTypoLinkLD;
2256  $LD['totalURL'] = $this->parent_cObj->lastTypoLinkUrl;
2257  return $LD;
2258  }
2259 
2271  protected function ‪sectionIndex($altSortField, $pid = null)
2272  {
2273  $pid = (int)($pid ?: $this->id);
2274  $basePageRow = $this->sys_page->getPage($pid);
2275  if (!is_array($basePageRow)) {
2276  return [];
2277  }
2278  $tsfe = $this->‪getTypoScriptFrontendController();
2279  $configuration = $this->mconf['sectionIndex.'] ?? [];
2280  $useColPos = 0;
2281  if (trim($configuration['useColPos'] ?? '') !== ''
2282  || (isset($configuration['useColPos.']) && is_array($configuration['useColPos.']))
2283  ) {
2284  $useColPos = $tsfe->cObj->stdWrap($configuration['useColPos'] ?? '', $configuration['useColPos.'] ?? []);
2285  $useColPos = (int)$useColPos;
2286  }
2287  $selectSetup = [
2288  'pidInList' => $pid,
2289  'orderBy' => $altSortField,
2290  'languageField' => 'sys_language_uid',
2291  'where' => ''
2292  ];
2293 
2294  if ($useColPos >= 0) {
2295  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
2296  ->getConnectionForTable('tt_content')
2297  ->getExpressionBuilder();
2298  $selectSetup['where'] = $expressionBuilder->eq('colPos', $useColPos);
2299  }
2300 
2301  if ($basePageRow['content_from_pid'] ?? false) {
2302  // If the page is configured to show content from a referenced page the sectionIndex contains only contents of
2303  // the referenced page
2304  $selectSetup['pidInList'] = $basePageRow['content_from_pid'];
2305  }
2306  $statement = $this->parent_cObj->exec_getQuery('tt_content', $selectSetup);
2307  if (!$statement) {
2308  $message = 'SectionIndex: Query to fetch the content elements failed!';
2309  throw new \UnexpectedValueException($message, 1337334849);
2310  }
2311  ‪$result = [];
2312  while ($row = $statement->fetch()) {
2313  $this->sys_page->versionOL('tt_content', $row);
2314  if ($this->‪getCurrentLanguageAspect()->doOverlays() && $basePageRow['_PAGES_OVERLAY_LANGUAGE']) {
2315  $row = $this->sys_page->getRecordOverlay(
2316  'tt_content',
2317  $row,
2318  $basePageRow['_PAGES_OVERLAY_LANGUAGE'],
2319  $this->‪getCurrentLanguageAspect()->getOverlayType() === ‪LanguageAspect::OVERLAYS_MIXED ? '1' : 'hideNonTranslated'
2320  );
2321  }
2322  if ($this->mconf['sectionIndex.']['type'] !== 'all') {
2323  $doIncludeInSectionIndex = $row['sectionIndex'] >= 1;
2324  $doHeaderCheck = $this->mconf['sectionIndex.']['type'] === 'header';
2325  $isValidHeader = ((int)$row['header_layout'] !== 100 || !empty($this->mconf['sectionIndex.']['includeHiddenHeaders'])) && trim($row['header']) !== '';
2326  if (!$doIncludeInSectionIndex || $doHeaderCheck && !$isValidHeader) {
2327  continue;
2328  }
2329  }
2330  if (is_array($row)) {
2331  $uid = $row['uid'] ?? null;
2332  ‪$result[$uid] = $basePageRow;
2333  ‪$result[$uid]['title'] = $row['header'];
2334  ‪$result[$uid]['nav_title'] = $row['header'];
2335  // Prevent false exclusion in filterMenuPages, thus: Always show tt_content records
2336  ‪$result[$uid]['nav_hide'] = 0;
2337  ‪$result[$uid]['subtitle'] = $row['subheader'] ?? '';
2338  ‪$result[$uid]['starttime'] = $row['starttime'] ?? '';
2339  ‪$result[$uid]['endtime'] = $row['endtime'] ?? '';
2340  ‪$result[$uid]['fe_group'] = $row['fe_group'] ?? '';
2341  ‪$result[$uid]['media'] = $row['media'] ?? '';
2342  ‪$result[$uid]['header_layout'] = $row['header_layout'] ?? '';
2343  ‪$result[$uid]['bodytext'] = $row['bodytext'] ?? '';
2344  ‪$result[$uid]['image'] = $row['image'] ?? '';
2345  ‪$result[$uid]['sectionIndex_uid'] = $uid;
2346  }
2347  }
2348 
2349  return ‪$result;
2350  }
2351 
2357  public function ‪getSysPage()
2358  {
2359  return ‪$this->sys_page;
2360  }
2361 
2367  public function ‪getParentContentObject()
2368  {
2369  return ‪$this->parent_cObj;
2370  }
2371 
2375  protected function ‪getTypoScriptFrontendController()
2376  {
2377  return ‪$GLOBALS['TSFE'];
2378  }
2380  protected function ‪getCurrentLanguageAspect(): ‪LanguageAspect
2381  {
2382  return GeneralUtility::makeInstance(Context::class)->getAspect('language');
2383  }
2384 
2388  protected function ‪getTimeTracker()
2389  {
2390  return GeneralUtility::makeInstance(TimeTracker::class);
2391  }
2392 
2396  protected function ‪getCache()
2397  {
2398  return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
2399  }
2400 
2404  protected function ‪getRuntimeCache()
2405  {
2406  return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
2407  }
2408 
2415  protected function ‪getCurrentSite(): SiteInterface
2416  {
2417  $matcher = GeneralUtility::makeInstance(SiteMatcher::class);
2418  return $matcher->matchByPageId((int)$this->‪getTypoScriptFrontendController()->id);
2419  }
2420 
2429  public function ‪setParentMenu(array ‪$menuArr = [], $menuItemKey)
2430  {
2431  // check if menuArr is a valid array and that menuItemKey matches an existing menuItem in menuArr
2432  if (is_array(‪$menuArr)
2433  && (is_int($menuItemKey) && $menuItemKey >= 0 && isset(‪$menuArr[$menuItemKey]))
2434  ) {
2435  $this->parentMenuArr = ‪$menuArr;
2436  $this->parentMenuArrItemKey = $menuItemKey;
2437  }
2438  }
2439 
2445  protected function ‪hasParentMenuArr()
2446  {
2447  return
2448  $this->menuNumber > 1
2449  && is_array($this->parentMenuArr)
2450  && !empty($this->parentMenuArr)
2451  ;
2452  }
2457  protected function ‪hasParentMenuItemKey()
2458  {
2459  return null !== ‪$this->parentMenuArrItemKey;
2460  }
2461 
2465  protected function ‪hasParentMenuItem()
2466  {
2467  return
2468  $this->‪hasParentMenuArr()
2469  && $this->‪hasParentMenuItemKey()
2470  && isset($this->‪getParentMenuArr()[$this->parentMenuArrItemKey])
2471  ;
2472  }
2473 
2479  public function ‪getParentMenuArr()
2480  {
2481  return $this->‪hasParentMenuArr() ? $this->parentMenuArr : [];
2482  }
2483 
2489  public function ‪getParentMenuItem()
2490  {
2491  // check if we have an parentMenuItem and if it is an array
2492  if ($this->‪hasParentMenuItem()
2493  && is_array($this->‪getParentMenuArr()[$this->parentMenuArrItemKey])
2494  ) {
2496  }
2497 
2498  return null;
2499  }
2500 }
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getBannedUids
‪array getBannedUids()
Definition: AbstractMenuContentObject.php:2162
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\subMenu
‪string subMenu($uid, $objSuffix='')
Definition: AbstractMenuContentObject.php:1853
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$spacerIDList
‪string $spacerIDList
Definition: AbstractMenuContentObject.php:123
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getParentMenuItem
‪array null getParentMenuItem()
Definition: AbstractMenuContentObject.php:2453
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getTimeTracker
‪TimeTracker getTimeTracker()
Definition: AbstractMenuContentObject.php:2352
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getSysPage
‪TYPO3 CMS Frontend Page PageRepository getSysPage()
Definition: AbstractMenuContentObject.php:2321
‪TYPO3\CMS\Core\Site\Entity\SiteInterface
Definition: SiteInterface.php:25
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$WMfreezePrefix
‪string $WMfreezePrefix
Definition: AbstractMenuContentObject.php:231
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$imgNameNotRandom
‪int $imgNameNotRandom
Definition: AbstractMenuContentObject.php:141
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$MP_array
‪string[] $MP_array
Definition: AbstractMenuContentObject.php:161
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$WMresult
‪string $WMresult
Definition: AbstractMenuContentObject.php:227
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$parentMenuArrItemKey
‪int null $parentMenuArrItemKey
Definition: AbstractMenuContentObject.php:272
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\hasParentMenuArr
‪bool hasParentMenuArr()
Definition: AbstractMenuContentObject.php:2409
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getPageTitle
‪string getPageTitle($title, $nav_title)
Definition: AbstractMenuContentObject.php:2122
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\writeMenu
‪string writeMenu()
Definition: AbstractMenuContentObject.php:567
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:73
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$alternativeMenuTempArray
‪string $alternativeMenuTempArray
Definition: AbstractMenuContentObject.php:253
‪TYPO3\CMS\Core\Context\LanguageAspect\OVERLAYS_MIXED
‪const OVERLAYS_MIXED
Definition: LanguageAspect.php:73
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\changeLinksForAccessRestrictedPages
‪changeLinksForAccessRestrictedPages(&$LD, $page, $mainTarget, $typeOverride)
Definition: AbstractMenuContentObject.php:1826
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForRootlineMenu
‪array prepareMenuItemsForRootlineMenu()
Definition: AbstractMenuContentObject.php:1122
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$parentMenuArr
‪array $parentMenuArr
Definition: AbstractMenuContentObject.php:276
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\hasParentMenuItem
‪hasParentMenuItem()
Definition: AbstractMenuContentObject.php:2429
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$nextActive
‪string $nextActive
Definition: AbstractMenuContentObject.php:194
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$imgNamePrefix
‪string $imgNamePrefix
Definition: AbstractMenuContentObject.php:137
‪TYPO3\CMS\Frontend\Page\PageRepository\DOKTYPE_SHORTCUT
‪const DOKTYPE_SHORTCUT
Definition: PageRepository.php:170
‪TYPO3\CMS\Core\Database\RelationHandler
Definition: RelationHandler.php:32
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$conf
‪array $conf
Definition: AbstractMenuContentObject.php:167
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\procesItemStates
‪array procesItemStates($splitCount)
Definition: AbstractMenuContentObject.php:1377
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$GMENU_fixKey
‪string $GMENU_fixKey
Definition: AbstractMenuContentObject.php:155
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getParentContentObject
‪TYPO3 CMS Frontend ContentObject ContentObjectRenderer getParentContentObject()
Definition: AbstractMenuContentObject.php:2331
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItems
‪array prepareMenuItems()
Definition: AbstractMenuContentObject.php:595
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
‪TYPO3\CMS\Frontend\Page\PageRepository\DOKTYPE_SPACER
‪const DOKTYPE_SPACER
Definition: PageRepository.php:173
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\sectionIndex
‪array sectionIndex($altSortField, $pid=null)
Definition: AbstractMenuContentObject.php:2235
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\filterMenuPages
‪bool filterMenuPages(&$data, $banUidArray, $spacer)
Definition: AbstractMenuContentObject.php:1322
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForUpdatedMenu
‪array prepareMenuItemsForUpdatedMenu($specialValue, $sortingField)
Definition: AbstractMenuContentObject.php:897
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$deprecatedPublicProperties
‪$deprecatedPublicProperties
Definition: AbstractMenuContentObject.php:47
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForLanguageMenu
‪array prepareMenuItemsForLanguageMenu($specialValue)
Definition: AbstractMenuContentObject.php:693
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getRuntimeCache
‪TYPO3 CMS Core Cache Frontend FrontendInterface getRuntimeCache()
Definition: AbstractMenuContentObject.php:2368
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\link
‪array link($key, $altTarget='', $typeOverride='')
Definition: AbstractMenuContentObject.php:1637
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\hasParentMenuItemKey
‪hasParentMenuItemKey()
Definition: AbstractMenuContentObject.php:2421
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$WMextraScript
‪string $WMextraScript
Definition: AbstractMenuContentObject.php:243
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject
Definition: AbstractMenuContentObject.php:45
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$nameAttribute
‪string $nameAttribute
Definition: AbstractMenuContentObject.php:259
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getCurrentSite
‪SiteInterface getCurrentSite()
Definition: AbstractMenuContentObject.php:2379
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:49
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForUserSpecificMenu
‪array prepareMenuItemsForUserSpecificMenu($specialValue, $sortingField)
Definition: AbstractMenuContentObject.php:674
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$result
‪array $result
Definition: AbstractMenuContentObject.php:208
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$INPfixMD5
‪string $INPfixMD5
Definition: AbstractMenuContentObject.php:219
‪TYPO3\CMS\Frontend\Page\PageRepository\SHORTCUT_MODE_RANDOM_SUBPAGE
‪const SHORTCUT_MODE_RANDOM_SUBPAGE
Definition: PageRepository.php:182
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\userProcess
‪mixed userProcess($mConfKey, $passVar)
Definition: AbstractMenuContentObject.php:2094
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSlogMessage
‪setTSlogMessage($content, $num=0)
Definition: TimeTracker.php:193
‪TYPO3\CMS\Frontend\Page\PageRepository\DOKTYPE_LINK
‪const DOKTYPE_LINK
Definition: PageRepository.php:169
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getDoktypeExcludeWhere
‪string getDoktypeExcludeWhere()
Definition: AbstractMenuContentObject.php:2152
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\isNext
‪bool isNext($uid, $MPvar='')
Definition: AbstractMenuContentObject.php:1911
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$sys_page
‪TYPO3 CMS Frontend Page PageRepository $sys_page
Definition: AbstractMenuContentObject.php:181
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$I
‪mixed[] $I
Definition: AbstractMenuContentObject.php:223
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$menuArr
‪array[] $menuArr
Definition: AbstractMenuContentObject.php:200
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\generate
‪generate()
Definition: AbstractMenuContentObject.php:560
‪$languageItems
‪$languageItems
Definition: be_users.php:7
‪TYPO3\CMS\Frontend\Page\PageRepository
Definition: PageRepository.php:53
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$WMsubmenuObjSuffixes
‪array[] $WMsubmenuObjSuffixes
Definition: AbstractMenuContentObject.php:239
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:34
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$id
‪int $id
Definition: AbstractMenuContentObject.php:187
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$useCacheHash
‪bool $useCacheHash
Definition: AbstractMenuContentObject.php:266
‪TYPO3\CMS\Frontend\Page\PageRepository\getExtURL
‪string bool getExtURL($pagerow)
Definition: PageRepository.php:1259
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuFilterPagesHookInterface
Definition: AbstractMenuFilterPagesHookInterface.php:21
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$doktypeExcludeList
‪string $doktypeExcludeList
Definition: AbstractMenuContentObject.php:129
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController\get_cache_timeout
‪int get_cache_timeout()
Definition: TypoScriptFrontendController.php:4456
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\setParentMenu
‪setParentMenu(array $menuArr=[], $menuItemKey)
Definition: AbstractMenuContentObject.php:2393
‪TYPO3\CMS\Core\Context\LanguageAspect
Definition: LanguageAspect.php:55
‪TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait
Definition: PublicMethodDeprecationTrait.php:68
‪TYPO3\CMS\Frontend\ContentObject\Menu
Definition: AbstractMenuContentObject.php:2
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForBrowseMenu
‪array prepareMenuItemsForBrowseMenu($specialValue, $sortingField, $additionalWhere)
Definition: AbstractMenuContentObject.php:1178
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\makeMenu
‪makeMenu()
Definition: AbstractMenuContentObject.php:463
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$entryLevel
‪int $entryLevel
Definition: AbstractMenuContentObject.php:117
‪TYPO3\CMS\Core\TypoScript\TypoScriptService
Definition: TypoScriptService.php:23
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getCurrentLanguageAspect
‪getCurrentLanguageAspect()
Definition: AbstractMenuContentObject.php:2344
‪debug
‪debug($variable='', $title=null, $group=null)
Definition: GlobalDebugFunctions.php:5
‪TYPO3\CMS\Core\Resource\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\TypoScript\TemplateService
Definition: TemplateService.php:50
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\removeInaccessiblePages
‪array removeInaccessiblePages(array $pages)
Definition: AbstractMenuContentObject.php:578
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$WMmenuItems
‪int $WMmenuItems
Definition: AbstractMenuContentObject.php:235
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$rL_uidRegister
‪array $rL_uidRegister
Definition: AbstractMenuContentObject.php:215
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:97
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$debug
‪bool $debug
Definition: AbstractMenuContentObject.php:145
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\start
‪bool start($tmpl, $sys_page, $id, $conf, $menuNumber, $objSuffix='')
Definition: AbstractMenuContentObject.php:290
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getTypoScriptFrontendController
‪TypoScriptFrontendController getTypoScriptFrontendController()
Definition: AbstractMenuContentObject.php:2339
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$menuNumber
‪int $menuNumber
Definition: AbstractMenuContentObject.php:111
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForKeywordsMenu
‪array prepareMenuItemsForKeywordsMenu($specialValue, $sortingField)
Definition: AbstractMenuContentObject.php:987
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\determineOriginalShortcutPage
‪array determineOriginalShortcutPage(array $page)
Definition: AbstractMenuContentObject.php:1797
‪TYPO3\CMS\Frontend\Page\CacheHashCalculator
Definition: CacheHashCalculator.php:24
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:21
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:91
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\isActive
‪bool isActive($uid, $MPvar='')
Definition: AbstractMenuContentObject.php:1931
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$hash
‪string $hash
Definition: AbstractMenuContentObject.php:204
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$deprecatedPublicMethods
‪$deprecatedPublicMethods
Definition: AbstractMenuContentObject.php:80
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\isCurrent
‪bool isCurrent($uid, $MPvar='')
Definition: AbstractMenuContentObject.php:1951
‪TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait
Definition: PublicPropertyDeprecationTrait.php:66
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$mconf
‪array $mconf
Definition: AbstractMenuContentObject.php:173
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:44
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$alwaysActivePIDlist
‪int[] $alwaysActivePIDlist
Definition: AbstractMenuContentObject.php:133
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\setATagParts
‪setATagParts()
Definition: AbstractMenuContentObject.php:2107
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\menuTypoLink
‪array menuTypoLink($page, $oTarget, $no_cache, $script, $overrideArray='', $addParams='', $typeOverride='')
Definition: AbstractMenuContentObject.php:2188
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getParentMenuArr
‪array getParentMenuArr()
Definition: AbstractMenuContentObject.php:2443
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:27
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\accessKey
‪array accessKey($title)
Definition: AbstractMenuContentObject.php:2066
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\prepareMenuItemsForDirectoryMenu
‪array prepareMenuItemsForDirectoryMenu($specialValue, $sortingField)
Definition: AbstractMenuContentObject.php:759
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getMPvar
‪string getMPvar($key)
Definition: AbstractMenuContentObject.php:2134
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$WMcObj
‪ContentObjectRenderer $WMcObj
Definition: AbstractMenuContentObject.php:247
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\getCache
‪TYPO3 CMS Core Cache Frontend FrontendInterface getCache()
Definition: AbstractMenuContentObject.php:2360
‪TYPO3\CMS\Core\Routing\SiteMatcher
Definition: SiteMatcher.php:53
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\isItemState
‪bool isItemState($kind, $key)
Definition: AbstractMenuContentObject.php:2024
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$parent_cObj
‪TYPO3 CMS Frontend ContentObject ContentObjectRenderer $parent_cObj
Definition: AbstractMenuContentObject.php:151
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\analyzeCacheHashRequirements
‪analyzeCacheHashRequirements($queryString)
Definition: AbstractMenuContentObject.php:1291
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\isSubMenu
‪bool isSubMenu($uid)
Definition: AbstractMenuContentObject.php:1964
‪TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject\$tmpl
‪TYPO3 CMS Core TypoScript TemplateService $tmpl
Definition: AbstractMenuContentObject.php:177