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