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