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