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