‪TYPO3CMS  ‪main
RecordListController.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\EventDispatcher\EventDispatcherInterface;
21 use Psr\Http\Message\ResponseInterface;
22 use Psr\Http\Message\ServerRequestInterface;
36 use TYPO3\CMS\Backend\Utility\BackendUtility;
46 use TYPO3\CMS\Core\Imaging\IconSize;
54 
60 #[Controller]
62 {
66  protected ‪$pagePermissions;
67 
68  protected int ‪$id = 0;
69  protected string ‪$table = '';
70  protected string ‪$searchTerm = '';
71  protected array ‪$pageInfo = [];
72  protected string ‪$returnUrl = '';
73  protected array ‪$modTSconfig = [];
74  protected ?‪ModuleData ‪$moduleData = null;
75  protected bool ‪$allowClipboard = true;
76  protected bool ‪$allowSearch = true;
77 
78  public function ‪__construct(
79  protected readonly ‪IconFactory $iconFactory,
80  protected readonly ‪PageRenderer $pageRenderer,
81  protected readonly EventDispatcherInterface $eventDispatcher,
82  protected readonly ‪UriBuilder $uriBuilder,
83  protected readonly ‪ModuleTemplateFactory $moduleTemplateFactory,
84  ) {}
85 
86  public function ‪mainAction(ServerRequestInterface $request): ResponseInterface
87  {
88  $this->moduleData = $request->getAttribute('moduleData');
89 
90  $languageService = $this->‪getLanguageService();
91  $backendUser = $this->‪getBackendUserAuthentication();
92  $parsedBody = $request->getParsedBody();
93  $queryParams = $request->getQueryParams();
94 
95  $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf');
96 
97  BackendUtility::lockRecords();
98  $perms_clause = $backendUser->getPagePermsClause(‪Permission::PAGE_SHOW);
99  $this->id = (int)($parsedBody['id'] ?? $queryParams['id'] ?? 0);
100  $pointer = max(0, (int)($parsedBody['pointer'] ?? $queryParams['pointer'] ?? 0));
101  $this->table = (string)($parsedBody['table'] ?? $queryParams['table'] ?? '');
102  $this->searchTerm = trim((string)($parsedBody['searchTerm'] ?? $queryParams['searchTerm'] ?? ''));
103  $search_levels = (int)($parsedBody['search_levels'] ?? $queryParams['search_levels'] ?? 0);
104  $this->returnUrl = GeneralUtility::sanitizeLocalUrl((string)($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? ''));
105  $cmd = (string)($parsedBody['cmd'] ?? $queryParams['cmd'] ?? '');
106  $siteLanguages = $request->getAttribute('site')->getAvailableLanguages($this->‪getBackendUserAuthentication(), false, ‪$this->id);
107 
108  // Loading module configuration, clean up settings, current page and page access
109  $this->modTSconfig = BackendUtility::getPagesTSconfig($this->id)['mod.']['web_list.'] ?? [];
110  $pageinfo = BackendUtility::readPageAccess($this->id, $perms_clause);
111  $access = is_array($pageinfo);
112  $this->pageInfo = is_array($pageinfo) ? $pageinfo : [];
113  $this->pagePermissions = new ‪Permission($backendUser->calcPerms($pageinfo));
114 
115  // Check if Clipboard is allowed to be shown:
116  if (($this->modTSconfig['enableClipBoard'] ?? '') === 'activated') {
117  $this->allowClipboard = true;
118  } elseif (($this->modTSconfig['enableClipBoard'] ?? '') === 'selectable') {
119  $this->allowClipboard = true;
120  } elseif (($this->modTSconfig['enableClipBoard'] ?? '') === 'deactivated') {
121  $this->allowClipboard = false;
122  }
123 
124  // Check if SearchBox is allowed to be shown:
125  if (!($this->modTSconfig['disableSearchBox'] ?? false)) {
126  $this->allowSearch = true;
127  } elseif ($this->modTSconfig['disableSearchBox'] ?? false) {
128  $this->allowSearch = false;
129  }
130  // Overwrite to show search on search request
131  if (!empty($this->searchTerm)) {
132  $this->allowSearch = true;
133  $this->moduleData->set('searchBox', true);
134  }
135 
136  $dbList = GeneralUtility::makeInstance(DatabaseRecordList::class);
137  $dbList->setRequest($request);
138  $dbList->setModuleData($this->moduleData);
139  $dbList->calcPerms = ‪$this->pagePermissions;
140  $dbList->returnUrl = ‪$this->returnUrl;
141  $dbList->showClipboardActions = true;
142  $dbList->disableSingleTableView = $this->modTSconfig['disableSingleTableView'] ?? false;
143  $dbList->listOnlyInSingleTableMode = $this->modTSconfig['listOnlyInSingleTableView'] ?? false;
144  $dbList->hideTables = $this->modTSconfig['hideTables'] ?? '';
145  $dbList->hideTranslations = (string)($this->modTSconfig['hideTranslations'] ?? '');
146  $dbList->tableTSconfigOverTCA = $this->modTSconfig['table.'] ?? [];
147  $dbList->allowedNewTables = GeneralUtility::trimExplode(',', $this->modTSconfig['allowedNewTables'] ?? '', true);
148  $dbList->deniedNewTables = GeneralUtility::trimExplode(',', $this->modTSconfig['deniedNewTables'] ?? '', true);
149  $dbList->pageRow = ‪$this->pageInfo;
150  $dbList->modTSconfig = ‪$this->modTSconfig;
151  $dbList->setLanguagesAllowedForUser($siteLanguages);
152  $clickTitleMode = trim($this->modTSconfig['clickTitleMode'] ?? '');
153  $dbList->clickTitleMode = $clickTitleMode === '' ? 'edit' : $clickTitleMode;
154  if (isset($this->modTSconfig['tableDisplayOrder.'])) {
155  $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
156  $dbList->setTableDisplayOrder($typoScriptService->convertTypoScriptArrayToPlainArray($this->modTSconfig['tableDisplayOrder.']));
157  }
158  $clipboard = $this->‪initializeClipboard($request, $this->allowClipboard && (bool)$this->moduleData->get('clipBoard'));
159  $dbList->clipObj = $clipboard;
160  $additionalRecordListEvent = $this->eventDispatcher->dispatch(new ‪RenderAdditionalContentToRecordListEvent($request));
161 
162  $view = $this->moduleTemplateFactory->create($request);
163 
164  $tableListHtml = '';
165  if ($access || ($this->id === 0 && $search_levels !== 0 && $this->searchTerm !== '')) {
166  // If there is access to the page or root page is used for searching, then perform actions and render table list.
167  if ($cmd === 'delete' && $request->getMethod() === 'POST') {
168  $this->‪deleteRecords($request, $clipboard);
169  }
170  $dbList->start($this->id, $this->table, $pointer, $this->searchTerm, $search_levels);
171  $tableListHtml = $dbList->generateList();
172  }
173 
174  if (!$this->id) {
175  $title = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
176  } else {
177  $title = $pageinfo['title'] ?? '';
178  }
179  $languageSelectorHtml = '';
180  if ($this->id && !$this->searchTerm && !$cmd && !$this->table) {
181  // Show the selector to add page translations, but only when in "default" mode.
182  $languageSelectorHtml = $this->‪languageSelector($siteLanguages, $request->getAttribute('normalizedParams')->getRequestUri());
183  }
184  $pageTranslationsHtml = '';
185  if ($this->id && !$this->searchTerm && !$cmd && !$this->table && $this->‪showPageTranslations()) {
186  // Show page translation table if there are any and display is allowed.
187  $pageTranslationsHtml = $this->‪renderPageTranslations($dbList, $siteLanguages);
188  }
189  $searchBoxHtml = '';
190  if ($this->allowSearch && $this->moduleData->get('searchBox') && ($tableListHtml || !empty($this->searchTerm))) {
191  $searchBoxHtml = $this->‪renderSearchBox($request, $dbList, $this->searchTerm, $search_levels);
192  }
193  $clipboardHtml = '';
194  if ($this->allowClipboard && $this->moduleData->get('clipBoard') && ($tableListHtml || $clipboard->hasElements())) {
195  $clipboardHtml = '<hr class="spacer"><typo3-backend-clipboard-panel return-url="' . htmlspecialchars($dbList->listURL()) . '"></typo3-backend-clipboard-panel>';
196  }
197 
198  $view->setTitle($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:mlang_tabs_tab'), $title);
199  if (empty($tableListHtml)) {
200  $this->‪addNoRecordsFlashMessage($view, $this->table);
201  }
202  if ($pageinfo) {
203  $view->getDocHeaderComponent()->setMetaInformation($pageinfo);
204  }
205  $this->‪getDocHeaderButtons($view, $clipboard, $request, $this->table, $dbList->listURL(), []);
206  $view->assignMultiple([
207  'pageId' => $this->id,
208  'pageTitle' => $title,
209  'isPageEditable' => $this->‪isPageEditable(),
210  'additionalContentTop' => $additionalRecordListEvent->getAdditionalContentAbove(),
211  'languageSelectorHtml' => $languageSelectorHtml,
212  'pageTranslationsHtml' => $pageTranslationsHtml,
213  'searchBoxHtml' => $searchBoxHtml,
214  'tableListHtml' => $tableListHtml,
215  'clipboardHtml' => $clipboardHtml,
216  'additionalContentBottom' => $additionalRecordListEvent->getAdditionalContentBelow(),
217  ]);
218  return $view->renderResponse('RecordList');
219  }
220 
224  protected function ‪initializeClipboard(ServerRequestInterface $request, bool $isClipboardShown): ‪Clipboard
225  {
226  $clipboard = GeneralUtility::makeInstance(Clipboard::class);
227  $cmd = (string)($request->getParsedBody()['cmd'] ?? $request->getQueryParams()['cmd'] ?? '');
228  // Initialize - reads the clipboard content from the user session
229  $clipboard->initializeClipboard($request);
230  // Clipboard actions are handled:
231  $clipboardCommandArray = array_replace_recursive($request->getQueryParams()['CB'] ?? [], $request->getParsedBody()['CB'] ?? []);
232  if ($cmd === 'copyMarked' || $cmd === 'removeMarked') {
233  // Get CBC from request, and map the element values (true => copy, false => remove)
234  $CBC = array_map(static fn(): bool => ($cmd === 'copyMarked'), (array)($request->getParsedBody()['CBC'] ?? []));
235  $cmd_table = (string)($request->getParsedBody()['cmd_table'] ?? $request->getQueryParams()['cmd_table'] ?? '');
236  // Cleanup CBC
237  $clipboardCommandArray['el'] = $clipboard->cleanUpCBC($CBC, $cmd_table);
238  }
239  if (!$isClipboardShown) {
240  // If the clipboard is NOT shown, set the pad to 'normal'.
241  $clipboardCommandArray['setP'] = 'normal';
242  }
243  // Execute commands.
244  $clipboard->setCmd($clipboardCommandArray);
245  // Clean up pad
246  $clipboard->cleanCurrent();
247  // Save the clipboard content
248  $clipboard->endClipboard();
249  return $clipboard;
250  }
251 
252  protected function ‪deleteRecords(ServerRequestInterface $request, ‪Clipboard $clipboard): void
253  {
254  // This is the 'delete' button in table header with multi record selection.
255  // The clipboard object is used to clean up the submitted entries to only the selected table.
256  $parsedBody = $request->getParsedBody();
257  $items = $clipboard->‪cleanUpCBC((array)($parsedBody['CBC'] ?? []), (string)($parsedBody['cmd_table'] ?? ''), true);
258  if (!empty($items)) {
259  // Create data handler command array
260  $dataHandlerCmd = [];
261  foreach ($items as $iK => $value) {
262  $iKParts = explode('|', (string)$iK);
263  $dataHandlerCmd[$iKParts[0]][$iKParts[1]]['delete'] = 1;
264  }
265  $tce = GeneralUtility::makeInstance(DataHandler::class);
266  $tce->start([], $dataHandlerCmd);
267  $tce->process_cmdmap();
268  if (isset($dataHandlerCmd['pages'])) {
269  BackendUtility::setUpdateSignal('updatePageTree');
270  }
271  $tce->printLogErrorMessages();
272  }
273  }
274 
275  protected function ‪renderSearchBox(ServerRequestInterface $request, ‪DatabaseRecordList $dbList, string $searchWord, int $searchLevels): string
276  {
277  $searchBox = GeneralUtility::makeInstance(RecordSearchBoxComponent::class)
278  ->setAllowedSearchLevels((array)($this->modTSconfig['searchLevel.']['items.'] ?? []))
279  ->setSearchWord($searchWord)
280  ->setSearchLevel($searchLevels)
281  ->render($request, $dbList->‪listURL('', '-1', 'pointer,searchTerm'));
282  return $searchBox;
283  }
284 
288  protected function ‪getDocHeaderButtons(‪ModuleTemplate $view, ‪Clipboard $clipboard, ServerRequestInterface $request, string ‪$table, string $listUrl, array $moduleSettings): void
289  {
290  $queryParams = $request->getQueryParams();
291  $buttonBar = $view->‪getDocHeaderComponent()->getButtonBar();
292  $lang = $this->‪getLanguageService();
293  // New record on pages that are not locked by editlock
294  if (!($this->modTSconfig['noCreateRecordsLink'] ?? false) && $this->‪editLockPermissions()) {
295  $newRecordButton = $buttonBar->makeLinkButton()
296  ->setHref((string)$this->uriBuilder->buildUriFromRoute('db_new', ['id' => $this->id, 'returnUrl' => $listUrl]))
297  ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:newRecordGeneral'))
298  ->setShowLabelText(true)
299  ->setIcon($this->iconFactory->getIcon('actions-plus', IconSize::SMALL));
300  $buttonBar->addButton($newRecordButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 10);
301  }
302 
303  if ($this->id !== 0) {
304  if ($this->‪canCreatePreviewLink()) {
305  $previewDataAttributes = ‪PreviewUriBuilder::create((int)$this->id)
306  ->withRootLine(BackendUtility::BEgetRootLine($this->id))
307  ->buildDispatcherDataAttributes();
308  $viewButton = $buttonBar->makeLinkButton()
309  ->setHref('#')
310  ->setDataAttributes($previewDataAttributes ?? [])
311  ->setDisabled(!$previewDataAttributes)
312  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
313  ->setIcon($this->iconFactory->getIcon('actions-view-page', IconSize::SMALL))
314  ->setShowLabelText(true);
315  $buttonBar->addButton($viewButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 15);
316  }
317  // If edit permissions are set, see BackendUserAuthentication
318  if ($this->‪isPageEditable()) {
319  // Edit
320  $editLink = $this->uriBuilder->buildUriFromRoute('record_edit', [
321  'edit' => [
322  'pages' => [
323  $this->id => 'edit',
324  ],
325  ],
326  'returnUrl' => $listUrl,
327  ]);
328  $editButton = $buttonBar->makeLinkButton()
329  ->setHref((string)$editLink)
330  ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:editPage'))
331  ->setShowLabelText(true)
332  ->setIcon($this->iconFactory->getIcon('actions-page-open', IconSize::SMALL));
333  $buttonBar->addButton($editButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 20);
334  }
335  }
336 
337  // Paste
338  if (($this->pagePermissions->createPagePermissionIsGranted() || $this->pagePermissions->editContentPermissionIsGranted()) && $this->editLockPermissions()) {
339  $elFromTable = $clipboard->‪elFromTable();
340  if (!empty($elFromTable)) {
341  $confirmMessage = $clipboard->‪confirmMsgText('pages', $this->pageInfo, 'into', $elFromTable);
342  $pasteButton = $buttonBar->makeLinkButton()
343  ->setHref($clipboard->‪pasteUrl('', $this->id))
344  ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:clip_paste'))
345  ->setClasses('t3js-modal-trigger')
346  ->setDataAttributes([
347  'severity' => 'warning',
348  'bs-content' => $confirmMessage,
349  'title' => $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:clip_paste'),
350  ])
351  ->setIcon($this->iconFactory->getIcon('actions-document-paste-into', IconSize::SMALL))
352  ->setShowLabelText(true);
353  $buttonBar->addButton($pasteButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 40);
354  }
355  }
356  // Cache
357  if ($this->id !== 0) {
358  $clearCacheButton = $buttonBar->makeLinkButton()
359  ->setHref('#')
360  ->setDataAttributes(['id' => $this->id])
361  ->setClasses('t3js-clear-page-cache')
362  ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.clear_cache'))
363  ->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', IconSize::SMALL));
364  $buttonBar->addButton($clearCacheButton, ‪ButtonBar::BUTTON_POSITION_RIGHT);
365  }
366  if (‪$table && (!isset($this->modTSconfig['noExportRecordsLinks'])
367  || (isset($this->modTSconfig['noExportRecordsLinks'])
368  && !$this->modTSconfig['noExportRecordsLinks']))
369  ) {
370  // Export
372  ‪$url = (string)$this->uriBuilder->buildUriFromRoute('tx_impexp_export', ['tx_impexp' => ['list' => [‪$table . ':' . $this->id]]]);
373  $exportButton = $buttonBar->makeLinkButton()
374  ->setHref(‪$url)
375  ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.export'))
376  ->setIcon($this->iconFactory->getIcon('actions-document-export-t3d', IconSize::SMALL))
377  ->setShowLabelText(true);
378  $buttonBar->addButton($exportButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 50);
379  }
380  }
381  // Reload
382  $reloadButton = $buttonBar->makeLinkButton()
383  ->setHref($listUrl)
384  ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.reload'))
385  ->setIcon($this->iconFactory->getIcon('actions-refresh', IconSize::SMALL));
386  $buttonBar->addButton($reloadButton, ‪ButtonBar::BUTTON_POSITION_RIGHT);
387 
388  // ViewMode
389  $viewModeItems = [];
390  if ($this->allowSearch) {
391  $viewModeItems[] = GeneralUtility::makeInstance(DropDownToggle::class)
392  ->setActive((bool)$this->moduleData->get('searchBox'))
393  ->setHref($this->‪createModuleUri($request, ['searchBox' => $this->moduleData->get('searchBox') ? 0 : 1, 'searchTerm' => '']))
394  ->setLabel($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.view.showSearch'))
395  ->setIcon($this->iconFactory->getIcon('actions-search'));
396  }
397  if ($this->allowClipboard) {
398  $viewModeItems[] = GeneralUtility::makeInstance(DropDownToggle::class)
399  ->setActive((bool)$this->moduleData->get('clipBoard'))
400  ->setHref($this->‪createModuleUri($request, ['clipBoard' => $this->moduleData->get('clipBoard') ? 0 : 1]))
401  ->setLabel($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.view.showClipboard'))
402  ->setIcon($this->iconFactory->getIcon('actions-clipboard'));
403  }
404  if (!empty($viewModeItems)) {
405  $viewModeButton = $buttonBar->makeDropDownButton()
406  ->setLabel($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.view'))
407  ->setShowLabelText(true);
408  foreach ($viewModeItems as $viewModeItem) {
410  $viewModeButton->addItem($viewModeItem);
411  }
412  $buttonBar->addButton($viewModeButton, ‪ButtonBar::BUTTON_POSITION_RIGHT, 3);
413  }
414 
415  // Shortcut
416  $shortCutButton = $buttonBar->makeShortcutButton()->setRouteIdentifier('web_list');
417  $arguments = [
418  'id' => ‪$this->id,
419  ];
420  $potentialArguments = [
421  'pointer',
422  'table',
423  'searchTerm',
424  'search_levels',
425  'sortField',
426  'sortRev',
427  ];
428  foreach ($potentialArguments as $argument) {
429  if (!empty($queryParams[$argument])) {
430  $arguments[$argument] = $queryParams[$argument];
431  }
432  }
433  foreach ($moduleSettings as $moduleSettingKey => $moduleSettingValue) {
434  $arguments['GET'][$moduleSettingKey] = $moduleSettingValue;
435  }
436  $shortCutButton->setArguments($arguments);
437  $shortCutButton->setDisplayName($this->‪getShortcutTitle($arguments));
438  $buttonBar->addButton($shortCutButton, ‪ButtonBar::BUTTON_POSITION_RIGHT);
439 
440  // Back
441  if ($this->returnUrl) {
442  $backButton = $buttonBar->makeLinkButton()
443  ->setHref($this->returnUrl)
444  ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
445  ->setShowLabelText(true)
446  ->setIcon($this->iconFactory->getIcon('actions-view-go-back', IconSize::SMALL));
447  $buttonBar->addButton($backButton, ‪ButtonBar::BUTTON_POSITION_LEFT);
448  }
449  }
450 
451  protected function ‪addNoRecordsFlashMessage(‪ModuleTemplate $view, string ‪$table)
452  {
453  $languageService = $this->‪getLanguageService();
454  if ($table && isset(‪$GLOBALS['TCA'][‪$table]['ctrl']['title'])) {
455  $message = sprintf($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:noRecordsOfTypeOnThisPage'), $languageService->sL(‪$GLOBALS['TCA'][‪$table]['ctrl']['title']));
456  } else {
457  $message = $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:noRecordsOnThisPage');
458  }
459  $view->‪addFlashMessage($message, '', ContextualFeedbackSeverity::INFO);
460  }
461 
470  protected function ‪languageSelector(array $siteLanguages, string $requestUri): string
471  {
472  if (!$this->‪getBackendUserAuthentication()->check('tables_modify', 'pages')) {
473  return '';
474  }
475  $availableTranslations = [];
476  foreach ($siteLanguages as $siteLanguage) {
477  if ($siteLanguage->getLanguageId() === 0) {
478  continue;
479  }
480  $availableTranslations[$siteLanguage->getLanguageId()] = $siteLanguage->getTitle();
481  }
482  // Then, subtract the languages which are already on the page:
483  $localizationParentField = ‪$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
484  $languageField = ‪$GLOBALS['TCA']['pages']['ctrl']['languageField'];
485  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
486  $queryBuilder->getRestrictions()->removeAll()
487  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
488  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->‪getBackendUserAuthentication()->workspace));
489  $statement = $queryBuilder->select('uid', $languageField)
490  ->from('pages')
491  ->where(
492  $queryBuilder->expr()->eq(
493  $localizationParentField,
494  $queryBuilder->createNamedParameter($this->id, ‪Connection::PARAM_INT)
495  )
496  )
497  ->executeQuery();
498  while ($pageTranslation = $statement->fetchAssociative()) {
499  unset($availableTranslations[(int)$pageTranslation[$languageField]]);
500  }
501  // If any languages are left, make selector:
502  if (!empty($availableTranslations)) {
503  ‪$output = '<option value="">' . htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:new_language')) . '</option>';
504  foreach ($availableTranslations as $languageUid => $languageTitle) {
505  // Build localize command URL to DataHandler (tce_db)
506  // which redirects to FormEngine (record_edit)
507  // which, when finished editing should return back to the current page (returnUrl)
508  $parameters = [
509  'justLocalized' => 'pages:' . $this->id . ':' . $languageUid,
510  'returnUrl' => $requestUri,
511  ];
512  $redirectUrl = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $parameters);
513  $params = [];
514  $params['redirect'] = $redirectUrl;
515  $params['cmd']['pages'][‪$this->id]['localize'] = $languageUid;
516  ‪$targetUrl = (string)$this->uriBuilder->buildUriFromRoute('tce_db', $params);
517  ‪$output .= '<option value="' . htmlspecialchars(‪$targetUrl) . '">' . htmlspecialchars($languageTitle) . '</option>';
518  }
519 
520  return ''
521  . '<div class="form-row">'
522  . '<div class="form-group">'
523  . '<select class="form-select" name="createNewLanguage" data-global-event="change" data-action-navigate="$value">'
524  . ‪$output
525  . '</select>'
526  . '</div>'
527  . '</div>';
528  }
529  return '';
530  }
531 
536  protected function ‪canCreatePreviewLink(): bool
537  {
538  if (isset($this->modTSconfig['noViewWithDokTypes'])) {
539  $noViewDokTypes = GeneralUtility::trimExplode(',', $this->modTSconfig['noViewWithDokTypes'], true);
540  } else {
541  $noViewDokTypes = [
543  ];
544  }
545  return !in_array($this->pageInfo['doktype'] ?? 0, $noViewDokTypes);
546  }
547 
551  protected function ‪editLockPermissions(): bool
552  {
553  return $this->‪getBackendUserAuthentication()->isAdmin() || !($this->pageInfo['editlock'] ?? false);
554  }
555 
559  protected function ‪getShortcutTitle(array $arguments): string
560  {
561  $pageTitle = '';
562  $tableTitle = '';
563  $languageService = $this->‪getLanguageService();
564  if (isset($arguments['table'])) {
565  $tableTitle = ': ' . (isset(‪$GLOBALS['TCA'][$arguments['table']]['ctrl']['title']) ? $languageService->sL(‪$GLOBALS['TCA'][$arguments['table']]['ctrl']['title']) : $arguments['table']);
566  }
567  if ($this->pageInfo !== []) {
568  $pageTitle = BackendUtility::getRecordTitle('pages', $this->pageInfo);
569  }
570  return trim(sprintf(
571  $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:shortcut.title'),
572  $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_web_list.xlf:mlang_tabs_tab'),
573  $tableTitle,
574  $pageTitle,
575  $this->id
576  ));
577  }
578 
579  protected function ‪showPageTranslations(): bool
580  {
581  if (!$this->‪getBackendUserAuthentication()->check('tables_select', 'pages')) {
582  return false;
583  }
584  if (isset($this->modTSconfig['table.']['pages.']['hideTable'])) {
585  return !$this->modTSconfig['table.']['pages.']['hideTable'];
586  }
587  $hideTables = $this->modTSconfig['hideTables'] ?? '';
588  return !(‪$GLOBALS['TCA']['pages']['ctrl']['hideTable'] ?? false)
589  && $hideTables !== '*'
590  && !in_array('pages', GeneralUtility::trimExplode(',', $hideTables), true);
591  }
592 
593  protected function ‪renderPageTranslations(‪DatabaseRecordList $dbList, array $siteLanguages): string
594  {
595  $pageTranslationsDatabaseRecordList = clone $dbList;
596  $pageTranslationsDatabaseRecordList->id = ‪$this->id;
597  $pageTranslationsDatabaseRecordList->listOnlyInSingleTableMode = false;
598  $pageTranslationsDatabaseRecordList->disableSingleTableView = true;
599  $pageTranslationsDatabaseRecordList->deniedNewTables = ['pages'];
600  $pageTranslationsDatabaseRecordList->hideTranslations = '';
601  $pageTranslationsDatabaseRecordList->setLanguagesAllowedForUser($siteLanguages);
602  $pageTranslationsDatabaseRecordList->showOnlyTranslatedRecords(true);
603  return $pageTranslationsDatabaseRecordList->getTable('pages');
604  }
605 
606  protected function ‪createModuleUri(ServerRequestInterface $request, array $params = []): string
607  {
608  $params = array_replace_recursive([
609  'id' => $this->id,
610  'table' => $this->table,
611  'searchTerm' => $this->searchTerm,
612  ], $params);
613 
614  $params = array_filter($params, static function (mixed $value): bool {
615  return $value !== null && trim((string)$value) !== '';
616  });
617 
618  return (string)$this->uriBuilder->buildUriFromRequest($request, $params);
619  }
620 
624  protected function ‪isPageEditable(): bool
625  {
626  if (‪$GLOBALS['TCA']['pages']['ctrl']['readOnly'] ?? false) {
627  return false;
628  }
629  $backendUser = $this->‪getBackendUserAuthentication();
630  if ($backendUser->isAdmin()) {
631  return true;
632  }
633  if (‪$GLOBALS['TCA']['pages']['ctrl']['adminOnly'] ?? false) {
634  return false;
635  }
636 
637  return $this->pageInfo !== []
638  && $this->‪editLockPermissions()
639  && $this->pagePermissions->editPagePermissionIsGranted()
640  && $backendUser->checkLanguageAccess(0)
641  && $backendUser->check('tables_modify', 'pages');
642  }
643 
645  {
646  return ‪$GLOBALS['BE_USER'];
647  }
648 
649  protected function ‪getLanguageService(): ‪LanguageService
650  {
651  return ‪$GLOBALS['LANG'];
652  }
653 }
‪TYPO3\CMS\Backend\Controller\RecordListController\$pageInfo
‪array $pageInfo
Definition: RecordListController.php:70
‪TYPO3\CMS\Core\DataHandling\DataHandler
Definition: DataHandler.php:95
‪TYPO3\CMS\Backend\Controller\RecordListController\canCreatePreviewLink
‪canCreatePreviewLink()
Definition: RecordListController.php:535
‪TYPO3\CMS\Backend\Controller\RecordListController\$searchTerm
‪string $searchTerm
Definition: RecordListController.php:69
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:50
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_LEFT
‪const BUTTON_POSITION_LEFT
Definition: ButtonBar.php:37
‪TYPO3\CMS\Backend\Template\Components\ButtonBar
Definition: ButtonBar.php:33
‪TYPO3\CMS\Backend\Clipboard\Clipboard
Definition: Clipboard.php:48
‪TYPO3\CMS\Backend\RecordList\DatabaseRecordList\listURL
‪string listURL($altId='', $table='-1', $exclList='')
Definition: DatabaseRecordList.php:2656
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:33
‪TYPO3\CMS\Backend\Clipboard\Clipboard\pasteUrl
‪pasteUrl(string $table, $identifier, bool $setRedirect=true, array $update=null)
Definition: Clipboard.php:537
‪TYPO3\CMS\Backend\Controller\RecordListController\renderSearchBox
‪renderSearchBox(ServerRequestInterface $request, DatabaseRecordList $dbList, string $searchWord, int $searchLevels)
Definition: RecordListController.php:274
‪TYPO3\CMS\Backend\Controller\RecordListController\editLockPermissions
‪editLockPermissions()
Definition: RecordListController.php:550
‪TYPO3\CMS\Backend\Module\ModuleData
Definition: ModuleData.php:30
‪TYPO3\CMS\Backend\Template\ModuleTemplate\addFlashMessage
‪addFlashMessage(string $messageBody, string $messageTitle='', ContextualFeedbackSeverity $severity=ContextualFeedbackSeverity::OK, bool $storeInSession=true)
Definition: ModuleTemplate.php:229
‪TYPO3\CMS\Backend\Attribute\Controller
Definition: Controller.php:25
‪TYPO3\CMS\Redirects\Message\$targetUrl
‪identifier readonly UriInterface $targetUrl
Definition: RedirectWasHitMessage.php:33
‪TYPO3\CMS\Backend\Controller\RecordListController\$id
‪int $id
Definition: RecordListController.php:67
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Backend\RecordList\DatabaseRecordList
Definition: DatabaseRecordList.php:68
‪TYPO3\CMS\Backend\Controller\RecordListController\$allowSearch
‪bool $allowSearch
Definition: RecordListController.php:75
‪TYPO3\CMS\Backend\Controller\RecordListController\$moduleData
‪ModuleData $moduleData
Definition: RecordListController.php:73
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:46
‪TYPO3\CMS\Backend\Template\Components\Buttons\DropDown\DropDownToggle
Definition: DropDownToggle.php:39
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isLoaded
‪static isLoaded(string $key)
Definition: ExtensionManagementUtility.php:54
‪TYPO3\CMS\Backend\Controller\RecordListController\$pagePermissions
‪Permission $pagePermissions
Definition: RecordListController.php:65
‪TYPO3\CMS\Backend\Controller\RecordListController\createModuleUri
‪createModuleUri(ServerRequestInterface $request, array $params=[])
Definition: RecordListController.php:605
‪TYPO3\CMS\Backend\Controller\RecordListController\renderPageTranslations
‪renderPageTranslations(DatabaseRecordList $dbList, array $siteLanguages)
Definition: RecordListController.php:592
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:31
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪TYPO3\CMS\Backend\Controller\RecordListController\getLanguageService
‪getLanguageService()
Definition: RecordListController.php:648
‪TYPO3\CMS\Core\Page\PageRenderer
Definition: PageRenderer.php:46
‪TYPO3\CMS\Backend\Controller\Event\RenderAdditionalContentToRecordListEvent
Definition: RenderAdditionalContentToRecordListEvent.php:26
‪TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
Definition: RouteNotFoundException.php:21
‪TYPO3\CMS\Backend\Template\Components\Buttons\DropDown\DropDownItemInterface
Definition: DropDownItemInterface.php:19
‪TYPO3\CMS\Backend\Controller\RecordListController\addNoRecordsFlashMessage
‪addNoRecordsFlashMessage(ModuleTemplate $view, string $table)
Definition: RecordListController.php:450
‪TYPO3\CMS\Backend\Controller\RecordListController\$modTSconfig
‪array $modTSconfig
Definition: RecordListController.php:72
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder\create
‪static static create(int $pageId)
Definition: PreviewUriBuilder.php:65
‪TYPO3\CMS\Backend\Controller\RecordListController\initializeClipboard
‪initializeClipboard(ServerRequestInterface $request, bool $isClipboardShown)
Definition: RecordListController.php:223
‪TYPO3\CMS\Backend\Controller\RecordListController\mainAction
‪mainAction(ServerRequestInterface $request)
Definition: RecordListController.php:85
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:60
‪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:114
‪TYPO3\CMS\Backend\Clipboard\Clipboard\cleanUpCBC
‪array cleanUpCBC(array $CBarr, string $table, bool $removeDeselected=false)
Definition: Clipboard.php:223
‪TYPO3\CMS\Backend\Controller\RecordListController\$allowClipboard
‪bool $allowClipboard
Definition: RecordListController.php:74
‪TYPO3\CMS\Backend\View\RecordSearchBoxComponent
Definition: RecordSearchBoxComponent.php:28
‪TYPO3\CMS\Core\TypoScript\TypoScriptService
Definition: TypoScriptService.php:27
‪$output
‪$output
Definition: annotationChecker.php:119
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:39
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Backend\Template\ModuleTemplate\getDocHeaderComponent
‪getDocHeaderComponent()
Definition: ModuleTemplate.php:181
‪TYPO3\CMS\Backend\Controller\RecordListController\languageSelector
‪string languageSelector(array $siteLanguages, string $requestUri)
Definition: RecordListController.php:469
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Backend\Controller\RecordListController\__construct
‪__construct(protected readonly IconFactory $iconFactory, protected readonly PageRenderer $pageRenderer, protected readonly EventDispatcherInterface $eventDispatcher, protected readonly UriBuilder $uriBuilder, protected readonly ModuleTemplateFactory $moduleTemplateFactory,)
Definition: RecordListController.php:77
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Backend\Controller\RecordListController\getBackendUserAuthentication
‪getBackendUserAuthentication()
Definition: RecordListController.php:643
‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder
Definition: PreviewUriBuilder.php:44
‪TYPO3\CMS\Backend\Controller\RecordListController\showPageTranslations
‪showPageTranslations()
Definition: RecordListController.php:578
‪TYPO3\CMS\Backend\Controller\RecordListController\getDocHeaderButtons
‪getDocHeaderButtons(ModuleTemplate $view, Clipboard $clipboard, ServerRequestInterface $request, string $table, string $listUrl, array $moduleSettings)
Definition: RecordListController.php:287
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:61
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:48
‪TYPO3\CMS\Backend\Controller\RecordListController\getShortcutTitle
‪getShortcutTitle(array $arguments)
Definition: RecordListController.php:558
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Backend\Controller\RecordListController\$returnUrl
‪string $returnUrl
Definition: RecordListController.php:71
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_RIGHT
‪const BUTTON_POSITION_RIGHT
Definition: ButtonBar.php:42
‪TYPO3\CMS\Backend\Clipboard\Clipboard\confirmMsgText
‪string confirmMsgText(string $table, $reference, string $type, array $selectedElements, string $columnLabel='')
Definition: Clipboard.php:564
‪TYPO3\CMS\Backend\Controller
Definition: AboutController.php:18
‪TYPO3\CMS\Backend\Controller\RecordListController\$table
‪string $table
Definition: RecordListController.php:68
‪TYPO3\CMS\Backend\Clipboard\Clipboard\elFromTable
‪array elFromTable(string $matchTable='', string $padIdentifier='')
Definition: Clipboard.php:699
‪TYPO3\CMS\Backend\Controller\RecordListController\isPageEditable
‪isPageEditable()
Definition: RecordListController.php:623
‪TYPO3\CMS\Backend\Controller\RecordListController\deleteRecords
‪deleteRecords(ServerRequestInterface $request, Clipboard $clipboard)
Definition: RecordListController.php:251
‪TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction
Definition: WorkspaceRestriction.php:39
‪TYPO3\CMS\Backend\Controller\RecordListController
Definition: RecordListController.php:62