TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
PageLayoutController.php
Go to the documentation of this file.
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;
52 
57 {
63  public $id;
64 
70  public $pointer;
71 
77  public $imagemode;
78 
84  public $search_field;
85 
92 
98  public $showLimit;
99 
105  public $returnUrl;
106 
112  public $clear_cache;
113 
119  public $popView;
120 
127  public $edit_record;
128 
136 
143 
149  public $modTSconfig;
150 
157 
163  public $pageinfo;
164 
171  public $descrTable;
172 
178  public $colPosList;
179 
186 
192  public $CALC_PERMS;
193 
200 
206  public $MCONF = [];
207 
213  public $MOD_MENU = [];
214 
220  public $MOD_SETTINGS = [];
221 
228  public $externalTables = [];
229 
235  public $content;
236 
244 
248  protected $eRParts = [];
249 
253  protected $editSelect;
254 
258  protected $deleteButton;
259 
263  protected $undoButton;
264 
268  protected $undoButtonR;
269 
273  protected $R_URI;
274 
278  protected $closeUrl;
279 
285  protected $languagesInColumnCache = [];
286 
290  protected $iconFactory;
291 
297  protected $moduleName = 'web_layout';
298 
302  protected $moduleTemplate;
303 
307  protected $buttonBar;
308 
312  protected $searchContent;
313 
319  public function init()
320  {
321  $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
322  $this->iconFactory = $this->moduleTemplate->getIconFactory();
323  $this->buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
324  $this->getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
325  // Setting module configuration / page select clause
326  $this->MCONF['name'] = $this->moduleName;
327  $this->perms_clause = $this->getBackendUser()->getPagePermsClause(1);
328  // Get session data
329  $sessionData = $this->getBackendUser()->getSessionData(RecordList::class);
330  $this->search_field = !empty($sessionData['search_field']) ? $sessionData['search_field'] : '';
331  // GPvars:
332  $this->id = (int)GeneralUtility::_GP('id');
333  $this->pointer = GeneralUtility::_GP('pointer');
334  $this->imagemode = GeneralUtility::_GP('imagemode');
335  $this->clear_cache = GeneralUtility::_GP('clear_cache');
336  $this->popView = GeneralUtility::_GP('popView');
337  $this->edit_record = GeneralUtility::_GP('edit_record');
338  $this->new_unique_uid = GeneralUtility::_GP('new_unique_uid');
339  $this->search_field = GeneralUtility::_GP('search_field');
340  $this->search_levels = GeneralUtility::_GP('search_levels');
341  $this->showLimit = GeneralUtility::_GP('showLimit');
342  $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
343  $this->externalTables = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cms']['db_layout']['addTables'];
344  $sessionData['search_field'] = $this->search_field;
345  // Store session data
346  $this->getBackendUser()->setAndSaveSessionData(RecordList::class, $sessionData);
347  // Load page info array:
348  $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
349  // Initialize menu
350  $this->menuConfig();
351  // Setting sys language from session var:
352  $this->current_sys_language = (int)$this->MOD_SETTINGS['language'];
353  // CSH / Descriptions:
354  $this->descrTable = '_MOD_' . $this->moduleName;
355  }
356 
362  public function menuConfig()
363  {
364  $lang = $this->getLanguageService();
365  // MENU-ITEMS:
366  $this->MOD_MENU = [
367  'tt_content_showHidden' => '',
368  'function' => [
369  1 => $lang->getLL('m_function_1'),
370  0 => $lang->getLL('m_function_0'),
371  2 => $lang->getLL('m_function_2')
372  ],
373  'language' => [
374  0 => $lang->getLL('m_default')
375  ]
376  ];
377  // initialize page/be_user TSconfig settings
378  $this->modSharedTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.SHARED');
379  $this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.' . $this->moduleName);
380  // example settings:
381  // $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cms']['db_layout']['addTables']['tx_myext'] =
382  // array ('default' => array(
383  // 'MENU' => 'LLL:EXT:tx_myext/locallang_db.xlf:menuDefault',
384  // 'fList' => 'title,description,image',
385  // 'icon' => TRUE));
386  if (is_array($this->externalTables)) {
387  if (!empty($this->externalTables)) {
388  GeneralUtility::deprecationLog('The rendering of records in the page module by using '
389  . '$GLOBALS[\'TYPO3_CONF_VARS\'][\'EXTCONF\'][\'cms\'][\'db_layout\'][\'addTables\']'
390  . ' has been deprecated since TYPO3 CMS 8 and will be removed in TYPO3 CMS 9.'
391  );
392  }
393  foreach ($this->externalTables as $table => $tableSettings) {
394  // delete the default settings from above
395  if (is_array($this->MOD_MENU[$table])) {
396  unset($this->MOD_MENU[$table]);
397  }
398  if (is_array($tableSettings) && count($tableSettings) > 1) {
399  foreach ($tableSettings as $key => $settings) {
400  $this->MOD_MENU[$table][$key] = $lang->sL($settings['MENU']);
401  }
402  }
403  }
404  }
405  // First, select all pages_language_overlay records on the current page. Each represents a possibility for a language on the page. Add these to language selector.
406  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
407  $queryBuilder->getRestrictions()->removeAll();
408  if ($this->id) {
409  $queryBuilder->select('sys_language.uid AS uid', 'sys_language.title AS title')
410  ->from('sys_language')
411  ->join(
412  'sys_language',
413  'pages_language_overlay',
414  'pages_language_overlay',
415  $queryBuilder->expr()->eq(
416  'sys_language.uid',
417  $queryBuilder->quoteIdentifier('pages_language_overlay.sys_language_uid')
418  )
419  )
420  ->where(
421  $queryBuilder->expr()->eq(
422  'pages_language_overlay.deleted',
423  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
424  ),
425  $queryBuilder->expr()->eq(
426  'pages_language_overlay.pid',
427  $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
428  ),
429  $queryBuilder->expr()->orX(
430  $queryBuilder->expr()->gte(
431  'pages_language_overlay.t3ver_state',
432  $queryBuilder->createNamedParameter(
434  \PDO::PARAM_INT
435  )
436  ),
437  $queryBuilder->expr()->eq(
438  'pages_language_overlay.t3ver_wsid',
439  $queryBuilder->createNamedParameter($this->getBackendUser()->workspace, \PDO::PARAM_INT)
440  )
441  )
442  )
443  ->groupBy('pages_language_overlay.sys_language_uid', 'sys_language.uid', 'sys_language.pid',
444  'sys_language.tstamp', 'sys_language.hidden', 'sys_language.title',
445  'sys_language.language_isocode', 'sys_language.static_lang_isocode', 'sys_language.flag')
446  ->orderBy('sys_language.sorting');
447  if (!$this->getBackendUser()->isAdmin()) {
448  $queryBuilder->andWhere(
449  $queryBuilder->expr()->eq(
450  'sys_language.hidden',
451  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
452  )
453  );
454  }
455  $statement = $queryBuilder->execute();
456  } else {
457  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(HiddenRestriction::class));
458  $statement = $queryBuilder->select('uid', 'title')
459  ->from('sys_language')
460  ->orderBy('sorting')
461  ->execute();
462  }
463  while ($lRow = $statement->fetch()) {
464  if ($this->getBackendUser()->checkLanguageAccess($lRow['uid'])) {
465  $this->MOD_MENU['language'][$lRow['uid']] = $lRow['title'];
466  }
467  }
468  // Setting alternative default label:
469  if (($this->modSharedTSconfig['properties']['defaultLanguageLabel'] || $this->modTSconfig['properties']['defaultLanguageLabel']) && isset($this->MOD_MENU['language'][0])) {
470  $this->MOD_MENU['language'][0] = $this->modTSconfig['properties']['defaultLanguageLabel'] ? $this->modTSconfig['properties']['defaultLanguageLabel'] : $this->modSharedTSconfig['properties']['defaultLanguageLabel'];
471  }
472  // Clean up settings
473  $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->moduleName);
474  // For all elements to be shown in draft workspaces & to also show hidden elements by default if user hasn't disabled the option
475  if ($this->getBackendUser()->workspace != 0 || $this->MOD_SETTINGS['tt_content_showHidden'] !== '0') {
476  $this->MOD_SETTINGS['tt_content_showHidden'] = 1;
477  }
478  $this->makeActionMenu();
479  }
480 
487  protected function makeActionMenu()
488  {
489  $availableActionArray = [
490  0 => $this->getLanguageService()->getLL('m_function_0'),
491  1 => $this->getLanguageService()->getLL('m_function_1'),
492  2 => $this->getLanguageService()->getLL('m_function_2')
493  ];
494  // Find if there are ANY languages at all (and if not, remove the language option from function menu).
495  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
496  if ($this->getBackendUser()->isAdmin()) {
497  $queryBuilder->getRestrictions()->removeAll();
498  }
499 
500  $count = $queryBuilder
501  ->count('uid')
502  ->from('sys_language')
503  ->execute()
504  ->fetchColumn(0);
505 
506  if (!$count) {
507  unset($availableActionArray['2']);
508  }
509  // page/be_user TSconfig settings and blinding of menu-items
510  if ($this->modTSconfig['properties']['QEisDefault']) {
511  ksort($availableActionArray);
512  }
513  $availableActionArray = BackendUtility::unsetMenuItems($this->modTSconfig['properties'], $availableActionArray, 'menu.function');
514  // Remove QuickEdit as option if page type is not...
515  if (!GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['FE']['content_doktypes'] . ',6', $this->pageinfo['doktype'])) {
516  unset($availableActionArray[0]);
517  }
518  $actionMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
519  $actionMenu->setIdentifier('actionMenu');
520  $actionMenu->setLabel('');
521 
522  $defaultKey = null;
523  $foundDefaultKey = false;
524  foreach ($availableActionArray as $key => $action) {
525  $menuItem = $actionMenu
526  ->makeMenuItem()
527  ->setTitle($action)
528  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&SET[function]=' . $key);
529 
530  if (!$foundDefaultKey) {
531  $defaultKey = $key;
532  $foundDefaultKey = true;
533  }
534  if ((int)$this->MOD_SETTINGS['function'] === $key) {
535  $menuItem->setActive(true);
536  $defaultKey = null;
537  }
538  $actionMenu->addMenuItem($menuItem);
539  }
540  if (isset($defaultKey)) {
541  $this->MOD_SETTINGS['function'] = $defaultKey;
542  }
543  $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($actionMenu);
544  }
545 
551  public function clearCache()
552  {
553  if ($this->clear_cache && !empty($this->pageinfo)) {
554  $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
555  $dataHandler->start([], []);
556  $dataHandler->clear_cacheCmd($this->id);
557  }
558  }
559 
566  {
567  $content = '';
568  $lang = $this->getLanguageService();
569 
570  $view = GeneralUtility::makeInstance(StandaloneView::class);
571  $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
572 
573  // If page is a folder
574  if ($this->pageinfo['doktype'] == PageRepository::DOKTYPE_SYSFOLDER) {
575  $moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
576  $moduleLoader->load($GLOBALS['TBE_MODULES']);
577  $modules = $moduleLoader->modules;
578  if (is_array($modules['web']['sub']['list'])) {
579  $title = $lang->getLL('goToListModule');
580  $message = '<p>' . $lang->getLL('goToListModuleMessage') . '</p>';
581  $message .= '<a class="btn btn-info" href="javascript:top.goToModule(\'web_list\',1);">' . $lang->getLL('goToListModule') . '</a>';
582  $view->assignMultiple([
583  'title' => $title,
584  'message' => $message,
586  ]);
587  $content .= $view->render();
588  }
589  } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_SHORTCUT) {
590  $shortcutMode = (int)$this->pageinfo['shortcut_mode'];
591  $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
592  $targetPage = [];
593 
594  if ($this->pageinfo['shortcut'] || $shortcutMode) {
595  switch ($shortcutMode) {
597  $targetPage = $pageRepository->getPage($this->pageinfo['shortcut']);
598  break;
600  $targetPage = reset($pageRepository->getMenu($this->pageinfo['shortcut'] ?: $this->pageinfo['uid']));
601  break;
603  $targetPage = $pageRepository->getPage($this->pageinfo['pid']);
604  break;
605  }
606 
607  $message = '';
608  if ($shortcutMode === PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
609  $message .= sprintf($lang->getLL('pageIsRandomInternalLinkMessage'));
610  } else {
611  $linkToPid = $this->local_linkThisScript(['id' => $targetPage['uid']]);
612  $path = BackendUtility::getRecordPath($targetPage['uid'], $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW), 1000);
613  $linkedPath = '<a href="' . $linkToPid . '">' . htmlspecialchars($path) . '</a>';
614  $message .= sprintf($lang->getLL('pageIsInternalLinkMessage'), $linkedPath);
615  }
616 
617  $message .= ' (' . htmlspecialchars($lang->sL(BackendUtility::getLabelFromItemlist('pages', 'shortcut_mode', $shortcutMode))) . ')';
618 
619  $view->assignMultiple([
620  'title' => $this->pageinfo['title'],
621  'message' => $message,
623  ]);
624  $content .= $view->render();
625  } else {
626  if (empty($targetPage) && $shortcutMode !== PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
627  $view->assignMultiple([
628  'title' => $this->pageinfo['title'],
629  'message' => $lang->getLL('pageIsMisconfiguredInternalLinkMessage'),
631  ]);
632  $content .= $view->render();
633  }
634  }
635  } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_LINK) {
636  if (empty($this->pageinfo['url'])) {
637  $view->assignMultiple([
638  'title' => $this->pageinfo['title'],
639  'message' => $lang->getLL('pageIsMisconfiguredExternalLinkMessage'),
641  ]);
642  $content .= $view->render();
643  } else {
644  $externalUrl = htmlspecialchars(GeneralUtility::makeInstance(PageRepository::class)->getExtURL($this->pageinfo));
645  if ($externalUrl !== false) {
646  $externalUrlHtml = '<a href="' . $externalUrl . '" target="_blank" rel="noopener">' . $externalUrl . '</a>';
647  $view->assignMultiple([
648  'title' => $this->pageinfo['title'],
649  'message' => sprintf($lang->getLL('pageIsExternalLinkMessage'), $externalUrlHtml),
651  ]);
652  $content .= $view->render();
653  }
654  }
655  }
656  // If content from different pid is displayed
657  if ($this->pageinfo['content_from_pid']) {
658  $contentPage = BackendUtility::getRecord('pages', (int)$this->pageinfo['content_from_pid']);
659  $linkToPid = $this->local_linkThisScript(['id' => $this->pageinfo['content_from_pid']]);
660  $title = BackendUtility::getRecordTitle('pages', $contentPage);
661  $link = '<a href="' . $linkToPid . '">' . htmlspecialchars($title) . ' (PID ' . (int)$this->pageinfo['content_from_pid'] . ')</a>';
662  $message = sprintf($lang->getLL('content_from_pid_title'), $link);
663  $view->assignMultiple([
664  'title' => $title,
665  'message' => $message,
667  ]);
668  $content .= $view->render();
669  }
670  return $content;
671  }
672 
677  protected function getLocalizedPageTitle()
678  {
679  if ($this->current_sys_language > 0) {
680  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
681  ->getQueryBuilderForTable('pages_language_overlay');
682  $queryBuilder->getRestrictions()
683  ->removeAll()
684  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
685  ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
686  $overlayRecord = $queryBuilder
687  ->select('title')
688  ->from('pages_language_overlay')
689  ->where(
690  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)),
691  $queryBuilder->expr()->eq(
692  'sys_language_uid',
693  $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
694  )
695  )
696  ->setMaxResults(1)
697  ->execute()
698  ->fetch();
699  return $overlayRecord['title'];
700  } else {
701  return $this->pageinfo['title'];
702  }
703  }
704 
713  public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
714  {
715  $GLOBALS['SOBE'] = $this;
716  $this->init();
717  $this->clearCache();
718  $this->main();
719  $response->getBody()->write($this->moduleTemplate->renderContent());
720  return $response;
721  }
722 
729  public function main()
730  {
731  $lang = $this->getLanguageService();
732  // Access check...
733  // The page will show only if there is a valid page and if this page may be viewed by the user
734  $access = is_array($this->pageinfo) ? 1 : 0;
735  // Content
736  $content = '';
737  if ($this->id && $access) {
738  // Initialize permission settings:
739  $this->CALC_PERMS = $this->getBackendUser()->calcPerms($this->pageinfo);
740  $this->EDIT_CONTENT = $this->contentIsNotLockedForEditors();
741 
742  $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
743 
744  // override the default jumpToUrl
745  $this->moduleTemplate->addJavaScriptCode('jumpToUrl', '
746  function jumpToUrl(URL,formEl) {
747  if (document.editform && TBE_EDITOR.isFormChanged) { // Check if the function exists... (works in all browsers?)
748  if (!TBE_EDITOR.isFormChanged()) {
749  window.location.href = URL;
750  } else if (formEl) {
751  if (formEl.type=="checkbox") formEl.checked = formEl.checked ? 0 : 1;
752  }
753  } else {
754  window.location.href = URL;
755  }
756  }
757  ');
758  $this->moduleTemplate->addJavaScriptCode('mainJsFunctions', '
759  if (top.fsMod) {
760  top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
761  top.fsMod.navFrameHighlightedID["web"] = "pages' . (int)$this->id . '_"+top.fsMod.currentBank; ' . (int)$this->id . ';
762  }
763  ' . ($this->popView ? BackendUtility::viewOnClick($this->id, '', BackendUtility::BEgetRootLine($this->id)) : '') . '
764  function deleteRecord(table,id,url) { //
765  window.location.href = ' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('tce_db') . '&cmd[')
766  . ' + table + "][" + id + "][delete]=1&redirect=" + encodeURIComponent(url) + "&vC=' . $this->getBackendUser()->veriCode() . '&prErr=1&uPT=1";
767  return false;
768  }
769  ');
770 
771  // Find backend layout / columns
772  $backendLayout = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getSelectedBackendLayout', $this->id, $this);
773  if (!empty($backendLayout['__colPosList'])) {
774  $this->colPosList = implode(',', $backendLayout['__colPosList']);
775  }
776  // Removing duplicates, if any
777  $this->colPosList = array_unique(GeneralUtility::intExplode(',', $this->colPosList));
778  // Accessible columns
779  if (isset($this->modSharedTSconfig['properties']['colPos_list']) && trim($this->modSharedTSconfig['properties']['colPos_list']) !== '') {
780  $this->activeColPosList = array_unique(GeneralUtility::intExplode(',', trim($this->modSharedTSconfig['properties']['colPos_list'])));
781  // Match with the list which is present in the colPosList for the current page
782  if (!empty($this->colPosList) && !empty($this->activeColPosList)) {
783  $this->activeColPosList = array_unique(array_intersect(
784  $this->activeColPosList,
785  $this->colPosList
786  ));
787  }
788  } else {
789  $this->activeColPosList = $this->colPosList;
790  }
791  $this->activeColPosList = implode(',', $this->activeColPosList);
792  $this->colPosList = implode(',', $this->colPosList);
793 
795 
796  // Render the primary module content:
797  if ($this->MOD_SETTINGS['function'] == 0) {
798  // QuickEdit
799  $content .= '<form action="' . htmlspecialchars(BackendUtility::getModuleUrl('tce_db', ['prErr' => 1, 'uPT' => 1])) . '" method="post" enctype="multipart/form-data" name="editform" id="PageLayoutController" onsubmit="return TBE_EDITOR.checkSubmit(1);">';
800  $content .= $this->renderQuickEdit();
801  } else {
802  $content .= '<form action="' . htmlspecialchars(BackendUtility::getModuleUrl($this->moduleName, ['id' => $this->id, 'imagemode' => $this->imagemode])) . '" id="PageLayoutController" method="post">';
803  // Page title
804  $content .= '<h1 class="t3js-title-inlineedit">' . htmlspecialchars($this->getLocalizedPageTitle()) . '</h1>';
805  // All other listings
806  $content .= $this->renderListContent();
807  }
808  $content .= '</form>';
810  // Setting up the buttons for the docheader
811  $this->makeButtons($this->MOD_SETTINGS['function'] == 0 ? 'quickEdit' : '');
812  // Create LanguageMenu
813  $this->makeLanguageMenu();
814  } else {
815  $this->moduleTemplate->addJavaScriptCode(
816  'mainJsFunctions',
817  'if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';'
818  );
819  $content .= '<h1>' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '</h1>';
820  $view = GeneralUtility::makeInstance(StandaloneView::class);
821  $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
822  $view->assignMultiple([
823  'title' => $lang->getLL('clickAPage_header'),
824  'message' => $lang->getLL('clickAPage_content'),
826  ]);
827  $content .= $view->render();
828  }
829  // Set content
830  $this->moduleTemplate->setContent($content);
831  }
832 
838  public function renderQuickEdit()
839  {
840  $beUser = $this->getBackendUser();
841  $lang = $this->getLanguageService();
842  // Set the edit_record value for internal use in this function:
844  // If a command to edit all records in a column is issue, then select all those elements, and redirect to FormEngine
845  if (substr($edit_record, 0, 9) == '_EDIT_COL') {
846  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
847  if ($this->MOD_SETTINGS['tt_content_showHidden']) {
848  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
849  }
850  $statement = $queryBuilder->select('*')
851  ->from('tt_content')
852  ->orderBy('sorting')
853  ->where(
854  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)),
855  $queryBuilder->expr()->eq(
856  'colPos',
857  $queryBuilder->createNamedParameter(substr($edit_record, 10), \PDO::PARAM_INT)
858  ),
859  $queryBuilder->expr()->eq(
860  'sys_language_uid',
861  $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
862  ),
863  $queryBuilder->expr()->orX(
864  $queryBuilder->expr()->gte(
865  't3ver_state',
866  $queryBuilder->createNamedParameter(
867  (string)new VersionState(VersionState::DEFAULT_STATE),
868  \PDO::PARAM_INT
869  )
870  ),
871  $queryBuilder->expr()->eq(
872  't3ver_wsid',
873  $queryBuilder->createNamedParameter($beUser->workspace, \PDO::PARAM_INT)
874  )
875  )
876  )
877  ->execute();
878  $idListA = [];
879  while ($cRow = $statement->fetch()) {
880  $idListA[] = $cRow['uid'];
881  }
882  $url = BackendUtility::getModuleUrl('record_edit', [
883  'edit[tt_content][' . implode(',', $idListA) . ']' => 'edit',
884  'returnUrl' => $this->local_linkThisScript(['edit_record' => ''])
885  ]);
886  HttpUtility::redirect($url);
887  }
888  // If the former record edited was the creation of a NEW record, this will look up the created records uid:
889  if ($this->new_unique_uid) {
890  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_log');
891  $queryBuilder->getRestrictions()->removeAll();
892  $sys_log_row = $queryBuilder->select('tablename', 'recuid')
893  ->from('sys_log')
894  ->where(
895  $queryBuilder->expr()->eq(
896  'userid',
897  $queryBuilder->createNamedParameter($beUser->user['uid'], \PDO::PARAM_INT)
898  ),
899  $queryBuilder->expr()->eq(
900  'NEWid',
901  $queryBuilder->createNamedParameter($this->new_unique_uid, \PDO::PARAM_INT)
902  )
903  )
904  ->execute()
905  ->fetch();
906  if (is_array($sys_log_row)) {
907  $edit_record = $sys_log_row['tablename'] . ':' . $sys_log_row['recuid'];
908  }
909  }
911  // Splitting the edit-record cmd value into table/uid:
912  $this->eRParts = explode(':', $edit_record);
913  $tableName = $this->eRParts[0];
914  // Delete-button flag?
915  $this->deleteButton = MathUtility::canBeInterpretedAsInteger($this->eRParts[1]) && $edit_record && ($tableName !== 'pages' && $this->EDIT_CONTENT || $tableName === 'pages' && $this->CALC_PERMS & Permission::PAGE_DELETE);
916  // If undo-button should be rendered (depends on available items in sys_history)
917  $this->undoButton = false;
918 
919  // if there is no content on a page
920  // the parameter $this->eRParts[1] will be set to e.g. /new/1
921  // which is not an integer value and it will throw an exception here on certain dbms
922  // thus let's check that before as there cannot be a history for a new record
923  $this->undoButtonR = false;
924  if (MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
925  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_history');
926  $queryBuilder->getRestrictions()->removeAll();
927  $this->undoButtonR = $queryBuilder->select('tstamp')
928  ->from('sys_history')
929  ->where(
930  $queryBuilder->expr()->eq(
931  'tablename',
932  $queryBuilder->createNamedParameter($tableName, \PDO::PARAM_STR)
933  ),
934  $queryBuilder->expr()->eq(
935  'recuid',
936  $queryBuilder->createNamedParameter($this->eRParts[1], \PDO::PARAM_INT)
937  )
938  )
939  ->orderBy('tstamp', 'DESC')
940  ->setMaxResults(1)
941  ->execute()
942  ->fetch();
943  }
944  if ($this->undoButtonR) {
945  $this->undoButton = true;
946  }
947  // Setting up the Return URL for coming back to THIS script (if links take the user to another script)
948  $R_URL_parts = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
949  $R_URL_getvars = GeneralUtility::_GET();
950  unset($R_URL_getvars['popView']);
951  unset($R_URL_getvars['new_unique_uid']);
952  $R_URL_getvars['edit_record'] = $edit_record;
953  $this->R_URI = $R_URL_parts['path'] . '?' . GeneralUtility::implodeArrayForUrl('', $R_URL_getvars);
954 
955  // Creating editing form:
956  if ($edit_record) {
957  // Splitting uid parts for special features, if new:
958  list($uidVal, $neighborRecordUid, $ex_colPos) = explode('/', $this->eRParts[1]);
959 
960  if ($uidVal === 'new') {
961  $command = 'new';
962  // Page id of this new record
963  $theUid = $this->id;
964  if ($neighborRecordUid) {
965  $theUid = $neighborRecordUid;
966  }
967  } else {
968  $command = 'edit';
969  $theUid = $uidVal;
970  // Convert $uidVal to workspace version if any:
971  $draftRecord = BackendUtility::getWorkspaceVersionOfRecord($beUser->workspace, $tableName, $theUid, 'uid');
972  if ($draftRecord) {
973  $theUid = $draftRecord['uid'];
974  }
975  }
976 
977  // @todo: Hack because DatabaseInitializeNewRow reads from _GP directly
978  $GLOBALS['_GET']['defVals'][$tableName] = [
979  'colPos' => (int)$ex_colPos,
980  'sys_language_uid' => (int)$this->current_sys_language
981  ];
982 
984  $formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
986  $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
988  $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
989 
990  try {
991  $formDataCompilerInput = [
992  'tableName' => $tableName,
993  'vanillaUid' => (int)$theUid,
994  'command' => $command,
995  ];
996  $formData = $formDataCompiler->compile($formDataCompilerInput);
997 
998  if ($command !== 'new') {
999  BackendUtility::lockRecords($tableName, $formData['databaseRow']['uid'], $tableName === 'tt_content' ? $formData['databaseRow']['pid'] : 0);
1000  }
1001 
1002  $formData['renderType'] = 'outerWrapContainer';
1003  $formResult = $nodeFactory->create($formData)->render();
1004 
1005  $panel = $formResult['html'];
1006  $formResult['html'] = '';
1007 
1009  $formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
1010  $formResultCompiler->mergeResult($formResult);
1011 
1012  $row = $formData['databaseRow'];
1013  $new_unique_uid = '';
1014  if ($command === 'new') {
1015  $new_unique_uid = $row['uid'];
1016  }
1017 
1018  // Add hidden fields:
1019  if ($uidVal == 'new') {
1020  $panel .= '<input type="hidden" name="data[' . $tableName . '][' . $row['uid'] . '][pid]" value="' . $row['pid'] . '" />';
1021  }
1022  $redirect = ($uidVal == 'new' ? BackendUtility::getModuleUrl(
1023  $this->moduleName,
1024  ['id' => $this->id, 'new_unique_uid' => $new_unique_uid, 'returnUrl' => $this->returnUrl]
1025  ) : $this->R_URI);
1026  $panel .= '
1027  <input type="hidden" name="_serialNumber" value="' . md5(microtime()) . '" />
1028  <input type="hidden" name="edit_record" value="' . $edit_record . '" />
1029  <input type="hidden" name="redirect" value="' . htmlspecialchars($redirect) . '" />
1030  ';
1031  // Add JavaScript as needed around the form:
1032  $content = $formResultCompiler->addCssFiles() . $panel . $formResultCompiler->printNeededJSFunctions();
1033 
1034  // Display "is-locked" message:
1035  if ($command === 'edit') {
1036  $lockInfo = BackendUtility::isRecordLocked($tableName, $formData['databaseRow']['uid']);
1037  if ($lockInfo) {
1039  $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $lockInfo['msg'], '', FlashMessage::WARNING);
1041  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1043  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1044  $defaultFlashMessageQueue->enqueue($flashMessage);
1045  }
1046  }
1047  } catch (AccessDeniedException $e) {
1048  // If no edit access, print error message:
1049  $content = '<h2>' . htmlspecialchars($lang->getLL('noAccess')) . '</h2>';
1050  $content .= '<div>' . $lang->getLL('noAccess_msg') . '<br /><br />' . ($beUser->errorMsg ? 'Reason: ' . $beUser->errorMsg . '<br /><br />' : '') . '</div>';
1051  }
1052  } else {
1053  // If no edit access, print error message:
1054  $content = '<h2>' . $lang->getLL('noAccess') . '</h2>';
1055  $content .= '<div>' . $lang->getLL('noAccess_msg') . '</div>';
1056  }
1057 
1058  // Element selection matrix:
1059  if ($tableName === 'tt_content' && MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
1060  $content .= '<h2>' . $lang->getLL('CEonThisPage') . '</h2>';
1061  // PositionMap
1062  $posMap = GeneralUtility::makeInstance(ContentLayoutPagePositionMap::class);
1063  $posMap->cur_sys_language = $this->current_sys_language;
1064  $content .= $posMap->printContentElementColumns(
1065  $this->id,
1066  $this->eRParts[1],
1067  $this->colPosList,
1068  $this->MOD_SETTINGS['tt_content_showHidden'],
1069  $this->R_URI
1070  );
1071  // Toggle hidden ContentElements
1072  $numberOfHiddenElements = $this->getNumberOfHiddenElements();
1073  if ($numberOfHiddenElements) {
1074  $content .= '<div class="checkbox">';
1075  $content .= '<label for="checkTt_content_showHidden">';
1076  $content .= BackendUtility::getFuncCheck($this->id, 'SET[tt_content_showHidden]', $this->MOD_SETTINGS['tt_content_showHidden'], '', '', 'id="checkTt_content_showHidden"');
1077  $content .= (!$numberOfHiddenElements ? ('<span class="text-muted">' . htmlspecialchars($lang->getLL('hiddenCE')) . '</span>') : htmlspecialchars($lang->getLL('hiddenCE')) . ' (' . $numberOfHiddenElements . ')');
1078  $content .= '</label>';
1079  $content .= '</div>';
1080  }
1081  // CSH
1082  $content .= BackendUtility::cshItem($this->descrTable, 'quickEdit_selElement', null, '<span class="btn btn-default btn-sm">|</span>');
1083  }
1084 
1085  return $content;
1086  }
1087 
1093  public function renderListContent()
1094  {
1095  $this->moduleTemplate->getPageRenderer()->loadJquery();
1096  $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ClickMenu');
1098  $dbList = GeneralUtility::makeInstance(PageLayoutView::class);
1099  $dbList->thumbs = $this->imagemode;
1100  $dbList->no_noWrap = 1;
1101  $dbList->descrTable = $this->descrTable;
1102  $this->pointer = MathUtility::forceIntegerInRange($this->pointer, 0, 100000);
1103  $dbList->script = BackendUtility::getModuleUrl($this->moduleName);
1104  $dbList->showIcon = 0;
1105  $dbList->setLMargin = 0;
1106  $dbList->doEdit = $this->EDIT_CONTENT;
1107  $dbList->ext_CALC_PERMS = $this->CALC_PERMS;
1108  $dbList->agePrefixes = $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears');
1109  $dbList->id = $this->id;
1110  $dbList->nextThree = MathUtility::forceIntegerInRange($this->modTSconfig['properties']['editFieldsAtATime'], 0, 10);
1111  $dbList->option_newWizard = $this->modTSconfig['properties']['disableNewContentElementWizard'] ? 0 : 1;
1112  $dbList->defLangBinding = $this->modTSconfig['properties']['defLangBinding'] ? 1 : 0;
1113  if (!$dbList->nextThree) {
1114  $dbList->nextThree = 1;
1115  }
1116  $dbList->externalTables = $this->externalTables;
1117  // Create menu for selecting a table to jump to (this is, if more than just pages/tt_content elements are found on the page!)
1118  // also fills $dbList->activeTables
1119  $dbList->getTableMenu($this->id);
1120  // Initialize other variables:
1121  $tableOutput = [];
1122  $tableJSOutput = [];
1123  $CMcounter = 0;
1124  // Traverse the list of table names which has records on this page (that array is populated
1125  // by the $dblist object during the function getTableMenu()):
1126  foreach ($dbList->activeTables as $table => $value) {
1127  $h_func = '';
1128  $h_func_b = '';
1129  if (!isset($dbList->externalTables[$table])) {
1130  // Toggle hidden ContentElements
1131  $numberOfHiddenElements = $this->getNumberOfHiddenElements();
1132  if ($numberOfHiddenElements > 0) {
1133  $h_func_b = '
1134  <div class="checkbox">
1135  <label for="checkTt_content_showHidden">
1136  <input type="checkbox" id="checkTt_content_showHidden" class="checkbox" name="SET[tt_content_showHidden]" value="1" ' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? 'checked="checked"' : '') . ' />
1137  ' . htmlspecialchars($this->getLanguageService()->getLL('hiddenCE')) . ' (<span class="t3js-hidden-counter">' . $numberOfHiddenElements . '</span>)
1138  </label>
1139  </div>';
1140  }
1141 
1142  // Boolean: Display up/down arrows and edit icons for tt_content records
1143  $dbList->tt_contentConfig['showCommands'] = 1;
1144  // Boolean: Display info-marks or not
1145  $dbList->tt_contentConfig['showInfo'] = 1;
1146  // Setting up the tt_content columns to show:
1147  if (is_array($GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'])) {
1148  $colList = [];
1149  $tcaItems = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getColPosListItemsParsed', $this->id, $this);
1150  foreach ($tcaItems as $temp) {
1151  $colList[] = $temp[1];
1152  }
1153  } else {
1154  // ... should be impossible that colPos has no array. But this is the fallback should it make any sense:
1155  $colList = ['1', '0', '2', '3'];
1156  }
1157  if ($this->colPosList !== '') {
1158  $colList = array_intersect(GeneralUtility::intExplode(',', $this->colPosList), $colList);
1159  }
1160  // The order of the rows: Default is left(1), Normal(0), right(2), margin(3)
1161  $dbList->tt_contentConfig['cols'] = implode(',', $colList);
1162  $dbList->tt_contentConfig['activeCols'] = $this->activeColPosList;
1163  $dbList->tt_contentConfig['showHidden'] = $this->MOD_SETTINGS['tt_content_showHidden'];
1164  $dbList->tt_contentConfig['sys_language_uid'] = (int)$this->current_sys_language;
1165  // If the function menu is set to "Language":
1166  if ($this->MOD_SETTINGS['function'] == 2) {
1167  $dbList->tt_contentConfig['languageMode'] = 1;
1168  $dbList->tt_contentConfig['languageCols'] = $this->MOD_MENU['language'];
1169  $dbList->tt_contentConfig['languageColsPointer'] = $this->current_sys_language;
1170  }
1171  } else {
1172  if (isset($this->MOD_SETTINGS) && isset($this->MOD_MENU)) {
1173  $h_func = BackendUtility::getFuncMenu($this->id, 'SET[' . $table . ']', $this->MOD_SETTINGS[$table], $this->MOD_MENU[$table], '', '');
1174  }
1175  }
1176  // Start the dblist object:
1177  $dbList->itemsLimitSingleTable = 1000;
1178  $dbList->start($this->id, $table, $this->pointer, $this->search_field, $this->search_levels, $this->showLimit);
1179  $dbList->counter = $CMcounter;
1180  $dbList->ext_function = $this->MOD_SETTINGS['function'];
1181  // Render versioning selector:
1182  $dbList->HTMLcode .= $this->moduleTemplate->getVersionSelector($this->id);
1183  // Generate the list of elements here:
1184  $dbList->generateList();
1185  // Adding the list content to the tableOutput variable:
1186  $tableOutput[$table] = $h_func . $dbList->HTMLcode . $h_func_b;
1187  // ... and any accumulated JavaScript goes the same way!
1188  $tableJSOutput[$table] = $dbList->JScode;
1189  // Increase global counter:
1190  $CMcounter += $dbList->counter;
1191  // Reset variables after operation:
1192  $dbList->HTMLcode = '';
1193  $dbList->JScode = '';
1194  }
1195  // END: traverse tables
1196  // For Context Sensitive Menus:
1197  // Init the content
1198  $content = '';
1199  // Additional header content
1200  $headerContentHook = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawHeaderHook'];
1201  if (is_array($headerContentHook)) {
1202  foreach ($headerContentHook as $hook) {
1203  $params = [];
1204  $content .= GeneralUtility::callUserFunction($hook, $params, $this);
1205  }
1206  }
1207  // Add the content for each table we have rendered (traversing $tableOutput variable)
1208  foreach ($tableOutput as $table => $output) {
1209  $content .= $output;
1210  }
1211  // Making search form:
1212  if (!$this->modTSconfig['properties']['disableSearchBox'] && ($dbList->counter > 0 || $this->currentPageHasSubPages())) {
1213  $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ToggleSearchToolbox');
1214  $toggleSearchFormButton = $this->buttonBar->makeLinkButton()
1215  ->setClasses('t3js-toggle-search-toolbox')
1216  ->setTitle($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.title.searchIcon'))
1217  ->setIcon($this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL))
1218  ->setHref('#');
1219  $this->buttonBar->addButton($toggleSearchFormButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
1220  $this->searchContent = $dbList->getSearchBox();
1221  }
1222  // Additional footer content
1223  $footerContentHook = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawFooterHook'];
1224  if (is_array($footerContentHook)) {
1225  foreach ($footerContentHook as $hook) {
1226  $params = [];
1227  $content .= GeneralUtility::callUserFunction($hook, $params, $this);
1228  }
1229  }
1230  return $content;
1231  }
1232 
1236  public function getModuleTemplate()
1237  {
1238  return $this->moduleTemplate;
1239  }
1240 
1246  public function printContent()
1247  {
1248  echo $this->moduleTemplate->renderContent();
1249  }
1250 
1251  /***************************
1252  *
1253  * Sub-content functions, rendering specific parts of the module content.
1254  *
1255  ***************************/
1262  protected function makeButtons($function = '')
1263  {
1264  $lang = $this->getLanguageService();
1265  // View page
1266  if (!VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
1267  $viewButton = $this->buttonBar->makeLinkButton()
1268  ->setOnClick(BackendUtility::viewOnClick($this->pageinfo['uid'], '', BackendUtility::BEgetRootLine($this->pageinfo['uid'])))
1269  ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
1270  ->setIcon($this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL))
1271  ->setHref('#');
1272 
1273  $this->buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1274  }
1275  // Shortcut
1276  $shortcutButton = $this->buttonBar->makeShortcutButton()
1277  ->setModuleName($this->moduleName)
1278  ->setGetVariables([
1279  'id',
1280  'M',
1281  'edit_record',
1282  'pointer',
1283  'new_unique_uid',
1284  'search_field',
1285  'search_levels',
1286  'showLimit'
1287  ])
1288  ->setSetVariables(array_keys($this->MOD_MENU));
1289  $this->buttonBar->addButton($shortcutButton);
1290 
1291  // Cache
1292  if (!$this->modTSconfig['properties']['disableAdvanced']) {
1293  $clearCacheButton = $this->buttonBar->makeLinkButton()
1294  ->setHref(BackendUtility::getModuleUrl($this->moduleName, ['id' => $this->pageinfo['uid'], 'clear_cache' => '1']))
1295  ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.clear_cache'))
1296  ->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', Icon::SIZE_SMALL));
1297  $this->buttonBar->addButton($clearCacheButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
1298  }
1299  if (!$this->modTSconfig['properties']['disableIconToolbar']) {
1300  // Move record
1301  if (MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
1302  $urlParameters = [
1303  'table' => $this->eRParts[0],
1304  'uid' => $this->eRParts[1],
1305  'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1306  ];
1307  $moveButton = $this->buttonBar->makeLinkButton()
1308  ->setHref(BackendUtility::getModuleUrl('move_element', $urlParameters))
1309  ->setTitle($lang->getLL('move_' . ($this->eRParts[0] == 'tt_content' ? 'record' : 'page')))
1310  ->setIcon($this->iconFactory->getIcon('actions-' . ($this->eRParts[0] == 'tt_content' ? 'document' : 'page') . '-move', Icon::SIZE_SMALL));
1311  $this->buttonBar->addButton($moveButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
1312  }
1313 
1314  // Edit page properties and page language overlay icons
1315  if ($this->pageIsNotLockedForEditors() && $this->getBackendUser()->checkLanguageAccess(0)) {
1316  // Edit localized page_language_overlay only when one specific language is selected
1317  if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
1318  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1319  ->getQueryBuilderForTable('pages_language_overlay');
1320  $queryBuilder->getRestrictions()
1321  ->removeAll()
1322  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1323  ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1324  $overlayRecord = $queryBuilder
1325  ->select('uid')
1326  ->from('pages_language_overlay')
1327  ->where(
1328  $queryBuilder->expr()->eq(
1329  'pid',
1330  $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
1331  ),
1332  $queryBuilder->expr()->eq(
1333  'sys_language_uid',
1334  $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
1335  )
1336  )
1337  ->setMaxResults(1)
1338  ->execute()
1339  ->fetch();
1340  // Edit button
1341  $urlParameters = [
1342  'edit' => [
1343  'pages_language_overlay' => [
1344  $overlayRecord['uid'] => 'edit'
1345  ]
1346  ],
1347  'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1348  ];
1349  $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
1350  $editLanguageButton = $this->buttonBar->makeLinkButton()
1351  ->setHref($url)
1352  ->setTitle($lang->getLL('editPageLanguageOverlayProperties'))
1353  ->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
1354  $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1355  }
1356  $urlParameters = [
1357  'edit' => [
1358  'pages' => [
1359  $this->id => 'edit'
1360  ]
1361  ],
1362  'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1363  ];
1364  $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
1365  $editPageButton = $this->buttonBar->makeLinkButton()
1366  ->setHref($url)
1367  ->setTitle($lang->getLL('editPageProperties'))
1368  ->setIcon($this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL));
1369  $this->buttonBar->addButton($editPageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1370  }
1371 
1372  // Add CSH (Context Sensitive Help) icon to tool bar
1373  $contextSensitiveHelpButton = $this->buttonBar->makeHelpButton()
1374  ->setModuleName($this->descrTable)
1375  ->setFieldName(($function === 'quickEdit' ? 'quickEdit' : 'columns_' . $this->MOD_SETTINGS['function']));
1376  $this->buttonBar->addButton($contextSensitiveHelpButton);
1377 
1378  // QuickEdit
1379  if ($function == 'quickEdit') {
1380  // Close Record
1381  $closeButton = $this->buttonBar->makeLinkButton()
1382  ->setHref('#')
1383  ->setOnClick('jumpToUrl(' . GeneralUtility::quoteJSvalue($this->closeUrl) . '); return false;')
1384  ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.closeDoc'))
1385  ->setIcon($this->iconFactory->getIcon('actions-document-close', Icon::SIZE_SMALL));
1386  $this->buttonBar->addButton($closeButton, ButtonBar::BUTTON_POSITION_LEFT, 0);
1387 
1388  // Save Record
1389  $saveButtonDropdown = $this->buttonBar->makeSplitButton();
1390  $saveButton = $this->buttonBar->makeInputButton()
1391  ->setName('_savedok')
1392  ->setValue('1')
1393  ->setForm('PageLayoutController')
1394  ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.saveDoc'))
1395  ->setIcon($this->iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL));
1396  $saveButtonDropdown->addItem($saveButton);
1397  $saveAndCloseButton = $this->buttonBar->makeInputButton()
1398  ->setName('_saveandclosedok')
1399  ->setValue('1')
1400  ->setForm('PageLayoutController')
1401  ->setOnClick('document.editform.redirect.value=\'' . $this->closeUrl . '\';')
1402  ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.saveCloseDoc'))
1403  ->setIcon($this->iconFactory->getIcon('actions-document-save-close', Icon::SIZE_SMALL));
1404  $saveButtonDropdown->addItem($saveAndCloseButton);
1405  $saveAndShowPageButton = $this->buttonBar->makeInputButton()
1406  ->setName('_savedokview')
1407  ->setValue('1')
1408  ->setForm('PageLayoutController')
1409  ->setOnClick('document.editform.redirect.value+=\'&popView=1\';')
1410  ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.saveDocShow'))
1411  ->setIcon($this->iconFactory->getIcon('actions-document-save-view', Icon::SIZE_SMALL));
1412  $saveButtonDropdown->addItem($saveAndShowPageButton);
1413  $this->buttonBar->addButton($saveButtonDropdown, ButtonBar::BUTTON_POSITION_LEFT, 1);
1414 
1415  // Delete record
1416  if ($this->deleteButton) {
1417  $dataAttributes = [];
1418  $dataAttributes['table'] = $this->eRParts[0];
1419  $dataAttributes['uid'] = $this->eRParts[1];
1420  $dataAttributes['return-url'] = BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id;
1421  $deleteButton = $this->buttonBar->makeLinkButton()
1422  ->setHref('#')
1423  ->setClasses('t3js-editform-delete-record')
1424  ->setDataAttributes($dataAttributes)
1425  ->setTitle($lang->getLL('deleteItem'))
1426  ->setIcon($this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL));
1427  $this->buttonBar->addButton($deleteButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
1428  }
1429 
1430  // History
1431  if ($this->undoButton) {
1432  $undoButton = $this->buttonBar->makeLinkButton()
1433  ->setHref('#')
1434  ->setOnClick('window.location.href=' .
1436  BackendUtility::getModuleUrl(
1437  'record_history',
1438  [
1439  'element' => $this->eRParts[0] . ':' . $this->eRParts[1],
1440  'revert' => 'ALL_FIELDS',
1441  'returnUrl' => $this->R_URI,
1442  ]
1443  )
1444  ) . '; return false;')
1445  ->setTitle(sprintf($lang->getLL('undoLastChange'), BackendUtility::calcAge($GLOBALS['EXEC_TIME'] - $this->undoButtonR['tstamp'], $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears'))))
1446  ->setIcon($this->iconFactory->getIcon('actions-edit-undo', Icon::SIZE_SMALL));
1447  $this->buttonBar->addButton($undoButton, ButtonBar::BUTTON_POSITION_LEFT, 5);
1448  $historyButton = $this->buttonBar->makeLinkButton()
1449  ->setHref('#')
1450  ->setOnClick('jumpToUrl(' .
1452  BackendUtility::getModuleUrl(
1453  'record_history',
1454  [
1455  'element' => $this->eRParts[0] . ':' . $this->eRParts[1],
1456  'returnUrl' => $this->R_URI,
1457  ]
1458  ) . '#latest'
1459  ) . ');return false;')
1460  ->setTitle($lang->getLL('recordHistory'))
1461  ->setIcon($this->iconFactory->getIcon('actions-document-history-open', Icon::SIZE_SMALL));
1462  $this->buttonBar->addButton($historyButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1463  }
1464  }
1465  }
1466  }
1467 
1468  /*******************************
1469  *
1470  * Other functions
1471  *
1472  ******************************/
1479  public function getNumberOfHiddenElements()
1480  {
1482  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
1483  $queryBuilder->getRestrictions()
1484  ->removeAll()
1485  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1486  ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1487 
1488  $queryBuilder
1489  ->count('uid')
1490  ->from('tt_content')
1491  ->where(
1492  $queryBuilder->expr()->eq(
1493  'pid',
1494  $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
1495  ),
1496  $queryBuilder->expr()->eq(
1497  'sys_language_uid',
1498  $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
1499  )
1500  );
1501 
1502  if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['disabled'])) {
1503  $andWhere[] = $queryBuilder->expr()->neq(
1504  'hidden',
1505  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1506  );
1507  }
1508 
1509  if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['starttime'])) {
1510  $andWhere[] = $queryBuilder->expr()->andX(
1511  $queryBuilder->expr()->neq(
1512  'starttime',
1513  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1514  ),
1515  $queryBuilder->expr()->gt(
1516  'starttime',
1517  $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
1518  )
1519  );
1520  }
1521 
1522  if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['endtime'])) {
1523  $andWhere[] = $queryBuilder->expr()->andX(
1524  $queryBuilder->expr()->neq(
1525  'endtime',
1526  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1527  ),
1528  $queryBuilder->expr()->lte(
1529  'endtime',
1530  $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
1531  )
1532  );
1533  }
1534 
1535  if (!empty($andWhere)) {
1536  $queryBuilder->andWhere(
1537  $queryBuilder->expr()->orX(...$andWhere)
1538  );
1539  }
1540 
1541  $count = $queryBuilder
1542  ->execute()
1543  ->fetchColumn(0);
1544 
1545  return (int)$count;
1546  }
1547 
1555  public function local_linkThisScript($params)
1556  {
1557  $params['popView'] = '';
1558  $params['new_unique_uid'] = '';
1559  return GeneralUtility::linkThisScript($params);
1560  }
1561 
1567  public function pageIsNotLockedForEditors()
1568  {
1569  return $this->getBackendUser()->isAdmin() || ($this->CALC_PERMS & Permission::PAGE_EDIT) === Permission::PAGE_EDIT && !$this->pageinfo['editlock'];
1570  }
1571 
1578  {
1579  return $this->getBackendUser()->isAdmin() || ($this->CALC_PERMS & Permission::CONTENT_EDIT) === Permission::CONTENT_EDIT && !$this->pageinfo['editlock'];
1580  }
1581 
1587  protected function getLanguageService()
1588  {
1589  return $GLOBALS['LANG'];
1590  }
1591 
1597  protected function getBackendUser()
1598  {
1599  return $GLOBALS['BE_USER'];
1600  }
1601 
1607  protected function getPageRenderer()
1608  {
1609  return GeneralUtility::makeInstance(PageRenderer::class);
1610  }
1611 
1617  protected function makeQuickEditMenu($edit_record)
1618  {
1619  $lang = $this->getLanguageService();
1620  $beUser = $this->getBackendUser();
1621 
1622  $quickEditMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
1623  $quickEditMenu->setIdentifier('quickEditMenu');
1624  $quickEditMenu->setLabel('');
1625 
1626  // Setting close url/return url for exiting this script:
1627  // Goes to 'Columns' view if close is pressed (default)
1628  $this->closeUrl = $this->local_linkThisScript(['SET' => ['function' => 1]]);
1629  if ($this->returnUrl) {
1630  $this->closeUrl = $this->returnUrl;
1631  }
1632  $retUrlStr = $this->returnUrl ? '&returnUrl=' . rawurlencode($this->returnUrl) : '';
1633 
1634  // Creating the selector box, allowing the user to select which element to edit:
1635  $isSelected = 0;
1636  $languageOverlayRecord = '';
1637  if ($this->current_sys_language) {
1638  list($languageOverlayRecord) = BackendUtility::getRecordsByField(
1639  'pages_language_overlay',
1640  'pid',
1641  $this->id,
1642  'AND sys_language_uid=' . (int)$this->current_sys_language
1643  );
1644  }
1645  if (is_array($languageOverlayRecord)) {
1646  $inValue = 'pages_language_overlay:' . $languageOverlayRecord['uid'];
1647  $isSelected += (int)$edit_record == $inValue;
1648  $menuItem = $quickEditMenu->makeMenuItem()
1649  ->setTitle('[ ' . $lang->getLL('editLanguageHeader') . ' ]')
1650  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=' . $inValue . $retUrlStr)
1651  ->setActive($edit_record == $inValue);
1652  $quickEditMenu->addMenuItem($menuItem);
1653  } else {
1654  $inValue = 'pages:' . $this->id;
1655  $isSelected += (int)$edit_record == $inValue;
1656  $menuItem = $quickEditMenu->makeMenuItem()
1657  ->setTitle('[ ' . $lang->getLL('editPageProperties') . ' ]')
1658  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=' . $inValue . $retUrlStr)
1659  ->setActive($edit_record == $inValue);
1660  $quickEditMenu->addMenuItem($menuItem);
1661  }
1662 
1663  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
1664  if ($this->MOD_SETTINGS['tt_content_showHidden']) {
1665  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1666  }
1667  $queryBuilder->select('*')
1668  ->from('tt_content')
1669  ->where(
1670  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)),
1671  $queryBuilder->expr()->eq(
1672  'sys_language_uid',
1673  $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
1674  ),
1675  $queryBuilder->expr()->in(
1676  'colPos',
1677  $queryBuilder->createNamedParameter(
1678  GeneralUtility::intExplode(',', $this->colPosList, true),
1679  Connection::PARAM_INT_ARRAY
1680  )
1681  ),
1682  $queryBuilder->expr()->orX(
1683  $queryBuilder->expr()->gte(
1684  't3ver_state',
1685  $queryBuilder->createNamedParameter(
1687  \PDO::PARAM_INT
1688  )
1689  ),
1690  $queryBuilder->expr()->eq(
1691  't3ver_wsid',
1692  $queryBuilder->createNamedParameter(
1694  \PDO::PARAM_INT
1695  )
1696  )
1697  )
1698  )
1699  ->orderBy('colPos')
1700  ->addOrderBy('sorting');
1701  if (!$beUser->user['admin']) {
1702  $queryBuilder->andWhere(
1703  $queryBuilder->expr()->eq(
1704  'editlock',
1705  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1706  )
1707  );
1708  }
1709  $statement = $queryBuilder->execute();
1710  $colPos = null;
1711  $first = 1;
1712  // Page is the pid if no record to put this after.
1713  $prev = $this->id;
1714  while ($cRow = $statement->fetch()) {
1715  BackendUtility::workspaceOL('tt_content', $cRow);
1716  if (is_array($cRow)) {
1717  if ($first) {
1718  if (!$edit_record) {
1719  $edit_record = 'tt_content:' . $cRow['uid'];
1720  }
1721  $first = 0;
1722  }
1723  if (!isset($colPos) || $cRow['colPos'] !== $colPos) {
1724  $colPos = $cRow['colPos'];
1725  $menuItem = $quickEditMenu->makeMenuItem()
1726  ->setTitle(' ')
1727  ->setHref('#');
1728  $quickEditMenu->addMenuItem($menuItem);
1729  $menuItem = $quickEditMenu->makeMenuItem()
1730  ->setTitle('__' . $lang->sL(BackendUtility::getLabelFromItemlist('tt_content', 'colPos', $colPos)) . ':__')
1731  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=_EDIT_COL:' . $colPos . $retUrlStr);
1732  $quickEditMenu->addMenuItem($menuItem);
1733  }
1734  $inValue = 'tt_content:' . $cRow['uid'];
1735  $isSelected += (int)$edit_record == $inValue;
1736  $menuItem = $quickEditMenu->makeMenuItem()
1737  ->setTitle(GeneralUtility::fixed_lgd_cs(($cRow['header'] ? $cRow['header'] : '[' . $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title') . '] ' . strip_tags($cRow['bodytext'])), $beUser->uc['titleLen']))
1738  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=' . $inValue . $retUrlStr)
1739  ->setActive($edit_record == $inValue);
1740  $quickEditMenu->addMenuItem($menuItem);
1741  $prev = -$cRow['uid'];
1742  }
1743  }
1744  // If edit_record is not set (meaning, no content elements was found for this language) we simply set it to create a new element:
1745  if (!$edit_record) {
1746  $edit_record = 'tt_content:new/' . $prev . '/' . $colPos;
1747  $inValue = 'tt_content:new/' . $prev . '/' . $colPos;
1748  $isSelected += (int)$edit_record == $inValue;
1749  $menuItem = $quickEditMenu->makeMenuItem()
1750  ->setTitle('[ ' . $lang->getLL('newLabel') . ' ]')
1751  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=' . $inValue . $retUrlStr)
1752  ->setActive($edit_record == $inValue);
1753  $quickEditMenu->addMenuItem($menuItem);
1754  }
1755  // If none is yet selected...
1756  if (!$isSelected) {
1757  $menuItem = $quickEditMenu->makeMenuItem()
1758  ->setTitle('__________')
1759  ->setHref('#');
1760  $quickEditMenu->addMenuItem($menuItem);
1761  $menuItem = $quickEditMenu->makeMenuItem()
1762  ->setTitle('[ ' . $lang->getLL('newLabel') . ' ]')
1763  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=' . $edit_record . $retUrlStr)
1764  ->setActive($edit_record == $inValue);
1765  $quickEditMenu->addMenuItem($menuItem);
1766  }
1767  $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($quickEditMenu);
1768  return $edit_record;
1769  }
1770 
1776  protected function makeLanguageMenu()
1777  {
1778  if (count($this->MOD_MENU['language']) > 1) {
1779  $lang = $this->getLanguageService();
1780  $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
1781  $languageMenu->setIdentifier('languageMenu');
1782  $languageMenu->setLabel(htmlspecialchars($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.language')));
1783  foreach ($this->MOD_MENU['language'] as $key => $language) {
1784  $menuItem = $languageMenu
1785  ->makeMenuItem()
1786  ->setTitle($language)
1787  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&SET[language]=' . $key);
1788  if ((int)$this->current_sys_language === $key) {
1789  $menuItem->setActive(true);
1790  }
1791  $languageMenu->addMenuItem($menuItem);
1792  }
1793  $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
1794  }
1795  }
1796 
1802  protected function currentPageHasSubPages()
1803  {
1805  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1806  $queryBuilder->getRestrictions()
1807  ->removeAll()
1808  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1809  ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1810 
1811  // get workspace id
1812  $workspaceId = (int)$this->getBackendUser()->workspace;
1813  $comparisonExpression = $workspaceId === 0 ? 'neq' : 'eq';
1814 
1815  $count = $queryBuilder
1816  ->count('uid')
1817  ->from('pages')
1818  ->where(
1819  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)),
1820  $queryBuilder->expr()->eq(
1821  't3ver_wsid',
1822  $queryBuilder->createNamedParameter($workspaceId, \PDO::PARAM_INT)
1823  ),
1824  $queryBuilder->expr()->{$comparisonExpression}(
1825  'pid',
1826  $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
1827  )
1828  )
1829  ->execute()
1830  ->fetchColumn(0);
1831 
1832  return (bool)$count;
1833  }
1834 }
static getRecordPath($uid, $clause, $titleLimit, $fullTitleLimit=0)
static redirect($url, $httpStatus=self::HTTP_STATUS_303)
Definition: HttpUtility.php:76
static calcAge($seconds, $labels= 'min|hrs|days|yrs|min|hour|day|year')
static implodeArrayForUrl($name, array $theArray, $str= '', $skipBlank=false, $rawurlencodeParamName=false)
mainAction(ServerRequestInterface $request, ResponseInterface $response)
static getRecordTitle($table, $row, $prep=false, $forceResult=true)
static getModuleData($MOD_MENU, $CHANGED_SETTINGS, $modName, $type= '', $dontValidateList= '', $setDefaultList= '')
static viewOnClick($pageUid, $backPath= '', $rootLine=null, $anchorSection= '', $alternativeUrl= '', $additionalGetVars= '', $switchFocus=true)
static getFuncMenu($mainParams, $elementName, $currentValue, $menuItems, $script= '', $addParams= '')
static workspaceOL($table, &$row, $wsid=-99, $unsetMovePointers=false)
static BEgetRootLine($uid, $clause= '', $workspaceOL=false)
static getRecord($table, $uid, $fields= '*', $where= '', $useDeleteClause=true)
static getRecordsByField($theTable, $theField, $theValue, $whereClause= '', $groupBy= '', $orderBy= '', $limit= '', $useDeleteClause=true, $queryBuilder=null)
static linkThisScript(array $getParams=[])
static getFuncCheck($mainParams, $elementName, $currentValue, $script= '', $addParams= '', $tagParams= '')
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
static getFileAbsFileName($filename, $_=null, $_2=null)
static getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields= '*')
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
static callUserFunction($funcName, &$params, &$ref, $_= '', $errorMode=0)