[TASK] Move linkData() functionality in PageLinkBuilder
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Typolink / PageLinkBuilder.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\Database\ConnectionPool;
19 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Core\Utility\MathUtility;
22 use TYPO3\CMS\Frontend\ContentObject\TypolinkModifyLinkConfigForPageLinksHookInterface;
23 use TYPO3\CMS\Frontend\Page\CacheHashCalculator;
24 use TYPO3\CMS\Frontend\Page\PageRepository;
25
26 /**
27 * Builds a TypoLink to a certain page
28 */
29 class PageLinkBuilder extends AbstractTypolinkBuilder
30 {
31 /**
32 * @inheritdoc
33 */
34 public function build(array &$linkDetails, string $linkText, string $target, array $conf): array
35 {
36 $tsfe = $this->getTypoScriptFrontendController();
37 // Checking if the id-parameter is an alias.
38 if (!empty($linkDetails['pagealias'])) {
39 $linkDetails['pageuid'] = $tsfe->sys_page->getPageIdFromAlias($linkDetails['pagealias']);
40 } elseif (empty($linkDetails['pageuid']) || $linkDetails['pageuid'] === 'current') {
41 // If no id or alias is given
42 $linkDetails['pageuid'] = $tsfe->id;
43 }
44
45 // Link to page even if access is missing?
46 if (isset($conf['linkAccessRestrictedPages'])) {
47 $disableGroupAccessCheck = (bool)$conf['linkAccessRestrictedPages'];
48 } else {
49 $disableGroupAccessCheck = (bool)$tsfe->config['config']['typolinkLinkAccessRestrictedPages'];
50 }
51
52 // Looking up the page record to verify its existence:
53 $page = $tsfe->sys_page->getPage($linkDetails['pageuid'], $disableGroupAccessCheck);
54
55 if (empty($page)) {
56 throw new UnableToLinkException('Page id "' . $linkDetails['typoLinkParameter'] . '" was not found, so "' . $linkText . '" was not linked.', 1490987336, null, $linkText);
57 }
58
59 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typolinkProcessing']['typolinkModifyParameterForPageLinks'] ?? [] as $classData) {
60 $hookObject = GeneralUtility::makeInstance($classData);
61 if (!$hookObject instanceof TypolinkModifyLinkConfigForPageLinksHookInterface) {
62 throw new \UnexpectedValueException('$hookObject must implement interface ' . TypolinkModifyLinkConfigForPageLinksHookInterface::class, 1483114905);
63 }
64 /** @var $hookObject TypolinkModifyLinkConfigForPageLinksHookInterface */
65 $conf = $hookObject->modifyPageLinkConfiguration($conf, $linkDetails, $page);
66 }
67 $enableLinksAcrossDomains = $tsfe->config['config']['typolinkEnableLinksAcrossDomains'];
68 if ($conf['no_cache.']) {
69 $conf['no_cache'] = (string)$this->contentObjectRenderer->stdWrap($conf['no_cache'], $conf['no_cache.']);
70 }
71
72 $sectionMark = trim(isset($conf['section.']) ? (string)$this->contentObjectRenderer->stdWrap($conf['section'], $conf['section.']) : (string)$conf['section']);
73 if ($sectionMark === '' && isset($linkDetails['fragment'])) {
74 $sectionMark = $linkDetails['fragment'];
75 }
76 if ($sectionMark !== '') {
77 $sectionMark = '#' . (MathUtility::canBeInterpretedAsInteger($sectionMark) ? 'c' : '') . $sectionMark;
78 }
79 // Overruling 'type'
80 $pageType = $linkDetails['pagetype'] ?? 0;
81
82 if (isset($linkDetails['parameters'])) {
83 $conf['additionalParams'] .= '&' . ltrim($linkDetails['parameters'], '&');
84 }
85 // MointPoints, look for closest MPvar:
86 $MPvarAcc = [];
87 if (!$tsfe->config['config']['MP_disableTypolinkClosestMPvalue']) {
88 $temp_MP = $this->getClosestMountPointValueForPage($page['uid']);
89 if ($temp_MP) {
90 $MPvarAcc['closest'] = $temp_MP;
91 }
92 }
93 // Look for overlay Mount Point:
94 $mount_info = $tsfe->sys_page->getMountPointInfo($page['uid'], $page);
95 if (is_array($mount_info) && $mount_info['overlay']) {
96 $page = $tsfe->sys_page->getPage($mount_info['mount_pid'], $disableGroupAccessCheck);
97 if (empty($page)) {
98 throw new UnableToLinkException('Mount point "' . $mount_info['mount_pid'] . '" was not available, so "' . $linkText . '" was not linked.', 1490987337, null, $linkText);
99 }
100 $MPvarAcc['re-map'] = $mount_info['MPvar'];
101 }
102 // Setting title if blank value to link
103 $linkText = $this->parseFallbackLinkTextIfLinkTextIsEmpty($linkText, $page['title']);
104 // Query Params:
105 $addQueryParams = $conf['addQueryString'] ? $this->contentObjectRenderer->getQueryArguments($conf['addQueryString.']) : '';
106 $addQueryParams .= isset($conf['additionalParams.']) ? trim((string)$this->contentObjectRenderer->stdWrap($conf['additionalParams'], $conf['additionalParams.'])) : trim((string)$conf['additionalParams']);
107 if ($addQueryParams === '&' || $addQueryParams[0] !== '&') {
108 $addQueryParams = '';
109 }
110 $targetDomain = '';
111 $currentDomain = (string)GeneralUtility::getIndpEnv('HTTP_HOST');
112 // Mount pages are always local and never link to another domain
113 if (!empty($MPvarAcc)) {
114 // Add "&MP" var:
115 $addQueryParams .= '&MP=' . rawurlencode(implode(',', $MPvarAcc));
116 } elseif (strpos($addQueryParams, '&MP=') === false) {
117 // We do not come here if additionalParams had '&MP='. This happens when typoLink is called from
118 // menu. Mount points always work in the content of the current domain and we must not change
119 // domain if MP variables exist.
120 // If we link across domains and page is free type shortcut, we must resolve the shortcut first!
121 // If we do not do it, TYPO3 will fail to (1) link proper page in RealURL/CoolURI because
122 // they return relative links and (2) show proper page if no RealURL/CoolURI exists when link is clicked
123 if ($enableLinksAcrossDomains
124 && (int)$page['doktype'] === PageRepository::DOKTYPE_SHORTCUT
125 && (int)$page['shortcut_mode'] === PageRepository::SHORTCUT_MODE_NONE
126 ) {
127 // Save in case of broken destination or endless loop
128 $page2 = $page;
129 // Same as in RealURL, seems enough
130 $maxLoopCount = 20;
131 while ($maxLoopCount
132 && is_array($page)
133 && (int)$page['doktype'] === PageRepository::DOKTYPE_SHORTCUT
134 && (int)$page['shortcut_mode'] === PageRepository::SHORTCUT_MODE_NONE
135 ) {
136 $page = $tsfe->sys_page->getPage($page['shortcut'], $disableGroupAccessCheck);
137 $maxLoopCount--;
138 }
139 if (empty($page) || $maxLoopCount === 0) {
140 // We revert if shortcut is broken or maximum number of loops is exceeded (indicates endless loop)
141 $page = $page2;
142 }
143 }
144
145 $targetDomainRecord = $tsfe->getDomainDataForPid($page['uid']);
146 $targetDomain = $targetDomainRecord ? $targetDomainRecord['domainName'] : null;
147 // Do not prepend the domain if it is the current hostname
148 if (!$targetDomain || $tsfe->domainNameMatchesCurrentRequest($targetDomain)) {
149 $targetDomain = '';
150 }
151 }
152 if ($conf['useCacheHash']) {
153 $params = $tsfe->linkVars . $addQueryParams . '&id=' . $page['uid'];
154 if (trim($params, '& ') !== '') {
155 $cacheHash = GeneralUtility::makeInstance(CacheHashCalculator::class);
156 $cHash = $cacheHash->generateForParameters($params);
157 $addQueryParams .= $cHash ? '&cHash=' . $cHash : '';
158 }
159 unset($params);
160 }
161 $absoluteUrlScheme = 'http';
162 // URL shall be absolute:
163 if (isset($conf['forceAbsoluteUrl']) && $conf['forceAbsoluteUrl']) {
164 // Override scheme:
165 if (isset($conf['forceAbsoluteUrl.']['scheme']) && $conf['forceAbsoluteUrl.']['scheme']) {
166 $absoluteUrlScheme = $conf['forceAbsoluteUrl.']['scheme'];
167 } elseif (GeneralUtility::getIndpEnv('TYPO3_SSL')) {
168 $absoluteUrlScheme = 'https';
169 }
170 // If no domain records are defined, use current domain:
171 $currentUrlScheme = parse_url(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), PHP_URL_SCHEME);
172 if ($targetDomain === '' && ($conf['forceAbsoluteUrl'] || $absoluteUrlScheme !== $currentUrlScheme)) {
173 $targetDomain = $currentDomain;
174 }
175 // If go for an absolute link, add site path if it's not taken care about by absRefPrefix
176 if (!$tsfe->config['config']['absRefPrefix'] && $targetDomain === $currentDomain) {
177 $targetDomain = $currentDomain . rtrim(GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'), '/');
178 }
179 }
180 // If target page has a different domain and the current domain's linking scheme (e.g. RealURL/...) should not be used
181 if ($targetDomain !== '' && $targetDomain !== $currentDomain && !$enableLinksAcrossDomains) {
182 $target = $target ?: $this->resolveTargetAttribute($conf, 'extTarget', false, $tsfe->extTarget);
183 // Convert IDNA-like domain (if any)
184 if (!preg_match('/^[a-z0-9.\\-]*$/i', $targetDomain)) {
185 $targetDomain = GeneralUtility::idnaEncode($targetDomain);
186 }
187 $url = $absoluteUrlScheme . '://' . $targetDomain . '/index.php?id=' . $page['uid'] . $addQueryParams . $sectionMark;
188 } else {
189 // Internal link or current domain's linking scheme should be used
190 // Internal target:
191 $target = (isset($page['target']) && trim($page['target'])) ? $page['target'] : $target;
192 if (empty($target)) {
193 $target = $this->resolveTargetAttribute($conf, 'target', true, $tsfe->intTarget);
194 }
195 $LD = $this->createTotalUrlAndLinkData($page, $target, $conf['no_cache'], $addQueryParams, $pageType, $targetDomain);
196 if ($targetDomain !== '') {
197 // We will add domain only if URL does not have it already.
198 if ($enableLinksAcrossDomains && $targetDomain !== $currentDomain && isset($tsfe->config['config']['absRefPrefix'])) {
199 // Get rid of the absRefPrefix if necessary. absRefPrefix is applicable only
200 // to the current web site. If we have domain here it means we link across
201 // domains. absRefPrefix can contain domain name, which will screw up
202 // the link to the external domain.
203 $prefixLength = strlen($tsfe->config['config']['absRefPrefix']);
204 if (substr($LD['totalURL'], 0, $prefixLength) === $tsfe->config['config']['absRefPrefix']) {
205 $LD['totalURL'] = substr($LD['totalURL'], $prefixLength);
206 }
207 }
208 $urlParts = parse_url($LD['totalURL']);
209 if (empty($urlParts['host'])) {
210 $LD['totalURL'] = $absoluteUrlScheme . '://' . $targetDomain . ($LD['totalURL'][0] === '/' ? '' : '/') . $LD['totalURL'];
211 }
212 }
213 $url = $LD['totalURL'] . $sectionMark;
214 }
215 // If sectionMark is set, there is no baseURL AND the current page is the page the link is to, check if there are any additional parameters or addQueryString parameters and if not, drop the url.
216 if ($sectionMark
217 && !$tsfe->config['config']['baseURL']
218 && (int)$page['uid'] === (int)$tsfe->id
219 && !trim($addQueryParams)
220 && (empty($conf['addQueryString']) || !isset($conf['addQueryString.']))
221 ) {
222 $currentQueryArray = GeneralUtility::explodeUrl2Array(GeneralUtility::getIndpEnv('QUERY_STRING'), true);
223 $currentQueryParams = GeneralUtility::implodeArrayForUrl('', $currentQueryArray, '', false, true);
224
225 if (!trim($currentQueryParams)) {
226 list(, $URLparams) = explode('?', $url);
227 list($URLparams) = explode('#', (string)$URLparams);
228 parse_str($URLparams . $LD['orig_type'], $URLparamsArray);
229 // Type nums must match as well as page ids
230 if ((int)$URLparamsArray['type'] === (int)$tsfe->type) {
231 unset($URLparamsArray['id']);
232 unset($URLparamsArray['type']);
233 // If there are no parameters left.... set the new url.
234 if (empty($URLparamsArray)) {
235 $url = $sectionMark;
236 }
237 }
238 }
239 }
240
241 // If link is to an access restricted page which should be redirected, then find new URL:
242 if (empty($conf['linkAccessRestrictedPages'])
243 && $tsfe->config['config']['typolinkLinkAccessRestrictedPages']
244 && $tsfe->config['config']['typolinkLinkAccessRestrictedPages'] !== 'NONE'
245 && !$tsfe->checkPageGroupAccess($page)
246 ) {
247 $thePage = $tsfe->sys_page->getPage($tsfe->config['config']['typolinkLinkAccessRestrictedPages']);
248 $addParams = str_replace(
249 [
250 '###RETURN_URL###',
251 '###PAGE_ID###'
252 ],
253 [
254 rawurlencode($url),
255 $page['uid']
256 ],
257 $tsfe->config['config']['typolinkLinkAccessRestrictedPages_addParams']
258 );
259 $url = $this->contentObjectRenderer->getTypoLink_URL($thePage['uid'] . ($pageType ? ',' . $pageType : ''), $addParams, $target);
260 $url = $this->forceAbsoluteUrl($url, $conf);
261 $this->contentObjectRenderer->lastTypoLinkLD['totalUrl'] = $url;
262 }
263
264 return [$url, $linkText, $target];
265 }
266
267 /**
268 * Returns the &MP variable value for a page id.
269 * The function will do its best to find a MP value that will keep the page id inside the current Mount Point rootline if any.
270 *
271 * @param int $pageId page id
272 * @return string MP value, prefixed with &MP= (depending on $raw)
273 */
274 protected function getClosestMountPointValueForPage($pageId)
275 {
276 $tsfe = $this->getTypoScriptFrontendController();
277 if (empty($GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) || !$tsfe->MP) {
278 return '';
279 }
280 // Same page as current.
281 if ((int)$tsfe->id === (int)$pageId) {
282 return $tsfe->MP;
283 }
284
285 // Find closest mount point
286 // Gets rootline of linked-to page
287 $tCR_rootline = $tsfe->sys_page->getRootLine($pageId, '', true);
288 $inverseTmplRootline = array_reverse($tsfe->tmpl->rootLine);
289 $rl_mpArray = [];
290 $startMPaccu = false;
291 // Traverse root line of link uid and inside of that the REAL root line of current position.
292 foreach ($tCR_rootline as $tCR_data) {
293 foreach ($inverseTmplRootline as $rlKey => $invTmplRLRec) {
294 // Force accumulating when in overlay mode: Links to this page have to stay within the current branch
295 if ($invTmplRLRec['_MOUNT_OL'] && (int)$tCR_data['uid'] === (int)$invTmplRLRec['uid']) {
296 $startMPaccu = true;
297 }
298 // Accumulate MP data:
299 if ($startMPaccu && $invTmplRLRec['_MP_PARAM']) {
300 $rl_mpArray[] = $invTmplRLRec['_MP_PARAM'];
301 }
302 // If two PIDs matches and this is NOT the site root, start accumulation of MP data (on the next level):
303 // (The check for site root is done so links to branches outsite the site but sharing the site roots PID
304 // is NOT detected as within the branch!)
305 if ((int)$tCR_data['pid'] === (int)$invTmplRLRec['pid'] && count($inverseTmplRootline) !== $rlKey + 1) {
306 $startMPaccu = true;
307 }
308 }
309 if ($startMPaccu) {
310 // Good enough...
311 break;
312 }
313 }
314 return !empty($rl_mpArray) ? implode(',', array_reverse($rl_mpArray)) : '';
315 }
316
317 /**
318 * Initializes the automatically created mountPointMap coming from the "config.MP_mapRootPoints" setting
319 * Can be called many times with overhead only the first time since then the map is generated and cached in memory.
320 *
321 * Previously located within TemplateService::getFromMPmap()
322 *
323 * @param int $pageId Page id to return MPvar value for.
324 * @return string
325 */
326 public function getMountPointParameterFromRootPointMaps(int $pageId)
327 {
328 // Create map if not found already
329 $mountPointMap = $this->initializeMountPointMap(
330 $this->getTypoScriptFrontendController()->config['config']['MP_defaults'] ?: null,
331 $this->getTypoScriptFrontendController()->config['config']['MP_mapRootPoints'] ?: null
332 );
333
334 // Finding MP var for Page ID:
335 if (is_array($mountPointMap[$pageId]) && !empty($mountPointMap[$pageId])) {
336 return implode(',', $mountPointMap[$pageId]);
337 }
338 return '';
339 }
340
341 /**
342 * Create mount point map, based on TypoScript config.MP_mapRootPoints and config.MP_defaults.
343 *
344 * @param string $defaultMountPoints a string as defined in config.MP_defaults
345 * @param string|null $mapRootPointList a string as defined in config.MP_mapRootPoints
346 * @return array
347 */
348 protected function initializeMountPointMap(string $defaultMountPoints = null, string $mapRootPointList = null): array
349 {
350 static $mountPointMap = [];
351 if (!empty($mountPointMap) || (empty($mapRootPointList) && empty($defaultMountPoints))) {
352 return $mountPointMap;
353 }
354 if ($defaultMountPoints) {
355 $defaultMountPoints = GeneralUtility::trimExplode('|', $defaultMountPoints, true);
356 foreach ($defaultMountPoints as $temp_p) {
357 list($temp_idP, $temp_MPp) = explode(':', $temp_p, 2);
358 $temp_ids = GeneralUtility::intExplode(',', $temp_idP);
359 foreach ($temp_ids as $temp_id) {
360 $mountPointMap[$temp_id] = trim($temp_MPp);
361 }
362 }
363 }
364
365 $rootPoints = GeneralUtility::trimExplode(',', strtolower($mapRootPointList), true);
366 // Traverse rootpoints
367 foreach ($rootPoints as $p) {
368 $initMParray = [];
369 if ($p === 'root') {
370 $rootPage = $this->getTypoScriptFrontendController()->tmpl->rootLine[0];
371 $p = $rootPage['uid'];
372 if ($p['_MOUNT_OL'] && $p['_MP_PARAM']) {
373 $initMParray[] = $p['_MP_PARAM'];
374 }
375 }
376 $this->populateMountPointMapForPageRecursively($mountPointMap, $p, $initMParray);
377 }
378 return $mountPointMap;
379 }
380
381 /**
382 * Creating mountPointMap for a certain ID root point.
383 * Previously called TemplateService->initMPmap_create()
384 *
385 * @param array $mountPointMap the exiting mount point map
386 * @param int $id Root id from which to start map creation.
387 * @param array $MP_array MP_array passed from root page.
388 * @param int $level Recursion brake. Incremented for each recursive call. 20 is the limit.
389 * @see getMountPointParameterFromRootPointMaps()
390 */
391 protected function populateMountPointMapForPageRecursively(array &$mountPointMap, int $id, $MP_array = [], $level = 0)
392 {
393 if ($id <= 0) {
394 return;
395 }
396 // First level, check id
397 if (!$level) {
398 // Find mount point if any:
399 $mount_info = $this->getTypoScriptFrontendController()->sys_page->getMountPointInfo($id);
400 // Overlay mode:
401 if (is_array($mount_info) && $mount_info['overlay']) {
402 $MP_array[] = $mount_info['MPvar'];
403 $id = $mount_info['mount_pid'];
404 }
405 // Set mapping information for this level:
406 $mountPointMap[$id] = $MP_array;
407 // Normal mode:
408 if (is_array($mount_info) && !$mount_info['overlay']) {
409 $MP_array[] = $mount_info['MPvar'];
410 $id = $mount_info['mount_pid'];
411 }
412 }
413 if ($id && $level < 20) {
414 $nextLevelAcc = [];
415 // Select and traverse current level pages:
416 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
417 $queryBuilder->getRestrictions()
418 ->removeAll()
419 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
420 $queryResult = $queryBuilder
421 ->select('uid', 'pid', 'doktype', 'mount_pid', 'mount_pid_ol')
422 ->from('pages')
423 ->where(
424 $queryBuilder->expr()->eq(
425 'pid',
426 $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)
427 ),
428 $queryBuilder->expr()->neq(
429 'doktype',
430 $queryBuilder->createNamedParameter(PageRepository::DOKTYPE_RECYCLER, \PDO::PARAM_INT)
431 ),
432 $queryBuilder->expr()->neq(
433 'doktype',
434 $queryBuilder->createNamedParameter(PageRepository::DOKTYPE_BE_USER_SECTION, \PDO::PARAM_INT)
435 )
436 )->execute();
437 while ($row = $queryResult->fetch()) {
438 // Find mount point if any:
439 $next_id = $row['uid'];
440 $next_MP_array = $MP_array;
441 $mount_info = $this->getTypoScriptFrontendController()->sys_page->getMountPointInfo($next_id, $row);
442 // Overlay mode:
443 if (is_array($mount_info) && $mount_info['overlay']) {
444 $next_MP_array[] = $mount_info['MPvar'];
445 $next_id = $mount_info['mount_pid'];
446 }
447 if (!isset($mountPointMap[$next_id])) {
448 // Set mapping information for this level:
449 $mountPointMap[$next_id] = $next_MP_array;
450 // Normal mode:
451 if (is_array($mount_info) && !$mount_info['overlay']) {
452 $next_MP_array[] = $mount_info['MPvar'];
453 $next_id = $mount_info['mount_pid'];
454 }
455 // Register recursive call
456 // (have to do it this way since ALL of the current level should be registered BEFORE the sublevel at any time)
457 $nextLevelAcc[] = [$next_id, $next_MP_array];
458 }
459 }
460 // Call recursively, if any:
461 foreach ($nextLevelAcc as $pSet) {
462 $this->populateMountPointMapForPageRecursively($mountPointMap, $pSet[0], $pSet[1], $level + 1);
463 }
464 }
465 }
466
467 /**
468 * The mother of all functions creating links/URLs etc in a TypoScript environment.
469 * See the references below.
470 * Basically this function takes care of issues such as type,id,alias and Mount Points, URL rewriting (through hooks), M5/B6 encoded parameters etc.
471 * It is important to pass all links created through this function since this is the guarantee that globally configured settings for link creating are observed and that your applications will conform to the various/many configuration options in TypoScript Templates regarding this.
472 *
473 * @param array $page The page record of the page to which we are creating a link. Needed due to fields like uid, alias, target, title and sectionIndex_uid.
474 * @param string $target Target string
475 * @param bool $no_cache If set, then the "&no_cache=1" parameter is included in the URL.
476 * @param string $addParams Additional URL parameters to set in the URL. Syntax is "&foo=bar&foo2=bar2" etc. Also used internally to add parameters if needed.
477 * @param string $typeOverride If you set this value to something else than a blank string, then the typeNumber used in the link will be forced to this value. Normally the typeNum is based on the target set OR on $this->getTypoScriptFrontendController()->config['config']['forceTypeValue'] if found.
478 * @param string $targetDomain The target Doamin, if any was detected in typolink
479 * @return array Contains keys like "totalURL", "url", "sectionIndex", "linkVars", "no_cache", "type" of which "totalURL" is normally the value you would use while the other keys contains various parts that was used to construct "totalURL
480 */
481 protected function createTotalUrlAndLinkData($page, $target, $no_cache, $addParams = '', $typeOverride = '', $targetDomain = '')
482 {
483 $LD = [];
484 // Adding Mount Points, "&MP=", parameter for the current page if any is set
485 // but non other set explicitly
486 if (strpos($addParams, '&MP=') === false) {
487 $mountPointParameter = $this->getMountPointParameterFromRootPointMaps((int)$page['uid']);
488 if ($mountPointParameter) {
489 $addParams .= '&MP=' . rawurlencode($mountPointParameter);
490 }
491 }
492 // Setting ID/alias:
493 $script = 'index.php';
494 if ($page['alias']) {
495 $LD['url'] = $script . '?id=' . rawurlencode($page['alias']);
496 } else {
497 $LD['url'] = $script . '?id=' . $page['uid'];
498 }
499 // typeNum
500 $typeNum = $this->getTypoScriptFrontendController()->tmpl->setup[$target . '.']['typeNum'];
501 if (!MathUtility::canBeInterpretedAsInteger($typeOverride) && (int)$this->getTypoScriptFrontendController()->config['config']['forceTypeValue']) {
502 $typeOverride = (int)$this->getTypoScriptFrontendController()->config['config']['forceTypeValue'];
503 }
504 if ((string)$typeOverride !== '') {
505 $typeNum = $typeOverride;
506 }
507 // Override...
508 if ($typeNum) {
509 $LD['type'] = '&type=' . (int)$typeNum;
510 } else {
511 $LD['type'] = '';
512 }
513 // Preserving the type number.
514 $LD['orig_type'] = $LD['type'];
515 // noCache
516 $LD['no_cache'] = $no_cache ? '&no_cache=1' : '';
517 // linkVars
518 if ($addParams) {
519 $LD['linkVars'] = GeneralUtility::implodeArrayForUrl('', GeneralUtility::explodeUrl2Array($this->getTypoScriptFrontendController()->linkVars . $addParams), '', false, true);
520 } else {
521 $LD['linkVars'] = $this->getTypoScriptFrontendController()->linkVars;
522 }
523 // Add absRefPrefix if exists.
524 $LD['url'] = $this->getTypoScriptFrontendController()->absRefPrefix . $LD['url'];
525 // If the special key 'sectionIndex_uid' (added 'manually' in tslib/menu.php to the page-record) is set, then the link jumps directly to a section on the page.
526 $LD['sectionIndex'] = $page['sectionIndex_uid'] ? '#c' . $page['sectionIndex_uid'] : '';
527 // Compile the normal total url
528 $LD['totalURL'] = rtrim($LD['url'] . $LD['type'] . $LD['no_cache'] . $LD['linkVars'] . $this->getTypoScriptFrontendController()->getMethodUrlIdToken, '?') . $LD['sectionIndex'];
529 // Call post processing function for link rendering:
530 $_params = [
531 'LD' => &$LD,
532 'args' => ['page' => $page, 'oTarget' => $target, 'no_cache' => $no_cache, 'script' => $script, 'addParams' => $addParams, 'typeOverride' => $typeOverride, 'targetDomain' => $targetDomain],
533 'typeNum' => $typeNum
534 ];
535 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tstemplate.php']['linkData-PostProc'] ?? [] as $_funcRef) {
536 GeneralUtility::callUserFunction($_funcRef, $_params, $this->getTypoScriptFrontendController()->tmpl);
537 }
538 return $LD;
539 }
540 }