3db75ef7cadc374d4f9165e7cec88ff6acf2c2d2
[Packages/TYPO3.CMS.git] / typo3 / sysext / cms / tslib / class.tslib_menu.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Generating navigation / menus from TypoScript
29 *
30 * This file contains five classes, four of which are extensions to the main class, tslib_menu.
31 * The main class, tslib_menu, is also extended by other external PHP scripts such as the GMENU_LAYERS and GMENU_FOLDOUT scripts which creates pop-up menus.
32 * Notice that extension classes (like "tslib_tmenu") must have their suffix (here "tmenu") listed in $this->tmpl->menuclasses - otherwise they cannot be instantiated.
33 *
34 * Revised for TYPO3 3.6 June/2003 by Kasper Skårhøj
35 * XHTML compliant
36 *
37 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
38 */
39
40 /**
41 * Base class. The HMENU content object uses this (or more precisely one of the extension classes).
42 * Amoung others the class generates an array of menuitems. Thereafter functions from the subclasses are called.
43 * The class is ALWAYS used through extension classes (like tslib_gmenu or tslib_tmenu which are classics) and
44 *
45 * Example of usage (from tslib_cObj):
46 *
47 * $menu = t3lib_div::makeInstance('tslib_'.$cls);
48 * $menu->parent_cObj = $this;
49 * $menu->start($GLOBALS['TSFE']->tmpl, $GLOBALS['TSFE']->sys_page, '', $conf,1);
50 * $menu->makeMenu();
51 * $content.=$menu->writeMenu();
52 *
53 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
54 * @package TYPO3
55 * @subpackage tslib
56 * @see tslib_cObj::HMENU()
57 */
58 class tslib_menu {
59 // tells you which menu-number this is. This is important when getting data from the setup
60 var $menuNumber = 1;
61 // 0 = rootFolder
62 var $entryLevel = 0;
63 // The doktype-number that defines a spacer
64 var $spacerIDList = '199';
65 // Doktypes that define which should not be included in a menu
66 var $doktypeExcludeList = '6';
67 var $alwaysActivePIDlist = array();
68 var $imgNamePrefix = 'img';
69 var $imgNameNotRandom = 0;
70 var $debug = 0;
71
72 /**
73 * Loaded with the parent cObj-object when a new HMENU is made
74 *
75 * @var tslib_cObj
76 */
77 var $parent_cObj;
78 var $GMENU_fixKey = 'gmenu';
79 // accumulation of mount point data
80 var $MP_array = array();
81
82 // internal
83 // HMENU configuration
84 var $conf = array();
85 // xMENU configuration (TMENU, GMENU etc)
86 var $mconf = array();
87
88 /**
89 * template-object
90 *
91 * @var t3lib_TStemplate
92 */
93 var $tmpl;
94
95 /**
96 * sys_page-object, pagefunctions
97 *
98 * @var t3lib_pageSelect
99 */
100 var $sys_page;
101 // The base page-id of the menu.
102 var $id;
103 // Holds the page uid of the NEXT page in the root line from the page pointed to by entryLevel;
104 // Used to expand the menu automatically if in a certain root line.
105 var $nextActive;
106 // The array of menuItems which is built
107 var $menuArr;
108 var $hash;
109 var $result = array();
110 // Array: Is filled with an array of page uid numbers + RL parameters which are in the current
111 // root line (used to evaluate whether a menu item is in active state)
112 var $rL_uidRegister = '';
113 var $INPfixMD5;
114 var $I;
115 var $WMresult;
116 var $WMfreezePrefix;
117 var $WMmenuItems;
118 var $WMsubmenuObjSuffixes;
119 var $WMextraScript;
120 // Can be set to contain menu item arrays for sub-levels.
121 var $alternativeMenuTempArray = '';
122 // Will be 'id' in XHTML-mode
123 var $nameAttribute = 'name';
124
125 /**
126 * The initialization of the object. This just sets some internal variables.
127 *
128 * @param object $tmpl The $GLOBALS['TSFE']->tmpl object
129 * @param object $sys_page The $GLOBALS['TSFE']->sys_page object
130 * @param integer $id A starting point page id. This should probably be blank since the 'entryLevel' value will be used then.
131 * @param array $conf The TypoScript configuration for the HMENU cObject
132 * @param integer $menuNumber Menu number; 1,2,3. Should probably be '1'
133 * @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")
134 * @return boolean Returns TRUE on success
135 * @see tslib_cObj::HMENU()
136 */
137 function start(&$tmpl, &$sys_page, $id, $conf, $menuNumber, $objSuffix = '') {
138
139 // Init:
140 $this->conf = $conf;
141 $this->menuNumber = $menuNumber;
142 $this->mconf = $conf[$this->menuNumber.$objSuffix . '.'];
143 $this->debug=$GLOBALS['TSFE']->debug;
144
145 // In XHTML there is no "name" attribute anymore
146 switch ($GLOBALS['TSFE']->xhtmlDoctype) {
147 case 'xhtml_strict':
148 case 'xhtml_11':
149 case 'xhtml_2':
150 case 'html5':
151 $this->nameAttribute = 'id';
152 break;
153 default:
154 $this->nameAttribute = 'name';
155 break;
156 }
157
158 // Sets the internal vars. $tmpl MUST be the template-object. $sys_page MUST be the sys_page object
159 if ($this->conf[$this->menuNumber.$objSuffix] && is_object($tmpl) && is_object($sys_page)) {
160 $this->tmpl = $tmpl;
161 $this->sys_page = $sys_page;
162
163 // alwaysActivePIDlist initialized:
164 if (trim($this->conf['alwaysActivePIDlist']) || isset($this->conf['alwaysActivePIDlist.'])) {
165 if (isset($this->conf['alwaysActivePIDlist.'])) {
166 $this->conf['alwaysActivePIDlist'] = $this->parent_cObj->stdWrap($this->conf['alwaysActivePIDlist'], $this->conf['alwaysActivePIDlist.']);
167 }
168 $this->alwaysActivePIDlist = t3lib_div::intExplode(',', $this->conf['alwaysActivePIDlist']);
169 }
170
171 // 'not in menu' doktypes
172 if ($this->conf['excludeDoktypes']) {
173 $this->doktypeExcludeList = $GLOBALS['TYPO3_DB']->cleanIntList($this->conf['excludeDoktypes']);
174 }
175 // EntryLevel
176 $this->entryLevel = tslib_cObj::getKey (
177 isset($conf['entryLevel.'])
178 ? $this->parent_cObj->stdWrap($conf['entryLevel'], $conf['entryLevel.'])
179 : $conf['entryLevel'],
180 $this->tmpl->rootLine
181 );
182 // Set parent page: If $id not stated with start() then the base-id will be found from rootLine[$this->entryLevel]
183 // Called as the next level in a menu. It is assumed that $this->MP_array is set from parent menu.
184 if ($id) {
185 $this->id = intval($id);
186 } else { // This is a BRAND NEW menu, first level. So we take ID from rootline and also find MP_array (mount points)
187 $this->id = intval($this->tmpl->rootLine[$this->entryLevel]['uid']);
188
189 // Traverse rootline to build MP_array of pages BEFORE the entryLevel
190 // (MP var for ->id is picked up in the next part of the code...)
191 foreach ($this->tmpl->rootLine as $entryLevel => $levelRec) {
192 // For overlaid mount points, set the variable right now:
193 if ($levelRec['_MP_PARAM'] && $levelRec['_MOUNT_OL']) {
194 $this->MP_array[] = $levelRec['_MP_PARAM'];
195 }
196 // Break when entry level is reached:
197 if ($entryLevel >= $this->entryLevel) {
198 break;
199 }
200
201 // For normal mount points, set the variable for next level.
202 if ($levelRec['_MP_PARAM'] && !$levelRec['_MOUNT_OL']) {
203 $this->MP_array[] = $levelRec['_MP_PARAM'];
204 }
205 }
206 }
207
208 // Return FALSE if no page ID was set (thus no menu of subpages can be made).
209 if ($this->id <= 0) {
210 return FALSE;
211 }
212
213 // Check if page is a mount point, and if so set id and MP_array
214 // (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...)
215 $mount_info = $this->sys_page->getMountPointInfo($this->id);
216 if (is_array($mount_info)) {
217 $this->MP_array[] = $mount_info['MPvar'];
218 $this->id = $mount_info['mount_pid'];
219 }
220
221 // Gather list of page uids in root line (for "isActive" evaluation). Also adds the MP params in the path so Mount Points are respected.
222 // (List is specific for this rootline, so it may be supplied from parent menus for speed...)
223 if (!is_array($this->rL_uidRegister)) {
224 $rl_MParray = array();
225 foreach ($this->tmpl->rootLine as $v_rl) {
226 // For overlaid mount points, set the variable right now:
227 if ($v_rl['_MP_PARAM'] && $v_rl['_MOUNT_OL']) {
228 $rl_MParray[] = $v_rl['_MP_PARAM'];
229 }
230
231 // Add to register:
232 $this->rL_uidRegister[] = 'ITEM:'.$v_rl['uid'].(count($rl_MParray) ? ':'.implode(',', $rl_MParray) : '');
233
234 // For normal mount points, set the variable for next level.
235 if ($v_rl['_MP_PARAM'] && !$v_rl['_MOUNT_OL']) {
236 $rl_MParray[] = $v_rl['_MP_PARAM'];
237 }
238 }
239 }
240
241 // Set $directoryLevel so the following evalution of the nextActive will not return
242 // an invalid value if .special=directory was set
243 $directoryLevel = 0;
244 if ($this->conf['special'] == 'directory') {
245 $value = isset($this->conf['special.']['value.'])
246 ? $this->parent_cObj->stdWrap($this->conf['special.']['value'], $this->conf['special.']['value.'])
247 : $this->conf['special.']['value'];
248 if ($value == '') {
249 $value=$GLOBALS['TSFE']->page['uid'];
250 }
251 $directoryLevel = intval($GLOBALS['TSFE']->tmpl->getRootlineLevel($value));
252 }
253
254 // 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
255 // Notice: The automatic expansion of a menu is designed to work only when no "special" modes (except "directory") are used.
256 $startLevel = $directoryLevel ? $directoryLevel : $this->entryLevel;
257 $currentLevel = $startLevel + $this->menuNumber;
258 if (is_array($this->tmpl->rootLine[$currentLevel])) {
259 $nextMParray = $this->MP_array;
260 if (!count($nextMParray) && !$this->tmpl->rootLine[$currentLevel]['_MOUNT_OL'] && $currentLevel > 0) {
261 // Make sure to slide-down any mount point information (_MP_PARAM) to children records in the rootline
262 // otherwise automatic expansion will not work
263 $parentRecord = $this->tmpl->rootLine[$currentLevel - 1];
264 if (isset($parentRecord['_MP_PARAM'])) {
265 $nextMParray[] = $parentRecord['_MP_PARAM'];
266 }
267 }
268
269 // In overlay mode, add next level MPvars as well:
270 if ($this->tmpl->rootLine[$currentLevel]['_MOUNT_OL']) {
271 $nextMParray[] = $this->tmpl->rootLine[$currentLevel]['_MP_PARAM'];
272 }
273 $this->nextActive = $this->tmpl->rootLine[$currentLevel]['uid'] . (count($nextMParray) ? ':' . implode(',', $nextMParray) : '');
274 } else {
275 $this->nextActive = '';
276 }
277
278 // imgNamePrefix
279 if ($this->mconf['imgNamePrefix']) {
280 $this->imgNamePrefix=$this->mconf['imgNamePrefix'];
281 }
282 $this->imgNameNotRandom = $this->mconf['imgNameNotRandom'];
283
284 $retVal = TRUE;
285 } else {
286 $GLOBALS['TT']->setTSlogMessage('ERROR in menu', 3);
287 $retVal = FALSE;
288 }
289 return $retVal;
290 }
291
292 /**
293 * Creates the menu in the internal variables, ready for output.
294 * Basically this will read the page records needed and fill in the internal $this->menuArr
295 * 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.
296 *
297 * @return void
298 */
299 function makeMenu() {
300 if ($this->id) {
301
302 // Initializing showAccessRestrictedPages
303 if ($this->mconf['showAccessRestrictedPages']) {
304 // SAVING where_groupAccess
305 $SAVED_where_groupAccess = $this->sys_page->where_groupAccess;
306 // Temporarily removing fe_group checking!
307 $this->sys_page->where_groupAccess = '';
308 }
309
310 // Begin production of menu:
311 $temp = array();
312 $altSortFieldValue = trim($this->mconf['alternativeSortingField']);
313 $altSortField = $altSortFieldValue ? $altSortFieldValue : 'sorting';
314 // ... only for the FIRST level of a HMENU
315 if ($this->menuNumber == 1 && $this->conf['special']) {
316 $value = isset($this->conf['special.']['value.'])
317 ? $this->parent_cObj->stdWrap($this->conf['special.']['value'], $this->conf['special.']['value.'])
318 : $this->conf['special.']['value'];
319
320 switch($this->conf['special']) {
321 case 'userfunction':
322 $temp = $this->parent_cObj->callUserFunction(
323 $this->conf['special.']['userFunc'],
324 array_merge($this->conf['special.'], array('_altSortField'=>$altSortField)),
325 ''
326 );
327 if (!is_array($temp)) {
328 $temp = array();
329 }
330 break;
331 case 'language':
332 $temp = array();
333
334 // Getting current page record NOT overlaid by any translation:
335 $currentPageWithNoOverlay = $this->sys_page->getRawRecord('pages', $GLOBALS['TSFE']->page['uid']);
336
337 // Traverse languages set up:
338 $languageItems = t3lib_div::intExplode(',', $value);
339 foreach($languageItems as $sUid) {
340 // Find overlay record:
341 if ($sUid) {
342 $lRecs = $this->sys_page->getPageOverlay($GLOBALS['TSFE']->page['uid'], $sUid);
343 } else {
344 $lRecs = array();
345 }
346 // Checking if the "disabled" state should be set.
347 if (
348 (t3lib_div::hideIfNotTranslated($GLOBALS['TSFE']->page['l18n_cfg']) && $sUid && !count($lRecs)) // Blocking for all translations?
349 || ($GLOBALS['TSFE']->page['l18n_cfg']&1 && (!$sUid || !count($lRecs))) // Blocking default translation?
350 || (!$this->conf['special.']['normalWhenNoLanguage'] && $sUid && !count($lRecs))
351 ) {
352 $iState = $GLOBALS['TSFE']->sys_language_uid==$sUid ? 'USERDEF2' : 'USERDEF1';
353 } else {
354 $iState = $GLOBALS['TSFE']->sys_language_uid==$sUid ? 'ACT' : 'NO';
355 }
356
357 if ($this->conf['addQueryString']) {
358 $getVars = $this->parent_cObj->getQueryArguments($this->conf['addQueryString.'], array('L'=>$sUid), TRUE);
359 } else {
360 $getVars = '&L='.$sUid;
361 }
362
363 // Adding menu item:
364 $temp[] = array_merge(
365 array_merge($currentPageWithNoOverlay, $lRecs),
366 array(
367 'ITEM_STATE' => $iState,
368 '_ADD_GETVARS' => $getVars,
369 '_SAFE' => TRUE
370 )
371 );
372 }
373 break;
374 case 'directory':
375 if ($value == '') {
376 $value = $GLOBALS['TSFE']->page['uid'];
377 }
378 $items = t3lib_div::intExplode(',', $value);
379
380 foreach ($items as $id) {
381 $MP = $this->tmpl->getFromMPmap($id);
382
383 // Checking if a page is a mount page and if so, change the ID and set the MP var properly.
384 $mount_info = $this->sys_page->getMountPointInfo($id);
385 if (is_array($mount_info)) {
386 if ($mount_info['overlay']) { // Overlays should already have their full MPvars calculated:
387 $MP = $this->tmpl->getFromMPmap($mount_info['mount_pid']);
388 $MP = $MP ? $MP : $mount_info['MPvar'];
389 } else {
390 $MP = ($MP ? $MP.',' : '').$mount_info['MPvar'];
391 }
392 $id = $mount_info['mount_pid'];
393 }
394
395 // Get sub-pages:
396 $res = $this->parent_cObj->exec_getQuery('pages', Array('pidInList'=>$id, 'orderBy'=>$altSortField));
397 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
398 $GLOBALS['TSFE']->sys_page->versionOL('pages', $row);
399
400 if (is_array($row)) {
401 // Keep mount point?
402 $mount_info = $this->sys_page->getMountPointInfo($row['uid'], $row);
403 // There is a valid mount point.
404 if (is_array($mount_info) && $mount_info['overlay']) {
405 // Using "getPage" is OK since we need the check for enableFields
406 // AND for type 2 of mount pids we DO require a doktype < 200!
407 $mp_row = $this->sys_page->getPage($mount_info['mount_pid']);
408 if (count($mp_row)) {
409 $row = $mp_row;
410 $row['_MP_PARAM'] = $mount_info['MPvar'];
411 } else {
412 // If the mount point could not be fetched with respect
413 // to enableFields, unset the row so it does not become a part of the menu!
414 unset($row);
415 }
416 }
417
418 // Add external MP params, then the row:
419 if (is_array($row)) {
420 if ($MP) {
421 $row['_MP_PARAM'] = $MP.($row['_MP_PARAM'] ? ','.$row['_MP_PARAM'] : '');
422 }
423 $temp[$row['uid']] = $this->sys_page->getPageOverlay($row);
424 }
425 }
426 }
427 }
428 break;
429 case 'list':
430 if ($value == '') {
431 $value = $this->id;
432 }
433 $loadDB = t3lib_div::makeInstance('FE_loadDBGroup');
434 $loadDB->start($value, 'pages');
435 $loadDB->additionalWhere['pages']=tslib_cObj::enableFields('pages');
436 $loadDB->getFromDB();
437
438 foreach ($loadDB->itemArray as $val) {
439 $MP = $this->tmpl->getFromMPmap($val['id']);
440
441 // Keep mount point?
442 $mount_info = $this->sys_page->getMountPointInfo($val['id']);
443 // There is a valid mount point.
444 if (is_array($mount_info) && $mount_info['overlay']) {
445 // Using "getPage" is OK since we need the check for enableFields
446 // AND for type 2 of mount pids we DO require a doktype < 200!
447 $mp_row = $this->sys_page->getPage($mount_info['mount_pid']);
448 if (count($mp_row)) {
449 $row = $mp_row;
450 $row['_MP_PARAM'] = $mount_info['MPvar'];
451
452 // Overlays should already have their full MPvars calculated
453 if ($mount_info['overlay']) {
454 $MP = $this->tmpl->getFromMPmap($mount_info['mount_pid']);
455 if ($MP) {
456 unset($row['_MP_PARAM']);
457 }
458 }
459 } else {
460 // If the mount point could not be fetched with respect to
461 // enableFields, unset the row so it does not become a part of the menu!
462 unset($row);
463 }
464 } else {
465 $row = $loadDB->results['pages'][$val['id']];
466 }
467
468 //Add versioning overlay for current page (to respect workspaces)
469 if (is_array($row)) {
470 $this->sys_page->versionOL('pages', $row, TRUE);
471 }
472
473 // Add external MP params, then the row:
474 if (is_array($row)) {
475 if ($MP) {
476 $row['_MP_PARAM'] = $MP.($row['_MP_PARAM'] ? ','.$row['_MP_PARAM'] : '');
477 }
478 $temp[] = $this->sys_page->getPageOverlay($row);
479 }
480 }
481 break;
482 case 'updated':
483 if ($value == '') {
484 $value = $GLOBALS['TSFE']->page['uid'];
485 }
486 $items = t3lib_div::intExplode(',', $value);
487 if (t3lib_utility_Math::canBeInterpretedAsInteger($this->conf['special.']['depth'])) {
488 $depth = t3lib_utility_Math::forceIntegerInRange($this->conf['special.']['depth'], 1, 20); // Tree depth
489 } else {
490 $depth = 20;
491 }
492 // Max number of items
493 $limit = t3lib_utility_Math::forceIntegerInRange($this->conf['special.']['limit'], 0, 100);
494 $maxAge = intval(tslib_cObj::calc($this->conf['special.']['maxAge']));
495 if (!$limit) {
496 $limit = 10;
497 }
498 // *'auto', 'manual', 'tstamp'
499 $mode = $this->conf['special.']['mode'];
500 // Get id's
501 $id_list_arr = Array();
502
503 foreach($items as $id) {
504 $bA = t3lib_utility_Math::forceIntegerInRange($this->conf['special.']['beginAtLevel'], 0, 100);
505 $id_list_arr[] = tslib_cObj::getTreeList(-1*$id, $depth-1+$bA, $bA-1);
506 }
507 $id_list = implode(',', $id_list_arr);
508 // Get sortField (mode)
509 switch($mode) {
510 case 'starttime':
511 $sortField = 'starttime';
512 break;
513 case 'lastUpdated':
514 case 'manual':
515 $sortField = 'lastUpdated';
516 break;
517 case 'tstamp':
518 $sortField = 'tstamp';
519 break;
520 case 'crdate':
521 $sortField = 'crdate';
522 break;
523 default:
524 $sortField = 'SYS_LASTCHANGED';
525 break;
526 }
527 // Get
528 $extraWhere = ($this->conf['includeNotInMenu'] ? '' : ' AND pages.nav_hide=0').$this->getDoktypeExcludeWhere();
529
530 if ($this->conf['special.']['excludeNoSearchPages']) {
531 $extraWhere.= ' AND pages.no_search=0';
532 }
533 if ($maxAge > 0) {
534 $extraWhere.=' AND '.$sortField.'>'.($GLOBALS['SIM_ACCESS_TIME']-$maxAge);
535 }
536
537 $res = $this->parent_cObj->exec_getQuery('pages', Array(
538 'pidInList' =>'0',
539 'uidInList' => $id_list,
540 'where' => $sortField . '>=0' . $extraWhere,
541 'orderBy' => ($altSortFieldValue ? $altSortFieldValue : $sortField . ' desc'),
542 'max' => $limit)
543 );
544 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
545 $GLOBALS['TSFE']->sys_page->versionOL('pages', $row);
546 if (is_array($row)) {
547 $temp[$row['uid']]=$this->sys_page->getPageOverlay($row);
548 }
549 }
550 break;
551 case 'keywords':
552 list($value) = t3lib_div::intExplode(',', $value);
553 if (!$value) {
554 $value=$GLOBALS['TSFE']->page['uid'];
555 }
556 if ($this->conf['special.']['setKeywords'] || $this->conf['special.']['setKeywords.']) {
557 $kw = isset($this->conf['special.']['setKeywords.'])
558 ? $this->parent_cObj->stdWrap($this->conf['special.']['setKeywords'], $this->conf['special.']['setKeywords.'])
559 : $this->conf['special.']['setKeywords'];
560 } else {
561 // The page record of the 'value'.
562 $value_rec=$this->sys_page->getPage($value);
563
564 $kfieldSrc = $this->conf['special.']['keywordsField.']['sourceField'] ? $this->conf['special.']['keywordsField.']['sourceField'] : 'keywords';
565 // keywords.
566 $kw = trim(tslib_cObj::keywords($value_rec[$kfieldSrc]));
567 }
568
569 // *'auto', 'manual', 'tstamp'
570 $mode = $this->conf['special.']['mode'];
571 switch($mode) {
572 case 'starttime':
573 $sortField = 'starttime';
574 break;
575 case 'lastUpdated':
576 case 'manual':
577 $sortField = 'lastUpdated';
578 break;
579 case 'tstamp':
580 $sortField = 'tstamp';
581 break;
582 case 'crdate':
583 $sortField = 'crdate';
584 break;
585 default:
586 $sortField = 'SYS_LASTCHANGED';
587 break;
588 }
589
590 // Depth, limit, extra where
591 if (t3lib_utility_Math::canBeInterpretedAsInteger($this->conf['special.']['depth'])) {
592 $depth = t3lib_utility_Math::forceIntegerInRange($this->conf['special.']['depth'], 0, 20); // Tree depth
593 } else {
594 $depth = 20;
595 }
596 // Max number of items
597 $limit = t3lib_utility_Math::forceIntegerInRange($this->conf['special.']['limit'], 0, 100);
598 $extraWhere = ' AND pages.uid<>'.$value.($this->conf['includeNotInMenu'] ? '' : ' AND pages.nav_hide=0').$this->getDoktypeExcludeWhere();
599 if ($this->conf['special.']['excludeNoSearchPages']) {
600 $extraWhere.= ' AND pages.no_search=0';
601 }
602 // Start point
603 $eLevel = tslib_cObj::getKey(
604 isset($this->conf['special.']['entryLevel.'])
605 ? $this->parent_cObj->stdWrap($this->conf['special.']['entryLevel'], $this->conf['special.']['entryLevel.'])
606 : $this->conf['special.']['entryLevel'],
607 $this->tmpl->rootLine
608 );
609 $startUid = intval($this->tmpl->rootLine[$eLevel]['uid']);
610
611 // Which field is for keywords
612 $kfield = 'keywords';
613 if ( $this->conf['special.']['keywordsField'] ) {
614 list($kfield) = explode(' ', trim ($this->conf['special.']['keywordsField']));
615 }
616
617 // If there are keywords and the startuid is present.
618 if ($kw && $startUid) {
619 $bA = t3lib_utility_Math::forceIntegerInRange($this->conf['special.']['beginAtLevel'], 0, 100);
620 $id_list = tslib_cObj::getTreeList(-1*$startUid, $depth-1 + $bA, $bA - 1);
621
622 $kwArr = explode(',', $kw);
623 foreach ($kwArr as $word) {
624 $word = trim($word);
625 if ($word) {
626 $keyWordsWhereArr[] = $kfield.' LIKE \'%'.$GLOBALS['TYPO3_DB']->quoteStr($word, 'pages').'%\'';
627 }
628 }
629 $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));
630 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
631 $GLOBALS['TSFE']->sys_page->versionOL('pages', $row);
632 if (is_array($row)) {
633 $temp[$row['uid']]=$this->sys_page->getPageOverlay($row);
634 }
635 }
636 }
637 break;
638 case 'rootline':
639 $range = isset($this->conf['special.']['range.'])
640 ? $this->parent_cObj->stdWrap($this->conf['special.']['range'], $this->conf['special.']['range.'])
641 : $this->conf['special.']['range'];
642 $begin_end = explode('|', $range);
643 $begin_end[0] = intval($begin_end[0]);
644 if (!t3lib_utility_Math::canBeInterpretedAsInteger($begin_end[1])) {
645 $begin_end[1] = -1;
646 }
647
648 $beginKey = tslib_cObj::getKey ($begin_end[0], $this->tmpl->rootLine);
649 $endKey = tslib_cObj::getKey ($begin_end[1], $this->tmpl->rootLine);
650 if ($endKey<$beginKey) {$endKey=$beginKey;}
651
652 $rl_MParray = array();
653 foreach ($this->tmpl->rootLine as $k_rl => $v_rl) {
654 // For overlaid mount points, set the variable right now:
655 if ($v_rl['_MP_PARAM'] && $v_rl['_MOUNT_OL']) {
656 $rl_MParray[] = $v_rl['_MP_PARAM'];
657 }
658 // Traverse rootline:
659 if ($k_rl>=$beginKey && $k_rl<=$endKey) {
660 $temp_key = $k_rl;
661 $temp[$temp_key] = $this->sys_page->getPage($v_rl['uid']);
662 if (count($temp[$temp_key])) {
663 // If there are no specific target for the page, put the level specific target on.
664 if (!$temp[$temp_key]['target']) {
665 $temp[$temp_key]['target'] = $this->conf['special.']['targets.'][$k_rl];
666 $temp[$temp_key]['_MP_PARAM'] = implode(',', $rl_MParray);
667 }
668 } else {
669 unset($temp[$temp_key]);
670 }
671 }
672 // For normal mount points, set the variable for next level.
673 if ($v_rl['_MP_PARAM'] && !$v_rl['_MOUNT_OL']) {
674 $rl_MParray[] = $v_rl['_MP_PARAM'];
675 }
676 }
677 // Reverse order of elements (e.g. "1,2,3,4" gets "4,3,2,1"):
678 if (isset($this->conf['special.']['reverseOrder']) && $this->conf['special.']['reverseOrder']) {
679 $temp = array_reverse($temp);
680 $rl_MParray = array_reverse($rl_MParray);
681 }
682 break;
683 case 'browse':
684 list($value) = t3lib_div::intExplode(',', $value);
685 if (!$value) {
686 $value=$GLOBALS['TSFE']->page['uid'];
687 }
688 // Will not work out of rootline
689 if ($value != $this->tmpl->rootLine[0]['uid']) {
690 $recArr = array();
691 // The page record of the 'value'.
692 $value_rec = $this->sys_page->getPage($value);
693 // 'up' page cannot be outside rootline
694 if ($value_rec['pid']) {
695 // The page record of 'up'.
696 $recArr['up']=$this->sys_page->getPage($value_rec['pid']);
697 }
698 // If the 'up' item was NOT level 0 in rootline...
699 if ($recArr['up']['pid'] && $value_rec['pid']!=$this->tmpl->rootLine[0]['uid']) {
700 // The page record of "index".
701 $recArr['index']=$this->sys_page->getPage($recArr['up']['pid']);
702 }
703
704 // prev / next is found
705 $prevnext_menu = $this->sys_page->getMenu($value_rec['pid'], '*', $altSortField);
706 $lastKey = 0;
707 $nextActive = 0;
708 foreach ($prevnext_menu as $k_b => $v_b) {
709 if ($nextActive) {
710 $recArr['next'] = $v_b;
711 $nextActive = 0;
712 }
713 if ($v_b['uid'] == $value) {
714 if ($lastKey) {
715 $recArr['prev'] = $prevnext_menu[$lastKey];
716 }
717 $nextActive = 1;
718 }
719 $lastKey = $k_b;
720 }
721 reset($prevnext_menu);
722 $recArr['first'] = pos($prevnext_menu);
723 end($prevnext_menu);
724 $recArr['last'] = pos($prevnext_menu);
725
726 // prevsection / nextsection is found
727 // You can only do this, if there is a valid page two levels up!
728 if (is_array($recArr['index'])) {
729 $prevnextsection_menu = $this->sys_page->getMenu($recArr['index']['uid'], '*', $altSortField);
730 $lastKey = 0;
731 $nextActive = 0;
732 foreach ($prevnextsection_menu as $k_b => $v_b) {
733 if ($nextActive) {
734 $sectionRec_temp = $this->sys_page->getMenu($v_b['uid'], '*', $altSortField);
735 if (count($sectionRec_temp)) {
736 reset($sectionRec_temp);
737 $recArr['nextsection'] = pos($sectionRec_temp);
738 end($sectionRec_temp);
739 $recArr['nextsection_last'] = pos($sectionRec_temp);
740 $nextActive = 0;
741 }
742 }
743 if ($v_b['uid'] == $value_rec['pid']) {
744 if ($lastKey) {
745 $sectionRec_temp = $this->sys_page->getMenu($prevnextsection_menu[$lastKey]['uid'], '*', $altSortField);
746 if (count($sectionRec_temp)) {
747 reset($sectionRec_temp);
748 $recArr['prevsection'] = pos($sectionRec_temp);
749 end($sectionRec_temp);
750 $recArr['prevsection_last'] = pos($sectionRec_temp);
751 }
752 }
753 $nextActive = 1;
754 }
755 $lastKey = $k_b;
756 }
757 }
758 if ($this->conf['special.']['items.']['prevnextToSection']) {
759 if (!is_array($recArr['prev']) && is_array($recArr['prevsection_last'])) {
760 $recArr['prev']=$recArr['prevsection_last'];
761 }
762 if (!is_array($recArr['next']) && is_array($recArr['nextsection'])) {
763 $recArr['next']=$recArr['nextsection'];
764 }
765 }
766
767 $items = explode('|', $this->conf['special.']['items']);
768 $c = 0;
769 foreach ($items as $k_b => $v_b) {
770 $v_b = strtolower(trim($v_b));
771 if (intval($this->conf['special.'][$v_b.'.']['uid'])) {
772 $recArr[$v_b] = $this->sys_page->getPage(intval($this->conf['special.'][$v_b.'.']['uid'])); // fetches the page in case of a hardcoded pid in template
773 }
774 if (is_array($recArr[$v_b])) {
775 $temp[$c] = $recArr[$v_b];
776 if ($this->conf['special.'][$v_b.'.']['target']) {
777 $temp[$c]['target'] = $this->conf['special.'][$v_b.'.']['target'];
778 }
779 $tmpSpecialFields = $this->conf['special.'][$v_b.'.']['fields.'];
780 if (is_array($tmpSpecialFields)) {
781 foreach ($tmpSpecialFields as $fk => $val) {
782 $temp[$c][$fk]=$val;
783 }
784 }
785 $c++;
786 }
787 }
788 }
789 break;
790 }
791 if ($this->mconf['sectionIndex']) {
792 $sectionIndexes = array();
793
794 foreach ($temp as $page) {
795 $sectionIndexes = $sectionIndexes + $this->sectionIndex($altSortField, $page['uid']);
796 }
797
798 $temp = $sectionIndexes;
799 }
800 } elseif (is_array($this->alternativeMenuTempArray)) { // Setting $temp array if not level 1.
801 $temp = $this->alternativeMenuTempArray;
802 } elseif ($this->mconf['sectionIndex']) {
803 $temp = $this->sectionIndex($altSortField);
804 } else { // Default:
805 // gets the menu
806 $temp = $this->sys_page->getMenu($this->id, '*', $altSortField);
807 }
808
809 $c = 0;
810 $c_b = 0;
811
812 $minItems = intval($this->mconf['minItems'] ? $this->mconf['minItems'] : $this->conf['minItems']);
813 $maxItems = intval($this->mconf['maxItems'] ? $this->mconf['maxItems'] : $this->conf['maxItems']);
814 $begin = tslib_cObj::calc($this->mconf['begin'] ? $this->mconf['begin'] : $this->conf['begin']);
815
816 $minItemsConf = isset($this->mconf['minItems.'])
817 ? $this->mconf['minItems.']
818 : (isset($this->conf['minItems.']) ? $this->conf['minItems.'] : NULL);
819 $minItems = is_array($minItemsConf)
820 ? $this->parent_cObj->stdWrap($minItems, $minItemsConf)
821 : $minItems;
822
823 $maxItemsConf = isset($this->mconf['maxItems.'])
824 ? $this->mconf['maxItems.']
825 : (isset($this->conf['maxItems.']) ? $this->conf['maxItems.'] : NULL);
826 $maxItems = is_array($maxItemsConf)
827 ? $this->parent_cObj->stdWrap($maxItems, $maxItemsConf)
828 : $maxItems;
829
830 $beginConf = isset($this->mconf['begin.'])
831 ? $this->mconf['begin.']
832 : (isset($this->conf['begin.']) ? $this->conf['begin.'] : NULL);
833 $begin = is_array($beginConf)
834 ? $this->parent_cObj->stdWrap($begin, $beginConf)
835 : $begin;
836
837 $banUidArray = $this->getBannedUids();
838
839 // Fill in the menuArr with elements that should go into the menu:
840 $this->menuArr = array();
841 foreach($temp as $data) {
842 $spacer = (t3lib_div::inList($this->spacerIDList, $data['doktype']) || !strcmp($data['ITEM_STATE'], 'SPC')) ? 1 : 0; // if item is a spacer, $spacer is set
843 if ($this->filterMenuPages($data, $banUidArray, $spacer)) {
844 $c_b++;
845 // If the beginning item has been reached.
846 if ($begin<=$c_b) {
847 $this->menuArr[$c] = $data;
848 $this->menuArr[$c]['isSpacer'] = $spacer;
849 $c++;
850 if ($maxItems && $c >= $maxItems) {
851 break;
852 }
853 }
854 }
855 }
856
857 // Fill in fake items, if min-items is set.
858 if ($minItems) {
859 while ($c<$minItems) {
860 $this->menuArr[$c] = Array(
861 'title' => '...',
862 'uid' => $GLOBALS['TSFE']->id
863 );
864 $c++;
865 }
866 }
867 // Passing the menuArr through a user defined function:
868 if ($this->mconf['itemArrayProcFunc']) {
869 if (!is_array($this->parentMenuArr)) {$this->parentMenuArr=array();}
870 $this->menuArr = $this->userProcess('itemArrayProcFunc', $this->menuArr);
871 }
872 // Setting number of menu items
873 $GLOBALS['TSFE']->register['count_menuItems'] = count($this->menuArr);
874
875 $this->hash = md5(serialize($this->menuArr).serialize($this->mconf).serialize($this->tmpl->rootLine).serialize($this->MP_array));
876
877 // Get the cache timeout:
878 if ($this->conf['cache_period']) {
879 $cacheTimeout = $this->conf['cache_period'];
880 } else {
881 $cacheTimeout = $GLOBALS['TSFE']->get_cache_timeout();
882 }
883
884 $serData = $this->sys_page->getHash($this->hash);
885 if (!$serData) {
886 $this->generate();
887 $this->sys_page->storeHash($this->hash, serialize($this->result), 'MENUDATA', $cacheTimeout);
888 } else {
889 $this->result = unserialize($serData);
890 }
891
892 // End showAccessRestrictedPages
893 if ($this->mconf['showAccessRestrictedPages']) {
894 // RESTORING where_groupAccess
895 $this->sys_page->where_groupAccess = $SAVED_where_groupAccess;
896 }
897 }
898 }
899
900 /**
901 * 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.
902 *
903 * @param array $data Array of menu items
904 * @param array $banUidArray Array of page uids which are to be excluded
905 * @param boolean $spacer If set, then the page is a spacer.
906 * @return boolean Returns TRUE if the page can be safely included.
907 */
908 function filterMenuPages(&$data, $banUidArray, $spacer) {
909
910 $includePage = TRUE;
911 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/tslib/class.tslib_menu.php']['filterMenuPages'])) {
912 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/tslib/class.tslib_menu.php']['filterMenuPages'] as $classRef) {
913 $hookObject = t3lib_div::getUserObj($classRef);
914
915 if (!($hookObject instanceof tslib_menu_filterMenuPagesHook)) {
916 throw new UnexpectedValueException('$hookObject must implement interface tslib_menu_filterMenuPagesHook', 1269877402);
917 }
918
919 $includePage = $includePage && $hookObject->processFilter($data, $banUidArray, $spacer, $this);
920 }
921 }
922 if (!$includePage) {
923 return FALSE;
924 }
925
926 if ($data['_SAFE']) {
927 return TRUE;
928 }
929
930 $uid = $data['uid'];
931 // If the spacer-function is not enabled, spacers will not enter the $menuArr
932 if ($this->mconf['SPC'] || !$spacer) {
933 // Page may not be 'not_in_menu' or 'Backend User Section'
934 if (!t3lib_div::inList($this->doktypeExcludeList, $data['doktype'])) {
935 // Not hidden in navigation
936 if (!$data['nav_hide'] || $this->conf['includeNotInMenu']) {
937 // not in banned uid's
938 if (!t3lib_div::inArray($banUidArray, $uid)) {
939
940 // Checks if the default language version can be shown:
941 // Block page is set, if l18n_cfg allows plus: 1) Either default language or 2) another language but NO overlay record set for page!
942 $blockPage = $data['l18n_cfg']&1 && (!$GLOBALS['TSFE']->sys_language_uid || ($GLOBALS['TSFE']->sys_language_uid && !$data['_PAGES_OVERLAY']));
943 if (!$blockPage) {
944
945 // Checking if a page should be shown in the menu depending on whether a translation exists:
946 $tok = TRUE;
947 // There is an alternative language active AND the current page requires a translation:
948 if ($GLOBALS['TSFE']->sys_language_uid && t3lib_div::hideIfNotTranslated($data['l18n_cfg'])) {
949 if (!$data['_PAGES_OVERLAY']) {
950 $tok = FALSE;
951 }
952 }
953
954 // Continue if token is TRUE:
955 if ($tok) {
956
957 // Checking if "&L" should be modified so links to non-accessible pages will not happen.
958 if ($this->conf['protectLvar']) {
959 $languageUid = intval($GLOBALS['TSFE']->config['config']['sys_language_uid']);
960 if ($languageUid && ($this->conf['protectLvar']=='all' || t3lib_div::hideIfNotTranslated($data['l18n_cfg']))) {
961 $olRec = $GLOBALS['TSFE']->sys_page->getPageOverlay($data['uid'], $languageUid);
962 if (!count($olRec)) {
963 // 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"
964 $data['_ADD_GETVARS'].= '&L=0';
965 }
966 }
967 }
968
969 return TRUE;
970 }
971 }
972 }
973 }
974 }
975 }
976 }
977
978 /**
979 * 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)
980 * 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.
981 * 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.
982 *
983 * @param integer $splitCount Number of menu items in the menu
984 * @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)
985 * @access private
986 */
987 function procesItemStates($splitCount) {
988
989 // Prepare normal settings
990 if (!is_array($this->mconf['NO.']) && $this->mconf['NO']) {
991 // Setting a blank array if NO=1 and there are no properties.
992 $this->mconf['NO.'] = array();
993 }
994 $NOconf = $this->tmpl->splitConfArray($this->mconf['NO.'], $splitCount);
995
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
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] ? $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] ? $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] ? $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] ? $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] ? $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] ? $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] ? $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] ? $USERDEF2ROconf[$key] : $USERDEF2conf[$key];
1224 }
1225 }
1226 }
1227 }
1228
1229 return array($NOconf, $ROconf);
1230 }
1231
1232 /**
1233 * 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
1234 * This function doesn't care about the url, because if we let the url be redirected, it will be logged in the stat!!!
1235 *
1236 * @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)
1237 * @param string $altTarget Alternative target
1238 * @param integer $typeOverride Alternative type
1239 * @return array Returns an array with A-tag attributes as key/value pairs (HREF, TARGET and onClick)
1240 * @access private
1241 */
1242 function link($key, $altTarget = '', $typeOverride = '') {
1243
1244 // Mount points:
1245 $MP_var = $this->getMPvar($key);
1246 $MP_params = $MP_var ? '&MP='.rawurlencode($MP_var) : '';
1247
1248 // Setting override ID
1249 if ($this->mconf['overrideId'] || $this->menuArr[$key]['overrideId']) {
1250 $overrideArray = array();
1251 // If a user script returned the value overrideId in the menu array we use that as page id
1252 $overrideArray['uid'] = $this->mconf['overrideId']?$this->mconf['overrideId']:$this->menuArr[$key]['overrideId'];
1253 $overrideArray['alias'] = '';
1254 // Clear MP parameters since ID was changed.
1255 $MP_params = '';
1256 } else {
1257 $overrideArray='';
1258 }
1259
1260 // Setting main target:
1261 if ($altTarget) {
1262 $mainTarget = $altTarget;
1263 } elseif ($this->mconf['target.']) {
1264 $mainTarget = $this->parent_cObj->stdWrap($this->mconf['target'], $this->mconf['target.']);
1265 } else {
1266 $mainTarget = $this->mconf['target'];
1267 }
1268
1269 // Creating link:
1270 if ($this->mconf['collapse'] && $this->isActive($this->menuArr[$key]['uid'], $this->getMPvar($key))) {
1271 $thePage = $this->sys_page->getPage($this->menuArr[$key]['pid']);
1272 $LD = $this->menuTypoLink($thePage, $mainTarget, '', '', $overrideArray, $this->mconf['addParams'].$MP_params.$this->menuArr[$key]['_ADD_GETVARS'], $typeOverride);
1273 } else {
1274 $LD = $this->menuTypoLink($this->menuArr[$key], $mainTarget, '', '', $overrideArray, $this->mconf['addParams'].$MP_params.$this->I['val']['additionalParams'].$this->menuArr[$key]['_ADD_GETVARS'], $typeOverride);
1275 }
1276
1277 // Override URL if using "External URL" as doktype with a valid e-mail address:
1278 if ($this->menuArr[$key]['doktype'] == t3lib_pageSelect::DOKTYPE_LINK && $this->menuArr[$key]['urltype'] == 3 && t3lib_div::validEmail($this->menuArr[$key]['url'])) {
1279 // Create mailto-link using tslib_cObj::typolink (concerning spamProtectEmailAddresses):
1280 $LD['totalURL'] = $this->parent_cObj->typoLink_URL(array('parameter' => $this->menuArr[$key]['url']));
1281 $LD['target'] = '';
1282 }
1283
1284 // Override url if current page is a shortcut
1285 if ($this->menuArr[$key]['doktype'] == t3lib_pageSelect::DOKTYPE_SHORTCUT
1286 && $this->menuArr[$key]['shortcut_mode'] != t3lib_pageSelect::SHORTCUT_MODE_RANDOM_SUBPAGE) {
1287
1288 $shortcut = NULL;
1289 try {
1290 $shortcut = $GLOBALS['TSFE']->getPageShortcut(
1291 $this->menuArr[$key]['shortcut'],
1292 $this->menuArr[$key]['shortcut_mode'],
1293 $this->menuArr[$key]['uid']
1294 );
1295 } catch (Exception $ex) {
1296 // shortcut configuration is wrong and Exception is thrown
1297 // this will be catched with the next is_array() check
1298 }
1299
1300 if (!is_array($shortcut)) {
1301 return array();
1302 }
1303
1304 // Only setting url, not target
1305 $LD['totalURL'] = $this->parent_cObj->typoLink_URL(array(
1306 'parameter' => $shortcut['uid'],
1307 'additionalParams' => $this->mconf['addParams'] . $MP_params . $this->I['val']['additionalParams'] . $this->menuArr[$key]['_ADD_GETVARS'],
1308 ));
1309 }
1310
1311 // Manipulation in case of access restricted pages:
1312 $this->changeLinksForAccessRestrictedPages($LD, $this->menuArr[$key], $mainTarget, $typeOverride);
1313
1314 // Overriding URL / Target if set to do so:
1315 if ($this->menuArr[$key]['_OVERRIDE_HREF']) {
1316 $LD['totalURL'] = $this->menuArr[$key]['_OVERRIDE_HREF'];
1317 if ($this->menuArr[$key]['_OVERRIDE_TARGET']) {
1318 $LD['target'] = $this->menuArr[$key]['_OVERRIDE_TARGET'];
1319 }
1320 }
1321
1322 // OnClick open in windows.
1323 $onClick='';
1324 if ($this->mconf['JSWindow']) {
1325 $conf=$this->mconf['JSWindow.'];
1326 $url=$LD['totalURL'];
1327 $LD['totalURL'] = '#';
1328 $onClick= 'openPic(\''.$GLOBALS['TSFE']->baseUrlWrap($url).'\',\''.($conf['newWindow']?md5($url):'theNewPage').'\',\''.$conf['params'].'\'); return false;';
1329 $GLOBALS['TSFE']->setJS('openPic');
1330 }
1331
1332 // look for type and popup
1333 // following settings are valid in field target:
1334 // 230 will add type=230 to the link
1335 // 230 500x600 will add type=230 to the link and open in popup window with 500x600 pixels
1336 // 230 _blank will add type=230 to the link and open with target "_blank"
1337 // 230x450:resizable=0,location=1 will open in popup window with 500x600 pixels with settings "resizable=0,location=1"
1338 $matches = array();
1339 $targetIsType = $LD['target'] && (string) intval($LD['target']) == trim($LD['target']) ? intval($LD['target']) : FALSE;
1340 if (preg_match('/([0-9]+[\s])?(([0-9]+)x([0-9]+))?(:.+)?/s', $LD['target'], $matches) || $targetIsType) {
1341 // has type?
1342 if (intval($matches[1]) || $targetIsType) {
1343 $LD['totalURL'] = $this->parent_cObj->URLqMark(
1344 $LD['totalURL'],
1345 '&type=' . ($targetIsType ? $targetIsType : intval($matches[1]))
1346 );
1347 $LD['target'] = $targetIsType ? '' : trim(substr($LD['target'], strlen($matches[1]) + 1));
1348 }
1349 // Open in popup window?
1350 if ($matches[3] && $matches[4]) {
1351 $JSparamWH = 'width=' . $matches[3] . ',height=' . $matches[4] . ($matches[5] ? ',' . substr($matches[5], 1) : '');
1352 $onClick = 'vHWin=window.open(\'' . $LD['totalURL'] . '\',\'FEopenLink\',\'' . $JSparamWH . '\');vHWin.focus();return false;';
1353 $LD['target'] = '';
1354 }
1355 }
1356
1357 // out:
1358 $list = array();
1359 // Added this check: What it does is to enter the baseUrl (if set, which it should for "realurl" based sites)
1360 // as URL if the calculated value is empty. The problem is that no link is generated with a blank URL
1361 // and blank URLs might appear when the realurl encoding is used and a link to the frontpage is generated.
1362 $list['HREF'] = strlen($LD['totalURL']) ? $LD['totalURL'] : $GLOBALS['TSFE']->baseUrl;
1363 $list['TARGET'] = $LD['target'];
1364 $list['onClick'] = $onClick;
1365
1366 return $list;
1367 }
1368
1369 /**
1370 * Will change $LD (passed by reference) if the page is access restricted
1371 *
1372 * @param array $LD The array from the linkData() function
1373 * @param array $page Page array
1374 * @param string $mainTarget Main target value
1375 * @param string $typeOverride Type number override if any
1376 * @return void ($LD passed by reference might be changed.)
1377 */
1378 function changeLinksForAccessRestrictedPages(&$LD, $page, $mainTarget, $typeOverride) {
1379
1380 // If access restricted pages should be shown in menus, change the link of such pages to link to a redirection page:
1381 if ($this->mconf['showAccessRestrictedPages'] && $this->mconf['showAccessRestrictedPages'] !== 'NONE' && !$GLOBALS['TSFE']->checkPageGroupAccess($page)) {
1382 $thePage = $this->sys_page->getPage($this->mconf['showAccessRestrictedPages']);
1383
1384 $addParams = $this->mconf['showAccessRestrictedPages.']['addParams'];
1385 $addParams = str_replace('###RETURN_URL###', rawurlencode($LD['totalURL']), $addParams);
1386 $addParams = str_replace('###PAGE_ID###', $page['uid'], $addParams);
1387 $LD = $this->menuTypoLink($thePage, $mainTarget, '', '', '', $addParams, $typeOverride);
1388 }
1389 }
1390
1391 /**
1392 * Creates a submenu level to the current level - if configured for.
1393 *
1394 * @param integer $uid Page id of the current page for which a submenu MAY be produced (if conditions are met)
1395 * @param string $objSuffix Object prefix, see ->start()
1396 * @return string HTML content of the submenu
1397 * @access private
1398 */
1399 function subMenu($uid, $objSuffix = '') {
1400
1401 // Setting alternative menu item array if _SUB_MENU has been defined in the current ->menuArr
1402 $altArray = '';
1403 if (is_array($this->menuArr[$this->I['key']]['_SUB_MENU']) && count($this->menuArr[$this->I['key']]['_SUB_MENU'])) {
1404 $altArray = $this->menuArr[$this->I['key']]['_SUB_MENU'];
1405 }
1406
1407 // Make submenu if the page is the next active
1408 $cls = strtolower($this->conf[($this->menuNumber+1).$objSuffix]);
1409 $subLevelClass = ($cls && t3lib_div::inList($this->tmpl->menuclasses, $cls)) ? $cls : '';
1410
1411 // stdWrap for expAll
1412 if (isset($this->mconf['expAll.'])) {
1413 $this->mconf['expAll'] = $this->parent_cObj->stdWrap($this->mconf['expAll'], $this->mconf['expAll.']);
1414 }
1415
1416 if ($subLevelClass && ($this->mconf['expAll'] || $this->isNext($uid, $this->getMPvar($this->I['key'])) || is_array($altArray)) && !$this->mconf['sectionIndex']) {
1417 $submenu = t3lib_div::makeInstance('tslib_'.$subLevelClass);
1418 $submenu->entryLevel = $this->entryLevel+1;
1419 $submenu->rL_uidRegister = $this->rL_uidRegister;
1420 $submenu->MP_array = $this->MP_array;
1421 if ($this->menuArr[$this->I['key']]['_MP_PARAM']) {
1422 $submenu->MP_array[] = $this->menuArr[$this->I['key']]['_MP_PARAM'];
1423 }
1424
1425 // Especially scripts that build the submenu needs the parent data
1426 $submenu->parent_cObj = $this->parent_cObj;
1427 $submenu->parentMenuArr = $this->menuArr;
1428
1429 // Setting alternativeMenuTempArray (will be effective only if an array)
1430 if (is_array($altArray)) {
1431 $submenu->alternativeMenuTempArray = $altArray;
1432 }
1433
1434 if ($submenu->start($this->tmpl, $this->sys_page, $uid, $this->conf, $this->menuNumber+1, $objSuffix)) {
1435 $submenu->makeMenu();
1436 // Memorize the current menu item count
1437 $tempCountMenuObj = $GLOBALS['TSFE']->register['count_MENUOBJ'];
1438 // Reset the menu item count for the submenu
1439 $GLOBALS['TSFE']->register['count_MENUOBJ'] = 0;
1440 $content = $submenu->writeMenu();
1441 // Restore the item count now that the submenu has been handled
1442 $GLOBALS['TSFE']->register['count_MENUOBJ'] = $tempCountMenuObj;
1443 $GLOBALS['TSFE']->register['count_menuItems'] = count($this->menuArr);
1444 return $content;
1445 }
1446 }
1447 }
1448
1449 /**
1450 * Returns TRUE if the page with UID $uid is the NEXT page in root line (which means a submenu should be drawn)
1451 *
1452 * @param integer $uid Page uid to evaluate.
1453 * @param string $MPvar MPvar for the current position of item.
1454 * @return boolean TRUE if page with $uid is active
1455 * @access private
1456 * @see subMenu()
1457 */
1458 function isNext($uid, $MPvar = '') {
1459
1460 // Check for always active PIDs:
1461 if (count($this->alwaysActivePIDlist) && in_array($uid, $this->alwaysActivePIDlist)) {
1462 return TRUE;
1463 }
1464
1465 $testUid = $uid . ($MPvar ? ':' . $MPvar : '');
1466 if ($uid && $testUid == $this->nextActive) {
1467 return TRUE;
1468 }
1469 }
1470
1471 /**
1472 * Returns TRUE if the page with UID $uid is active (in the current rootline)
1473 *
1474 * @param integer $uid Page uid to evaluate.
1475 * @param string $MPvar MPvar for the current position of item.
1476 * @return boolean TRUE if page with $uid is active
1477 * @access private
1478 */
1479 function isActive($uid, $MPvar = '') {
1480
1481 // Check for always active PIDs:
1482 if (count($this->alwaysActivePIDlist) && in_array($uid, $this->alwaysActivePIDlist)) {
1483 return TRUE;
1484 }
1485
1486 $testUid = $uid . ($MPvar ? ':' . $MPvar : '');
1487 if ($uid && in_array('ITEM:' . $testUid, $this->rL_uidRegister)) {
1488 return TRUE;
1489 }
1490 }
1491
1492 /**
1493 * Returns TRUE if the page with UID $uid is the CURRENT page (equals $GLOBALS['TSFE']->id)
1494 *
1495 * @param integer $uid Page uid to evaluate.
1496 * @param string $MPvar MPvar for the current position of item.
1497 * @return boolean TRUE if page $uid = $GLOBALS['TSFE']->id
1498 * @access private
1499 */
1500 function isCurrent($uid, $MPvar = '') {
1501 $testUid = $uid . ($MPvar ? ':' . $MPvar : '');
1502 if ($uid && !strcmp(end($this->rL_uidRegister), 'ITEM:' . $testUid)) {
1503 return TRUE;
1504 }
1505 }
1506
1507 /**
1508 * Returns TRUE if there is a submenu with items for the page id, $uid
1509 * Used by the item states "IFSUB", "ACTIFSUB" and "CURIFSUB" to check if there is a submenu
1510 *
1511 * @param integer $uid Page uid for which to search for a submenu
1512 * @return boolean Returns TRUE if there was a submenu with items found
1513 * @access private
1514 */
1515 function isSubMenu($uid) {
1516 // Looking for a mount-pid for this UID since if that
1517 // exists we should look for a subpages THERE and not in the input $uid;
1518 $mount_info = $this->sys_page->getMountPointInfo($uid);
1519 if (is_array($mount_info)) {
1520 $uid = $mount_info['mount_pid'];
1521 }
1522
1523 $recs = $this->sys_page->getMenu(
1524 $uid,
1525 'uid,pid,doktype,mount_pid,mount_pid_ol,nav_hide,shortcut,shortcut_mode,l18n_cfg'
1526 );
1527
1528 $hasSubPages = FALSE;
1529 foreach ($recs as $theRec) {
1530 // no valid subpage if the document type is excluded from the menu
1531 if (t3lib_div::inList($this->doktypeExcludeList, $theRec['doktype'])) {
1532 continue;
1533 }
1534
1535 // No valid subpage if the page is hidden inside menus and
1536 // it wasn't forced to show such entries
1537 if ($theRec['nav_hide'] && !$this->conf['includeNotInMenu']) {
1538 continue;
1539 }
1540
1541 // No valid subpage if the default language should be shown and the page settings
1542 // are excluding the visibility of the default language
1543 if (!$GLOBALS['TSFE']->sys_language_uid && t3lib_div::hideIfDefaultLanguage($theRec['l18n_cfg'])) {
1544 continue;
1545 }
1546
1547 // No valid subpage if the alternative language should be shown and the page settings
1548 // are requiring a valid overlay but it doesn't exists
1549 $hideIfNotTranslated = t3lib_div::hideIfNotTranslated($theRec['l18n_cfg']);
1550 if ($GLOBALS['TSFE']->sys_language_uid && $hideIfNotTranslated && !$theRec['_PAGES_OVERLAY']) {
1551 continue;
1552 }
1553
1554 $hasSubPages = TRUE;
1555 break;
1556 }
1557
1558 return $hasSubPages;
1559 }
1560
1561 /**
1562 * Used by procesItemStates() to evaluate if a menu item (identified by $key) is in a certain state.
1563 *
1564 * @param string $kind The item state to evaluate (SPC, IFSUB, ACT etc... but no xxxRO states of course)
1565 * @param integer $key Key pointing to menu item from ->menuArr
1566 * @return boolean True (integer!=0) if match, otherwise FALSE (=0, zero)
1567 * @access private
1568 * @see procesItemStates()
1569 */
1570 function isItemState($kind, $key) {
1571 $natVal = 0;
1572 // If any value is set for ITEM_STATE the normal evaluation is discarded
1573 if ($this->menuArr[$key]['ITEM_STATE']) {
1574 if (!strcmp($this->menuArr[$key]['ITEM_STATE'], $kind)) {
1575 $natVal = 1;
1576 }
1577 } else {
1578 switch($kind) {
1579 case 'SPC':
1580 $natVal = $this->menuArr[$key]['isSpacer'];
1581 break;
1582 case 'IFSUB':
1583 $natVal = $this->isSubMenu($this->menuArr[$key]['uid']);
1584 break;
1585 case 'ACT':
1586 $natVal = $this->isActive($this->menuArr[$key]['uid'], $this->getMPvar($key));
1587 break;
1588 case 'ACTIFSUB':
1589 $natVal = $this->isActive($this->menuArr[$key]['uid'], $this->getMPvar($key)) && $this->isSubMenu($this->menuArr[$key]['uid']);
1590 break;
1591 case 'CUR':
1592 $natVal = $this->isCurrent($this->menuArr[$key]['uid'], $this->getMPvar($key));
1593 break;
1594 case 'CURIFSUB':
1595 $natVal = $this->isCurrent($this->menuArr[$key]['uid'], $this->getMPvar($key)) && $this->isSubMenu($this->menuArr[$key]['uid']);
1596 break;
1597 case 'USR':
1598 $natVal = $this->menuArr[$key]['fe_group'];
1599 break;
1600 }
1601 }
1602
1603 return $natVal;
1604 }
1605
1606 /**
1607 * Creates an access-key for a TMENU/GMENU menu item based on the menu item titles first letter
1608 *
1609 * @param string $title Menu item title.
1610 * @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
1611 * @access private
1612 */
1613 function accessKey($title) {
1614 // The global array ACCESSKEY is used to globally control if letters are already used!!
1615 $result = array();
1616
1617 $title = trim(strip_tags($title));
1618 $titleLen = strlen($title);
1619 for ($a = 0; $a < $titleLen; $a++) {
1620 $key = strtoupper(substr($title, $a, 1));
1621 if (preg_match('/[A-Z]/', $key) && !isset($GLOBALS['TSFE']->accessKey[$key])) {
1622 $GLOBALS['TSFE']->accessKey[$key] = 1;
1623 $result['code'] = ' accesskey="'.$key.'"';
1624 $result['alt'] = ' (ALT+'.$key.')';
1625 $result['key'] = $key;
1626 break;
1627 }
1628 }
1629 return $result;
1630 }
1631
1632 /**
1633 * Calls a user function for processing of internal data.
1634 * Used for the properties "IProcFunc" and "itemArrayProcFunc"
1635 *
1636 * @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".
1637 * @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
1638 * @return mixed The processed $passVar
1639 * @access private
1640 */
1641 function userProcess($mConfKey, $passVar) {
1642 if ($this->mconf[$mConfKey]) {
1643 $funcConf = $this->mconf[$mConfKey.'.'];
1644 $funcConf['parentObj'] = $this;
1645 $passVar = $this->parent_cObj->callUserFunction($this->mconf[$mConfKey], $funcConf, $passVar);
1646 }
1647 return $passVar;
1648 }
1649
1650 /**
1651 * 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'])
1652 *
1653 * @return void
1654 * @access private
1655 */
1656 function setATagParts() {
1657 $this->I['A1'] = '<a '.t3lib_div::implodeAttributes($this->I['linkHREF'], 1).' '.$this->I['val']['ATagParams'].$this->I['accessKey']['code'].'>';
1658 $this->I['A2'] = '</a>';
1659 }
1660
1661 /**
1662 * Returns the title for the navigation
1663 *
1664 * @param string $title The current page title
1665 * @param string $nav_title The current value of the navigation title
1666 * @return string Returns the navigation title if it is NOT blank, otherwise the page title.
1667 * @access private
1668 */
1669 function getPageTitle($title, $nav_title) {
1670 return strcmp(trim($nav_title), '') ? $nav_title : $title;
1671 }
1672
1673 /**
1674 * Return MPvar string for entry $key in ->menuArr
1675 *
1676 * @param integer $key Pointer to element in ->menuArr
1677 * @param string Implode token.
1678 * @return string MP vars for element.
1679 * @see link()
1680 */
1681 function getMPvar($key) {
1682 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) {
1683 $localMP_array = $this->MP_array;
1684 // NOTICE: "_MP_PARAM" is allowed to be a commalist of PID pairs!
1685 if ($this->menuArr[$key]['_MP_PARAM']) {
1686 $localMP_array[] = $this->menuArr[$key]['_MP_PARAM'];
1687 }
1688 $MP_params = count($localMP_array) ? implode(',', $localMP_array) : '';
1689 return $MP_params;
1690 }
1691 }
1692
1693 /**
1694 * Returns where clause part to exclude 'not in menu' pages
1695 *
1696 * @return string where clause part.
1697 * @access private
1698 */
1699 function getDoktypeExcludeWhere() {
1700 return $this->doktypeExcludeList ? ' AND pages.doktype NOT IN ('.$this->doktypeExcludeList.')' : '';
1701 }
1702
1703 /**
1704 * Returns an array of banned UIDs (from excludeUidList)
1705 *
1706 * @return array Array of banned UIDs
1707 * @access private
1708 */
1709 function getBannedUids() {
1710 $banUidArray = array();
1711
1712 if (trim($this->conf['excludeUidList'])) {
1713 $banUidList = str_replace('current', $GLOBALS['TSFE']->page['uid'], $this->conf['excludeUidList']);
1714 $banUidArray = t3lib_div::intExplode(',', $banUidList);
1715 }
1716
1717 return $banUidArray;
1718 }
1719
1720 /**
1721 * Calls typolink to create menu item links.
1722 *
1723 * @param array $page Page record (uid points where to link to)
1724 * @param string $oTarget Target frame/window
1725 * @param boolean $no_cache TRUE if caching should be disabled
1726 * @param string $script Alternative script name
1727 * @param array $overrideArray Array to override values in $page
1728 * @param string $addParams Parameters to add to URL
1729 * @param array $typeOverride "type" value
1730 * @return array See linkData
1731 */
1732 function menuTypoLink($page, $oTarget, $no_cache, $script, $overrideArray = '', $addParams = '', $typeOverride = '') {
1733 $conf = array(
1734 'parameter' => is_array($overrideArray) && $overrideArray['uid'] ? $overrideArray['uid'] : $page['uid'],
1735 );
1736 if ($typeOverride && t3lib_utility_Math::canBeInterpretedAsInteger($typeOverride)) {
1737 $conf['parameter'] .= ',' . $typeOverride;
1738 }
1739 if ($addParams) {
1740 $conf['additionalParams'] = $addParams;
1741 }
1742 if ($no_cache) {
1743 $conf['no_cache'] = TRUE;
1744 }
1745 if ($oTarget) {
1746 $conf['target'] = $oTarget;
1747 }
1748 if ($page['sectionIndex_uid']) {
1749 $conf['section'] = $page['sectionIndex_uid'];
1750 }
1751
1752 $this->parent_cObj->typoLink('|', $conf);
1753 $LD = $this->parent_cObj->lastTypoLinkLD;
1754 $LD['totalURL'] = $this->parent_cObj->lastTypoLinkUrl;
1755 return $LD;
1756 }
1757
1758 /**
1759 * Generates a list of content objects with sectionIndex enabled
1760 * available on a specific page
1761 *
1762 * Used for menus with sectionIndex enabled
1763 *
1764 * @param string $altSortField Alternative sorting field
1765 * @param integer $pid The page id to search for sections
1766 * @throws UnexpectedValueException if the query to fetch the content elements unexpectedly fails
1767 * @return array
1768 */
1769 protected function sectionIndex($altSortField, $pid = NULL) {
1770 $pid = intval($pid ? $pid : $this->id);
1771 $basePageRow = $this->sys_page->getPage($pid);
1772 if (!is_array($basePageRow)) {
1773 return array();
1774 }
1775
1776 $selectSetup = array(
1777 'pidInList' => $pid,
1778 'orderBy' => $altSortField,
1779 'languageField' => 'sys_language_uid',
1780 'where' => 'colPos=0'
1781 );
1782
1783 $resource = $this->parent_cObj->exec_getQuery('tt_content', $selectSetup);
1784 if (!$resource) {
1785 $message = 'SectionIndex: Query to fetch the content elements failed!';
1786 throw new UnexpectedValueException($message, 1337334849);
1787 }
1788
1789 $result = array();
1790 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($resource)) {
1791 $this->sys_page->versionOL('tt_content', $row);
1792
1793 if ($GLOBALS['TSFE']->sys_language_contentOL && $basePageRow['_PAGES_OVERLAY_LANGUAGE']) {
1794 $row = $this->sys_page->getRecordOverlay(
1795 'tt_content',
1796 $row,
1797 $basePageRow['_PAGES_OVERLAY_LANGUAGE'],
1798 $GLOBALS['TSFE']->sys_language_contentOL
1799 );
1800 }
1801
1802 if ($this->mconf['sectionIndex.']['type'] !== 'all') {
1803 $doIncludeInSectionIndex = ($row['sectionIndex'] >= 1);
1804 $doHeaderCheck = ($this->mconf['sectionIndex.']['type'] === 'header');
1805 $isValidHeader = (intval($row['header_layout']) !== 100 && trim($row['header']) !== '');
1806 if (!$doIncludeInSectionIndex || ($doHeaderCheck && !$isValidHeader)) {
1807 continue;
1808 }
1809 }
1810
1811 if (is_array($row)) {
1812 $uid = $row['uid'];
1813 $result[$uid] = $basePageRow;
1814 $result[$uid]['title'] = $row['header'];
1815 $result[$uid]['nav_title'] = $row['header'];
1816 $result[$uid]['subtitle'] = $row['subheader'];
1817 $result[$uid]['starttime'] = $row['starttime'];
1818 $result[$uid]['endtime'] = $row['endtime'];
1819 $result[$uid]['fe_group'] = $row['fe_group'];
1820 $result[$uid]['media'] = $row['media'];
1821
1822 $result[$uid]['header_layout'] = $row['header_layout'];
1823 $result[$uid]['bodytext'] = $row['bodytext'];
1824 $result[$uid]['image'] = $row['image'];
1825
1826 $result[$uid]['sectionIndex_uid'] = $uid;
1827 }
1828 }
1829
1830 $GLOBALS['TYPO3_DB']->sql_free_result($resource);
1831
1832 return $result;
1833 }
1834 }
1835
1836 /**
1837 * Extension class creating text based menus
1838 *
1839 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
1840 * @package TYPO3
1841 * @subpackage tslib
1842 */
1843 class tslib_tmenu extends tslib_menu {
1844
1845 /**
1846 * Calls procesItemStates() so that the common configuration for the menu items are resolved into individual configuration per item.
1847 * Sets the result for the new "normal state" in $this->result
1848 *
1849 * @return void
1850 * @see tslib_menu::procesItemStates()
1851 */
1852 function generate() {
1853 $splitCount = count($this->menuArr);
1854 if ($splitCount) {
1855 list($NOconf) = $this->procesItemStates($splitCount);
1856 }
1857 if ($this->mconf['debugItemConf']) {
1858 echo '<h3>$NOconf:</h3>';
1859 debug($NOconf);
1860 }
1861 $this->result = $NOconf;
1862 }
1863
1864 /**
1865 * Traverses the ->result array of menu items configuration (made by ->generate()) and renders each item.
1866 * During the execution of this function many internal methods prefixed "extProc_" from this class is called and many of these are for now dummy functions. But they can be used for processing as they are used by the TMENU_LAYERS
1867 * An instance of tslib_cObj is also made and for each menu item rendered it is loaded with the record for that page so that any stdWrap properties that applies will have the current menu items record available.
1868 *
1869 * @return string The HTML for the menu (returns result through $this->extProc_finish(); )
1870 */
1871 function writeMenu() {
1872 if (is_array($this->result) && count($this->result)) {
1873 // Create new tslib_cObj for our use
1874 $this->WMcObj = t3lib_div::makeInstance('tslib_cObj');
1875 $this->WMresult = '';
1876 $this->INPfixMD5 = substr(md5(microtime().'tmenu'), 0, 4);
1877 $this->WMmenuItems = count($this->result);
1878
1879 $this->WMsubmenuObjSuffixes = $this->tmpl->splitConfArray(array('sOSuffix'=>$this->mconf['submenuObjSuffixes']), $this->WMmenuItems);
1880
1881 $this->extProc_init();
1882 foreach ($this->result as $key => $val) {
1883 $GLOBALS['TSFE']->register['count_HMENU_MENUOBJ']++;
1884 $GLOBALS['TSFE']->register['count_MENUOBJ']++;
1885
1886 // Initialize the cObj with the page record of the menu item
1887 $this->WMcObj->start($this->menuArr[$key], 'pages');
1888
1889 $this->I = array();
1890 $this->I['key'] = $key;
1891 $this->I['INPfix'] = ($this->imgNameNotRandom ? '' : '_'.$this->INPfixMD5).'_'.$key;
1892 $this->I['val'] = $val;
1893 $this->I['title'] = isset($this->I['val']['stdWrap.'])
1894 ? $this->WMcObj->stdWrap($this->getPageTitle($this->menuArr[$key]['title'], $this->menuArr[$key]['nav_title']), $this->I['val']['stdWrap.'])
1895 : $this->getPageTitle($this->menuArr[$key]['title'], $this->menuArr[$key]['nav_title']);
1896 $this->I['uid'] = $this->menuArr[$key]['uid'];
1897 $this->I['mount_pid'] = $this->menuArr[$key]['mount_pid'];
1898 $this->I['pid'] = $this->menuArr[$key]['pid'];
1899 $this->I['spacer'] = $this->menuArr[$key]['isSpacer'];
1900
1901 // Set access key
1902 if ($this->mconf['accessKey']) {
1903 $this->I['accessKey'] = $this->accessKey($this->I['title']);
1904 } else {
1905 $this->I['accessKey'] = Array();
1906 }
1907
1908 // Make link tag
1909 $this->I['val']['ATagParams'] = $this->WMcObj->getATagParams($this->I['val']);
1910 if (isset($this->I['val']['additionalParams.'])) {
1911 $this->I['val']['additionalParams'] = $this->WMcObj->stdWrap($this->I['val']['additionalParams'], $this->I['val']['additionalParams.']);
1912 }
1913 $this->I['linkHREF'] = $this->link($key, $this->I['val']['altTarget'], $this->mconf['forceTypeValue']);
1914
1915 // Title attribute of links:
1916 $titleAttrValue = isset($this->I['val']['ATagTitle.'])
1917 ? $this->WMcObj->stdWrap($this->I['val']['ATagTitle'], $this->I['val']['ATagTitle.']) . $this->I['accessKey']['alt']
1918 : $this->I['val']['ATagTitle'].$this->I['accessKey']['alt'];
1919 if (strlen($titleAttrValue)) {
1920 $this->I['linkHREF']['title'] = $titleAttrValue;
1921 }
1922
1923 // Make link:
1924 if ($this->I['val']['RO']) {
1925 $this->I['theName'] = $this->imgNamePrefix.$this->I['uid'].$this->I['INPfix'];
1926 $over = '';
1927 $out = '';
1928 if ($this->I['val']['beforeROImg']) {
1929 $over.= $this->WMfreezePrefix."over('".$this->I['theName']."before');";
1930 $out.= $this->WMfreezePrefix."out('".$this->I['theName']."before');";
1931 }
1932 if ($this->I['val']['afterROImg']) {
1933 $over.= $this->WMfreezePrefix."over('".$this->I['theName']."after');";
1934 $out.= $this->WMfreezePrefix."out('".$this->I['theName']."after');";
1935 }
1936 $this->I['linkHREF']['onMouseover']=$over;
1937 $this->I['linkHREF']['onMouseout']=$out;
1938 if ($over || $out) {
1939 $GLOBALS['TSFE']->setJS('mouseOver');
1940 }
1941
1942 // Change background color:
1943 if ($this->I['val']['RO_chBgColor']) {
1944 $this->addJScolorShiftFunction();
1945 $chBgP = t3lib_div::trimExplode('|', $this->I['val']['RO_chBgColor']);
1946 $this->I['linkHREF']['onMouseover'].="changeBGcolor('".$chBgP[2].$this->I['uid']."', '".$chBgP[0]."');";
1947 $this->I['linkHREF']['onMouseout'].="changeBGcolor('".$chBgP[2].$this->I['uid']."', '".$chBgP[1]."');";
1948 }
1949
1950 $this->extProc_RO($key);
1951 }
1952
1953 // Calling extra processing function
1954 $this->extProc_beforeLinking($key);
1955
1956 // stdWrap for doNotLinkIt
1957 if (isset($this->I['val']['doNotLinkIt.'])) {
1958 $this->I['val']['doNotLinkIt'] = $this->WMcObj->stdWrap($this->I['val']['doNotLinkIt'], $this->I['val']['doNotLinkIt.']);
1959 }
1960
1961 // Compile link tag
1962 if (!$this->I['val']['doNotLinkIt']) {
1963 $this->I['val']['doNotLinkIt'] = 0;
1964 }
1965 if (!$this->I['spacer'] && $this->I['val']['doNotLinkIt'] != 1) {
1966 $this->setATagParts();
1967 } else {
1968 $this->I['A1'] = '';
1969 $this->I['A2'] = '';
1970 }
1971
1972 // ATagBeforeWrap processing:
1973 if ($this->I['val']['ATagBeforeWrap']) {
1974 $wrapPartsBefore = explode('|', $this->I['val']['linkWrap']);
1975 $wrapPartsAfter = array('', '');
1976 } else {
1977 $wrapPartsBefore = array('', '');
1978 $wrapPartsAfter = explode('|', $this->I['val']['linkWrap']);
1979 }
1980 if ($this->I['val']['stdWrap2'] || isset($this->I['val']['stdWrap2.'])) {
1981 $stdWrap2 = isset($this->I['val']['stdWrap2.'])
1982 ? $this->WMcObj->stdWrap('|', $this->I['val']['stdWrap2.'])
1983 : '|';
1984 $wrapPartsStdWrap = explode($this->I['val']['stdWrap2'] ? $this->I['val']['stdWrap2'] : '|', $stdWrap2);
1985 } else {
1986 $wrapPartsStdWrap = array('', '');
1987 }
1988
1989 // Make before, middle and after parts
1990 $this->I['parts'] = array();
1991 $this->I['parts']['before'] = $this->getBeforeAfter('before');
1992 $this->I['parts']['stdWrap2_begin'] = $wrapPartsStdWrap[0];
1993
1994 // stdWrap for doNotShowLink
1995 if (isset($this->I['val']['doNotShowLink.'])) {
1996 $this->I['val']['doNotShowLink'] = $this->WMcObj->stdWrap($this->I['val']['doNotShowLink'], $this->I['val']['doNotShowLink.']);
1997 }
1998
1999 if (!$this->I['val']['doNotShowLink']) {
2000 $this->I['parts']['notATagBeforeWrap_begin'] = $wrapPartsAfter[0];
2001 $this->I['parts']['ATag_begin'] = $this->I['A1'];
2002 $this->I['parts']['ATagBeforeWrap_begin'] = $wrapPartsBefore[0];
2003 $this->I['parts']['title'] = $this->I['title'];
2004 $this->I['parts']['ATagBeforeWrap_end'] = $wrapPartsBefore[1];
2005 $this->I['parts']['ATag_end'] = $this->I['A2'];
2006 $this->I['parts']['notATagBeforeWrap_end'] = $wrapPartsAfter[1];
2007 }
2008 $this->I['parts']['stdWrap2_end'] = $wrapPartsStdWrap[1];
2009 $this->I['parts']['after'] = $this->getBeforeAfter('after');
2010
2011 // Passing I to a user function
2012 if ($this->mconf['IProcFunc']) {
2013 $this->I = $this->userProcess('IProcFunc', $this->I);
2014 }
2015
2016 // Merge parts + beforeAllWrap
2017 $this->I['theItem'] = implode('', $this->I['parts']);
2018 $this->I['theItem'] = $this->extProc_beforeAllWrap($this->I['theItem'], $key);
2019
2020 // allWrap:
2021 $allWrap = isset($this->I['val']['allWrap.'])
2022 ? $this->WMcObj->stdWrap($this->I['val']['allWrap'], $this->I['val']['allWrap.'])
2023 : $this->I['val']['allWrap'];
2024 $this->I['theItem'] = $this->tmpl->wrap($this->I['theItem'], $allWrap);
2025
2026 if ($this->I['val']['subst_elementUid']) {
2027 $this->I['theItem'] = str_replace('{elementUid}', $this->I['uid'], $this->I['theItem']);
2028 }
2029
2030 // allStdWrap:
2031 if (is_array($this->I['val']['allStdWrap.'])) {
2032 $this->I['theItem'] = $this->WMcObj->stdWrap($this->I['theItem'], $this->I['val']['allStdWrap.']);
2033 }
2034
2035 // Calling extra processing function
2036 $this->extProc_afterLinking($key);
2037 }
2038 return $this->extProc_finish();
2039 }
2040 }
2041
2042 /**
2043 * Generates the before* and after* images for TMENUs
2044 *
2045 * @param string $pref Can be "before" or "after" and determines which kind of image to create (basically this is the prefix of the TypoScript properties that are read from the ->I['val'] array
2046 * @return string The resulting HTML of the image, if any.
2047 */
2048 function getBeforeAfter($pref) {
2049 $res = '';
2050 if ($imgInfo = $this->WMcObj->getImgResource($this->I['val'][$pref.'Img'], $this->I['val'][$pref.'Img.'])) {
2051 $imgInfo[3] = t3lib_div::png_to_gif_by_imagemagick($imgInfo[3]);
2052 if ($this->I['val']['RO'] && $this->I['val'][$pref.'ROImg'] && !$this->I['spacer']) {
2053 $imgROInfo = $this->WMcObj->getImgResource($this->I['val'][$pref.'ROImg'], $this->I['val'][$pref.'ROImg.']);
2054 $imgROInfo[3] = t3lib_div::png_to_gif_by_imagemagick($imgROInfo[3]);
2055 if ($imgROInfo) {
2056 $theName = $this->imgNamePrefix.$this->I['uid'].$this->I['INPfix'].$pref;
2057 $name = ' '.$this->nameAttribute.'="'.$theName.'"';
2058 $GLOBALS['TSFE']->JSImgCode.= LF.$theName.'_n=new Image(); '.$theName.'_n.src = "'.$GLOBALS['TSFE']->absRefPrefix.$imgInfo[3].'"; ';
2059 $GLOBALS['TSFE']->JSImgCode.= LF.$theName.'_h=new Image(); '.$theName.'_h.src = "'.$GLOBALS['TSFE']->absRefPrefix.$imgROInfo[3].'"; ';
2060 }
2061 }
2062 $GLOBALS['TSFE']->imagesOnPage[]=$imgInfo[3];
2063 $res='<img' .
2064 ' src="' . $GLOBALS['TSFE']->absRefPrefix . $imgInfo[3] . '"' .
2065 ' width="' . $imgInfo[0] . '"' .
2066 ' height="' . $imgInfo[1] . '"' .
2067 $name .
2068 ($this->I['val'][$pref.'ImgTagParams'] ? ' ' . $this->I['val'][$pref.'ImgTagParams'] : '') .
2069 tslib_cObj::getBorderAttr(' border="0"');
2070 if (!strstr($res, 'alt="')) {
2071 // Adding alt attribute if not set.
2072 $res .= ' alt=""';
2073 }
2074 $res .= ' />';
2075 if ($this->I['val'][$pref.'ImgLink']) {
2076 $res=$this->I['A1'].$res.$this->I['A2'];
2077 }
2078 }
2079 $processedPref = isset($this->I['val'][$pref . '.'])
2080 ? $this->WMcObj->stdWrap($this->I['val'][$pref], $this->I['val'][$pref . '.'])
2081 : $this->I['val'][$pref];
2082 if (isset($this->I['val'][$pref . 'Wrap'])) {
2083 return $this->tmpl->wrap($res . $processedPref, $this->I['val'][$pref . 'Wrap']);
2084 } else {
2085 return $res . $processedPref;
2086 }
2087 }
2088
2089 /**
2090 * Adds a JavaScript function to the $GLOBALS['TSFE']->additionalJavaScript array
2091 *
2092 * @return void
2093 * @access private
2094 * @see writeMenu()
2095 */
2096 function addJScolorShiftFunction() {
2097 $GLOBALS['TSFE']->additionalJavaScript['TMENU:changeBGcolor()']='
2098 function changeBGcolor(id,color) { //
2099 if (document.getElementById && document.getElementById(id)) {
2100 document.getElementById(id).style.background = color;
2101 return true;
2102 } else if (document.layers && document.layers[id]) {
2103 document.layers[id].bgColor = color;
2104 return true;
2105 }
2106 }
2107 ';
2108 }
2109
2110 /**
2111 * Called right before the traversing of $this->result begins.
2112 * Can be used for various initialization
2113 *
2114 * @return void
2115 * @access private
2116 * @see writeMenu(), tslib_tmenu_layers::extProc_init()
2117 */
2118 function extProc_init() {
2119 }
2120
2121 /**
2122 * Called after all processing for RollOver of an element has been done.
2123 *
2124 * @param integer Pointer to $this->menuArr[$key] where the current menu element record is found
2125 * @return void
2126 * @access private
2127 * @see writeMenu(), tslib_tmenu_layers::extProc_RO()
2128 */
2129 function extProc_RO($key) {
2130 }
2131
2132 /**
2133 * Called right before the creation of the link for the menu item
2134 *
2135 * @param integer Pointer to $this->menuArr[$key] where the current menu element record is found
2136 * @return void
2137 * @access private
2138 * @see writeMenu(), tslib_tmenu_layers::extProc_beforeLinking()
2139 */
2140 function extProc_beforeLinking($key) {
2141 }
2142
2143 /**
2144 * Called right after the creation of links for the menu item. This is also the last function call before the while-loop traversing menu items goes to the next item.
2145 * This function MUST set $this->WMresult.=[HTML for menu item] to add the generated menu item to the internal accumulation of items.
2146 *
2147 * @param integer Pointer to $this->menuArr[$key] where the current menu element record is found
2148 * @return void
2149 * @access private
2150 * @see writeMenu(), tslib_tmenu_layers::extProc_afterLinking()
2151 */
2152 function extProc_afterLinking($key) {
2153 // Add part to the accumulated result + fetch submenus
2154 if (!$this->I['spacer']) {
2155 $this->I['theItem'] .= $this->subMenu($this->I['uid'], $this->WMsubmenuObjSuffixes[$key]['sOSuffix']);
2156 }
2157 $part = isset($this->I['val']['wrapItemAndSub.'])
2158 ? $this->WMcObj->stdWrap($this->I['val']['wrapItemAndSub'], $this->I['val']['wrapItemAndSub.'])
2159 : $this->I['val']['wrapItemAndSub'];
2160 $this->WMresult .= $part ? $this->tmpl->wrap($this->I['theItem'], $part) : $this->I['theItem'];
2161 }
2162
2163 /**
2164 * Called before the "allWrap" happens on the menu item.
2165 *
2166 * @param string $item The current content of the menu item, $this->I['theItem'], passed along.
2167 * @param integer $key Pointer to $this->menuArr[$key] where the current menu element record is found
2168 * @return string The modified version of $item, going back into $this->I['theItem']
2169 * @access private
2170 * @see writeMenu(), tslib_tmenu_layers::extProc_beforeAllWrap()
2171 */
2172 function extProc_beforeAllWrap($item, $key) {
2173 return $item;
2174 }
2175
2176 /**
2177 * Called before the writeMenu() function returns (only if a menu was generated)
2178 *
2179 * @return string The total menu content should be returned by this function
2180 * @access private
2181 * @see writeMenu(), tslib_tmenu_layers::extProc_finish()
2182 */
2183 function extProc_finish() {
2184 // stdWrap:
2185 if (is_array($this->mconf['stdWrap.'])) {
2186 $this->WMresult = $this->WMcObj->stdWrap($this->WMresult, $this->mconf['stdWrap.']);
2187 }
2188 return $this->tmpl->wrap($this->WMresult, $this->mconf['wrap']).$this->WMextraScript;
2189 }
2190 }
2191
2192 /**
2193 * Extension class creating graphic based menus (PNG or GIF files)
2194 *
2195 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
2196 * @package TYPO3
2197 * @subpackage tslib
2198 */
2199 class tslib_gmenu extends tslib_menu {
2200
2201 /**
2202 * Calls procesItemStates() so that the common configuration for the menu items are resolved into individual configuration per item.
2203 * Calls makeGifs() for all "normal" items and if configured for, also the "rollover" items.
2204 *
2205 * @return void
2206 * @see tslib_menu::procesItemStates(), makeGifs()
2207 */
2208 function generate() {
2209 $splitCount = count($this->menuArr);
2210 if ($splitCount) {
2211 list($NOconf,$ROconf) = $this->procesItemStates($splitCount);
2212
2213 //store initial count value
2214 $temp_HMENU_MENUOBJ = $GLOBALS['TSFE']->register['count_HMENU_MENUOBJ'];
2215 $temp_MENUOBJ = $GLOBALS['TSFE']->register['count_MENUOBJ'];
2216 // Now we generate the giffiles:
2217 $this->makeGifs($NOconf, 'NO');
2218 // store count from NO obj
2219 $tempcnt_HMENU_MENUOBJ = $GLOBALS['TSFE']->register['count_HMENU_MENUOBJ'];
2220 $tempcnt_MENUOBJ = $GLOBALS['TSFE']->register['count_MENUOBJ'];
2221
2222 if ($this->mconf['debugItemConf']) {
2223 echo '<h3>$NOconf:</h3>';
2224 debug($NOconf);
2225 }
2226 // RollOver
2227 if ($ROconf) {
2228 // Start recount for rollover with initial values
2229 $GLOBALS['TSFE']->register['count_HMENU_MENUOBJ']= $temp_HMENU_MENUOBJ;
2230 $GLOBALS['TSFE']->register['count_MENUOBJ']= $temp_MENUOBJ;
2231 $this->makeGifs($ROconf, 'RO');
2232 if ($this->mconf['debugItemConf']) {
2233 echo '<h3>$ROconf:</h3>';
2234 debug($ROconf);
2235 }
2236 }
2237 // Use count from NO obj
2238 $GLOBALS['TSFE']->register['count_HMENU_MENUOBJ'] = $tempcnt_HMENU_MENUOBJ;
2239 $GLOBALS['TSFE']->register['count_MENUOBJ'] = $tempcnt_MENUOBJ;
2240 }
2241 }
2242
2243 /**
2244 * Will traverse input array with configuratoin per-item and create corresponding GIF files for the menu.
2245 * The data of the files are stored in $this->result
2246 *
2247 * @param array $conf Array with configuration for each item.
2248 * @param string $resKey Type of images: normal ("NO") or rollover ("RO"). Valid values are "NO" and "RO"
2249 * @return void
2250 * @access private
2251 * @see generate()
2252 */
2253 function makeGifs($conf, $resKey) {
2254 $isGD = $GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib'];
2255
2256 if (!is_array($conf)) {
2257 $conf = Array();
2258 }
2259
2260 $totalWH=array();
2261 $items = count($conf);
2262 if ($isGD) {
2263 // Generate the gif-files. the $menuArr is filled with some values like output_w, output_h, output_file
2264 $Hcounter = 0;
2265 $Wcounter = 0;
2266 $Hobjs = $this->mconf['applyTotalH'];
2267 if ($Hobjs) {
2268 $Hobjs = t3lib_div::intExplode(',', $Hobjs);
2269 }
2270 $Wobjs = $this->mconf['applyTotalW'];
2271 if ($Wobjs) {
2272 $Wobjs = t3lib_div::intExplode(',', $Wobjs);
2273 }
2274 $minDim = $this->mconf['min'];
2275 if ($minDim) {
2276 $minDim = tslib_cObj::calcIntExplode(',', $minDim.',');
2277 }
2278 $maxDim = $this->mconf['max'];
2279 if ($maxDim) {
2280 $maxDim = tslib_cObj::calcIntExplode(',', $maxDim.',');
2281 }
2282
2283 if ($minDim) {
2284 $conf[$items] = $conf[$items-1];
2285 $this->menuArr[$items] = array();
2286 $items = count($conf);
2287 }
2288
2289 // TOTAL width
2290 if ($this->mconf['useLargestItemX'] || $this->mconf['useLargestItemY'] || $this->mconf['distributeX'] || $this->mconf['distributeY']) {
2291 $totalWH = $this->findLargestDims($conf, $items, $Hobjs, $Wobjs, $minDim, $maxDim);
2292 }
2293 }
2294
2295 $c = 0;
2296 $maxFlag = 0;
2297 $distributeAccu = array('H' => 0, 'W' => 0);
2298 foreach ($conf as $key => $val) {
2299 $GLOBALS['TSFE']->register['count_HMENU_MENUOBJ']++;
2300 $GLOBALS['TSFE']->register['count_MENUOBJ']++;
2301
2302 if ($items == ($c+1) && $minDim) {
2303 $Lobjs = $this->mconf['removeObjectsOfDummy'];
2304 if ($Lobjs) {
2305 $Lobjs = t3lib_div::intExplode(',', $Lobjs);
2306 foreach ($Lobjs as $remItem) {
2307 unset($val[$remItem]);
2308 unset($val[$remItem.'.']);
2309 }
2310 }
2311
2312 $flag = 0;
2313 $tempXY = explode(',', $val['XY']);
2314 if ($Wcounter < $minDim[0]) {
2315 $tempXY[0] = $minDim[0] - $Wcounter;
2316 $flag = 1;
2317 }
2318 if ($Hcounter < $minDim[1]) {
2319 $tempXY[1] = $minDim[1] - $Hcounter;
2320 $flag = 1;
2321 }
2322 $val['XY'] = implode(',', $tempXY);
2323 if (!$flag){
2324 break;
2325 }
2326 }
2327 $c++;
2328
2329 if ($isGD) {
2330 // Pre-working the item
2331 $gifCreator = t3lib_div::makeInstance('tslib_gifBuilder');
2332 $gifCreator->init();
2333 $gifCreator->start($val, $this->menuArr[$key]);
2334
2335 // If useLargestItemH/W is specified
2336 if (count($totalWH) && ($this->mconf['useLargestItemX'] || $this->mconf['useLargestItemY'])) {
2337 $tempXY = explode(',', $gifCreator->setup['XY']);
2338 if ($this->mconf['useLargestItemX']) {
2339 $tempXY[0] = max($totalWH['W']);
2340 }
2341 if ($this->mconf['useLargestItemY']) {
2342 $tempXY[1] = max($totalWH['H']);
2343 }
2344 // Regenerate the new values...
2345 $val['XY'] = implode(',', $tempXY);
2346 $gifCreator = t3lib_div::makeInstance('tslib_gifBuilder');
2347 $gifCreator->init();
2348 $gifCreator->start($val, $this->menuArr[$key]);
2349 }
2350
2351 // If distributeH/W is specified
2352 if (count($totalWH) && ($this->mconf['distributeX'] || $this->mconf['distributeY'])) {
2353 $tempXY = explode(',', $gifCreator->setup['XY']);
2354
2355 if ($this->mconf['distributeX']) {
2356 $diff = $this->mconf['distributeX']-$totalWH['W_total']-$distributeAccu['W'];
2357 $compensate = round($diff /($items-$c+1));
2358 $distributeAccu['W']+=$compensate;
2359 $tempXY[0] = $totalWH['W'][$key]+$compensate;
2360 }
2361 if ($this->mconf['distributeY']) {
2362 $diff = $this->mconf['distributeY']-$totalWH['H_total']-$distributeAccu['H'];
2363 $compensate = round($diff /($items-$c+1));
2364 $distributeAccu['H']+=$compensate;
2365 $tempXY[1] = $totalWH['H'][$key]+$compensate;
2366 }
2367 // Regenerate the new values...
2368 $val['XY'] = implode(',', $tempXY);
2369 $gifCreator = t3lib_div::makeInstance('tslib_gifBuilder');
2370 $gifCreator->init();
2371 $gifCreator->start($val, $this->menuArr[$key]);
2372 }
2373
2374 // If max dimensions are specified
2375 if ($maxDim) {
2376 $tempXY = explode(',', $val['XY']);
2377 if ($maxDim[0] && $Wcounter + $gifCreator->XY[0] >= $maxDim[0]) {
2378 $tempXY[0] == $maxDim[0] - $Wcounter;
2379 $maxFlag = 1;
2380 }
2381 if ($maxDim[1] && $Hcounter + $gifCreator->XY[1] >= $maxDim[1]) {
2382 $tempXY[1] = $maxDim[1] - $Hcounter;
2383 $maxFlag = 1;
2384 }
2385 if ($maxFlag) {
2386 $val['XY'] = implode(',', $tempXY);
2387 $gifCreator = t3lib_div::makeInstance('tslib_gifBuilder');
2388 $gifCreator->init();
2389 $gifCreator->start($val, $this->menuArr[$key]);
2390 }
2391 }
2392
2393 // displace
2394 if ($Hobjs) {
2395 foreach ($Hobjs as $index) {
2396 if ($gifCreator->setup[$index] && $gifCreator->setup[$index.'.']) {
2397 $oldOffset = explode(',', $gifCreator->setup[$index.'.']['offset']);
2398 $gifCreator->setup[$index.'.']['offset'] = implode(',', $gifCreator->applyOffset($oldOffset, Array(0, -$Hcounter)));
2399 }
2400 }
2401 }
2402
2403 if ($Wobjs) {
2404 foreach ($Wobjs as $index) {
2405 if ($gifCreator->setup[$index] && $gifCreator->setup[$index.'.']) {
2406 $oldOffset = explode(',', $gifCreator->setup[$index.'.']['offset']);
2407 $gifCreator->setup[$index.'.']['offset'] = implode(',', $gifCreator->applyOffset($oldOffset, Array(-$Wcounter, 0)));
2408 }
2409 }
2410 }
2411 }
2412
2413 // Finding alternative GIF names if any (by altImgResource)
2414 $gifFileName = '';
2415 if ($conf[$key]['altImgResource'] || is_array($conf[$key]['altImgResource.'])) {
2416 if (!is_object($cObj)) {
2417 $cObj = t3lib_div::makeInstance('tslib_cObj');
2418 }
2419 $cObj->start($this->menuArr[$key], 'pages');
2420 $altImgInfo = $cObj->getImgResource($conf[$key]['altImgResource'], $conf[$key]['altImgResource.']);
2421 $gifFileName = $altImgInfo[3];
2422 }
2423
2424 // If an alternative name was NOT given, find the GIFBUILDER name.
2425 if (!$gifFileName && $isGD) {
2426 $gifCreator->createTempSubDir('menu/');
2427 $gifFileName = $gifCreator->fileName('menu/');
2428 }
2429
2430 $this->result[$resKey][$key] = $conf[$key];
2431
2432 // Generation of image file:
2433 // File exists
2434 if (file_exists($gifFileName)) {
2435 $info = @getimagesize($gifFileName);
2436 $this->result[$resKey][$key]['output_w'] = intval($info[0]);
2437 $this->result[$resKey][$key]['output_h'] = intval($info[1]);
2438 $this->result[$resKey][$key]['output_file'] = $gifFileName;
2439 } elseif ($isGD) { // file is generated
2440 $gifCreator->make();
2441 $this->result[$resKey][$key]['output_w'] = $gifCreator->w;
2442 $this->result[$resKey][$key]['output_h'] = $gifCreator->h;
2443 $this->result[$resKey][$key]['output_file'] = $gifFileName;
2444 $gifCreator->output($this->result[$resKey][$key]['output_file']);
2445 $gifCreator->destroy();
2446 }
2447
2448 $this->result[$resKey][$key]['output_file'] = t3lib_div::png_to_gif_by_imagemagick($this->result[$resKey][$key]['output_file']);
2449 // counter is increased
2450 $Hcounter += $this->result[$resKey][$key]['output_h'];
2451 // counter is increased
2452 $Wcounter += $this->result[$resKey][$key]['output_w'];
2453
2454 if ($maxFlag) {
2455 break;
2456 }
2457 }
2458 }
2459
2460 /**
2461 * Function searching for the largest width and height of the menu items to be generated.
2462 * Uses some of the same code as makeGifs and even instantiates some gifbuilder objects BUT does not render the images - only reading out which width they would have.
2463 * Remember to upgrade the code in here if the makeGifs function is updated.
2464 *
2465 * @param array $conf Same configuration array as passed to makeGifs()
2466 * @param integer $items The number of menu items
2467 * @param array $Hobjs Array with "applyTotalH" numbers
2468 * @param array $Wobjs Array with "applyTotalW" numbers
2469 * @param array $minDim Array with "min" x/y
2470 * @param array $maxDim Array with "max" x/y
2471 * @return array Array with keys "H" and "W" which are in themselves arrays with the heights and widths of menu items inside. This can be used to find the max/min size of the menu items.
2472 * @access private
2473 * @see makeGifs()
2474 */
2475 function findLargestDims($conf, $items, $Hobjs, $Wobjs, $minDim, $maxDim) {
2476 $totalWH = array(
2477 'W' => array(),
2478 'H' => array(),
2479 'W_total' => 0,
2480 'H_total' => 0
2481 );
2482
2483 $Hcounter = 0;
2484 $Wcounter = 0;
2485 $c = 0;
2486 $maxFlag = 0;
2487 foreach ($conf as $key => $val) {
2488 // SAME CODE AS makeGifs()! BEGIN
2489 if ($items == ($c+1) && $minDim) {
2490 $Lobjs = $this->mconf['removeObjectsOfDummy'];
2491 if ($Lobjs) {
2492 $Lobjs = t3lib_div::intExplode(',', $Lobjs);
2493 foreach ($Lobjs as $remItem) {
2494 unset($val[$remItem]);
2495 unset($val[$remItem.'.']);
2496 }
2497 }
2498
2499 $flag = 0;
2500 $tempXY = explode(',', $val['XY']);
2501 if ($Wcounter < $minDim[0]) {
2502 $tempXY[0] = $minDim[0] - $Wcounter;
2503 $flag = 1;
2504 }
2505 if ($Hcounter < $minDim[1]) {
2506 $tempXY[1] = $minDim[1] - $Hcounter;
2507 $flag = 1;
2508 }
2509 $val['XY'] = implode(',', $tempXY);
2510 if (!$flag) {
2511 break;
2512 }
2513 }
2514 $c++;
2515
2516 $gifCreator = t3lib_div::makeInstance('tslib_gifBuilder');
2517 $gifCreator->init();
2518 $gifCreator->start($val, $this->menuArr[$key]);
2519 if ($maxDim) {
2520 $tempXY = explode(',', $val['XY']);
2521 if ($maxDim[0] && $Wcounter + $gifCreator->XY[0] >= $maxDim[0]) {
2522 $tempXY[0] == $maxDim[0] - $Wcounter;
2523 $maxFlag = 1;
2524 }
2525 if ($maxDim[1] && $Hcounter + $gifCreator->XY[1] >= $maxDim[1]) {
2526 $tempXY[1] = $maxDim[1] - $Hcounter;
2527 $maxFlag = 1;
2528 }
2529 if ($maxFlag) {
2530 $val['XY'] = implode(',', $tempXY);
2531 $gifCreator = t3lib_div::makeInstance('tslib_gifBuilder');
2532 $gifCreator->init();
2533 $gifCreator->start($val, $this->menuArr[$key]);
2534 }
2535 }
2536 // SAME CODE AS makeGifs()! END
2537
2538 // Setting the width/height
2539 $totalWH['W'][$key] = $gifCreator->XY[0];
2540 $totalWH['H'][$key] = $gifCreator->XY[1];
2541 $totalWH['W_total'] += $gifCreator->XY[0];
2542 $totalWH['H_total'] += $gifCreator->XY[1];
2543
2544 // counter is increased
2545 $Hcounter += $gifCreator->XY[1];
2546 // counter is increased
2547 $Wcounter += $gifCreator->XY[0];
2548
2549 if ($maxFlag) {
2550 break;
2551 }
2552 }
2553 return $totalWH;
2554 }
2555
2556 /**
2557 * Traverses the ->result['NO'] array of menu items configuration (made by ->generate()) and renders the HTML of each item (the images themselves was made with makeGifs() before this. See ->generate())
2558 * During the execution of this function many internal methods prefixed "extProc_" from this class is called and many of these are for now dummy functions. But they can be used for processing as they are used by the GMENU_LAYERS
2559 *
2560 * @return string The HTML for the menu (returns result through $this->extProc_finish(); )
2561 */
2562 function writeMenu() {
2563 if (is_array($this->menuArr) && is_array($this->result) && count($this->result) && is_array($this->result['NO'])) {
2564 // Create new tslib_cObj for our use
2565 $this->WMcObj = t3lib_div::makeInstance('tslib_cObj');
2566 $this->WMresult = '';
2567 $this->INPfixMD5 = substr(md5(microtime().$this->GMENU_fixKey), 0, 4);
2568 $this->WMmenuItems = count($this->result['NO']);
2569
2570 $this->WMsubmenuObjSuffixes = $this->tmpl->splitConfArray(array('sOSuffix'=>$this->mconf['submenuObjSuffixes']), $this->WMmenuItems);
2571
2572 $this->extProc_init();
2573 for ($key = 0; $key < $this->WMmenuItems; $key++) {
2574 if ($this->result['NO'][$key]['output_file']) {
2575 // Initialize the cObj with the page record of the menu item
2576 $this->WMcObj->start($this->menuArr[$key], 'pages');
2577
2578 $this->I = array();
2579 $this->I['key'] = $key;
2580 $this->I['INPfix']= ($this->imgNameNotRandom ? '' : '_'.$this->INPfixMD5).'_'.$key;
2581 $this->I['val'] = $this->result['NO'][$key];
2582 $this->I['title'] = $this->getPageTitle($this->menuArr[$key]['title'], $this->menuArr[$key]['nav_title']);
2583 $this->I['uid'] = $this->menuArr[$key]['uid'];
2584 $this->I['mount_pid'] = $this->menuArr[$key]['mount_pid'];
2585 $this->I['pid'] = $this->menuArr[$key]['pid'];
2586 $this->I['spacer'] = $this->menuArr[$key]['isSpacer'];
2587 if (!$this->I['uid'] && !$this->menuArr[$key]['_OVERRIDE_HREF']) {
2588 $this->I['spacer'] = 1;
2589 }
2590 $this->I['noLink'] = ($this->I['spacer'] || $this->I['val']['noLink'] || !count($this->menuArr[$key])); // !count($this->menuArr[$key]) means that this item is a dummyItem
2591 $this->I['name'] = '';
2592
2593 // Set access key
2594 if ($this->mconf['accessKey']) {
2595 $this->I['accessKey'] = $this->accessKey($this->I['title']);
2596 } else {
2597 $this->I['accessKey'] = array();
2598 }
2599
2600 // Make link tag
2601 $this->I['val']['ATagParams'] = $this->WMcObj->getATagParams($this->I['val']);
2602 if (isset($this->I['val']['additionalParams.'])) {
2603 $this->I['val']['additionalParams'] = $this->WMcObj->stdWrap($this->I['val']['additionalParams'], $this->I['val']['additionalParams.']);
2604 }
2605 $this->I['linkHREF'] = $this->link($key, $this->I['val']['altTarget'], $this->mconf['forceTypeValue']);
2606
2607 // Title attribute of links:
2608 $titleAttrValue = isset($this->I['val']['ATagTitle.'])
2609 ? $this->WMcObj->stdWrap($this->I['val']['ATagTitle'], $this->I['val']['ATagTitle.']) . $this->I['accessKey']['alt']
2610 : $this->I['val']['ATagTitle'].$this->I['accessKey']['alt'];
2611 if (strlen($titleAttrValue)) {
2612 $this->I['linkHREF']['title'] = $titleAttrValue;
2613 }
2614
2615 // Set rollover
2616 if ($this->result['RO'][$key] && !$this->I['noLink']) {
2617 $this->I['theName'] = $this->imgNamePrefix.$this->I['uid'].$this->I['INPfix'];
2618 $this->I['name'] = ' '.$this->nameAttribute.'="'.$this->I['theName'].'"';
2619 $this->I['linkHREF']['onMouseover']=$this->WMfreezePrefix.'over(\''.$this->I['theName'].'\');';
2620 $this->I['linkHREF']['onMouseout']=$this->WMfreezePrefix.'out(\''.$this->I['theName'].'\');';
2621 $GLOBALS['TSFE']->JSImgCode.= LF.$this->I['theName'].'_n=new Image(); '.$this->I['theName'].'_n.src = "'.$GLOBALS['TSFE']->absRefPrefix.$this->I['val']['output_file'].'"; ';
2622 $GLOBALS['TSFE']->JSImgCode.= LF.$this->I['theName'].'_h=new Image(); '.$this->I['theName'].'_h.src = "'.$GLOBALS['TSFE']->absRefPrefix.$this->result['RO'][$key]['output_file'].'"; ';
2623 $GLOBALS['TSFE']->imagesOnPage[]=$this->result['RO'][$key]['output_file'];
2624 $GLOBALS['TSFE']->setJS('mouseOver');
2625 $this->extProc_RO($key);
2626 }
2627
2628 // Set altText
2629 $this->I['altText'] = $this->I['title'].$this->I['accessKey']['alt'];
2630
2631 // Calling extra processing function
2632 $this->extProc_beforeLinking($key);
2633
2634 // Set linking
2635 if (!$this->I['noLink']) {
2636 $this->setATagParts();
2637 } else {
2638 $this->I['A1'] = '';
2639 $this->I['A2'] = '';
2640 }
2641 $this->I['IMG'] = '<img src="'.$GLOBALS['TSFE']->absRefPrefix.$this->I['val']['output_file'].'" width="'.$this->I['val']['output_w'].'" height="'.$this->I['val']['output_h'].'" '.tslib_cObj::getBorderAttr('border="0"').($this->mconf['disableAltText'] ? '' : ' alt="'.htmlspecialchars($this->I['altText']).'"').$this->I['name'].($this->I['val']['imgParams']?' '.$this->I['val']['imgParams']:'').' />';
2642
2643 // Make before, middle and after parts
2644 $this->I['parts'] = array();
2645 $this->I['parts']['ATag_begin'] = $this->I['A1'];
2646 $this->I['parts']['image'] = $this->I['IMG'];
2647 $this->I['parts']['ATag_end'] = $this->I['A2'];
2648
2649 // Passing I to a user function
2650 if ($this->mconf['IProcFunc']) {
2651 $this->I = $this->userProcess('IProcFunc', $this->I);
2652 }
2653
2654 // Putting the item together.
2655 // Merge parts + beforeAllWrap
2656 $this->I['theItem']= implode('', $this->I['parts']);
2657 $this->I['theItem']= $this->extProc_beforeAllWrap($this->I['theItem'], $key);
2658
2659 // wrap:
2660 $this->I['theItem']= $this->tmpl->wrap($this->I