pageDoktypeRegistry = GeneralUtility::makeInstance(PageDoktypeRegistry::class); } /** * Checks if this is the handler for the given link * * The handler may store this information locally for later usage. * * @param array $linkParts Link parts as returned from TypoLinkCodecService * * @return bool */ public function canHandleLink(array $linkParts) { if (empty($linkParts['url'] ?? '')) { return false; } $data = $linkParts['url']; // Resolve "current" to the actual page ID from the link browser context if (($data['pageuid'] ?? '') === 'current') { $currentPageId = (int)($this->linkBrowser->getParameters()['pid'] ?? 0); if ($currentPageId > 0) { $linkParts['url']['pageuid'] = $currentPageId; $data = $linkParts['url']; } } // Check if the page still exists if ((int)($data['pageuid'] ?? 0) > 0) { $pageRow = BackendUtility::getRecordWSOL('pages', $data['pageuid']); if (!$pageRow) { return false; } } else { return false; } $this->linkParts = $linkParts; return true; } /** * Format the current link for HTML output * * @return string */ public function formatCurrentUrl() { $lang = $this->getLanguageService(); $id = (int)$this->linkParts['url']['pageuid']; $idInfo = 'ID: ' . $id . (!empty($this->linkParts['url']['fragment']) ? ', #' . $this->linkParts['url']['fragment'] : ''); $permsClause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW); $pageRecord = BackendUtility::readPageAccess($id, $permsClause); if ($pageRecord === false) { return $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:page') . ' ' . $idInfo; } $pageTitle = $pageRecord['title'] ?? ''; return $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:page') . ($pageTitle ? ' \'' . BackendUtility::cropToTitleLength($pageTitle) . '\'' : '') . ' (' . $idInfo . ')'; } /** * Render the link handler */ public function render(ServerRequestInterface $request): string { $this->pageRenderer->loadJavaScriptModule('@typo3/backend/page-link-handler.js'); $this->pageRenderer->loadJavaScriptModule('@typo3/backend/tree/page-browser.js'); $this->getBackendUser()->initializeWebmountsForElementBrowser(); $this->expandPage = isset($request->getQueryParams()['expandPage']) ? (int)$request->getQueryParams()['expandPage'] : 0; $this->view->assign('initialNavigationWidth', $this->getBackendUser()->uc['selector']['navigation']['width'] ?? 250); $this->view->assign('treeActions', ['link']); $this->getRecordsOnExpandedPage($this->expandPage); return $this->view->render('LinkBrowser/Page'); } /** * This adds all content elements on a page to the view and lets you create a link to the element. * * @param int $pageId Page uid to expand */ protected function getRecordsOnExpandedPage($pageId) { // If there is an anchor value (content element reference) in the element reference, then force an ID to expand: if (!$pageId && isset($this->linkParts['url']['fragment'])) { // Set to the current link page id. $pageId = $this->linkParts['url']['pageuid']; } $linkService = GeneralUtility::makeInstance(LinkService::class); $this->view->assign('expandedPage', $pageId ?: $this->linkParts['url']['pageuid'] ?? 0); // Draw the record list IF there is a page id to expand: if ($pageId && MathUtility::canBeInterpretedAsInteger($pageId) && $this->getBackendUser()->isInWebMount($pageId)) { $pageId = (int)$pageId; $activePageRecord = BackendUtility::getRecordWSOL('pages', $pageId); $this->view->assign('expandActivePage', true); // Create header for listing, showing the page title/icon $this->view->assign('activePage', $activePageRecord); $this->view->assign('activePageTitle', BackendUtility::getRecordTitle('pages', $activePageRecord, true)); $this->view->assign('activePageIcon', $this->iconFactory->getIconForRecord('pages', $activePageRecord, IconSize::SMALL)->render()); if ($this->isPageLinkable($activePageRecord)) { $this->view->assign('activePageLink', $linkService->asString(['type' => LinkService::TYPE_PAGE, 'pageuid' => $pageId])); } // Look up tt_content elements from the expanded page // @todo: this should be grouped by colPos and use the layout from the page module $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) ->getQueryBuilderForTable('tt_content'); $queryBuilder->getRestrictions() ->removeAll() ->add(GeneralUtility::makeInstance(DeletedRestriction::class)) ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->getBackendUser()->workspace)); $contentElements = $queryBuilder ->select('*') ->from('tt_content') ->where( $queryBuilder->expr()->and( $queryBuilder->expr()->eq( 'pid', $queryBuilder->createNamedParameter($pageId, Connection::PARAM_INT) ), $queryBuilder->expr()->in( 'sys_language_uid', $queryBuilder->createNamedParameter([$activePageRecord['language_tag'], -1], Connection::PARAM_INT_ARRAY) ) ) ) ->orderBy('colPos') ->addOrderBy('sorting') ->executeQuery() ->fetchAllAssociative(); // Enrich list of records $items = []; foreach ($contentElements as $contentElement) { BackendUtility::workspaceOL('tt_content', $contentElement, $this->getBackendUser()->workspace, true); if (is_array($contentElement)) { // Ensure to always link to the live version of the record if ((int)$contentElement['t3ver_oid'] > 0) { $contentElementId = (int)$contentElement['t3ver_oid']; } else { $contentElementId = (int)$contentElement['uid']; } $contentElement['url'] = $linkService->asString(['type' => LinkService::TYPE_PAGE, 'pageuid' => $pageId, 'fragment' => $contentElementId]); $contentElement['isSelected'] = (int)($this->linkParts['url']['fragment'] ?? 0) === $contentElementId; $contentElement['icon'] = $this->iconFactory->getIconForRecord('tt_content', $contentElement, IconSize::SMALL)->render(); $contentElement['title'] = BackendUtility::getRecordTitle('tt_content', $contentElement, true); $items[] = $contentElement; } } $this->view->assign('contentElements', $items); } } /** * @return string[] Array of body-tag attributes */ public function getBodyTagAttributes() { if (count($this->linkParts) === 0 || empty($this->linkParts['url']['pageuid'])) { return []; } return [ 'data-linkbrowser-current-link' => GeneralUtility::makeInstance(LinkService::class)->asString([ 'type' => LinkService::TYPE_PAGE, 'pageuid' => (int)$this->linkParts['url']['pageuid'], 'fragment' => $this->linkParts['url']['fragment'] ?? '', ]), ]; } /** * @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 { $parameters = [ 'expandPage' => isset($values['pid']) ? (int)$values['pid'] : $this->expandPage, ]; return array_merge($this->linkBrowser->getUrlParameters($values), $parameters); } /** * @param string[] $fieldDefinitions Array of link attribute field definitions * @return string[] */ public function modifyLinkAttributes(array $fieldDefinitions) { $configuration = $this->linkBrowser->getConfiguration(); // Depending on where the configuration is set it can be 'pageIdSelector' (CKEditor yaml) or 'pageIdSelector.' (TSconfig) if (!empty($configuration['pageIdSelector']['enabled']) || !empty($configuration['pageIdSelector.']['enabled'])) { $this->linkAttributes[] = 'pageIdSelector'; $fieldDefinitions['pageIdSelector'] = '
'; } return $fieldDefinitions; } protected function isPageLinkable(array $page): bool { return $this->pageDoktypeRegistry->isPageViewable( (int)$page['doktype'], (int)$page['uid'] ); } }