‪TYPO3CMS  ‪main
PreviewUriBuilder.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\UriInterface;
23 use ‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder as BackendPreviewUriBuilder;
25 use TYPO3\CMS\Backend\Utility\BackendUtility;
40 
47 {
51  protected ‪$pageCache = [];
52 
56  protected ‪$workspaceService;
57 
61  protected ‪$eventDispatcher;
62 
66  protected ‪$previewLinkLifetime;
67 
68  public function ‪__construct(EventDispatcherInterface ‪$eventDispatcher, ‪WorkspaceService ‪$workspaceService)
69  {
70  $this->eventDispatcher = ‪$eventDispatcher;
71  $this->workspaceService = ‪$workspaceService;
72  $this->previewLinkLifetime = $this->workspaceService->‪getPreviewLinkLifetime();
73  }
74 
82  public function ‪buildUriForPage(int ‪$uid, int $languageId = 0): string
83  {
84  $previewKeyword = $this->‪compilePreviewKeyword(
85  $this->previewLinkLifetime * 3600,
86  $this->workspaceService->getCurrentWorkspace()
87  );
88 
89  $siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
90  try {
91  $site = $siteFinder->getSiteByPageId(‪$uid);
92  try {
93  $language = $site->getLanguageById($languageId);
94  } catch (\InvalidArgumentException $e) {
95  $language = $site->getDefaultLanguage();
96  }
97  $uri = $site->getRouter()->generateUri(‪$uid, ['ADMCMD_prev' => $previewKeyword, '_language' => $language], '');
98  return (string)$uri;
99  } catch (SiteNotFoundException | InvalidRouteArgumentsException $e) {
100  throw new UnableToLinkToPageException(sprintf('The link to the page with ID "%d" could not be generated: %s', ‪$uid, $e->getMessage()), 1559794916, $e);
101  }
102  }
103 
107  public function ‪buildUrisForAllLanguagesOfPage(int $pageId): array
108  {
109  $previewLanguages = $this->‪getAvailableLanguages($pageId);
110  $previewLinks = [];
111 
112  foreach ($previewLanguages as $languageUid => $language) {
113  $previewLinks[$language] = $this->‪buildUriForPage($pageId, $languageUid);
114  }
115 
116  return $previewLinks;
117  }
118 
125  public function ‪buildUriForWorkspaceSplitPreview(int ‪$uid): UriInterface
126  {
127  // In case a $pageUid is submitted we need to make sure it points to a live-page
128  if (‪$uid > 0) {
129  ‪$uid = $this->‪getLivePageUid($uid);
130  }
131  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
132  return $uriBuilder->buildUriFromRoute(
133  'workspace_previewcontrols',
134  ['id' => ‪$uid],
136  );
137  }
138 
147  public function ‪buildUriForElement(string $table, int ‪$uid, array $liveRecord = null, array $versionRecord = null): string
148  {
149  $previewUri = $this->‪createPreviewUriForElement($table, ‪$uid, $liveRecord, $versionRecord);
150  $event = new RetrievedPreviewUrlEvent($table, ‪$uid, $previewUri, [
151  'liveRecord' => $liveRecord,
152  'versionRecord' => $versionRecord,
153  ]);
154  $event = $this->eventDispatcher->dispatch($event);
155  $previewUri = (string)($event->getPreviewUri() ?? '');
156  return $previewUri;
157  }
158 
159  protected function ‪createPreviewUriForElement(string $table, int ‪$uid, array $liveRecord = null, array $versionRecord = null): ?UriInterface
160  {
161  if ($table === 'pages') {
162  return BackendPreviewUriBuilder::create((int)BackendUtility::getLiveVersionIdOfRecord('pages', ‪$uid))
163  ->buildUri();
164  }
165 
166  if ($liveRecord === null) {
167  $liveRecord = BackendUtility::getLiveVersionOfRecord($table, ‪$uid);
168  }
169  if ($versionRecord === null) {
170  $versionRecord = BackendUtility::getRecord($table, ‪$uid);
171  }
172 
173  // Directly use pid value and consider move placeholders
174  $previewPageId = (int)(empty($versionRecord['pid']) ? $liveRecord['pid'] : $versionRecord['pid']);
175  $linkParameters = [
176  'previewWS' => $versionRecord['t3ver_wsid'],
177  ];
178  // Add language parameter if record is a localization
179  if (BackendUtility::isTableLocalizable($table)) {
180  $languageField = ‪$GLOBALS['TCA'][$table]['ctrl']['languageField'];
181  if ($versionRecord[$languageField] > 0) {
182  $linkParameters['_language'] = $versionRecord[$languageField];
183  }
184  }
185 
186  $pageTsConfig = BackendUtility::getPagesTSconfig($previewPageId);
187  // Directly use determined direct page id
188  if ($table === 'tt_content') {
189  return BackendPreviewUriBuilder::create($previewPageId)
190  ->withAdditionalQueryParameters(
191  ‪HttpUtility::buildQueryString($linkParameters, '&')
192  )
193  ->buildUri();
194  }
195  if (!empty($pageTsConfig['options.']['workspaces.']['previewPageId.'][$table]) || !empty($pageTsConfig['options.']['workspaces.']['previewPageId'])) {
196  // Analyze Page TSconfig options.workspaces.previewPageId
197  if (!empty($pageTsConfig['options.']['workspaces.']['previewPageId.'][$table])) {
198  $previewConfiguration = $pageTsConfig['options.']['workspaces.']['previewPageId.'][$table];
199  } else {
200  $previewConfiguration = $pageTsConfig['options.']['workspaces.']['previewPageId'];
201  }
202  // Extract possible settings (e.g. "field:pid")
203  $previewValueParts = explode(':', $previewConfiguration, 2);
204  if (count($previewValueParts) === 2 && $previewValueParts[0] === 'field') {
205  $previewPageId = (int)($liveRecord[$previewValueParts[1]] ?? 0);
206  } else {
207  $previewPageId = (int)$previewConfiguration;
208  }
209 
210  // Add preview parameters from standard backend preview mechanism
211  // map record data to GET parameters
212  $backendPreviewConfiguration = $pageTsConfig['TCEMAIN.']['preview.'][$table . '.'] ?? [];
213  if (isset($backendPreviewConfiguration['fieldToParameterMap.'])) {
214  foreach ($backendPreviewConfiguration['fieldToParameterMap.'] as $field => $parameterName) {
215  $value = $versionRecord[$field] ?? '';
216  if ($field === 'uid') {
217  $value = $versionRecord['t3ver_oid'] === 0 ? $versionRecord['uid'] : $versionRecord['t3ver_oid'];
218  }
219  $linkParameters[$parameterName] = $value;
220  }
221  }
222  // add/override parameters by configuration
223  if (isset($backendPreviewConfiguration['additionalGetParameters.'])) {
224  $additionalGetParameters = GeneralUtility::removeDotsFromTS($backendPreviewConfiguration['additionalGetParameters.']);
225  $linkParameters = array_replace($linkParameters, $additionalGetParameters);
226  }
227 
228  return BackendPreviewUriBuilder::create($previewPageId)
229  ->withAdditionalQueryParameters(
230  ‪HttpUtility::buildQueryString($linkParameters, '&')
231  )
232  ->buildUri();
233  }
234  return null;
235  }
236 
244  protected function ‪compilePreviewKeyword(int $ttl = 172800, int $workspaceId = null): string
245  {
246  $keyword = md5(‪StringUtility::getUniqueId());
247  GeneralUtility::makeInstance(ConnectionPool::class)
248  ->getConnectionForTable('sys_preview')
249  ->insert(
250  'sys_preview',
251  [
252  'keyword' => $keyword,
253  'tstamp' => ‪$GLOBALS['EXEC_TIME'],
254  'endtime' => ‪$GLOBALS['EXEC_TIME'] + $ttl,
255  'config' => json_encode([
256  'fullWorkspace' => $workspaceId,
257  ]),
258  ]
259  );
260 
261  return $keyword;
262  }
263 
270  protected function ‪getLivePageUid(int ‪$uid): int
271  {
272  if (!isset($this->pageCache[‪$uid])) {
273  $pageRecord = BackendUtility::getRecord('pages', ‪$uid);
274  if (is_array($pageRecord)) {
275  $this->pageCache[‪$uid] = $pageRecord['t3ver_oid'] ? (int)$pageRecord['t3ver_oid'] : ‪$uid;
276  } else {
277  throw new \InvalidArgumentException('uid is supposed to point to an existing page - given value was: ' . ‪$uid, 1290628113);
278  }
279  }
280  return $this->pageCache[‪$uid];
281  }
282 
288  protected function ‪getAvailableLanguages(int $pageId): array
289  {
290  $languageOptions = [];
291  $translationConfigurationProvider = GeneralUtility::makeInstance(TranslationConfigurationProvider::class);
292  $systemLanguages = $translationConfigurationProvider->getSystemLanguages($pageId);
293 
294  if ($this->‪getBackendUser()->checkLanguageAccess(0)) {
295  // Use configured label for default language
296  $languageOptions[0] = $systemLanguages[0]['title'];
297  }
298 
299  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
300  ->getQueryBuilderForTable('pages');
301  $queryBuilder->getRestrictions()
302  ->removeAll()
303  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
304  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->‪getBackendUser()->workspace));
305 
306  $result = $queryBuilder->select('sys_language_uid')
307  ->from('pages')
308  ->where(
309  $queryBuilder->expr()->eq(
310  ‪$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
311  $queryBuilder->createNamedParameter($pageId, ‪Connection::PARAM_INT)
312  )
313  )
314  ->executeQuery();
315 
316  while ($row = $result->fetchAssociative()) {
317  $languageId = (int)$row['sys_language_uid'];
318  // Only add links to active languages the user has access to
319  if (isset($systemLanguages[$languageId]) && $this->‪getBackendUser()->checkLanguageAccess($languageId)) {
320  $languageOptions[$languageId] = $systemLanguages[$languageId]['title'];
321  }
322  }
323 
324  return $languageOptions;
325  }
326 
327  protected function ‪getBackendUser(): ‪BackendUserAuthentication
328  {
329  return ‪$GLOBALS['BE_USER'];
330  }
331 }
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\$pageCache
‪array $pageCache
Definition: PreviewUriBuilder.php:50
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\__construct
‪__construct(EventDispatcherInterface $eventDispatcher, WorkspaceService $workspaceService)
Definition: PreviewUriBuilder.php:64
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:50
‪TYPO3\CMS\Workspaces\Event\RetrievedPreviewUrlEvent
Definition: RetrievedPreviewUrlEvent.php:27
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\getBackendUser
‪getBackendUser()
Definition: PreviewUriBuilder.php:323
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\$workspaceService
‪WorkspaceService $workspaceService
Definition: PreviewUriBuilder.php:54
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\createPreviewUriForElement
‪createPreviewUriForElement(string $table, int $uid, array $liveRecord=null, array $versionRecord=null)
Definition: PreviewUriBuilder.php:155
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\compilePreviewKeyword
‪string compilePreviewKeyword(int $ttl=172800, int $workspaceId=null)
Definition: PreviewUriBuilder.php:240
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:25
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\$previewLinkLifetime
‪int $previewLinkLifetime
Definition: PreviewUriBuilder.php:62
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\buildUriForPage
‪string buildUriForPage(int $uid, int $languageId=0)
Definition: PreviewUriBuilder.php:78
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\getAvailableLanguages
‪array getAvailableLanguages(int $pageId)
Definition: PreviewUriBuilder.php:284
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Core\Utility\HttpUtility\buildQueryString
‪static string buildQueryString(array $parameters, string $prependCharacter='', bool $skipEmptyParameters=false)
Definition: HttpUtility.php:124
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder
Definition: PreviewUriBuilder.php:47
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\getLivePageUid
‪getLivePageUid(int $uid)
Definition: PreviewUriBuilder.php:266
‪TYPO3\CMS\Core\Routing\InvalidRouteArgumentsException
Definition: InvalidRouteArgumentsException.php:25
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:60
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\buildUrisForAllLanguagesOfPage
‪buildUrisForAllLanguagesOfPage(int $pageId)
Definition: PreviewUriBuilder.php:103
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\buildUriForElement
‪buildUriForElement(string $table, int $uid, array $liveRecord=null, array $versionRecord=null)
Definition: PreviewUriBuilder.php:143
‪TYPO3\CMS\Workspaces\Service\WorkspaceService
Definition: WorkspaceService.php:35
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:39
‪TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider
Definition: TranslationConfigurationProvider.php:39
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\getPreviewLinkLifetime
‪int getPreviewLinkLifetime()
Definition: WorkspaceService.php:97
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\$eventDispatcher
‪EventDispatcherInterface $eventDispatcher
Definition: PreviewUriBuilder.php:58
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder
Definition: PreviewUriBuilder.php:44
‪TYPO3\CMS\Workspaces\Preview
Definition: PreviewUriBuilder.php:18
‪TYPO3\CMS\Core\Utility\HttpUtility
Definition: HttpUtility.php:24
‪TYPO3\CMS\Workspaces\Preview\PreviewUriBuilder\buildUriForWorkspaceSplitPreview
‪buildUriForWorkspaceSplitPreview(int $uid)
Definition: PreviewUriBuilder.php:121
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:48
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Backend\Routing\UriBuilder\ABSOLUTE_URL
‪const ABSOLUTE_URL
Definition: UriBuilder.php:48
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static getUniqueId(string $prefix='')
Definition: StringUtility.php:57
‪TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction
Definition: WorkspaceRestriction.php:39