‪TYPO3CMS  11.5
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 
32 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
35 
41 {
45  protected ‪$contentObjectRenderer;
46 
51 
59  {
60  $this->contentObjectRenderer = ‪$contentObjectRenderer;
61  $this->typoScriptFrontendController = ‪$typoScriptFrontendController ?? ‪$GLOBALS['TSFE'] ?? null;
62  }
63 
77  abstract public function ‪build(array &$linkDetails, string $linkText, string $target, array $conf);
78 
86  protected function ‪forceAbsoluteUrl(string $url, array $configuration): string
87  {
88  if (!empty($url) && !empty($configuration['forceAbsoluteUrl']) && preg_match('#^(?:([a-z]+)(://)([^/]*)/?)?(.*)$#', $url, $matches)) {
89  $urlParts = [
90  'scheme' => $matches[1],
91  'delimiter' => '://',
92  'host' => $matches[3],
93  'path' => $matches[4],
94  ];
95  $isUrlModified = false;
96  // Set scheme and host if not yet part of the URL:
97  if (empty($urlParts['host'])) {
98  $urlParts['scheme'] = GeneralUtility::getIndpEnv('TYPO3_SSL') ? 'https' : 'http';
99  $urlParts['host'] = GeneralUtility::getIndpEnv('HTTP_HOST');
100  $urlParts['path'] = '/' . ltrim($urlParts['path'], '/');
101  // absRefPrefix has been prepended to $url beforehand
102  // so we only modify the path if no absRefPrefix has been set
103  // otherwise we would destroy the path
104  if ($this->‪getTypoScriptFrontendController()->absRefPrefix === '') {
105  $urlParts['path'] = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH') . ltrim($urlParts['path'], '/');
106  }
107  $isUrlModified = true;
108  }
109  // Override scheme:
110  $forcedScheme = $configuration['forceAbsoluteUrl.']['scheme'] ?? null;
111  if (!empty($forcedScheme) && $urlParts['scheme'] !== $forcedScheme) {
112  $urlParts['scheme'] = $forcedScheme;
113  $isUrlModified = true;
114  }
115  // Also ensure the path has a "/" at the beginning when concatenating everything else together
116  if ($urlParts['path'] !== '') {
117  $urlParts['path'] = '/' . ltrim($urlParts['path'], '/');
118  $isUrlModified = true;
119  }
120  // Recreate the absolute URL:
121  if ($isUrlModified) {
122  $url = implode('', $urlParts);
123  }
124  }
125  return $url;
126  }
127 
133  protected function ‪isLibParseFuncDefined(): bool
134  {
135  $configuration = $this->contentObjectRenderer->mergeTSRef(
136  ['parseFunc' => '< lib.parseFunc'],
137  'parseFunc'
138  );
139  return !empty($configuration['parseFunc.']) && is_array($configuration['parseFunc.']);
140  }
141 
149  protected function ‪parseFallbackLinkTextIfLinkTextIsEmpty(string $originalLinkText, string $fallbackLinkText): string
150  {
151  if ($originalLinkText !== '') {
152  return $originalLinkText;
153  }
154  if ($this->‪isLibParseFuncDefined()) {
155  return $this->contentObjectRenderer->parseFunc($fallbackLinkText, ['makelinks' => 0], '< lib.parseFunc');
156  }
157  // encode in case `lib.parseFunc` is not configured
158  return $this->‪encodeFallbackLinkTextIfLinkTextIsEmpty($originalLinkText, $fallbackLinkText);
159  }
160 
168  protected function ‪encodeFallbackLinkTextIfLinkTextIsEmpty(string $originalLinkText, string $fallbackLinkText): string
169  {
170  if ($originalLinkText !== '') {
171  return $originalLinkText;
172  }
173  return htmlspecialchars($fallbackLinkText, ENT_QUOTES);
174  }
175 
185  protected function ‪resolveTargetAttribute(array $conf, string $name, bool $respectFrameSetOption = false, string $fallbackTarget = ''): string
186  {
187  $tsfe = $this->‪getTypoScriptFrontendController();
188  $targetAttributeAllowed = !$respectFrameSetOption
189  || (!isset($tsfe->config['config']['doctype']) || !$tsfe->config['config']['doctype'])
190  || in_array((string)$tsfe->config['config']['doctype'], ['xhtml_trans', 'xhtml_basic', 'html5'], true);
191 
192  $target = '';
193  if (isset($conf[$name]) && $conf[$name] !== '') {
194  $target = $conf[$name];
195  } elseif ($targetAttributeAllowed && !($conf['directImageLink'] ?? false)) {
196  $target = $fallbackTarget;
197  }
198  if (isset($conf[$name . '.']) && $conf[$name . '.']) {
199  $target = (string)$this->contentObjectRenderer->stdWrap($target, $conf[$name . '.'] ?? []);
200  }
201  return $target;
202  }
203 
213  protected function ‪processUrl(string $context, string $url, array $typolinkConfiguration = [])
214  {
215  $urlProcessors = ‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'] ?? false;
216  if (!$urlProcessors) {
217  return $url;
218  }
219 
220  foreach ($urlProcessors as $identifier => $configuration) {
221  if (empty($configuration) || !is_array($configuration)) {
222  throw new \RuntimeException('Missing configuration for URI processor "' . $identifier . '".', 1491130459);
223  }
224  if (!is_string($configuration['processor']) || empty($configuration['processor']) || !class_exists($configuration['processor']) || !is_subclass_of($configuration['processor'], UrlProcessorInterface::class)) {
225  throw new \RuntimeException('The URI processor "' . $identifier . '" defines an invalid provider. Ensure the class exists and implements the "' . UrlProcessorInterface::class . '".', 1491130460);
226  }
227  }
228 
229  $orderedProcessors = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($urlProcessors);
230  $keepProcessing = true;
231 
232  foreach ($orderedProcessors as $configuration) {
234  $urlProcessor = GeneralUtility::makeInstance($configuration['processor']);
235  $url = $urlProcessor->process($context, $url, $typolinkConfiguration, $this->contentObjectRenderer, $keepProcessing);
236  if (!$keepProcessing) {
237  break;
238  }
239  }
240 
241  return $url;
242  }
243 
248  {
249  if ($this->typoScriptFrontendController instanceof ‪TypoScriptFrontendController) {
251  }
252 
253  // This usually happens when typolink is created by the TYPO3 Backend, where no TSFE object
254  // is there. This functionality is currently completely internal, as these links cannot be
255  // created properly from the Backend.
256  // However, this is added to avoid any exceptions when trying to create a link.
257  // Detecting the "first" site usually comes from the fact that TSFE needs to be instantiated
258  // during tests
259  $request = ‪$GLOBALS['TYPO3_REQUEST'] ?? ‪ServerRequestFactory::fromGlobals();
260  $site = $request->getAttribute('site');
261  if (!$site instanceof ‪Site) {
262  $sites = GeneralUtility::makeInstance(SiteFinder::class)->getAllSites();
263  $site = reset($sites);
264  if (!$site instanceof ‪Site) {
265  $site = new ‪NullSite();
266  }
267  }
268  $language = $request->getAttribute('language');
269  if (!$language instanceof ‪SiteLanguage) {
270  $language = $site->getDefaultLanguage();
271  }
272 
273  $id = $request->getQueryParams()['id'] ?? $request->getParsedBody()['id'] ?? $site->getRootPageId();
274  $type = $request->getQueryParams()['type'] ?? $request->getParsedBody()['type'] ?? '0';
275 
276  $this->typoScriptFrontendController = GeneralUtility::makeInstance(
277  TypoScriptFrontendController::class,
278  GeneralUtility::makeInstance(Context::class),
279  $site,
280  $language,
281  $request->getAttribute('routing', new ‪PageArguments((int)$id, (string)$type, [])),
282  GeneralUtility::makeInstance(FrontendUserAuthentication::class)
283  );
284  $this->typoScriptFrontendController->sys_page = GeneralUtility::makeInstance(PageRepository::class);
285  $this->typoScriptFrontendController->tmpl = GeneralUtility::makeInstance(TemplateService::class);
287  }
288 }
‪TYPO3\CMS\Core\Routing\PageArguments
Definition: PageArguments.php:26
‪TYPO3\CMS\Frontend\Http\UrlProcessorInterface
Definition: UrlProcessorInterface.php:27
‪TYPO3\CMS\Core\Site\Entity\NullSite
Definition: NullSite.php:32
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:53
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:42
‪TYPO3\CMS\Core\Site\Entity\SiteLanguage
Definition: SiteLanguage.php:26
‪TYPO3\CMS\Core\Http\ServerRequestFactory
Definition: ServerRequestFactory.php:34
‪TYPO3\CMS\Core\Service\DependencyOrderingService
Definition: DependencyOrderingService.php:32
‪TYPO3\CMS\Core\TypoScript\TemplateService
Definition: TemplateService.php:46
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:104
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
Definition: FrontendUserAuthentication.php:32
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:53
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Http\ServerRequestFactory\fromGlobals
‪static ServerRequest fromGlobals()
Definition: ServerRequestFactory.php:59