‪TYPO3CMS  ‪main
AbstractTypolinkBuilder.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 
28 
34 {
37 
39  {
40  $this->contentObjectRenderer = ‪$contentObjectRenderer;
41  $this->typoScriptFrontendController = ‪$typoScriptFrontendController ?? ‪$GLOBALS['TSFE'] ?? null;
42  }
43 
57  abstract public function ‪build(array &$linkDetails, string $linkText, string $target, array $conf): ‪LinkResultInterface;
58 
66  protected function ‪forceAbsoluteUrl(string ‪$url, array $configuration): string
67  {
68  $request = $this->contentObjectRenderer->getRequest();
69  $frontendTypoScriptConfigArray = $request->getAttribute('frontend.typoscript')?->getConfigArray();
70  if ($frontendTypoScriptConfigArray['forceAbsoluteUrls'] ?? false) {
71  $forceAbsoluteUrl = true;
72  } else {
73  $forceAbsoluteUrl = !empty($configuration['forceAbsoluteUrl']);
74  }
75  if (!empty(‪$url) && $forceAbsoluteUrl && preg_match('#^(?:([a-z]+)(://)([^/]*)/?)?(.*)$#', ‪$url, $matches)) {
76  $urlParts = [
77  'scheme' => $matches[1],
78  'delimiter' => '://',
79  'host' => $matches[3],
80  'path' => $matches[4],
81  ];
82  $isUrlModified = false;
83  // Set scheme and host if not yet part of the URL
84  if (empty($urlParts['host'])) {
85  $normalizedParams = $request->getAttribute('normalizedParams');
86  // @todo: This fallback should vanish mid-term: typolink has a dependency to ServerRequest
87  // and should expect the normalizedParams argument is properly set as well. When for
88  // instance CLI triggers this code, it should have set up a proper request.
89  $normalizedParams ??= ‪NormalizedParams::createFromRequest($this->contentObjectRenderer->getRequest());
90  $urlParts['scheme'] = $normalizedParams->isHttps() ? 'https' : 'http';
91  $urlParts['host'] = $normalizedParams->getHttpHost();
92  $urlParts['path'] = '/' . ltrim($urlParts['path'], '/');
93  // absRefPrefix has been prepended to $url beforehand
94  // so we only modify the path if no absRefPrefix has been set
95  // otherwise we would destroy the path
96  if ($this->‪getTypoScriptFrontendController()->absRefPrefix === '') {
97  $urlParts['path'] = $normalizedParams->getSitePath() . ltrim($urlParts['path'], '/');
98  }
99  $isUrlModified = true;
100  }
101  // Override scheme:
102  $forcedScheme = $configuration['forceAbsoluteUrl.']['scheme'] ?? null;
103  if (!empty($forcedScheme) && $urlParts['scheme'] !== $forcedScheme) {
104  $urlParts['scheme'] = $forcedScheme;
105  $isUrlModified = true;
106  }
107  // Also ensure the path has a "/" at the beginning when concatenating everything else together
108  if ($urlParts['path'] !== '') {
109  $urlParts['path'] = '/' . ltrim($urlParts['path'], '/');
110  $isUrlModified = true;
111  }
112  // Recreate the absolute URL:
113  if ($isUrlModified) {
114  ‪$url = implode('', $urlParts);
115  }
116  }
117  return ‪$url;
118  }
119 
123  protected function ‪isLibParseFuncDefined(): bool
124  {
125  $configuration = $this->contentObjectRenderer->mergeTSRef(
126  ['parseFunc' => '< lib.parseFunc'],
127  'parseFunc'
128  );
129  return !empty($configuration['parseFunc.']) && is_array($configuration['parseFunc.']);
130  }
131 
139  protected function ‪parseFallbackLinkTextIfLinkTextIsEmpty(string $originalLinkText, string $fallbackLinkText): string
140  {
141  if ($originalLinkText !== '') {
142  return $originalLinkText;
143  }
144  if ($this->‪isLibParseFuncDefined()) {
145  return $this->contentObjectRenderer->parseFunc($fallbackLinkText, ['makelinks' => 0], '< lib.parseFunc');
146  }
147  // encode in case `lib.parseFunc` is not configured
148  return $this->‪encodeFallbackLinkTextIfLinkTextIsEmpty($originalLinkText, $fallbackLinkText);
149  }
150 
158  protected function ‪encodeFallbackLinkTextIfLinkTextIsEmpty(string $originalLinkText, string $fallbackLinkText): string
159  {
160  if ($originalLinkText !== '') {
161  return $originalLinkText;
162  }
163  return htmlspecialchars($fallbackLinkText, ENT_QUOTES);
164  }
165 
173  protected function ‪resolveTargetAttribute(array $conf, string $name): string
174  {
175  $target = '';
176  if (isset($conf[$name]) && $conf[$name] !== '') {
177  $target = $conf[$name];
178  } elseif (!($conf['directImageLink'] ?? false)) {
179  $frontendTypoScriptConfigArray = $this->contentObjectRenderer->getRequest()->getAttribute('frontend.typoscript')?->getConfigArray();
180  switch ($name) {
181  case 'extTarget':
182  case 'fileTarget':
183  $target = (string)($frontendTypoScriptConfigArray[$name] ?? '');
184  break;
185  case 'target':
186  $target = (string)($frontendTypoScriptConfigArray['intTarget'] ?? '');
187  break;
188  }
189  }
190  if (isset($conf[$name . '.']) && $conf[$name . '.']) {
191  $target = (string)$this->contentObjectRenderer->stdWrap($target, $conf[$name . '.'] ?? []);
192  }
193  return $target;
194  }
195 
197  {
198  if ($this->typoScriptFrontendController instanceof ‪TypoScriptFrontendController) {
200  }
201 
202  // This usually happens when typolink is created by the TYPO3 Backend, where no TSFE object
203  // is there. This functionality is currently completely internal, as these links cannot be
204  // created properly from the Backend.
205  // However, this is added to avoid any exceptions when trying to create a link.
206  // Detecting the "first" site usually comes from the fact that TSFE needs to be instantiated
207  // during tests
208  $request = $this->contentObjectRenderer->getRequest();
209  $site = $request->getAttribute('site');
210  if (!$site instanceof ‪Site) {
211  $sites = GeneralUtility::makeInstance(SiteFinder::class)->getAllSites();
212  $site = reset($sites);
213  if (!$site instanceof ‪Site) {
214  $site = new ‪NullSite();
215  }
216  }
217  $language = $request->getAttribute('language');
218  if (!$language instanceof ‪SiteLanguage) {
219  $language = $site->getDefaultLanguage();
220  }
221  $request = $request->withAttribute('language', $language);
222 
223  $this->typoScriptFrontendController = GeneralUtility::makeInstance(TypoScriptFrontendController::class);
224  $this->typoScriptFrontendController->initializePageRenderer($request);
225  $this->typoScriptFrontendController->initializeLanguageService($request);
227  }
228 }
‪TYPO3\CMS\Core\Site\Entity\NullSite
Definition: NullSite.php:32
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:42
‪TYPO3\CMS\Core\Site\Entity\SiteLanguage
Definition: SiteLanguage.php:27
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:82
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Http\NormalizedParams\createFromRequest
‪static static createFromRequest(ServerRequestInterface $request, array $systemConfiguration=null)
Definition: NormalizedParams.php:840
‪TYPO3\CMS\Core\Http\NormalizedParams
Definition: NormalizedParams.php:38