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