Fixed bug #9346: Preinitialise GLOBALS[LANG] in init.php
[Packages/TYPO3.CMS.git] / typo3 / classes / class.modulemenu.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2007-2008 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 $rawModuleData = $this->getRawModuleData();
137
138 foreach($rawModuleData as $moduleKey => $moduleData) {
139 $menuState = $GLOBALS['BE_USER']->uc['moduleData']['menuState'][$moduleKey];
140 $moduleLabel = $moduleData['title'];
141
142 if($moduleData['link'] && $this->linkModules) {
143 $moduleLabel = '<a href="#" onclick="top.goToModule(\''.$moduleData['name'].'\');'.$onBlur.'return false;">'.$moduleLabel.'</a>';
144 }
145
146 $menu .= '<li id="'.$moduleKey.'" class="menuSection" title="'.$moduleData['description'].'"><div class="'.($menuState ? 'collapsed' : 'expanded').'">'.$moduleData['icon']['html'].' '.$moduleLabel.'</div>';
147
148 // traverse submodules
149 if(is_array($moduleData['subitems'])) {
150 $menu .= $this->renderSubModules($moduleData['subitems'], $menuState);
151 }
152
153 $menu .= '</li>'."\n";
154 }
155
156 return ($wrapInUl ? '<ul id="typo3-menu">'."\n".$menu.'</ul>'."\n" : $menu);
157 }
158
159 /**
160 * renders the backend menu as unordered list as an AJAX response without
161 * the wrapping ul tags
162 *
163 * @param array array of parameters from the AJAX interface, currently unused
164 * @param TYPO3AJAX object of type TYPO3AJAX
165 * @return void
166 */
167 public function renderAjax($params = array(), TYPO3AJAX &$ajaxObj = null) {
168 $menu = $this->render(false);
169 $menuSwitch = $this->getGotoModuleJavascript();
170
171 // JS rocks: we can just overwrite a function with a new definition.
172 // and yes, we actually do that =)
173 $menuSwitchUpdate = '
174 <script type="text/javascript">
175 top.goToModule = '.$menuSwitch.';
176 </script>';
177
178 $ajaxObj->addContent('typo3-menu', $menu.$menuSwitchUpdate);
179 }
180
181 /**
182 * renders submodules
183 *
184 * @param array array of (sub)module data
185 * @param boolean collapse state of menu item, defaults to false
186 * @return string (sub)module html code
187 */
188 public function renderSubModules($modules, $menuState=false) {
189 $moduleMenu = '';
190 $onBlur = $GLOBALS['CLIENT']['FORMSTYLE'] ? 'this.blur();' : '';
191
192 foreach($modules as $moduleKey => $moduleData) {
193 // Setting additional JavaScript
194 $additionalJavascript = '';
195 if($moduleData['parentNavigationFrameScript']) {
196 $parentModuleName = substr($moduleData['name'], 0, strpos($moduleData['name'], '_'));
197 $additionalJavascript = "+'&id='+top.rawurlencode(top.fsMod.recentIds['".$parentModuleName."'])";
198 }
199
200 if($moduleData['link'] && $this->linkModules) {
201
202 $onClickString = htmlspecialchars('top.goToModule(\''.$moduleData['name'].'\');'.$onBlur.'return false;');
203 $submoduleLink = '<a href="#" onclick="'.$onClickString.'" title="'.$moduleData['description'].'">'
204 //TODO make icon a background image using css
205 .'<span class="submodule-icon">'.$moduleData['icon']['html'].'</span>'
206 .'<span>'.htmlspecialchars($moduleData['title']).'</span>'
207 .'</a>';
208 }
209
210 $moduleMenu .= '<li id="'.$moduleData['cssId'].'">'.$submoduleLink.'</li>'."\n";
211 }
212
213 return '<ul'.($menuState ? ' style="display:none;"' : '').'>'."\n".$moduleMenu.'</ul>'."\n";
214 }
215
216 /**
217 * gets the raw module data
218 *
219 * @return array multi dimension array with module data
220 */
221 public function getRawModuleData() {
222 $modules = array();
223
224 // Remove the 'doc' module?
225 if($GLOBALS['BE_USER']->getTSConfigVal('options.disableDocModuleInAB')) {
226 unset($this->loadedModules['doc']);
227 }
228
229 foreach($this->loadedModules as $moduleName => $moduleData) {
230 $moduleNavigationFramePrefix = $this->getNavigationFramePrefix($moduleData);
231
232 if($moduleNavigationFramePrefix) {
233 $this->fsMod[$moduleName] = 'fsMod.recentIds["'.$moduleName.'"]="";';
234 }
235
236 $moduleLink = '';
237 if(!is_array($moduleData['sub'])) {
238 $moduleLink = $moduleData['script'];
239 }
240 $moduleLink = t3lib_div::resolveBackPath($moduleLink);
241
242 $moduleKey = $moduleName.'_tab';
243 $moduleCssId = 'ID_'.t3lib_div::md5int($moduleName);
244 $moduleIcon = $this->getModuleIcon($moduleKey);
245
246 if($moduleLink && $moduleNavigationFramePrefix) {
247 $moduleLink = $moduleNavigationFramePrefix.rawurlencode($moduleLink);
248 }
249
250 $modules[$moduleKey] = array(
251 'name' => $moduleName,
252 'title' => $GLOBALS['LANG']->moduleLabels['tabs'][$moduleKey],
253 'onclick' => 'top.goToModule(\''.$moduleName.'\');',
254 'cssId' => $moduleCssId,
255 'icon' => $moduleIcon,
256 'link' => $moduleLink,
257 'prefix' => $moduleNavigationFramePrefix,
258 'description' => $GLOBALS['LANG']->moduleLabels['labels'][$moduleKey.'label']
259 );
260
261 if(is_array($moduleData['sub'])) {
262
263 foreach($moduleData['sub'] as $submoduleName => $submoduleData) {
264 $submoduleLink = t3lib_div::resolveBackPath($submoduleData['script']);
265 $submoduleNavigationFramePrefix = $this->getNavigationFramePrefix($moduleData, $submoduleData);
266
267 $submoduleKey = $moduleName.'_'.$submoduleName.'_tab';
268 $submoduleCssId = 'ID_'.t3lib_div::md5int($moduleName.'_'.$submoduleName);
269 $submoduleIcon = $this->getModuleIcon($submoduleKey);
270 $submoduleDescription = $GLOBALS['LANG']->moduleLabels['labels'][$submoduleKey.'label'];
271
272 $originalLink = $submoduleLink;
273 if($submoduleLink && $submoduleNavigationFramePrefix) {
274 $submoduleLink = $submoduleNavigationFramePrefix.rawurlencode($submoduleLink);
275 }
276
277 $modules[$moduleKey]['subitems'][$submoduleKey] = array(
278 'name' => $moduleName.'_'.$submoduleName,
279 'title' => $GLOBALS['LANG']->moduleLabels['tabs'][$submoduleKey],
280 'onclick' => 'top.goToModule(\''.$moduleName.'_'.$submoduleName.'\');',
281 'cssId' => $submoduleCssId,
282 'icon' => $submoduleIcon,
283 'link' => $submoduleLink,
284 'originalLink' => $originalLink,
285 'prefix' => $submoduleNavigationFramePrefix,
286 'description' => $submoduleDescription,
287 'navigationFrameScript' => $submoduleData['navFrameScript'],
288 );
289
290 if($moduleData['navFrameScript']) {
291 $modules[$moduleKey]['subitems'][$submoduleKey]['parentNavigationFrameScript'] = $moduleData['navFrameScript'];
292 }
293 }
294 }
295 }
296
297 return $modules;
298 }
299
300 /**
301 * gets the module icon and its size
302 *
303 * @param string module key
304 * @return array icon data array with 'filename', 'size', and 'html'
305 */
306 protected function getModuleIcon($moduleKey) {
307 $icon = array();
308 $iconFileRelative = $this->getModuleIconRelative($GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleKey]);
309 $iconFileAbsolute = $this->getModuleIconAbsolute($GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleKey]);
310 $iconSizes = @getimagesize($iconFileAbsolute);
311 $iconTitle = $GLOBALS['LANG']->moduleLabels['tabs'][$moduleKey];
312
313 $icon['filename'] = $iconFileRelative;
314 $icon['size'] = $iconSizes[3];
315 $icon['title'] = htmlspecialchars($iconTitle);
316 $icon['html'] = '<img src="'.$iconFileRelative.'" '.$iconSizes[3].' title="'.htmlspecialchars($iconTitle).'" alt="'.htmlspecialchars($iconTitle).'" />';
317
318 return $icon;
319 }
320
321 /**
322 * Returns the filename readable for the script from PATH_typo3.
323 * That means absolute names are just returned while relative names are
324 * prepended with the path pointing back to typo3/ dir
325 *
326 * @param string icon filename
327 * @return string icon filename with absolute path
328 * @see getModuleIconRelative()
329 */
330 protected function getModuleIconAbsolute($iconFilename) {
331
332 if(!t3lib_div::isAbsPath($iconFilename)) {
333 $iconFilename = $this->backPath.$iconFilename;
334 }
335
336 return $iconFilename;
337 }
338
339 /**
340 * Returns relative path to the icon filename for use in img-tags
341 *
342 * @param string icon filename
343 * @return string icon filename with relative path
344 * @see getModuleIconAbsolute()
345 */
346 protected function getModuleIconRelative($iconFilename) {
347 if(t3lib_div::isAbsPath($iconFilename)) {
348 $iconFilename = '../'.substr($iconFilename, strlen(PATH_site));
349 }
350
351 return $this->backPath.$iconFilename;
352 }
353
354 /**
355 * Returns a prefix used to call the navigation frame with parameters which then will call the scripts defined in the modules info array.
356 *
357 * @param array module data array
358 * @param array submodule data array
359 * @return string result URL string
360 */
361 protected function getNavigationFramePrefix($moduleData, $subModuleData = array()) {
362 $prefix = '';
363
364 $navigationFrameScript = $moduleData['navFrameScript'];
365 if($subModuleData['navFrameScript']) {
366 $navigationFrameScript = $subModuleData['navFrameScript'];
367 }
368
369 $navigationFrameParameter = $moduleData['navFrameScriptParam'];
370 if($subModuleData['navFrameScriptParam']) {
371 $navigationFrameParameter = $subModuleData['navFrameScriptParam'];
372 }
373
374 if($navigationFrameScript) {
375 $navigationFrameScript = t3lib_div::resolveBackPath($navigationFrameScript);
376 $navigationFrameScript = $this->appendQuestionmarkToLink($navigationFrameScript);
377
378 if($GLOBALS['BE_USER']->uc['condensedMode']) {
379 $prefix = $navigationFrameScript.$navigationFrameParameter.'&currentSubScript=';
380 } else {
381 $prefix = 'alt_mod_frameset.php?'
382 .'fW="+top.TS.navFrameWidth+"'
383 .'&nav="+top.TS.PATH_typo3+"'
384 .rawurlencode($navigationFrameScript.$navigationFrameParameter)
385 .'&script=';
386 }
387 }
388
389 return $prefix;
390 }
391
392 /**
393 * generates javascript code to switch between modules
394 *
395 * @return string javascript code snippet to switch modules
396 */
397 public function getGotoModuleJavascript() {
398
399 $moduleJavascriptCommands = array();
400 $rawModuleData = $this->getRawModuleData();
401
402 foreach($rawModuleData as $mainModuleKey => $mainModuleData) {
403 if($mainModuleData['subitems']) {
404 foreach($mainModuleData['subitems'] as $subModuleKey => $subModuleData) {
405
406 $parentModuleName = substr($subModuleData['name'], 0, strpos($subModuleData['name'], '_'));
407 $javascriptCommand = '';
408
409 // Setting additional JavaScript if frameset script:
410 $additionalJavascript = '';
411 if($subModuleData['parentNavigationFrameScript']) {
412 $additionalJavascript = "+'&id='+top.rawurlencode(top.fsMod.recentIds['".$parentModuleName."'])";
413 }
414
415 if($subModuleData['link'] && $this->linkModules) {
416 // For condensed mode, send &cMR parameter to frameset script.
417 if($additionalJavascript && $GLOBALS['BE_USER']->uc['condensedMode']) {
418 $additionalJavascript .= "+(cMR?'&cMR=1':'')";
419 }
420
421 $javascriptCommand = '
422 $(\'content\').src = top.getModuleUrl(top.TS.PATH_typo3+"'.$this->appendQuestionmarkToLink($subModuleData['link']).'"'.$additionalJavascript.'+additionalGetVariables);
423 top.fsMod.currentMainLoaded="'.$parentModuleName.'";
424 ';
425
426 if($subModuleData['navFrameScript']) {
427 $javascriptCommand .= '
428 top.currentSubScript="'.$subModuleData['originalLink'].'";';
429 }
430
431 if(!$GLOBALS['BE_USER']->uc['condensedMode'] && $subModuleData['parentNavigationFrameScript']) {
432 $additionalJavascript = "+'&id='+top.rawurlencode(top.fsMod.recentIds['".$parentModuleName."'])";
433
434 $submoduleNavigationFrameScript = $subModuleData['navigationFrameScript'] ? $subModuleData['navigationFrameScript'] : $subModuleData['parentNavigationFrameScript'];
435 $submoduleNavigationFrameScript = t3lib_div::resolveBackPath($submoduleNavigationFrameScript);
436
437 // add GET parameters for sub module to the navigation script
438 $submoduleNavigationFrameScript = $this->appendQuestionmarkToLink($submoduleNavigationFrameScript).$subModuleData['navigationFrameScript'];
439
440 $javascriptCommand = '
441 if (top.content.list_frame && top.fsMod.currentMainLoaded=="'.$parentModuleName.'") {
442 top.currentSubScript="'.$subModuleData['originalLink'].'";
443 top.content.list_frame.location=top.getModuleUrl(top.TS.PATH_typo3+"'.$this->appendQuestionmarkToLink($subModuleData['originalLink']).'"'.$additionalJavascript.'+additionalGetVariables);
444 if(top.currentSubNavScript!="'.$submoduleNavigationFrameScript.'") {
445 top.currentSubNavScript="'.$submoduleNavigationFrameScript.'";
446 top.content.nav_frame.location=top.getModuleUrl(top.TS.PATH_typo3+"'.$submoduleNavigationFrameScript.'");
447 }
448 } else {
449 $(\'content\').src = top.TS.PATH_typo3+(
450 top.nextLoadModuleUrl?
451 "'.($subModuleData['prefix'] ? $this->appendQuestionmarkToLink($subModuleData['link']).'&exScript=' : '').'listframe_loader.php":
452 "'.$this->appendQuestionmarkToLink($subModuleData['link']).'"'.$additionalJavascript.'+additionalGetVariables
453 );
454 top.fsMod.currentMainLoaded="'.$parentModuleName.'";
455 top.currentSubScript="'.$subModuleData['originalLink'].'";
456 }
457 ';
458 }
459
460 $javascriptCommand .= '
461 top.highlightModuleMenuItem("'.$subModuleData['cssId'].'");
462 ';
463 $moduleJavascriptCommands[] = " case '".$subModuleData['name']."': \n ".$javascriptCommand." \n break;";
464 }
465 }
466 } elseif(!$mainModuleData['subitems'] && !empty($mainModuleData['link'])) {
467 // main module has no sub modules but instead is linked itself (doc module f.e.)
468 $javascriptCommand = '
469 top.content.location=top.getModuleUrl(top.TS.PATH_typo3+"'.$this->appendQuestionmarkToLink($mainModuleData['link']).'"+additionalGetVariables);
470 top.highlightModuleMenuItem("'.$mainModuleData['cssId'].'", 1);
471 ';
472 $moduleJavascriptCommands[] = " case '".$mainModuleData['name']."': \n ".$javascriptCommand." \n break;";
473 }
474 }
475
476 $javascriptCode = 'function(modName, cMR_flag, addGetVars) {
477 var additionalGetVariables = "";
478 if (addGetVars) additionalGetVariables = addGetVars;
479
480 var cMR = 0;
481 if (cMR_flag) cMR = 1;
482
483 currentModuleLoaded = modName;
484
485 switch(modName) {'
486 ."\n".implode("\n", $moduleJavascriptCommands)."\n".'
487 }
488 }';
489
490 return $javascriptCode;
491 }
492
493 /**
494 * Appends a '?' if there is none in the string already
495 *
496 * @param string Link URL
497 * @return string link URl appended with ? if there wasn't one
498 */
499 protected function appendQuestionmarkToLink($link) {
500 if(!strstr($link, '?')) {
501 $link .= '?';
502 }
503
504 return $link;
505 }
506
507 /**
508 * renders the logout button form
509 *
510 * @return string html code snippet displaying the logout button
511 */
512 public function renderLogoutButton() {
513 $buttonLabel = $GLOBALS['BE_USER']->user['ses_backuserid'] ? 'LLL:EXT:lang/locallang_core.php:buttons.exit' : 'LLL:EXT:lang/locallang_core.php:buttons.logout';
514
515 $buttonForm = '
516 <form action="logout.php" target="_top">
517 <input type="submit" value="'.$GLOBALS['LANG']->sL($buttonLabel, 1).'" />
518 </form>';
519
520 return $buttonForm;
521 }
522
523 /**
524 * turns linking of modules on or off
525 *
526 * @param boolean status for linking modules with a-tags, set to false to turn lining off
527 */
528 public function setLinkModules($linkModules) {
529 if(!is_bool($linkModules)) {
530 throw new InvalidArgumentException('parameter $linkModules must be of type bool', 1193326558);
531 }
532
533 $this->linkModules = $linkModules;
534 }
535
536 /**
537 * gets the frameset (leftover) helper
538 *
539 * @return array array of javascript snippets
540 */
541 public function getFsMod() {
542 return $this->fsMod;
543 }
544 }
545
546
547 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.modulemenu.php']) {
548 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.modulemenu.php']);
549 }
550
551 ?>