TYPO3 CMS  TYPO3_8-7
BackendController.php
Go to the documentation of this file.
1 <?php
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 
35 
40 {
44  protected $content = '';
45 
49  protected $css = '';
50 
54  protected $cssFiles = [];
55 
59  protected $js = '';
60 
64  protected $jsFiles = [];
65 
69  protected $toolbarItems = [];
70 
74  protected $debug;
75 
79  protected $templatePath = 'EXT:backend/Resources/Private/Templates/';
80 
84  protected $partialPath = 'EXT:backend/Resources/Private/Partials/';
85 
90 
94  protected $moduleLoader;
95 
99  protected $pageRenderer;
100 
104  protected $iconFactory;
105 
109  public function __construct()
110  {
111  $this->getLanguageService()->includeLLFile('EXT:lang/Resources/Private/Language/locallang_misc.xlf');
112  $this->backendModuleRepository = GeneralUtility::makeInstance(BackendModuleRepository::class);
113  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
114 
115  // Set debug flag for BE development only
116  $this->debug = (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] === 1;
117  // Initializes the backend modules structure for use later.
118  $this->moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
119  $this->moduleLoader->load($GLOBALS['TBE_MODULES']);
120  $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
121  $this->pageRenderer->loadExtJS();
122  // included for the module menu JavaScript, please note that this is subject to change
123  $this->pageRenderer->loadJquery();
124  $this->pageRenderer->addExtDirectCode();
125  // Add default BE javascript
126  $this->jsFiles = [
127  'locallang' => $this->getLocalLangFileName(),
128  'md5' => 'EXT:backend/Resources/Public/JavaScript/md5.js',
129  'evalfield' => 'EXT:backend/Resources/Public/JavaScript/jsfunc.evalfield.js',
130  'backend' => 'EXT:backend/Resources/Public/JavaScript/backend.js',
131  ];
132  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LoginRefresh', 'function(LoginRefresh) {
133  LoginRefresh.setIntervalTime(' . MathUtility::forceIntegerInRange((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['sessionTimeout'] - 60, 60) . ');
134  LoginRefresh.setLoginFramesetUrl(' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('login_frameset')) . ');
135  LoginRefresh.setLogoutUrl(' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('logout')) . ');
136  LoginRefresh.initialize();
137  }');
138 
139  // load module menu
140  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ModuleMenu');
141 
142  // load Toolbar class
143  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Toolbar');
144 
145  // load Utility class
146  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Utility');
147 
148  // load Notification functionality
149  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Notification');
150 
151  // load Modals
152  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
153 
154  // load ContextMenu
155  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
156 
157  // load the storage API and fill the UC into the PersistentStorage, so no additional AJAX call is needed
158  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Storage', 'function(Storage) {
159  Storage.Persistent.load(' . json_encode($this->getBackendUser()->uc) . ');
160  }');
161 
162  // load debug console
163  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DebugConsole');
164 
165  $this->pageRenderer->addInlineSetting('ShowItem', 'moduleUrl', BackendUtility::getModuleUrl('show_item'));
166 
167  $this->pageRenderer->addInlineLanguageLabelFile('EXT:lang/Resources/Private/Language/locallang_mod_web_list.xlf');
168 
169  $this->css = '';
170 
171  $this->initializeToolbarItems();
172  $this->executeHook('constructPostProcess');
173  $this->includeLegacyBackendItems();
174  }
175 
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  GeneralUtility::deprecationLog('The hook $TYPO3_CONF_VARS["typo3/backend.php"]["additionalBackendItems"] is deprecated in TYPO3 v8, and will be removed in TYPO3 v9. Use the "constructPostProcess" hook within BackendController instead.');
188  foreach ($GLOBALS['TYPO3_CONF_VARS']['typo3/backend.php']['additionalBackendItems'] as $additionalBackendItem) {
189  include_once $additionalBackendItem;
190  }
191  }
192 
193  // Process ExtJS module js and css
194  if (is_array($GLOBALS['TBE_MODULES']['_configuration'])) {
195  foreach ($GLOBALS['TBE_MODULES']['_configuration'] as $moduleName => $moduleConfig) {
196  if (is_array($moduleConfig['cssFiles'])) {
197  foreach ($moduleConfig['cssFiles'] as $cssFileName => $cssFile) {
198  $cssFile = GeneralUtility::getFileAbsFileName($cssFile);
199  $cssFile = PathUtility::getAbsoluteWebPath($cssFile);
200  $TYPO3backend->addCssFile($cssFileName, $cssFile);
201  }
202  }
203  if (is_array($moduleConfig['jsFiles'])) {
204  foreach ($moduleConfig['jsFiles'] as $jsFile) {
205  $jsFile = GeneralUtility::getFileAbsFileName($jsFile);
206  $jsFile = PathUtility::getAbsoluteWebPath($jsFile);
207  $TYPO3backend->addJavascriptFile($jsFile);
208  }
209  }
210  }
211  }
212  }
213 
219  protected function initializeToolbarItems()
220  {
221  $toolbarItemInstances = [];
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 
257  public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
258  {
259  $this->render();
260  $response->getBody()->write($this->content);
261  return $response;
262  }
263 
267  public function render()
268  {
269  $this->executeHook('renderPreProcess');
270 
271  // Prepare the scaffolding, at this point extension may still add javascript and css
272  $view = $this->getFluidTemplateObject($this->templatePath . 'Backend/Main.html');
273 
274  $view->assign('moduleMenu', $this->generateModuleMenu());
275  $view->assign('topbar', $this->renderTopbar());
276 
277  /******************************************************
278  * Now put the complete backend document together
279  ******************************************************/
280  foreach ($this->cssFiles as $cssFileName => $cssFile) {
281  $this->pageRenderer->addCssFile($cssFile);
282  // Load additional css files to overwrite existing core styles
283  if (!empty($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName])) {
284  $this->pageRenderer->addCssFile($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName]);
285  }
286  }
287  if (!empty($this->css)) {
288  $this->pageRenderer->addCssInlineBlock('BackendInlineCSS', $this->css);
289  }
290  foreach ($this->jsFiles as $jsFile) {
291  $this->pageRenderer->addJsFile($jsFile);
292  }
293  $this->generateJavascript();
294  $this->pageRenderer->addJsInlineCode('BackendInlineJavascript', $this->js, false);
296  // @todo: remove this when ExtJS is removed
297  $states = $this->getBackendUser()->uc['BackendComponents']['States'];
298  $this->pageRenderer->addExtOnReadyCode('
299  var TYPO3ExtJSStateProviderBridge = function() {};
300  Ext.extend(TYPO3ExtJSStateProviderBridge, Ext.state.Provider, {
301  state: {},
302  queue: [],
303  dirty: false,
304  prefix: "BackendComponents.States.",
305  initState: function(state) {
306  if (Ext.isArray(state)) {
307  Ext.each(state, function(item) {
308  this.state[item.name] = item.value;
309  }, this);
310  } else if (Ext.isObject(state)) {
311  Ext.iterate(state, function(key, value) {
312  this.state[key] = value;
313  }, this);
314  } else {
315  this.state = {};
316  }
317  var me = this;
318  window.setInterval(function() {
319  me.submitState(me)
320  }, 750);
321  },
322  get: function(name, defaultValue) {
323  return TYPO3.Storage.Persistent.isset(this.prefix + name) ? TYPO3.Storage.Persistent.get(this.prefix + name) : defaultValue;
324  },
325  clear: function(name) {
326  TYPO3.Storage.Persistent.unset(this.prefix + name);
327  },
328  set: function(name, value) {
329  if (!name) {
330  return;
331  }
332  this.queueChange(name, value);
333  },
334  queueChange: function(name, value) {
335  var o = {};
336  var i;
337  var found = false;
338 
339  var lastValue = this.state[name];
340  for (i = 0; i < this.queue.length; i++) {
341  if (this.queue[i].name === name) {
342  lastValue = this.queue[i].value;
343  }
344  }
345  var changed = undefined === lastValue || lastValue !== value;
346 
347  if (changed) {
348  o.name = name;
349  o.value = value;
350  for (i = 0; i < this.queue.length; i++) {
351  if (this.queue[i].name === o.name) {
352  this.queue[i] = o;
353  found = true;
354  }
355  }
356  if (false === found) {
357  this.queue.push(o);
358  }
359  this.dirty = true;
360  }
361  },
362  submitState: function(context) {
363  if (!context.dirty) {
364  return;
365  }
366  for (var i = 0; i < context.queue.length; ++i) {
367  TYPO3.Storage.Persistent.set(context.prefix + context.queue[i].name, context.queue[i].value).done(function() {
368  if (!context.dirty) {
369  context.queue = [];
370  }
371  });
372  }
373  context.dirty = false;
374  }
375  });
376  Ext.state.Manager.setProvider(new TYPO3ExtJSStateProviderBridge());
377  Ext.state.Manager.getProvider().initState(' . (!empty($states) ? json_encode($states) : []) . ');
378  ');
379  // Set document title:
380  $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ' [TYPO3 CMS ' . TYPO3_version . ']' : 'TYPO3 CMS ' . TYPO3_version;
381  // Renders the module page
382  $this->content = $this->getDocumentTemplate()->render($title, $view->render());
383  $hookConfiguration = ['content' => &$this->content];
384  $this->executeHook('renderPostProcess', $hookConfiguration);
385  }
386 
392  protected function renderTopbar()
393  {
394  $view = $this->getFluidTemplateObject($this->partialPath . 'Backend/Topbar.html');
395 
396  // Extension Configuration to find the TYPO3 logo in the left corner
397  $extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['backend'], ['allowed_classes' => false]);
398  $logoPath = '';
399  if (!empty($extConf['backendLogo'])) {
400  $customBackendLogo = GeneralUtility::getFileAbsFileName($extConf['backendLogo']);
401  if (!empty($customBackendLogo)) {
402  $logoPath = $customBackendLogo;
403  }
404  }
405  // if no custom logo was set or the path is invalid, use the original one
406  if (empty($logoPath) || !file_exists($logoPath)) {
407  $logoPath = GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Public/Images/typo3_logo_orange.svg');
408  $logoWidth = 22;
409  $logoHeight = 22;
410  } else {
411  // set width/height for custom logo
412  $imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $logoPath);
413  $logoWidth = $imageInfo->getWidth() ?? '22';
414  $logoHeight = $imageInfo->getHeight() ?? '22';
415 
416  // High-resolution?
417  if (strpos($logoPath, '@2x.') !== false) {
418  $logoWidth /= 2;
419  $logoHeight /= 2;
420  }
421  }
422 
423  $view->assign('logoUrl', PathUtility::getAbsoluteWebPath($logoPath));
424  $view->assign('logoWidth', $logoWidth);
425  $view->assign('logoHeight', $logoHeight);
426  $view->assign('applicationVersion', TYPO3_version);
427  $view->assign('siteName', $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']);
428  $view->assign('toolbar', $this->renderToolbar());
429 
430  return $view->render();
431  }
432 
437  {
438  if (!is_array($GLOBALS['TBE_MODULES']['_navigationComponents'])) {
439  return;
440  }
441  $loadedComponents = [];
442  foreach ($GLOBALS['TBE_MODULES']['_navigationComponents'] as $module => $info) {
443  if (in_array($info['componentId'], $loadedComponents)) {
444  continue;
445  }
446  $loadedComponents[] = $info['componentId'];
447  $component = strtolower(substr($info['componentId'], strrpos($info['componentId'], '-') + 1));
448  $componentDirectory = 'components/' . $component . '/';
449  if ($info['isCoreComponent']) {
450  $componentDirectory = 'Resources/Public/JavaScript/extjs/' . $componentDirectory;
451  $info['extKey'] = 'backend';
452  }
453  $absoluteComponentPath = ExtensionManagementUtility::extPath($info['extKey']) . $componentDirectory;
454  $relativeComponentPath = PathUtility::getRelativePath(PATH_site . TYPO3_mainDir, $absoluteComponentPath);
455  $cssFiles = GeneralUtility::getFilesInDir($absoluteComponentPath . 'css/', 'css');
456  if (file_exists($absoluteComponentPath . 'css/loadorder.txt')) {
457  // Don't allow inclusion outside directory
458  $loadOrder = str_replace('../', '', file_get_contents($absoluteComponentPath . 'css/loadorder.txt'));
459  $cssFilesOrdered = GeneralUtility::trimExplode(LF, $loadOrder, true);
460  $cssFiles = array_merge($cssFilesOrdered, $cssFiles);
461  }
462  foreach ($cssFiles as $cssFile) {
463  $this->pageRenderer->addCssFile($relativeComponentPath . 'css/' . $cssFile);
464  }
465  $jsFiles = GeneralUtility::getFilesInDir($absoluteComponentPath . 'javascript/', 'js');
466  if (file_exists($absoluteComponentPath . 'javascript/loadorder.txt')) {
467  // Don't allow inclusion outside directory
468  $loadOrder = str_replace('../', '', file_get_contents($absoluteComponentPath . 'javascript/loadorder.txt'));
469  $jsFilesOrdered = GeneralUtility::trimExplode(LF, $loadOrder, true);
470  $jsFiles = array_merge($jsFilesOrdered, $jsFiles);
471  }
472  foreach ($jsFiles as $jsFile) {
473  $this->pageRenderer->addJsFile($relativeComponentPath . 'javascript/' . $jsFile);
474  }
475  $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', BackendUtility::getModuleUrl('record_history'));
476  $this->pageRenderer->addInlineSetting('NewRecord', 'moduleUrl', BackendUtility::getModuleUrl('db_new'));
477  $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', BackendUtility::getModuleUrl('record_edit'));
478  $this->pageRenderer->addInlineSetting('RecordCommit', 'moduleUrl', BackendUtility::getModuleUrl('tce_db'));
479  $this->pageRenderer->addInlineSetting('WebLayout', 'moduleUrl', BackendUtility::getModuleUrl('web_layout'));
480  }
481  }
482 
488  protected function renderToolbar()
489  {
490  $toolbar = [];
491  foreach ($this->toolbarItems as $toolbarItem) {
493  if ($toolbarItem->checkAccess()) {
494  $hasDropDown = (bool)$toolbarItem->hasDropDown();
495  $additionalAttributes = (array)$toolbarItem->getAdditionalAttributes();
496 
497  $liAttributes = [];
498 
499  // Merge class: Add dropdown class if hasDropDown, add classes from additional attributes
500  $classes = [];
501  $classes[] = 'toolbar-item';
502  $classes[] = 't3js-toolbar-item';
503  if (isset($additionalAttributes['class'])) {
504  $classes[] = $additionalAttributes['class'];
505  unset($additionalAttributes['class']);
506  }
507  $liAttributes['class'] = implode(' ', $classes);
508 
509  // Add further attributes
510  foreach ($additionalAttributes as $name => $value) {
511  $liAttributes[$name] = $value;
512  }
513 
514  // Create a unique id from class name
515  $fullyQualifiedClassName = \get_class($toolbarItem);
516  $className = GeneralUtility::underscoredToLowerCamelCase($fullyQualifiedClassName);
517  $className = GeneralUtility::camelCaseToLowerCaseUnderscored($className);
518  $className = str_replace(['_', '\\'], '-', $className);
519  $liAttributes['id'] = $className;
520 
521  // Create data attribute identifier
522  $shortName = substr($fullyQualifiedClassName, strrpos($fullyQualifiedClassName, '\\') + 1);
523  $dataToolbarIdentifier = GeneralUtility::camelCaseToLowerCaseUnderscored($shortName);
524  $dataToolbarIdentifier = str_replace('_', '-', $dataToolbarIdentifier);
525  $liAttributes['data-toolbar-identifier'] = $dataToolbarIdentifier;
526 
527  $toolbar[] = '<li ' . GeneralUtility::implodeAttributes($liAttributes, true) . '>';
528 
529  if ($hasDropDown) {
530  $toolbar[] = '<a href="#" class="toolbar-item-link dropdown-toggle" data-toggle="dropdown">';
531  $toolbar[] = $toolbarItem->getItem();
532  $toolbar[] = '</a>';
533  $toolbar[] = '<div class="dropdown-menu" role="menu">';
534  $toolbar[] = $toolbarItem->getDropDown();
535  $toolbar[] = '</div>';
536  } else {
537  $toolbar[] = $toolbarItem->getItem();
538  }
539  $toolbar[] = '</li>';
540  }
541  }
542  return implode(LF, $toolbar);
543  }
544 
552  protected function getLocalLangFileName()
553  {
554  $code = $this->generateLocalLang();
555  $filePath = 'typo3temp/assets/js/backend-' . sha1($code) . '.js';
556  if (!file_exists(PATH_site . $filePath)) {
557  // writeFileToTypo3tempDir() returns NULL on success (please double-read!)
558  $error = GeneralUtility::writeFileToTypo3tempDir(PATH_site . $filePath, $code);
559  if ($error !== null) {
560  throw new \RuntimeException('Locallang JS file could not be written to ' . $filePath . '. Reason: ' . $error, 1295193026);
561  }
562  }
563  return '../' . $filePath;
564  }
565 
572  protected function generateLocalLang()
573  {
574  $lang = $this->getLanguageService();
575  $coreLabels = [
576  'waitTitle' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refresh_login_logging_in'),
577  'refresh_login_failed' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refresh_login_failed'),
578  'refresh_login_failed_message' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refresh_login_failed_message'),
579  'refresh_login_title' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refresh_login_title'),
580  'login_expired' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.login_expired'),
581  'refresh_login_username' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refresh_login_username'),
582  'refresh_login_password' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refresh_login_password'),
583  'refresh_login_emptyPassword' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refresh_login_emptyPassword'),
584  'refresh_login_button' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refresh_login_button'),
585  'refresh_exit_button' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refresh_exit_button'),
586  'please_wait' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.please_wait'),
587  'be_locked' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.be_locked'),
588  'login_about_to_expire' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.login_about_to_expire'),
589  'login_about_to_expire_title' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.login_about_to_expire_title'),
590  'refresh_login_logout_button' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refresh_login_logout_button'),
591  'refresh_login_refresh_button' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refresh_login_refresh_button'),
592  'csh_tooltip_loading' => $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:csh_tooltip_loading')
593  ];
594  $labels = [
595  'fileUpload' => [
596  'windowTitle',
597  'buttonSelectFiles',
598  'buttonCancelAll',
599  'infoComponentMaxFileSize',
600  'infoComponentFileUploadLimit',
601  'infoComponentFileTypeLimit',
602  'infoComponentOverrideFiles',
603  'processRunning',
604  'uploadWait',
605  'uploadStarting',
606  'uploadProgress',
607  'uploadSuccess',
608  'errorQueueLimitExceeded',
609  'errorQueueFileSizeLimit',
610  'errorQueueZeroByteFile',
611  'errorQueueInvalidFiletype',
612  'errorUploadHttp',
613  'errorUploadMissingUrl',
614  'errorUploadIO',
615  'errorUploadSecurityError',
616  'errorUploadLimit',
617  'errorUploadFailed',
618  'errorUploadFileIDNotFound',
619  'errorUploadFileValidation',
620  'errorUploadFileCancelled',
621  'errorUploadStopped',
622  'allErrorMessageTitle',
623  'allErrorMessageText',
624  'allError401',
625  'allError2038'
626  ],
627  'liveSearch' => [
628  'title',
629  'helpTitle',
630  'emptyText',
631  'loadingText',
632  'listEmptyText',
633  'showAllResults',
634  'helpDescription',
635  'helpDescriptionPages',
636  'helpDescriptionContent'
637  ],
638  'viewPort' => [
639  'tooltipModuleMenuSplit',
640  'tooltipNavigationContainerSplitDrag',
641  'tooltipNavigationContainerSplitClick',
642  'tooltipDebugPanelSplitDrag'
643  ]
644  ];
645  $generatedLabels = [];
646  $generatedLabels['core'] = $coreLabels;
647  // First loop over all categories (fileUpload, liveSearch, ..)
648  foreach ($labels as $categoryName => $categoryLabels) {
649  // Then loop over every single label
650  foreach ($categoryLabels as $label) {
651  // LLL identifier must be called $categoryName_$label, e.g. liveSearch_loadingText
652  $generatedLabels[$categoryName][$label] = $this->getLanguageService()->getLL($categoryName . '_' . $label);
653  }
654  }
655  return 'TYPO3.LLL = ' . json_encode($generatedLabels) . ';';
656  }
657 
661  protected function generateJavascript()
662  {
663  $beUser = $this->getBackendUser();
664  // Needed for FormEngine manipulation (date picker)
665  $dateFormat = ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? ['MM-DD-YYYY', 'HH:mm MM-DD-YYYY'] : ['DD-MM-YYYY', 'HH:mm DD-MM-YYYY']);
666  $this->pageRenderer->addInlineSetting('DateTimePicker', 'DateFormat', $dateFormat);
667 
668  // If another page module was specified, replace the default Page module with the new one
669  $newPageModule = trim($beUser->getTSConfigVal('options.overridePageModule'));
670  $pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
671  if (!$beUser->check('modules', $pageModule)) {
672  $pageModule = '';
673  }
674  $t3Configuration = [
675  'username' => htmlspecialchars($beUser->user['username']),
676  'uniqueID' => GeneralUtility::shortMD5(uniqid('', true)),
677  'pageModule' => $pageModule,
678  'inWorkspace' => $beUser->workspace !== 0,
679  'showRefreshLoginPopup' => isset($GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup']) ? (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup'] : false
680  ];
681  $this->js .= '
682  TYPO3.configuration = ' . json_encode($t3Configuration) . ';
683 
687  function typoSetup() { //
688  this.username = TYPO3.configuration.username;
689  this.uniqueID = TYPO3.configuration.uniqueID;
690  }
691  var TS = new typoSetup();
692  //backwards compatibility
701  function fsModules() { //
702  this.recentIds=new Array(); // used by frameset modules to track the most recent used id for list frame.
703  this.navFrameHighlightedID=new Array(); // used by navigation frames to track which row id was highlighted last time
704  this.currentBank="0";
705  }
706  var fsMod = new fsModules();
707 
708  top.goToModule = function(modName, cMR_flag, addGetVars) {
709  TYPO3.ModuleMenu.App.showModule(modName, addGetVars);
710  }
711  ' . $this->setStartupModule();
712  // Check editing of page:
713  $this->handlePageEditing();
714  }
715 
719  protected function handlePageEditing()
720  {
721  $beUser = $this->getBackendUser();
722  // EDIT page:
723  $editId = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('edit'));
724  if ($editId) {
725  // Looking up the page to edit, checking permissions:
726  $where = ' AND (' . $beUser->getPagePermsClause(2) . ' OR ' . $beUser->getPagePermsClause(16) . ')';
728  $editRecord = BackendUtility::getRecordWSOL('pages', $editId, '*', $where);
729  } else {
730  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
731  $queryBuilder->getRestrictions()
732  ->removeAll()
733  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
734  ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
735 
736  $editRecord = $queryBuilder->select('*')
737  ->from('pages')
738  ->where(
739  $queryBuilder->expr()->eq(
740  'alias',
741  $queryBuilder->createNamedParameter($editId, \PDO::PARAM_STR)
742  ),
743  $queryBuilder->expr()->orX(
744  $beUser->getPagePermsClause(Permission::PAGE_EDIT),
745  $beUser->getPagePermsClause(Permission::CONTENT_EDIT)
746  )
747  )
748  ->setMaxResults(1)
749  ->execute()
750  ->fetch();
751 
752  if ($editRecord !== false) {
753  BackendUtility::workspaceOL('pages', $editRecord);
754  }
755  }
756  // If the page was accessible, then let the user edit it.
757  if (is_array($editRecord) && $beUser->isInWebMount($editRecord['uid'])) {
758  // Setting JS code to open editing:
759  $this->js .= '
760  // Load page to edit:
761  window.setTimeout("top.loadEditId(' . (int)$editRecord['uid'] . ');", 500);
762  ';
763  // Checking page edit parameter:
764  if (!$beUser->getTSConfigVal('options.bookmark_onEditId_dontSetPageTree')) {
765  $bookmarkKeepExpanded = $beUser->getTSConfigVal('options.bookmark_onEditId_keepExistingExpanded');
766  // Expanding page tree:
767  BackendUtility::openPageTree((int)$editRecord['pid'], !$bookmarkKeepExpanded);
768  }
769  } else {
770  $this->js .= '
771  // Warning about page editing:
772  require(["TYPO3/CMS/Backend/Modal", "TYPO3/CMS/Backend/Severity"], function(Modal, Severity) {
773  Modal.show("", ' . GeneralUtility::quoteJSvalue(sprintf($this->getLanguageService()->getLL('noEditPage'), $editId)) . ', Severity.notice, [{
774  text: ' . GeneralUtility::quoteJSvalue($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_common.xlf:close')) . ',
775  active: true,
776  btnClass: "btn-info",
777  name: "cancel",
778  trigger: function () {
779  Modal.currentModal.trigger("modal-dismiss");
780  }
781  }])
782  });';
783  }
784  }
785  }
786 
792  protected function setStartupModule()
793  {
794  $startModule = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('module'));
795  $startModuleParameters = '';
796  if (!$startModule) {
797  $beUser = $this->getBackendUser();
798  // start module on first login, will be removed once used the first time
799  if (isset($beUser->uc['startModuleOnFirstLogin'])) {
800  $startModule = $beUser->uc['startModuleOnFirstLogin'];
801  unset($beUser->uc['startModuleOnFirstLogin']);
802  $beUser->writeUC();
803  } elseif ($beUser->uc['startModule']) {
804  $startModule = $beUser->uc['startModule'];
805  }
806 
807  // check if the start module has additional parameters, so a redirect to a specific
808  // action is possible
809  if (strpos($startModule, '->') !== false) {
810  list($startModule, $startModuleParameters) = explode('->', $startModule, 2);
811  }
812  }
813 
814  $moduleParameters = GeneralUtility::_GET('modParams');
815  // if no GET parameters are set, check if there are parameters given from the UC
816  if (!$moduleParameters && $startModuleParameters) {
817  $moduleParameters = $startModuleParameters;
818  }
819 
820  if ($startModule) {
821  return '
822  // start in module:
823  top.startInModule = [' . GeneralUtility::quoteJSvalue($startModule) . ', ' . GeneralUtility::quoteJSvalue($moduleParameters) . '];
824  ';
825  }
826  return '';
827  }
828 
836  public function addJavascript($javascript)
837  {
839  // @todo do we need more checks?
840  if (!is_string($javascript)) {
841  throw new \InvalidArgumentException('parameter $javascript must be of type string', 1195129553);
842  }
843  $this->js .= $javascript;
844  }
845 
853  public function addJavascriptFile($javascriptFile)
854  {
856  $jsFileAdded = false;
857  // @todo add more checks if necessary
858  if (file_exists(GeneralUtility::resolveBackPath(PATH_typo3 . $javascriptFile))) {
859  $this->jsFiles[] = $javascriptFile;
860  $jsFileAdded = true;
861  }
862  return $jsFileAdded;
863  }
864 
871  public function addCss($css)
872  {
873  if (!is_string($css)) {
874  throw new \InvalidArgumentException('parameter $css must be of type string', 1195129642);
875  }
876  $this->css .= $css;
877  }
878 
887  public function addCssFile($cssFileName, $cssFile)
888  {
890  $cssFileAdded = false;
891  if (empty($this->cssFiles[$cssFileName])) {
892  $this->cssFiles[$cssFileName] = $cssFile;
893  $cssFileAdded = true;
894  }
895  return $cssFileAdded;
896  }
897 
909  protected function executeHook($identifier, array $hookConfiguration = [])
910  {
911  $options = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/backend.php'];
912  if (isset($options[$identifier]) && is_array($options[$identifier])) {
913  foreach ($options[$identifier] as $hookFunction) {
914  GeneralUtility::callUserFunction($hookFunction, $hookConfiguration, $this);
915  }
916  }
917  }
918 
925  protected function generateModuleMenu()
926  {
927  // get all modules except the user modules for the side menu
928  $moduleStorage = $this->backendModuleRepository->loadAllowedModules(['user', 'help']);
929 
930  $view = $this->getFluidTemplateObject($this->templatePath . 'ModuleMenu/Main.html');
931  $view->assign('modules', $moduleStorage);
932  return $view->render();
933  }
934 
942  public function getModuleMenu(ServerRequestInterface $request, ResponseInterface $response)
943  {
944  $content = $this->generateModuleMenu();
945 
946  $response->getBody()->write(json_encode(['menu' => $content]));
947  return $response;
948  }
949 
957  public function getTopbar(ServerRequestInterface $request, ResponseInterface $response)
958  {
959  $response->getBody()->write(json_encode(['topbar' => $this->renderTopbar()]));
960  return $response;
961  }
962 
969  protected function getFluidTemplateObject($templatePathAndFileName = null)
970  {
971  $view = GeneralUtility::makeInstance(StandaloneView::class);
972  $view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials')]);
973  if ($templatePathAndFileName) {
974  $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templatePathAndFileName));
975  }
976  return $view;
977  }
978 
984  protected function getLanguageService()
985  {
986  return $GLOBALS['LANG'];
987  }
988 
994  protected function getBackendUser()
995  {
996  return $GLOBALS['BE_USER'];
997  }
998 
1004  protected function getDocumentTemplate()
1005  {
1006  return $GLOBALS['TBE_TEMPLATE'];
1007  }
1008 }
static getRecordWSOL( $table, $uid, $fields=' *', $where='', $useDeleteClause=true, $unsetMovePointers=false)
mainAction(ServerRequestInterface $request, ResponseInterface $response)
static implodeAttributes(array $arr, $xhtmlSafe=false, $dontOmitBlankAttribs=false)
static getFilesInDir($path, $extensionList='', $prependPath=false, $order='', $excludePattern='')
debug($variable='', $name=' *variable *', $line=' *line *', $file=' *file *', $recursiveDepth=3, $debugLevel='E_DEBUG')
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
static callUserFunction($funcName, &$params, &$ref, $_='', $errorMode=0)
static openPageTree($pid, $clearExpansion)
static writeFileToTypo3tempDir($filepath, $content)
static getAbsoluteWebPath($targetPath)
Definition: PathUtility.php:40
static getFileAbsFileName($filename, $_=null, $_2=null)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static workspaceOL($table, &$row, $wsid=-99, $unsetMovePointers=false)
static makeInstance($className,... $constructorArguments)
getTopbar(ServerRequestInterface $request, ResponseInterface $response)
executeHook($identifier, array $hookConfiguration=[])
$extConf
getFluidTemplateObject($templatePathAndFileName=null)
static getRelativePath($sourcePath, $targetPath)
Definition: PathUtility.php:70
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
getModuleMenu(ServerRequestInterface $request, ResponseInterface $response)