Fixed bug #12466: PHP-5.3 Warning: strlen() expects parameter 1 to be string, array...
[Packages/TYPO3.CMS.git] / typo3 / classes / class.modulemenu.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2007-2009 Ingo Renner <ingo@typo3.org>
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
29 if(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
30 $GLOBALS['LANG']->includeLLFile('EXT:lang/locallang_misc.xml');
31 }
32
33 /**
34 * class to render the TYPO3 backend menu for the modules
35 *
36 * @author Ingo Renner <ingo@typo3.org>
37 * @package TYPO3
38 * @subpackage core
39 */
40 class ModuleMenu {
41
42 /**
43 * module loading object
44 *
45 * @var t3lib_loadModules
46 */
47 protected $moduleLoader;
48
49 protected $backPath;
50 protected $linkModules;
51 protected $loadedModules;
52 protected $fsMod; //TODO find a more descriptive name, left over from alt_menu_functions
53
54 /**
55 * constructor, initializes several variables
56 *
57 * @return void
58 */
59 public function __construct() {
60
61 $this->backPath = '';
62 $this->fsMod = array();
63 $this->linkModules = true;
64
65 // Loads the backend modules available for the logged in user.
66 $this->moduleLoader = t3lib_div::makeInstance('t3lib_loadModules');
67 $this->moduleLoader->observeWorkspaces = true;
68 $this->moduleLoader->load($GLOBALS['TBE_MODULES']);
69 $this->loadedModules = $this->moduleLoader->modules;
70
71 }
72
73 /**
74 * sets the path back to /typo3/
75 *
76 * @param string path back to /typo3/
77 * @return void
78 */
79 public function setBackPath($backPath) {
80 if(!is_string($backPath)) {
81 throw new InvalidArgumentException('parameter $backPath must be of type string', 1193315266);
82 }
83
84 $this->backPath = $backPath;
85 }
86
87 /**
88 * loads the collapse states for the main modules from user's configuration (uc)
89 *
90 * @return array collapse states
91 */
92 protected function getCollapsedStates() {
93
94 $collapsedStates = array();
95 if($GLOBALS['BE_USER']->uc['moduleData']['moduleMenu']) {
96 $collapsedStates = $GLOBALS['BE_USER']->uc['moduleData']['moduleMenu'];
97 }
98
99 return $collapsedStates;
100 }
101
102 /**
103 * returns the loaded modules
104 *
105 * @return array array of loaded modules
106 */
107 public function getLoadedModules() {
108 return $this->loadedModules;
109 }
110
111 /**
112 * saves the menu's toggle state in the backend user's uc
113 *
114 * @param array array of parameters from the AJAX interface, currently unused
115 * @param TYPO3AJAX object of type TYPO3AJAX
116 * @return void
117 */
118 public function saveMenuState($params, $ajaxObj) {
119 $menuItem = t3lib_div::_POST('menuid');
120 $state = t3lib_div::_POST('state') === 'true' ? 1 : 0;
121
122 $GLOBALS['BE_USER']->uc['moduleData']['menuState'][$menuItem] = $state;
123 $GLOBALS['BE_USER']->writeUC();
124 }
125
126 /**
127 * renders the backend menu as unordered list
128 *
129 * @param boolean optional parameter used to switch wrapping the menu in ul tags off for AJAX calls
130 * @return string menu html code to use in the backend
131 */
132 public function render($wrapInUl = true) {
133 $menu = '';
134 $onBlur = $GLOBALS['CLIENT']['FORMSTYLE'] ? 'this.blur();' : '';
135
136 $tsConfiguration = $GLOBALS['BE_USER']->getTSConfig('options.moduleMenuCollapsable');
137 $collapsable = (isset($tsConfiguration['value']) && $tsConfiguration['value'] == 0) ? 0 : 1;
138
139 $rawModuleData = $this->getRawModuleData();
140
141 foreach($rawModuleData as $moduleKey => $moduleData) {
142 $menuState = $GLOBALS['BE_USER']->uc['moduleData']['menuState'][$moduleKey];
143 $moduleLabel = $moduleData['title'];
144
145 if($moduleData['link'] && $this->linkModules) {
146 $moduleLabel = '<a href="#" onclick="top.goToModule(\''.$moduleData['name'].'\');'.$onBlur.'return false;">'.$moduleLabel.'</a>';
147 }
148
149 $menu .= '<li id="modmenu_' . $moduleData['name'] . '" '.
150 ($collapsable ? 'class="menuSection"' : '') .
151 ' title="' . $moduleData['description'] . '">
152 <div class="' . ($menuState ? 'collapsed' : 'expanded') . '">' .
153 $moduleData['icon']['html'] . ' ' . $moduleLabel . '</div>';
154
155 // traverse submodules
156 if(is_array($moduleData['subitems'])) {
157 $menu .= $this->renderSubModules($moduleData['subitems'], $menuState);
158 }
159
160 $menu .= '</li>'."\n";
161 }
162
163 return ($wrapInUl ? '<ul id="typo3-menu">'."\n".$menu.'</ul>'."\n" : $menu);
164 }
165
166 /**
167 * renders the backend menu as unordered list as an AJAX response without
168 * the wrapping ul tags
169 *
170 * @param array array of parameters from the AJAX interface, currently unused
171 * @param TYPO3AJAX object of type TYPO3AJAX
172 * @return void
173 */
174 public function renderAjax($params = array(), TYPO3AJAX &$ajaxObj = null) {
175 $menu = $this->render(false);
176 $menuSwitch = $this->getGotoModuleJavascript();
177
178 // JS rocks: we can just overwrite a function with a new definition.
179 // and yes, we actually do that =)
180 $menuSwitchUpdate = '
181 <script type="text/javascript">
182 top.goToModule = '.$menuSwitch.';
183 </script>';
184
185 $ajaxObj->addContent('typo3-menu', $menu.$menuSwitchUpdate);
186 }
187
188 /**
189 * renders submodules
190 *
191 * @param array array of (sub)module data
192 * @param boolean collapse state of menu item, defaults to false
193 * @return string (sub)module html code
194 */
195 public function renderSubModules($modules, $menuState=false) {
196 $moduleMenu = '';
197 $onBlur = $GLOBALS['CLIENT']['FORMSTYLE'] ? 'this.blur();' : '';
198
199 foreach($modules as $moduleKey => $moduleData) {
200 // Setting additional JavaScript
201 $additionalJavascript = '';
202 if($moduleData['parentNavigationFrameScript']) {
203 $parentModuleName = substr($moduleData['name'], 0, strpos($moduleData['name'], '_'));
204 $additionalJavascript = "+'&id='+top.rawurlencodeAndRemoveSiteUrl(top.fsMod.recentIds['" . $parentModuleName . "'])";
205 }
206
207 if($moduleData['link'] && $this->linkModules) {
208
209 $onClickString = htmlspecialchars('top.goToModule(\''.$moduleData['name'].'\');'.$onBlur.'return false;');
210 $submoduleLink = '<a href="#" onclick="'.$onClickString.'" title="'.$moduleData['description'].'">'
211 //TODO make icon a background image using css
212 .'<span class="submodule-icon">'.$moduleData['icon']['html'].'</span>'
213 .'<span>'.htmlspecialchars($moduleData['title']).'</span>'
214 .'</a>';
215 }
216
217 $moduleMenu .= '<li id="modmenu_' . $moduleData['name'] . '">' . $submoduleLink . '</li>' . "\n";
218 }
219
220 return '<ul'.($menuState ? ' style="display:none;"' : '').'>'."\n".$moduleMenu.'</ul>'."\n";
221 }
222
223 /**
224 * gets the raw module data
225 *
226 * @return array multi dimension array with module data
227 */
228 public function getRawModuleData() {
229 $modules = array();
230
231 // Remove the 'doc' module?
232 if($GLOBALS['BE_USER']->getTSConfigVal('options.disableDocModuleInAB')) {
233 unset($this->loadedModules['doc']);
234 }
235
236 foreach($this->loadedModules as $moduleName => $moduleData) {
237 $moduleNavigationFramePrefix = $this->getNavigationFramePrefix($moduleData);
238
239 if($moduleNavigationFramePrefix) {
240 $this->fsMod[$moduleName] = 'fsMod.recentIds["'.$moduleName.'"]="";';
241 }
242
243 $moduleLink = '';
244 if(!is_array($moduleData['sub'])) {
245 $moduleLink = $moduleData['script'];
246 }
247 $moduleLink = t3lib_div::resolveBackPath($moduleLink);
248
249 $moduleKey = 'modmenu_' . $moduleName;
250 $moduleIcon = $this->getModuleIcon($moduleKey);
251
252 if($moduleLink && $moduleNavigationFramePrefix) {
253 $moduleLink = $moduleNavigationFramePrefix.rawurlencode($moduleLink);
254 }
255
256 $modules[$moduleKey] = array(
257 'name' => $moduleName,
258 'title' => $GLOBALS['LANG']->moduleLabels['tabs'][$moduleName . '_tab'],
259 'onclick' => 'top.goToModule(\''.$moduleName.'\');',
260 'icon' => $moduleIcon,
261 'link' => $moduleLink,
262 'prefix' => $moduleNavigationFramePrefix,
263 'description' => $GLOBALS['LANG']->moduleLabels['labels'][$moduleKey.'label']
264 );
265
266 if(is_array($moduleData['sub'])) {
267
268 foreach($moduleData['sub'] as $submoduleName => $submoduleData) {
269 $submoduleLink = t3lib_div::resolveBackPath($submoduleData['script']);
270 $submoduleNavigationFramePrefix = $this->getNavigationFramePrefix($moduleData, $submoduleData);
271
272 $submoduleKey = $moduleName.'_'.$submoduleName.'_tab';
273 $submoduleIcon = $this->getModuleIcon($submoduleKey);
274 $submoduleDescription = $GLOBALS['LANG']->moduleLabels['labels'][$submoduleKey.'label'];
275
276 $originalLink = $submoduleLink;
277 if($submoduleLink && $submoduleNavigationFramePrefix) {
278 $submoduleLink = $submoduleNavigationFramePrefix.rawurlencode($submoduleLink);
279 }
280
281 $modules[$moduleKey]['subitems'][$submoduleKey] = array(
282 'name' => $moduleName.'_'.$submoduleName,
283 'title' => $GLOBALS['LANG']->moduleLabels['tabs'][$submoduleKey],
284 'onclick' => 'top.goToModule(\''.$moduleName.'_'.$submoduleName.'\');',
285 'icon' => $submoduleIcon,
286 'link' => $submoduleLink,
287 'originalLink' => $originalLink,
288 'prefix' => $submoduleNavigationFramePrefix,
289 'description' => $submoduleDescription,
290 'navigationFrameScript' => $submoduleData['navFrameScript'],
291 );
292
293 if($moduleData['navFrameScript']) {
294 $modules[$moduleKey]['subitems'][$submoduleKey]['parentNavigationFrameScript'] = $moduleData['navFrameScript'];
295 }
296 }
297 }
298 }
299
300 return $modules;
301 }
302
303 /**
304 * gets the module icon and its size
305 *
306 * @param string module key
307 * @return array icon data array with 'filename', 'size', and 'html'
308 */
309 protected function getModuleIcon($moduleKey) {
310 $icon = array(
311 'filename' => '',
312 'size' => '',
313 'title' => '',
314 'html' => ''
315 );
316
317 $iconFileRelative = $this->getModuleIconRelative($GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleKey]);
318 $iconFileAbsolute = $this->getModuleIconAbsolute($GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleKey]);
319 $iconSizes = @getimagesize($iconFileAbsolute);
320 $iconTitle = $GLOBALS['LANG']->moduleLabels['tabs'][$moduleKey];
321
322 if(!empty($iconFileRelative)) {
323 $icon['filename'] = $iconFileRelative;
324 $icon['size'] = $iconSizes[3];
325 $icon['title'] = htmlspecialchars($iconTitle);
326 $icon['html'] = '<img src="'.$iconFileRelative.'" '.$iconSizes[3].' title="'.htmlspecialchars($iconTitle).'" alt="'.htmlspecialchars($iconTitle).'" />';
327 }
328
329 return $icon;
330 }
331
332 /**
333 * Returns the filename readable for the script from PATH_typo3.
334 * That means absolute names are just returned while relative names are
335 * prepended with the path pointing back to typo3/ dir
336 *
337 * @param string icon filename
338 * @return string icon filename with absolute path
339 * @see getModuleIconRelative()
340 */
341 protected function getModuleIconAbsolute($iconFilename) {
342
343 if(!t3lib_div::isAbsPath($iconFilename)) {
344 $iconFilename = $this->backPath . $iconFilename;
345 }
346
347 return $iconFilename;
348 }
349
350 /**
351 * Returns relative path to the icon filename for use in img-tags
352 *
353 * @param string icon filename
354 * @return string icon filename with relative path
355 * @see getModuleIconAbsolute()
356 */
357 protected function getModuleIconRelative($iconFilename) {
358 if (t3lib_div::isAbsPath($iconFilename)) {
359 $iconFilename = '../' . substr($iconFilename, strlen(PATH_site));
360 }
361 return $this->backPath.$iconFilename;
362 }
363
364 /**
365 * Returns a prefix used to call the navigation frame with parameters which then will call the scripts defined in the modules info array.
366 *
367 * @param array module data array
368 * @param array submodule data array
369 * @return string result URL string
370 */
371 protected function getNavigationFramePrefix($moduleData, $subModuleData = array()) {
372 $prefix = '';
373
374 $navigationFrameScript = $moduleData['navFrameScript'];
375 if($subModuleData['navFrameScript']) {
376 $navigationFrameScript = $subModuleData['navFrameScript'];
377 }
378
379 $navigationFrameParameter = $moduleData['navFrameScriptParam'];
380 if($subModuleData['navFrameScriptParam']) {
381 $navigationFrameParameter = $subModuleData['navFrameScriptParam'];
382 }
383
384 if($navigationFrameScript) {
385 $navigationFrameScript = t3lib_div::resolveBackPath($navigationFrameScript);
386 $navigationFrameScript = $this->appendQuestionmarkToLink($navigationFrameScript);
387
388 if($GLOBALS['BE_USER']->uc['condensedMode']) {
389 $prefix = $navigationFrameScript.$navigationFrameParameter.'&currentSubScript=';
390 } else {
391 $prefix = 'alt_mod_frameset.php?'
392 .'fW="+top.TS.navFrameWidth+"'
393 .'&nav="+top.TS.PATH_typo3+"'
394 .rawurlencode($navigationFrameScript.$navigationFrameParameter)
395 .'&script=';
396 }
397 }
398
399 return $prefix;
400 }
401
402 /**
403 * generates javascript code to switch between modules
404 *
405 * @return string javascript code snippet to switch modules
406 */
407 public function getGotoModuleJavascript() {
408
409 $moduleJavascriptCommands = array();
410 $rawModuleData = $this->getRawModuleData();
411 $navFrameScripts = array();
412
413 foreach($rawModuleData as $mainModuleKey => $mainModuleData) {
414 if ($mainModuleData['subitems']) {
415 foreach ($mainModuleData['subitems'] as $subModuleKey => $subModuleData) {
416
417 $parentModuleName = substr($subModuleData['name'], 0, strpos($subModuleData['name'], '_'));
418 $javascriptCommand = '';
419
420 // Setting additional JavaScript if frameset script:
421 $additionalJavascript = '';
422 if($subModuleData['parentNavigationFrameScript']) {
423 $additionalJavascript = "+'&id='+top.rawurlencodeAndRemoveSiteUrl(top.fsMod.recentIds['" . $parentModuleName . "'])";
424 }
425
426 if ($subModuleData['link'] && $this->linkModules) {
427 // For condensed mode, send &cMR parameter to frameset script.
428 if ($additionalJavascript && $GLOBALS['BE_USER']->uc['condensedMode']) {
429 $additionalJavascript .= "+(cMR ? '&cMR=1' : '')";
430 }
431
432 $javascriptCommand = '
433 modScriptURL = "'.$this->appendQuestionmarkToLink($subModuleData['link']).'"'.$additionalJavascript.';';
434
435 if ($subModuleData['navFrameScript']) {
436 $javascriptCommand .= '
437 top.currentSubScript="'.$subModuleData['originalLink'].'";';
438 }
439
440 if (!$GLOBALS['BE_USER']->uc['condensedMode'] && $subModuleData['parentNavigationFrameScript']) {
441 $additionalJavascript = "+'&id='+top.rawurlencodeAndRemoveSiteUrl(top.fsMod.recentIds['" . $parentModuleName . "'])";
442
443 $submoduleNavigationFrameScript = $subModuleData['navigationFrameScript'] ? $subModuleData['navigationFrameScript'] : $subModuleData['parentNavigationFrameScript'];
444 $submoduleNavigationFrameScript = t3lib_div::resolveBackPath($submoduleNavigationFrameScript);
445
446 // add GET parameters for sub module to the navigation script
447 $submoduleNavigationFrameScript = $this->appendQuestionmarkToLink($submoduleNavigationFrameScript).$subModuleData['navigationFrameScript'];
448 $navFrameScripts[$parentModuleName] = $submoduleNavigationFrameScript;
449 $javascriptCommand = '
450 top.currentSubScript = "'.$subModuleData['originalLink'].'";
451 if (top.content.list_frame && top.fsMod.currentMainLoaded == mainModName) {
452 modScriptURL = "'.$this->appendQuestionmarkToLink($subModuleData['originalLink']).'"'.$additionalJavascript.';
453 } else if (top.nextLoadModuleUrl) {
454 modScriptURL = "'.($subModuleData['prefix'] ? $this->appendQuestionmarkToLink($subModuleData['link']) . '&exScript=' : '') . 'listframe_loader.php";
455 } else {
456 modScriptURL = "'.$this->appendQuestionmarkToLink($subModuleData['link']).'"'.$additionalJavascript.' + additionalGetVariables;
457 }';
458 }
459 }
460 $moduleJavascriptCommands[] = "
461 case '".$subModuleData['name']."':".$javascriptCommand."
462 break;";
463 }
464 } elseif(!$mainModuleData['subitems'] && !empty($mainModuleData['link'])) {
465 // main module has no sub modules but instead is linked itself (doc module f.e.)
466 $javascriptCommand = '
467 modScriptURL = "'.$this->appendQuestionmarkToLink($mainModuleData['link']).'";';
468 $moduleJavascriptCommands[] = "
469 case '".$mainModuleData['name']."':".$javascriptCommand."
470 break;";
471 }
472 }
473
474 $javascriptCode = 'function(modName, cMR_flag, addGetVars) {
475 var useCondensedMode = '.($GLOBALS['BE_USER']->uc['condensedMode'] ? 'true' : 'false').';
476 var mainModName = (modName.slice(0, modName.indexOf("_")) || modName);
477
478 var additionalGetVariables = "";
479 if (addGetVars) {
480 additionalGetVariables = addGetVars;
481 }
482
483 var cMR = (cMR_flag ? 1 : 0);
484 var modScriptURL = "";
485
486 switch(modName) {'
487 ."\n".implode("\n", $moduleJavascriptCommands)."\n".'
488 }
489
490 var navFrames = {};';
491 foreach ($navFrameScripts as $mainMod => $frameScript) {
492 $javascriptCode .= '
493 navFrames["'.$mainMod.'"] = "'.$frameScript.'";';
494 }
495 $javascriptCode .= '
496
497 if (!useCondensedMode && navFrames[mainModName]) {
498 if (top.content.list_frame && top.fsMod.currentMainLoaded == mainModName) {
499 top.content.list_frame.location = top.getModuleUrl(top.TS.PATH_typo3 + modScriptURL + additionalGetVariables);
500 if (top.currentSubNavScript != navFrames[mainModName]) {
501 top.currentSubNavScript = navFrames[mainModName];
502 top.content.nav_frame.location = top.getModuleUrl(top.TS.PATH_typo3 + navFrames[mainModName]);
503 }
504 } else {
505 $("content").src = top.TS.PATH_typo3 + modScriptURL;
506 }
507 } else if (modScriptURL) {
508 $("content").src = top.getModuleUrl(top.TS.PATH_typo3 + modScriptURL + additionalGetVariables);
509 }
510 currentModuleLoaded = modName;
511 top.fsMod.currentMainLoaded = mainModName;
512 TYPO3ModuleMenu.highlightModule("modmenu_" + modName, (modName == mainModName ? 1 : 0));
513 }';
514
515 return $javascriptCode;
516 }
517
518 /**
519 * Appends a '?' if there is none in the string already
520 *
521 * @param string Link URL
522 * @return string link URl appended with ? if there wasn't one
523 */
524 protected function appendQuestionmarkToLink($link) {
525 if(!strstr($link, '?')) {
526 $link .= '?';
527 }
528
529 return $link;
530 }
531
532 /**
533 * renders the logout button form
534 *
535 * @return string html code snippet displaying the logout button
536 */
537 public function renderLogoutButton() {
538 $buttonLabel = $GLOBALS['BE_USER']->user['ses_backuserid'] ? 'LLL:EXT:lang/locallang_core.php:buttons.exit' : 'LLL:EXT:lang/locallang_core.php:buttons.logout';
539
540 $buttonForm = '
541 <form action="logout.php" target="_top">
542 <input type="submit" value="&nbsp;' . $GLOBALS['LANG']->sL($buttonLabel, 1) . '&nbsp;" />
543 </form>';
544
545 return $buttonForm;
546 }
547
548 /**
549 * turns linking of modules on or off
550 *
551 * @param boolean status for linking modules with a-tags, set to false to turn lining off
552 */
553 public function setLinkModules($linkModules) {
554 if(!is_bool($linkModules)) {
555 throw new InvalidArgumentException('parameter $linkModules must be of type bool', 1193326558);
556 }
557
558 $this->linkModules = $linkModules;
559 }
560
561 /**
562 * gets the frameset (leftover) helper
563 *
564 * @return array array of javascript snippets
565 */
566 public function getFsMod() {
567 return $this->fsMod;
568 }
569 }
570
571
572 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.modulemenu.php']) {
573 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.modulemenu.php']);
574 }
575
576 ?>