[FEATURE] Refactor toolbar / topbar HTML5 & layout
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / BackendController.php
1 <?php
2 namespace TYPO3\CMS\Backend\Controller;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * Class for rendering the TYPO3 backend version 4.2+
23 *
24 * @author Ingo Renner <ingo@typo3.org>
25 */
26 class BackendController {
27
28 protected $content;
29
30 protected $css;
31
32 protected $cssFiles;
33
34 protected $js;
35
36 protected $jsFiles;
37
38 protected $jsFilesAfterInline;
39
40 protected $toolbarItems;
41
42 // intentionally private as nobody should modify defaults
43 private $menuWidthDefault = 190;
44
45 protected $menuWidth;
46
47 protected $debug;
48
49 /**
50 * @var \TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository
51 */
52 protected $backendModuleRepository;
53
54 /**
55 * Object for loading backend modules
56 *
57 * @var \TYPO3\CMS\Backend\Module\ModuleLoader
58 */
59 protected $moduleLoader;
60
61 /**
62 * Pagerenderer
63 *
64 * @var \TYPO3\CMS\Core\Page\PageRenderer
65 */
66 protected $pageRenderer;
67
68 /**
69 * @return \TYPO3\CMS\Core\Page\PageRenderer
70 */
71 public function getPageRenderer() {
72 return $this->pageRenderer;
73 }
74
75 /**
76 * Constructor
77 */
78 public function __construct() {
79 $this->backendModuleRepository = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Domain\\Repository\\Module\\BackendModuleRepository');
80
81 // Set debug flag for BE development only
82 $this->debug = (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] === 1;
83 // Initializes the backend modules structure for use later.
84 $this->moduleLoader = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Module\\ModuleLoader');
85 $this->moduleLoader->load($GLOBALS['TBE_MODULES']);
86 $this->pageRenderer = $GLOBALS['TBE_TEMPLATE']->getPageRenderer();
87 $this->pageRenderer->loadScriptaculous('builder,effects,controls,dragdrop');
88 $this->pageRenderer->loadExtJS();
89 $this->pageRenderer->loadJquery(NULL, NULL, \TYPO3\CMS\Core\Page\PageRenderer::JQUERY_NAMESPACE_DEFAULT_NOCONFLICT);
90 $this->pageRenderer->enableExtJSQuickTips();
91 $this->pageRenderer->addJsInlineCode('consoleOverrideWithDebugPanel', '//already done', FALSE);
92 $this->pageRenderer->addExtDirectCode();
93 // Add default BE javascript
94 $this->js = '';
95 $this->jsFiles = array(
96 'common' => 'sysext/backend/Resources/Public/JavaScript/common.js',
97 'locallang' => $this->getLocalLangFileName(),
98 'modernizr' => 'contrib/modernizr/modernizr.min.js',
99 'md5' => 'sysext/backend/Resources/Public/JavaScript/md5.js',
100 'toolbarmanager' => 'sysext/backend/Resources/Public/JavaScript/toolbarmanager.js',
101 'modulemenu' => 'sysext/backend/Resources/Public/JavaScript/modulemenu.js',
102 'evalfield' => 'sysext/backend/Resources/Public/JavaScript/jsfunc.evalfield.js',
103 'flashmessages' => 'sysext/backend/Resources/Public/JavaScript/flashmessages.js',
104 'tabclosemenu' => 'js/extjs/ux/ext.ux.tabclosemenu.js',
105 'notifications' => 'sysext/backend/Resources/Public/JavaScript/notifications.js',
106 'backend' => 'sysext/backend/Resources/Public/JavaScript/backend.js',
107 'loginrefresh' => 'sysext/backend/Resources/Public/JavaScript/loginrefresh.js',
108 'debugPanel' => 'js/extjs/debugPanel.js',
109 'viewport' => 'js/extjs/viewport.js',
110 'iframepanel' => 'sysext/backend/Resources/Public/JavaScript/iframepanel.js',
111 'backendcontentiframe' => 'js/extjs/backendcontentiframe.js',
112 'modulepanel' => 'js/extjs/modulepanel.js',
113 'viewportConfiguration' => 'js/extjs/viewportConfiguration.js',
114 'util' => 'sysext/backend/Resources/Public/JavaScript/util.js'
115 );
116 if ($this->debug) {
117 unset($this->jsFiles['loginrefresh']);
118 }
119 // Add default BE css
120 $this->pageRenderer->addCssLibrary('contrib/normalize/normalize.css', 'stylesheet', 'all', '', TRUE, TRUE);
121 $this->css = '';
122 $this->cssFiles = array();
123 $this->toolbarItems = array();
124 $this->initializeCoreToolbarItems();
125 $this->menuWidth = $this->menuWidthDefault;
126 if (isset($GLOBALS['TBE_STYLES']['dims']['leftMenuFrameW']) && (int)$GLOBALS['TBE_STYLES']['dims']['leftMenuFrameW'] != (int)$this->menuWidth) {
127 $this->menuWidth = (int)$GLOBALS['TBE_STYLES']['dims']['leftMenuFrameW'];
128 }
129 $this->executeHook('constructPostProcess');
130 }
131
132 /**
133 * Initializes the core toolbar items
134 *
135 * @return void
136 */
137 protected function initializeCoreToolbarItems() {
138 $coreToolbarItems = array(
139 'shortcuts' => 'TYPO3\\CMS\\Backend\\Toolbar\\ShortcutToolbarItem',
140 'clearCacheActions' => 'TYPO3\\CMS\\Backend\\Toolbar\\ClearCacheToolbarItem',
141 'liveSearch' => 'TYPO3\\CMS\\Backend\\Toolbar\\LiveSearchToolbarItem'
142 );
143 foreach ($coreToolbarItems as $toolbarItemName => $toolbarItemClassName) {
144 $toolbarItem = GeneralUtility::makeInstance($toolbarItemClassName, $this);
145 if (!$toolbarItem instanceof \TYPO3\CMS\Backend\Toolbar\ToolbarItemHookInterface) {
146 throw new \UnexpectedValueException('$toolbarItem "' . $toolbarItemName . '" must implement interface TYPO3\\CMS\\Backend\\Toolbar\\ToolbarItemHookInterface', 1195126772);
147 }
148 if ($toolbarItem->checkAccess()) {
149 $this->toolbarItems[$toolbarItemName] = $toolbarItem;
150 } else {
151 unset($toolbarItem);
152 }
153 }
154 }
155
156 /**
157 * Main function generating the BE scaffolding
158 *
159 * @return void
160 */
161 public function render() {
162 $this->executeHook('renderPreProcess');
163
164 // Prepare the scaffolding, at this point extension may still add javascript and css
165 $logo = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\View\\LogoView');
166
167 // Create backend scaffolding
168 $backendScaffolding = '
169 <div class="navbar navbar-inverse x-hide-display" role="navigation" id="typo3-top-container">
170 <div class="container-fluid">
171 <div class="navbar-header" id="typo3-logo">' .
172 $logo->render() .
173 '</div>
174 <div id="typo3-top">
175 <ul class="nav navbar-nav navbar-right typo3-top-toolbar" id="typo3-toolbar">' .
176 $this->renderToolbar() .
177 '</ul>
178 </div>
179 </div>
180 </div>' .
181 $this->generateModuleMenu();
182
183 /******************************************************
184 * Now put the complete backend document together
185 ******************************************************/
186 foreach ($this->cssFiles as $cssFileName => $cssFile) {
187 $this->pageRenderer->addCssFile($cssFile);
188 // Load additional css files to overwrite existing core styles
189 if (!empty($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName])) {
190 $this->pageRenderer->addCssFile($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName]);
191 }
192 }
193 if (!empty($this->css)) {
194 $this->pageRenderer->addCssInlineBlock('BackendInlineCSS', $this->css);
195 }
196 foreach ($this->jsFiles as $jsFile) {
197 $this->pageRenderer->addJsFile($jsFile);
198 }
199 $this->generateJavascript();
200 $this->pageRenderer->addJsInlineCode('BackendInlineJavascript', $this->js, FALSE);
201 $this->loadResourcesForRegisteredNavigationComponents();
202
203 // Add state provider
204 $GLOBALS['TBE_TEMPLATE']->setExtDirectStateProvider();
205 $states = $GLOBALS['BE_USER']->uc['BackendComponents']['States'];
206 // Save states in BE_USER->uc
207 $extOnReadyCode = '
208 Ext.state.Manager.setProvider(new TYPO3.state.ExtDirectProvider({
209 key: "BackendComponents.States",
210 autoRead: false
211 }));
212 ';
213
214 if ($states) {
215 $extOnReadyCode .= 'Ext.state.Manager.getProvider().initState(' . json_encode($states) . ');';
216 }
217
218 $extOnReadyCode .= '
219 TYPO3.Backend = new TYPO3.Viewport(TYPO3.Viewport.configuration);
220 if (typeof console === "undefined") {
221 console = TYPO3.Backend.DebugConsole;
222 }
223 TYPO3.ContextHelpWindow.init(' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('help_cshmanual')) . ');';
224 $this->pageRenderer->addExtOnReadyCode($extOnReadyCode);
225 // Set document title:
226 $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ' [TYPO3 CMS ' . TYPO3_version . ']' : 'TYPO3 CMS ' . TYPO3_version;
227 $this->content = $backendScaffolding;
228 // Renders the module page
229 $this->content = $GLOBALS['TBE_TEMPLATE']->render($title, $this->content);
230 $hookConfiguration = array('content' => &$this->content);
231 $this->executeHook('renderPostProcess', $hookConfiguration);
232 echo $this->content;
233 }
234
235 /**
236 * Loads the css and javascript files of all registered navigation widgets
237 *
238 * @return void
239 */
240 protected function loadResourcesForRegisteredNavigationComponents() {
241 if (!is_array($GLOBALS['TBE_MODULES']['_navigationComponents'])) {
242 return;
243 }
244 $loadedComponents = array();
245 foreach ($GLOBALS['TBE_MODULES']['_navigationComponents'] as $module => $info) {
246 if (in_array($info['componentId'], $loadedComponents)) {
247 continue;
248 }
249 $loadedComponents[] = $info['componentId'];
250 $component = strtolower(substr($info['componentId'], strrpos($info['componentId'], '-') + 1));
251 $componentDirectory = 'components/' . $component . '/';
252 if ($info['isCoreComponent']) {
253 $absoluteComponentPath = PATH_site . 'typo3/js/extjs/' . $componentDirectory;
254 $relativeComponentPath = '../' . str_replace(PATH_site, '', $absoluteComponentPath);
255 } else {
256 $absoluteComponentPath = ExtensionManagementUtility::extPath($info['extKey']) . $componentDirectory;
257 $relativeComponentPath = ExtensionManagementUtility::extRelPath($info['extKey']) . $componentDirectory;
258 }
259 $cssFiles = GeneralUtility::getFilesInDir($absoluteComponentPath . 'css/', 'css');
260 if (file_exists($absoluteComponentPath . 'css/loadorder.txt')) {
261 // Don't allow inclusion outside directory
262 $loadOrder = str_replace('../', '', GeneralUtility::getUrl($absoluteComponentPath . 'css/loadorder.txt'));
263 $cssFilesOrdered = GeneralUtility::trimExplode(LF, $loadOrder, TRUE);
264 $cssFiles = array_merge($cssFilesOrdered, $cssFiles);
265 }
266 foreach ($cssFiles as $cssFile) {
267 $this->pageRenderer->addCssFile($relativeComponentPath . 'css/' . $cssFile);
268 }
269 $jsFiles = GeneralUtility::getFilesInDir($absoluteComponentPath . 'javascript/', 'js');
270 if (file_exists($absoluteComponentPath . 'javascript/loadorder.txt')) {
271 // Don't allow inclusion outside directory
272 $loadOrder = str_replace('../', '', GeneralUtility::getUrl($absoluteComponentPath . 'javascript/loadorder.txt'));
273 $jsFilesOrdered = GeneralUtility::trimExplode(LF, $loadOrder, TRUE);
274 $jsFiles = array_merge($jsFilesOrdered, $jsFiles);
275 }
276 foreach ($jsFiles as $jsFile) {
277 $this->pageRenderer->addJsFile($relativeComponentPath . 'javascript/' . $jsFile);
278 }
279 $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', BackendUtility::getModuleUrl('record_history'));
280 }
281 }
282
283 /**
284 * Renders the items in the top toolbar
285 *
286 * @return string top toolbar elements as HTML
287 */
288 protected function renderToolbar() {
289 // Move search to last position
290 if (array_key_exists('liveSearch', $this->toolbarItems)) {
291 $search = $this->toolbarItems['liveSearch'];
292 unset($this->toolbarItems['liveSearch']);
293 $this->toolbarItems['liveSearch'] = $search;
294 }
295 $toolbar = $this->getLoggedInUserLabel();
296 $toolbar .= '<li class="toolbar-item" id="logout-button">' . $this->renderLogoutButton() . '</li>';
297
298 $i = 0;
299 $numberOfToolbarItems = count($this->toolbarItems);
300 foreach ($this->toolbarItems as $key => $toolbarItem) {
301 $i++;
302 $menu = $toolbarItem->render();
303 if ($menu) {
304 $additionalAttributes = trim($toolbarItem->getAdditionalAttributes());
305 if ($numberOfToolbarItems > 1 && $i == $numberOfToolbarItems - 1) {
306 if (strpos($additionalAttributes, 'class="') !== FALSE) {
307 $additionalAttributes = str_replace('class="', 'class="separator ', $additionalAttributes);
308 } else {
309 $additionalAttributes .= ' class="separator"';
310 }
311 }
312 if ($additionalAttributes !== '') {
313 $additionalAttributes = ' ' . $additionalAttributes;
314 }
315 $toolbar .= '<li' . $additionalAttributes . ' role="menu">' . $menu . '</li>';
316 }
317 }
318 return $toolbar;
319 }
320
321 /**
322 * Gets the label of the BE user currently logged in
323 *
324 * @return string Html code snippet displaying the currently logged in user
325 */
326 protected function getLoggedInUserLabel() {
327 $css = '';
328 $icon = \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('status-user-' . ($GLOBALS['BE_USER']->isAdmin() ? 'admin' : 'backend'));
329 $realName = $GLOBALS['BE_USER']->user['realName'];
330 $username = $GLOBALS['BE_USER']->user['username'];
331 $label = $realName ?: $username;
332 $title = $username;
333
334 // Link to user setup if it's loaded and user has access
335 $link = '';
336 if (ExtensionManagementUtility::isLoaded('setup') && $GLOBALS['BE_USER']->check('modules', 'user_setup')) {
337 $link = '<a href="#" onclick="top.goToModule(\'user_setup\'); this.blur(); return false;">';
338 } else {
339 $link = '<a name="username">';
340 }
341
342 // Superuser mode
343 if ($GLOBALS['BE_USER']->user['ses_backuserid']) {
344 $css .= ' su-user';
345 $title = $GLOBALS['LANG']->getLL('switchtouser') . ': ' . $username;
346 $label = $GLOBALS['LANG']->getLL('switchtousershort') . ' ' . ($realName ? $realName . ' (' . $username . ')' : $username);
347 }
348
349 return '<li id="username" class="' . $css . '">' .
350 $link . $icon . '<span title="' . htmlspecialchars($title) . '">' . htmlspecialchars($label) . '</span></a>' .
351 '</li>';
352 }
353
354 /**
355 * Returns the file name to the LLL JavaScript, containing the localized labels,
356 * which can be used in JavaScript code.
357 *
358 * @return string File name of the JS file, relative to TYPO3_mainDir
359 */
360 protected function getLocalLangFileName() {
361 $code = $this->generateLocalLang();
362 $filePath = 'typo3temp/locallang-BE-' . sha1($code) . '.js';
363 if (!file_exists((PATH_site . $filePath))) {
364 // writeFileToTypo3tempDir() returns NULL on success (please double-read!)
365 if (GeneralUtility::writeFileToTypo3tempDir(PATH_site . $filePath, $code) !== NULL) {
366 throw new \RuntimeException('LocalLangFile could not be written to ' . $filePath, 1295193026);
367 }
368 }
369 return '../' . $filePath;
370 }
371
372 /**
373 * Reads labels required in JavaScript code from the localization system and returns them as JSON
374 * array in TYPO3.LLL.
375 *
376 * @return string JavaScript code containing the LLL labels in TYPO3.LLL
377 */
378 protected function generateLocalLang() {
379 $coreLabels = array(
380 'waitTitle' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_logging_in'),
381 'refresh_login_failed' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed'),
382 'refresh_login_failed_message' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed_message'),
383 'refresh_login_title' => sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_title'), htmlspecialchars($GLOBALS['BE_USER']->user['username'])),
384 'login_expired' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_expired'),
385 'refresh_login_username' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_username'),
386 'refresh_login_password' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_password'),
387 'refresh_login_emptyPassword' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_emptyPassword'),
388 'refresh_login_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_button'),
389 'refresh_logout_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_logout_button'),
390 'please_wait' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.please_wait'),
391 'loadingIndicator' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:loadingIndicator'),
392 'be_locked' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.be_locked'),
393 'refresh_login_countdown_singular' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown_singular'),
394 'refresh_login_countdown' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown'),
395 'login_about_to_expire' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_about_to_expire'),
396 'login_about_to_expire_title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_about_to_expire_title'),
397 'refresh_login_refresh_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_refresh_button'),
398 'refresh_direct_logout_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_direct_logout_button'),
399 'tabs_closeAll' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeAll'),
400 'tabs_closeOther' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeOther'),
401 'tabs_close' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.close'),
402 'tabs_openInBrowserWindow' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.openInBrowserWindow'),
403 'csh_tooltip_loading' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:csh_tooltip_loading')
404 );
405 $labels = array(
406 'fileUpload' => array(
407 'windowTitle',
408 'buttonSelectFiles',
409 'buttonCancelAll',
410 'infoComponentMaxFileSize',
411 'infoComponentFileUploadLimit',
412 'infoComponentFileTypeLimit',
413 'infoComponentOverrideFiles',
414 'processRunning',
415 'uploadWait',
416 'uploadStarting',
417 'uploadProgress',
418 'uploadSuccess',
419 'errorQueueLimitExceeded',
420 'errorQueueFileSizeLimit',
421 'errorQueueZeroByteFile',
422 'errorQueueInvalidFiletype',
423 'errorUploadHttp',
424 'errorUploadMissingUrl',
425 'errorUploadIO',
426 'errorUploadSecurityError',
427 'errorUploadLimit',
428 'errorUploadFailed',
429 'errorUploadFileIDNotFound',
430 'errorUploadFileValidation',
431 'errorUploadFileCancelled',
432 'errorUploadStopped',
433 'allErrorMessageTitle',
434 'allErrorMessageText',
435 'allError401',
436 'allError2038'
437 ),
438 'liveSearch' => array(
439 'title',
440 'helpTitle',
441 'emptyText',
442 'loadingText',
443 'listEmptyText',
444 'showAllResults',
445 'helpDescription',
446 'helpDescriptionPages',
447 'helpDescriptionContent'
448 ),
449 'viewPort' => array(
450 'tooltipModuleMenuSplit',
451 'tooltipNavigationContainerSplitDrag',
452 'tooltipDebugPanelSplitDrag'
453 )
454 );
455 $generatedLabels = array();
456 $generatedLabels['core'] = $coreLabels;
457 // First loop over all categories (fileUpload, liveSearch, ..)
458 foreach ($labels as $categoryName => $categoryLabels) {
459 // Then loop over every single label
460 foreach ($categoryLabels as $label) {
461 // LLL identifier must be called $categoryName_$label, e.g. liveSearch_loadingText
462 $generatedLabels[$categoryName][$label] = $GLOBALS['LANG']->getLL($categoryName . '_' . $label);
463 }
464 }
465 return 'TYPO3.LLL = ' . json_encode($generatedLabels) . ';';
466 }
467
468 /**
469 * Generates the JavaScript code for the backend.
470 *
471 * @return void
472 */
473 protected function generateJavascript() {
474 $pathTYPO3 = GeneralUtility::dirname(GeneralUtility::getIndpEnv('SCRIPT_NAME')) . '/';
475 // If another page module was specified, replace the default Page module with the new one
476 $newPageModule = trim($GLOBALS['BE_USER']->getTSConfigVal('options.overridePageModule'));
477 $pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
478 if (!$GLOBALS['BE_USER']->check('modules', $pageModule)) {
479 $pageModule = '';
480 }
481 $menuFrameName = 'menu';
482 if ($GLOBALS['BE_USER']->uc['noMenuMode'] === 'icons') {
483 $menuFrameName = 'topmenuFrame';
484 }
485 // Determine security level from conf vars and default to super challenged
486 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel']) {
487 $this->loginSecurityLevel = $GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel'];
488 } else {
489 $this->loginSecurityLevel = 'superchallenged';
490 }
491 $t3Configuration = array(
492 'siteUrl' => GeneralUtility::getIndpEnv('TYPO3_SITE_URL'),
493 'PATH_typo3' => $pathTYPO3,
494 'PATH_typo3_enc' => rawurlencode($pathTYPO3),
495 'username' => htmlspecialchars($GLOBALS['BE_USER']->user['username']),
496 'uniqueID' => GeneralUtility::shortMD5(uniqid('', TRUE)),
497 'securityLevel' => $this->loginSecurityLevel,
498 'TYPO3_mainDir' => TYPO3_mainDir,
499 'pageModule' => $pageModule,
500 'inWorkspace' => $GLOBALS['BE_USER']->workspace !== 0 ? 1 : 0,
501 'workspaceFrontendPreviewEnabled' => $GLOBALS['BE_USER']->user['workspace_preview'] ? 1 : 0,
502 'veriCode' => $GLOBALS['BE_USER']->veriCode(),
503 'denyFileTypes' => PHP_EXTENSIONS_DEFAULT,
504 'moduleMenuWidth' => $this->menuWidth - 1,
505 'topBarHeight' => isset($GLOBALS['TBE_STYLES']['dims']['topFrameH']) ? (int)$GLOBALS['TBE_STYLES']['dims']['topFrameH'] : 30,
506 'showRefreshLoginPopup' => isset($GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup']) ? (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup'] : FALSE,
507 'listModulePath' => ExtensionManagementUtility::extRelPath('recordlist') . 'mod1/',
508 'debugInWindow' => $GLOBALS['BE_USER']->uc['debugInWindow'] ? 1 : 0,
509 'ContextHelpWindows' => array(
510 'width' => 600,
511 'height' => 400
512 ),
513 );
514 $this->js .= '
515 TYPO3.configuration = ' . json_encode($t3Configuration) . ';
516
517 /**
518 * TypoSetup object.
519 */
520 function typoSetup() { //
521 this.PATH_typo3 = TYPO3.configuration.PATH_typo3;
522 this.PATH_typo3_enc = TYPO3.configuration.PATH_typo3_enc;
523 this.username = TYPO3.configuration.username;
524 this.uniqueID = TYPO3.configuration.uniqueID;
525 this.navFrameWidth = 0;
526 this.securityLevel = TYPO3.configuration.securityLevel;
527 this.veriCode = TYPO3.configuration.veriCode;
528 this.denyFileTypes = TYPO3.configuration.denyFileTypes;
529 }
530 var TS = new typoSetup();
531 //backwards compatibility
532 /**
533 * Frameset Module object
534 *
535 * Used in main modules with a frameset for submodules to keep the ID between modules
536 * Typically that is set by something like this in a Web>* sub module:
537 * if (top.fsMod) top.fsMod.recentIds["web"] = "\'.(int)$this->id.\'";
538 * if (top.fsMod) top.fsMod.recentIds["file"] = "...(file reference/string)...";
539 */
540 function fsModules() { //
541 this.recentIds=new Array(); // used by frameset modules to track the most recent used id for list frame.
542 this.navFrameHighlightedID=new Array(); // used by navigation frames to track which row id was highlighted last time
543 this.currentMainLoaded="";
544 this.currentBank="0";
545 }
546 var fsMod = new fsModules();
547
548 top.goToModule = function(modName, cMR_flag, addGetVars) {
549 TYPO3.ModuleMenu.App.showModule(modName, addGetVars);
550 }
551 ' . $this->setStartupModule();
552 // Check editing of page:
553 $this->handlePageEditing();
554 }
555
556 /**
557 * Checking if the "&edit" variable was sent so we can open it for editing the page.
558 *
559 * @return void
560 */
561 protected function handlePageEditing() {
562 // EDIT page:
563 $editId = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('edit'));
564 $editRecord = '';
565 if ($editId) {
566 // Looking up the page to edit, checking permissions:
567 $where = ' AND (' . $GLOBALS['BE_USER']->getPagePermsClause(2) . ' OR ' . $GLOBALS['BE_USER']->getPagePermsClause(16) . ')';
568 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($editId)) {
569 $editRecord = BackendUtility::getRecordWSOL('pages', $editId, '*', $where);
570 } else {
571 $records = BackendUtility::getRecordsByField('pages', 'alias', $editId, $where);
572 if (is_array($records)) {
573 $editRecord = reset($records);
574 BackendUtility::workspaceOL('pages', $editRecord);
575 }
576 }
577 // If the page was accessible, then let the user edit it.
578 if (is_array($editRecord) && $GLOBALS['BE_USER']->isInWebMount($editRecord['uid'])) {
579 // Setting JS code to open editing:
580 $this->js .= '
581 // Load page to edit:
582 window.setTimeout("top.loadEditId(' . (int)$editRecord['uid'] . ');", 500);
583 ';
584 // Checking page edit parameter:
585 if (!$GLOBALS['BE_USER']->getTSConfigVal('options.bookmark_onEditId_dontSetPageTree')) {
586 $bookmarkKeepExpanded = $GLOBALS['BE_USER']->getTSConfigVal('options.bookmark_onEditId_keepExistingExpanded');
587 // Expanding page tree:
588 BackendUtility::openPageTree((int)$editRecord['pid'], !$bookmarkKeepExpanded);
589 }
590 } else {
591 $this->js .= '
592 // Warning about page editing:
593 alert(' . GeneralUtility::quoteJSvalue(sprintf($GLOBALS['LANG']->getLL('noEditPage'), $editId)) . ');
594 ';
595 }
596 }
597 }
598
599 /**
600 * Sets the startup module from either GETvars module and modParams or user configuration.
601 *
602 * @return string the JavaScript code for the startup module
603 */
604 protected function setStartupModule() {
605 $startModule = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('module'));
606 if (!$startModule) {
607 // start module on first login, will be removed once used the first time
608 if (isset($GLOBALS['BE_USER']->uc['startModuleOnFirstLogin'])) {
609 $startModule = $GLOBALS['BE_USER']->uc['startModuleOnFirstLogin'];
610 unset($GLOBALS['BE_USER']->uc['startModuleOnFirstLogin']);
611 $GLOBALS['BE_USER']->writeUC();
612 } elseif ($GLOBALS['BE_USER']->uc['startModule']) {
613 $startModule = $GLOBALS['BE_USER']->uc['startModule'];
614 } elseif ($GLOBALS['BE_USER']->uc['startInTaskCenter']) {
615 $startModule = 'user_task';
616 }
617
618 // check if the start module has additional parameters, so a redirect to a specific
619 // action is possible
620 if (strpos($startModule, '->') !== FALSE) {
621 list($startModule, $startModuleParameters) = explode('->', $startModule, 2);
622 }
623 }
624
625 $moduleParameters = GeneralUtility::_GET('modParams');
626 // if no GET parameters are set, check if there are parameters given from the UC
627 if (!$moduleParameters && $startModuleParameters) {
628 $moduleParameters = $startModuleParameters;
629 }
630
631 if ($startModule) {
632 return '
633 // start in module:
634 top.startInModule = [\'' . $startModule . '\', ' . GeneralUtility::quoteJSvalue($moduleParameters) . '];
635 ';
636 } else {
637 return '';
638 }
639 }
640
641 /**
642 * Adds a javascript snippet to the backend
643 *
644 * @param string $javascript Javascript snippet
645 * @return void
646 * @throws \InvalidArgumentException
647 */
648 public function addJavascript($javascript) {
649 // TODO do we need more checks?
650 if (!is_string($javascript)) {
651 throw new \InvalidArgumentException('parameter $javascript must be of type string', 1195129553);
652 }
653 $this->js .= $javascript;
654 }
655
656 /**
657 * Adds a javscript file to the backend after it has been checked that it exists
658 *
659 * @param string $javascriptFile Javascript file reference
660 * @return boolean TRUE if the javascript file was successfully added, FALSE otherwise
661 */
662 public function addJavascriptFile($javascriptFile) {
663 $jsFileAdded = FALSE;
664 //TODO add more checks if necessary
665 if (file_exists(GeneralUtility::resolveBackPath(PATH_typo3 . $javascriptFile))) {
666 $this->jsFiles[] = $javascriptFile;
667 $jsFileAdded = TRUE;
668 }
669 return $jsFileAdded;
670 }
671
672 /**
673 * Adds a css snippet to the backend
674 *
675 * @param string $css Css snippet
676 * @return void
677 */
678 public function addCss($css) {
679 if (!is_string($css)) {
680 throw new \InvalidArgumentException('parameter $css must be of type string', 1195129642);
681 }
682 $this->css .= $css;
683 }
684
685 /**
686 * Adds a css file to the backend after it has been checked that it exists
687 *
688 * @param string $cssFileName The css file's name with out the .css ending
689 * @param string $cssFile Css file reference
690 * @return boolean TRUE if the css file was added, FALSE otherwise
691 */
692 public function addCssFile($cssFileName, $cssFile) {
693 $cssFileAdded = FALSE;
694 if (empty($this->cssFiles[$cssFileName])) {
695 $this->cssFiles[$cssFileName] = $cssFile;
696 $cssFileAdded = TRUE;
697 }
698 return $cssFileAdded;
699 }
700
701 /**
702 * Adds an item to the toolbar, the class file for the toolbar item must be loaded at this point
703 *
704 * @param string $toolbarItemName Toolbar item name, f.e. tx_toolbarExtension_coolItem
705 * @param string $toolbarItemClassName Toolbar item class name, f.e. tx_toolbarExtension_coolItem
706 * @return void
707 * @throws \UnexpectedValueException
708 */
709 public function addToolbarItem($toolbarItemName, $toolbarItemClassName) {
710 $toolbarItem = GeneralUtility::makeInstance($toolbarItemClassName, $this);
711 if (!$toolbarItem instanceof \TYPO3\CMS\Backend\Toolbar\ToolbarItemHookInterface) {
712 throw new \UnexpectedValueException('$toolbarItem "' . $toolbarItemName . '" must implement interface TYPO3\\CMS\\Backend\\Toolbar\\ToolbarItemHookInterface', 1195125501);
713 }
714 if ($toolbarItem->checkAccess()) {
715 $this->toolbarItems[$toolbarItemName] = $toolbarItem;
716 } else {
717 unset($toolbarItem);
718 }
719 }
720
721 /**
722 * Executes defined hooks functions for the given identifier.
723 *
724 * These hook identifiers are valid:
725 * + constructPostProcess
726 * + renderPreProcess
727 * + renderPostProcess
728 *
729 * @param string $identifier Specific hook identifier
730 * @param array $hookConfiguration Additional configuration passed to hook functions
731 * @return void
732 */
733 protected function executeHook($identifier, array $hookConfiguration = array()) {
734 $options = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/backend.php'];
735 if (isset($options[$identifier]) && is_array($options[$identifier])) {
736 foreach ($options[$identifier] as $hookFunction) {
737 GeneralUtility::callUserFunction($hookFunction, $hookConfiguration, $this);
738 }
739 }
740 }
741
742 /**
743 * renders the logout button form
744 *
745 * @return string Html code snippet displaying the logout button
746 */
747 protected function renderLogoutButton() {
748 // show logout or "exit" (from switch user mode) label
749 $buttonLabel = 'LLL:EXT:lang/locallang_core.xlf:' . ($GLOBALS['BE_USER']->user['ses_backuserid'] ? 'buttons.exit' : 'buttons.logout');
750 $buttonForm = '
751 <form action="logout.php" target="_top">
752 <input type="submit" id="logout-submit-button" value="' . $GLOBALS['LANG']->sL($buttonLabel, TRUE) . '" />
753 </form>';
754 return $buttonForm;
755 }
756
757 /**
758 * loads all modules from the repository
759 * and renders it with a template
760 *
761 * @return string
762 */
763 protected function generateModuleMenu() {
764 $moduleStorage = $this->backendModuleRepository->loadAllowedModules();
765
766 /** @var $view \TYPO3\CMS\Fluid\View\StandaloneView */
767 $view = GeneralUtility::makeInstance('TYPO3\\CMS\\Fluid\\View\\StandaloneView');
768 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/ModuleMenu/Main.html'));
769 $view->assign('modules', $moduleStorage);
770 return $view->render();
771 }
772
773 /**
774 * Returns the Module menu for the AJAX API
775 *
776 * @param array $params
777 * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler
778 * @return void
779 */
780 public function getModuleMenuForReload($params, $ajaxRequestHandler) {
781 $content = $this->generateModuleMenu();
782 $ajaxRequestHandler->addContent('menu', $content);
783 $ajaxRequestHandler->setContentFormat('json');
784 }
785 }