‪TYPO3CMS  ‪main
PageLinkHandler.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\Http\Message\ServerRequestInterface;
22 use TYPO3\CMS\Backend\Utility\BackendUtility;
28 use TYPO3\CMS\Core\Imaging\IconSize;
33 
40 {
44  protected ‪$expandPage = 0;
45 
51  protected ‪$linkParts = [];
52 
62  public function ‪canHandleLink(array ‪$linkParts)
63  {
64  if (empty(‪$linkParts['url'] ?? '')) {
65  return false;
66  }
67  $data = ‪$linkParts['url'];
68  // Check if the page still exists
69  if ((int)($data['pageuid'] ?? 0) > 0) {
70  $pageRow = BackendUtility::getRecordWSOL('pages', $data['pageuid']);
71  if (!$pageRow) {
72  return false;
73  }
74  } elseif ($data['pageuid'] ?? '' !== 'current') {
75  return false;
76  }
77 
78  $this->linkParts = ‪$linkParts;
79  return true;
80  }
81 
87  public function ‪formatCurrentUrl()
88  {
89  $lang = $this->‪getLanguageService();
90  $titleLen = (int)$this->‪getBackendUser()->uc['titleLen'];
91 
92  $id = (int)$this->linkParts['url']['pageuid'];
93 
94  $idInfo = 'ID: ' . $id . (!empty($this->linkParts['url']['fragment']) ? ', #' . $this->linkParts['url']['fragment'] : '');
95 
96  $permsClause = $this->‪getBackendUser()->getPagePermsClause(‪Permission::PAGE_SHOW);
97  $pageRecord = BackendUtility::readPageAccess($id, $permsClause);
98  if ($pageRecord === false) {
99  return $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:page') . ' ' . $idInfo;
100  }
101 
102  $pageTitle = $pageRecord['title'] ?? '';
103  return $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:page')
104  . ($pageTitle ? ' \'' . ‪GeneralUtility::fixed_lgd_cs($pageTitle, $titleLen) . '\'' : '')
105  . ' (' . $idInfo . ')';
106  }
107 
111  public function ‪render(ServerRequestInterface $request): string
112  {
113  $this->pageRenderer->loadJavaScriptModule('@typo3/backend/page-link-handler.js');
114  $this->pageRenderer->loadJavaScriptModule('@typo3/backend/viewport/resizable-navigation.js');
115  $this->pageRenderer->loadJavaScriptModule('@typo3/backend/tree/page-browser.js');
116  $this->‪getBackendUser()->initializeWebmountsForElementBrowser();
117 
118  $this->expandPage = isset($request->getQueryParams()['expandPage']) ? (int)$request->getQueryParams()['expandPage'] : 0;
119 
120  $this->view->assign('initialNavigationWidth', $this->‪getBackendUser()->uc['selector']['navigation']['width'] ?? 250);
121  $this->view->assign('treeActions', ['link']);
122  $this->‪getRecordsOnExpandedPage($this->expandPage);
123  return $this->view->render('LinkBrowser/Page');
124  }
125 
131  protected function ‪getRecordsOnExpandedPage($pageId)
132  {
133  // If there is an anchor value (content element reference) in the element reference, then force an ID to expand:
134  if (!$pageId && isset($this->linkParts['url']['fragment'])) {
135  // Set to the current link page id.
136  $pageId = $this->linkParts['url']['pageuid'];
137  }
138  $linkService = GeneralUtility::makeInstance(LinkService::class);
139  $this->view->assign('expandedPage', $pageId ?: $this->linkParts['url']['pageuid'] ?? 0);
140  // Draw the record list IF there is a page id to expand:
141  if ($pageId && ‪MathUtility::canBeInterpretedAsInteger($pageId) && $this->‪getBackendUser()->isInWebMount($pageId)) {
142  $pageId = (int)$pageId;
143 
144  $activePageRecord = BackendUtility::getRecordWSOL('pages', $pageId);
145  $this->view->assign('expandActivePage', true);
146 
147  // Create header for listing, showing the page title/icon
148  $this->view->assign('activePage', $activePageRecord);
149  $this->view->assign('activePageTitle', BackendUtility::getRecordTitle('pages', $activePageRecord, true));
150  $this->view->assign('activePageIcon', $this->iconFactory->getIconForRecord('pages', $activePageRecord, IconSize::SMALL)->render());
151  if ($this->‪isPageLinkable($activePageRecord)) {
152  $this->view->assign('activePageLink', $linkService->asString(['type' => ‪LinkService::TYPE_PAGE, 'pageuid' => $pageId]));
153  }
154 
155  // Look up tt_content elements from the expanded page
156  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
157  ->getQueryBuilderForTable('tt_content');
158 
159  $queryBuilder->getRestrictions()
160  ->removeAll()
161  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
162  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->‪getBackendUser()->workspace));
163 
164  $contentElements = $queryBuilder
165  ->select('*')
166  ->from('tt_content')
167  ->where(
168  $queryBuilder->expr()->and(
169  $queryBuilder->expr()->eq(
170  'pid',
171  $queryBuilder->createNamedParameter($pageId, ‪Connection::PARAM_INT)
172  ),
173  $queryBuilder->expr()->in(
174  'sys_language_uid',
175  $queryBuilder->createNamedParameter([$activePageRecord['sys_language_uid'], -1], ‪Connection::PARAM_INT_ARRAY)
176  )
177  )
178  )
179  ->orderBy('colPos')
180  ->addOrderBy('sorting')
181  ->executeQuery()
182  ->fetchAllAssociative();
183 
184  // Enrich list of records
185  foreach ($contentElements as &$contentElement) {
186  BackendUtility::workspaceOL('tt_content', $contentElement);
187  $contentElement['url'] = $linkService->asString(['type' => ‪LinkService::TYPE_PAGE, 'pageuid' => $pageId, 'fragment' => $contentElement['uid']]);
188  $contentElement['isSelected'] = (int)($this->linkParts['url']['fragment'] ?? 0) === (int)$contentElement['uid'];
189  $contentElement['icon'] = $this->iconFactory->getIconForRecord('tt_content', $contentElement, IconSize::SMALL)->render();
190  $contentElement['title'] = BackendUtility::getRecordTitle('tt_content', $contentElement, true);
191  }
192  $this->view->assign('contentElements', $contentElements);
193  }
194  }
195 
199  public function ‪getBodyTagAttributes()
200  {
201  if (count($this->linkParts) === 0 || empty($this->linkParts['url']['pageuid'])) {
202  return [];
203  }
204  return [
205  'data-linkbrowser-current-link' => GeneralUtility::makeInstance(LinkService::class)->asString([
206  'type' => ‪LinkService::TYPE_PAGE,
207  'pageuid' => (int)$this->linkParts['url']['pageuid'],
208  'fragment' => $this->linkParts['url']['fragment'] ?? '',
209  ]),
210  ];
211  }
212 
217  public function ‪getUrlParameters(array $values): array
218  {
219  $parameters = [
220  'expandPage' => isset($values['pid']) ? (int)$values['pid'] : $this->expandPage,
221  ];
222  return array_merge($this->linkBrowser->getUrlParameters($values), $parameters);
223  }
224 
229  public function ‪modifyLinkAttributes(array $fieldDefinitions)
230  {
231  $configuration = $this->linkBrowser->getConfiguration();
232  // Depending on where the configuration is set it can be 'pageIdSelector' (CKEditor yaml) or 'pageIdSelector.' (TSconfig)
233  if (!empty($configuration['pageIdSelector']['enabled']) || !empty($configuration['pageIdSelector.']['enabled'])) {
234  $this->linkAttributes[] = 'pageIdSelector';
235  $fieldDefinitions['pageIdSelector'] = '
236  <form><div class="row mt-3">
237  <label class="col-3 col-form-label">
238  ' . htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:page_id')) . '
239  </label>
240  <div class="col-2">
241  <input type="number" size="6" name="luid" id="luid" class="form-control" />
242  </div>
243  <div class="col-7">
244  <input class="btn btn-default t3js-pageLink" type="submit" value="' . htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:setLink')) . '" />
245  </div>
246  </div></form>';
247  }
248  return $fieldDefinitions;
249  }
250 
251  protected function ‪isPageLinkable(array $page): bool
252  {
253  return !in_array((int)$page['doktype'], [‪PageRepository::DOKTYPE_SYSFOLDER, ‪PageRepository::DOKTYPE_SPACER]);
254  }
255 }
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Core\Utility\GeneralUtility\fixed_lgd_cs
‪static string fixed_lgd_cs(string $string, int $chars, string $appendString='...')
Definition: GeneralUtility.php:92
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SPACER
‪const DOKTYPE_SPACER
Definition: PageRepository.php:103
‪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\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:69
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT_ARRAY
‪const PARAM_INT_ARRAY
Definition: Connection.php:72
‪TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction
Definition: WorkspaceRestriction.php:39