TYPO3 CMS  TYPO3_7-6
PageLayoutController.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 
46 
51 {
57  public $id;
58 
64  public $pointer;
65 
71  public $imagemode;
72 
78  public $search_field;
79 
86 
92  public $showLimit;
93 
99  public $returnUrl;
100 
106  public $clear_cache;
107 
113  public $popView;
114 
121  public $edit_record;
122 
130 
137 
143  public $modTSconfig;
144 
151 
157  public $pageinfo;
158 
165  public $descrTable;
166 
172  public $colPosList;
173 
180 
186  public $CALC_PERMS;
187 
194 
200  public $MCONF = [];
201 
207  public $MOD_MENU = [];
208 
214  public $MOD_SETTINGS = [];
215 
221  public $externalTables = [];
222 
228  public $content;
229 
237 
241  protected $eRParts = [];
242 
246  protected $editSelect;
247 
251  protected $deleteButton;
252 
256  protected $undoButton;
257 
261  protected $undoButtonR;
262 
266  protected $R_URI;
267 
271  protected $closeUrl;
272 
278  protected $languagesInColumnCache = [];
279 
286  public $contentElementCache = [];
287 
291  protected $iconFactory;
292 
298  protected $moduleName = 'web_layout';
299 
303  protected $moduleTemplate;
304 
308  protected $buttonBar;
309 
313  protected $searchContent;
314 
320  public function init()
321  {
322  $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
323  $this->iconFactory = $this->moduleTemplate->getIconFactory();
324  $this->buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
325  $this->getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
326  // Setting module configuration / page select clause
327  $this->MCONF['name'] = $this->moduleName;
328  $this->perms_clause = $this->getBackendUser()->getPagePermsClause(1);
329  // Get session data
330  $sessionData = $this->getBackendUser()->getSessionData(RecordList::class);
331  $this->search_field = !empty($sessionData['search_field']) ? $sessionData['search_field'] : '';
332  // GPvars:
333  $this->id = (int)GeneralUtility::_GP('id');
334  $this->pointer = GeneralUtility::_GP('pointer');
335  $this->imagemode = GeneralUtility::_GP('imagemode');
336  $this->clear_cache = GeneralUtility::_GP('clear_cache');
337  $this->popView = GeneralUtility::_GP('popView');
338  $this->edit_record = GeneralUtility::_GP('edit_record');
339  $this->new_unique_uid = GeneralUtility::_GP('new_unique_uid');
340  $this->search_field = GeneralUtility::_GP('search_field');
341  $this->search_levels = GeneralUtility::_GP('search_levels');
342  $this->showLimit = GeneralUtility::_GP('showLimit');
343  $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
344  $this->externalTables = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cms']['db_layout']['addTables'];
345  $sessionData['search_field'] = $this->search_field;
346  // Store session data
347  $this->getBackendUser()->setAndSaveSessionData(RecordList::class, $sessionData);
348  // Load page info array:
349  $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
350  // Initialize menu
351  $this->menuConfig();
352  // Setting sys language from session var:
353  $this->current_sys_language = (int)$this->MOD_SETTINGS['language'];
354  // CSH / Descriptions:
355  $this->descrTable = '_MOD_' . $this->moduleName;
356  }
357 
363  public function menuConfig()
364  {
365  $lang = $this->getLanguageService();
366  // MENU-ITEMS:
367  $this->MOD_MENU = [
368  'tt_content_showHidden' => '',
369  'function' => [
370  1 => $lang->getLL('m_function_1'),
371  0 => $lang->getLL('m_function_0'),
372  2 => $lang->getLL('m_function_2')
373  ],
374  'language' => [
375  0 => $lang->getLL('m_default')
376  ]
377  ];
378  // initialize page/be_user TSconfig settings
379  $this->modSharedTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.SHARED');
380  $this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.' . $this->moduleName);
381  // example settings:
382  // $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cms']['db_layout']['addTables']['tx_myext'] =
383  // array ('default' => array(
384  // 'MENU' => 'LLL:EXT:tx_myext/locallang_db.xlf:menuDefault',
385  // 'fList' => 'title,description,image',
386  // 'icon' => TRUE));
387  if (is_array($this->externalTables)) {
388  foreach ($this->externalTables as $table => $tableSettings) {
389  // delete the default settings from above
390  if (is_array($this->MOD_MENU[$table])) {
391  unset($this->MOD_MENU[$table]);
392  }
393  if (is_array($tableSettings) && count($tableSettings) > 1) {
394  foreach ($tableSettings as $key => $settings) {
395  $this->MOD_MENU[$table][$key] = $lang->sL($settings['MENU']);
396  }
397  }
398  }
399  }
400  // 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.
401  $res = $this->exec_languageQuery($this->id);
402  while ($lRow = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
403  if ($this->getBackendUser()->checkLanguageAccess($lRow['uid'])) {
404  $this->MOD_MENU['language'][$lRow['uid']] = $lRow['hidden'] ? '(' . $lRow['title'] . ')' : $lRow['title'];
405  }
406  }
407  // Setting alternative default label:
408  if (($this->modSharedTSconfig['properties']['defaultLanguageLabel'] || $this->modTSconfig['properties']['defaultLanguageLabel']) && isset($this->MOD_MENU['language'][0])) {
409  $this->MOD_MENU['language'][0] = $this->modTSconfig['properties']['defaultLanguageLabel'] ? $this->modTSconfig['properties']['defaultLanguageLabel'] : $this->modSharedTSconfig['properties']['defaultLanguageLabel'];
410  }
411  // Clean up settings
412  $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->moduleName);
413  // For all elements to be shown in draft workspaces & to also show hidden elements by default if user hasn't disabled the option
414  if ($this->getBackendUser()->workspace != 0 || $this->MOD_SETTINGS['tt_content_showHidden'] !== '0') {
415  $this->MOD_SETTINGS['tt_content_showHidden'] = 1;
416  }
417  $this->makeActionMenu();
418  }
419 
426  protected function makeActionMenu()
427  {
428  $availableActionArray = [
429  0 => $this->getLanguageService()->getLL('m_function_0'),
430  1 => $this->getLanguageService()->getLL('m_function_1'),
431  2 => $this->getLanguageService()->getLL('m_function_2')
432  ];
433  // Find if there are ANY languages at all (and if not, remove the language option from function menu).
434  $count = $this->getDatabaseConnection()->exec_SELECTcountRows('uid', 'sys_language', $this->getBackendUser()->isAdmin() ? '' : 'hidden=0');
435  if (!$count) {
436  unset($availableActionArray['2']);
437  }
438  // page/be_user TSconfig settings and blinding of menu-items
439  if ($this->modTSconfig['properties']['QEisDefault']) {
440  ksort($availableActionArray);
441  }
442  $availableActionArray = BackendUtility::unsetMenuItems($this->modTSconfig['properties'], $availableActionArray, 'menu.function');
443  // Remove QuickEdit as option if page type is not...
444  if (!GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['FE']['content_doktypes'] . ',6', $this->pageinfo['doktype'])) {
445  unset($availableActionArray[0]);
446  }
447  $actionMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
448  $actionMenu->setIdentifier('actionMenu');
449  $actionMenu->setLabel('');
450 
451  $defaultKey = null;
452  $foundDefaultKey = false;
453  foreach ($availableActionArray as $key => $action) {
454  $menuItem = $actionMenu
455  ->makeMenuItem()
456  ->setTitle($action)
457  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&SET[function]=' . $key);
458 
459  if (!$foundDefaultKey) {
460  $defaultKey = $key;
461  $foundDefaultKey = true;
462  }
463  if ((int)$this->MOD_SETTINGS['function'] === $key) {
464  $menuItem->setActive(true);
465  $defaultKey = null;
466  }
467  $actionMenu->addMenuItem($menuItem);
468  }
469  if (isset($defaultKey)) {
470  $this->MOD_SETTINGS['function'] = $defaultKey;
471  }
472  $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($actionMenu);
473  }
474 
480  public function clearCache()
481  {
482  if ($this->clear_cache) {
483  $tce = GeneralUtility::makeInstance(DataHandler::class);
484  $tce->stripslashes_values = false;
485  $tce->start([], []);
486  $tce->clear_cacheCmd($this->id);
487  }
488  }
489 
496  {
497  $content = '';
498  $lang = $this->getLanguageService();
499 
500  // If page is a folder
501  if ($this->pageinfo['doktype'] == PageRepository::DOKTYPE_SYSFOLDER) {
502  $moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
503  $moduleLoader->load($GLOBALS['TBE_MODULES']);
504  $modules = $moduleLoader->modules;
505  if (is_array($modules['web']['sub']['list'])) {
506  $title = $lang->getLL('goToListModule');
507  $message = '<p>' . $lang->getLL('goToListModuleMessage') . '</p>';
508  $message .= '<a class="btn btn-info" href="javascript:top.goToModule(\'web_list\',1);">' . $lang->getLL('goToListModule') . '</a>';
509  $view = GeneralUtility::makeInstance(StandaloneView::class);
510  $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
511  $view->assignMultiple([
512  'title' => $title,
513  'message' => $message,
515  ]);
516  $content .= $view->render();
517  }
518  }
519  // If content from different pid is displayed
520  if ($this->pageinfo['content_from_pid']) {
521  $contentPage = BackendUtility::getRecord('pages', (int)$this->pageinfo['content_from_pid']);
522  $linkToPid = $this->local_linkThisScript(['id' => $this->pageinfo['content_from_pid']]);
523  $title = BackendUtility::getRecordTitle('pages', $contentPage);
524  $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$this->pageinfo['content_from_pid'] . ')</a>';
525  $message = sprintf($lang->getLL('content_from_pid_title'), $link);
526  $view = GeneralUtility::makeInstance(StandaloneView::class);
527  $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
528  $view->assignMultiple([
529  'title' => $title,
530  'message' => $message,
532  ]);
533  $content .= $view->render();
534  }
535  return $content;
536  }
537 
542  protected function getLocalizedPageTitle()
543  {
544  if ($this->current_sys_language > 0) {
545  $overlayRecord = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
546  '*',
547  'pages_language_overlay',
548  'pid = ' . (int)$this->id .
549  ' AND sys_language_uid = ' . (int)$this->current_sys_language .
550  BackendUtility::deleteClause('pages_language_overlay') .
551  BackendUtility::versioningPlaceholderClause('pages_language_overlay'),
552  '',
553  '',
554  ''
555  );
556  BackendUtility::workspaceOL('pages_language_overlay', $overlayRecord);
557  return $overlayRecord['title'];
558  } else {
559  return $this->pageinfo['title'];
560  }
561  }
562 
571  public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
572  {
573  $GLOBALS['SOBE'] = $this;
574  $this->init();
575  $this->clearCache();
576  $this->main();
577  $response->getBody()->write($this->moduleTemplate->renderContent());
578  return $response;
579  }
580 
587  public function main()
588  {
589  $lang = $this->getLanguageService();
590  // Access check...
591  // The page will show only if there is a valid page and if this page may be viewed by the user
592  $access = is_array($this->pageinfo) ? 1 : 0;
593  // Content
594  $content = '';
595  if ($this->id && $access) {
596  // Initialize permission settings:
597  $this->CALC_PERMS = $this->getBackendUser()->calcPerms($this->pageinfo);
598  $this->EDIT_CONTENT = $this->contentIsNotLockedForEditors();
599 
600  $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
601 
602  // override the default jumpToUrl
603  $this->moduleTemplate->addJavaScriptCode('jumpToUrl', '
604  function jumpToUrl(URL,formEl) {
605  if (document.editform && TBE_EDITOR.isFormChanged) { // Check if the function exists... (works in all browsers?)
606  if (!TBE_EDITOR.isFormChanged()) {
607  window.location.href = URL;
608  } else if (formEl) {
609  if (formEl.type=="checkbox") formEl.checked = formEl.checked ? 0 : 1;
610  }
611  } else {
612  window.location.href = URL;
613  }
614  }
615  ');
616  $this->moduleTemplate->addJavaScriptCode('mainJsFunctions', '
617  if (top.fsMod) {
618  top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
619  top.fsMod.navFrameHighlightedID["web"] = "pages' . (int)$this->id . '_"+top.fsMod.currentBank; ' . (int)$this->id . ';
620  }
621  ' . ($this->popView ? BackendUtility::viewOnClick($this->id, '', BackendUtility::BEgetRootLine($this->id)) : '') . '
622  function deleteRecord(table,id,url) { //
623  window.location.href = ' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('tce_db') . '&cmd[')
624  . ' + table + "][" + id + "][delete]=1&redirect=" + encodeURIComponent(url) + "&vC=' . $this->getBackendUser()->veriCode() . '&prErr=1&uPT=1";
625  return false;
626  }
627  ');
628 
629  // Find backend layout / columns
630  $backendLayout = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getSelectedBackendLayout', $this->id, $this);
631  if (!empty($backendLayout['__colPosList'])) {
632  $this->colPosList = implode(',', $backendLayout['__colPosList']);
633  }
634  // Removing duplicates, if any
635  $this->colPosList = array_unique(GeneralUtility::intExplode(',', $this->colPosList));
636  // Accessible columns
637  if (isset($this->modSharedTSconfig['properties']['colPos_list']) && trim($this->modSharedTSconfig['properties']['colPos_list']) !== '') {
638  $this->activeColPosList = array_unique(GeneralUtility::intExplode(',', trim($this->modSharedTSconfig['properties']['colPos_list'])));
639  // Match with the list which is present in the colPosList for the current page
640  if (!empty($this->colPosList) && !empty($this->activeColPosList)) {
641  $this->activeColPosList = array_unique(array_intersect(
642  $this->activeColPosList,
643  $this->colPosList
644  ));
645  }
646  } else {
647  $this->activeColPosList = $this->colPosList;
648  }
649  $this->activeColPosList = implode(',', $this->activeColPosList);
650  $this->colPosList = implode(',', $this->colPosList);
651 
653 
654  // Render the primary module content:
655  if ($this->MOD_SETTINGS['function'] == 0) {
656  // QuickEdit
657  $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);">';
658  $content .= $this->renderQuickEdit();
659  } else {
660  $content .= '<form action="' . htmlspecialchars(BackendUtility::getModuleUrl($this->moduleName, ['id' => $this->id, 'imagemode' => $this->imagemode])) . '" id="PageLayoutController" method="post">';
661  // Page title
662  $content .= '<h1 class="t3js-title-inlineedit">' . htmlspecialchars($this->getLocalizedPageTitle()) . '</h1>';
663  // All other listings
664  $content .= $this->renderListContent();
665  }
666  $content .= '</form>';
668  // Setting up the buttons for the docheader
669  $this->makeButtons($this->MOD_SETTINGS['function'] == 0 ? 'quickEdit' : '');
670  // Create LanguageMenu
671  $this->makeLanguageMenu();
672  } else {
673  $this->moduleTemplate->addJavaScriptCode(
674  'mainJsFunctions',
675  'if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';'
676  );
677  $content .= '<h1>' . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) . '</h1>';
678  $view = GeneralUtility::makeInstance(StandaloneView::class);
679  $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
680  $view->assignMultiple([
681  'title' => $lang->getLL('clickAPage_header'),
682  'message' => $lang->getLL('clickAPage_content'),
684  ]);
685  $content .= $view->render();
686  }
687  // Set content
688  $this->moduleTemplate->setContent($content);
689  }
690 
696  public function renderQuickEdit()
697  {
698  $databaseConnection = $this->getDatabaseConnection();
699  $beUser = $this->getBackendUser();
700  $lang = $this->getLanguageService();
701  // Set the edit_record value for internal use in this function:
703  // If a command to edit all records in a column is issue, then select all those elements, and redirect to FormEngine
704  if (substr($edit_record, 0, 9) == '_EDIT_COL') {
705  $res = $databaseConnection->exec_SELECTquery('*', 'tt_content', 'pid=' . (int)$this->id . ' AND colPos=' . (int)substr($edit_record, 10) . ' AND sys_language_uid=' . (int)$this->current_sys_language . ($this->MOD_SETTINGS['tt_content_showHidden'] ? '' : BackendUtility::BEenableFields('tt_content')) . BackendUtility::deleteClause('tt_content') . BackendUtility::versioningPlaceholderClause('tt_content'), '', 'sorting');
706  $idListA = [];
707  while ($cRow = $databaseConnection->sql_fetch_assoc($res)) {
708  $idListA[] = $cRow['uid'];
709  }
710  $url = BackendUtility::getModuleUrl('record_edit', [
711  'edit[tt_content][' . implode(',', $idListA) . ']' => 'edit',
712  'returnUrl' => $this->local_linkThisScript(['edit_record' => ''])
713  ]);
714  HttpUtility::redirect($url);
715  }
716  // If the former record edited was the creation of a NEW record, this will look up the created records uid:
717  if ($this->new_unique_uid) {
718  $res = $databaseConnection->exec_SELECTquery('*', 'sys_log', 'userid=' . (int)$beUser->user['uid'] . ' AND NEWid=' . $databaseConnection->fullQuoteStr($this->new_unique_uid, 'sys_log'));
719  $sys_log_row = $databaseConnection->sql_fetch_assoc($res);
720  if (is_array($sys_log_row)) {
721  $edit_record = $sys_log_row['tablename'] . ':' . $sys_log_row['recuid'];
722  }
723  }
725  // Splitting the edit-record cmd value into table/uid:
726  $this->eRParts = explode(':', $edit_record);
727  $tableName = $this->eRParts[0];
728  // Delete-button flag?
729  $this->deleteButton = MathUtility::canBeInterpretedAsInteger($this->eRParts[1]) && $edit_record && ($tableName !== 'pages' && $this->EDIT_CONTENT || $tableName === 'pages' && $this->CALC_PERMS & Permission::PAGE_DELETE);
730  // If undo-button should be rendered (depends on available items in sys_history)
731  $this->undoButton = false;
732 
733  // if there is no content on a page
734  // the parameter $this->eRParts[1] will be set to e.g. /new/1
735  // which is not an integer value and it will throw an exception here on certain dbms
736  // thus let's check that before as there cannot be a history for a new record
737  $this->undoButtonR = false;
738  if (MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
739  $undoRes = $databaseConnection->exec_SELECTquery('tstamp', 'sys_history', 'tablename=' . $databaseConnection->fullQuoteStr($tableName, 'sys_history') . ' AND recuid=' . (int)$this->eRParts[1], '', 'tstamp DESC', '1');
740  $this->undoButtonR = $databaseConnection->sql_fetch_assoc($undoRes);
741  }
742  if ($this->undoButtonR) {
743  $this->undoButton = true;
744  }
745  // Setting up the Return URL for coming back to THIS script (if links take the user to another script)
746  $R_URL_parts = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
747  $R_URL_getvars = GeneralUtility::_GET();
748  unset($R_URL_getvars['popView']);
749  unset($R_URL_getvars['new_unique_uid']);
750  $R_URL_getvars['edit_record'] = $edit_record;
751  $this->R_URI = $R_URL_parts['path'] . '?' . GeneralUtility::implodeArrayForUrl('', $R_URL_getvars);
752 
753  // Creating editing form:
754  if ($edit_record) {
755  // Splitting uid parts for special features, if new:
756  list($uidVal, $neighborRecordUid, $ex_colPos) = explode('/', $this->eRParts[1]);
757 
758  if ($uidVal === 'new') {
759  $command = 'new';
760  // Page id of this new record
761  $theUid = $this->id;
762  if ($neighborRecordUid) {
763  $theUid = $neighborRecordUid;
764  }
765  } else {
766  $command = 'edit';
767  $theUid = $uidVal;
768  // Convert $uidVal to workspace version if any:
769  $draftRecord = BackendUtility::getWorkspaceVersionOfRecord($beUser->workspace, $tableName, $theUid, 'uid');
770  if ($draftRecord) {
771  $theUid = $draftRecord['uid'];
772  }
773  }
774 
775  // @todo: Hack because DatabaseInitializeNewRow reads from _GP directly
776  $GLOBALS['_GET']['defVals'][$tableName] = [
777  'colPos' => (int)$ex_colPos,
778  'sys_language_uid' => (int)$this->current_sys_language
779  ];
780 
782  $formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
784  $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
786  $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
787 
788  try {
789  $formDataCompilerInput = [
790  'tableName' => $tableName,
791  'vanillaUid' => (int)$theUid,
792  'command' => $command,
793  ];
794  $formData = $formDataCompiler->compile($formDataCompilerInput);
795 
796  if ($command !== 'new') {
797  BackendUtility::lockRecords($tableName, $formData['databaseRow']['uid'], $tableName === 'tt_content' ? $formData['databaseRow']['pid'] : 0);
798  }
799 
800  $formData['renderType'] = 'outerWrapContainer';
801  $formResult = $nodeFactory->create($formData)->render();
802 
803  $panel = $formResult['html'];
804  $formResult['html'] = '';
805 
807  $formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
808  $formResultCompiler->mergeResult($formResult);
809 
810  $row = $formData['databaseRow'];
811  $new_unique_uid = '';
812  if ($command === 'new') {
813  $new_unique_uid = $row['uid'];
814  }
815 
816  // Add hidden fields:
817  if ($uidVal == 'new') {
818  $panel .= '<input type="hidden" name="data[' . $tableName . '][' . $row['uid'] . '][pid]" value="' . $row['pid'] . '" />';
819  }
820  $redirect = ($uidVal == 'new' ? BackendUtility::getModuleUrl(
821  $this->moduleName,
822  ['id' => $this->id, 'new_unique_uid' => $new_unique_uid, 'returnUrl' => $this->returnUrl]
823  ) : $this->R_URI);
824  $panel .= '
825  <input type="hidden" name="_serialNumber" value="' . md5(microtime()) . '" />
826  <input type="hidden" name="edit_record" value="' . $edit_record . '" />
827  <input type="hidden" name="redirect" value="' . htmlspecialchars($redirect) . '" />
828  ';
829  // Add JavaScript as needed around the form:
830  $content = $formResultCompiler->JStop() . $panel . $formResultCompiler->printNeededJSFunctions();
831 
832  // Display "is-locked" message:
833  if ($command === 'edit') {
834  $lockInfo = BackendUtility::isRecordLocked($tableName, $formData['databaseRow']['uid']);
835  if ($lockInfo) {
837  $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $lockInfo['msg'], '', FlashMessage::WARNING);
839  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
841  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
842  $defaultFlashMessageQueue->enqueue($flashMessage);
843  }
844  }
845  } catch (AccessDeniedException $e) {
846  // If no edit access, print error message:
847  $content = '<h2>' . $lang->getLL('noAccess', true) . '</h2>';
848  $content .= '<div>' . $lang->getLL('noAccess_msg') . '<br /><br />' . ($beUser->errorMsg ? 'Reason: ' . $beUser->errorMsg . '<br /><br />' : '') . '</div>';
849  }
850  } else {
851  // If no edit access, print error message:
852  $content = '<h2>' . $lang->getLL('noAccess') . '</h2>';
853  $content .= '<div>' . $lang->getLL('noAccess_msg') . '</div>';
854  }
855 
856  // Element selection matrix:
857  if ($tableName === 'tt_content' && MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
858  $content .= '<h2>' . $lang->getLL('CEonThisPage') . '</h2>';
859  // PositionMap
860  $posMap = GeneralUtility::makeInstance(ContentLayoutPagePositionMap::class);
861  $posMap->cur_sys_language = $this->current_sys_language;
862  $content .= $posMap->printContentElementColumns(
863  $this->id,
864  $this->eRParts[1],
865  $this->colPosList,
866  $this->MOD_SETTINGS['tt_content_showHidden'],
867  $this->R_URI
868  );
869  // Toggle hidden ContentElements
870  $numberOfHiddenElements = $this->getNumberOfHiddenElements();
871  if ($numberOfHiddenElements) {
872  $content .= '<div class="checkbox">';
873  $content .= '<label for="checkTt_content_showHidden">';
874  $content .= BackendUtility::getFuncCheck($this->id, 'SET[tt_content_showHidden]', $this->MOD_SETTINGS['tt_content_showHidden'], '', '', 'id="checkTt_content_showHidden"');
875  $content .= (!$numberOfHiddenElements ? ('<span class="text-muted">' . $lang->getLL('hiddenCE', true) . '</span>') : $lang->getLL('hiddenCE', true) . ' (' . $numberOfHiddenElements . ')');
876  $content .= '</label>';
877  $content .= '</div>';
878  }
879  // CSH
880  $content .= BackendUtility::cshItem($this->descrTable, 'quickEdit_selElement', null, '<span class="btn btn-default btn-sm">|</span>');
881  }
882 
883  return $content;
884  }
885 
891  public function renderListContent()
892  {
893  $this->moduleTemplate->getPageRenderer()->loadJquery();
894  $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ClickMenu');
896  $dbList = GeneralUtility::makeInstance(PageLayoutView::class);
897  $dbList->thumbs = $this->imagemode;
898  $dbList->no_noWrap = 1;
899  $dbList->descrTable = $this->descrTable;
900  $this->pointer = MathUtility::forceIntegerInRange($this->pointer, 0, 100000);
901  $dbList->script = BackendUtility::getModuleUrl($this->moduleName);
902  $dbList->showIcon = 0;
903  $dbList->setLMargin = 0;
904  $dbList->doEdit = $this->EDIT_CONTENT;
905  $dbList->ext_CALC_PERMS = $this->CALC_PERMS;
906  $dbList->agePrefixes = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears');
907  $dbList->id = $this->id;
908  $dbList->nextThree = MathUtility::forceIntegerInRange($this->modTSconfig['properties']['editFieldsAtATime'], 0, 10);
909  $dbList->option_newWizard = $this->modTSconfig['properties']['disableNewContentElementWizard'] ? 0 : 1;
910  $dbList->defLangBinding = $this->modTSconfig['properties']['defLangBinding'] ? 1 : 0;
911  if (!$dbList->nextThree) {
912  $dbList->nextThree = 1;
913  }
914  $dbList->externalTables = $this->externalTables;
915  // Create menu for selecting a table to jump to (this is, if more than just pages/tt_content elements are found on the page!)
916  // also fills $dbList->activeTables
917  $dbList->getTableMenu($this->id);
918  // Initialize other variables:
919  $tableOutput = [];
920  $tableJSOutput = [];
921  $CMcounter = 0;
922  // Traverse the list of table names which has records on this page (that array is populated
923  // by the $dblist object during the function getTableMenu()):
924  foreach ($dbList->activeTables as $table => $value) {
925  $h_func = '';
926  $h_func_b = '';
927  if (!isset($dbList->externalTables[$table])) {
928  // Toggle hidden ContentElements
929  $numberOfHiddenElements = $this->getNumberOfHiddenElements();
930  if ($numberOfHiddenElements > 0) {
931  $h_func_b = '
932  <div class="checkbox">
933  <label for="checkTt_content_showHidden">
934  <input type="checkbox" id="checkTt_content_showHidden" class="checkbox" name="SET[tt_content_showHidden]" value="1" ' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? 'checked="checked"' : '') . ' />
935  ' . $this->getLanguageService()->getLL('hiddenCE', true) . ' (<span class="t3js-hidden-counter">' . $numberOfHiddenElements . '</span>)
936  </label>
937  </div>';
938  }
939 
940  // Boolean: Display up/down arrows and edit icons for tt_content records
941  $dbList->tt_contentConfig['showCommands'] = 1;
942  // Boolean: Display info-marks or not
943  $dbList->tt_contentConfig['showInfo'] = 1;
944  // Setting up the tt_content columns to show:
945  if (is_array($GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'])) {
946  $colList = [];
947  $tcaItems = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getColPosListItemsParsed', $this->id, $this);
948  foreach ($tcaItems as $temp) {
949  $colList[] = $temp[1];
950  }
951  } else {
952  // ... should be impossible that colPos has no array. But this is the fallback should it make any sense:
953  $colList = ['1', '0', '2', '3'];
954  }
955  if ($this->colPosList !== '') {
956  $colList = array_intersect(GeneralUtility::intExplode(',', $this->colPosList), $colList);
957  }
958  // The order of the rows: Default is left(1), Normal(0), right(2), margin(3)
959  $dbList->tt_contentConfig['cols'] = implode(',', $colList);
960  $dbList->tt_contentConfig['activeCols'] = $this->activeColPosList;
961  $dbList->tt_contentConfig['showHidden'] = $this->MOD_SETTINGS['tt_content_showHidden'];
962  $dbList->tt_contentConfig['sys_language_uid'] = (int)$this->current_sys_language;
963  // If the function menu is set to "Language":
964  if ($this->MOD_SETTINGS['function'] == 2) {
965  $dbList->tt_contentConfig['languageMode'] = 1;
966  $dbList->tt_contentConfig['languageCols'] = $this->MOD_MENU['language'];
967  $dbList->tt_contentConfig['languageColsPointer'] = $this->current_sys_language;
968  }
969  } else {
970  if (isset($this->MOD_SETTINGS) && isset($this->MOD_MENU)) {
971  $h_func = BackendUtility::getFuncMenu($this->id, 'SET[' . $table . ']', $this->MOD_SETTINGS[$table], $this->MOD_MENU[$table], '', '');
972  }
973  }
974  // Start the dblist object:
975  $dbList->itemsLimitSingleTable = 1000;
976  $dbList->start($this->id, $table, $this->pointer, $this->search_field, $this->search_levels, $this->showLimit);
977  $dbList->counter = $CMcounter;
978  $dbList->ext_function = $this->MOD_SETTINGS['function'];
979  // Render versioning selector:
980  $dbList->HTMLcode .= $this->moduleTemplate->getVersionSelector($this->id);
981  // Generate the list of elements here:
982  $dbList->generateList();
983  // Adding the list content to the tableOutput variable:
984  $tableOutput[$table] = $h_func . $dbList->HTMLcode . $h_func_b;
985  // ... and any accumulated JavaScript goes the same way!
986  $tableJSOutput[$table] = $dbList->JScode;
987  // Increase global counter:
988  $CMcounter += $dbList->counter;
989  // Reset variables after operation:
990  $dbList->HTMLcode = '';
991  $dbList->JScode = '';
992  }
993  // END: traverse tables
994  // For Context Sensitive Menus:
995  // Init the content
996  $content = '';
997  // Additional header content
998  $headerContentHook = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawHeaderHook'];
999  if (is_array($headerContentHook)) {
1000  foreach ($headerContentHook as $hook) {
1001  $params = [];
1003  }
1004  }
1005  // Add the content for each table we have rendered (traversing $tableOutput variable)
1006  foreach ($tableOutput as $table => $output) {
1007  $content .= $output;
1008  }
1009  // Making search form:
1010  if (!$this->modTSconfig['properties']['disableSearchBox'] && ($dbList->counter > 0 || $this->currentPageHasSubPages())) {
1011  $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ToggleSearchToolbox');
1012  $toggleSearchFormButton = $this->buttonBar->makeLinkButton()
1013  ->setClasses('t3js-toggle-search-toolbox')
1014  ->setTitle($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.title.searchIcon'))
1015  ->setIcon($this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL))
1016  ->setHref('#');
1017  $this->buttonBar->addButton($toggleSearchFormButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
1018  $this->searchContent = $dbList->getSearchBox();
1019  }
1020  // Additional footer content
1021  $footerContentHook = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawFooterHook'];
1022  if (is_array($footerContentHook)) {
1023  foreach ($footerContentHook as $hook) {
1024  $params = [];
1026  }
1027  }
1028  return $content;
1029  }
1030 
1034  public function getModuleTemplate()
1035  {
1036  return $this->moduleTemplate;
1037  }
1038 
1044  public function printContent()
1045  {
1046  echo $this->moduleTemplate->renderContent();
1047  }
1048 
1049  /***************************
1050  *
1051  * Sub-content functions, rendering specific parts of the module content.
1052  *
1053  ***************************/
1060  protected function makeButtons($function = '')
1061  {
1062  $lang = $this->getLanguageService();
1063  // View page
1064  if (!VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
1065  $viewButton = $this->buttonBar->makeLinkButton()
1066  ->setOnClick(BackendUtility::viewOnClick($this->pageinfo['uid'], '', BackendUtility::BEgetRootLine($this->pageinfo['uid'])))
1067  ->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage'))
1068  ->setIcon($this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL))
1069  ->setHref('#');
1070 
1071  $this->buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1072  }
1073  // Shortcut
1074  $shortcutButton = $this->buttonBar->makeShortcutButton()
1075  ->setModuleName($this->moduleName)
1076  ->setGetVariables([
1077  'id',
1078  'M',
1079  'edit_record',
1080  'pointer',
1081  'new_unique_uid',
1082  'search_field',
1083  'search_levels',
1084  'showLimit'
1085  ])
1086  ->setSetVariables(array_keys($this->MOD_MENU));
1087  $this->buttonBar->addButton($shortcutButton);
1088 
1089  // Cache
1090  if (!$this->modTSconfig['properties']['disableAdvanced']) {
1091  $clearCacheButton = $this->buttonBar->makeLinkButton()
1092  ->setHref(BackendUtility::getModuleUrl($this->moduleName, ['id' => $this->pageinfo['uid'], 'clear_cache' => '1']))
1093  ->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.clear_cache'))
1094  ->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', Icon::SIZE_SMALL));
1095  $this->buttonBar->addButton($clearCacheButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
1096  }
1097  if (!$this->modTSconfig['properties']['disableIconToolbar']) {
1098  // Move record
1099  if (MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
1100  $urlParameters = [
1101  'table' => $this->eRParts[0],
1102  'uid' => $this->eRParts[1],
1103  'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1104  ];
1105  $moveButton = $this->buttonBar->makeLinkButton()
1106  ->setHref(BackendUtility::getModuleUrl('move_element', $urlParameters))
1107  ->setTitle($lang->getLL('move_' . ($this->eRParts[0] == 'tt_content' ? 'record' : 'page')))
1108  ->setIcon($this->iconFactory->getIcon('actions-' . ($this->eRParts[0] == 'tt_content' ? 'document' : 'page') . '-move', Icon::SIZE_SMALL));
1109  $this->buttonBar->addButton($moveButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
1110  }
1111 
1112  // Edit page properties and page language overlay icons
1113  if ($this->pageIsNotLockedForEditors() && $this->getBackendUser()->checkLanguageAccess(0)) {
1114  // Edit localized page_language_overlay only when one specific language is selected
1115  if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
1116  $overlayRecord = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
1117  'uid',
1118  'pages_language_overlay',
1119  'pid = ' . (int)$this->id . ' ' .
1120  'AND sys_language_uid = ' . (int)$this->current_sys_language .
1121  BackendUtility::deleteClause('pages_language_overlay') .
1122  BackendUtility::versioningPlaceholderClause('pages_language_overlay'),
1123  '',
1124  '',
1125  ''
1126  );
1127  // Edit button
1128  $urlParameters = [
1129  'edit' => [
1130  'pages_language_overlay' => [
1131  $overlayRecord['uid'] => 'edit'
1132  ]
1133  ],
1134  'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1135  ];
1136  $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
1137  $editLanguageButton = $this->buttonBar->makeLinkButton()
1138  ->setHref($url)
1139  ->setTitle($lang->getLL('editPageLanguageOverlayProperties'))
1140  ->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
1141  $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1142  }
1143  $urlParameters = [
1144  'edit' => [
1145  'pages' => [
1146  $this->id => 'edit'
1147  ]
1148  ],
1149  'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1150  ];
1151  $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
1152  $editPageButton = $this->buttonBar->makeLinkButton()
1153  ->setHref($url)
1154  ->setTitle($lang->getLL('editPageProperties'))
1155  ->setIcon($this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL));
1156  $this->buttonBar->addButton($editPageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1157  }
1158 
1159  // Add CSH (Context Sensitive Help) icon to tool bar
1160  $contextSensitiveHelpButton = $this->buttonBar->makeHelpButton()
1161  ->setModuleName($this->descrTable)
1162  ->setFieldName(($function === 'quickEdit' ? 'quickEdit' : 'columns_' . $this->MOD_SETTINGS['function']));
1163  $this->buttonBar->addButton($contextSensitiveHelpButton);
1164 
1165  // QuickEdit
1166  if ($function == 'quickEdit') {
1167  // Close Record
1168  $closeButton = $this->buttonBar->makeLinkButton()
1169  ->setHref('#')
1170  ->setOnClick('jumpToUrl(' . GeneralUtility::quoteJSvalue($this->closeUrl) . '); return false;')
1171  ->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.closeDoc'))
1172  ->setIcon($this->iconFactory->getIcon('actions-document-close', Icon::SIZE_SMALL));
1173  $this->buttonBar->addButton($closeButton, ButtonBar::BUTTON_POSITION_LEFT, 0);
1174 
1175  // Save Record
1176  $saveButtonDropdown = $this->buttonBar->makeSplitButton();
1177  $saveButton = $this->buttonBar->makeInputButton()
1178  ->setName('_savedok')
1179  ->setValue('1')
1180  ->setForm('PageLayoutController')
1181  ->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDoc'))
1182  ->setIcon($this->iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL));
1183  $saveButtonDropdown->addItem($saveButton);
1184  $saveAndCloseButton = $this->buttonBar->makeInputButton()
1185  ->setName('_saveandclosedok')
1186  ->setValue('1')
1187  ->setForm('PageLayoutController')
1188  ->setOnClick('document.editform.redirect.value=\'' . $this->closeUrl . '\';')
1189  ->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveCloseDoc'))
1190  ->setIcon($this->iconFactory->getIcon('actions-document-save-close', Icon::SIZE_SMALL));
1191  $saveButtonDropdown->addItem($saveAndCloseButton);
1192  $saveAndShowPageButton = $this->buttonBar->makeInputButton()
1193  ->setName('_savedokview')
1194  ->setValue('1')
1195  ->setForm('PageLayoutController')
1196  ->setOnClick('document.editform.redirect.value+=\'&popView=1\';')
1197  ->setTitle($lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDocShow'))
1198  ->setIcon($this->iconFactory->getIcon('actions-document-save-view', Icon::SIZE_SMALL));
1199  $saveButtonDropdown->addItem($saveAndShowPageButton);
1200  $this->buttonBar->addButton($saveButtonDropdown, ButtonBar::BUTTON_POSITION_LEFT, 1);
1201 
1202  // Delete record
1203  if ($this->deleteButton) {
1204  $dataAttributes = [];
1205  $dataAttributes['table'] = $this->eRParts[0];
1206  $dataAttributes['uid'] = $this->eRParts[1];
1207  $dataAttributes['return-url'] = BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id;
1208  $deleteButton = $this->buttonBar->makeLinkButton()
1209  ->setHref('#')
1210  ->setClasses('t3js-editform-delete-record')
1211  ->setDataAttributes($dataAttributes)
1212  ->setTitle($lang->getLL('deleteItem'))
1213  ->setIcon($this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL));
1214  $this->buttonBar->addButton($deleteButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
1215  }
1216 
1217  // History
1218  if ($this->undoButton) {
1219  $undoButton = $this->buttonBar->makeLinkButton()
1220  ->setHref('#')
1221  ->setOnClick('window.location.href=' .
1223  BackendUtility::getModuleUrl(
1224  'record_history',
1225  [
1226  'element' => $this->eRParts[0] . ':' . $this->eRParts[1],
1227  'revert' => 'ALL_FIELDS',
1228  'sumUp' => -1,
1229  'returnUrl' => $this->R_URI,
1230  ]
1231  )
1232  ) . '; return false;')
1233  ->setTitle(sprintf($lang->getLL('undoLastChange'), BackendUtility::calcAge($GLOBALS['EXEC_TIME'] - $this->undoButtonR['tstamp'], $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears'))))
1234  ->setIcon($this->iconFactory->getIcon('actions-edit-undo', Icon::SIZE_SMALL));
1235  $this->buttonBar->addButton($undoButton, ButtonBar::BUTTON_POSITION_LEFT, 5);
1236  $historyButton = $this->buttonBar->makeLinkButton()
1237  ->setHref('#')
1238  ->setOnClick('jumpToUrl(' .
1240  BackendUtility::getModuleUrl(
1241  'record_history',
1242  [
1243  'element' => $this->eRParts[0] . ':' . $this->eRParts[1],
1244  'returnUrl' => $this->R_URI,
1245  ]
1246  ) . '#latest'
1247  ) . ');return false;')
1248  ->setTitle($lang->getLL('recordHistory'))
1249  ->setIcon($this->iconFactory->getIcon('actions-document-history-open', Icon::SIZE_SMALL));
1250  $this->buttonBar->addButton($historyButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1251  }
1252  }
1253  }
1254  }
1255 
1256  /*******************************
1257  *
1258  * Other functions
1259  *
1260  ******************************/
1267  public function getNumberOfHiddenElements()
1268  {
1269  return $this->getDatabaseConnection()->exec_SELECTcountRows(
1270  'uid',
1271  'tt_content',
1272  'pid=' . (int)$this->id . ' AND sys_language_uid=' . (int)$this->current_sys_language . BackendUtility::BEenableFields('tt_content', 1) . BackendUtility::deleteClause('tt_content') . BackendUtility::versioningPlaceholderClause('tt_content')
1273  );
1274  }
1275 
1284  {
1285  $params['popView'] = '';
1286  $params['new_unique_uid'] = '';
1288  }
1289 
1296  public function exec_languageQuery($id)
1297  {
1298  if ($id) {
1299  $exQ = BackendUtility::deleteClause('pages_language_overlay') .
1300  ($this->getBackendUser()->isAdmin() ? '' : ' AND sys_language.hidden=0');
1301  return $this->getDatabaseConnection()->exec_SELECTquery(
1302  'sys_language.*',
1303  'pages_language_overlay,sys_language',
1304  'pages_language_overlay.sys_language_uid=sys_language.uid AND pages_language_overlay.pid=' . (int)$id . $exQ .
1305  BackendUtility::versioningPlaceholderClause('pages_language_overlay'),
1306  'pages_language_overlay.sys_language_uid,sys_language.uid,sys_language.pid,sys_language.tstamp,sys_language.hidden,sys_language.title,sys_language.language_isocode,sys_language.static_lang_isocode,sys_language.flag',
1307  'sys_language.title'
1308  );
1309  } else {
1310  return $this->getDatabaseConnection()->exec_SELECTquery(
1311  'sys_language.*',
1312  'sys_language',
1313  'sys_language.hidden=0',
1314  '',
1315  'sys_language.title'
1316  );
1317  }
1318  }
1319 
1327  public function isColumnEmpty($colPos, $languageId)
1328  {
1329  foreach ($this->contentElementCache[$languageId][$colPos] as $uid => $row) {
1330  if ((int)$row['l18n_parent'] === 0) {
1331  return false;
1332  }
1333  }
1334  return true;
1335  }
1336 
1345  public function getElementsFromColumnAndLanguage($pageId, $colPos, $languageId)
1346  {
1347  if (!isset($this->contentElementCache[$languageId][$colPos])) {
1348  $languageId = (int)$languageId;
1349  $whereClause = 'tt_content.pid=' . (int)$pageId . ' AND tt_content.colPos=' . (int)$colPos . ' AND tt_content.sys_language_uid=' . $languageId . BackendUtility::deleteClause('tt_content');
1350  if ($languageId > 0) {
1351  $whereClause .= ' AND tt_content.l18n_parent=0 AND sys_language.uid=' . $languageId . ($this->getBackendUser()->isAdmin() ? '' : ' AND sys_language.hidden=0');
1352  }
1353 
1354  $databaseConnection = $this->getDatabaseConnection();
1355  $res = $databaseConnection->exec_SELECTquery(
1356  'tt_content.uid',
1357  'tt_content,sys_language',
1358  $whereClause
1359  );
1360  while ($row = $databaseConnection->sql_fetch_assoc($res)) {
1361  $this->contentElementCache[$languageId][$colPos][$row['uid']] = $row;
1362  }
1363  $databaseConnection->sql_free_result($res);
1364  }
1365  if (is_array($this->contentElementCache[$languageId][$colPos])) {
1366  return array_keys($this->contentElementCache[$languageId][$colPos]);
1367  }
1368  return [];
1369  }
1370 
1376  public function pageIsNotLockedForEditors()
1377  {
1378  return $this->getBackendUser()->isAdmin() || ($this->CALC_PERMS & Permission::PAGE_EDIT) === Permission::PAGE_EDIT && !$this->pageinfo['editlock'];
1379  }
1380 
1387  {
1388  return $this->getBackendUser()->isAdmin() || ($this->CALC_PERMS & Permission::CONTENT_EDIT) === Permission::CONTENT_EDIT && !$this->pageinfo['editlock'];
1389  }
1390 
1396  protected function getLanguageService()
1397  {
1398  return $GLOBALS['LANG'];
1399  }
1400 
1406  protected function getBackendUser()
1407  {
1408  return $GLOBALS['BE_USER'];
1409  }
1410 
1416  protected function getDatabaseConnection()
1417  {
1418  return $GLOBALS['TYPO3_DB'];
1419  }
1420 
1426  protected function getPageRenderer()
1427  {
1428  return GeneralUtility::makeInstance(PageRenderer::class);
1429  }
1430 
1436  protected function makeQuickEditMenu($edit_record)
1437  {
1438  $lang = $this->getLanguageService();
1439  $databaseConnection = $this->getDatabaseConnection();
1440  $beUser = $this->getBackendUser();
1441 
1442  $quickEditMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
1443  $quickEditMenu->setIdentifier('quickEditMenu');
1444  $quickEditMenu->setLabel('');
1445 
1446  // Setting close url/return url for exiting this script:
1447  // Goes to 'Columns' view if close is pressed (default)
1448  $this->closeUrl = $this->local_linkThisScript(['SET' => ['function' => 1]]);
1449  if ($this->returnUrl) {
1450  $this->closeUrl = $this->returnUrl;
1451  }
1452  $retUrlStr = $this->returnUrl ? '&returnUrl=' . rawurlencode($this->returnUrl) : '';
1453 
1454  // Creating the selector box, allowing the user to select which element to edit:
1455  $isSelected = 0;
1456  $languageOverlayRecord = '';
1457  if ($this->current_sys_language) {
1458  list($languageOverlayRecord) = BackendUtility::getRecordsByField(
1459  'pages_language_overlay',
1460  'pid',
1461  $this->id,
1462  'AND sys_language_uid=' . (int)$this->current_sys_language
1463  );
1464  }
1465  if (is_array($languageOverlayRecord)) {
1466  $inValue = 'pages_language_overlay:' . $languageOverlayRecord['uid'];
1467  $isSelected += (int)$edit_record == $inValue;
1468  $menuItem = $quickEditMenu->makeMenuItem()
1469  ->setTitle('[ ' . $lang->getLL('editLanguageHeader', true) . ' ]')
1470  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=' . $inValue . $retUrlStr)
1471  ->setActive($edit_record == $inValue);
1472  $quickEditMenu->addMenuItem($menuItem);
1473  } else {
1474  $inValue = 'pages:' . $this->id;
1475  $isSelected += (int)$edit_record == $inValue;
1476  $menuItem = $quickEditMenu->makeMenuItem()
1477  ->setTitle('[ ' . $lang->getLL('editPageProperties', true) . ' ]')
1478  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=' . $inValue . $retUrlStr)
1479  ->setActive($edit_record == $inValue);
1480  $quickEditMenu->addMenuItem($menuItem);
1481  }
1482  // Selecting all content elements from this language and allowed colPos:
1483  $whereClause = 'pid=' . (int)$this->id . ' AND sys_language_uid=' . (int)$this->current_sys_language . ' AND colPos IN (' . $this->colPosList . ')' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? '' : BackendUtility::BEenableFields('tt_content')) . BackendUtility::deleteClause('tt_content') . BackendUtility::versioningPlaceholderClause('tt_content');
1484  if (!$this->getBackendUser()->user['admin']) {
1485  $whereClause .= ' AND editlock = 0';
1486  }
1487  $res = $databaseConnection->exec_SELECTquery('*', 'tt_content', $whereClause, '', 'colPos,sorting');
1488  $colPos = null;
1489  $first = 1;
1490  // Page is the pid if no record to put this after.
1491  $prev = $this->id;
1492  while ($cRow = $databaseConnection->sql_fetch_assoc($res)) {
1493  BackendUtility::workspaceOL('tt_content', $cRow);
1494  if (is_array($cRow)) {
1495  if ($first) {
1496  if (!$edit_record) {
1497  $edit_record = 'tt_content:' . $cRow['uid'];
1498  }
1499  $first = 0;
1500  }
1501  if (!isset($colPos) || $cRow['colPos'] !== $colPos) {
1502  $colPos = $cRow['colPos'];
1503  $menuItem = $quickEditMenu->makeMenuItem()
1504  ->setTitle(' ')
1505  ->setHref('#');
1506  $quickEditMenu->addMenuItem($menuItem);
1507  $menuItem = $quickEditMenu->makeMenuItem()
1508  ->setTitle('__' . $lang->sL(BackendUtility::getLabelFromItemlist('tt_content', 'colPos', $colPos)) . ':__')
1509  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=_EDIT_COL:' . $colPos . $retUrlStr);
1510  $quickEditMenu->addMenuItem($menuItem);
1511  }
1512  $inValue = 'tt_content:' . $cRow['uid'];
1513  $isSelected += (int)$edit_record == $inValue;
1514  $menuItem = $quickEditMenu->makeMenuItem()
1515  ->setTitle(GeneralUtility::fixed_lgd_cs(($cRow['header'] ? $cRow['header'] : '[' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.no_title') . '] ' . strip_tags($cRow['bodytext'])), $beUser->uc['titleLen']))
1516  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=' . $inValue . $retUrlStr)
1517  ->setActive($edit_record == $inValue);
1518  $quickEditMenu->addMenuItem($menuItem);
1519  $prev = -$cRow['uid'];
1520  }
1521  }
1522  // If edit_record is not set (meaning, no content elements was found for this language) we simply set it to create a new element:
1523  if (!$edit_record) {
1524  $edit_record = 'tt_content:new/' . $prev . '/' . $colPos;
1525  $inValue = 'tt_content:new/' . $prev . '/' . $colPos;
1526  $isSelected += (int)$edit_record == $inValue;
1527  $menuItem = $quickEditMenu->makeMenuItem()
1528  ->setTitle('[ ' . $lang->getLL('newLabel', 1) . ' ]')
1529  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=' . $inValue . $retUrlStr)
1530  ->setActive($edit_record == $inValue);
1531  $quickEditMenu->addMenuItem($menuItem);
1532  }
1533  // If none is yet selected...
1534  if (!$isSelected) {
1535  $menuItem = $quickEditMenu->makeMenuItem()
1536  ->setTitle('__________')
1537  ->setHref('#');
1538  $quickEditMenu->addMenuItem($menuItem);
1539  $menuItem = $quickEditMenu->makeMenuItem()
1540  ->setTitle('[ ' . $lang->getLL('newLabel', true) . ' ]')
1541  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&edit_record=' . $edit_record . $retUrlStr)
1542  ->setActive($edit_record == $inValue);
1543  $quickEditMenu->addMenuItem($menuItem);
1544  }
1545  $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($quickEditMenu);
1546  return $edit_record;
1547  }
1548 
1554  protected function makeLanguageMenu()
1555  {
1556  if (count($this->MOD_MENU['language']) > 1) {
1557  $lang = $this->getLanguageService();
1558  $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
1559  $languageMenu->setIdentifier('languageMenu');
1560  $languageMenu->setLabel($lang->sL('LLL:EXT:lang/locallang_general.xlf:LGL.language', true));
1561  foreach ($this->MOD_MENU['language'] as $key => $language) {
1562  $menuItem = $languageMenu
1563  ->makeMenuItem()
1564  ->setTitle($language)
1565  ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&SET[language]=' . $key);
1566  if ((int)$this->current_sys_language === $key) {
1567  $menuItem->setActive(true);
1568  }
1569  $languageMenu->addMenuItem($menuItem);
1570  }
1571  $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
1572  }
1573  }
1574 
1580  protected function currentPageHasSubPages()
1581  {
1582  $count = $this->getDatabaseConnection()->exec_SELECTcountRows(
1583  'uid',
1584  'pages',
1585  'pid = ' . (int)$this->id
1586  . BackendUtility::deleteClause('pages')
1589  );
1590 
1591  return $count > 0;
1592  }
1593 }
static getFuncCheck($mainParams, $elementName, $currentValue, $script='', $addParams='', $tagParams='')
static getWorkspaceWhereClause($table, $workspaceId=null)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
static readPageAccess($id, $perms_clause)
static getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields=' *')
getElementsFromColumnAndLanguage($pageId, $colPos, $languageId)
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
static getRecordsByField($theTable, $theField, $theValue, $whereClause='', $groupBy='', $orderBy='', $limit='', $useDeleteClause=true)
static BEenableFields($table, $inv=false)
static BEgetRootLine($uid, $clause='', $workspaceOL=false)
static lockRecords($table='', $uid=0, $pid=0)
static workspaceOL($table, &$row, $wsid=-99, $unsetMovePointers=false)
static linkThisScript(array $getParams=[])
static callUserFunction($funcName, &$params, &$ref, $checkPrefix='', $errorMode=0)
static calcAge($seconds, $labels=' min|hrs|days|yrs|min|hour|day|year')
static implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=false, $rawurlencodeParamName=false)
static getModuleData($MOD_MENU, $CHANGED_SETTINGS, $modName, $type='', $dontValidateList='', $setDefaultList='')
mainAction(ServerRequestInterface $request, ResponseInterface $response)
static cshItem($table, $field, $_='', $wrap='')
static getRecordTitle($table, $row, $prep=false, $forceResult=true)
static getFuncMenu($mainParams, $elementName, $currentValue, $menuItems, $script='', $addParams='')
static unsetMenuItems($modTSconfig, $itemArray, $TSref)
static redirect($url, $httpStatus=self::HTTP_STATUS_303)
Definition: HttpUtility.php:76
static viewOnClick($pageUid, $backPath='', $rootLine=null, $anchorSection='', $alternativeUrl='', $additionalGetVars='', $switchFocus=true)
static fixed_lgd_cs($string, $chars, $appendString='...')
$uid
Definition: server.php:38
static getFileAbsFileName($filename, $onlyRelative=true, $relToTYPO3_mainDir=false)
static getRecord($table, $uid, $fields=' *', $where='', $useDeleteClause=true)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static deleteClause($table, $tableAlias='')
static getLabelFromItemlist($table, $col, $key)