‪TYPO3CMS  11.5
DatabaseRecordList.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Psr\EventDispatcher\EventDispatcherInterface;
26 use TYPO3\CMS\Backend\Utility\BackendUtility;
32 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
53 
59 {
60  // *********
61  // External:
62  // *********
63 
70  public ‪$allowedNewTables = [];
71 
78  public ‪$deniedNewTables = [];
79 
85  public ‪$showClipboardActions = false;
86 
92  public ‪$noControlPanels = false;
93 
99  public ‪$clickMenuEnabled = true;
100 
106  protected string ‪$spaceIcon;
107 
113  public ‪$disableSingleTableView = false;
114 
115  // *********
116  // Internal:
117  // *********
118 
124  public ‪$pageRow = [];
125 
131  public ‪$hideTranslations = '';
132 
138  protected array ‪$recPath_cache = [];
139 
145  public ‪$sortField;
146 
153  protected array ‪$moduleData = [];
154 
160  public ‪$id;
161 
167  protected array ‪$duplicateStack = [];
168 
174  public ‪$listOnlyInSingleTableMode = false;
175 
177 
181  public ‪$modTSconfig;
182 
188  protected array ‪$addElement_tdCssClass = [];
189 
195  public ‪$currentTable = [];
196 
202  public int ‪$showLimit = 0;
203 
209  public ‪$fieldArray = [];
210 
216  public ‪$hideTables = '';
217 
223  public ‪$perms_clause = '';
224 
230  public ‪$returnUrl = '';
231 
237  public ‪$table = '';
238 
245 
251  public ‪$clickTitleMode = '';
252 
258  protected int ‪$searchLevels = 0;
259 
265  public ‪$tableTSconfigOverTCA = [];
266 
272  public ‪$setFields = [];
273 
279  protected ‪$page = 0;
280 
286  public ‪$searchString = '';
287 
293  public ‪$sortRev;
294 
301 
307  public ‪$tableList = '';
308 
314  public ‪$clipObj;
315 
321  public ‪$CBnames = [];
322 
329  public bool ‪$displayColumnSelector = true;
330 
337  public bool ‪$displayRecordDownload = true;
338 
344  protected array ‪$referenceCount = [];
345 
351  protected bool ‪$editable = true;
353  protected ‪UriBuilder ‪$uriBuilder;
354 
365  protected array ‪$tableDisplayOrder = [];
366 
372  protected array ‪$overridePageIdList = [];
373 
378  protected array ‪$overrideUrlParameters = [];
379 
385  protected array ‪$currentLink = [];
386 
392  protected bool ‪$showOnlyTranslatedRecords = false;
393 
404  protected array ‪$possibleTranslations = [];
405 
413  protected array ‪$languagesAllowedForUser = [];
414 
419  protected array ‪$pagePermsCache = [];
420 
425  protected array ‪$backendUserCache = [];
426 
427  protected array ‪$showLocalizeColumn = [];
428 
429  protected EventDispatcherInterface ‪$eventDispatcher;
430 
431  public function ‪__construct(
435  EventDispatcherInterface ‪$eventDispatcher
436  ) {
437  $this->iconFactory = ‪$iconFactory;
438  $this->uriBuilder = ‪$uriBuilder;
439  $this->translateTools = ‪$translateTools;
440  $this->eventDispatcher = ‪$eventDispatcher;
441  $this->calcPerms = new ‪Permission();
442  $this->spaceIcon = '<span class="btn btn-default disabled" aria-hidden="true">' . $this->iconFactory->getIcon('empty-empty', ‪Icon::SIZE_SMALL)->render() . '</span>';
443  }
444 
453  public function ‪getColumnsToRender(string ‪$table, bool $includeMetaColumns): array
454  {
455  $titleCol = ‪$GLOBALS['TCA'][‪$table]['ctrl']['label'] ?? '';
456 
457  // Setting fields selected in columnSelectorBox (saved in uc)
458  $rowListArray = [];
459  if (is_array($this->setFields[‪$table] ?? null)) {
460  $rowListArray = BackendUtility::getAllowedFieldsForTable(‪$table);
461  if ($includeMetaColumns) {
462  $rowListArray[] = '_PATH_';
463  $rowListArray[] = '_REF_';
464  }
465  $rowListArray = array_intersect($rowListArray, $this->setFields[‪$table]);
466  }
467  // if no columns have been specified, show description (if configured)
468  if (!empty(‪$GLOBALS['TCA'][‪$table]['ctrl']['descriptionColumn']) && empty($rowListArray)) {
469  $rowListArray[] = ‪$GLOBALS['TCA'][‪$table]['ctrl']['descriptionColumn'];
470  }
471 
472  // Initialize columns to select
473  $columnsToSelect = [$titleCol];
474  if ($includeMetaColumns) {
475  // If meta columns are enabled, add the record icon
476  array_unshift($columnsToSelect, 'icon');
477 
478  if ($this->noControlPanels === false) {
479  // Add _SELECTOR_ as first item in case control panels are not disabled
480  array_unshift($columnsToSelect, '_SELECTOR_');
481 
482  // Control-Panel
483  $columnsToSelect[] = '_CONTROL_';
484  }
485  // Path
486  if (!in_array('_PATH_', $rowListArray, true) && $this->searchLevels) {
487  $columnsToSelect[] = '_PATH_';
488  }
489  // Localization
490  if (BackendUtility::isTableLocalizable(‪$table)) {
491  $columnsToSelect[] = '_LOCALIZATION_';
492  // Do not show the "Localize to:" field when only translated records should be shown
493  if (!$this->‪showOnlyTranslatedRecords) {
494  $columnsToSelect[] = '_LOCALIZATION_b';
495  }
496  }
497  }
498  return array_unique(array_merge($columnsToSelect, $rowListArray));
499  }
500 
509  public function ‪getFieldsToSelect(string ‪$table, array $columnsToRender): array
510  {
511  $selectFields = $columnsToRender;
512  $selectFields[] = 'uid';
513  $selectFields[] = 'pid';
514  if (‪$table === 'pages') {
515  $selectFields[] = 'module';
516  $selectFields[] = 'extendToSubpages';
517  $selectFields[] = 'nav_hide';
518  $selectFields[] = 'doktype';
519  $selectFields[] = 'shortcut';
520  $selectFields[] = 'shortcut_mode';
521  $selectFields[] = 'mount_pid';
522  }
523  if (is_array(‪$GLOBALS['TCA'][‪$table]['ctrl']['enablecolumns'] ?? null)) {
524  $selectFields = array_merge($selectFields, array_values(‪$GLOBALS['TCA'][‪$table]['ctrl']['enablecolumns']));
525  }
526  foreach (['type', 'typeicon_column', 'editlock'] as $field) {
527  if (‪$GLOBALS['TCA'][‪$table]['ctrl'][$field] ?? false) {
528  $selectFields[] = ‪$GLOBALS['TCA'][‪$table]['ctrl'][$field];
529  }
530  }
531  if (BackendUtility::isTableWorkspaceEnabled(‪$table)) {
532  $selectFields[] = 't3ver_state';
533  $selectFields[] = 't3ver_wsid';
534  $selectFields[] = 't3ver_oid';
535  }
536  if (BackendUtility::isTableLocalizable(‪$table)) {
537  $selectFields[] = ‪$GLOBALS['TCA'][‪$table]['ctrl']['languageField'];
538  $selectFields[] = ‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'];
539  }
540  if (‪$GLOBALS['TCA'][‪$table]['ctrl']['label_alt'] ?? false) {
541  $selectFields = array_merge(
542  $selectFields,
543  ‪GeneralUtility::trimExplode(',', ‪$GLOBALS['TCA'][‪$table]['ctrl']['label_alt'], true)
544  );
545  }
546  // Unique list!
547  $selectFields = array_unique($selectFields);
548  $fieldListFields = BackendUtility::getAllowedFieldsForTable(‪$table, false);
549  // Making sure that the fields in the field-list ARE in the field-list from TCA!
550  return array_intersect($selectFields, $fieldListFields);
551  }
552 
561  public function ‪getTable(‪$table, ‪$id)
562  {
563  // Finding the total amount of records on the page
564  $queryBuilderTotalItems = $this->‪getQueryBuilder($table, ‪$id, [], ['*'], false, 0, 1);
565  $queryBuilderTotalItems->resetQueryPart('orderBy');
566  $totalItems = (int)$queryBuilderTotalItems
567  ->count('*')
568  ->executeQuery()
569  ->fetchOne();
570  if ($totalItems === 0) {
571  return '';
572  }
573  // set the limits
574  // Use default value and overwrite with page ts config and tca config depending on the current view
575  // Force limit in range 5, 10000
576  // default 100
577  $itemsLimitSingleTable = ‪MathUtility::forceIntegerInRange((int)(
578  ‪$GLOBALS['TCA'][‪$table]['interface']['maxSingleDBListItems'] ??
579  $this->modTSconfig['itemsLimitSingleTable'] ??
580  100
581  ), 5, 10000);
582 
583  // default 20
584  $itemsLimitPerTable = ‪MathUtility::forceIntegerInRange((int)(
585  ‪$GLOBALS['TCA'][‪$table]['interface']['maxDBListItems'] ??
586  $this->modTSconfig['itemsLimitPerTable'] ??
587  20
588  ), 5, 10000);
589 
590  // Set limit depending on the view (single table vs. default)
591  $itemsPerPage = $this->table ? $itemsLimitSingleTable : $itemsLimitPerTable;
592 
593  // Set limit defined by calling code
594  if ($this->showLimit) {
595  $itemsPerPage = ‪$this->showLimit;
596  }
597 
598  // Set limit from search
599  if ($this->searchString) {
600  $itemsPerPage = $totalItems;
601  }
602 
603  // Init
604  $titleCol = ‪$GLOBALS['TCA'][‪$table]['ctrl']['label'];
605  $l10nEnabled = BackendUtility::isTableLocalizable(‪$table);
606 
607  $this->fieldArray = $this->‪getColumnsToRender($table, true);
608  // Creating the list of fields to include in the SQL query
609  $selectFields = $this->‪getFieldsToSelect($table, $this->fieldArray);
610 
611  $firstElement = ($this->page - 1) * $itemsPerPage;
612  if ($firstElement > 2 && $itemsPerPage > 0) {
613  // Get the two previous rows for sorting if displaying page > 1
614  $firstElement -= 2;
615  $itemsPerPage += 2;
616  $queryBuilder = $this->‪getQueryBuilder($table, ‪$id, [], $selectFields, true, $firstElement, $itemsPerPage);
617  $firstElement += 2;
618  $itemsPerPage -= 2;
619  } else {
620  $queryBuilder = $this->‪getQueryBuilder($table, ‪$id, [], $selectFields, true, $firstElement, $itemsPerPage);
621  }
622 
623  $queryResult = $queryBuilder->executeQuery();
624  $columnsOutput = '';
625  $onlyShowRecordsInSingleTableMode = $this->listOnlyInSingleTableMode && !‪$this->table;
626  // Fetch records only if not in single table mode
627  if ($onlyShowRecordsInSingleTableMode) {
628  $dbCount = $totalItems;
629  } elseif ($firstElement + $itemsPerPage <= $totalItems) {
630  $dbCount = $itemsPerPage + 2;
631  } else {
632  $dbCount = $totalItems - $firstElement + 2;
633  }
634  // If any records was selected, render the list:
635  if ($dbCount === 0) {
636  return '';
637  }
638 
639  // Get configuration of collapsed tables from user uc
640  $lang = $this->‪getLanguageService();
641 
642  $tableIdentifier = ‪$table;
643  // Use a custom table title for translated pages
644  if (‪$table === 'pages' && $this->‪showOnlyTranslatedRecords) {
645  // pages records in list module are split into two own sections, one for pages with
646  // sys_language_uid = 0 "Page" and an own section for sys_language_uid > 0 "Page Translation".
647  // This if sets the different title for the page translation case and a unique table identifier
648  // which is used in DOM as id.
649  $tableTitle = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:pageTranslation'));
650  $tableIdentifier = 'pages_translated';
651  } else {
652  $tableTitle = htmlspecialchars($lang->sL(‪$GLOBALS['TCA'][‪$table]['ctrl']['title']));
653  if ($tableTitle === '') {
654  $tableTitle = ‪$table;
655  }
656  }
657 
658  $backendUser = $this->‪getBackendUserAuthentication();
659  $tablesCollapsed = $backendUser->getModuleData('list') ?? [];
660  $tableCollapsed = (bool)($tablesCollapsed[$tableIdentifier] ?? false);
661 
662  // Header line is drawn
663  $theData = [];
664  if ($this->disableSingleTableView) {
665  $theData[$titleCol] = BackendUtility::wrapInHelp(‪$table, '', $tableTitle) . ' (<span class="t3js-table-total-items">' . $totalItems . '</span>)';
666  } else {
667  $icon = $this->table // @todo separate table header from contract/expand link
668  ? '<span title="' . htmlspecialchars($lang->getLL('contractView')) . '">' . $this->iconFactory->getIcon('actions-view-table-collapse', ‪Icon::SIZE_SMALL)->render() . '</span>'
669  : '<span title="' . htmlspecialchars($lang->getLL('expandView')) . '">' . $this->iconFactory->getIcon('actions-view-table-expand', ‪Icon::SIZE_SMALL)->render() . '</span>';
670  $theData[$titleCol] = $this->‪linkWrapTable($table, $tableTitle . ' (<span class="t3js-table-total-items">' . $totalItems . '</span>) ' . $icon);
671  }
672  $tableActions = '';
673  if ($onlyShowRecordsInSingleTableMode) {
674  $tableHeader = BackendUtility::wrapInHelp(‪$table, '', $theData[$titleCol]);
675  } else {
676  $tableHeader = $theData[$titleCol];
677  // Add the "new record" button
678  $tableActions .= $this->‪createNewRecordButton($table);
679  // Render collapse button if in multi table mode
680  if (!$this->table) {
681  $title = sprintf(htmlspecialchars($lang->getLL('collapseExpandTable')), $tableTitle);
682  $icon = '<span class="collapseIcon">' . $this->iconFactory->getIcon(($tableCollapsed ? 'actions-view-list-expand' : 'actions-view-list-collapse'), ‪Icon::SIZE_SMALL)->render() . '</span>';
683  $tableActions .= '<button type="button"'
684  . ' class="btn btn-default btn-sm float-end t3js-toggle-recordlist"'
685  . ' title="' . $title . '"'
686  . ' aria-label="' . $title . '"'
687  . ' aria-expanded="' . ($tableCollapsed ? 'false' : 'true') . '"'
688  . ' data-table="' . htmlspecialchars($tableIdentifier) . '"'
689  . ' data-bs-toggle="collapse"'
690  . ' data-bs-target="#recordlist-' . htmlspecialchars($tableIdentifier) . '">'
691  . $icon
692  . '</button>';
693  }
694  // Show the select box
695  $tableActions .= $this->‪columnSelector($table);
696  // Create the Download button
697  $tableActions .= $this->‪createDownloadButtonForTable($table, $totalItems);
698  }
699  $currentIdList = [];
700  // Render table rows only if in multi table view or if in single table view
701  $rowOutput = '';
702  if (!$onlyShowRecordsInSingleTableMode || $this->table) {
703  // Fixing an order table for sortby tables
704  $this->currentTable = [];
705  $allowManualSorting = (‪$GLOBALS['TCA'][‪$table]['ctrl']['sortby'] ?? false) && !$this->sortField;
706  $prevUid = 0;
707  $prevPrevUid = 0;
708  // Get first two rows and initialize prevPrevUid and prevUid if on page > 1
709  if ($firstElement > 2 && $itemsPerPage > 0) {
710  $row = $queryResult->fetchAssociative();
711  $prevPrevUid = -((int)$row['uid']);
712  $row = $queryResult->fetchAssociative();
713  $prevUid = $row['uid'];
714  }
715  $accRows = [];
716  // Accumulate rows here
717  while ($row = $queryResult->fetchAssociative()) {
718  if (!$this->‪isRowListingConditionFulfilled($table, $row)) {
719  continue;
720  }
721  // In offline workspace, look for alternative record
722  BackendUtility::workspaceOL(‪$table, $row, $backendUser->workspace, true);
723  if (is_array($row)) {
724  $accRows[] = $row;
725  $currentIdList[] = $row['uid'];
726  if ($allowManualSorting) {
727  if ($prevUid) {
728  $this->currentTable['prev'][$row['uid']] = $prevPrevUid;
729  $this->currentTable['next'][$prevUid] = '-' . $row['uid'];
730  $this->currentTable['prevUid'][$row['uid']] = $prevUid;
731  }
732  $prevPrevUid = isset($this->currentTable['prev'][$row['uid']]) ? -$prevUid : $row['pid'];
733  $prevUid = $row['uid'];
734  }
735  }
736  }
737  // Render items:
738  $this->CBnames = [];
739  $this->duplicateStack = [];
740  $cc = 0;
741 
742  // If no search happened it means that the selected
743  // records are either default or All language and here we will not select translations
744  // which point to the main record:
745  $listTranslatedRecords = $l10nEnabled && $this->searchString === '' && !($this->hideTranslations === '*' || GeneralUtility::inList($this->hideTranslations, ‪$table));
746  foreach ($accRows as $row) {
747  // Render item row if counter < limit
748  if ($cc < $itemsPerPage) {
749  $cc++;
750  // Reset translations
751  $translations = [];
752  // Initialize with FALSE which causes the localization panel to not be displayed as
753  // the record is already localized, in free mode or has sys_language_uid -1 set.
754  // Only set to TRUE if TranslationConfigurationProvider::translationInfo() returns
755  // an array indicating the record can be translated.
756  $translationEnabled = false;
757  // Guard clause so we can quickly return if a record is localized to "all languages"
758  // It should only be possible to localize a record off default (uid 0)
759  if ($l10nEnabled && ($row[‪$GLOBALS['TCA'][‪$table]['ctrl']['languageField'] ?? null] ?? false) !== -1) {
760  $translationsRaw = $this->translateTools->translationInfo(‪$table, $row['uid'], 0, $row, $selectFields);
761  if (is_array($translationsRaw)) {
762  $translationEnabled = true;
763  $translations = $translationsRaw['translations'] ?? [];
764  }
765  }
766  $rowOutput .= $this->‪renderListRow($table, $row, 0, $translations, $translationEnabled);
767  if ($listTranslatedRecords) {
768  foreach ($translations ?? [] as $lRow) {
769  if (!$this->‪isRowListingConditionFulfilled($table, $lRow)) {
770  continue;
771  }
772  // In offline workspace, look for alternative record:
773  BackendUtility::workspaceOL(‪$table, $lRow, $backendUser->workspace, true);
774  if (is_array($lRow) && $backendUser->checkLanguageAccess($lRow[‪$GLOBALS['TCA'][‪$table]['ctrl']['languageField']])) {
775  $currentIdList[] = $lRow['uid'];
776  $rowOutput .= $this->‪renderListRow($table, $lRow, 18, [], false);
777  }
778  }
779  }
780  }
781  }
782  // Record navigation is added to the beginning and end of the table if in single table mode
783  if ($this->table) {
784  $pagination = $this->‪renderListNavigation($this->table, $totalItems, $itemsPerPage);
785  $rowOutput = $pagination . $rowOutput . $pagination;
786  } elseif ($totalItems > $itemsLimitPerTable) {
787  // Show that there are more records than shown
788  $rowOutput .= '
789  <tr>
790  <td colspan="' . (count($this->fieldArray)) . '">
791  <a href="' . htmlspecialchars($this->‪listURL() . '&table=' . rawurlencode($tableIdentifier)) . '" class="btn btn-default">
792  ' . $this->iconFactory->getIcon('actions-caret-down', ‪Icon::SIZE_SMALL)->render() . '
793  ' . $this->‪getLanguageService()->‪sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.expandTable') . '
794  </a>
795  </td>
796  </tr>';
797  }
798  // The header row for the table is now created
799  $columnsOutput = $this->‪renderListHeader($table, $currentIdList);
800  }
801 
802  // Initialize multi record selection actions
803  $multiRecordSelectionActions = '';
804  if ($this->noControlPanels === false) {
805  $multiRecordSelectionActions = '
806  <div class="col t3js-multi-record-selection-actions hidden">
807  <div class="row row-cols-auto align-items-center g-2">
808  <div class="col">
809  <strong>
810  ' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.selection')) . '
811  </strong>
812  </div>
813  ' . $this->‪renderMultiRecordSelectionActions($table, $currentIdList) . '
814  </div>
815  </div>
816  ';
817  }
818 
819  $collapseClass = $tableCollapsed && !$this->table ? 'collapse' : 'collapse show';
820  $dataState = $tableCollapsed && !$this->table ? 'collapsed' : 'expanded';
821  return '
822  <div class="recordlist mb-5 mt-4 border" id="t3-table-' . htmlspecialchars($tableIdentifier) . '" data-multi-record-selection-identifier="t3-table-' . htmlspecialchars($tableIdentifier) . '">
823  <form action="' . htmlspecialchars($this->‪listURL()) . '#t3-table-' . htmlspecialchars($tableIdentifier) . '" method="post" name="list-table-form-' . htmlspecialchars($tableIdentifier) . '">
824  <input type="hidden" name="cmd_table" value="' . htmlspecialchars($tableIdentifier) . '" />
825  <input type="hidden" name="cmd" />
826  <div class="recordlist-heading row m-0 p-2 g-0 gap-1 align-items-center ' . ($multiRecordSelectionActions !== '' ? 'multi-record-selection-panel' : '') . '">
827  ' . $multiRecordSelectionActions . '
828  <div class="col ms-2">
829  <span class="text-truncate">
830  ' . $tableHeader . '
831  </span>
832  </div>
833  <div class="col-auto">
834  ' . $tableActions . '
835  </div>
836  </div>
837  <div class="' . $collapseClass . '" data-state="' . $dataState . '" id="recordlist-' . htmlspecialchars($tableIdentifier) . '">
838  <div class="table-fit mb-0">
839  <table data-table="' . htmlspecialchars($tableIdentifier) . '" class="table table-striped table-hover mb-0">
840  <thead>
841  ' . $columnsOutput . '
842  </thead>
843  <tbody data-multi-record-selection-row-selection="true">
844  ' . $rowOutput . '
845  </tbody>
846  </table>
847  </div>
848  </div>
849  </form>
850  </div>
851  ';
852  }
853 
860  protected function ‪createNewRecordButton(string ‪$table): string
861  {
862  if (!$this->‪isEditable($table)) {
863  return '';
864  }
865  if (!$this->‪showNewRecLink($table)) {
866  return '';
867  }
868  $permsAdditional = (‪$table === 'pages' ? ‪Permission::PAGE_NEW : ‪Permission::CONTENT_EDIT);
869  if (!$this->calcPerms->isGranted($permsAdditional)) {
870  return '';
871  }
872 
873  $iconIdentifier = 'actions-add';
874  $title = $this->‪getLanguageService()->‪getLL('new');
875  $attributes = [
876  'title' => $title,
877  'aria-label' => $title,
878  'class' => 'btn btn-default btn-sm',
879  ];
880 
881  if (‪$table === 'tt_content') {
882  $url = (string)$this->uriBuilder->buildUriFromRoute(
883  BackendUtility::getPagesTSconfig($this->id)['mod.']['newContentElementWizard.']['override'] ?? 'new_content_element_wizard',
884  [
885  'id' => ‪$this->id,
886  'returnUrl' => $this->‪listURL(),
887  ]
888  );
889  return '
890  <div class="btn-group me-2">
891  <typo3-backend-new-content-element-wizard-button url="' . htmlspecialchars($url) . '" title="' . htmlspecialchars($title) . '">
892  <button type="button" ' . GeneralUtility::implodeAttributes($attributes, true) . '>
893  ' . $this->iconFactory->getIcon($iconIdentifier, ‪Icon::SIZE_SMALL)->render() . '
894  ' . htmlspecialchars($title) . '
895  </button>
896  </typo3-backend-new-content-element-wizard-button>
897  </div>';
898  }
899 
900  if (‪$table === 'pages') {
901  $iconIdentifier = 'actions-page-new';
902  $attributes['data-new'] = 'page';
903  $attributes['href'] = (string)$this->uriBuilder->buildUriFromRoute(
904  'db_new_pages',
905  ['id' => $this->id, 'returnUrl' => $this->listURL()]
906  );
907  } else {
908  $attributes['href'] = $this->uriBuilder->buildUriFromRoute(
909  'record_edit',
910  [
911  'edit' => [
912  ‪$table => [
913  $this->id => 'new',
914  ],
915  ],
916  'returnUrl' => $this->‪listURL(),
917  ]
918  );
919  }
920 
921  return '
922  <div class="btn-group me-2">
923  <a ' . GeneralUtility::implodeAttributes($attributes, true) . '>
924  ' . $this->iconFactory->getIcon($iconIdentifier, ‪Icon::SIZE_SMALL)->render() . '
925  ' . htmlspecialchars($title) . '
926  </a>
927  </div>';
928  }
929 
930  protected function ‪createDownloadButtonForTable(string ‪$table, int $totalItems): string
931  {
932  // Do not render the download button for page translations or in case it is disabled
933  if (!$this->displayRecordDownload
934  || ($this->modTSconfig['noExportRecordsLinks'] ?? false)
936  ) {
937  return '';
938  }
939 
940  $downloadButtonLabel = $this->‪getLanguageService()->‪sL('LLL:EXT:recordlist/Resources/Private/Language/locallang_download.xlf:download');
941  $downloadButtonTitle = sprintf($this->‪getLanguageService()->sL('LLL:EXT:recordlist/Resources/Private/Language/locallang_download.xlf:' . ($totalItems === 1 ? 'downloadRecord' : 'downloadRecords')), $totalItems);
942  $downloadCancelTitle = $this->‪getLanguageService()->‪sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.cancel');
943  $downloadSettingsUrl = $this->uriBuilder->buildUriFromRoute(
944  'ajax_record_download_settings',
945  ['id' => $this->id, 'table' => $table, 'searchString' => $this->searchString, 'searchLevels' => $this->searchLevels]
946  );
947  $downloadSettingsTitle = sprintf(
948  $this->‪getLanguageService()->sL('LLL:EXT:recordlist/Resources/Private/Language/locallang_download.xlf:' . ($totalItems === 1 ? 'downloadRecordSettings' : 'downloadRecordsSettings')),
949  $this->‪getLanguageService()->sL(‪$GLOBALS['TCA'][‪$table]['ctrl']['title'] ?? '') ?: ‪$table,
950  $totalItems
951  );
952 
953  return '
954  <div class="float-end">
955  <typo3-recordlist-record-download-button
956  url="' . htmlspecialchars($downloadSettingsUrl) . '"
957  title="' . htmlspecialchars($downloadSettingsTitle) . '"
958  ok="' . htmlspecialchars($downloadButtonTitle) . '"
959  close="' . htmlspecialchars($downloadCancelTitle) . '"
960  >
961  <button type="button" class="btn btn-default btn-sm me-2" title="' . htmlspecialchars($downloadButtonTitle) . '">' .
962  $this->iconFactory->getIcon('actions-database-export', ‪Icon::SIZE_SMALL) . ' ' .
963  htmlspecialchars($downloadButtonLabel) .
964  '</button>
965  </typo3-recordlist-record-download-button>
966  </div>';
967  }
968 
977  protected function ‪getPreviewUriBuilder(string ‪$table, array $row): ‪PreviewUriBuilder
978  {
979  if (‪$table === 'tt_content') {
980  // Link to a content element, possibly translated and with anchor
981  $additionalParams = '';
982  $language = $row[‪$GLOBALS['TCA']['tt_content']['ctrl']['languageField'] ?? null] ?? 0;
983  if ($language > 0) {
984  $additionalParams = '&L=' . $language;
985  }
986  $previewUriBuilder = ‪PreviewUriBuilder::create((int)$this->id)
987  ->withSection('#c' . $row['uid'])
988  ->withAdditionalQueryParameters($additionalParams);
989  } elseif (‪$table === 'pages' && ($row[‪$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'] ?? null] ?? 0) > 0) {
990  // Link to a page translation needs uid of default language page as id
991  $languageParentId = $row[‪$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']] ?? 0;
992  $language = $row[‪$GLOBALS['TCA']['pages']['ctrl']['languageField']] ?? 0;
993  $previewUriBuilder = ‪PreviewUriBuilder::create($languageParentId)
994  ->withSection('#c' . $row['uid'])
995  ->withAdditionalQueryParameters('&L=' . $language);
996  } else {
997  // Link to a page in the default language
998  $previewUriBuilder = ‪PreviewUriBuilder::create((int)($row['uid'] ?? 0));
999  }
1000  return $previewUriBuilder;
1001  }
1002 
1012  protected function ‪isRowListingConditionFulfilled(‪$table, $row)
1013  {
1014  return true;
1015  }
1016 
1029  public function ‪renderListRow(‪$table, array $row, int $indent, array $translations, bool $translationEnabled)
1030  {
1031  $titleCol = ‪$GLOBALS['TCA'][‪$table]['ctrl']['label'] ?? '';
1032  $languageService = $this->‪getLanguageService();
1033  $rowOutput = '';
1034  $id_orig = ‪$this->id;
1035  // If in search mode, make sure the preview will show the correct page
1036  if ((string)$this->searchString !== '') {
1037  $this->id = $row['pid'];
1038  }
1039 
1040  $tagAttributes = [
1041  'class' => [],
1042  'data-table' => ‪$table,
1043  'title' => 'id=' . $row['uid'],
1044  ];
1045 
1046  // Add active class to record of current link
1047  if (
1048  isset($this->currentLink['tableNames'])
1049  && (int)$this->currentLink['uid'] === (int)$row['uid']
1050  && GeneralUtility::inList($this->currentLink['tableNames'], ‪$table)
1051  ) {
1052  $tagAttributes['class'][] = 'active';
1053  }
1054  // Overriding with versions background color if any:
1055  if (!empty($row['_CSSCLASS'])) {
1056  $tagAttributes['class'] = [$row['_CSSCLASS']];
1057  }
1058 
1059  $tagAttributes['class'][] = 't3js-entity';
1060 
1061  // Preparing and getting the data-array
1062  $theData = [];
1063  $deletePlaceholderClass = '';
1064  foreach ($this->fieldArray as $fCol) {
1065  if ($fCol === $titleCol) {
1066  $recTitle = BackendUtility::getRecordTitle(‪$table, $row, false, true);
1067  $warning = '';
1068  // If the record is edit-locked by another user, we will show a little warning sign:
1069  $lockInfo = BackendUtility::isRecordLocked(‪$table, $row['uid']);
1070  if ($lockInfo) {
1071  $warning = '<span tabindex="0" data-bs-toggle="tooltip" data-bs-placement="right"'
1072  . ' title="' . htmlspecialchars($lockInfo['msg']) . '"'
1073  . ' aria-label="' . htmlspecialchars($lockInfo['msg']) . '">'
1074  . $this->iconFactory->getIcon('warning-in-use', ‪Icon::SIZE_SMALL)->render()
1075  . '</span>';
1076  }
1077  if ($this->‪isRecordDeletePlaceholder($row)) {
1078  // Delete placeholder records do not link to formEngine edit and are rendered strike-through
1079  $deletePlaceholderClass = ' deletePlaceholder';
1080  $theData[$fCol] = $theData['__label'] =
1081  $warning
1082  . '<span title="' . htmlspecialchars($languageService->sL('LLL:EXT:recordlist/Resources/Private/Language/locallang.xlf:row.deletePlaceholder.title')) . '">'
1083  . htmlspecialchars($recTitle)
1084  . '</span>';
1085  } else {
1086  $theData[$fCol] = $theData['__label'] = $warning . $this->‪linkWrapItems($table, $row['uid'], $recTitle, $row);
1087  }
1088  } elseif ($fCol === 'pid') {
1089  $theData[$fCol] = $row[$fCol];
1090  } elseif ($fCol !== '' && $fCol === (‪$GLOBALS['TCA'][‪$table]['ctrl']['cruser_id'] ?? '')) {
1091  $theData[$fCol] = $this->‪getBackendUserInformation((int)$row[$fCol]);
1092  } elseif ($fCol === '_SELECTOR_') {
1093  if (‪$table !== 'pages' || !$this->‪showOnlyTranslatedRecords) {
1094  // Add checkbox for all tables except the special page translations table
1095  $theData[$fCol] = $this->‪makeCheckbox($table, $row);
1096  } else {
1097  // Remove "_SELECTOR_", which is always the first item, from the field list
1098  array_splice($this->fieldArray, 0, 1);
1099  }
1100  } elseif ($fCol === 'icon') {
1101  $iconImg = '
1102  <span ' . BackendUtility::getRecordToolTip($row, ‪$table) . ' ' . ($indent ? ' style="margin-left: ' . $indent . 'px;"' : '') . '>
1103  ' . $this->iconFactory->getIconForRecord(‪$table, $row, ‪Icon::SIZE_SMALL)->render() . '
1104  </span>';
1105  $theData[$fCol] = ($this->clickMenuEnabled && !$this->‪isRecordDeletePlaceholder($row)) ? BackendUtility::wrapClickMenuOnIcon($iconImg, ‪$table, $row['uid']) : $iconImg;
1106  } elseif ($fCol === '_PATH_') {
1107  $theData[$fCol] = $this->‪recPath($row['pid']);
1108  } elseif ($fCol === '_REF_') {
1109  $theData[$fCol] = $this->‪generateReferenceToolTip($table, $row['uid']);
1110  } elseif ($fCol === '_CONTROL_') {
1111  $theData[$fCol] = $this->‪makeControl($table, $row);
1112  } elseif ($fCol === '_LOCALIZATION_') {
1113  // Language flag an title
1114  $theData[$fCol] = $this->‪languageFlag($table, $row);
1115  // Localize record
1116  $localizationPanel = $translationEnabled ? $this->‪makeLocalizationPanel($table, $row, $translations) : '';
1117  if ($localizationPanel !== '') {
1118  $theData['_LOCALIZATION_b'] = '<div class="btn-group">' . $localizationPanel . '</div>';
1119  $this->showLocalizeColumn[‪$table] = true;
1120  }
1121  } elseif ($fCol !== '_LOCALIZATION_b') {
1122  // default for all other columns, except "_LOCALIZATION_b"
1123  $pageId = ‪$table === 'pages' ? $row['uid'] : $row['pid'];
1124  $tmpProc = BackendUtility::getProcessedValueExtra(‪$table, $fCol, $row[$fCol], 100, $row['uid'], true, $pageId);
1125  $theData[$fCol] = $this->‪linkUrlMail(htmlspecialchars((string)$tmpProc), (string)($row[$fCol] ?? ''));
1126  }
1127  }
1128  // Reset the ID if it was overwritten
1129  if ((string)$this->searchString !== '') {
1130  $this->id = $id_orig;
1131  }
1132  // Add classes to table cells
1133  $this->addElement_tdCssClass['_SELECTOR_'] = 'col-selector';
1134  $this->addElement_tdCssClass[$titleCol] = 'col-title col-responsive' . $deletePlaceholderClass;
1135  $this->addElement_tdCssClass['__label'] = $this->addElement_tdCssClass[$titleCol];
1136  $this->addElement_tdCssClass['icon'] = 'col-icon';
1137  $this->addElement_tdCssClass['_CONTROL_'] = 'col-control';
1138  $this->addElement_tdCssClass['_PATH_'] = 'col-path';
1139  $this->addElement_tdCssClass['_LOCALIZATION_'] = 'col-localizationa';
1140  $this->addElement_tdCssClass['_LOCALIZATION_b'] = 'col-localizationb';
1141  // Create element in table cells:
1142  $theData['uid'] = $row['uid'];
1143  if (isset(‪$GLOBALS['TCA'][‪$table]['ctrl']['languageField'])
1144  && isset(‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'])
1145  ) {
1146  $theData['_l10nparent_'] = $row[‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField']];
1147  }
1148 
1149  $tagAttributes = array_map(
1150  static function ($attributeValue) {
1151  if (is_array($attributeValue)) {
1152  return implode(' ', $attributeValue);
1153  }
1154  return $attributeValue;
1155  },
1156  $tagAttributes
1157  );
1158 
1159  $rowOutput .= $this->‪addElement($theData, GeneralUtility::implodeAttributes($tagAttributes, true));
1160  // Finally, return table row element:
1161  return $rowOutput;
1162  }
1172  protected function ‪getReferenceCount($tableName, $uid)
1173  {
1174  if (!isset($this->referenceCount[$tableName][$uid])) {
1175  $referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
1176  $numberOfReferences = $referenceIndex->getNumberOfReferencedRecords($tableName, $uid);
1177  $this->referenceCount[$tableName][$uid] = $numberOfReferences;
1178  }
1179  return $this->referenceCount[$tableName][$uid];
1180  }
1181 
1192  public function ‪renderListHeader(‪$table, $currentIdList)
1193  {
1194  $tsConfig = BackendUtility::getPagesTSconfig($this->id)['TCEFORM.'][$table . '.'] ?? null;
1195  $tsConfigOfTable = is_array($tsConfig) ? $tsConfig : null;
1196 
1197  $lang = $this->‪getLanguageService();
1198  // Init:
1199  $theData = [];
1200  // Traverse the fields:
1201  foreach ($this->fieldArray as $fCol) {
1202  // Calculate users permissions to edit records in the table:
1203  if (‪$table === 'pages') {
1204  $permsEdit = $this->calcPerms->editPagePermissionIsGranted();
1205  } else {
1206  $permsEdit = $this->calcPerms->editContentPermissionIsGranted();
1207  }
1208 
1209  $permsEdit = $permsEdit && $this->‪overlayEditLockPermissions($table);
1210  switch ((string)$fCol) {
1211  case '_SELECTOR_':
1212  if (‪$table !== 'pages' || !$this->‪showOnlyTranslatedRecords) {
1213  // Add checkbox actions for all tables except the special page translations table
1214  $theData[$fCol] = $this->‪renderCheckboxActions();
1215  } else {
1216  // Remove "_SELECTOR_", which is always the first item, from the field list
1217  array_splice($this->fieldArray, 0, 1);
1218  }
1219  break;
1220  case 'icon':
1221  // In case no checkboxes are rendered (page translations or disabled) add the icon
1222  // column, otherwise the selector column is using "colspan=2"
1223  if (!in_array('_SELECTOR_', $this->fieldArray, true)
1224  || (‪$table === 'pages' && $this->‪showOnlyTranslatedRecords)
1225  ) {
1226  $theData[$fCol] = '';
1227  }
1228  break;
1229  case '_CONTROL_':
1230  $theData[$fCol] = '<i>[' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels._CONTROL_')) . ']</i>';
1231  // In single table view, add button to edit displayed fields of marked / listed records
1232  if ($this->table && $permsEdit && is_array($currentIdList) && $this->‪isEditable($table)) {
1233  $theData[$fCol] = '<button type="button"'
1234  . ' class="btn btn-default t3js-record-edit-multiple"'
1235  . ' title="' . htmlspecialchars($lang->getLL('editShownColumns')) . '"'
1236  . ' aria-label="' . htmlspecialchars($lang->getLL('editShownColumns')) . '"'
1237  . ' data-return-url="' . htmlspecialchars($this->‪listURL()) . '"'
1238  . ' data-columns-only="' . htmlspecialchars(implode(',', $this->fieldArray)) . '">'
1239  . $this->iconFactory->getIcon('actions-document-open', ‪Icon::SIZE_SMALL)->render()
1240  . '</button>';
1241  }
1242  break;
1243  case '_PATH_':
1244  // Path
1245  $theData[$fCol] = '<i>[' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels._PATH_')) . ']</i>';
1246  break;
1247  case '_REF_':
1248  // References
1249  $theData[$fCol] = '<i>[' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels._REF_')) . ']</i>';
1250  break;
1251  case '_LOCALIZATION_':
1252  // Show language of record
1253  $theData[$fCol] = '<i>[' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels._LOCALIZATION_')) . ']</i>';
1254  break;
1255  case '_LOCALIZATION_b':
1256  // Show translation options
1257  if ($this->showLocalizeColumn[‪$table] ?? false) {
1258  $theData[$fCol] = htmlspecialchars($lang->getLL('Localize'));
1259  }
1260  break;
1261  default:
1262  // Regular fields header:
1263  $theData[$fCol] = '';
1264 
1265  // Check if $fCol is really a field and get the label and remove the colons
1266  // at the end
1267  $sortLabel = BackendUtility::getItemLabel(‪$table, $fCol);
1268  if ($sortLabel !== null) {
1269  $sortLabel = rtrim(trim($lang->sL($sortLabel)), ':');
1270 
1271  // Field label
1272  $fieldTSConfig = [];
1273  if (isset($tsConfigOfTable[$fCol . '.'])
1274  && is_array($tsConfigOfTable[$fCol . '.'])
1275  ) {
1276  $fieldTSConfig = $tsConfigOfTable[$fCol . '.'];
1277  }
1278  if (!empty($fieldTSConfig['label'])) {
1279  $sortLabel = $lang->sL($fieldTSConfig['label']);
1280  }
1281  if (!empty($fieldTSConfig['label.'][$lang->lang])) {
1282  $sortLabel = $lang->sL($fieldTSConfig['label.'][$lang->lang]);
1283  }
1284  $sortLabel = htmlspecialchars($sortLabel);
1285  } elseif ($specialLabel = $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.' . $fCol)) {
1286  // Special label exists for this field (Probably a management field, e.g. sorting)
1287  $sortLabel = htmlspecialchars($specialLabel);
1288  } else {
1289  // No TCA field, only output the $fCol variable with square brackets []
1290  $sortLabel = htmlspecialchars($fCol);
1291  $sortLabel = '<i>[' . rtrim(trim($sortLabel), ':') . ']</i>';
1292  }
1293 
1294  if ($this->table && is_array($currentIdList)) {
1295  // If the numeric clipboard pads are selected, show duplicate sorting link:
1296  if ($this->noControlPanels === false
1297  && $this->‪isClipboardFunctionalityEnabled($table)
1298  && $this->clipObj->current !== 'normal'
1299  ) {
1300  $theData[$fCol] .= '<a class="btn btn-default" href="' . htmlspecialchars($this->‪listURL() . '&duplicateField=' . $fCol)
1301  . '" title="' . htmlspecialchars($lang->getLL('clip_duplicates')) . '">'
1302  . $this->iconFactory->getIcon('actions-document-duplicates-select', ‪Icon::SIZE_SMALL)->render() . '</a>';
1303  }
1304  // If the table can be edited, add link for editing THIS field for all
1305  // listed records:
1306  if ($this->‪isEditable($table) && $permsEdit && (‪$GLOBALS['TCA'][‪$table]['columns'][$fCol] ?? false)) {
1307  $iTitle = sprintf($lang->getLL('editThisColumn'), $sortLabel);
1308  $theData[$fCol] .= '<button type="button"'
1309  . ' class="btn btn-default t3js-record-edit-multiple"'
1310  . ' title="' . htmlspecialchars($iTitle) . '"'
1311  . ' aria-label="' . htmlspecialchars($iTitle) . '"'
1312  . ' data-return-url="' . htmlspecialchars($this->‪listURL()) . '"'
1313  . ' data-columns-only="' . htmlspecialchars($fCol) . '">'
1314  . $this->iconFactory->getIcon('actions-document-open', ‪Icon::SIZE_SMALL)->render()
1315  . '</button>';
1316  }
1317  if (strlen($theData[$fCol]) > 0) {
1318  $theData[$fCol] = '<div class="btn-group">' . $theData[$fCol] . '</div> ';
1319  }
1320  }
1321  $theData[$fCol] .= $this->‪addSortLink($sortLabel, $fCol, ‪$table);
1322  }
1323  }
1324 
1325  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'])) {
1326  trigger_error(
1327  'The hook $TYPO3_CONF_VARS[SC_OPTIONS][typo3/class.db_list_extra.inc][actions] for calling method "renderListHeader" is deprecated and will stop working in TYPO3 v12.0. Use the ModifyRecordListHeaderColumnsEvent instead.',
1328  E_USER_DEPRECATED
1329  );
1330  }
1331 
1332  /*
1333  * hook: renderListHeader: Allows to change the contents of columns/cells of the Web>List table headers
1334  * usage: Above each listed table in Web>List a header row is shown.
1335  * Containing the labels of all shown fields and additional icons to create new records for this
1336  * table or perform special clipboard tasks like mark and copy all listed records to clipboard, etc.
1337  *
1338  * @deprecated in v11, will be removed in TYPO3 v12.0.
1339  */
1340  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'] ?? [] as $className) {
1341  $hookObject = GeneralUtility::makeInstance($className);
1342  if (!$hookObject instanceof ‪RecordListHookInterface) {
1343  throw new \UnexpectedValueException($className . ' must implement interface ' . RecordListHookInterface::class, 1195567855);
1344  }
1345  $theData = $hookObject->renderListHeader(‪$table, $currentIdList, $theData, $this);
1346  }
1347 
1348  $event = $this->eventDispatcher->dispatch(
1349  new ‪ModifyRecordListHeaderColumnsEvent($theData, ‪$table, $currentIdList, $this)
1350  );
1351 
1352  // Create and return header table row:
1353  return $this->‪addElement($event->getColumns(), GeneralUtility::implodeAttributes($event->getHeaderAttributes(), true), 'th');
1354  }
1355 
1364  protected function ‪renderListNavigation(string ‪$table, int $totalItems, int $itemsPerPage): string
1365  {
1366  $currentPage = ‪$this->page;
1367  $paginationColumns = count($this->fieldArray);
1368  $totalPages = (int)ceil($totalItems / $itemsPerPage);
1369  // Show page selector if not all records fit into one page
1370  if ($totalPages <= 1) {
1371  return '';
1372  }
1373  if ($totalItems > $currentPage * $itemsPerPage) {
1374  $lastElementNumber = $currentPage * $itemsPerPage;
1375  } else {
1376  $lastElementNumber = $totalItems;
1377  }
1378  return $this->‪getFluidTemplateObject('ListNavigation.html')
1379  ->‪assignMultiple([
1380  'currentUrl' => $this->‪listURL('', $table, 'pointer'),
1381  'currentPage' => $currentPage,
1382  'totalPages' => $totalPages,
1383  'firstElement' => ((($currentPage - 1) * $itemsPerPage) + 1),
1384  'lastElement' => $lastElementNumber,
1385  'colspan' => $paginationColumns,
1386  ])
1387  ->render();
1388  }
1389 
1390  /*********************************
1391  *
1392  * Rendering of various elements
1393  *
1394  *********************************/
1395 
1404  public function ‪makeControl(‪$table, $row)
1405  {
1406  $backendUser = $this->‪getBackendUserAuthentication();
1407  $userTsConfig = $backendUser->getTSConfig();
1408  $rowUid = $row['uid'];
1409  if (‪ExtensionManagementUtility::isLoaded('workspaces') && isset($row['_ORIG_uid'])) {
1410  $rowUid = $row['_ORIG_uid'];
1411  }
1412  $isDeletePlaceHolder = $this->‪isRecordDeletePlaceholder($row);
1413  $cells = [
1414  'primary' => [],
1415  'secondary' => [],
1416  ];
1417 
1418  // Hide the move elements for localized records - doesn't make much sense to perform these options for them
1419  $isL10nOverlay = (int)($row[‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'] ?? null] ?? 0) !== 0;
1420  $localCalcPerms = $this->‪getPagePermissionsForRecord($table, $row);
1421  if (‪$table === 'pages') {
1422  $permsEdit = ($backendUser->checkLanguageAccess($row[‪$GLOBALS['TCA']['pages']['ctrl']['languageField'] ?? null] ?? 0))
1423  && $localCalcPerms->editPagePermissionIsGranted();
1424  } else {
1425  $permsEdit = $localCalcPerms->editContentPermissionIsGranted() && $backendUser->recordEditAccessInternals(‪$table, $row);
1426  }
1427  $permsEdit = $this->‪overlayEditLockPermissions($table, $row, $permsEdit);
1428 
1429  // "Show" link (only pages and tt_content elements)
1430  $tsConfig = BackendUtility::getPagesTSconfig($this->id)['mod.']['web_list.'] ?? [];
1431  if ((
1432  $table === 'pages'
1433  && isset($row['doktype'])
1434  && !in_array((int)$row['doktype'], $this->‪getNoViewWithDokTypes($tsConfig), true)
1435  )
1436  || (
1437  ‪$table === 'tt_content'
1438  && isset($this->pageRow['doktype'])
1439  && !in_array((int)$this->pageRow['doktype'], $this->‪getNoViewWithDokTypes($tsConfig), true)
1440  )
1441  ) {
1442  if (!$isDeletePlaceHolder) {
1443  $attributes = $this->‪getPreviewUriBuilder($table, $row)->‪serializeDispatcherAttributes();
1444  $viewAction = '<a href="#"'
1445  . ' class="btn btn-default" ' . $attributes
1446  . ' title="' . htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage')) . '">';
1447  if (‪$table === 'pages') {
1448  $viewAction .= $this->iconFactory->getIcon('actions-view-page', ‪Icon::SIZE_SMALL)->render();
1449  } else {
1450  $viewAction .= $this->iconFactory->getIcon('actions-view', ‪Icon::SIZE_SMALL)->render();
1451  }
1452  $viewAction .= '</a>';
1453  $this->‪addActionToCellGroup($cells, $viewAction, 'view');
1454  } else {
1455  $this->‪addActionToCellGroup($cells, $this->spaceIcon, 'view');
1456  }
1457  } else {
1458  $this->‪addActionToCellGroup($cells, $this->spaceIcon, 'view');
1459  }
1460 
1461  // "Edit" link: ( Only if permissions to edit the page-record of the content of the parent page ($this->id)
1462  if ($permsEdit && !$isDeletePlaceHolder && $this->‪isEditable($table)) {
1463  $params = [
1464  'edit' => [
1465  ‪$table => [
1466  $row['uid'] => 'edit',
1467  ],
1468  ],
1469  ];
1470  $iconIdentifier = 'actions-open';
1471  if (‪$table === 'pages') {
1472  // Disallow manual adjustment of the language field for pages
1473  $params['overrideVals']['pages']['sys_language_uid'] = $row[‪$GLOBALS['TCA']['pages']['ctrl']['languageField'] ?? null] ?? 0;
1474  $iconIdentifier = 'actions-page-open';
1475  }
1476  $params['returnUrl'] = $this->‪listURL();
1477  $editLink = $this->uriBuilder->buildUriFromRoute('record_edit', $params);
1478  $editAction = '<a class="btn btn-default" href="' . htmlspecialchars($editLink) . '"'
1479  . ' title="' . htmlspecialchars($this->‪getLanguageService()->getLL('edit')) . '">' . $this->iconFactory->getIcon($iconIdentifier, ‪Icon::SIZE_SMALL)->render() . '</a>';
1480  } else {
1481  $editAction = ‪$this->spaceIcon;
1482  }
1483  $this->‪addActionToCellGroup($cells, $editAction, 'edit');
1484 
1485  // "Info"
1486  if (!$isDeletePlaceHolder) {
1487  $viewBigAction = '<button type="button" aria-haspopup="dialog"'
1488  . ' class="btn btn-default" '
1489  . $this->‪createShowItemTagAttributes($table . ',' . ($row['uid'] ?? 0))
1490  . ' title="' . htmlspecialchars($this->‪getLanguageService()->getLL('showInfo')) . '"'
1491  . ' aria-label="' . htmlspecialchars($this->‪getLanguageService()->getLL('showInfo')) . '">'
1492  . $this->iconFactory->getIcon('actions-document-info', ‪Icon::SIZE_SMALL)->render()
1493  . '</button>';
1494  $this->‪addActionToCellGroup($cells, $viewBigAction, 'viewBig');
1495  } else {
1496  $this->‪addActionToCellGroup($cells, $this->spaceIcon, 'viewBig');
1497  }
1498 
1499  // "Move" wizard link for pages/tt_content elements:
1500  if ($permsEdit && (‪$table === 'tt_content' || ‪$table === 'pages') && $this->‪isEditable($table)) {
1501  if ($isL10nOverlay || $isDeletePlaceHolder) {
1502  $moveAction = ‪$this->spaceIcon;
1503  } else {
1504  $linkTitleLL = htmlspecialchars($this->‪getLanguageService()->getLL('move_' . (‪$table === 'tt_content' ? 'record' : 'page')));
1505  $icon = (‪$table === 'pages' ? $this->iconFactory->getIcon('actions-page-move', ‪Icon::SIZE_SMALL) : $this->iconFactory->getIcon('actions-document-move', ‪Icon::SIZE_SMALL));
1506  $url = (string)$this->uriBuilder->buildUriFromRoute('move_element', [
1507  'table' => ‪$table,
1508  'uid' => $row['uid'],
1509  'returnUrl' => $this->listURL(),
1510  ]);
1511  $moveAction = '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" aria-label="' . $linkTitleLL . '">' . $icon->render() . '</a>';
1512  }
1513  $this->‪addActionToCellGroup($cells, $moveAction, 'move');
1514  }
1515 
1516  // If the table is NOT a read-only table, then show these links:
1517  if ($this->‪isEditable($table)) {
1518  // "Revert" link (history/undo)
1519  if (\trim($userTsConfig['options.']['showHistory.'][‪$table] ?? $userTsConfig['options.']['showHistory'] ?? '1')) {
1520  if (!$isDeletePlaceHolder) {
1521  $moduleUrl = $this->uriBuilder->buildUriFromRoute('record_history', [
1522  'element' => ‪$table . ':' . $row['uid'],
1523  'returnUrl' => $this->‪listURL(),
1524  ]) . '#latest';
1525  $historyAction = '<a class="btn btn-default" href="' . htmlspecialchars($moduleUrl) . '" title="'
1526  . htmlspecialchars($this->‪getLanguageService()->getLL('history')) . '">'
1527  . $this->iconFactory->getIcon('actions-document-history-open', ‪Icon::SIZE_SMALL)->render() . '</a>';
1528  $this->‪addActionToCellGroup($cells, $historyAction, 'history');
1529  } else {
1530  $this->‪addActionToCellGroup($cells, $this->spaceIcon, 'history');
1531  }
1532  }
1533 
1534  // "Edit Perms" link:
1535  if (‪$table === 'pages' && $backendUser->check('modules', 'system_BeuserTxPermission') && ‪ExtensionManagementUtility::isLoaded('beuser')) {
1536  if ($isL10nOverlay || $isDeletePlaceHolder) {
1537  $permsAction = ‪$this->spaceIcon;
1538  } else {
1539  $params = [
1540  'id' => $row['uid'],
1541  'action' => 'edit',
1542  'returnUrl' => $this->‪listURL(),
1543  ];
1544  $href = (string)$this->uriBuilder->buildUriFromRoute('system_BeuserTxPermission', $params);
1545  $permsAction = '<a class="btn btn-default" href="' . htmlspecialchars($href) . '" title="'
1546  . htmlspecialchars($this->‪getLanguageService()->getLL('permissions')) . '">'
1547  . $this->iconFactory->getIcon('actions-lock', ‪Icon::SIZE_SMALL)->render() . '</a>';
1548  }
1549  $this->‪addActionToCellGroup($cells, $permsAction, 'perms');
1550  }
1551 
1552  // "New record after" link (ONLY if the records in the table are sorted by a "sortby"-row
1553  // or if default values can depend on previous record):
1554  if (((‪$GLOBALS['TCA'][‪$table]['ctrl']['sortby'] ?? false) || (‪$GLOBALS['TCA'][‪$table]['ctrl']['useColumnsForDefaultValues'] ?? false)) && $permsEdit) {
1555  $neededPermission = ‪$table === 'pages' ? ‪Permission::PAGE_NEW : ‪Permission::CONTENT_EDIT;
1556  if ($this->calcPerms->isGranted($neededPermission)) {
1557  if ($isL10nOverlay || $isDeletePlaceHolder) {
1558  $this->‪addActionToCellGroup($cells, $this->spaceIcon, 'new');
1559  } elseif ($this->‪showNewRecLink($table)) {
1560  $params = [
1561  'edit' => [
1562  ‪$table => [
1563  (0 - (($row['_MOVE_PLH'] ?? 0) ? $row['_MOVE_PLH_uid'] : $row['uid'])) => 'new',
1564  ],
1565  ],
1566  'returnUrl' => $this->‪listURL(),
1567  ];
1568  $icon = (‪$table === 'pages' ? $this->iconFactory->getIcon('actions-page-new', ‪Icon::SIZE_SMALL) : $this->iconFactory->getIcon('actions-add', ‪Icon::SIZE_SMALL));
1569  $titleLabel = 'new';
1570  if (‪$GLOBALS['TCA'][‪$table]['ctrl']['sortby'] ?? false) {
1571  $titleLabel .= (‪$table === 'pages' ? 'Page' : 'Record');
1572  }
1573  $newLink = $this->uriBuilder->buildUriFromRoute('record_edit', $params);
1574  $newAction = '<a class="btn btn-default" href="' . htmlspecialchars($newLink) . '" title="' . htmlspecialchars($this->‪getLanguageService()->getLL($titleLabel)) . '">'
1575  . $icon->render() . '</a>';
1576  $this->‪addActionToCellGroup($cells, $newAction, 'new');
1577  }
1578  }
1579  }
1580 
1581  // "Hide/Unhide" links:
1582  $hiddenField = ‪$GLOBALS['TCA'][‪$table]['ctrl']['enablecolumns']['disabled'] ?? null;
1583  if ($hiddenField !== null
1584  && !empty(‪$GLOBALS['TCA'][‪$table]['columns'][$hiddenField])
1585  && (empty(‪$GLOBALS['TCA'][‪$table]['columns'][$hiddenField]['exclude']) || $backendUser->check('non_exclude_fields', ‪$table . ':' . $hiddenField))
1586  ) {
1587  if (!$permsEdit || $isDeletePlaceHolder || $this->‪isRecordCurrentBackendUser($table, $row)) {
1588  $hideAction = ‪$this->spaceIcon;
1589  } else {
1590  $hideTitle = htmlspecialchars($this->‪getLanguageService()->getLL('hide' . (‪$table === 'pages' ? 'Page' : '')));
1591  $unhideTitle = htmlspecialchars($this->‪getLanguageService()->getLL('unHide' . (‪$table === 'pages' ? 'Page' : '')));
1592  if ($row[$hiddenField] ?? false) {
1593  $params = 'data[' . ‪$table . '][' . $rowUid . '][' . $hiddenField . ']=0';
1594  $hideAction = '<button type="button"'
1595  . ' class="btn btn-default t3js-record-hide"'
1596  . ' data-state="hidden"'
1597  . ' data-params="' . htmlspecialchars($params) . '"'
1598  . ' data-toggle-title="' . $hideTitle . '"'
1599  . ' title="' . $unhideTitle . '">'
1600  . $this->iconFactory->getIcon('actions-edit-unhide', ‪Icon::SIZE_SMALL)->render()
1601  . '</button>';
1602  } else {
1603  $params = 'data[' . ‪$table . '][' . $rowUid . '][' . $hiddenField . ']=1';
1604  $hideAction = '<button type="button"'
1605  . ' class="btn btn-default t3js-record-hide"'
1606  . ' data-state="visible"'
1607  . ' data-params="' . htmlspecialchars($params) . '"'
1608  . ' data-toggle-title="' . $unhideTitle . '"'
1609  . ' title="' . $hideTitle . '">'
1610  . $this->iconFactory->getIcon('actions-edit-hide', ‪Icon::SIZE_SMALL)->render()
1611  . '</button>';
1612  }
1613  }
1614  $this->‪addActionToCellGroup($cells, $hideAction, 'hide');
1615  }
1616 
1617  // "Up/Down" links
1618  if ($permsEdit && (‪$GLOBALS['TCA'][‪$table]['ctrl']['sortby'] ?? false) && !$this->sortField && !$this->searchLevels) {
1619  if (!$isL10nOverlay && !$isDeletePlaceHolder && isset($this->currentTable['prev'][$row['uid']])) {
1620  // Up
1621  $params = [];
1622  $params['redirect'] = $this->‪listURL();
1623  $params['cmd'][‪$table][$row['uid']]['move'] = $this->currentTable['prev'][$row['uid']];
1624  $url = (string)$this->uriBuilder->buildUriFromRoute('tce_db', $params);
1625  $moveUpAction = '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" title="' . htmlspecialchars($this->‪getLanguageService()->getLL('moveUp')) . '">'
1626  . $this->iconFactory->getIcon('actions-move-up', ‪Icon::SIZE_SMALL)->render() . '</a>';
1627  } else {
1628  $moveUpAction = ‪$this->spaceIcon;
1629  }
1630  $this->‪addActionToCellGroup($cells, $moveUpAction, 'moveUp');
1631 
1632  if (!$isL10nOverlay && !$isDeletePlaceHolder && !empty($this->currentTable['next'][$row['uid']])) {
1633  // Down
1634  $params = [];
1635  $params['redirect'] = $this->‪listURL();
1636  $params['cmd'][‪$table][$row['uid']]['move'] = $this->currentTable['next'][$row['uid']];
1637  $url = (string)$this->uriBuilder->buildUriFromRoute('tce_db', $params);
1638  $moveDownAction = '<a class="btn btn-default" href="' . htmlspecialchars($url) . '" title="' . htmlspecialchars($this->‪getLanguageService()->getLL('moveDown')) . '">'
1639  . $this->iconFactory->getIcon('actions-move-down', ‪Icon::SIZE_SMALL)->render() . '</a>';
1640  } else {
1641  $moveDownAction = ‪$this->spaceIcon;
1642  }
1643  $this->‪addActionToCellGroup($cells, $moveDownAction, 'moveDown');
1644  }
1645 
1646  // "Delete" link:
1647  $disableDelete = (bool)\trim((string)($userTsConfig['options.']['disableDelete.'][‪$table] ?? $userTsConfig['options.']['disableDelete'] ?? ''));
1648  if ($permsEdit
1649  && !$disableDelete
1650  && ((‪$table === 'pages' && $localCalcPerms->deletePagePermissionIsGranted()) || (‪$table !== 'pages' && $this->calcPerms->editContentPermissionIsGranted()))
1651  && !$this->‪isRecordCurrentBackendUser($table, $row)
1652  && !$isDeletePlaceHolder
1653  ) {
1654  $actionName = 'delete';
1655  $refCountMsg = BackendUtility::referenceCount(
1656  ‪$table,
1657  $row['uid'],
1658  ' ' . $this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.referencesToRecord'),
1659  (string)$this->‪getReferenceCount($table, $row['uid'])
1660  ) . BackendUtility::translationCount(
1661  ‪$table,
1662  $row['uid'],
1663  ' ' . $this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.translationsOfRecord')
1664  );
1665  $title = BackendUtility::getRecordTitle(‪$table, $row);
1666  $warningText = $this->‪getLanguageService()->‪getLL($actionName . 'Warning') . ' "' . $title . '" [' . ‪$table . ':' . $row['uid'] . ']' . $refCountMsg;
1667  $params = 'cmd[' . ‪$table . '][' . $row['uid'] . '][delete]=1';
1668  $icon = $this->iconFactory->getIcon('actions-edit-' . $actionName, ‪Icon::SIZE_SMALL)->render();
1669  $linkTitle = htmlspecialchars($this->‪getLanguageService()->getLL($actionName));
1670  $l10nParentField = ‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'] ?? '';
1671  $deleteAction = '<button type="button" class="btn btn-default t3js-record-delete"'
1672  . ' title="' . $linkTitle . '"'
1673  . ' aria-label="' . $linkTitle . '"'
1674  . ' aria-haspopup="dialog"'
1675  . ' data-button-ok-text="' . htmlspecialchars($linkTitle) . '"'
1676  . ' data-l10parent="' . ($l10nParentField ? htmlspecialchars((string)$row[$l10nParentField]) : '') . '"'
1677  . ' data-params="' . htmlspecialchars($params) . '"'
1678  . ' data-message="' . htmlspecialchars($warningText) . '">'
1679  . $icon
1680  . '</button>';
1681  } else {
1682  $deleteAction = ‪$this->spaceIcon;
1683  }
1684  $this->‪addActionToCellGroup($cells, $deleteAction, 'delete');
1685 
1686  // "Levels" links: Moving pages into new levels...
1687  if ($permsEdit && ‪$table === 'pages' && !$this->searchLevels) {
1688  // Up (Paste as the page right after the current parent page)
1689  if ($this->calcPerms->createPagePermissionIsGranted()) {
1690  if (!$isDeletePlaceHolder && !$isL10nOverlay) {
1691  $params = [];
1692  $params['redirect'] = $this->‪listURL();
1693  $params['cmd'][‪$table][$row['uid']]['move'] = -‪$this->id;
1694  $url = (string)$this->uriBuilder->buildUriFromRoute('tce_db', $params);
1695  $moveLeftAction = '<a class="btn btn-default"'
1696  . ' href="' . htmlspecialchars($url) . '"'
1697  . ' title="' . htmlspecialchars($this->‪getLanguageService()->getLL('prevLevel')) . '"'
1698  . ' aria-label="' . htmlspecialchars($this->‪getLanguageService()->getLL('prevLevel')) . '">'
1699  . $this->iconFactory->getIcon('actions-move-left', ‪Icon::SIZE_SMALL)->render()
1700  . '</a>';
1701  $this->‪addActionToCellGroup($cells, $moveLeftAction, 'moveLeft');
1702  } else {
1703  $this->‪addActionToCellGroup($cells, $this->spaceIcon, 'moveLeft');
1704  }
1705  }
1706  // Down (Paste as subpage to the page right above)
1707  if (!$isL10nOverlay && !$isDeletePlaceHolder && !empty($this->currentTable['prevUid'][$row['uid']])) {
1708  $localCalcPerms = $this->‪getPagePermissionsForRecord(
1709  'pages',
1710  BackendUtility::getRecord('pages', $this->currentTable['prevUid'][$row['uid']]) ?? []
1711  );
1712  if ($localCalcPerms->createPagePermissionIsGranted()) {
1713  $params = [];
1714  $params['redirect'] = $this->‪listURL();
1715  $params['cmd'][‪$table][$row['uid']]['move'] = $this->currentTable['prevUid'][$row['uid']];
1716  $url = (string)$this->uriBuilder->buildUriFromRoute('tce_db', $params);
1717  $moveRightAction = '<a class="btn btn-default"'
1718  . ' href="' . htmlspecialchars($url) . '"'
1719  . ' title="' . htmlspecialchars($this->‪getLanguageService()->getLL('nextLevel')) . '"'
1720  . ' aria-label="' . htmlspecialchars($this->‪getLanguageService()->getLL('nextLevel')) . '">'
1721  . $this->iconFactory->getIcon('actions-move-right', ‪Icon::SIZE_SMALL)->render() . '</a>';
1722  } else {
1723  $moveRightAction = ‪$this->spaceIcon;
1724  }
1725  } else {
1726  $moveRightAction = ‪$this->spaceIcon;
1727  }
1728  $this->‪addActionToCellGroup($cells, $moveRightAction, 'moveRight');
1729  }
1730  }
1731 
1732  /*
1733  * hook: recStatInfoHooks: Allows to insert HTML before record icons on various places
1734  */
1735  $hooks = ‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['recStatInfoHooks'] ?? [];
1736  if (!empty($hooks)) {
1737  $stat = '';
1738  $_params = [‪$table, $row['uid']];
1739  foreach ($hooks as $_funcRef) {
1740  $stat .= GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1741  }
1742  $this->‪addActionToCellGroup($cells, $stat, 'stat');
1743  }
1744 
1745  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'])) {
1746  trigger_error(
1747  'The hook $TYPO3_CONF_VARS[SC_OPTIONS][typo3/class.db_list_extra.inc][actions] for calling method "makeControl" is deprecated and will stop working in TYPO3 v12.0. Use the ModifyRecordListRecordActionsEvent instead.',
1748  E_USER_DEPRECATED
1749  );
1750  }
1751 
1752  /*
1753  * hook: makeControl: Allows to change control icons of records in list-module
1754  * usage: This hook method gets passed the current $cells array as third parameter.
1755  * This array contains values for the icons/actions generated for each record in Web>List.
1756  * Each array entry is accessible by an index-key.
1757  * The order of the icons is depending on the order of those array entries.
1758  *
1759  * @deprecated in v11, will be removed in TYPO3 v12.0.
1760  */
1761  if (is_array(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'] ?? false)) {
1762  // for compatibility reason, we move all icons to the rootlevel
1763  // before calling the hooks
1764  foreach ($cells as $section => $actions) {
1765  foreach ($actions as $actionKey => $action) {
1766  $cells[$actionKey] = $action;
1767  }
1768  }
1769  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'] as $className) {
1770  $hookObject = GeneralUtility::makeInstance($className);
1771  if (!$hookObject instanceof RecordListHookInterface) {
1772  throw new \UnexpectedValueException($className . ' must implement interface ' . RecordListHookInterface::class, 1195567840);
1773  }
1774  $cells = $hookObject->makeControl(‪$table, $row, $cells, $this);
1775  }
1776  // now sort icons again into primary and secondary sections
1777  // after all hooks are processed
1778  $hookCells = $cells;
1779  foreach ($hookCells as $key => $value) {
1780  if ($key === 'primary' || $key === 'secondary') {
1781  continue;
1782  }
1783  $this->‪addActionToCellGroup($cells, $value, $key);
1784  }
1785  }
1786 
1787  // Add clipboard related actions
1788  $this->‪makeClip($table, $row, $cells);
1789 
1790  $event = $this->eventDispatcher->dispatch(
1791  new ModifyRecordListRecordActionsEvent($cells, ‪$table, $row, $this)
1792  );
1793 
1794  ‪$output = '';
1795  foreach ($event->getActions() as $classification => $actions) {
1796  if ($classification !== 'primary') {
1797  $cellOutput = '';
1798  foreach ($actions as $action) {
1799  if ($action === $this->spaceIcon) {
1800  continue;
1801  }
1802  // This is a backwards-compat layer for the existing hook items, which will be removed in TYPO3 v12.
1803  $action = str_replace('btn btn-default', 'dropdown-item', $action);
1804  $title = [];
1805  preg_match('/title="([^"]*)"/', $action, $title);
1806  if (empty($title)) {
1807  preg_match('/aria-label="([^"]*)"/', $action, $title);
1808  }
1809  if (!empty($title[1] ?? '')) {
1810  $action = str_replace(
1811  [
1812  '</a>',
1813  '</button>',
1814  ],
1815  [
1816  ' ' . $title[1] . '</a>',
1817  ' ' . $title[1] . '</button>',
1818  ],
1819  $action
1820  );
1821  // In case we added the title as tag content, we can remove the attribute,
1822  // since this is duplicated and would trigger a tooltip with the same content.
1823  if (!empty($title[0] ?? '')) {
1824  $action = str_replace($title[0], '', $action);
1825  }
1826  }
1827  $cellOutput .= '<li>' . $action . '</li>';
1828  }
1829 
1830  if ($cellOutput !== '') {
1831  $icon = $this->iconFactory->getIcon('actions-menu-alternative', ‪Icon::SIZE_SMALL);
1832  $title = $this->‪getLanguageService()->‪sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.more');
1833  ‪$output .= ' <div class="btn-group dropdown position-static" data-bs-toggle="tooltip" title="' . htmlspecialchars($title) . '">' .
1834  '<a href="#actions_' . ‪$table . '_' . $row['uid'] . '" class="btn btn-default dropdown-toggle dropdown-toggle-no-chevron" data-bs-toggle="dropdown" data-bs-boundary="window" aria-expanded="false">' . $icon->render() . '</a>' .
1835  '<ul id="actions_' . ‪$table . '_' . $row['uid'] . '" class="dropdown-menu dropdown-list">' . $cellOutput . '</ul>' .
1836  '</div>';
1837  } else {
1838  ‪$output .= ' <div class="btn-group">' . $this->spaceIcon . '</div>';
1839  }
1840  } else {
1841  ‪$output .= ' <div class="btn-group">' . implode('', $actions) . '</div>';
1842  }
1843  }
1844 
1845  return ‪$output;
1846  }
1847 
1855  public function ‪makeClip(string ‪$table, array $row, array &$cells): void
1856  {
1857  // Return, if disabled:
1858  if (!$this->‪isClipboardFunctionalityEnabled($table, $row)) {
1859  return;
1860  }
1861  $clipboardCells = [];
1862  $isEditable = $this->‪isEditable($table);
1863 
1864  if ($this->clipObj->current !== 'normal') {
1865  $clipboardCells['copy'] = $clipboardCells['cut'] = ‪$this->spaceIcon;
1866  } else {
1867  $this->‪addDividerToCellGroup($cells);
1868  $isSel = $this->clipObj->isSelected(‪$table, $row['uid']);
1869 
1870  $copyTitle = $this->‪getLanguageService()->‪sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.' . ($isSel === 'copy' ? 'copyrelease' : 'copy'));
1871  $copyUrl = $this->clipObj->selUrlDB(‪$table, (int)$row['uid'], true, $isSel === 'copy');
1872  $clipboardCells['copy'] = '
1873  <a class="btn btn-default" href="' . htmlspecialchars($copyUrl) . '" title="' . htmlspecialchars($copyTitle) . '" aria-label="' . htmlspecialchars($copyTitle) . '">
1874  ' . $this->iconFactory->getIcon($isSel === 'copy' ? 'actions-edit-copy-release' : 'actions-edit-copy', ‪Icon::SIZE_SMALL)->render() . '
1875  </a>';
1876 
1877  // Calculate permission to cut page or content
1878  if (‪$table === 'pages') {
1879  $localCalcPerms = $this->‪getPagePermissionsForRecord('pages', $row);
1880  $permsEdit = $localCalcPerms->editPagePermissionIsGranted();
1881  } else {
1882  $permsEdit = $this->calcPerms->editContentPermissionIsGranted() && $this->‪getBackendUserAuthentication()->‪recordEditAccessInternals($table, $row);
1883  }
1884  if (!$isEditable || !$this->‪overlayEditLockPermissions($table, $row, $permsEdit)) {
1885  $clipboardCells['cut'] = ‪$this->spaceIcon;
1886  } else {
1887  $cutTitle = $this->‪getLanguageService()->‪sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.' . ($isSel === 'cut' ? 'cutrelease' : 'cut'));
1888  $cutUrl = $this->clipObj->selUrlDB(‪$table, (int)$row['uid'], false, $isSel === 'cut');
1889  $clipboardCells['cut'] = '
1890  <a class="btn btn-default" href="' . htmlspecialchars($cutUrl) . '" title="' . htmlspecialchars($cutTitle) . '" aria-label="' . htmlspecialchars($cutTitle) . '">
1891  ' . $this->iconFactory->getIcon($isSel === 'cut' ? 'actions-edit-cut-release' : 'actions-edit-cut', ‪Icon::SIZE_SMALL)->render() . '
1892  </a>';
1893  }
1894  }
1895 
1896  // Now, looking for selected elements from the current table:
1897  $elFromTable = $this->clipObj->elFromTable(‪$table);
1898  if (!$isEditable
1899  || empty(‪$GLOBALS['TCA'][‪$table]['ctrl']['sortby'])
1900  || $this->clipObj->elFromTable(‪$table) === []
1901  || !$this->overlayEditLockPermissions(‪$table, $row)
1902  ) {
1903  $clipboardCells['pasteAfter'] = ‪$this->spaceIcon;
1904  } else {
1905  $this->‪addDividerToCellGroup($cells);
1906  $pasteAfterUrl = $this->clipObj->pasteUrl(‪$table, -$row['uid']);
1907  $pasteAfterTitle = $this->‪getLanguageService()->‪getLL('clip_pasteAfter');
1908  $pasteAfterContent = $this->clipObj->confirmMsgText(‪$table, $row, 'after', $elFromTable);
1909  $clipboardCells['pasteAfter'] = '
1910  <button type="button" class="btn btn-default t3js-modal-trigger" data-severity="warning" aria-haspopup="dialog" title="' . htmlspecialchars($pasteAfterTitle) . '" aria-label="' . htmlspecialchars($pasteAfterTitle) . '" data-uri="' . htmlspecialchars($pasteAfterUrl) . '" data-bs-content="' . htmlspecialchars($pasteAfterContent) . '">
1911  ' . $this->iconFactory->getIcon('actions-document-paste-after', ‪Icon::SIZE_SMALL)->render() . '
1912  </button>';
1913  }
1914 
1915  // Now, looking for elements in general:
1916  if (‪$table !== 'pages' || !$isEditable || $this->clipObj->elFromTable() === []) {
1917  $clipboardCells['pasteInto'] = ‪$this->spaceIcon;
1918  } else {
1919  $this->‪addDividerToCellGroup($cells);
1920  $pasteIntoUrl = $this->clipObj->pasteUrl('', $row['uid']);
1921  $pasteIntoTitle = $this->‪getLanguageService()->‪getLL('clip_pasteInto');
1922  $pasteIntoContent = $this->clipObj->confirmMsgText(‪$table, $row, 'into', $elFromTable);
1923  $clipboardCells['pasteInto'] = '
1924  <button type="button" class="btn btn-default t3js-modal-trigger" aria-haspopup="dialog" data-severity="warning" title="' . htmlspecialchars($pasteIntoTitle) . '" aria-label="' . htmlspecialchars($pasteIntoTitle) . '" data-uri="' . htmlspecialchars($pasteIntoUrl) . '" data-bs-content="' . htmlspecialchars($pasteIntoContent) . '">
1925  ' . $this->iconFactory->getIcon('actions-document-paste-into', ‪Icon::SIZE_SMALL)->render() . '
1926  </button>';
1927  }
1928 
1929  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'])) {
1930  trigger_error(
1931  'The hook $TYPO3_CONF_VARS[SC_OPTIONS][typo3/class.db_list_extra.inc][actions] for calling method "makeClip" is deprecated and will stop working in TYPO3 v12.0. Use the ModifyRecordListRecordActionsEvent instead.',
1932  E_USER_DEPRECATED
1933  );
1934  }
1935 
1936  /*
1937  * hook: makeClip: Allows to change clip-icons of records in list-module
1938  * usage: This hook method gets passed the current $cells array as third parameter.
1939  * This array contains values for the clipboard icons generated for each record in Web>List.
1940  * Each array entry is accessible by an index-key.
1941  * The order of the icons is depending on the order of those array entries.
1942  *
1943  * @deprecated in v11, will be removed in TYPO3 v12.0.
1944  */
1945  if (is_array(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'] ?? false)) {
1946  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'] as $className) {
1947  $hookObject = GeneralUtility::makeInstance($className);
1948  if (!$hookObject instanceof ‪RecordListHookInterface) {
1949  throw new \UnexpectedValueException($className . ' must implement interface ' . RecordListHookInterface::class, 1195567845);
1950  }
1951  $clipboardCells = $hookObject->makeClip(‪$table, $row, $clipboardCells, $this);
1952  }
1953  }
1954 
1955  // Add the clipboard actions to the cell group
1956  foreach ($clipboardCells as $key => $value) {
1957  if ($key === 'primary' || $key === 'secondary') {
1958  continue;
1959  }
1960  $this->‪addActionToCellGroup($cells, $value, $key);
1961  }
1962  }
1963 
1971  public function ‪makeCheckbox(string ‪$table, array $row): string
1972  {
1973  // Early return if current record is a "delete placeholder" or a translation
1974  if ($this->‪isRecordDeletePlaceholder($row)
1975  || (int)($row[‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'] ?? null] ?? 0) !== 0
1976  ) {
1977  return '';
1978  }
1979 
1980  // In case clipObj is not set, just add a checkbox without any clipboard functionality
1981  if ($this->clipObj === null) {
1982  return '
1983  <span class="form-check form-toggle">
1984  <input class="form-check-input t3js-multi-record-selection-check" type="checkbox" />
1985  </span>';
1986  }
1987 
1988  // For the numeric clipboard pads (showing checkboxes where one can select elements on/off)
1989  // Setting name of the element in ->CBnames array:
1990  $identifier = ‪$table . '|' . $row['uid'];
1991  $this->CBnames[] = $identifier;
1992  $isSelected = false;
1993  // If the "duplicateField" value is set then select all elements which are duplicates...
1994  if ($this->duplicateField && isset($row[$this->duplicateField])) {
1995  $isSelected = in_array((string)$row[$this->duplicateField], $this->duplicateStack, true);
1996  $this->duplicateStack[] = (string)$row[$this->duplicateField];
1997  }
1998  // Adding the checkbox to the panel:
1999  return '
2000  <span class="form-check form-toggle">
2001  <input class="form-check-input t3js-multi-record-selection-check" type="checkbox" name="CBC[' . $identifier . ']" value="1" ' . ($isSelected ? 'checked="checked" ' : '') . '/>
2002  </span>';
2003  }
2004 
2013  public function ‪makeLocalizationPanel(‪$table, $row, array $translations): string
2014  {
2015  $out = '';
2016  // All records excluding pages
2018  if (‪$table === 'pages') {
2019  // Calculate possible translations for pages
2020  ‪$possibleTranslations = array_map(static fn($siteLanguage) => $siteLanguage->getLanguageId(), $this->languagesAllowedForUser);
2021  ‪$possibleTranslations = array_filter(‪$possibleTranslations, static fn($languageUid) => $languageUid > 0);
2022  }
2023 
2024  // Traverse page translations and add icon for each language that does NOT yet exist and is included in site configuration:
2025  $pageId = (int)(‪$table === 'pages' ? $row['uid'] : $row['pid']);
2026  $languageInformation = $this->translateTools->getSystemLanguages($pageId);
2027 
2028  foreach (‪$possibleTranslations as $lUid_OnPage) {
2029  if ($this->‪isEditable($table)
2030  && !$this->‪isRecordDeletePlaceholder($row)
2031  && !isset($translations[$lUid_OnPage])
2032  && $this->‪getBackendUserAuthentication()->‪checkLanguageAccess($lUid_OnPage)
2033  ) {
2034  $redirectUrl = (string)$this->uriBuilder->buildUriFromRoute(
2035  'record_edit',
2036  [
2037  'justLocalized' => ‪$table . ':' . $row['uid'] . ':' . $lUid_OnPage,
2038  'returnUrl' => $this->listURL(),
2039  ]
2040  );
2041  $params = [];
2042  $params['redirect'] = $redirectUrl;
2043  $params['cmd'][‪$table][$row['uid']]['localize'] = $lUid_OnPage;
2044  $href = (string)$this->uriBuilder->buildUriFromRoute('tce_db', $params);
2045  $title = htmlspecialchars($languageInformation[$lUid_OnPage]['title'] ?? '');
2046 
2047  $lC = ($languageInformation[$lUid_OnPage]['flagIcon'] ?? false)
2048  ? $this->iconFactory->getIcon($languageInformation[$lUid_OnPage]['flagIcon'], ‪Icon::SIZE_SMALL)->render()
2049  : $title;
2050 
2051  $out .= '<a href="' . htmlspecialchars($href) . '"'
2052  . '" class="btn btn-default t3js-action-localize"'
2053  . ' title="' . $title . '">'
2054  . $lC . '</a> ';
2055  }
2056  }
2057  return $out;
2058  }
2059 
2066  protected function ‪columnSelector(string ‪$table): string
2067  {
2068  if ($this->displayColumnSelector === false) {
2069  // Early return in case column selector is disabled
2070  return '';
2071  }
2072 
2073  $shouldRenderSelector = true;
2074  // See if it is disabled in general
2075  if (isset($this->modTSconfig['displayColumnSelector'])) {
2076  $shouldRenderSelector = (bool)$this->modTSconfig['displayColumnSelector'];
2077  }
2078  // Table override was explicitly set to false
2079  if (isset($this->modTSconfig['table.'][‪$table . '.']['displayColumnSelector'])) {
2080  $shouldRenderSelector = (bool)$this->modTSconfig['table.'][‪$table . '.']['displayColumnSelector'];
2081  }
2082  // Do not render button if column selector is disabled
2083  if ($shouldRenderSelector === false) {
2084  return '';
2085  }
2086 
2087  $lang = $this->‪getLanguageService();
2088  $tableIdentifier = ‪$table . ((‪$table === 'pages' && ‪$this->showOnlyTranslatedRecords) ? '_translated' : '');
2089  $columnSelectorUrl = $this->uriBuilder->buildUriFromRoute(
2090  'ajax_show_columns_selector',
2091  ['id' => $this->id, 'table' => $table]
2092  );
2093  $columnSelectorTitle = sprintf(
2094  $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_column_selector.xlf:showColumnsSelection'),
2095  $lang->sL(‪$GLOBALS['TCA'][‪$table]['ctrl']['title'] ?? '') ?: ‪$table,
2096  );
2097 
2098  return '
2099  <div class="float-end me-2 p-0">
2100  <typo3-backend-column-selector-button
2101  url="' . htmlspecialchars($columnSelectorUrl) . '"
2102  target="' . htmlspecialchars($this->‪listURL() . '#t3-table-' . $tableIdentifier) . '"
2103  title="' . htmlspecialchars($columnSelectorTitle) . '"
2104  ok="' . htmlspecialchars($lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_column_selector.xlf:updateColumnView')) . '"
2105  close="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.cancel')) . '"
2106  error="' . htmlspecialchars($lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_column_selector.xlf:updateColumnView.error')) . '"
2107  >
2108  <button type="button" class="btn btn-default btn-sm" title="' . htmlspecialchars($columnSelectorTitle) . '">' .
2109  $this->iconFactory->getIcon('actions-options', ‪Icon::SIZE_SMALL) . ' ' .
2110  htmlspecialchars($lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_column_selector.xlf:showColumns')) .
2111  '</button>
2112  </typo3-backend-column-selector-button>
2113  </div>';
2114  }
2115 
2116  /*********************************
2117  *
2118  * Helper functions
2119  *
2120  *********************************/
2121 
2132  public function ‪addSortLink($code, $field, ‪$table)
2133  {
2134  // Certain circumstances just return string right away (no links):
2135  if ($this->disableSingleTableView
2136  || in_array($field, ['_SELECTOR', '_CONTROL_', '_LOCALIZATION_', '_REF_'], true)
2137  ) {
2138  return $code;
2139  }
2140  // If "_PATH_" (showing record path) is selected, force sorting by pid field (will at least group the records!)
2141  if ($field === '_PATH_') {
2142  $field = 'pid';
2143  }
2144  // Create the sort link:
2145  $sortUrl = $this->‪listURL('', $table, 'sortField,sortRev,table,pointer')
2146  . '&sortField=' . $field . '&sortRev=' . ($this->sortRev || $this->sortField != $field ? 0 : 1);
2147  $sortArrow = $this->sortField === $field
2148  ? $this->iconFactory->getIcon('status-status-sorting-' . ($this->sortRev ? 'desc' : 'asc'), ‪Icon::SIZE_SMALL)->render()
2149  : '';
2150  // Return linked field:
2151  return '<a href="' . htmlspecialchars($sortUrl) . '">' . $code . $sortArrow . '</a>';
2152  }
2153 
2162  public function ‪recPath($pid)
2163  {
2164  if (!isset($this->recPath_cache[$pid])) {
2165  $this->recPath_cache[$pid] = BackendUtility::getRecordPath($pid, $this->perms_clause, 20);
2166  }
2167  return $this->recPath_cache[$pid];
2168  }
2169 
2174  protected function ‪getPagePermissionsForRecord(string ‪$table, array $row): Permission
2175  {
2176  // If the listed table is 'pages' we have to request the permission settings for each page.
2177  // If the listed table is not 'pages' we have to request the permission settings from the parent page
2178  $pageId = (int)(‪$table === 'pages' ? ($row['l10n_parent'] ?: $row['uid']) : $row['pid']);
2179  if (!isset($this->pagePermsCache[$pageId])) {
2180  $this->pagePermsCache[$pageId] = new Permission($this->‪getBackendUserAuthentication()->calcPerms(BackendUtility::getRecord('pages', $pageId)));
2181  }
2182  return $this->pagePermsCache[$pageId];
2183  }
2184 
2189  protected function ‪getBackendUserInformation(int $backendUserId): string
2190  {
2191  if (!isset($this->backendUserCache[$backendUserId])) {
2192  $beUserRecord = BackendUtility::getRecord('be_users', $backendUserId);
2193  if (is_array($beUserRecord)) {
2194  $avatar = GeneralUtility::makeInstance(Avatar::class);
2195  $label = htmlspecialchars(BackendUtility::getRecordTitle('be_users', $beUserRecord));
2196  $content = $avatar->render($beUserRecord) . '<strong>' . $label . '</strong>';
2197  } else {
2198  $content = '<strong>&ndash;</strong>';
2199  }
2200  $this->backendUserCache[$backendUserId] = $content;
2201  }
2202  return $this->backendUserCache[$backendUserId];
2203  }
2204 
2211  public function ‪showNewRecLink(‪$table)
2212  {
2213  // No deny/allow tables are set:
2214  if (empty($this->allowedNewTables) && empty($this->deniedNewTables)) {
2215  return true;
2216  }
2217  return !in_array(‪$table, $this->deniedNewTables)
2218  && (empty($this->allowedNewTables) || in_array(‪$table, $this->allowedNewTables));
2219  }
2220 
2228  public function ‪addActionToCellGroup(&$cells, $action, $actionKey)
2229  {
2230  $cellsMap = [
2231  'primary' => [
2232  'edit', 'hide', 'delete', 'moveUp', 'moveDown',
2233  ],
2234  'secondary' => [
2235  'view', 'viewBig', 'history', 'stat', 'perms', 'new', 'move', 'moveLeft', 'moveRight', 'version', 'divider', 'copy', 'cut', 'pasteAfter', 'pasteInto',
2236  ],
2237  ];
2238  $classification = in_array($actionKey, $cellsMap['primary']) ? 'primary' : 'secondary';
2239  $cells[$classification][$actionKey] = $action;
2240  unset($cells[$actionKey]);
2241  }
2242 
2250  protected function ‪isRecordCurrentBackendUser(‪$table, $row)
2251  {
2252  return ‪$table === 'be_users' && (int)($row['uid'] ?? 0) === (int)$this->‪getBackendUserAuthentication()->user['uid'];
2253  }
2254 
2258  protected function ‪isRecordDeletePlaceholder(array $row): bool
2259  {
2260  return $this->‪getBackendUserAuthentication()->workspace > 0
2261  && isset($row['t3ver_state'])
2262  && ‪VersionState::cast($row['t3ver_state'])->equals(‪VersionState::DELETE_PLACEHOLDER);
2263  }
2264 
2268  public function ‪setIsEditable(bool $isEditable): void
2269  {
2270  $this->editable = $isEditable;
2271  }
2272 
2278  public function ‪isEditable(string ‪$table): bool
2279  {
2280  $backendUser = $this->‪getBackendUserAuthentication();
2281  return !($GLOBALS['TCA'][‪$table]['ctrl']['readOnly'] ?? false)
2282  && $this->editable
2283  && ($backendUser->isAdmin() || $backendUser->check('tables_modify', ‪$table));
2284  }
2285 
2296  protected function ‪overlayEditLockPermissions(‪$table, $row = [], $editPermission = true)
2297  {
2298  if ($editPermission && !$this->‪getBackendUserAuthentication()->isAdmin()) {
2299  // If no $row is submitted we only check for general edit lock of current page (except for table "pages")
2300  $pageHasEditLock = !empty($this->pageRow['editlock']);
2301  if (empty($row)) {
2302  return (‪$table === 'pages') || !$pageHasEditLock;
2303  }
2304  if ((‪$table === 'pages' && ($row['editlock'] ?? false)) || (‪$table !== 'pages' && $pageHasEditLock)) {
2305  $editPermission = false;
2306  } elseif (isset(‪$GLOBALS['TCA'][‪$table]['ctrl']['editlock']) && ($row[‪$GLOBALS['TCA'][‪$table]['ctrl']['editlock']] ?? false)) {
2307  $editPermission = false;
2308  }
2309  }
2310  return $editPermission;
2311  }
2312 
2320  public function ‪setModuleData(array ‪$moduleData = []): void
2321  {
2322  $this->moduleData = ‪$moduleData;
2323  }
2324 
2335  public function ‪start(‪$id, ‪$table, $pointer, $search = '', $levels = 0, ‪$showLimit = 0)
2336  {
2337  $backendUser = $this->‪getBackendUserAuthentication();
2338  // Setting internal variables:
2339  // sets the parent id
2340  $this->id = (int)‪$id;
2341  if (‪$GLOBALS['TCA'][‪$table] ?? false) {
2342  // Setting single table mode, if table exists:
2343  $this->table = ‪$table;
2344  }
2345  $this->page = ‪MathUtility::forceIntegerInRange((int)$pointer, 1, 1000);
2346  $this->showLimit = ‪MathUtility::forceIntegerInRange((int)‪$showLimit, 0, 10000);
2347  $this->searchString = trim($search);
2348  $this->searchLevels = (int)$levels;
2349  // Setting GPvars:
2350  $this->sortField = GeneralUtility::_GP('sortField');
2351  $this->sortRev = GeneralUtility::_GP('sortRev');
2352  $this->duplicateField = GeneralUtility::_GP('duplicateField');
2353 
2354  // If there is a current link to a record, set the current link uid and get the table name from the link handler configuration
2355  $currentLinkValue = trim($this->overrideUrlParameters['P']['currentValue'] ?? '');
2356  if ($currentLinkValue) {
2357  $linkService = GeneralUtility::makeInstance(LinkService::class);
2358  try {
2359  $currentLinkParts = $linkService->resolve($currentLinkValue);
2360  if ($currentLinkParts['type'] === 'record' && isset($currentLinkParts['identifier'])) {
2361  $this->currentLink['tableNames'] = ‪$this->tableList;
2362  $this->currentLink['uid'] = (int)$currentLinkParts['uid'];
2363  }
2364  } catch (UnknownLinkHandlerException $e) {
2365  }
2366  }
2367 
2368  // $table might be NULL at this point in the code. As the expressionBuilder
2369  // is used to limit returned records based on the page permissions and the
2370  // uid field of the pages it can hardcoded to work on the pages table.
2371  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
2372  ->getQueryBuilderForTable('pages')
2373  ->expr();
2374  $permsClause = $expressionBuilder->andX($backendUser->getPagePermsClause(‪Permission::PAGE_SHOW));
2375  // This will hide records from display - it has nothing to do with user rights!!
2376  $pidList = ‪GeneralUtility::intExplode(',', $backendUser->getTSConfig()['options.']['hideRecords.']['pages'] ?? '', true);
2377  if (!empty($pidList)) {
2378  $permsClause->add($expressionBuilder->notIn('pages.uid', $pidList));
2379  }
2380  $this->perms_clause = (string)$permsClause;
2381 
2382  $this->possibleTranslations = $this->‪getPossibleTranslations($this->id);
2383  }
2384 
2390  public function ‪generateList(): string
2391  {
2392  $tableNames = $this->‪getTablesToRender();
2393  ‪$output = '';
2394  foreach ($tableNames as $tableName) {
2395  ‪$output .= $this->‪getTable($tableName, $this->id);
2396  }
2397  return ‪$output;
2398  }
2399 
2406  protected function ‪getTablesToRender(): array
2407  {
2408  $hideTablesArray = ‪GeneralUtility::trimExplode(',', $this->hideTables);
2409  $backendUser = $this->‪getBackendUserAuthentication();
2410 
2411  // pre-process tables and add sorting instructions
2412  $tableNames = array_flip(array_keys(‪$GLOBALS['TCA']));
2413  foreach ($tableNames as $tableName => $_) {
2414  $hideTable = false;
2415 
2416  // Checking if the table should be rendered:
2417  // Checks that we see only permitted/requested tables:
2418  if (($this->table && $tableName !== $this->table)
2419  || ($this->tableList && !GeneralUtility::inList($this->tableList, (string)$tableName))
2420  || !$backendUser->check('tables_select', $tableName)
2421  ) {
2422  $hideTable = true;
2423  }
2424 
2425  if (!$hideTable) {
2426  // Don't show table if hidden by TCA ctrl section
2427  // Don't show table if hidden by pageTSconfig mod.web_list.hideTables
2428  $hideTable = !empty(‪$GLOBALS['TCA'][$tableName]['ctrl']['hideTable'])
2429  || in_array($tableName, $hideTablesArray, true)
2430  || in_array('*', $hideTablesArray, true);
2431  // Override previous selection if table is enabled or hidden by TSconfig TCA override mod.web_list.table
2432  $hideTable = (bool)($this->tableTSconfigOverTCA[$tableName . '.']['hideTable'] ?? $hideTable);
2433  }
2434  if ($hideTable) {
2435  unset($tableNames[$tableName]);
2436  } else {
2437  if (isset($this->tableDisplayOrder[$tableName])) {
2438  // Copy display order information
2439  $tableNames[$tableName] = $this->tableDisplayOrder[$tableName];
2440  } else {
2441  $tableNames[$tableName] = [];
2442  }
2443  }
2444  }
2445  $orderedTableNames = GeneralUtility::makeInstance(DependencyOrderingService::class)
2446  ->orderByDependencies($tableNames);
2447  return array_keys($orderedTableNames);
2448  }
2449 
2454  public function ‪setDispFields(): void
2455  {
2456  $this->setFields = $this->‪getBackendUserAuthentication()->‪getModuleData('list/displayFields');
2457  }
2458 
2472  public function ‪getQueryBuilder(
2473  string $table,
2474  int $pageId,
2475  array $additionalConstraints,
2476  array ‪$fields,
2477  bool $addSorting,
2478  int $firstResult,
2479  int $maxResult
2480  ): QueryBuilder {
2481  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
2482  ->getQueryBuilderForTable(‪$table);
2483  $queryBuilder->getRestrictions()
2484  ->removeAll()
2485  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
2486  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->‪getBackendUserAuthentication()->workspace));
2487  $queryBuilder
2488  ->select(...‪$fields)
2489  ->from(‪$table);
2490  if (!empty($additionalConstraints)) {
2491  $queryBuilder->andWhere(...$additionalConstraints);
2492  }
2493  // Legacy hook
2494  $addWhere = '';
2495  $selFieldList = implode(',', ‪$fields);
2496  if ($selFieldList === '*') {
2497  $selFieldList = '';
2498  }
2499  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['getTable'] ?? [] as $className) {
2500  $hookObject = GeneralUtility::makeInstance($className);
2501  if (!$hookObject instanceof RecordListGetTableHookInterface) {
2502  throw new \UnexpectedValueException($className . ' must implement interface ' . RecordListGetTableHookInterface::class, 1195114460);
2503  }
2504  $hookObject->getDBlistQuery(‪$table, $pageId, $addWhere, $selFieldList, $this);
2505  }
2506  if (!empty($addWhere)) {
2507  $queryBuilder->andWhere(‪QueryHelper::stripLogicalOperatorPrefix($addWhere));
2508  }
2509  ‪$fields = ‪GeneralUtility::trimExplode(',', $selFieldList, true);
2510  if (‪$fields === []) {
2511  ‪$fields = ['*'];
2512  }
2513  // Additional constraints from getTable
2514  if ((‪$GLOBALS['TCA'][‪$table]['ctrl']['languageField'] ?? false)
2515  && (‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'] ?? false)) {
2516  // Only restrict to the default language if no search request is in place
2517  // And if only translations should be shown
2518  if ($this->searchString === '' && !$this->‪showOnlyTranslatedRecords) {
2519  $queryBuilder->andWhere(
2520  $queryBuilder->expr()->orX(
2521  $queryBuilder->expr()->lte(‪$GLOBALS['TCA'][‪$table]['ctrl']['languageField'], 0),
2522  $queryBuilder->expr()->eq(‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'], 0)
2523  )
2524  );
2525  }
2526  }
2527  if (‪$table === 'pages' && $this->‪showOnlyTranslatedRecords) {
2528  $queryBuilder->andWhere(
2529  $queryBuilder->expr()->in(
2530  ‪$GLOBALS['TCA']['pages']['ctrl']['languageField'],
2531  array_keys($this->languagesAllowedForUser)
2532  )
2533  );
2534  }
2535  // Former prepareQueryBuilder
2536  if ($maxResult > 0) {
2537  $queryBuilder->setMaxResults($maxResult);
2538  }
2539  if ($firstResult > 0) {
2540  $queryBuilder->setFirstResult($firstResult);
2541  }
2542  if ($addSorting) {
2543  if ($this->sortField && in_array($this->sortField, BackendUtility::getAllowedFieldsForTable(‪$table, false))) {
2544  $queryBuilder->orderBy($this->sortField, $this->sortRev ? 'DESC' : 'ASC');
2545  } else {
2546  $orderBy = (‪$GLOBALS['TCA'][‪$table]['ctrl']['sortby'] ?? '') ?: ‪$GLOBALS['TCA'][‪$table]['ctrl']['default_sortby'] ?? '';
2547  $orderBys = ‪QueryHelper::parseOrderBy($orderBy);
2548  foreach ($orderBys as $orderBy) {
2549  $queryBuilder->addOrderBy($orderBy[0], $orderBy[1]);
2550  }
2551  }
2552  }
2553 
2554  // Build the query constraints
2555  $queryBuilder = $this->‪addPageIdConstraint($table, $queryBuilder, $this->searchLevels);
2556  $searchWhere = $this->‪makeSearchString($table, $pageId, $queryBuilder);
2557  if (!empty($searchWhere)) {
2558  $queryBuilder->andWhere($searchWhere);
2559  }
2560 
2561  // Filtering on displayable pages (permissions):
2562  if (‪$table === 'pages' && $this->perms_clause) {
2563  $queryBuilder->andWhere($this->perms_clause);
2564  }
2565 
2566  // Filter out records that are translated, if TSconfig mod.web_list.hideTranslations is set
2567  if (!empty(‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'])
2568  && (GeneralUtility::inList($this->hideTranslations, ‪$table) || $this->hideTranslations === '*')
2569  ) {
2570  $queryBuilder->andWhere(
2571  $queryBuilder->expr()->eq(
2572  ‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'],
2573  0
2574  )
2575  );
2576  } elseif (!empty(‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField']) && $this->‪showOnlyTranslatedRecords) {
2577  // When only translated records should be shown, it is necessary to use l10n_parent=pageId, instead of
2578  // a check to the PID
2579  $queryBuilder->andWhere(
2580  $queryBuilder->expr()->eq(
2581  ‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'],
2582  $queryBuilder->createNamedParameter(
2583  $this->id,
2585  )
2586  )
2587  );
2588  }
2589 
2590  $parameters = [
2591  'table' => ‪$table,
2592  'fields' => ‪$fields,
2593  'groupBy' => null,
2594  'orderBy' => null,
2595  'firstResult' => $firstResult,
2596  'maxResults' => $maxResult,
2597  ];
2598  $hookName = DatabaseRecordList::class;
2599  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$hookName]['modifyQuery'] ?? [] as $className) {
2600  $hookObject = GeneralUtility::makeInstance($className);
2601  if (method_exists($hookObject, 'modifyQuery')) {
2602  $hookObject->modifyQuery(
2603  $parameters,
2604  ‪$table,
2605  $pageId,
2606  $additionalConstraints,
2607  ‪$fields,
2608  $queryBuilder
2609  );
2610  }
2611  }
2612  return $queryBuilder;
2613  }
2614 
2624  public function ‪makeSearchString(‪$table, int $currentPid, QueryBuilder $queryBuilder)
2625  {
2626  $expressionBuilder = $queryBuilder->expr();
2627  $constraints = [];
2628  $tablePidField = ‪$table === 'pages' ? 'uid' : 'pid';
2629  // Make query only if table is valid and a search string is actually defined
2630  if (empty($this->searchString)) {
2631  return '';
2632  }
2633 
2634  $searchableFields = [];
2635  // Get fields from ctrl section of TCA first
2636  if (isset(‪$GLOBALS['TCA'][‪$table]['ctrl']['searchFields'])) {
2637  $searchableFields = ‪GeneralUtility::trimExplode(',', ‪$GLOBALS['TCA'][‪$table]['ctrl']['searchFields'], true);
2638  }
2639 
2640  if (‪MathUtility::canBeInterpretedAsInteger($this->searchString)) {
2641  $constraints[] = $expressionBuilder->eq('uid', (int)$this->searchString);
2642  foreach ($searchableFields as $fieldName) {
2643  if (!isset(‪$GLOBALS['TCA'][‪$table]['columns'][$fieldName])) {
2644  continue;
2645  }
2646  $fieldConfig = ‪$GLOBALS['TCA'][‪$table]['columns'][$fieldName]['config'];
2647  $fieldType = $fieldConfig['type'];
2648  $evalRules = ($fieldConfig['eval'] ?? false) ?: '';
2649  if ($fieldType === 'input' && $evalRules && GeneralUtility::inList($evalRules, 'int')) {
2650  if (!isset($fieldConfig['search']['pidonly'])
2651  || ($fieldConfig['search']['pidonly'] && $currentPid > 0)
2652  ) {
2653  $constraints[] = $expressionBuilder->andX(
2654  $expressionBuilder->eq($fieldName, (int)‪$this->searchString),
2655  $expressionBuilder->eq($tablePidField, (int)$currentPid)
2656  );
2657  }
2658  } elseif ($fieldType === 'text'
2659  || $fieldType === 'flex'
2660  || $fieldType === 'slug'
2661  || ($fieldType === 'input' && (!$evalRules || !preg_match('/\b(?:date|time|int)\b/', $evalRules)))
2662  ) {
2663  $constraints[] = $expressionBuilder->like(
2664  $fieldName,
2665  $queryBuilder->quote('%' . (int)$this->searchString . '%')
2666  );
2667  }
2668  }
2669  } elseif (!empty($searchableFields)) {
2670  $like = $queryBuilder->quote('%' . $queryBuilder->escapeLikeWildcards($this->searchString) . '%');
2671  foreach ($searchableFields as $fieldName) {
2672  if (!isset(‪$GLOBALS['TCA'][‪$table]['columns'][$fieldName])) {
2673  continue;
2674  }
2675  $fieldConfig = ‪$GLOBALS['TCA'][‪$table]['columns'][$fieldName]['config'];
2676  $fieldType = $fieldConfig['type'];
2677  $evalRules = ($fieldConfig['eval'] ?? false) ?: '';
2678  $searchConstraint = $expressionBuilder->andX(
2679  $expressionBuilder->comparison(
2680  'LOWER(' . $queryBuilder->castFieldToTextType($fieldName) . ')',
2681  'LIKE',
2682  'LOWER(' . $like . ')'
2683  )
2684  );
2685  if (is_array($fieldConfig['search'] ?? null)) {
2686  $searchConfig = $fieldConfig['search'];
2687  if ($searchConfig['case'] ?? false) {
2688  // Replace case insensitive default constraint
2689  $searchConstraint = $expressionBuilder->andX($expressionBuilder->like($fieldName, $like));
2690  }
2691  if (($searchConfig['pidonly'] ?? false) && $currentPid > 0) {
2692  $searchConstraint->add($expressionBuilder->eq($tablePidField, (int)$currentPid));
2693  }
2694  if ($searchConfig['andWhere'] ?? false) {
2695  if (GeneralUtility::makeInstance(Features::class)->isFeatureEnabled('runtimeDbQuotingOfTcaConfiguration')) {
2696  $searchConstraint->add(
2697  ‪QueryHelper::quoteDatabaseIdentifiers($queryBuilder->getConnection(), ‪QueryHelper::stripLogicalOperatorPrefix($fieldConfig['search']['andWhere']))
2698  );
2699  } else {
2700  $searchConstraint->add(
2701  ‪QueryHelper::stripLogicalOperatorPrefix($fieldConfig['search']['andWhere'])
2702  );
2703  }
2704  }
2705  }
2706  if ($fieldType === 'text'
2707  || $fieldType === 'flex'
2708  || $fieldType === 'slug'
2709  || ($fieldType === 'input' && (!$evalRules || !preg_match('/\b(?:date|time|int)\b/', $evalRules)))
2710  ) {
2711  if ($searchConstraint->count() !== 0) {
2712  $constraints[] = $searchConstraint;
2713  }
2714  }
2715  }
2716  }
2717  // Call hook to add or change the list
2718  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][DatabaseRecordList::class]['makeSearchStringConstraints'] ?? [] as $className) {
2719  $hookObject = GeneralUtility::makeInstance($className);
2720  if (method_exists($hookObject, 'makeSearchStringConstraints')) {
2721  $constraints = $hookObject->makeSearchStringConstraints(
2722  $queryBuilder,
2723  $constraints,
2724  $this->searchString,
2725  ‪$table,
2726  $currentPid
2727  );
2728  }
2729  }
2730  // If no search field conditions have been built ensure no results are returned
2731  if (empty($constraints)) {
2732  return '0=1';
2733  }
2734 
2735  return $expressionBuilder->orX(...$constraints);
2736  }
2737 
2746  public function ‪linkWrapTable(string ‪$table, string $label): string
2747  {
2748  if ($this->table !== ‪$table) {
2749  $url = $this->‪listURL('', $table, 'pointer');
2750  } else {
2751  $url = $this->‪listURL('', '', 'sortField,sortRev,table,pointer');
2752  }
2753  return '<a href="' . htmlspecialchars($url) . '">' . $label . '</a>';
2754  }
2755 
2765  public function ‪linkWrapItems(‪$table, $uid, $code, $row)
2766  {
2767  $lang = $this->‪getLanguageService();
2768  $origCode = $code;
2769  // If the title is blank, make a "no title" label:
2770  if ((string)$code === '') {
2771  $code = '<i>[' . htmlspecialchars(
2772  $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title')
2773  ) . ']</i> - '
2774  . htmlspecialchars(BackendUtility::getRecordTitle(‪$table, $row));
2775  } else {
2776  $code = htmlspecialchars($code);
2777  }
2778  switch ((string)$this->clickTitleMode) {
2779  case 'edit':
2780  // If the listed table is 'pages' we have to request the permission settings for each page:
2781  if (‪$table === 'pages') {
2782  $localCalcPerms = $this->‪getPagePermissionsForRecord('pages', $row);
2783  $permsEdit = $localCalcPerms->editPagePermissionIsGranted();
2784  } else {
2785  $backendUser = $this->‪getBackendUserAuthentication();
2786  $permsEdit = $this->calcPerms->editContentPermissionIsGranted() && $backendUser->recordEditAccessInternals(‪$table, $row);
2787  }
2788  // "Edit" link: ( Only if permissions to edit the page-record of the content of the parent page ($this->id)
2789  if ($permsEdit && $this->‪isEditable($table)) {
2790  $params = [
2791  'edit' => [
2792  ‪$table => [
2793  $row['uid'] => 'edit',
2794  ],
2795  ],
2796  'returnUrl' => $this->‪listURL(),
2797  ];
2798  $editLink = $this->uriBuilder->buildUriFromRoute('record_edit', $params);
2799  $code = '<a href="' . htmlspecialchars($editLink) . '"'
2800  . ' title="' . htmlspecialchars($lang->getLL('edit')) . '"'
2801  . ' aria-label="' . htmlspecialchars($lang->getLL('edit')) . '">'
2802  . $code . '</a>';
2803  }
2804  break;
2805  case 'show':
2806  // "Show" link (only pages and tt_content elements)
2807  if (‪$table === 'pages' || ‪$table === 'tt_content') {
2808  $attributes = $this->‪getPreviewUriBuilder($table, $row)->‪serializeDispatcherAttributes();
2809  $title = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'));
2810  $code = '<a href="#" ' . $attributes
2811  . ' title="' . $title . '"'
2812  . ' aria-label="' . $title . '">'
2813  . $code . '</a>';
2814  }
2815  break;
2816  case 'info':
2817  // "Info": (All records)
2818  $code = '<a href="#" role="button"' // @todo add handler that triggers click on space key
2819  . $this->‪createShowItemTagAttributes($table . ',' . (int)$row['uid'])
2820  . ' title="' . htmlspecialchars($lang->getLL('showInfo')) . '"'
2821  . ' aria-label="' . htmlspecialchars($lang->getLL('showInfo')) . '"'
2822  . ' aria-haspopup="dialog">'
2823  . $code
2824  . '</a>';
2825  break;
2826  default:
2827  // Output the label now:
2828  if (‪$table === 'pages') {
2829  $code = '<a href="' . htmlspecialchars(
2830  $this->‪listURL((string)$uid, '', 'pointer')
2831  ) . '">' . $code . '</a>';
2832  } else {
2833  $code = $this->‪linkUrlMail($code, $origCode);
2834  }
2835  }
2836  return $code;
2837  }
2838 
2847  public function ‪linkUrlMail($code, $testString)
2848  {
2849  // @todo Remove next two lines after changing signature in v12.
2850  $code = (string)$code;
2851  $testString = (string)$testString;
2852 
2853  // Check for URL:
2854  $scheme = parse_url($testString, PHP_URL_SCHEME);
2855  if ($scheme === 'http' || $scheme === 'https' || $scheme === 'ftp') {
2856  return '<a href="' . htmlspecialchars($testString) . '" target="_blank">' . $code . '</a>';
2857  }
2858  // Check for email:
2859  if (GeneralUtility::validEmail($testString)) {
2860  return '<a href="mailto:' . htmlspecialchars($testString) . '" target="_blank">' . $code . '</a>';
2861  }
2862  // Return if nothing else...
2863  return $code;
2864  }
2865 
2876  public function ‪listURL($altId = '', ‪$table = '-1', $exclList = '')
2877  {
2878  $urlParameters = [];
2879  if ((string)$altId !== '') {
2880  $urlParameters['id'] = $altId;
2881  } else {
2882  $urlParameters['id'] = ‪$this->id;
2883  }
2884  if (‪$table === '-1') {
2885  $urlParameters['table'] = ‪$this->table;
2886  } else {
2887  $urlParameters['table'] = ‪$table;
2888  }
2889  if ($this->returnUrl) {
2890  $urlParameters['returnUrl'] = ‪$this->returnUrl;
2891  }
2892  if ((!$exclList || !GeneralUtility::inList($exclList, 'search_field')) && $this->searchString) {
2893  $urlParameters['search_field'] = ‪$this->searchString;
2894  }
2895  if ($this->searchLevels) {
2896  $urlParameters['search_levels'] = ‪$this->searchLevels;
2897  }
2898  if ((!$exclList || !GeneralUtility::inList($exclList, 'pointer')) && $this->page) {
2899  $urlParameters['pointer'] = ‪$this->page;
2900  }
2901  if ((!$exclList || !GeneralUtility::inList($exclList, 'sortField')) && $this->sortField) {
2902  $urlParameters['sortField'] = ‪$this->sortField;
2903  }
2904  if ((!$exclList || !GeneralUtility::inList($exclList, 'sortRev')) && $this->sortRev) {
2905  $urlParameters['sortRev'] = ‪$this->sortRev;
2906  }
2907 
2908  return (string)$this->uriBuilder->buildUriFromRoute(
2909  ‪$GLOBALS['TYPO3_REQUEST']->getAttribute('route')->getOption('_identifier'),
2910  array_replace($urlParameters, $this->overrideUrlParameters)
2911  );
2912  }
2913 
2919  public function ‪setOverrideUrlParameters(array $urlParameters)
2920  {
2921  $currentUrlParameter = GeneralUtility::_GP('curUrl');
2922  if (isset($currentUrlParameter['url'])) {
2923  $urlParameters['P']['currentValue'] = $currentUrlParameter['url'];
2924  }
2925  $this->overrideUrlParameters = $urlParameters;
2926  }
2927 
2940  public function ‪setTableDisplayOrder(array $orderInformation)
2941  {
2942  foreach ($orderInformation as $tableName => &$configuration) {
2943  if (isset($configuration['before'])) {
2944  if (is_string($configuration['before'])) {
2945  $configuration['before'] = ‪GeneralUtility::trimExplode(',', $configuration['before'], true);
2946  } elseif (!is_array($configuration['before'])) {
2947  throw new \UnexpectedValueException(
2948  'The specified "before" order configuration for table "' . $tableName . '" is invalid.',
2949  1504793406
2950  );
2951  }
2952  }
2953  if (isset($configuration['after'])) {
2954  if (is_string($configuration['after'])) {
2955  $configuration['after'] = ‪GeneralUtility::trimExplode(',', $configuration['after'], true);
2956  } elseif (!is_array($configuration['after'])) {
2957  throw new \UnexpectedValueException(
2958  'The specified "after" order configuration for table "' . $tableName . '" is invalid.',
2959  1504793407
2960  );
2961  }
2962  }
2963  }
2964  $this->tableDisplayOrder = $orderInformation;
2965  }
2966 
2970  public function ‪getOverridePageIdList(): array
2971  {
2973  }
2974 
2978  public function ‪setOverridePageIdList(array ‪$overridePageIdList)
2979  {
2980  $this->overridePageIdList = array_map('intval', ‪$overridePageIdList);
2981  }
2982 
2991  protected function ‪getSearchableWebmounts(‪$id, $depth, ‪$perms_clause)
2992  {
2993  $runtimeCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('runtime');
2994  $hash = 'webmounts_list' . md5(‪$id . '-' . $depth . '-' . ‪$perms_clause);
2995  $idList = $runtimeCache->get($hash);
2996  if ($idList === false) {
2997  $backendUser = $this->‪getBackendUserAuthentication();
2998  $tree = GeneralUtility::makeInstance(PageTreeView::class);
2999  $tree->init('AND ' . ‪$perms_clause);
3000  $tree->makeHTML = 0;
3001  $tree->fieldArray = ['uid', 'php_tree_stop'];
3002  $idList = [];
3003 
3004  $allowedMounts = !$backendUser->isAdmin() && ‪$id === 0
3005  ? $backendUser->returnWebmounts()
3006  : [‪$id];
3007 
3008  foreach ($allowedMounts as $allowedMount) {
3009  $idList[] = $allowedMount;
3010  if ($depth) {
3011  $tree->getTree($allowedMount, $depth);
3012  }
3013  $idList = array_merge($idList, $tree->ids);
3014  }
3015  $idList = array_unique($idList);
3016  $runtimeCache->set($hash, $idList);
3017  }
3018 
3019  return $idList;
3020  }
3021 
3031  protected function ‪addPageIdConstraint(string $tableName, QueryBuilder $queryBuilder, int ‪$searchLevels): QueryBuilder
3032  {
3033  // Set search levels to 999 instead of -1 as the following methods
3034  // do not support -1 as valid value for infinite search.
3035  if (‪$searchLevels === -1) {
3036  ‪$searchLevels = 999;
3037  }
3038 
3039  // When querying translated pages, the PID of the translated pages should be the same as the
3040  // the PID of the current page
3041  if ($tableName === 'pages' && $this->‪showOnlyTranslatedRecords) {
3042  $pageRecord = BackendUtility::getRecordWSOL('pages', $this->id);
3043  $queryBuilder->andWhere(
3044  $queryBuilder->expr()->eq(
3045  $tableName . '.pid',
3046  $queryBuilder->createNamedParameter($pageRecord['pid'] ?? 0, ‪Connection::PARAM_INT)
3047  )
3048  );
3049  } elseif (‪$searchLevels === 0) {
3050  $queryBuilder->andWhere(
3051  $queryBuilder->expr()->eq(
3052  $tableName . '.pid',
3053  $queryBuilder->createNamedParameter($this->id, ‪Connection::PARAM_INT)
3054  )
3055  );
3056  } elseif (‪$searchLevels > 0) {
3057  $allowedMounts = $this->‪getSearchableWebmounts($this->id, $searchLevels, $this->perms_clause);
3058  $queryBuilder->andWhere(
3059  $queryBuilder->expr()->in(
3060  $tableName . '.pid',
3061  $queryBuilder->createNamedParameter($allowedMounts, Connection::PARAM_INT_ARRAY)
3062  )
3063  );
3064  }
3065 
3066  if (!empty($this->‪getOverridePageIdList())) {
3067  $queryBuilder->andWhere(
3068  $queryBuilder->expr()->in(
3069  $tableName . '.pid',
3070  $queryBuilder->createNamedParameter($this->getOverridePageIdList(), Connection::PARAM_INT_ARRAY)
3071  )
3072  );
3073  }
3074 
3075  return $queryBuilder;
3076  }
3077 
3081  protected function ‪getBackendUserAuthentication()
3082  {
3083  return ‪$GLOBALS['BE_USER'];
3084  }
3085 
3096  protected function ‪addElement($data, $rowParams = '', $colType = 'td')
3097  {
3098  $colType = ($colType === 'th') ? 'th' : 'td';
3099  $dataUid = ($colType === 'td') ? ($data['uid'] ?? 0) : 0;
3100  $l10nParent = $data['_l10nparent_'] ?? 0;
3101  $out = '<tr ' . $rowParams . ' data-uid="' . $dataUid . '" data-l10nparent="' . $l10nParent . '">';
3102 
3103  // Init rendering.
3104  $colsp = '';
3105  $lastKey = '';
3106  $c = 0;
3107  // __label is used as the label key to circumvent problems with uid used as label (see #67756)
3108  // as it was introduced later on, check if it really exists before using it
3110  if ($colType === 'td' && isset($data['__label'])) {
3111  // The title label column does always follow the icon column. Since
3112  // in some cases the first column - "_SELECTOR_" - might not be rendered,
3113  // we always have to calculate the key by searching for the icon column.
3114  $titleLabelKey = (int)(array_search('icon', ‪$fields, true)) + 1;
3115  ‪$fields[$titleLabelKey] = '__label';
3116  }
3117  // Traverse field array which contains the data to present:
3118  foreach (‪$fields as $vKey) {
3119  if (isset($data[$vKey])) {
3120  if ($lastKey) {
3121  $cssClass = $this->addElement_tdCssClass[$lastKey] ?? '';
3122  $out .= '
3123  <' . $colType . ' class="' . $cssClass . ' nowrap' . '"' . $colsp . '>' . $data[$lastKey] . '</' . $colType . '>';
3124  }
3125  $lastKey = $vKey;
3126  $c = 1;
3127  } else {
3128  if (!$lastKey) {
3129  $lastKey = $vKey;
3130  }
3131  $c++;
3132  }
3133  if ($c > 1) {
3134  $colsp = ' colspan="' . $c . '"';
3135  } else {
3136  $colsp = '';
3137  }
3138  }
3139  if ($lastKey) {
3140  $cssClass = $this->addElement_tdCssClass[$lastKey] ?? '';
3141  $out .= '
3142  <' . $colType . ' class="' . $cssClass . ' nowrap' . '"' . $colsp . '>' . $data[$lastKey] . '</' . $colType . '>';
3143  }
3144  // End row
3145  $out .= '
3146  </tr>';
3147  return $out;
3148  }
3149 
3159  protected function ‪getPossibleTranslations(int $pageUid): array
3160  {
3161  // Store languages that are included in the site configuration for the current page.
3162  $availableSystemLanguageUids = array_keys($this->translateTools->getSystemLanguages($pageUid));
3163  if ($availableSystemLanguageUids === []) {
3164  return [];
3165  }
3166  // Look up page overlays:
3167  $localizationParentField = ‪$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'] ?? '';
3168  $languageField = ‪$GLOBALS['TCA']['pages']['ctrl']['languageField'] ?? '';
3169  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
3170  ->getQueryBuilderForTable('pages');
3171  $queryBuilder->getRestrictions()
3172  ->removeAll()
3173  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
3174  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->‪getBackendUserAuthentication()->workspace));
3175  $result = $queryBuilder
3176  ->select('*')
3177  ->from('pages')
3178  ->where(
3179  $queryBuilder->expr()->andX(
3180  $queryBuilder->expr()->eq($localizationParentField, $queryBuilder->createNamedParameter($pageUid, ‪Connection::PARAM_INT)),
3181  $queryBuilder->expr()->in($languageField, $queryBuilder->createNamedParameter($availableSystemLanguageUids, Connection::PARAM_INT_ARRAY)),
3182  $queryBuilder->expr()->gt(
3183  $languageField,
3184  $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)
3185  )
3186  )
3187  )
3188  ->executeQuery();
3189  $allowedTranslationsOnPage = [];
3190  while ($row = $result->fetchAssociative()) {
3191  $allowedTranslationsOnPage[] = (int)$row[$languageField];
3192  }
3193  return $allowedTranslationsOnPage;
3194  }
3195 
3203  protected function ‪languageFlag(string ‪$table, array $row): string
3204  {
3205  $pageId = (int)(‪$table === 'pages' ? ($row[‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField']] ?: $row['uid']) : $row['pid']);
3206  $languageUid = (int)($row[‪$GLOBALS['TCA'][‪$table]['ctrl']['languageField'] ?? null] ?? 0);
3207  $languageInformation = $this->translateTools->getSystemLanguages($pageId);
3208  $title = htmlspecialchars($languageInformation[$languageUid]['title'] ?? '');
3209  $indent = (‪$table !== 'pages' && $this->‪isLocalized($table, $row)) ? ' style="margin-left: 16px;"' : '';
3210  if ($languageInformation[$languageUid]['flagIcon'] ?? false) {
3211  return '<span title="' . $title . '"' . $indent . '>' . $this->iconFactory->getIcon(
3212  $languageInformation[$languageUid]['flagIcon'],
3214  )->render() . '</span>&nbsp;' . $title;
3215  }
3216  return $title;
3217  }
3218 
3223  protected function ‪generateReferenceToolTip(string ‪$table, int $uid): string
3224  {
3225  $numberOfReferences = $this->‪getReferenceCount($table, $uid);
3226  if (!$numberOfReferences) {
3227  $htmlCode = '<button type="button" class="btn btn-default" disabled><span style="display:inline-block;min-width:16px">-</span></button>';
3228  } else {
3229  $showReferences = $this->‪getLanguageService()->‪sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:show_references');
3230  $htmlCode = '<button type="button"'
3231  . ' class="btn btn-default"'
3232  . ' aria-haspopup="dialog"'
3233  . ' ' . $this->‪createShowItemTagAttributes($table . ',' . $uid)
3234  . ' title="' . htmlspecialchars($showReferences) . ' (' . $numberOfReferences . ')' . '">'
3235  . '<span style="display:inline-block;min-width:16px">'
3236  . $numberOfReferences
3237  . '<span class="visually-hidden">' . $showReferences . '</span>'
3238  . '</span>'
3239  . '</button>';
3240  }
3241  return $htmlCode;
3242  }
3243 
3249  protected function ‪renderCheckboxActions(): string
3250  {
3251  $lang = $this->‪getLanguageService();
3252 
3253  $dropdownItems['checkAll'] = '
3254  <li>
3255  <button type="button" class="btn btn-link dropdown-item disabled" data-multi-record-selection-check-action="check-all" title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.checkAll')) . '">
3256  ' . $this->iconFactory->getIcon('actions-check-square', ‪Icon::SIZE_SMALL)->render() . '
3257  ' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.checkAll')) . '
3258  </button>
3259  </li>';
3260 
3261  $dropdownItems['checkNone'] = '
3262  <li>
3263  <button type="button" class="btn btn-link dropdown-item disabled" data-multi-record-selection-check-action="check-none" title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.uncheckAll')) . '">
3264  ' . $this->iconFactory->getIcon('actions-square', ‪Icon::SIZE_SMALL)->render() . '
3265  ' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.uncheckAll')) . '
3266  </button>
3267  </li>';
3268 
3269  $dropdownItems['toggleSelection'] = '
3270  <li>
3271  <button type="button" class="btn btn-link dropdown-item disabled" data-multi-record-selection-check-action="toggle" title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.toggleSelection')) . '">
3272  ' . $this->iconFactory->getIcon('actions-document-select', ‪Icon::SIZE_SMALL)->render() . '
3273  ' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.toggleSelection')) . '
3274  </button>
3275  </li>';
3276 
3277  return '
3278  <div class="btn-group dropdown position-static">
3279  <button type="button" class="btn btn-borderless dropdown-toggle t3js-multi-record-selection-check-actions-toggle" data-bs-toggle="dropdown" data-bs-boundary="window" aria-expanded="false">
3280  ' . $this->iconFactory->getIcon('content-special-div', ‪Icon::SIZE_SMALL) . '
3281  </button>
3282  <ul class="dropdown-menu t3js-multi-record-selection-check-actions">
3283  ' . implode(PHP_EOL, $dropdownItems) . '
3284  </ul>
3285  </div>';
3286  }
3287 
3295  protected function ‪renderMultiRecordSelectionActions(string ‪$table, array $currentIdList): string
3296  {
3297  $actions = [];
3298  $lang = $this->‪getLanguageService();
3299  $userTsConfig = $this->‪getBackendUserAuthentication()->‪getTSConfig();
3300  $addClipboardActions = $this->showClipboardActions && $this->‪isClipboardFunctionalityEnabled($table);
3301  $editPermission = (
3302  (‪$table === 'pages') ? $this->calcPerms->editPagePermissionIsGranted() : $this->calcPerms->editContentPermissionIsGranted()
3303  ) && $this->‪overlayEditLockPermissions($table);
3304 
3305  // Add actions in case table can be modified by the current user
3306  if ($editPermission && $this->‪isEditable($table)) {
3307  $editActionConfiguration = GeneralUtility::jsonEncodeForHtmlAttribute([
3308  'idField' => 'uid',
3309  'tableName' => ‪$table,
3310  'returnUrl' => $this->‪listURL(),
3311  ], true);
3312  $actions['edit'] = '
3313  <button type="button" class="btn btn-default btn-sm" data-multi-record-selection-action="edit" data-multi-record-selection-action-config="' . $editActionConfiguration . '">
3314  <span title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.edit')) . '">
3315  ' . $this->iconFactory->getIcon('actions-document-open', ‪Icon::SIZE_SMALL)->render() . ' ' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.edit')) . '
3316  </span>
3317  </button>';
3318 
3319  if (!(bool)trim((string)($userTsConfig['options.']['disableDelete.'][‪$table] ?? $userTsConfig['options.']['disableDelete'] ?? ''))) {
3320  $deleteActionConfiguration = GeneralUtility::jsonEncodeForHtmlAttribute([
3321  'idField' => 'uid',
3322  'ok' => $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete'),
3323  'title' => $lang->getLL('clip_deleteMarked'),
3324  'content' => sprintf($lang->getLL('clip_deleteMarkedWarning'), $lang->sL(‪$GLOBALS['TCA'][‪$table]['ctrl']['title'])),
3325  ], true);
3326  $actions['delete'] = '
3327  <button type="button" class="btn btn-default btn-sm" data-multi-record-selection-action="delete" data-multi-record-selection-action-config="' . $deleteActionConfiguration . '" aria-haspopup="dialog">
3328  <span title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete')) . '">
3329  ' . $this->iconFactory->getIcon('actions-edit-delete', ‪Icon::SIZE_SMALL)->render() . ' ' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete')) . '
3330  </span>
3331  </button>';
3332  }
3333  }
3334 
3335  // Add clipboard actions in case they are enabled and clipboard is not deactivated
3336  if ($addClipboardActions && (string)($this->modTSconfig['enableClipBoard'] ?? '') !== 'deactivated') {
3337  $copyMarked = '
3338  <button type="button" class="btn btn-default btn-sm ' . ($this->clipObj->current === 'normal' ? 'disabled' : '') . '" data-multi-record-selection-action="copyMarked">
3339  <span title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.transferToClipboard')) . '">
3340  ' . $this->iconFactory->getIcon('actions-edit-copy', ‪Icon::SIZE_SMALL)->render() . ' ' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.transferToClipboard')) . '
3341  </span>
3342  </button>';
3343  $removeMarked = '
3344  <button type="button" class="btn btn-default btn-sm ' . ($this->clipObj->current === 'normal' ? 'disabled' : '') . '" data-multi-record-selection-action="removeMarked">
3345  <span title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.removeFromClipboard')) . '">
3346  ' . $this->iconFactory->getIcon('actions-remove', ‪Icon::SIZE_SMALL)->render() . ' ' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.removeFromClipboard')) . '
3347  </span>
3348  </button>';
3349  // Add "copy marked" after "edit", or in case "edit" is not set, as first item
3350  if (!isset($actions['edit'])) {
3351  $actions = array_merge(['copyMarked' => $copyMarked], $actions);
3352  } else {
3353  $end = array_splice($actions, (int)(array_search('edit', array_keys($actions), true)) + 1);
3354  $actions = array_merge($actions, ['copyMarked' => $copyMarked, 'removeMarked' => $removeMarked], $end);
3355  }
3356  }
3357 
3358  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'])) {
3359  trigger_error(
3360  'The hook $TYPO3_CONF_VARS[SC_OPTIONS][typo3/class.db_list_extra.inc][actions] for calling method "renderListHeaderActions" is deprecated and will stop working in TYPO3 v12.0. Use the ModifyRecordListTableActionsEvent instead.',
3361  E_USER_DEPRECATED
3362  );
3363  }
3364 
3365  /*
3366  * hook: renderListHeaderActions: Allows to influence the table header actions
3367  * usage: Above each listed table in Web>List a header row is shown.
3368  * This hook allows to modify the icons responsible for the clipboard functions
3369  * or other "Action" functions which perform operations on the listed records.
3370  *
3371  * @deprecated in v11, will be removed in TYPO3 v12.0.
3372  */
3373  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['actions'] ?? [] as $className) {
3374  $hookObject = GeneralUtility::makeInstance($className);
3375  if (!$hookObject instanceof RecordListHookInterface) {
3376  throw new \UnexpectedValueException($className . ' must implement interface ' . RecordListHookInterface::class, 1195567850);
3377  }
3378  $actions = $hookObject->renderListHeaderActions(‪$table, $currentIdList, $actions, $this);
3379  }
3380 
3381  $event = $this->eventDispatcher->dispatch(
3382  new ModifyRecordListTableActionsEvent($actions, ‪$table, $currentIdList, $this)
3383  );
3384  $actions = $event->getActions();
3385 
3386  if ($actions === []) {
3387  // In case the user does not have permissions to execute on of the above
3388  // actions or a hook removed all remaining actions, inform the user about this.
3389  return '
3390  <div class="col pt-1 pb-1">
3391  <span class="label label-info">
3392  ' . htmlspecialchars($lang->sL($event->getNoActionLabel() ?: 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.noActionAvailable')) . '
3393  </span>
3394  </div>';
3395  }
3396 
3397  // In case both clipboard actions should be rendered, wrap them into a button group
3398  if (($actions['copyMarked'] ?? false) && ($actions['removeMarked'] ?? false)) {
3399  $actions['copyMarked'] = '<div class="btn-group">' . $actions['copyMarked'] . $actions['removeMarked'] . '</div>';
3400  unset($actions['removeMarked']);
3401  }
3402 
3403  return implode(LF, array_map(static fn(string $action): string => '<div class="col">' . $action . '</div>', $actions));
3404  }
3405 
3413  {
3415  }
3416 
3423  protected function ‪createShowItemTagAttributes(string $arguments): string
3424  {
3425  return GeneralUtility::implodeAttributes([
3426  'data-dispatch-action' => 'TYPO3.InfoWindow.showItem',
3427  'data-dispatch-args-list' => $arguments,
3428  ], true);
3429  }
3430 
3435  protected function ‪getLanguageService()
3436  {
3437  return ‪$GLOBALS['LANG'];
3438  }
3439 
3444  public function ‪setLanguagesAllowedForUser(array ‪$languagesAllowedForUser): DatabaseRecordList
3445  {
3446  $this->languagesAllowedForUser = ‪$languagesAllowedForUser;
3447  return $this;
3448  }
3449 
3456  protected function ‪getFluidTemplateObject(string $filename): ‪StandaloneView
3457  {
3458  $view = GeneralUtility::makeInstance(StandaloneView::class);
3459  $view->setLayoutRootPaths(['EXT:recordlist/Resources/Private/Layouts']);
3460  $view->setPartialRootPaths(['EXT:recordlist/Resources/Private/Partials']);
3461  $view->setTemplateRootPaths(['EXT:recordlist/Resources/Private/Templates']);
3462  $view->setTemplate($filename);
3463  return $view;
3464  }
3465 
3474  protected function ‪isLocalized(string ‪$table, array $row): bool
3475  {
3476  $languageField = ‪$GLOBALS['TCA'][‪$table]['ctrl']['languageField'] ?? '';
3477  $transOrigPointerField = ‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'] ?? '';
3478 
3479  return ($row[$languageField] ?? false) && ($row[$transOrigPointerField] ?? false);
3480  }
3481 
3489  protected function ‪getNoViewWithDokTypes(array $tsConfig): array
3490  {
3491  if (isset($tsConfig['noViewWithDokTypes'])) {
3492  $noViewDokTypes = ‪GeneralUtility::intExplode(',', $tsConfig['noViewWithDokTypes'], true);
3493  } else {
3494  $noViewDokTypes = [
3498  ];
3499  }
3500 
3501  return $noViewDokTypes;
3502  }
3503 
3513  protected function ‪isClipboardFunctionalityEnabled(string ‪$table, array $row = []): bool
3514  {
3515  return $this->clipObj !== null
3516  && (‪$table !== 'pages' || !‪$this->showOnlyTranslatedRecords)
3517  && (
3518  $row === []
3519  || (
3520  !$this->‪isRecordDeletePlaceholder($row)
3521  && (int)($row[‪$GLOBALS['TCA'][‪$table]['ctrl']['transOrigPointerField'] ?? null] ?? 0) === 0
3522  )
3523  );
3524  }
3525 
3531  protected function ‪addDividerToCellGroup(array &$cells): void
3532  {
3533  if (!($cells['secondary']['divider'] ?? false)) {
3534  $this->‪addActionToCellGroup($cells, '<hr class="dropdown-divider me-0 ms-0 border-white">', 'divider');
3535  }
3536  }
3537 }
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$sortField
‪string $sortField
Definition: DatabaseRecordList.php:136
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$uriBuilder
‪UriBuilder $uriBuilder
Definition: DatabaseRecordList.php:324
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$hideTables
‪string $hideTables
Definition: DatabaseRecordList.php:201
‪TYPO3\CMS\Core\Database\Query\QueryHelper\parseOrderBy
‪static array array[] parseOrderBy(string $input)
Definition: QueryHelper.php:44
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getColumnsToRender
‪array getColumnsToRender(string $table, bool $includeMetaColumns)
Definition: DatabaseRecordList.php:424
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\setLanguagesAllowedForUser
‪DatabaseRecordList setLanguagesAllowedForUser(array $languagesAllowedForUser)
Definition: DatabaseRecordList.php:3415
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$clickTitleMode
‪string $clickTitleMode
Definition: DatabaseRecordList.php:231
‪TYPO3\CMS\Backend\RecordList\RecordListGetTableHookInterface
Definition: RecordListGetTableHookInterface.php:23
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$duplicateField
‪string $duplicateField
Definition: DatabaseRecordList.php:274
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\makeLocalizationPanel
‪string makeLocalizationPanel($table, $row, array $translations)
Definition: DatabaseRecordList.php:1984
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication\recordEditAccessInternals
‪bool recordEditAccessInternals($table, $idOrRow, $newRecord=false, $deletedRecord=false, $checkFullLanguageAccess=false)
Definition: BackendUserAuthentication.php:740
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\renderListRow
‪string renderListRow($table, array $row, int $indent, array $translations, bool $translationEnabled)
Definition: DatabaseRecordList.php:1000
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:49
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$pagePermsCache
‪array $pagePermsCache
Definition: DatabaseRecordList.php:390
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$setFields
‪string[][] $setFields
Definition: DatabaseRecordList.php:250
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$showOnlyTranslatedRecords
‪bool $showOnlyTranslatedRecords
Definition: DatabaseRecordList.php:363
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$perms_clause
‪string $perms_clause
Definition: DatabaseRecordList.php:207
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$moduleData
‪array $moduleData
Definition: DatabaseRecordList.php:144
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$possibleTranslations
‪array $possibleTranslations
Definition: DatabaseRecordList.php:375
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$CBnames
‪string[] $CBnames
Definition: DatabaseRecordList.php:292
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\addPageIdConstraint
‪QueryBuilder addPageIdConstraint(string $tableName, QueryBuilder $queryBuilder, int $searchLevels)
Definition: DatabaseRecordList.php:3002
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$showClipboardActions
‪bool $showClipboardActions
Definition: DatabaseRecordList.php:82
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_NEW
‪const PAGE_NEW
Definition: Permission.php:50
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getLanguageService
‪LanguageService getLanguageService()
Definition: DatabaseRecordList.php:3406
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$duplicateStack
‪array $duplicateStack
Definition: DatabaseRecordList.php:157
‪TYPO3\CMS\Backend\Clipboard\Clipboard
Definition: Clipboard.php:49
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$noControlPanels
‪bool $noControlPanels
Definition: DatabaseRecordList.php:88
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\generateReferenceToolTip
‪generateReferenceToolTip(string $table, int $uid)
Definition: DatabaseRecordList.php:3194
‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder\create
‪static static create(int $pageId, string $alternativeUri=null)
Definition: PreviewUriBuilder.php:75
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$calcPerms
‪Permission $calcPerms
Definition: DatabaseRecordList.php:225
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$displayRecordDownload
‪bool $displayRecordDownload
Definition: DatabaseRecordList.php:308
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\addSortLink
‪string addSortLink($code, $field, $table)
Definition: DatabaseRecordList.php:2103
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\listURL
‪string listURL($altId='', $table='-1', $exclList='')
Definition: DatabaseRecordList.php:2847
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$iconFactory
‪IconFactory $iconFactory
Definition: DatabaseRecordList.php:323
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$hideTranslations
‪string $hideTranslations
Definition: DatabaseRecordList.php:123
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\isClipboardFunctionalityEnabled
‪bool isClipboardFunctionalityEnabled(string $table, array $row=[])
Definition: DatabaseRecordList.php:3484
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$disableSingleTableView
‪bool $disableSingleTableView
Definition: DatabaseRecordList.php:107
‪TYPO3\CMS\Core\Database\ReferenceIndex
Definition: ReferenceIndex.php:42
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication\getTSConfig
‪array getTSConfig()
Definition: BackendUserAuthentication.php:1000
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\makeClip
‪makeClip(string $table, array $row, array &$cells)
Definition: DatabaseRecordList.php:1826
‪TYPO3\CMS\Backend\Backend\Avatar\Avatar
Definition: Avatar.php:33
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$searchLevels
‪int $searchLevels
Definition: DatabaseRecordList.php:238
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$tableTSconfigOverTCA
‪mixed[][] $tableTSconfigOverTCA
Definition: DatabaseRecordList.php:244
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\addElement
‪string addElement($data, $rowParams='', $colType='td')
Definition: DatabaseRecordList.php:3067
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\isRecordCurrentBackendUser
‪bool isRecordCurrentBackendUser($table, $row)
Definition: DatabaseRecordList.php:2221
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\linkUrlMail
‪string linkUrlMail($code, $testString)
Definition: DatabaseRecordList.php:2818
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\isRowListingConditionFulfilled
‪bool isRowListingConditionFulfilled($table, $row)
Definition: DatabaseRecordList.php:983
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\isRecordDeletePlaceholder
‪isRecordDeletePlaceholder(array $row)
Definition: DatabaseRecordList.php:2229
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$overridePageIdList
‪array $overridePageIdList
Definition: DatabaseRecordList.php:343
‪TYPO3\CMS\Recordlist\Event\ModifyRecordListTableActionsEvent
Definition: ModifyRecordListTableActionsEvent.php:27
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$tableDisplayOrder
‪array $tableDisplayOrder
Definition: DatabaseRecordList.php:336
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$displayColumnSelector
‪bool $displayColumnSelector
Definition: DatabaseRecordList.php:300
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\renderListNavigation
‪string renderListNavigation(string $table, int $totalItems, int $itemsPerPage)
Definition: DatabaseRecordList.php:1335
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getQueryBuilder
‪TYPO3 CMS Core Database Query QueryBuilder getQueryBuilder(string $table, int $pageId, array $additionalConstraints, array $fields, bool $addSorting, int $firstResult, int $maxResult)
Definition: DatabaseRecordList.php:2443
‪TYPO3\CMS\Core\Versioning\VersionState\DELETE_PLACEHOLDER
‪const DELETE_PLACEHOLDER
Definition: VersionState.php:61
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getFluidTemplateObject
‪StandaloneView getFluidTemplateObject(string $filename)
Definition: DatabaseRecordList.php:3427
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$addElement_tdCssClass
‪array $addElement_tdCssClass
Definition: DatabaseRecordList.php:176
‪TYPO3\CMS\Core\Localization\LanguageService\sL
‪string sL($input)
Definition: LanguageService.php:161
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$fieldArray
‪array $fieldArray
Definition: DatabaseRecordList.php:195
‪TYPO3\CMS\Core\Database\Query\QueryHelper\quoteDatabaseIdentifiers
‪static string quoteDatabaseIdentifiers(Connection $connection, string $sql)
Definition: QueryHelper.php:228
‪TYPO3\CMS\Backend\Tree\View\PageTreeView
Definition: PageTreeView.php:24
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$listOnlyInSingleTableMode
‪bool $listOnlyInSingleTableMode
Definition: DatabaseRecordList.php:163
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$table
‪string $table
Definition: DatabaseRecordList.php:219
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\showNewRecLink
‪bool showNewRecLink($table)
Definition: DatabaseRecordList.php:2182
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$showLimit
‪int $showLimit
Definition: DatabaseRecordList.php:189
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$currentTable
‪int[][] $currentTable
Definition: DatabaseRecordList.php:182
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$tableList
‪string $tableList
Definition: DatabaseRecordList.php:280
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\setDispFields
‪setDispFields()
Definition: DatabaseRecordList.php:2425
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\setTableDisplayOrder
‪setTableDisplayOrder(array $orderInformation)
Definition: DatabaseRecordList.php:2911
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\renderCheckboxActions
‪string renderCheckboxActions()
Definition: DatabaseRecordList.php:3220
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:43
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList
Definition: DatabaseRecordList.php:59
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\createNewRecordButton
‪string createNewRecordButton(string $table)
Definition: DatabaseRecordList.php:831
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\renderListHeader
‪string renderListHeader($table, $currentIdList)
Definition: DatabaseRecordList.php:1163
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$pageRow
‪string[] $pageRow
Definition: DatabaseRecordList.php:117
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\renderMultiRecordSelectionActions
‪string renderMultiRecordSelectionActions(string $table, array $currentIdList)
Definition: DatabaseRecordList.php:3266
‪TYPO3\CMS\Core\Type\Enumeration\cast
‪static static cast($value)
Definition: Enumeration.php:186
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\__construct
‪__construct(IconFactory $iconFactory, UriBuilder $uriBuilder, TranslationConfigurationProvider $translateTools, EventDispatcherInterface $eventDispatcher)
Definition: DatabaseRecordList.php:402
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$allowedNewTables
‪string[] $allowedNewTables
Definition: DatabaseRecordList.php:69
‪TYPO3\CMS\Recordlist\RecordList
Definition: DatabaseRecordList.php:16
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$clipObj
‪Clipboard $clipObj
Definition: DatabaseRecordList.php:286
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$currentLink
‪array $currentLink
Definition: DatabaseRecordList.php:356
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$recPath_cache
‪array $recPath_cache
Definition: DatabaseRecordList.php:130
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getPossibleTranslations
‪int[] getPossibleTranslations(int $pageUid)
Definition: DatabaseRecordList.php:3130
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$deniedNewTables
‪string[] $deniedNewTables
Definition: DatabaseRecordList.php:76
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\setOverrideUrlParameters
‪setOverrideUrlParameters(array $urlParameters)
Definition: DatabaseRecordList.php:2890
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getSearchableWebmounts
‪int[] getSearchableWebmounts($id, $depth, $perms_clause)
Definition: DatabaseRecordList.php:2962
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\generateList
‪string generateList()
Definition: DatabaseRecordList.php:2361
‪TYPO3\CMS\Recordlist\Event\ModifyRecordListRecordActionsEvent
Definition: ModifyRecordListRecordActionsEvent.php:27
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$clickMenuEnabled
‪bool $clickMenuEnabled
Definition: DatabaseRecordList.php:94
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:40
‪TYPO3\CMS\Core\Authentication\AbstractUserAuthentication\getModuleData
‪mixed getModuleData($module, $type='')
Definition: AbstractUserAuthentication.php:1054
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$id
‪int $id
Definition: DatabaseRecordList.php:150
‪TYPO3\CMS\Core\Configuration\Features
Definition: Features.php:56
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getTable
‪string getTable($table, $id)
Definition: DatabaseRecordList.php:532
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SPACER
‪const DOKTYPE_SPACER
Definition: PageRepository.php:115
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getReferenceCount
‪int getReferenceCount($tableName, $uid)
Definition: DatabaseRecordList.php:1143
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$eventDispatcher
‪EventDispatcherInterface $eventDispatcher
Definition: DatabaseRecordList.php:400
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Core\Service\DependencyOrderingService
Definition: DependencyOrderingService.php:32
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$showLocalizeColumn
‪array $showLocalizeColumn
Definition: DatabaseRecordList.php:398
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$page
‪int $page
Definition: DatabaseRecordList.php:256
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$searchString
‪string $searchString
Definition: DatabaseRecordList.php:262
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\makeSearchString
‪string makeSearchString($table, int $currentPid, QueryBuilder $queryBuilder)
Definition: DatabaseRecordList.php:2595
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication\checkLanguageAccess
‪bool checkLanguageAccess($langValue)
Definition: BackendUserAuthentication.php:670
‪TYPO3\CMS\Extbase\Mvc\View\ViewInterface\assignMultiple
‪self assignMultiple(array $values)
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\isLocalized
‪bool isLocalized(string $table, array $row)
Definition: DatabaseRecordList.php:3445
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:35
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SYSFOLDER
‪const DOKTYPE_SYSFOLDER
Definition: PageRepository.php:116
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\makeControl
‪string makeControl($table, $row)
Definition: DatabaseRecordList.php:1375
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getPagePermissionsForRecord
‪getPagePermissionsForRecord(string $table, array $row)
Definition: DatabaseRecordList.php:2145
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\languageFlag
‪string languageFlag(string $table, array $row)
Definition: DatabaseRecordList.php:3174
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\setOverridePageIdList
‪setOverridePageIdList(array $overridePageIdList)
Definition: DatabaseRecordList.php:2949
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getTablesToRender
‪array getTablesToRender()
Definition: DatabaseRecordList.php:2377
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\makeCheckbox
‪string makeCheckbox(string $table, array $row)
Definition: DatabaseRecordList.php:1942
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\addActionToCellGroup
‪addActionToCellGroup(&$cells, $action, $actionKey)
Definition: DatabaseRecordList.php:2199
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$returnUrl
‪string $returnUrl
Definition: DatabaseRecordList.php:213
‪TYPO3\CMS\Core\Versioning\VersionState
Definition: VersionState.php:24
‪TYPO3\CMS\Recordlist\RecordList\RecordListHookInterface
Definition: RecordListHookInterface.php:23
‪$output
‪$output
Definition: annotationChecker.php:121
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\overlayEditLockPermissions
‪bool overlayEditLockPermissions($table, $row=[], $editPermission=true)
Definition: DatabaseRecordList.php:2267
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:38
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$modTSconfig
‪array[] $modTSconfig
Definition: DatabaseRecordList.php:169
‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder\serializeDispatcherAttributes
‪string null serializeDispatcherAttributes(array $options=null)
Definition: PreviewUriBuilder.php:210
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$editable
‪bool $editable
Definition: DatabaseRecordList.php:322
‪TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider
Definition: TranslationConfigurationProvider.php:37
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\linkWrapTable
‪string linkWrapTable(string $table, string $label)
Definition: DatabaseRecordList.php:2717
‪TYPO3\CMS\Fluid\View\StandaloneView
Definition: StandaloneView.php:31
‪TYPO3\CMS\Core\Type\Bitmask\Permission\CONTENT_EDIT
‪const CONTENT_EDIT
Definition: Permission.php:55
‪TYPO3\CMS\Core\Database\Query\QueryHelper\stripLogicalOperatorPrefix
‪static string stripLogicalOperatorPrefix(string $constraint)
Definition: QueryHelper.php:171
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\createShowItemTagAttributes
‪string createShowItemTagAttributes(string $arguments)
Definition: DatabaseRecordList.php:3394
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\createDownloadButtonForTable
‪createDownloadButtonForTable(string $table, int $totalItems)
Definition: DatabaseRecordList.php:901
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\recPath
‪mixed[] recPath($pid)
Definition: DatabaseRecordList.php:2133
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\isEditable
‪bool isEditable(string $table)
Definition: DatabaseRecordList.php:2249
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\setIsEditable
‪setIsEditable(bool $isEditable)
Definition: DatabaseRecordList.php:2239
‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder
Definition: PreviewUriBuilder.php:32
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\linkWrapItems
‪string linkWrapItems($table, $uid, $code, $row)
Definition: DatabaseRecordList.php:2736
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\columnSelector
‪string columnSelector(string $table)
Definition: DatabaseRecordList.php:2037
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\addDividerToCellGroup
‪addDividerToCellGroup(array &$cells)
Definition: DatabaseRecordList.php:3502
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static int[] intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:927
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\start
‪start($id, $table, $pointer, $search='', $levels=0, $showLimit=0)
Definition: DatabaseRecordList.php:2306
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$referenceCount
‪array $referenceCount
Definition: DatabaseRecordList.php:315
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$spaceIcon
‪string $spaceIcon
Definition: DatabaseRecordList.php:101
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\showOnlyTranslatedRecords
‪showOnlyTranslatedRecords(bool $showOnlyTranslatedRecords)
Definition: DatabaseRecordList.php:3383
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getBackendUserAuthentication
‪BackendUserAuthentication getBackendUserAuthentication()
Definition: DatabaseRecordList.php:3052
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getNoViewWithDokTypes
‪array getNoViewWithDokTypes(array $tsConfig)
Definition: DatabaseRecordList.php:3460
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:53
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$backendUserCache
‪array $backendUserCache
Definition: DatabaseRecordList.php:396
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$overrideUrlParameters
‪array $overrideUrlParameters
Definition: DatabaseRecordList.php:349
‪TYPO3\CMS\Core\Localization\LanguageService\getLL
‪string getLL($index)
Definition: LanguageService.php:121
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$languagesAllowedForUser
‪array $languagesAllowedForUser
Definition: DatabaseRecordList.php:384
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getFieldsToSelect
‪string[] getFieldsToSelect(string $table, array $columnsToRender)
Definition: DatabaseRecordList.php:480
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isLoaded
‪static bool isLoaded($key)
Definition: ExtensionManagementUtility.php:114
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$translateTools
‪TranslationConfigurationProvider $translateTools
Definition: DatabaseRecordList.php:165
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\$sortRev
‪bool $sortRev
Definition: DatabaseRecordList.php:268
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getOverridePageIdList
‪array getOverridePageIdList()
Definition: DatabaseRecordList.php:2941
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_RECYCLER
‪const DOKTYPE_RECYCLER
Definition: PageRepository.php:117
‪TYPO3\CMS\Recordlist\Event\ModifyRecordListHeaderColumnsEvent
Definition: ModifyRecordListHeaderColumnsEvent.php:26
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\setModuleData
‪setModuleData(array $moduleData=[])
Definition: DatabaseRecordList.php:2291
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getBackendUserInformation
‪getBackendUserInformation(int $backendUserId)
Definition: DatabaseRecordList.php:2160
‪TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList\getPreviewUriBuilder
‪PreviewUriBuilder getPreviewUriBuilder(string $table, array $row)
Definition: DatabaseRecordList.php:948
‪TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction
Definition: WorkspaceRestriction.php:40