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