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