[!!!][TASK] Drop "documentation" extension
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Typolink / AbstractTypolinkBuilder.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Frontend\Typolink;
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
18 use TYPO3\CMS\Core\Service\DependencyOrderingService;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
21 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
22 use TYPO3\CMS\Frontend\Http\UrlProcessorInterface;
23 use TYPO3\CMS\Frontend\Page\PageRepository;
24
25 /**
26 * Abstract class to provide proper helper for most types necessary
27 * Hands in the contentobject which is needed here for all the stdWrap magic.
28 */
29 abstract class AbstractTypolinkBuilder
30 {
31 /**
32 * @var ContentObjectRenderer
33 */
34 protected $contentObjectRenderer;
35
36 /**
37 * AbstractTypolinkBuilder constructor.
38 *
39 * @param $contentObjectRenderer ContentObjectRenderer
40 */
41 public function __construct(ContentObjectRenderer $contentObjectRenderer)
42 {
43 $this->contentObjectRenderer = $contentObjectRenderer;
44 }
45
46 /**
47 * Should be implemented by all subclasses to return an array with three parts:
48 * - URL
49 * - Link Text (can be modified)
50 * - Target (can be modified)
51 *
52 * @param array $linkDetails parsed link details by the LinkService
53 * @param string $linkText the link text
54 * @param string $target the target to point to
55 * @param array $conf the TypoLink configuration array
56 * @return array an array with three parts (URL, Link Text, Target)
57 */
58 abstract public function build(array &$linkDetails, string $linkText, string $target, array $conf): array;
59
60 /**
61 * Forces a given URL to be absolute.
62 *
63 * @param string $url The URL to be forced to be absolute
64 * @param array $configuration TypoScript configuration of typolink
65 * @return string The absolute URL
66 */
67 protected function forceAbsoluteUrl(string $url, array $configuration): string
68 {
69 if (!empty($url) && !empty($configuration['forceAbsoluteUrl']) && preg_match('#^(?:([a-z]+)(://)([^/]*)/?)?(.*)$#', $url, $matches)) {
70 $urlParts = [
71 'scheme' => $matches[1],
72 'delimiter' => '://',
73 'host' => $matches[3],
74 'path' => $matches[4]
75 ];
76 $isUrlModified = false;
77 // Set scheme and host if not yet part of the URL:
78 if (empty($urlParts['host'])) {
79 $urlParts['scheme'] = GeneralUtility::getIndpEnv('TYPO3_SSL') ? 'https' : 'http';
80 $urlParts['host'] = GeneralUtility::getIndpEnv('HTTP_HOST');
81 $urlParts['path'] = '/' . ltrim($urlParts['path'], '/');
82 // absRefPrefix has been prepended to $url beforehand
83 // so we only modify the path if no absRefPrefix has been set
84 // otherwise we would destroy the path
85 if ($this->getTypoScriptFrontendController()->absRefPrefix === '') {
86 $urlParts['path'] = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH') . ltrim($urlParts['path'], '/');
87 }
88 $isUrlModified = true;
89 }
90 // Override scheme:
91 $forceAbsoluteUrl = &$configuration['forceAbsoluteUrl.']['scheme'];
92 if (!empty($forceAbsoluteUrl) && $urlParts['scheme'] !== $forceAbsoluteUrl) {
93 $urlParts['scheme'] = $forceAbsoluteUrl;
94 $isUrlModified = true;
95 }
96 // Recreate the absolute URL:
97 if ($isUrlModified) {
98 $url = implode('', $urlParts);
99 }
100 }
101 return $url;
102 }
103
104 /**
105 * Helper method to a fallback method parsing HTML out of it
106 *
107 * @param string $originalLinkText the original string, if empty, the fallback link text
108 * @param string $fallbackLinkText the string to be used.
109 * @return string the final text
110 */
111 protected function parseFallbackLinkTextIfLinkTextIsEmpty(string $originalLinkText, string $fallbackLinkText): string
112 {
113 if ($originalLinkText === '') {
114 return $this->contentObjectRenderer->parseFunc($fallbackLinkText, ['makelinks' => 0], '< lib.parseFunc');
115 }
116 return $originalLinkText;
117 }
118
119 /**
120 * Creates the value for target="..." in a typolink configuration
121 *
122 * @param array $conf the typolink configuration
123 * @param string $name the key, usually "target", "extTarget" or "fileTarget"
124 * @param bool $respectFrameSetOption if set, then the fallback is only used as target if the doctype allows it
125 * @param string $fallbackTarget the string to be used when no target is found in the configuration
126 * @return string the value of the target attribute, if there is one
127 */
128 protected function resolveTargetAttribute(array $conf, string $name, bool $respectFrameSetOption = false, string $fallbackTarget = ''): string
129 {
130 $tsfe = $this->getTypoScriptFrontendController();
131 $targetAttributeAllowed = (!$respectFrameSetOption || !$tsfe->config['config']['doctype'] ||
132 in_array((string)$tsfe->config['config']['doctype'], ['xhtml_trans', 'xhtml_basic', 'html5'], true));
133
134 $target = '';
135 if (isset($conf[$name])) {
136 $target = $conf[$name];
137 } elseif ($targetAttributeAllowed) {
138 $target = $fallbackTarget;
139 }
140 if ($conf[$name . '.']) {
141 $target = (string)$this->contentObjectRenderer->stdWrap($target, $conf[$name . '.']);
142 }
143 return $target;
144 }
145
146 /**
147 * Loops over all configured URL modifier hooks (if available) and returns the generated URL or NULL if no URL was generated.
148 *
149 * @param string $context The context in which the method is called (e.g. typoLink).
150 * @param string $url The URL that should be processed.
151 * @param array $typolinkConfiguration The current link configuration array.
152 * @return string|null Returns NULL if URL was not processed or the processed URL as a string.
153 * @throws \RuntimeException if a hook was registered but did not fulfill the correct parameters.
154 */
155 protected function processUrl(string $context, string $url, array $typolinkConfiguration = [])
156 {
157 $urlProcessors = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'] ?? false;
158 if (!$urlProcessors) {
159 return $url;
160 }
161
162 foreach ($urlProcessors as $identifier => $configuration) {
163 if (empty($configuration) || !is_array($configuration)) {
164 throw new \RuntimeException('Missing configuration for URI processor "' . $identifier . '".', 1491130459);
165 }
166 if (!is_string($configuration['processor']) || empty($configuration['processor']) || !class_exists($configuration['processor']) || !is_subclass_of($configuration['processor'], UrlProcessorInterface::class)) {
167 throw new \RuntimeException('The URI processor "' . $identifier . '" defines an invalid provider. Ensure the class exists and implements the "' . UrlProcessorInterface::class . '".', 1491130460);
168 }
169 }
170
171 $orderedProcessors = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($urlProcessors);
172 $keepProcessing = true;
173
174 foreach ($orderedProcessors as $configuration) {
175 /** @var UrlProcessorInterface $urlProcessor */
176 $urlProcessor = GeneralUtility::makeInstance($configuration['processor']);
177 $url = $urlProcessor->process($context, $url, $typolinkConfiguration, $this->contentObjectRenderer, $keepProcessing);
178 if (!$keepProcessing) {
179 break;
180 }
181 }
182
183 return $url;
184 }
185
186 /**
187 * @return TypoScriptFrontendController
188 */
189 public function getTypoScriptFrontendController(): TypoScriptFrontendController
190 {
191 if (!$GLOBALS['TSFE']) {
192 // This usually happens when typolink is created by the TYPO3 Backend, where no TSFE object
193 // is there. This functionality is currently completely internal, as these links cannot be
194 // created properly from the Backend.
195 // However, this is added to avoid any exceptions when trying to create a link
196 $GLOBALS['TSFE'] = GeneralUtility::makeInstance(
197 TypoScriptFrontendController::class,
198 null,
199 GeneralUtility::_GP('id'),
200 (int)GeneralUtility::_GP('type')
201 );
202 $GLOBALS['TSFE']->sys_page = GeneralUtility::makeInstance(PageRepository::class);
203 $GLOBALS['TSFE']->sys_page->init(false);
204 $GLOBALS['TSFE']->initTemplate();
205 }
206 return $GLOBALS['TSFE'];
207 }
208 }