1<?php 2declare(strict_types = 1); 3namespace TYPO3\CMS\Recordlist\LinkHandler; 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 18use Psr\Http\Message\ServerRequestInterface; 19use TYPO3\CMS\Backend\Utility\BackendUtility; 20use TYPO3\CMS\Core\LinkHandling\LinkService; 21use TYPO3\CMS\Core\Page\PageRenderer; 22use TYPO3\CMS\Core\Utility\GeneralUtility; 23use TYPO3\CMS\Fluid\View\StandaloneView; 24use TYPO3\CMS\Recordlist\Browser\RecordBrowser; 25use TYPO3\CMS\Recordlist\Controller\AbstractLinkBrowserController; 26use TYPO3\CMS\Recordlist\Tree\View\LinkParameterProviderInterface; 27use TYPO3\CMS\Recordlist\Tree\View\RecordBrowserPageTreeView; 28 29/** 30 * Link handler for arbitrary database records 31 * @internal This class is a specific LinkHandler implementation and is not part of the TYPO3's Core API. 32 */ 33class RecordLinkHandler extends AbstractLinkHandler implements LinkHandlerInterface, LinkParameterProviderInterface 34{ 35 /** 36 * Configuration key in TSconfig TCEMAIN.linkHandler.record 37 * 38 * @var string 39 */ 40 protected $identifier; 41 42 /** 43 * Specific TSconfig for the current instance (corresponds to TCEMAIN.linkHandler.record.identifier.configuration) 44 * 45 * @var array 46 */ 47 protected $configuration = []; 48 49 /** 50 * Parts of the current link 51 * 52 * @var array 53 */ 54 protected $linkParts = []; 55 56 /** 57 * @var int 58 */ 59 protected $expandPage = 0; 60 61 /** 62 * Initializes the handler. 63 * 64 * @param AbstractLinkBrowserController $linkBrowser 65 * @param string $identifier 66 * @param array $configuration Page TSconfig 67 */ 68 public function initialize(AbstractLinkBrowserController $linkBrowser, $identifier, array $configuration) 69 { 70 parent::initialize($linkBrowser, $identifier, $configuration); 71 $this->identifier = $identifier; 72 $this->configuration = $configuration; 73 } 74 75 /** 76 * Checks if this is the right handler for the given link. 77 * 78 * Also stores information locally about currently linked record. 79 * 80 * @param array $linkParts Link parts as returned from TypoLinkCodecService 81 * @return bool 82 */ 83 public function canHandleLink(array $linkParts): bool 84 { 85 if (!$linkParts['url'] || !isset($linkParts['url']['identifier']) || $linkParts['url']['identifier'] !== $this->identifier) { 86 return false; 87 } 88 89 $data = $linkParts['url']; 90 91 // Get the related record 92 $table = $this->configuration['table']; 93 $record = BackendUtility::getRecord($table, $data['uid']); 94 if ($record === null) { 95 $linkParts['title'] = $this->getLanguageService()->getLL('recordNotFound'); 96 } else { 97 $linkParts['tableName'] = $this->getLanguageService()->sL($GLOBALS['TCA'][$table]['ctrl']['title']); 98 $linkParts['pid'] = (int)$record['pid']; 99 $linkParts['title'] = $linkParts['title'] ?: BackendUtility::getRecordTitle($table, $record); 100 } 101 $linkParts['url']['type'] = $linkParts['type']; 102 $this->linkParts = $linkParts; 103 104 return true; 105 } 106 107 /** 108 * Formats information for the current record for HTML output. 109 * 110 * @return string 111 */ 112 public function formatCurrentUrl(): string 113 { 114 return sprintf( 115 '%s: %s [uid: %d]', 116 $this->linkParts['tableName'], 117 $this->linkParts['title'], 118 $this->linkParts['url']['uid'] 119 ); 120 } 121 122 /** 123 * Renders the link handler. 124 * 125 * @param ServerRequestInterface $request 126 * @return string 127 */ 128 public function render(ServerRequestInterface $request): string 129 { 130 // Declare JS module 131 GeneralUtility::makeInstance(PageRenderer::class)->loadRequireJsModule('TYPO3/CMS/Recordlist/RecordLinkHandler'); 132 133 // Define the current page 134 if (isset($request->getQueryParams()['expandPage'])) { 135 $this->expandPage = (int)$request->getQueryParams()['expandPage']; 136 } elseif (isset($this->configuration['storagePid'])) { 137 $this->expandPage = (int)$this->configuration['storagePid']; 138 } elseif (isset($this->linkParts['pid'])) { 139 $this->expandPage = (int)$this->linkParts['pid']; 140 } 141 $this->setTemporaryDbMounts(); 142 143 $databaseBrowser = GeneralUtility::makeInstance(RecordBrowser::class); 144 145 $recordList = $databaseBrowser->displayRecordsForPage( 146 $this->expandPage, 147 $this->configuration['table'], 148 $this->getUrlParameters([]) 149 ); 150 151 $path = GeneralUtility::getFileAbsFileName('EXT:recordlist/Resources/Private/Templates/LinkBrowser/Record.html'); 152 $view = GeneralUtility::makeInstance(StandaloneView::class); 153 $view->setTemplatePathAndFilename($path); 154 $view->assignMultiple([ 155 'tree' => $this->configuration['hidePageTree'] ? '' : $this->renderPageTree(), 156 'recordList' => $recordList, 157 ]); 158 159 return $view->render(); 160 } 161 162 /** 163 * Renders the page tree. 164 * 165 * @return string 166 */ 167 protected function renderPageTree(): string 168 { 169 $userTsConfig = $this->getBackendUser()->getTSConfig(); 170 171 /** @var RecordBrowserPageTreeView $pageTree */ 172 $pageTree = GeneralUtility::makeInstance(RecordBrowserPageTreeView::class); 173 $pageTree->setLinkParameterProvider($this); 174 $pageTree->ext_showPageId = (bool)($userTsConfig['options.']['pageTree.']['showPageIdWithTitle'] ?? false); 175 $pageTree->ext_showNavTitle = (bool)($userTsConfig['options.']['pageTree.']['showNavTitle'] ?? false); 176 $pageTree->ext_showPathAboveMounts = (bool)($userTsConfig['options.']['pageTree.']['showPathAboveMounts'] ?? false); 177 $pageTree->addField('nav_title'); 178 179 // Load the mount points, if any 180 // NOTE: mount points actually override the page tree 181 if (!empty($this->configuration['pageTreeMountPoints'])) { 182 $pageTree->MOUNTS = GeneralUtility::intExplode(',', $this->configuration['pageTreeMountPoints'], true); 183 } 184 185 return $pageTree->getBrowsableTree(); 186 } 187 188 /** 189 * Returns attributes for the body tag. 190 * 191 * @return string[] Array of body-tag attributes 192 */ 193 public function getBodyTagAttributes(): array 194 { 195 $attributes = [ 196 'data-identifier' => 't3://record?identifier=' . $this->identifier . '&uid=', 197 ]; 198 if (!empty($this->linkParts)) { 199 $attributes['data-current-link'] = GeneralUtility::makeInstance(LinkService::class)->asString($this->linkParts['url']); 200 } 201 202 return $attributes; 203 } 204 205 /** 206 * Returns all parameters needed to build a URL with all the necessary information. 207 * 208 * @param array $values Array of values to include into the parameters or which might influence the parameters 209 * @return string[] Array of parameters which have to be added to URLs 210 */ 211 public function getUrlParameters(array $values): array 212 { 213 $pid = isset($values['pid']) ? (int)$values['pid'] : $this->expandPage; 214 $parameters = [ 215 'expandPage' => $pid, 216 ]; 217 218 return array_merge( 219 $this->linkBrowser->getUrlParameters($values), 220 ['P' => $this->linkBrowser->getParameters()], 221 $parameters 222 ); 223 } 224 225 /** 226 * Checks if the submitted page matches the current page. 227 * 228 * @param array $values Values to be checked 229 * @return bool Returns TRUE if the given values match the currently selected item 230 */ 231 public function isCurrentlySelectedItem(array $values): bool 232 { 233 return !empty($this->linkParts) && (int)$this->linkParts['pid'] === (int)$values['pid']; 234 } 235 236 /** 237 * Returns the URL of the current script 238 * 239 * @return string 240 */ 241 public function getScriptUrl(): string 242 { 243 return $this->linkBrowser->getScriptUrl(); 244 } 245} 246