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