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