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