*/ private string $identifier; /** * Specific TSconfig for the current instance (corresponds to TCEMAIN.linkHandler.record..configuration) */ private array $configuration = []; /** * Parts of the current link */ private array $linkParts = []; private int $expandPage = 0; public function __construct( private readonly ElementBrowserRecordList $elementBrowserRecordList, private readonly RecordSearchBoxComponent $recordSearchBoxComponent, private readonly LinkService $linkService, private readonly TcaSchemaFactory $tcaSchemaFactory, ) { parent::__construct(); } public function initialize(AbstractLinkBrowserController $linkBrowser, $identifier, array $configuration) { parent::initialize($linkBrowser, $identifier, $configuration); $this->identifier = $identifier; if (empty($configuration['table'])) { throw new \LogicException( 'Page TSconfig TCEMAIN.linkHandler.' . $identifier . '.configuration.table is mandatory and must be set to a table name.', 1657960610 ); } $this->configuration = $configuration; } /** * Checks if this is the right handler for the given link. * Also stores information locally about currently linked record. * * @param array $linkParts Link parts as returned from TypoLinkCodecService */ public function canHandleLink(array $linkParts): bool { if (!$linkParts['url'] || !isset($linkParts['url']['identifier']) || $linkParts['url']['identifier'] !== $this->identifier) { return false; } $data = $linkParts['url']; // Get the related record $table = $this->configuration['table']; $record = BackendUtility::getRecord($table, $data['uid']); if ($record === null) { $linkParts['title'] = $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:recordNotFound'); } else { $linkParts['pid'] = (int)$record['pid']; $linkParts['title'] = !empty($linkParts['title']) ? $linkParts['title'] : BackendUtility::getRecordTitle($table, $record); } $linkParts['tableName'] = $this->tcaSchemaFactory->get($table)->getTitle($this->getLanguageService()->sL(...)); $linkParts['url']['type'] = $linkParts['type']; $this->linkParts = $linkParts; return true; } /** * Formats information for the current record for HTML output. */ public function formatCurrentUrl(): string { return sprintf( '%s: %s [uid: %d]', $this->linkParts['tableName'], $this->linkParts['title'], $this->linkParts['url']['uid'] ); } /** * Renders the link handler. */ public function render(ServerRequestInterface $request): string { $this->pageRenderer->loadJavaScriptModule('@typo3/backend/record-link-handler.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/recordlist.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/record-search.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/column-selector-button.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/tree/page-browser.js'); $this->getBackendUser()->initializeWebmountsForElementBrowser(); // Define the current page if (isset($request->getQueryParams()['expandPage'])) { $this->expandPage = (int)$request->getQueryParams()['expandPage']; } elseif (isset($this->configuration['storagePid'])) { $this->expandPage = (int)$this->configuration['storagePid']; } elseif (isset($this->linkParts['pid'])) { $this->expandPage = (int)$this->linkParts['pid']; } $pageTreeMountPoints = (string)($this->configuration['pageTreeMountPoints'] ?? ''); $this->view->assignMultiple([ 'treeEnabled' => (bool)($this->configuration['hidePageTree'] ?? false) === false, 'pageTreeMountPoints' => GeneralUtility::intExplode(',', $pageTreeMountPoints, true), 'recordList' => $this->renderTableRecords($request), 'initialNavigationWidth' => $this->getBackendUser()->uc['selector']['navigation']['width'] ?? 250, 'treeActions' => ['link'], ]); return $this->view->render('LinkBrowser/Record'); } /** * Returns attributes for the body tag. * * @return string[] Array of body-tag attributes */ public function getBodyTagAttributes(): array { $attributes = [ 'data-linkbrowser-identifier' => 't3://record?identifier=' . $this->identifier . '&uid=', ]; if (!empty($this->linkParts)) { $attributes['data-linkbrowser-current-link'] = $this->linkService->asString($this->linkParts['url']); } return $attributes; } /** * Returns all parameters needed to build a URL with all the necessary information. * * @param array $values Array of values to include into the parameters or which might influence the parameters * @return array Array of parameters which have to be added to URLs */ public function getUrlParameters(array $values): array { $pid = isset($values['pid']) ? (int)$values['pid'] : $this->expandPage; $parameters = [ 'expandPage' => $pid, ]; return array_merge( $this->linkBrowser->getUrlParameters($values), ['P' => $this->linkBrowser->getParameters()], $parameters ); } /** * Render elements of configured table */ private function renderTableRecords(ServerRequestInterface $request): string { $html = []; $backendUser = $this->getBackendUser(); $selectedPage = $this->expandPage; if ($selectedPage < 0 || !$backendUser->isInWebMount($selectedPage)) { return ''; } $table = $this->configuration['table']; $modTSconfig = BackendUtility::getPagesTSconfig($selectedPage)['mod.']['web_list.'] ?? []; $permsClause = $backendUser->getPagePermsClause(Permission::PAGE_SHOW); $pageInfo = BackendUtility::readPageAccess($selectedPage, $permsClause); $selectedTable = (string)($request->getParsedBody()['table'] ?? $request->getQueryParams()['table'] ?? ''); $searchWord = (string)($request->getParsedBody()['searchTerm'] ?? $request->getQueryParams()['searchTerm'] ?? ''); $pointer = (int)($request->getParsedBody()['pointer'] ?? $request->getQueryParams()['pointer'] ?? 0); $searchLevels = (int)($request->getParsedBody()['search_levels'] ?? $request->getQueryParams()['search_levels'] ?? $modTSconfig['searchLevel.']['default'] ?? 0); $existingModuleData = $backendUser->getModuleData('records'); $moduleData = new ModuleData('records', is_array($existingModuleData) ? $existingModuleData : []); // If table is 'pages', add a pre-entry to make selected page selectable directly. $mainPageRecord = BackendUtility::getRecordWSOL('pages', $selectedPage); if (is_array($mainPageRecord)) { $pText = htmlspecialchars(BackendUtility::cropToTitleLength($mainPageRecord['title'])); $html[] = '

' . $this->iconFactory->getIconForRecord('pages', $mainPageRecord, IconSize::SMALL)->render() . ' '; if ($table === 'pages') { $html[] = ''; $html[] = '' . $this->iconFactory->getIcon('actions-plus', IconSize::SMALL)->render() . ''; $html[] = '' . $pText . ''; $html[] = ''; } else { $html[] = $pText; } $html[] = '

'; } $dbList = $this->elementBrowserRecordList; $dbList->setRequest($request); $dbList->setModuleData($moduleData); $dbList->setOverrideUrlParameters(array_merge($this->getUrlParameters([]), ['mode' => 'db', 'expandPage' => $selectedPage]), $request); $dbList->setIsEditable(false); $dbList->calcPerms = new Permission($backendUser->calcPerms($pageInfo)); $dbList->noControlPanels = true; $dbList->clickMenuEnabled = false; $dbList->displayRecordDownload = false; $dbList->tableList = $table; $dbList->start($selectedPage, $selectedTable, MathUtility::forceIntegerInRange($pointer, 0, 100000), $searchWord, $searchLevels); $html[] = $this->recordSearchBoxComponent ->setAllowedSearchLevels((array)($modTSconfig['searchLevel.']['items.'] ?? [])) ->setSearchLevel($searchLevels) ->setSearchWord($searchWord) ->render($request, $dbList->listURL('', null, 'pointer,searchTerm')); $html[] = $dbList->generateList(); return implode("\n", $html); } }