[BUGFIX] inval() -> (int) regressions in ternaries
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / ContentObject / Menu / AbstractMenuContentObject.php
1 <?php
2 namespace TYPO3\CMS\Frontend\ContentObject\Menu;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 1999-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the text file GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\CMS\Core\Utility\MathUtility;
32
33 /**
34 * Generating navigation / menus from TypoScript
35 *
36 * Base class. The HMENU content object uses this (or more precisely one of the extension classes).
37 * Among others the class generates an array of menuitems. Thereafter functions from the subclasses are called.
38 * The class is ALWAYS used through extension classes (like GraphicalMenuContentObject or TextMenuContentObject which are classics) and
39 *
40 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
41 */
42 class AbstractMenuContentObject {
43
44 // tells you which menu-number this is. This is important when getting data from the setup
45 /**
46 * @todo Define visibility
47 */
48 public $menuNumber = 1;
49
50 // 0 = rootFolder
51 /**
52 * @todo Define visibility
53 */
54 public $entryLevel = 0;
55
56 // The doktype-number that defines a spacer
57 /**
58 * @todo Define visibility
59 */
60 public $spacerIDList = '199';
61
62 // Doktypes that define which should not be included in a menu
63 /**
64 * @todo Define visibility
65 */
66 public $doktypeExcludeList = '6';
67
68 /**
69 * @todo Define visibility
70 */
71 public $alwaysActivePIDlist = array();
72
73 /**
74 * @todo Define visibility
75 */
76 public $imgNamePrefix = 'img';
77
78 /**
79 * @todo Define visibility
80 */
81 public $imgNameNotRandom = 0;
82
83 /**
84 * @todo Define visibility
85 */
86 public $debug = 0;
87
88 /**
89 * Loaded with the parent cObj-object when a new HMENU is made
90 *
91 * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
92 * @todo Define visibility
93 */
94 public $parent_cObj;
95
96 /**
97 * @todo Define visibility
98 */
99 public $GMENU_fixKey = 'gmenu';
100
101 // accumulation of mount point data
102 /**
103 * @todo Define visibility
104 */
105 public $MP_array = array();
106
107 // internal
108 // HMENU configuration
109 /**
110 * @todo Define visibility
111 */
112 public $conf = array();
113
114 // xMENU configuration (TMENU, GMENU etc)
115 /**
116 * @todo Define visibility
117 */
118 public $mconf = array();
119
120 /**
121 * template-object
122 *
123 * @var \TYPO3\CMS\Core\TypoScript\TemplateService
124 * @todo Define visibility
125 */
126 public $tmpl;
127
128 /**
129 * sys_page-object, pagefunctions
130 *
131 * @var \TYPO3\CMS\Frontend\Page\PageRepository
132 * @todo Define visibility
133 */
134 public $sys_page;
135
136 // The base page-id of the menu.
137 /**
138 * @todo Define visibility
139 */
140 public $id;
141
142 // Holds the page uid of the NEXT page in the root line from the page pointed to by entryLevel;
143 // Used to expand the menu automatically if in a certain root line.
144 /**
145 * @todo Define visibility
146 */
147 public $nextActive;
148
149 // The array of menuItems which is built
150 /**
151 * @todo Define visibility
152 */
153 public $menuArr;
154
155 /**
156 * @todo Define visibility
157 */
158 public $hash;
159
160 /**
161 * @todo Define visibility
162 */
163 public $result = array();
164
165 // Array: Is filled with an array of page uid numbers + RL parameters which are in the current
166 // root line (used to evaluate whether a menu item is in active state)
167 /**
168 * @todo Define visibility
169 */
170 public $rL_uidRegister = '';
171
172 /**
173 * @todo Define visibility
174 */
175 public $INPfixMD5;
176
177 /**
178 * @todo Define visibility
179 */
180 public $I;
181
182 /**
183 * @todo Define visibility
184 */
185 public $WMresult;
186
187 /**
188 * @todo Define visibility
189 */
190 public $WMfreezePrefix;
191
192 /**
193 * @todo Define visibility
194 */
195 public $WMmenuItems;
196
197 /**
198 * @todo Define visibility
199 */
200 public $WMsubmenuObjSuffixes;
201
202 /**
203 * @todo Define visibility
204 */
205 public $WMextraScript;
206
207 // Can be set to contain menu item arrays for sub-levels.
208 /**
209 * @todo Define visibility
210 */
211 public $alternativeMenuTempArray = '';
212
213 // Will be 'id' in XHTML-mode
214 /**
215 * @todo Define visibility
216 */
217 public $nameAttribute = 'name';
218
219 /**
220 * The initialization of the object. This just sets some internal variables.
221 *
222 * @param object $tmpl The $GLOBALS['TSFE']->tmpl object
223 * @param object $sys_page The $GLOBALS['TSFE']->sys_page object
224 * @param integer $id A starting point page id. This should probably be blank since the 'entryLevel' value will be used then.
225 * @param array $conf The TypoScript configuration for the HMENU cObject
226 * @param integer $menuNumber Menu number; 1,2,3. Should probably be '1'
227 * @param string $objSuffix Submenu Object suffix. This offers submenus a way to use alternative configuration for specific positions in the menu; By default "1 = TMENU" would use "1." for the TMENU configuration, but if this string is set to eg. "a" then "1a." would be used for configuration instead (while "1 = " is still used for the overall object definition of "TMENU")
228 * @return boolean Returns TRUE on success
229 * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::HMENU()
230 * @todo Define visibility
231 */
232 public function start(&$tmpl, &$sys_page, $id, $conf, $menuNumber, $objSuffix = '') {
233 // Init:
234 $this->conf = $conf;
235 $this->menuNumber = $menuNumber;
236 $this->mconf = $conf[$this->menuNumber . $objSuffix . '.'];
237 $this->debug = $GLOBALS['TSFE']->debug;
238 // In XHTML there is no "name" attribute anymore
239 switch ($GLOBALS['TSFE']->xhtmlDoctype) {
240 case 'xhtml_strict':
241
242 case 'xhtml_11':
243
244 case 'xhtml_2':
245
246 case 'html5':
247 $this->nameAttribute = 'id';
248 break;
249 default:
250 $this->nameAttribute = 'name';
251 }
252 // Sets the internal vars. $tmpl MUST be the template-object. $sys_page MUST be the sys_page object
253 if ($this->conf[$this->menuNumber . $objSuffix] && is_object($tmpl) && is_object($sys_page)) {
254 $this->tmpl = $tmpl;
255 $this->sys_page = $sys_page;
256 // alwaysActivePIDlist initialized:
257 if (trim($this->conf['alwaysActivePIDlist']) || isset($this->conf['alwaysActivePIDlist.'])) {
258 if (isset($this->conf['alwaysActivePIDlist.'])) {
259 $this->conf['alwaysActivePIDlist'] = $this->parent_cObj->stdWrap($this->conf['alwaysActivePIDlist'], $this->conf['alwaysActivePIDlist.']);
260 }
261 $this->alwaysActivePIDlist = GeneralUtility::intExplode(',', $this->conf['alwaysActivePIDlist']);
262 }
263 // 'not in menu' doktypes
264 if ($this->conf['excludeDoktypes']) {
265 $this->doktypeExcludeList = $GLOBALS['TYPO3_DB']->cleanIntList($this->conf['excludeDoktypes']);
266 }
267 // EntryLevel
268 $this->entryLevel = $this->parent_cObj->getKey(isset($conf['entryLevel.']) ? $this->parent_cObj->stdWrap($conf['entryLevel'], $conf['entryLevel.']) : $conf['entryLevel'], $this->tmpl->rootLine);
269 // Set parent page: If $id not stated with start() then the base-id will be found from rootLine[$this->entryLevel]
270 // Called as the next level in a menu. It is assumed that $this->MP_array is set from parent menu.
271 if ($id) {
272 $this->id = (int)$id;
273 } else {
274 // This is a BRAND NEW menu, first level. So we take ID from rootline and also find MP_array (mount points)
275 $this->id = (int)$this->tmpl->rootLine[$this->entryLevel]['uid'];
276 // Traverse rootline to build MP_array of pages BEFORE the entryLevel
277 // (MP var for ->id is picked up in the next part of the code...)
278 foreach ($this->tmpl->rootLine as $entryLevel => $levelRec) {
279 // For overlaid mount points, set the variable right now:
280 if ($levelRec['_MP_PARAM'] && $levelRec['_MOUNT_OL']) {
281 $this->MP_array[] = $levelRec['_MP_PARAM'];
282 }
283 // Break when entry level is reached:
284 if ($entryLevel >= $this->entryLevel) {
285 break;
286 }
287 // For normal mount points, set the variable for next level.
288 if ($levelRec['_MP_PARAM'] && !$levelRec['_MOUNT_OL']) {
289 $this->MP_array[] = $levelRec['_MP_PARAM'];
290 }
291 }
292 }
293 // Return FALSE if no page ID was set (thus no menu of subpages can be made).
294 if ($this->id <= 0) {
295 return FALSE;
296 }
297 // Check if page is a mount point, and if so set id and MP_array
298 // (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...)
299 $mount_info = $this->sys_page->getMountPointInfo($this->id);
300 if (is_array($mount_info)) {
301 $this->MP_array[] = $mount_info['MPvar'];
302 $this->id = $mount_info['mount_pid'];
303 }
304 // Gather list of page uids in root line (for "isActive" evaluation). Also adds the MP params in the path so Mount Points are respected.
305 // (List is specific for this rootline, so it may be supplied from parent menus for speed...)
306 if (!is_array($this->rL_uidRegister)) {
307 $rl_MParray = array();
308 foreach ($this->tmpl->rootLine as $v_rl) {
309 // For overlaid mount points, set the variable right now:
310 if ($v_rl['_MP_PARAM'] && $v_rl['_MOUNT_OL']) {
311 $rl_MParray[] = $v_rl['_MP_PARAM'];
312 }
313 // Add to register:
314 $this->rL_uidRegister[] = 'ITEM:' . $v_rl['uid'] . (count($rl_MParray) ? ':' . implode(',', $rl_MParray) : '');
315 // For normal mount points, set the variable for next level.
316 if ($v_rl['_MP_PARAM'] && !$v_rl['_MOUNT_OL']) {
317 $rl_MParray[] = $v_rl['_MP_PARAM'];
318 }
319 }
320 }
321 // Set $directoryLevel so the following evalution of the nextActive will not return
322 // an invalid value if .special=directory was set
323 $directoryLevel = 0;
324 if ($this->conf['special'] == 'directory') {
325 $value = isset($this->conf['special.']['value.']) ? $this->parent_cObj->stdWrap($this->conf['special.']['value'], $this->conf['special.']['value.']) : $this->conf['special.']['value'];
326 if ($value == '') {
327 $value = $GLOBALS['TSFE']->page['uid'];
328 }
329 $directoryLevel = (int)$GLOBALS['TSFE']->tmpl->getRootlineLevel($value);
330 }
331 // 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
332 // Notice: The automatic expansion of a menu is designed to work only when no "special" modes (except "directory") are used.
333 $startLevel = $directoryLevel ?: $this->entryLevel;
334 $currentLevel = $startLevel + $this->menuNumber;
335 if (is_array($this->tmpl->rootLine[$currentLevel])) {
336 $nextMParray = $this->MP_array;
337 if (!count($nextMParray) && !$this->tmpl->rootLine[$currentLevel]['_MOUNT_OL'] && $currentLevel > 0) {
338 // Make sure to slide-down any mount point information (_MP_PARAM) to children records in the rootline
339 // otherwise automatic expansion will not work
340 $parentRecord = $this->tmpl->rootLine[$currentLevel - 1];
341 if (isset($parentRecord['_MP_PARAM'])) {
342 $nextMParray[] = $parentRecord['_MP_PARAM'];
343 }
344 }
345 // In overlay mode, add next level MPvars as well:
346 if ($this->tmpl->rootLine[$currentLevel]['_MOUNT_OL']) {
347 $nextMParray[] = $this->tmpl->rootLine[$currentLevel]['_MP_PARAM'];
348 }
349 $this->nextActive = $this->tmpl->rootLine[$currentLevel]['uid'] . (count($nextMParray) ? ':' . implode(',', $nextMParray) : '');
350 } else {
351 $this->nextActive = '';
352 }
353 // imgNamePrefix
354 if ($this->mconf['imgNamePrefix']) {
355 $this->imgNamePrefix = $this->mconf['imgNamePrefix'];
356 }
357 $this->imgNameNotRandom = $this->mconf['imgNameNotRandom'];
358 $retVal = TRUE;
359 } else {
360 $GLOBALS['TT']->setTSlogMessage('ERROR in menu', 3);
361 $retVal = FALSE;
362 }
363 return $retVal;
364 }
365
366 /**
367 * Creates the menu in the internal variables, ready for output.
368 * Basically this will read the page records needed and fill in the internal $this->menuArr
369 * Based on a hash of this array and some other variables the $this->result variable will be loaded either from cache OR by calling the generate() method of the class to create the menu for real.
370 *
371 * @return void
372 * @todo Define visibility
373 */
374 public function makeMenu() {
375 if ($this->id) {
376 // Initializing showAccessRestrictedPages
377 if ($this->mconf['showAccessRestrictedPages']) {
378 // SAVING where_groupAccess
379 $SAVED_where_groupAccess = $this->sys_page->where_groupAccess;
380 // Temporarily removing fe_group checking!
381 $this->sys_page->where_groupAccess = '';
382 }
383 // Begin production of menu:
384 $temp = array();
385 $altSortFieldValue = trim($this->mconf['alternativeSortingField']);
386 $altSortField = $altSortFieldValue ?: 'sorting';
387 // ... only for the FIRST level of a HMENU
388 if ($this->menuNumber == 1 && $this->conf['special']) {
389 $value = isset($this->conf['special.']['value.']) ? $this->parent_cObj->stdWrap($this->conf['special.']['value'], $this->conf['special.']['value.']) : $this->conf['special.']['value'];
390 switch ($this->conf['special']) {
391 case 'userfunction':
392 $temp = $this->parent_cObj->callUserFunction($this->conf['special.']['userFunc'], array_merge($this->conf['special.'], array('_altSortField' => $altSortField)), '');
393 if (!is_array($temp)) {
394 $temp = array();
395 }
396 break;
397 case 'language':
398 $temp = array();
399 // Getting current page record NOT overlaid by any translation:
400 $currentPageWithNoOverlay = $this->sys_page->getRawRecord('pages', $GLOBALS['TSFE']->page['uid']);
401 // Traverse languages set up:
402 $languageItems = GeneralUtility::intExplode(',', $value);
403 foreach ($languageItems as $sUid) {
404 // Find overlay record:
405 if ($sUid) {
406 $lRecs = $this->sys_page->getPageOverlay($GLOBALS['TSFE']->page['uid'], $sUid);
407 } else {
408 $lRecs = array();
409 }
410 // Checking if the "disabled" state should be set.
411 if (GeneralUtility::hideIfNotTranslated($GLOBALS['TSFE']->page['l18n_cfg']) && $sUid && !count($lRecs) || $GLOBALS['TSFE']->page['l18n_cfg'] & 1 && (!$sUid || !count($lRecs)) || !$this->conf['special.']['normalWhenNoLanguage'] && $sUid && !count($lRecs)) {
412 $iState = $GLOBALS['TSFE']->sys_language_uid == $sUid ? 'USERDEF2' : 'USERDEF1';
413 } else {
414 $iState = $GLOBALS['TSFE']->sys_language_uid == $sUid ? 'ACT' : 'NO';
415 }
416 if ($this->conf['addQueryString']) {
417 $getVars = $this->parent_cObj->getQueryArguments($this->conf['addQueryString.'], array('L' => $sUid), TRUE);
418 } else {
419 $getVars = '&L=' . $sUid;
420 }
421 // Adding menu item:
422 $temp[] = array_merge(array_merge($currentPageWithNoOverlay, $lRecs), array(
423 'ITEM_STATE' => $iState,
424 '_ADD_GETVARS' => $getVars,
425 '_SAFE' => TRUE
426 ));
427 }
428 break;
429 case 'directory':
430 if ($value == '') {
431 $value = $GLOBALS['TSFE']->page['uid'];
432 }
433 $items = GeneralUtility::intExplode(',', $value);
434 foreach ($items as $id) {
435 $MP = $this->tmpl->getFromMPmap($id);
436 // Checking if a page is a mount page and if so, change the ID and set the MP var properly.
437 $mount_info = $this->sys_page->getMountPointInfo($id);
438 if (is_array($mount_info)) {
439 if ($mount_info['overlay']) {
440 // Overlays should already have their full MPvars calculated:
441 $MP = $this->tmpl->getFromMPmap($mount_info['mount_pid']);
442 $MP = $MP ? $MP : $mount_info['MPvar'];
443 } else {
444 $MP = ($MP ? $MP . ',' : '') . $mount_info['MPvar'];
445 }
446 $id = $mount_info['mount_pid'];
447 }
448 // Get sub-pages:
449 $res = $this->parent_cObj->exec_getQuery('pages', array('pidInList' => $id, 'orderBy' => $altSortField));
450 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
451 $GLOBALS['TSFE']->sys_page->versionOL('pages', $row, TRUE);
452 if (is_array($row)) {
453 // Keep mount point?
454 $mount_info = $this->sys_page->getMountPointInfo($row['uid'], $row);
455 // There is a valid mount point.
456 if (is_array($mount_info) && $mount_info['overlay']) {
457 // Using "getPage" is OK since we need the check for enableFields
458 // AND for type 2 of mount pids we DO require a doktype < 200!
459 $mp_row = $this->sys_page->getPage($mount_info['mount_pid']);
460 if (count($mp_row)) {
461 $row = $mp_row;
462 $row['_MP_PARAM'] = $mount_info['MPvar'];
463 } else {
464 // If the mount point could not be fetched with respect
465 // to enableFields, unset the row so it does not become a part of the menu!
466 unset($row);
467 }
468 }
469 // Add external MP params, then the row:
470 if (is_array($row)) {
471 if ($MP) {
472 $row['_MP_PARAM'] = $MP . ($row['_MP_PARAM'] ? ',' . $row['_MP_PARAM'] : '');
473 }
474 $temp[$row['uid']] = $this->sys_page->getPageOverlay($row);
475 }
476 }
477 }
478 }
479 break;
480 case 'list':
481 if ($value == '') {
482 $value = $this->id;
483 }
484 /** @var \TYPO3\CMS\Core\Database\RelationHandler $loadDB*/
485 $loadDB = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
486 $loadDB->setFetchAllFields(TRUE);
487 $loadDB->start($value, 'pages');
488 $loadDB->additionalWhere['pages'] = $this->parent_cObj->enableFields('pages');
489 $loadDB->getFromDB();
490 foreach ($loadDB->itemArray as $val) {
491 $MP = $this->tmpl->getFromMPmap($val['id']);
492 // Keep mount point?
493 $mount_info = $this->sys_page->getMountPointInfo($val['id']);
494 // There is a valid mount point.
495 if (is_array($mount_info) && $mount_info['overlay']) {
496 // Using "getPage" is OK since we need the check for enableFields
497 // AND for type 2 of mount pids we DO require a doktype < 200!
498 $mp_row = $this->sys_page->getPage($mount_info['mount_pid']);
499 if (count($mp_row)) {
500 $row = $mp_row;
501 $row['_MP_PARAM'] = $mount_info['MPvar'];
502 // Overlays should already have their full MPvars calculated
503 if ($mount_info['overlay']) {
504 $MP = $this->tmpl->getFromMPmap($mount_info['mount_pid']);
505 if ($MP) {
506 unset($row['_MP_PARAM']);
507 }
508 }
509 } else {
510 // If the mount point could not be fetched with respect to
511 // enableFields, unset the row so it does not become a part of the menu!
512 unset($row);
513 }
514 } else {
515 $row = $loadDB->results['pages'][$val['id']];
516 }
517 //Add versioning overlay for current page (to respect workspaces)
518 if (is_array($row)) {
519 $this->sys_page->versionOL('pages', $row, TRUE);
520 }
521 // Add external MP params, then the row:
522 if (is_array($row)) {
523 if ($MP) {
524 $row['_MP_PARAM'] = $MP . ($row['_MP_PARAM'] ? ',' . $row['_MP_PARAM'] : '');
525 }
526 $temp[] = $this->sys_page->getPageOverlay($row);
527 }
528 }
529 break;
530 case 'updated':
531 if ($value == '') {
532 $value = $GLOBALS['TSFE']->page['uid'];
533 }
534 $items = GeneralUtility::intExplode(',', $value);
535 if (MathUtility::canBeInterpretedAsInteger($this->conf['special.']['depth'])) {
536 $depth = MathUtility::forceIntegerInRange($this->conf['special.']['depth'], 1, 20);
537 } else {
538 $depth = 20;
539 }
540 // Max number of items
541 $limit = MathUtility::forceIntegerInRange($this->conf['special.']['limit'], 0, 100);
542 $maxAge = (int)$this->parent_cObj->calc($this->conf['special.']['maxAge']);
543 if (!$limit) {
544 $limit = 10;
545 }
546 // *'auto', 'manual', 'tstamp'
547 $mode = $this->conf['special.']['mode'];
548 // Get id's
549 $id_list_arr = array();
550 foreach ($items as $id) {
551 $bA = MathUtility::forceIntegerInRange($this->conf['special.']['beginAtLevel'], 0, 100);
552 $id_list_arr[] = \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::getTreeList(-1 * $id, $depth - 1 + $bA, $bA - 1);
553 }
554 $id_list = implode(',', $id_list_arr);
555 // Get sortField (mode)
556 switch ($mode) {
557 case 'starttime':
558 $sortField = 'starttime';
559 break;
560 case 'lastUpdated':
561
562 case 'manual':
563 $sortField = 'lastUpdated';
564 break;
565 case 'tstamp':
566 $sortField = 'tstamp';
567 break;
568 case 'crdate':
569 $sortField = 'crdate';
570 break;
571 default:
572 $sortField = 'SYS_LASTCHANGED';
573 }
574 // Get
575 $extraWhere = ($this->conf['includeNotInMenu'] ? '' : ' AND pages.nav_hide=0') . $this->getDoktypeExcludeWhere();
576 if ($this->conf['special.']['excludeNoSearchPages']) {
577 $extraWhere .= ' AND pages.no_search=0';
578 }
579 if ($maxAge > 0) {
580 $extraWhere .= ' AND ' . $sortField . '>' . ($GLOBALS['SIM_ACCESS_TIME'] - $maxAge);
581 }
582 $res = $this->parent_cObj->exec_getQuery('pages', array(
583 'pidInList' => '0',
584 'uidInList' => $id_list,
585 'where' => $sortField . '>=0' . $extraWhere,
586 'orderBy' => $altSortFieldValue ? $altSortFieldValue : $sortField . ' desc',
587 'max' => $limit
588 ));
589 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
590 $GLOBALS['TSFE']->sys_page->versionOL('pages', $row, TRUE);
591 if (is_array($row)) {
592 $temp[$row['uid']] = $this->sys_page->getPageOverlay($row);
593 }
594 }
595 break;
596 case 'keywords':
597 list($value) = GeneralUtility::intExplode(',', $value);
598 if (!$value) {
599 $value = $GLOBALS['TSFE']->page['uid'];
600 }
601 if ($this->conf['special.']['setKeywords'] || $this->conf['special.']['setKeywords.']) {
602 $kw = isset($this->conf['special.']['setKeywords.']) ? $this->parent_cObj->stdWrap($this->conf['special.']['setKeywords'], $this->conf['special.']['setKeywords.']) : $this->conf['special.']['setKeywords'];
603 } else {
604 // The page record of the 'value'.
605 $value_rec = $this->sys_page->getPage($value);
606 $kfieldSrc = $this->conf['special.']['keywordsField.']['sourceField'] ? $this->conf['special.']['keywordsField.']['sourceField'] : 'keywords';
607 // keywords.
608 $kw = trim(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::keywords($value_rec[$kfieldSrc]));
609 }
610 // *'auto', 'manual', 'tstamp'
611 $mode = $this->conf['special.']['mode'];
612 switch ($mode) {
613 case 'starttime':
614 $sortField = 'starttime';
615 break;
616 case 'lastUpdated':
617
618 case 'manual':
619 $sortField = 'lastUpdated';
620 break;
621 case 'tstamp':
622 $sortField = 'tstamp';
623 break;
624 case 'crdate':
625 $sortField = 'crdate';
626 break;
627 default:
628 $sortField = 'SYS_LASTCHANGED';
629 }
630 // Depth, limit, extra where
631 if (MathUtility::canBeInterpretedAsInteger($this->conf['special.']['depth'])) {
632 $depth = MathUtility::forceIntegerInRange($this->conf['special.']['depth'], 0, 20);
633 } else {
634 $depth = 20;
635 }
636 // Max number of items
637 $limit = MathUtility::forceIntegerInRange($this->conf['special.']['limit'], 0, 100);
638 $extraWhere = ' AND pages.uid<>' . $value . ($this->conf['includeNotInMenu'] ? '' : ' AND pages.nav_hide=0') . $this->getDoktypeExcludeWhere();
639 if ($this->conf['special.']['excludeNoSearchPages']) {
640 $extraWhere .= ' AND pages.no_search=0';
641 }
642 // Start point
643 $eLevel = $this->parent_cObj->getKey(isset($this->conf['special.']['entryLevel.']) ? $this->parent_cObj->stdWrap($this->conf['special.']['entryLevel'], $this->conf['special.']['entryLevel.']) : $this->conf['special.']['entryLevel'], $this->tmpl->rootLine);
644 $startUid = (int)$this->tmpl->rootLine[$eLevel]['uid'];
645 // Which field is for keywords
646 $kfield = 'keywords';
647 if ($this->conf['special.']['keywordsField']) {
648 list($kfield) = explode(' ', trim($this->conf['special.']['keywordsField']));
649 }
650 // If there are keywords and the startuid is present.
651 if ($kw && $startUid) {
652 $bA = MathUtility::forceIntegerInRange($this->conf['special.']['beginAtLevel'], 0, 100);
653 $id_list = \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::getTreeList(-1 * $startUid, $depth - 1 + $bA, $bA - 1);
654 $kwArr = explode(',', $kw);
655 foreach ($kwArr as $word) {
656 $word = trim($word);
657 if ($word) {
658 $keyWordsWhereArr[] = $kfield . ' LIKE \'%' . $GLOBALS['TYPO3_DB']->quoteStr($word, 'pages') . '%\'';
659 }
660 }
661 $res = $this->parent_cObj->exec_getQuery('pages', array('pidInList' => '0', 'uidInList' => $id_list, 'where' => '(' . implode(' OR ', $keyWordsWhereArr) . ')' . $extraWhere, 'orderBy' => $altSortFieldValue ? $altSortFieldValue : $sortField . ' desc', 'max' => $limit));
662 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
663 $GLOBALS['TSFE']->sys_page->versionOL('pages', $row, TRUE);
664 if (is_array($row)) {
665 $temp[$row['uid']] = $this->sys_page->getPageOverlay($row);
666 }
667 }
668 }
669 break;
670 case 'categories':
671 /** @var \TYPO3\CMS\Frontend\ContentObject\Menu\CategoryMenuUtility $categoryMenuUtility */
672 $categoryMenuUtility = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\ContentObject\\Menu\\CategoryMenuUtility');
673 $temp = $categoryMenuUtility->collectPages($value, $this->conf['special.'], $this);
674 break;
675 case 'rootline':
676 $range = isset($this->conf['special.']['range.']) ? $this->parent_cObj->stdWrap($this->conf['special.']['range'], $this->conf['special.']['range.']) : $this->conf['special.']['range'];
677 $begin_end = explode('|', $range);
678 $begin_end[0] = (int)$begin_end[0];
679 if (!MathUtility::canBeInterpretedAsInteger($begin_end[1])) {
680 $begin_end[1] = -1;
681 }
682 $beginKey = $this->parent_cObj->getKey($begin_end[0], $this->tmpl->rootLine);
683 $endKey = $this->parent_cObj->getKey($begin_end[1], $this->tmpl->rootLine);
684 if ($endKey < $beginKey) {
685 $endKey = $beginKey;
686 }
687 $rl_MParray = array();
688 foreach ($this->tmpl->rootLine as $k_rl => $v_rl) {
689 // For overlaid mount points, set the variable right now:
690 if ($v_rl['_MP_PARAM'] && $v_rl['_MOUNT_OL']) {
691 $rl_MParray[] = $v_rl['_MP_PARAM'];
692 }
693 // Traverse rootline:
694 if ($k_rl >= $beginKey && $k_rl <= $endKey) {
695 $temp_key = $k_rl;
696 $temp[$temp_key] = $this->sys_page->getPage($v_rl['uid']);
697 if (count($temp[$temp_key])) {
698 // If there are no specific target for the page, put the level specific target on.
699 if (!$temp[$temp_key]['target']) {
700 $temp[$temp_key]['target'] = $this->conf['special.']['targets.'][$k_rl];
701 $temp[$temp_key]['_MP_PARAM'] = implode(',', $rl_MParray);
702 }
703 } else {
704 unset($temp[$temp_key]);
705 }
706 }
707 // For normal mount points, set the variable for next level.
708 if ($v_rl['_MP_PARAM'] && !$v_rl['_MOUNT_OL']) {
709 $rl_MParray[] = $v_rl['_MP_PARAM'];
710 }
711 }
712 // Reverse order of elements (e.g. "1,2,3,4" gets "4,3,2,1"):
713 if (isset($this->conf['special.']['reverseOrder']) && $this->conf['special.']['reverseOrder']) {
714 $temp = array_reverse($temp);
715 $rl_MParray = array_reverse($rl_MParray);
716 }
717 break;
718 case 'browse':
719 list($value) = GeneralUtility::intExplode(',', $value);
720 if (!$value) {
721 $value = $GLOBALS['TSFE']->page['uid'];
722 }
723 // Will not work out of rootline
724 if ($value != $this->tmpl->rootLine[0]['uid']) {
725 $recArr = array();
726 // The page record of the 'value'.
727 $value_rec = $this->sys_page->getPage($value);
728 // 'up' page cannot be outside rootline
729 if ($value_rec['pid']) {
730 // The page record of 'up'.
731 $recArr['up'] = $this->sys_page->getPage($value_rec['pid']);
732 }
733 // If the 'up' item was NOT level 0 in rootline...
734 if ($recArr['up']['pid'] && $value_rec['pid'] != $this->tmpl->rootLine[0]['uid']) {
735 // The page record of "index".
736 $recArr['index'] = $this->sys_page->getPage($recArr['up']['pid']);
737 }
738 // prev / next is found
739 $prevnext_menu = $this->sys_page->getMenu($value_rec['pid'], '*', $altSortField);
740 $lastKey = 0;
741 $nextActive = 0;
742 foreach ($prevnext_menu as $k_b => $v_b) {
743 if ($nextActive) {
744 $recArr['next'] = $v_b;
745 $nextActive = 0;
746 }
747 if ($v_b['uid'] == $value) {
748 if ($lastKey) {
749 $recArr['prev'] = $prevnext_menu[$lastKey];
750 }
751 $nextActive = 1;
752 }
753 $lastKey = $k_b;
754 }
755 reset($prevnext_menu);
756 $recArr['first'] = pos($prevnext_menu);
757 end($prevnext_menu);
758 $recArr['last'] = pos($prevnext_menu);
759 // prevsection / nextsection is found
760 // You can only do this, if there is a valid page two levels up!
761 if (is_array($recArr['index'])) {
762 $prevnextsection_menu = $this->sys_page->getMenu($recArr['index']['uid'], '*', $altSortField);
763 $lastKey = 0;
764 $nextActive = 0;
765 foreach ($prevnextsection_menu as $k_b => $v_b) {
766 if ($nextActive) {
767 $sectionRec_temp = $this->sys_page->getMenu($v_b['uid'], '*', $altSortField);
768 if (count($sectionRec_temp)) {
769 reset($sectionRec_temp);
770 $recArr['nextsection'] = pos($sectionRec_temp);
771 end($sectionRec_temp);
772 $recArr['nextsection_last'] = pos($sectionRec_temp);
773 $nextActive = 0;
774 }
775 }
776 if ($v_b['uid'] == $value_rec['pid']) {
777 if ($lastKey) {
778 $sectionRec_temp = $this->sys_page->getMenu($prevnextsection_menu[$lastKey]['uid'], '*', $altSortField);
779 if (count($sectionRec_temp)) {
780 reset($sectionRec_temp);
781 $recArr['prevsection'] = pos($sectionRec_temp);
782 end($sectionRec_temp);
783 $recArr['prevsection_last'] = pos($sectionRec_temp);
784 }
785 }
786 $nextActive = 1;
787 }
788 $lastKey = $k_b;
789 }
790 }
791 if ($this->conf['special.']['items.']['prevnextToSection']) {
792 if (!is_array($recArr['prev']) && is_array($recArr['prevsection_last'])) {
793 $recArr['prev'] = $recArr['prevsection_last'];
794 }
795 if (!is_array($recArr['next']) && is_array($recArr['nextsection'])) {
796 $recArr['next'] = $recArr['nextsection'];
797 }
798 }
799 $items = explode('|', $this->conf['special.']['items']);
800 $c = 0;
801 foreach ($items as $k_b => $v_b) {
802 $v_b = strtolower(trim($v_b));
803 if ((int)$this->conf['special.'][$v_b . '.']['uid']) {
804 $recArr[$v_b] = $this->sys_page->getPage((int)$this->conf['special.'][$v_b . '.']['uid']);
805 }
806 if (is_array($recArr[$v_b])) {
807 $temp[$c] = $recArr[$v_b];
808 if ($this->conf['special.'][$v_b . '.']['target']) {
809 $temp[$c]['target'] = $this->conf['special.'][$v_b . '.']['target'];
810 }
811 $tmpSpecialFields = $this->conf['special.'][$v_b . '.']['fields.'];
812 if (is_array($tmpSpecialFields)) {
813 foreach ($tmpSpecialFields as $fk => $val) {
814 $temp[$c][$fk] = $val;
815 }
816 }
817 $c++;
818 }
819 }
820 }
821 break;
822 }
823 if ($this->mconf['sectionIndex']) {
824 $sectionIndexes = array();
825 foreach ($temp as $page) {
826 $sectionIndexes = $sectionIndexes + $this->sectionIndex($altSortField, $page['uid']);
827 }
828 $temp = $sectionIndexes;
829 }
830 } elseif (is_array($this->alternativeMenuTempArray)) {
831 // Setting $temp array if not level 1.
832 $temp = $this->alternativeMenuTempArray;
833 } elseif ($this->mconf['sectionIndex']) {
834 $temp = $this->sectionIndex($altSortField);
835 } else {
836 // Default:
837 // gets the menu
838 $temp = $this->sys_page->getMenu($this->id, '*', $altSortField);
839 }
840 $c = 0;
841 $c_b = 0;
842 $minItems = (int)($this->mconf['minItems'] ?: $this->conf['minItems']);
843 $maxItems = (int)($this->mconf['maxItems'] ?: $this->conf['maxItems']);
844 $begin = $this->parent_cObj->calc($this->mconf['begin'] ? $this->mconf['begin'] : $this->conf['begin']);
845 $minItemsConf = isset($this->mconf['minItems.']) ? $this->mconf['minItems.'] : (isset($this->conf['minItems.']) ? $this->conf['minItems.'] : NULL);
846 $minItems = is_array($minItemsConf) ? $this->parent_cObj->stdWrap($minItems, $minItemsConf) : $minItems;
847 $maxItemsConf = isset($this->mconf['maxItems.']) ? $this->mconf['maxItems.'] : (isset($this->conf['maxItems.']) ? $this->conf['maxItems.'] : NULL);
848 $maxItems = is_array($maxItemsConf) ? $this->parent_cObj->stdWrap($maxItems, $maxItemsConf) : $maxItems;
849 $beginConf = isset($this->mconf['begin.']) ? $this->mconf['begin.'] : (isset($this->conf['begin.']) ? $this->conf['begin.'] : NULL);
850 $begin = is_array($beginConf) ? $this->parent_cObj->stdWrap($begin, $beginConf) : $begin;
851 $banUidArray = $this->getBannedUids();
852 // Fill in the menuArr with elements that should go into the menu:
853 $this->menuArr = array();
854 foreach ($temp as $data) {
855 $spacer = GeneralUtility::inList($this->spacerIDList, $data['doktype']) || $data['ITEM_STATE'] === 'SPC' ? 1 : 0;
856 // if item is a spacer, $spacer is set
857 if ($this->filterMenuPages($data, $banUidArray, $spacer)) {
858 $c_b++;
859 // If the beginning item has been reached.
860 if ($begin <= $c_b) {
861 $this->menuArr[$c] = $data;
862 $this->menuArr[$c]['isSpacer'] = $spacer;
863 $c++;
864 if ($maxItems && $c >= $maxItems) {
865 break;
866 }
867 }
868 }
869 }
870 // Fill in fake items, if min-items is set.
871 if ($minItems) {
872 while ($c < $minItems) {
873 $this->menuArr[$c] = array(
874 'title' => '...',
875 'uid' => $GLOBALS['TSFE']->id
876 );
877 $c++;
878 }
879 }
880 // Passing the menuArr through a user defined function:
881 if ($this->mconf['itemArrayProcFunc']) {
882 if (!is_array($this->parentMenuArr)) {
883 $this->parentMenuArr = array();
884 }
885 $this->menuArr = $this->userProcess('itemArrayProcFunc', $this->menuArr);
886 }
887 // Setting number of menu items
888 $GLOBALS['TSFE']->register['count_menuItems'] = count($this->menuArr);
889 $this->hash = md5(serialize($this->menuArr) . serialize($this->mconf) . serialize($this->tmpl->rootLine) . serialize($this->MP_array));
890 // Get the cache timeout:
891 if ($this->conf['cache_period']) {
892 $cacheTimeout = $this->conf['cache_period'];
893 } else {
894 $cacheTimeout = $GLOBALS['TSFE']->get_cache_timeout();
895 }
896 $cachedData = $this->sys_page->getHash($this->hash);
897 if (!is_array($cachedData)) {
898 $this->generate();
899 $this->sys_page->storeHash($this->hash, $this->result, 'MENUDATA', $cacheTimeout);
900 } else {
901 $this->result = $cachedData;
902 }
903 // End showAccessRestrictedPages
904 if ($this->mconf['showAccessRestrictedPages']) {
905 // RESTORING where_groupAccess
906 $this->sys_page->where_groupAccess = $SAVED_where_groupAccess;
907 }
908 }
909 }
910
911 /**
912 * Checks if a page is OK to include in the final menu item array. Pages can be excluded if the doktype is wrong, if they are hidden in navigation, have a uid in the list of banned uids etc.
913 *
914 * @param array $data Array of menu items
915 * @param array $banUidArray Array of page uids which are to be excluded
916 * @param boolean $spacer If set, then the page is a spacer.
917 * @return boolean Returns TRUE if the page can be safely included.
918 * @todo Define visibility
919 */
920 public function filterMenuPages(&$data, $banUidArray, $spacer) {
921 $includePage = TRUE;
922 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/tslib/class.tslib_menu.php']['filterMenuPages'])) {
923 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/tslib/class.tslib_menu.php']['filterMenuPages'] as $classRef) {
924 $hookObject = GeneralUtility::getUserObj($classRef);
925 if (!$hookObject instanceof \TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuFilterPagesHookInterface) {
926 throw new \UnexpectedValueException('$hookObject must implement interface TYPO3\\CMS\\Frontend\\ContentObject\\Menu\\AbstractMenuFilterPagesHookInterface', 1269877402);
927 }
928 $includePage = $includePage && $hookObject->processFilter($data, $banUidArray, $spacer, $this);
929 }
930 }
931 if (!$includePage) {
932 return FALSE;
933 }
934 if ($data['_SAFE']) {
935 return TRUE;
936 }
937 $uid = $data['uid'];
938 // If the spacer-function is not enabled, spacers will not enter the $menuArr
939 if ($this->mconf['SPC'] || !$spacer) {
940 // Page may not be 'not_in_menu' or 'Backend User Section'
941 if (!GeneralUtility::inList($this->doktypeExcludeList, $data['doktype'])) {
942 // Not hidden in navigation
943 if (!$data['nav_hide'] || $this->conf['includeNotInMenu']) {
944 // not in banned uid's
945 if (!GeneralUtility::inArray($banUidArray, $uid)) {
946 // Checks if the default language version can be shown:
947 // Block page is set, if l18n_cfg allows plus: 1) Either default language or 2) another language but NO overlay record set for page!
948 $blockPage = $data['l18n_cfg'] & 1 && (!$GLOBALS['TSFE']->sys_language_uid || $GLOBALS['TSFE']->sys_language_uid && !$data['_PAGES_OVERLAY']);
949 if (!$blockPage) {
950 // Checking if a page should be shown in the menu depending on whether a translation exists:
951 $tok = TRUE;
952 // There is an alternative language active AND the current page requires a translation:
953 if ($GLOBALS['TSFE']->sys_language_uid && GeneralUtility::hideIfNotTranslated($data['l18n_cfg'])) {
954 if (!$data['_PAGES_OVERLAY']) {
955 $tok = FALSE;
956 }
957 }
958 // Continue if token is TRUE:
959 if ($tok) {
960 // Checking if "&L" should be modified so links to non-accessible pages will not happen.
961 if ($this->conf['protectLvar']) {
962 $languageUid = (int)$GLOBALS['TSFE']->config['config']['sys_language_uid'];
963 if ($languageUid && ($this->conf['protectLvar'] == 'all' || GeneralUtility::hideIfNotTranslated($data['l18n_cfg']))) {
964 $olRec = $GLOBALS['TSFE']->sys_page->getPageOverlay($data['uid'], $languageUid);
965 if (!count($olRec)) {
966 // If no pages_language_overlay record then page can NOT be accessed in the language pointed to by "&L" and therefore we protect the link by setting "&L=0"
967 $data['_ADD_GETVARS'] .= '&L=0';
968 }
969 }
970 }
971 return TRUE;
972 }
973 }
974 }
975 }
976 }
977 }
978 }
979
980 /**
981 * Generating the per-menu-item configuration arrays based on the settings for item states (NO, RO, ACT, CUR etc) set in ->mconf (config for the current menu object)
982 * Basically it will produce an individual array for each menu item based on the item states. BUT in addition the "optionSplit" syntax for the values is ALSO evaluated here so that all property-values are "option-splitted" and the output will thus be resolved.
983 * Is called from the "generate" functions in the extension classes. The function is processor intensive due to the option split feature in particular. But since the generate function is not always called (since the ->result array may be cached, see makeMenu) it doesn't hurt so badly.
984 *
985 * @param integer $splitCount Number of menu items in the menu
986 * @return array An array with two keys: array($NOconf,$ROconf) - where $NOconf contains the resolved configuration for each item when NOT rolled-over and $ROconf contains the ditto for the mouseover state (if any)
987 * @access private
988 * @todo Define visibility
989 */
990 public function procesItemStates($splitCount) {
991 // Prepare normal settings
992 if (!is_array($this->mconf['NO.']) && $this->mconf['NO']) {
993 // Setting a blank array if NO=1 and there are no properties.
994 $this->mconf['NO.'] = array();
995 }
996 $NOconf = $this->tmpl->splitConfArray($this->mconf['NO.'], $splitCount);
997 // Prepare rollOver settings, overriding normal settings
998 $ROconf = array();
999 if ($this->mconf['RO']) {
1000 $ROconf = $this->tmpl->splitConfArray($this->mconf['RO.'], $splitCount);
1001 }
1002 // Prepare IFSUB settings, overriding normal settings
1003 // IFSUB is TRUE if there exist submenu items to the current item
1004 if ($this->mconf['IFSUB']) {
1005 // Flag: If $IFSUB is generated
1006 $IFSUBinit = 0;
1007 foreach ($NOconf as $key => $val) {
1008 if ($this->isItemState('IFSUB', $key)) {
1009 // if this is the first IFSUB element, we must generate IFSUB.
1010 if (!$IFSUBinit) {
1011 $IFSUBconf = $this->tmpl->splitConfArray($this->mconf['IFSUB.'], $splitCount);
1012 if ($this->mconf['IFSUBRO']) {
1013 $IFSUBROconf = $this->tmpl->splitConfArray($this->mconf['IFSUBRO.'], $splitCount);
1014 }
1015 $IFSUBinit = 1;
1016 }
1017 // Substitute normal with ifsub
1018 $NOconf[$key] = $IFSUBconf[$key];
1019 // If rollOver on normal, we must apply a state for rollOver on the active
1020 if ($ROconf) {
1021 // If RollOver on active then apply this
1022 $ROconf[$key] = $IFSUBROconf[$key] ?: $IFSUBconf[$key];
1023 }
1024 }
1025 }
1026 }
1027 // Prepare active settings, overriding normal settings
1028 if ($this->mconf['ACT']) {
1029 // Flag: If $ACT is generated
1030 $ACTinit = 0;
1031 // Find active
1032 foreach ($NOconf as $key => $val) {
1033 if ($this->isItemState('ACT', $key)) {
1034 // If this is the first 'active', we must generate ACT.
1035 if (!$ACTinit) {
1036 $ACTconf = $this->tmpl->splitConfArray($this->mconf['ACT.'], $splitCount);
1037 // Prepare active rollOver settings, overriding normal active settings
1038 if ($this->mconf['ACTRO']) {
1039 $ACTROconf = $this->tmpl->splitConfArray($this->mconf['ACTRO.'], $splitCount);
1040 }
1041 $ACTinit = 1;
1042 }
1043 // Substitute normal with active
1044 $NOconf[$key] = $ACTconf[$key];
1045 // If rollOver on normal, we must apply a state for rollOver on the active
1046 if ($ROconf) {
1047 // If RollOver on active then apply this
1048 $ROconf[$key] = $ACTROconf[$key] ?: $ACTconf[$key];
1049 }
1050 }
1051 }
1052 }
1053 // Prepare ACT (active)/IFSUB settings, overriding normal settings
1054 // ACTIFSUB is TRUE if there exist submenu items to the current item and the current item is active
1055 if ($this->mconf['ACTIFSUB']) {
1056 // Flag: If $ACTIFSUB is generated
1057 $ACTIFSUBinit = 0;
1058 // Find active
1059 foreach ($NOconf as $key => $val) {
1060 if ($this->isItemState('ACTIFSUB', $key)) {
1061 // If this is the first 'active', we must generate ACTIFSUB.
1062 if (!$ACTIFSUBinit) {
1063 $ACTIFSUBconf = $this->tmpl->splitConfArray($this->mconf['ACTIFSUB.'], $splitCount);
1064 // Prepare active rollOver settings, overriding normal active settings
1065 if ($this->mconf['ACTIFSUBRO']) {
1066 $ACTIFSUBROconf = $this->tmpl->splitConfArray($this->mconf['ACTIFSUBRO.'], $splitCount);
1067 }
1068 $ACTIFSUBinit = 1;
1069 }
1070 // Substitute normal with active
1071 $NOconf[$key] = $ACTIFSUBconf[$key];
1072 // If rollOver on normal, we must apply a state for rollOver on the active
1073 if ($ROconf) {
1074 // If RollOver on active then apply this
1075 $ROconf[$key] = $ACTIFSUBROconf[$key] ?: $ACTIFSUBconf[$key];
1076 }
1077 }
1078 }
1079 }
1080 // Prepare CUR (current) settings, overriding normal settings
1081 // CUR is TRUE if the current page equals the item here!
1082 if ($this->mconf['CUR']) {
1083 // Flag: If $CUR is generated
1084 $CURinit = 0;
1085 foreach ($NOconf as $key => $val) {
1086 if ($this->isItemState('CUR', $key)) {
1087 // if this is the first 'current', we must generate CUR. Basically this control is just inherited
1088 // from the other implementations as current would only exist one time and thats it
1089 // (unless you use special-features of HMENU)
1090 if (!$CURinit) {
1091 $CURconf = $this->tmpl->splitConfArray($this->mconf['CUR.'], $splitCount);
1092 if ($this->mconf['CURRO']) {
1093 $CURROconf = $this->tmpl->splitConfArray($this->mconf['CURRO.'], $splitCount);
1094 }
1095 $CURinit = 1;
1096 }
1097 // Substitute normal with current
1098 $NOconf[$key] = $CURconf[$key];
1099 // If rollOver on normal, we must apply a state for rollOver on the active
1100 if ($ROconf) {
1101 // If RollOver on active then apply this
1102 $ROconf[$key] = $CURROconf[$key] ?: $CURconf[$key];
1103 }
1104 }
1105 }
1106 }
1107 // Prepare CUR (current)/IFSUB settings, overriding normal settings
1108 // CURIFSUB is TRUE if there exist submenu items to the current item and the current page equals the item here!
1109 if ($this->mconf['CURIFSUB']) {
1110 // Flag: If $CURIFSUB is generated
1111 $CURIFSUBinit = 0;
1112 foreach ($NOconf as $key => $val) {
1113 if ($this->isItemState('CURIFSUB', $key)) {
1114 // If this is the first 'current', we must generate CURIFSUB.
1115 if (!$CURIFSUBinit) {
1116 $CURIFSUBconf = $this->tmpl->splitConfArray($this->mconf['CURIFSUB.'], $splitCount);
1117 // Prepare current rollOver settings, overriding normal current settings
1118 if ($this->mconf['CURIFSUBRO']) {
1119 $CURIFSUBROconf = $this->tmpl->splitConfArray($this->mconf['CURIFSUBRO.'], $splitCount);
1120 }
1121 $CURIFSUBinit = 1;
1122 }
1123 // Substitute normal with active
1124 $NOconf[$key] = $CURIFSUBconf[$key];
1125 // If rollOver on normal, we must apply a state for rollOver on the current
1126 if ($ROconf) {
1127 // If RollOver on current then apply this
1128 $ROconf[$key] = $CURIFSUBROconf[$key] ?: $CURIFSUBconf[$key];
1129 }
1130 }
1131 }
1132 }
1133 // Prepare active settings, overriding normal settings
1134 if ($this->mconf['USR']) {
1135 // Flag: If $USR is generated
1136 $USRinit = 0;
1137 // Find active
1138 foreach ($NOconf as $key => $val) {
1139 if ($this->isItemState('USR', $key)) {
1140 // if this is the first active, we must generate USR.
1141 if (!$USRinit) {
1142 $USRconf = $this->tmpl->splitConfArray($this->mconf['USR.'], $splitCount);
1143 // Prepare active rollOver settings, overriding normal active settings
1144 if ($this->mconf['USRRO']) {
1145 $USRROconf = $this->tmpl->splitConfArray($this->mconf['USRRO.'], $splitCount);
1146 }
1147 $USRinit = 1;
1148 }
1149 // Substitute normal with active
1150 $NOconf[$key] = $USRconf[$key];
1151 // If rollOver on normal, we must apply a state for rollOver on the active
1152 if ($ROconf) {
1153 // If RollOver on active then apply this
1154 $ROconf[$key] = $USRROconf[$key] ?: $USRconf[$key];
1155 }
1156 }
1157 }
1158 }
1159 // Prepare spacer settings, overriding normal settings
1160 if ($this->mconf['SPC']) {
1161 // Flag: If $SPC is generated
1162 $SPCinit = 0;
1163 // Find spacers
1164 foreach ($NOconf as $key => $val) {
1165 if ($this->isItemState('SPC', $key)) {
1166 // If this is the first spacer, we must generate SPC.
1167 if (!$SPCinit) {
1168 $SPCconf = $this->tmpl->splitConfArray($this->mconf['SPC.'], $splitCount);
1169 $SPCinit = 1;
1170 }
1171 // Substitute normal with spacer
1172 $NOconf[$key] = $SPCconf[$key];
1173 }
1174 }
1175 }
1176 // Prepare Userdefined settings
1177 if ($this->mconf['USERDEF1']) {
1178 // Flag: If $USERDEF1 is generated
1179 $USERDEF1init = 0;
1180 // Find active
1181 foreach ($NOconf as $key => $val) {
1182 if ($this->isItemState('USERDEF1', $key)) {
1183 // If this is the first active, we must generate USERDEF1.
1184 if (!$USERDEF1init) {
1185 $USERDEF1conf = $this->tmpl->splitConfArray($this->mconf['USERDEF1.'], $splitCount);
1186 // Prepare active rollOver settings, overriding normal active settings
1187 if ($this->mconf['USERDEF1RO']) {
1188 $USERDEF1ROconf = $this->tmpl->splitConfArray($this->mconf['USERDEF1RO.'], $splitCount);
1189 }
1190 $USERDEF1init = 1;
1191 }
1192 // Substitute normal with active
1193 $NOconf[$key] = $USERDEF1conf[$key];
1194 // If rollOver on normal, we must apply a state for rollOver on the active
1195 if ($ROconf) {
1196 // If RollOver on active then apply this
1197 $ROconf[$key] = $USERDEF1ROconf[$key] ?: $USERDEF1conf[$key];
1198 }
1199 }
1200 }
1201 }
1202 // Prepare Userdefined settings
1203 if ($this->mconf['USERDEF2']) {
1204 // Flag: If $USERDEF2 is generated
1205 $USERDEF2init = 0;
1206 // Find active
1207 foreach ($NOconf as $key => $val) {
1208 if ($this->isItemState('USERDEF2', $key)) {
1209 // If this is the first active, we must generate USERDEF2.
1210 if (!$USERDEF2init) {
1211 $USERDEF2conf = $this->tmpl->splitConfArray($this->mconf['USERDEF2.'], $splitCount);
1212 // Prepare active rollOver settings, overriding normal active settings
1213 if ($this->mconf['USERDEF2RO']) {
1214 $USERDEF2ROconf = $this->tmpl->splitConfArray($this->mconf['USERDEF2RO.'], $splitCount);
1215 }
1216 $USERDEF2init = 1;
1217 }
1218 // Substitute normal with active
1219 $NOconf[$key] = $USERDEF2conf[$key];
1220 // If rollOver on normal, we must apply a state for rollOver on the active
1221 if ($ROconf) {
1222 // If RollOver on active then apply this
1223 $ROconf[$key] = $USERDEF2ROconf[$key] ?: $USERDEF2conf[$key];
1224 }
1225 }
1226 }
1227 }
1228 return array($NOconf, $ROconf);
1229 }
1230
1231 /**
1232 * Creates the URL, target and onclick values for the menu item link. Returns them in an array as key/value pairs for <A>-tag attributes
1233 * This function doesn't care about the url, because if we let the url be redirected, it will be logged in the stat!!!
1234 *
1235 * @param integer $key Pointer to a key in the $this->menuArr array where the value for that key represents the menu item we are linking to (page record)
1236 * @param string $altTarget Alternative target
1237 * @param integer $typeOverride Alternative type
1238 * @return array Returns an array with A-tag attributes as key/value pairs (HREF, TARGET and onClick)
1239 * @access private
1240 * @todo Define visibility
1241 */
1242 public function link($key, $altTarget = '', $typeOverride = '') {
1243 // Mount points:
1244 $MP_var = $this->getMPvar($key);
1245 $MP_params = $MP_var ? '&MP=' . rawurlencode($MP_var) : '';
1246 // Setting override ID
1247 if ($this->mconf['overrideId'] || $this->menuArr[$key]['overrideId']) {
1248 $overrideArray = array();
1249 // If a user script returned the value overrideId in the menu array we use that as page id
1250 $overrideArray['uid'] = $this->mconf['overrideId'] ?: $this->menuArr[$key]['overrideId'];
1251 $overrideArray['alias'] = '';
1252 // Clear MP parameters since ID was changed.
1253 $MP_params = '';
1254 } else {
1255 $overrideArray = '';
1256 }
1257 // Setting main target:
1258 if ($altTarget) {
1259 $mainTarget = $altTarget;
1260 } elseif ($this->mconf['target.']) {
1261 $mainTarget = $this->parent_cObj->stdWrap($this->mconf['target'], $this->mconf['target.']);
1262 } else {
1263 $mainTarget = $this->mconf['target'];
1264 }
1265 // Creating link:
1266 if ($this->mconf['collapse'] && $this->isActive($this->menuArr[$key]['uid'], $this->getMPvar($key))) {
1267 $thePage = $this->sys_page->getPage($this->menuArr[$key]['pid']);
1268 $LD = $this->menuTypoLink($thePage, $mainTarget, '', '', $overrideArray, $this->mconf['addParams'] . $MP_params . $this->menuArr[$key]['_ADD_GETVARS'], $typeOverride);
1269 } else {
1270 $LD = $this->menuTypoLink($this->menuArr[$key], $mainTarget, '', '', $overrideArray, $this->mconf['addParams'] . $MP_params . $this->I['val']['additionalParams'] . $this->menuArr[$key]['_ADD_GETVARS'], $typeOverride);
1271 }
1272 // Override URL if using "External URL" as doktype with a valid e-mail address:
1273 if ($this->menuArr[$key]['doktype'] == \TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_LINK && $this->menuArr[$key]['urltype'] == 3 && GeneralUtility::validEmail($this->menuArr[$key]['url'])) {
1274 // Create mailto-link using \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::typolink (concerning spamProtectEmailAddresses):
1275 $LD['totalURL'] = $this->parent_cObj->typoLink_URL(array('parameter' => $this->menuArr[$key]['url']));
1276 $LD['target'] = '';
1277 }
1278 // Override url if current page is a shortcut
1279 if ($this->menuArr[$key]['doktype'] == \TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_SHORTCUT && $this->menuArr[$key]['shortcut_mode'] != \TYPO3\CMS\Frontend\Page\PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
1280 $shortcut = NULL;
1281 try {
1282 $shortcut = $GLOBALS['TSFE']->getPageShortcut($this->menuArr[$key]['shortcut'], $this->menuArr[$key]['shortcut_mode'], $this->menuArr[$key]['uid']);
1283 } catch (\Exception $ex) {
1284
1285 }
1286 if (!is_array($shortcut)) {
1287 return array();
1288 }
1289 // Only setting url, not target
1290 $LD['totalURL'] = $this->parent_cObj->typoLink_URL(array(
1291 'parameter' => $shortcut['uid'],
1292 'additionalParams' => $this->mconf['addParams'] . $MP_params . $this->I['val']['additionalParams'] . $this->menuArr[$key]['_ADD_GETVARS']
1293 ));
1294 }
1295 // Manipulation in case of access restricted pages:
1296 $this->changeLinksForAccessRestrictedPages($LD, $this->menuArr[$key], $mainTarget, $typeOverride);
1297 // Overriding URL / Target if set to do so:
1298 if ($this->menuArr[$key]['_OVERRIDE_HREF']) {
1299 $LD['totalURL'] = $this->menuArr[$key]['_OVERRIDE_HREF'];
1300 if ($this->menuArr[$key]['_OVERRIDE_TARGET']) {
1301 $LD['target'] = $this->menuArr[$key]['_OVERRIDE_TARGET'];
1302 }
1303 }
1304 // OnClick open in windows.
1305 $onClick = '';
1306 if ($this->mconf['JSWindow']) {
1307 $conf = $this->mconf['JSWindow.'];
1308 $url = $LD['totalURL'];
1309 $LD['totalURL'] = '#';
1310 $onClick = 'openPic(\'' . $GLOBALS['TSFE']->baseUrlWrap($url) . '\',\'' . ($conf['newWindow'] ? md5($url) : 'theNewPage') . '\',\'' . $conf['params'] . '\'); return false;';
1311 $GLOBALS['TSFE']->setJS('openPic');
1312 }
1313 // look for type and popup
1314 // following settings are valid in field target:
1315 // 230 will add type=230 to the link
1316 // 230 500x600 will add type=230 to the link and open in popup window with 500x600 pixels
1317 // 230 _blank will add type=230 to the link and open with target "_blank"
1318 // 230x450:resizable=0,location=1 will open in popup window with 500x600 pixels with settings "resizable=0,location=1"
1319 $matches = array();
1320 $targetIsType = $LD['target'] && MathUtility::canBeInterpretedAsInteger($LD['target']) ? (int)$LD['target'] : FALSE;
1321 if (preg_match('/([0-9]+[\\s])?(([0-9]+)x([0-9]+))?(:.+)?/s', $LD['target'], $matches) || $targetIsType) {
1322 // has type?
1323 if ((int)$matches[1] || $targetIsType) {
1324 $LD['totalURL'] = $this->parent_cObj->URLqMark($LD['totalURL'], '&type=' . ($targetIsType ?: (int)$matches[1]));
1325 $LD['target'] = $targetIsType ? '' : trim(substr($LD['target'], strlen($matches[1]) + 1));
1326 }
1327 // Open in popup window?
1328 if ($matches[3] && $matches[4]) {
1329 $JSparamWH = 'width=' . $matches[3] . ',height=' . $matches[4] . ($matches[5] ? ',' . substr($matches[5], 1) : '');
1330 $onClick = 'vHWin=window.open('
1331 . GeneralUtility::quoteJSvalue($GLOBALS['TSFE']->baseUrlWrap($LD['totalURL']))
1332 . ',\'FEopenLink\',\'' . $JSparamWH . '\');vHWin.focus();return false;';
1333 $LD['target'] = '';
1334 }
1335 }
1336 // out:
1337 $list = array();
1338 // Added this check: What it does is to enter the baseUrl (if set, which it should for "realurl" based sites)
1339 // as URL if the calculated value is empty. The problem is that no link is generated with a blank URL
1340 // and blank URLs might appear when the realurl encoding is used and a link to the frontpage is generated.
1341 $list['HREF'] = strlen($LD['totalURL']) ? $LD['totalURL'] : $GLOBALS['TSFE']->baseUrl;
1342 $list['TARGET'] = $LD['target'];
1343 $list['onClick'] = $onClick;
1344 return $list;
1345 }
1346
1347 /**
1348 * Will change $LD (passed by reference) if the page is access restricted
1349 *
1350 * @param array $LD The array from the linkData() function
1351 * @param array $page Page array
1352 * @param string $mainTarget Main target value
1353 * @param string $typeOverride Type number override if any
1354 * @return void ($LD passed by reference might be changed.)
1355 * @todo Define visibility
1356 */
1357 public function changeLinksForAccessRestrictedPages(&$LD, $page, $mainTarget, $typeOverride) {
1358 // If access restricted pages should be shown in menus, change the link of such pages to link to a redirection page:
1359 if ($this->mconf['showAccessRestrictedPages'] && $this->mconf['showAccessRestrictedPages'] !== 'NONE' && !$GLOBALS['TSFE']->checkPageGroupAccess($page)) {
1360 $thePage = $this->sys_page->getPage($this->mconf['showAccessRestrictedPages']);
1361 $addParams = str_replace(
1362 array(
1363 '###RETURN_URL###',
1364 '###PAGE_ID###'
1365 ),
1366 array(
1367 rawurlencode($LD['totalURL']),
1368 $page['uid']
1369 ),
1370 $this->mconf['showAccessRestrictedPages.']['addParams']
1371 );
1372 $LD = $this->menuTypoLink($thePage, $mainTarget, '', '', '', $addParams, $typeOverride);
1373 }
1374 }
1375
1376 /**
1377 * Creates a submenu level to the current level - if configured for.
1378 *
1379 * @param integer $uid Page id of the current page for which a submenu MAY be produced (if conditions are met)
1380 * @param string $objSuffix Object prefix, see ->start()
1381 * @return string HTML content of the submenu
1382 * @access private
1383 * @todo Define visibility
1384 */
1385 public function subMenu($uid, $objSuffix = '') {
1386 // Setting alternative menu item array if _SUB_MENU has been defined in the current ->menuArr
1387 $altArray = '';
1388 if (is_array($this->menuArr[$this->I['key']]['_SUB_MENU']) && count($this->menuArr[$this->I['key']]['_SUB_MENU'])) {
1389 $altArray = $this->menuArr[$this->I['key']]['_SUB_MENU'];
1390 }
1391 // Make submenu if the page is the next active
1392 $menuType = $this->conf[($this->menuNumber + 1) . $objSuffix];
1393 // stdWrap for expAll
1394 if (isset($this->mconf['expAll.'])) {
1395 $this->mconf['expAll'] = $this->parent_cObj->stdWrap($this->mconf['expAll'], $this->mconf['expAll.']);
1396 }
1397 if (($this->mconf['expAll'] || $this->isNext($uid, $this->getMPvar($this->I['key'])) || is_array($altArray)) && !$this->mconf['sectionIndex']) {
1398 try {
1399 $menuObjectFactory = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\ContentObject\\Menu\\MenuContentObjectFactory');
1400 $submenu = $menuObjectFactory->getMenuObjectByType($menuType);
1401 $submenu->entryLevel = $this->entryLevel + 1;
1402 $submenu->rL_uidRegister = $this->rL_uidRegister;
1403 $submenu->MP_array = $this->MP_array;
1404 if ($this->menuArr[$this->I['key']]['_MP_PARAM']) {
1405 $submenu->MP_array[] = $this->menuArr[$this->I['key']]['_MP_PARAM'];
1406 }
1407 // Especially scripts that build the submenu needs the parent data
1408 $submenu->parent_cObj = $this->parent_cObj;
1409 $submenu->parentMenuArr = $this->menuArr;
1410 // Setting alternativeMenuTempArray (will be effective only if an array)
1411 if (is_array($altArray)) {
1412 $submenu->alternativeMenuTempArray = $altArray;
1413 }
1414 if ($submenu->start($this->tmpl, $this->sys_page, $uid, $this->conf, $this->menuNumber + 1, $objSuffix)) {
1415 $submenu->makeMenu();
1416 // Memorize the current menu item count
1417 $tempCountMenuObj = $GLOBALS['TSFE']->register['count_MENUOBJ'];
1418 // Reset the menu item count for the submenu
1419 $GLOBALS['TSFE']->register['count_MENUOBJ'] = 0;
1420 $content = $submenu->writeMenu();
1421 // Restore the item count now that the submenu has been handled
1422 $GLOBALS['TSFE']->register['count_MENUOBJ'] = $tempCountMenuObj;
1423 $GLOBALS['TSFE']->register['count_menuItems'] = count($this->menuArr);
1424 return $content;
1425 }
1426 } catch (\TYPO3\CMS\Frontend\ContentObject\Menu\Exception\NoSuchMenuTypeException $e) {
1427 }
1428 }
1429 }
1430
1431 /**
1432 * Returns TRUE if the page with UID $uid is the NEXT page in root line (which means a submenu should be drawn)
1433 *
1434 * @param integer $uid Page uid to evaluate.
1435 * @param string $MPvar MPvar for the current position of item.
1436 * @return boolean TRUE if page with $uid is active
1437 * @access private
1438 * @see subMenu()
1439 * @todo Define visibility
1440 */
1441 public function isNext($uid, $MPvar = '') {
1442 // Check for always active PIDs:
1443 if (count($this->alwaysActivePIDlist) && in_array($uid, $this->alwaysActivePIDlist)) {
1444 return TRUE;
1445 }
1446 $testUid = $uid . ($MPvar ? ':' . $MPvar : '');
1447 if ($uid && $testUid == $this->nextActive) {
1448 return TRUE;
1449 }
1450 }
1451
1452 /**
1453 * Returns TRUE if the page with UID $uid is active (in the current rootline)
1454 *
1455 * @param integer $uid Page uid to evaluate.
1456 * @param string $MPvar MPvar for the current position of item.
1457 * @return boolean TRUE if page with $uid is active
1458 * @access private
1459 * @todo Define visibility
1460 */
1461 public function isActive($uid, $MPvar = '') {
1462 // Check for always active PIDs:
1463 if (count($this->alwaysActivePIDlist) && in_array($uid, $this->alwaysActivePIDlist)) {
1464 return TRUE;
1465 }
1466 $testUid = $uid . ($MPvar ? ':' . $MPvar : '');
1467 if ($uid && in_array('ITEM:' . $testUid, $this->rL_uidRegister)) {
1468 return TRUE;
1469 }
1470 }
1471
1472 /**
1473 * Returns TRUE if the page with UID $uid is the CURRENT page (equals $GLOBALS['TSFE']->id)
1474 *
1475 * @param integer $uid Page uid to evaluate.
1476 * @param string $MPvar MPvar for the current position of item.
1477 * @return boolean TRUE if page $uid = $GLOBALS['TSFE']->id
1478 * @access private
1479 * @todo Define visibility
1480 */
1481 public function isCurrent($uid, $MPvar = '') {
1482 $testUid = $uid . ($MPvar ? ':' . $MPvar : '');
1483 if ($uid && end($this->rL_uidRegister) === 'ITEM:' . $testUid) {
1484 return TRUE;
1485 }
1486 }
1487
1488 /**
1489 * Returns TRUE if there is a submenu with items for the page id, $uid
1490 * Used by the item states "IFSUB", "ACTIFSUB" and "CURIFSUB" to check if there is a submenu
1491 *
1492 * @param integer $uid Page uid for which to search for a submenu
1493 * @return boolean Returns TRUE if there was a submenu with items found
1494 * @access private
1495 * @todo Define visibility
1496 */
1497 public function isSubMenu($uid) {
1498 // Looking for a mount-pid for this UID since if that
1499 // exists we should look for a subpages THERE and not in the input $uid;
1500 $mount_info = $this->sys_page->getMountPointInfo($uid);
1501 if (is_array($mount_info)) {
1502 $uid = $mount_info['mount_pid'];
1503 }
1504 $recs = $this->sys_page->getMenu($uid, 'uid,pid,doktype,mount_pid,mount_pid_ol,nav_hide,shortcut,shortcut_mode,l18n_cfg');
1505 $hasSubPages = FALSE;
1506 $bannedUids = $this->getBannedUids();
1507 foreach ($recs as $theRec) {
1508 // no valid subpage if the document type is excluded from the menu
1509 if (GeneralUtility::inList($this->doktypeExcludeList, $theRec['doktype'])) {
1510 continue;
1511 }
1512 // No valid subpage if the page is hidden inside menus and
1513 // it wasn't forced to show such entries
1514 if ($theRec['nav_hide'] && !$this->conf['includeNotInMenu']) {
1515 continue;
1516 }
1517 // No valid subpage if the default language should be shown and the page settings
1518 // are excluding the visibility of the default language
1519 if (!$GLOBALS['TSFE']->sys_language_uid && GeneralUtility::hideIfDefaultLanguage($theRec['l18n_cfg'])) {
1520 continue;
1521 }
1522 // No valid subpage if the alternative language should be shown and the page settings
1523 // are requiring a valid overlay but it doesn't exists
1524 $hideIfNotTranslated = GeneralUtility::hideIfNotTranslated($theRec['l18n_cfg']);
1525 if ($GLOBALS['TSFE']->sys_language_uid && $hideIfNotTranslated && !$theRec['_PAGES_OVERLAY']) {
1526 continue;
1527 }
1528 // No valid subpage if the subpage is banned by excludeUidList
1529 if (in_array($theRec['uid'], $bannedUids)) {
1530 continue;
1531 }
1532 $hasSubPages = TRUE;
1533 break;
1534 }
1535 return $hasSubPages;
1536 }
1537
1538 /**
1539 * Used by procesItemStates() to evaluate if a menu item (identified by $key) is in a certain state.
1540 *
1541 * @param string $kind The item state to evaluate (SPC, IFSUB, ACT etc... but no xxxRO states of course)
1542 * @param integer $key Key pointing to menu item from ->menuArr
1543 * @return boolean True (integer!=0) if match, otherwise FALSE (=0, zero)
1544 * @access private
1545 * @see procesItemStates()
1546 * @todo Define visibility
1547 */
1548 public function isItemState($kind, $key) {
1549 $natVal = 0;
1550 // If any value is set for ITEM_STATE the normal evaluation is discarded
1551 if ($this->menuArr[$key]['ITEM_STATE']) {
1552 if ((string)$this->menuArr[$key]['ITEM_STATE'] === (string)$kind) {
1553 $natVal = 1;
1554 }
1555 } else {
1556 switch ($kind) {
1557 case 'SPC':
1558 $natVal = $this->menuArr[$key]['isSpacer'];
1559 break;
1560 case 'IFSUB':
1561 $natVal = $this->isSubMenu($this->menuArr[$key]['uid']);
1562 break;
1563 case 'ACT':
1564 $natVal = $this->isActive($this->menuArr[$key]['uid'], $this->getMPvar($key));
1565 break;
1566 case 'ACTIFSUB':
1567 $natVal = $this->isActive($this->menuArr[$key]['uid'], $this->getMPvar($key)) && $this->isSubMenu($this->menuArr[$key]['uid']);
1568 break;
1569 case 'CUR':
1570 $natVal = $this->isCurrent($this->menuArr[$key]['uid'], $this->getMPvar($key));
1571 break;
1572 case 'CURIFSUB':
1573 $natVal = $this->isCurrent($this->menuArr[$key]['uid'], $this->getMPvar($key)) && $this->isSubMenu($this->menuArr[$key]['uid']);
1574 break;
1575 case 'USR':
1576 $natVal = $this->menuArr[$key]['fe_group'];
1577 break;
1578 }
1579 }
1580 return $natVal;
1581 }
1582
1583 /**
1584 * Creates an access-key for a TMENU/GMENU menu item based on the menu item titles first letter
1585 *
1586 * @param string $title Menu item title.
1587 * @return array Returns an array with keys "code" ("accesskey" attribute for the img-tag) and "alt" (text-addition to the "alt" attribute) if an access key was defined. Otherwise array was empty
1588 * @access private
1589 * @todo Define visibility
1590 */
1591 public function accessKey($title) {
1592 // The global array ACCESSKEY is used to globally control if letters are already used!!
1593 $result = array();
1594 $title = trim(strip_tags($title));
1595 $titleLen = strlen($title);
1596 for ($a = 0; $a < $titleLen; $a++) {
1597 $key = strtoupper(substr($title, $a, 1));
1598 if (preg_match('/[A-Z]/', $key) && !isset($GLOBALS['TSFE']->accessKey[$key])) {
1599 $GLOBALS['TSFE']->accessKey[$key] = 1;
1600 $result['code'] = ' accesskey="' . $key . '"';
1601 $result['alt'] = ' (ALT+' . $key . ')';
1602 $result['key'] = $key;
1603 break;
1604 }
1605 }
1606 return $result;
1607 }
1608
1609 /**
1610 * Calls a user function for processing of internal data.
1611 * Used for the properties "IProcFunc" and "itemArrayProcFunc"
1612 *
1613 * @param string $mConfKey Key pointing for the property in the current ->mconf array holding possibly parameters to pass along to the function/method. Currently the keys used are "IProcFunc" and "itemArrayProcFunc".
1614 * @param mixed $passVar A variable to pass to the user function and which should be returned again from the user function. The idea is that the user function modifies this variable according to what you want to achieve and then returns it. For "itemArrayProcFunc" this variable is $this->menuArr, for "IProcFunc" it is $this->I
1615 * @return mixed The processed $passVar
1616 * @access private
1617 * @todo Define visibility
1618 */
1619 public function userProcess($mConfKey, $passVar) {
1620 if ($this->mconf[$mConfKey]) {
1621 $funcConf = $this->mconf[$mConfKey . '.'];
1622 $funcConf['parentObj'] = $this;
1623 $passVar = $this->parent_cObj->callUserFunction($this->mconf[$mConfKey], $funcConf, $passVar);
1624 }
1625 return $passVar;
1626 }
1627
1628 /**
1629 * Creates the <A> tag parts for the current item (in $this->I, [A1] and [A2]) based on other information in this array (like $this->I['linkHREF'])
1630 *
1631 * @return void
1632 * @access private
1633 * @todo Define visibility
1634 */
1635 public function setATagParts() {
1636 $this->I['A1'] = '<a ' . GeneralUtility::implodeAttributes($this->I['linkHREF'], 1) . ' ' . $this->I['val']['ATagParams'] . $this->I['accessKey']['code'] . '>';
1637 $this->I['A2'] = '</a>';
1638 }
1639
1640 /**
1641 * Returns the title for the navigation
1642 *
1643 * @param string $title The current page title
1644 * @param string $nav_title The current value of the navigation title
1645 * @return string Returns the navigation title if it is NOT blank, otherwise the page title.
1646 * @access private
1647 * @todo Define visibility
1648 */
1649 public function getPageTitle($title, $nav_title) {
1650 return trim($nav_title) !== '' ? $nav_title : $title;
1651 }
1652
1653 /**
1654 * Return MPvar string for entry $key in ->menuArr
1655 *
1656 * @param integer $key Pointer to element in ->menuArr
1657 * @return string MP vars for element.
1658 * @see link()
1659 * @todo Define visibility
1660 */
1661 public function getMPvar($key) {
1662 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) {
1663 $localMP_array = $this->MP_array;
1664 // NOTICE: "_MP_PARAM" is allowed to be a commalist of PID pairs!
1665 if ($this->menuArr[$key]['_MP_PARAM']) {
1666 $localMP_array[] = $this->menuArr[$key]['_MP_PARAM'];
1667 }
1668 $MP_params = count($localMP_array) ? implode(',', $localMP_array) : '';
1669 return $MP_params;
1670 }
1671 }
1672
1673 /**
1674 * Returns where clause part to exclude 'not in menu' pages
1675 *
1676 * @return string where clause part.
1677 * @access private
1678 * @todo Define visibility
1679 */
1680 public function getDoktypeExcludeWhere() {
1681 return $this->doktypeExcludeList ? ' AND pages.doktype NOT IN (' . $this->doktypeExcludeList . ')' : '';
1682 }
1683
1684 /**
1685 * Returns an array of banned UIDs (from excludeUidList)
1686 *
1687 * @return array Array of banned UIDs
1688 * @access private
1689 * @todo Define visibility
1690 */
1691 public function getBannedUids() {
1692 $excludeUidList = isset($this->conf['excludeUidList.'])
1693 ? $this->parent_cObj->stdWrap($this->conf['excludeUidList'], $this->conf['excludeUidList.'])
1694 : $this->conf['excludeUidList'];
1695
1696 if (!trim($excludeUidList)) {
1697 return array();
1698 }
1699
1700 $banUidList = str_replace('current', $GLOBALS['TSFE']->page['uid'], $excludeUidList);
1701 return GeneralUtility::intExplode(',', $banUidList);
1702 }
1703
1704 /**
1705 * Calls typolink to create menu item links.
1706 *
1707 * @param array $page Page record (uid points where to link to)
1708 * @param string $oTarget Target frame/window
1709 * @param boolean $no_cache TRUE if caching should be disabled
1710 * @param string $script Alternative script name
1711 * @param array $overrideArray Array to override values in $page
1712 * @param string $addParams Parameters to add to URL
1713 * @param array $typeOverride "type" value
1714 * @return array See linkData
1715 * @todo Define visibility
1716 */
1717 public function menuTypoLink($page, $oTarget, $no_cache, $script, $overrideArray = '', $addParams = '', $typeOverride = '') {
1718 $conf = array(
1719 'parameter' => is_array($overrideArray) && $overrideArray['uid'] ? $overrideArray['uid'] : $page['uid']
1720 );
1721 if ($typeOverride && MathUtility::canBeInterpretedAsInteger($typeOverride)) {
1722 $conf['parameter'] .= ',' . $typeOverride;
1723 }
1724 if ($addParams) {
1725 $conf['additionalParams'] = $addParams;
1726 }
1727 if ($no_cache) {
1728 $conf['no_cache'] = TRUE;
1729 }
1730 if ($oTarget) {
1731 $conf['target'] = $oTarget;
1732 }
1733 if ($page['sectionIndex_uid']) {
1734 $conf['section'] = $page['sectionIndex_uid'];
1735 }
1736 $this->parent_cObj->typoLink('|', $conf);
1737 $LD = $this->parent_cObj->lastTypoLinkLD;
1738 $LD['totalURL'] = $this->parent_cObj->lastTypoLinkUrl;
1739 return $LD;
1740 }
1741
1742 /**
1743 * Generates a list of content objects with sectionIndex enabled
1744 * available on a specific page
1745 *
1746 * Used for menus with sectionIndex enabled
1747 *
1748 * @param string $altSortField Alternative sorting field
1749 * @param integer $pid The page id to search for sections
1750 * @throws UnexpectedValueException if the query to fetch the content elements unexpectedly fails
1751 * @return array
1752 */
1753 protected function sectionIndex($altSortField, $pid = NULL) {
1754 $pid = (int)($pid ?: $this->id);
1755 $basePageRow = $this->sys_page->getPage($pid);
1756 if (!is_array($basePageRow)) {
1757 return array();
1758 }
1759 $configuration = $this->mconf['sectionIndex.'];
1760 $useColPos = 0;
1761 if (trim($configuration['useColPos']) !== '' || is_array($configuration['useColPos.'])) {
1762 $useColPos = $GLOBALS['TSFE']->cObj->stdWrap($configuration['useColPos'], $configuration['useColPos.']);
1763 $useColPos = (int)$useColPos;
1764 }
1765 $selectSetup = array(
1766 'pidInList' => $pid,
1767 'orderBy' => $altSortField,
1768 'languageField' => 'sys_language_uid',
1769 'where' => $useColPos >= 0 ? 'colPos=' . $useColPos : ''
1770 );
1771 $resource = $this->parent_cObj->exec_getQuery('tt_content', $selectSetup);
1772 if (!$resource) {
1773 $message = 'SectionIndex: Query to fetch the content elements failed!';
1774 throw new \UnexpectedValueException($message, 1337334849);
1775 }
1776 $result = array();
1777 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($resource)) {
1778 $this->sys_page->versionOL('tt_content', $row);
1779 if ($GLOBALS['TSFE']->sys_language_contentOL && $basePageRow['_PAGES_OVERLAY_LANGUAGE']) {
1780 $row = $this->sys_page->getRecordOverlay('tt_content', $row, $basePageRow['_PAGES_OVERLAY_LANGUAGE'], $GLOBALS['TSFE']->sys_language_contentOL);
1781 }
1782 if ($this->mconf['sectionIndex.']['type'] !== 'all') {
1783 $doIncludeInSectionIndex = $row['sectionIndex'] >= 1;
1784 $doHeaderCheck = $this->mconf['sectionIndex.']['type'] === 'header';
1785 $isValidHeader = (int)$row['header_layout'] !== 100 && trim($row['header']) !== '';
1786 if (!$doIncludeInSectionIndex || $doHeaderCheck && !$isValidHeader) {
1787 continue;
1788 }
1789 }
1790 if (is_array($row)) {
1791 $uid = $row['uid'];
1792 $result[$uid] = $basePageRow;
1793 $result[$uid]['title'] = $row['header'];
1794 $result[$uid]['nav_title'] = $row['header'];
1795 $result[$uid]['subtitle'] = $row['subheader'];
1796 $result[$uid]['starttime'] = $row['starttime'];
1797 $result[$uid]['endtime'] = $row['endtime'];
1798 $result[$uid]['fe_group'] = $row['fe_group'];
1799 $result[$uid]['media'] = $row['media'];
1800 $result[$uid]['header_layout'] = $row['header_layout'];
1801 $result[$uid]['bodytext'] = $row['bodytext'];
1802 $result[$uid]['image'] = $row['image'];
1803 $result[$uid]['sectionIndex_uid'] = $uid;
1804 }
1805 }
1806 $GLOBALS['TYPO3_DB']->sql_free_result($resource);
1807 return $result;
1808 }
1809
1810 /**
1811 * Returns the sys_page object
1812 *
1813 * @return \TYPO3\CMS\Frontend\Page\PageRepository
1814 */
1815 public function getSysPage() {
1816 return $this->sys_page;
1817 }
1818
1819 /**
1820 * Returns the parent content object
1821 *
1822 * @return \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
1823 */
1824 public function getParentContentObject() {
1825 return $this->parent_cObj;
1826 }
1827 }