98beccd4a498da686e4beaaf49dabae6bbb050c5
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Database / SoftReferenceIndex.php
1 <?php
2 namespace TYPO3\CMS\Core\Database;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
18 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
19 use TYPO3\CMS\Core\LinkHandling\LinkService;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
22
23 /**
24 * Soft Reference processing class
25 * "Soft References" are references to database elements, files, email addresses, URls etc.
26 * which are found in-text in content. The <link [page_id]> tag from typical bodytext fields
27 * are an example of this.
28 * This class contains generic parsers for the most well-known types
29 * which are default for most TYPO3 installations. Soft References can also be userdefined.
30 * The Soft Reference parsers are used by the system to find these references and process them accordingly in import/export actions and copy operations.
31 *
32 * Example of usage
33 * Soft References:
34 * if ($conf['softref'] && (strong)$value !== '')) { // Check if a TCA configured field has softreferences defined (see TYPO3 Core API document)
35 * $softRefs = \TYPO3\CMS\Backend\Utility\BackendUtility::explodeSoftRefParserList($conf['softref']); // Explode the list of softreferences/parameters
36 * if ($softRefs !== FALSE) { // If there are soft references
37 * foreach($softRefs as $spKey => $spParams) { // Traverse soft references
38 * $softRefObj = \TYPO3\CMS\Backend\Utility\BackendUtility::softRefParserObj($spKey); // create / get object
39 * if (is_object($softRefObj)) { // If there was an object returned...:
40 * $resultArray = $softRefObj->findRef($table, $field, $uid, $softRefValue, $spKey, $spParams); // Do processing
41 *
42 * Result Array:
43 * The Result array should contain two keys: "content" and "elements".
44 * "content" is a string containing the input content but possibly with tokens inside.
45 * Tokens are strings like {softref:[tokenID]} which is a placeholder for a value extracted by a softref parser
46 * For each token there MUST be an entry in the "elements" key which has a "subst" key defining the tokenID and the tokenValue. See below.
47 * "elements" is an array where the keys are insignificant, but the values are arrays with these keys:
48 * "matchString" => The value of the match. This is only for informational purposes to show what was found.
49 * "error" => An error message can be set here, like "file not found" etc.
50 * "subst" => array( // If this array is found there MUST be a token in the output content as well!
51 * "tokenID" => The tokenID string corresponding to the token in output content, {softref:[tokenID]}. This is typically an md5 hash of a string defining uniquely the position of the element.
52 * "tokenValue" => The value that the token substitutes in the text. Basically, if this value is inserted instead of the token the content should match what was inputted originally.
53 * "type" => file / db / string = the type of substitution. "file" means it is a relative file [automatically mapped], "db" means a database record reference [automatically mapped], "string" means it is manually modified string content (eg. an email address)
54 * "relFileName" => (for "file" type): Relative filename. May not necessarily exist. This could be noticed in the error key.
55 * "recordRef" => (for "db" type) : Reference to DB record on the form [table]:[uid]. May not necessarily exist.
56 * "title" => Title of element (for backend information)
57 * "description" => Description of element (for backend information)
58 * )
59 */
60 /**
61 * Class for processing of the default soft reference types for CMS:
62 *
63 * - 'substitute' : A full field value targeted for manual substitution (for import /export features)
64 * - 'notify' : Just report if a value is found, nothing more.
65 * - 'images' : HTML <img> tags for RTE images
66 * - 'typolink' : references to page id or file, possibly with anchor/target, possibly commaseparated list.
67 * - 'typolink_tag' : As typolink, but searching for <link> tag to encapsulate it.
68 * - 'email' : Email highlight
69 * - 'url' : URL highlights (with a scheme)
70 */
71 class SoftReferenceIndex
72 {
73 /**
74 * @var string
75 */
76 public $tokenID_basePrefix = '';
77
78 /**
79 * Main function through which all processing happens
80 *
81 * @param string $table Database table name
82 * @param string $field Field name for which processing occurs
83 * @param int $uid UID of the record
84 * @param string $content The content/value of the field
85 * @param string $spKey The softlink parser key. This is only interesting if more than one parser is grouped in the same class. That is the case with this parser.
86 * @param array $spParams Parameters of the softlink parser. Basically this is the content inside optional []-brackets after the softref keys. Parameters are exploded by ";
87 * @param string $structurePath If running from inside a FlexForm structure, this is the path of the tag.
88 * @return array Result array on positive matches, see description above. Otherwise FALSE
89 */
90 public function findRef($table, $field, $uid, $content, $spKey, $spParams, $structurePath = '')
91 {
92 $retVal = false;
93 $this->tokenID_basePrefix = $table . ':' . $uid . ':' . $field . ':' . $structurePath . ':' . $spKey;
94 switch ($spKey) {
95 case 'notify':
96 // Simple notification
97 $resultArray = [
98 'elements' => [
99 [
100 'matchString' => $content
101 ]
102 ]
103 ];
104 $retVal = $resultArray;
105 break;
106 case 'substitute':
107 $tokenID = $this->makeTokenID();
108 $resultArray = [
109 'content' => '{softref:' . $tokenID . '}',
110 'elements' => [
111 [
112 'matchString' => $content,
113 'subst' => [
114 'type' => 'string',
115 'tokenID' => $tokenID,
116 'tokenValue' => $content
117 ]
118 ]
119 ]
120 ];
121 $retVal = $resultArray;
122 break;
123 case 'images':
124 $retVal = $this->findRef_images($content, $spParams);
125 break;
126 case 'typolink':
127 $retVal = $this->findRef_typolink($content, $spParams);
128 break;
129 case 'typolink_tag':
130 $retVal = $this->findRef_typolink_tag($content, $spParams);
131 break;
132 case 'ext_fileref':
133 $retVal = $this->findRef_extension_fileref($content, $spParams);
134 break;
135 case 'email':
136 $retVal = $this->findRef_email($content, $spParams);
137 break;
138 case 'url':
139 $retVal = $this->findRef_url($content, $spParams);
140 break;
141 default:
142 $retVal = false;
143 }
144 return $retVal;
145 }
146
147 /**
148 * Finding image tags in the content.
149 * All images that are not from external URLs will be returned with an info text
150 * Will only return files in uploads/ folders which are prefixed with "RTEmagic[C|P]_" for substitution
151 * Any "clear.gif" images are ignored.
152 *
153 * @param string $content The input content to analyse
154 * @param array $spParams Parameters set for the softref parser key in TCA/columns
155 * @return array Result array on positive matches, see description above. Otherwise FALSE
156 */
157 public function findRef_images($content, $spParams)
158 {
159 // Start HTML parser and split content by image tag:
160 $htmlParser = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Html\HtmlParser::class);
161 $splitContent = $htmlParser->splitTags('img', $content);
162 $elements = [];
163 // Traverse splitted parts:
164 foreach ($splitContent as $k => $v) {
165 if ($k % 2) {
166 // Get file reference:
167 $attribs = $htmlParser->get_tag_attributes($v);
168 $srcRef = htmlspecialchars_decode($attribs[0]['src']);
169 $pI = pathinfo($srcRef);
170 // If it looks like a local image, continue. Otherwise ignore it.
171 $absPath = GeneralUtility::getFileAbsFileName(PATH_site . $srcRef);
172 if (!$pI['scheme'] && !$pI['query'] && $absPath && $srcRef !== 'clear.gif') {
173 // Initialize the element entry with info text here:
174 $tokenID = $this->makeTokenID($k);
175 $elements[$k] = [];
176 $elements[$k]['matchString'] = $v;
177 // If the image seems to be an RTE image, then proceed to set up substitution token:
178 if (GeneralUtility::isFirstPartOfStr($srcRef, 'uploads/') && preg_match('/^RTEmagicC_/', basename($srcRef))) {
179 // Token and substitute value:
180 // Make sure the value we work on is found and will get substituted in the content (Very important that the src-value is not DeHSC'ed)
181 if (strstr($splitContent[$k], $attribs[0]['src'])) {
182 // Substitute value with token (this is not be an exact method if the value is in there twice, but we assume it will not)
183 $splitContent[$k] = str_replace($attribs[0]['src'], '{softref:' . $tokenID . '}', $splitContent[$k]);
184 $elements[$k]['subst'] = [
185 'type' => 'file',
186 'relFileName' => $srcRef,
187 'tokenID' => $tokenID,
188 'tokenValue' => $attribs[0]['src']
189 ];
190 // Finally, notice if the file does not exist.
191 if (!@is_file($absPath)) {
192 $elements[$k]['error'] = 'File does not exist!';
193 }
194 } else {
195 $elements[$k]['error'] = 'Could not substitute image source with token!';
196 }
197 }
198 }
199 }
200 }
201 // Return result:
202 if (!empty($elements)) {
203 $resultArray = [
204 'content' => implode('', $splitContent),
205 'elements' => $elements
206 ];
207 return $resultArray;
208 }
209 }
210
211 /**
212 * TypoLink value processing.
213 * Will process input value as a TypoLink value.
214 *
215 * @param string $content The input content to analyse
216 * @param array $spParams Parameters set for the softref parser key in TCA/columns. value "linkList" will split the string by comma before processing.
217 * @return array Result array on positive matches, see description above. Otherwise FALSE
218 * @see \TYPO3\CMS\Frontend\ContentObject::typolink(), getTypoLinkParts()
219 */
220 public function findRef_typolink($content, $spParams)
221 {
222 // First, split the input string by a comma if the "linkList" parameter is set.
223 // An example: the link field for images in content elements of type "textpic" or "image". This field CAN be configured to define a link per image, separated by comma.
224 if (is_array($spParams) && in_array('linkList', $spParams)) {
225 // Preserving whitespace on purpose.
226 $linkElement = explode(',', $content);
227 } else {
228 // If only one element, just set in this array to make it easy below.
229 $linkElement = [$content];
230 }
231 // Traverse the links now:
232 $elements = [];
233 foreach ($linkElement as $k => $typolinkValue) {
234 $tLP = $this->getTypoLinkParts($typolinkValue);
235 $linkElement[$k] = $this->setTypoLinkPartsElement($tLP, $elements, $typolinkValue, $k);
236 }
237 // Return output:
238 if (!empty($elements)) {
239 $resultArray = [
240 'content' => implode(',', $linkElement),
241 'elements' => $elements
242 ];
243 return $resultArray;
244 }
245 }
246
247 /**
248 * TypoLink tag processing.
249 * Will search for <link ...> tags in the content string and process any found.
250 *
251 * @param string $content The input content to analyse
252 * @param array $spParams Parameters set for the softref parser key in TCA/columns
253 * @return array Result array on positive matches, see description above. Otherwise FALSE
254 * @see \TYPO3\CMS\Frontend\ContentObject::typolink(), getTypoLinkParts()
255 */
256 public function findRef_typolink_tag($content, $spParams)
257 {
258 // Parse string for special TYPO3 <link> tag:
259 $htmlParser = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Html\HtmlParser::class);
260 $linkTags = $htmlParser->splitTags('link', $content);
261 // Traverse result:
262 $elements = [];
263 foreach ($linkTags as $k => $foundValue) {
264 if ($k % 2) {
265 $typolinkValue = preg_replace('/<LINK[[:space:]]+/i', '', substr($foundValue, 0, -1));
266 $tLP = $this->getTypoLinkParts($typolinkValue);
267 $linkTags[$k] = '<LINK ' . $this->setTypoLinkPartsElement($tLP, $elements, $typolinkValue, $k) . '>';
268 }
269 }
270 // Return output:
271 if (!empty($elements)) {
272 $resultArray = [
273 'content' => implode('', $linkTags),
274 'elements' => $elements
275 ];
276 return $resultArray;
277 }
278 }
279
280 /**
281 * Finding email addresses in content and making them substitutable.
282 *
283 * @param string $content The input content to analyse
284 * @param array $spParams Parameters set for the softref parser key in TCA/columns
285 * @return array Result array on positive matches, see description above. Otherwise FALSE
286 */
287 public function findRef_email($content, $spParams)
288 {
289 // Email:
290 $parts = preg_split('/([^[:alnum:]]+)([A-Za-z0-9\\._-]+[@][A-Za-z0-9\\._-]+[\\.].[A-Za-z0-9]+)/', ' ' . $content . ' ', 10000, PREG_SPLIT_DELIM_CAPTURE);
291 foreach ($parts as $idx => $value) {
292 if ($idx % 3 == 2) {
293 $tokenID = $this->makeTokenID($idx);
294 $elements[$idx] = [];
295 $elements[$idx]['matchString'] = $value;
296 if (is_array($spParams) && in_array('subst', $spParams)) {
297 $parts[$idx] = '{softref:' . $tokenID . '}';
298 $elements[$idx]['subst'] = [
299 'type' => 'string',
300 'tokenID' => $tokenID,
301 'tokenValue' => $value
302 ];
303 }
304 }
305 }
306 // Return output:
307 if (!empty($elements)) {
308 $resultArray = [
309 'content' => substr(implode('', $parts), 1, -1),
310 'elements' => $elements
311 ];
312 return $resultArray;
313 }
314 }
315
316 /**
317 * Finding URLs in content
318 *
319 * @param string $content The input content to analyse
320 * @param array $spParams Parameters set for the softref parser key in TCA/columns
321 * @return array Result array on positive matches, see description above. Otherwise FALSE
322 */
323 public function findRef_url($content, $spParams)
324 {
325 // URLs
326 $parts = preg_split('/([^[:alnum:]"\']+)((http|ftp):\\/\\/[^[:space:]"\'<>]*)([[:space:]])/', ' ' . $content . ' ', 10000, PREG_SPLIT_DELIM_CAPTURE);
327 foreach ($parts as $idx => $value) {
328 if ($idx % 5 == 3) {
329 unset($parts[$idx]);
330 }
331 if ($idx % 5 == 2) {
332 $tokenID = $this->makeTokenID($idx);
333 $elements[$idx] = [];
334 $elements[$idx]['matchString'] = $value;
335 if (is_array($spParams) && in_array('subst', $spParams)) {
336 $parts[$idx] = '{softref:' . $tokenID . '}';
337 $elements[$idx]['subst'] = [
338 'type' => 'string',
339 'tokenID' => $tokenID,
340 'tokenValue' => $value
341 ];
342 }
343 }
344 }
345 // Return output:
346 if (!empty($elements)) {
347 $resultArray = [
348 'content' => substr(implode('', $parts), 1, -1),
349 'elements' => $elements
350 ];
351 return $resultArray;
352 }
353 }
354
355 /**
356 * Finding reference to files from extensions in content, but only to notify about their existence. No substitution
357 *
358 * @param string $content The input content to analyse
359 * @param array $spParams Parameters set for the softref parser key in TCA/columns
360 * @return array Result array on positive matches, see description above. Otherwise FALSE
361 */
362 public function findRef_extension_fileref($content, $spParams)
363 {
364 // Files starting with EXT:
365 $parts = preg_split('/([^[:alnum:]"\']+)(EXT:[[:alnum:]_]+\\/[^[:space:]"\',]*)/', ' ' . $content . ' ', 10000, PREG_SPLIT_DELIM_CAPTURE);
366 foreach ($parts as $idx => $value) {
367 if ($idx % 3 == 2) {
368 $this->makeTokenID($idx);
369 $elements[$idx] = [];
370 $elements[$idx]['matchString'] = $value;
371 }
372 }
373 // Return output:
374 if (!empty($elements)) {
375 $resultArray = [
376 'content' => substr(implode('', $parts), 1, -1),
377 'elements' => $elements
378 ];
379 return $resultArray;
380 }
381 }
382
383 /*************************
384 *
385 * Helper functions
386 *
387 *************************/
388
389 /**
390 * Analyse content as a TypoLink value and return an array with properties.
391 * TypoLinks format is: <link [typolink] [browser target] [css class] [title attribute] [additionalParams]>.
392 * See TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::typolink()
393 * The syntax of the [typolink] part is: [typolink] = [page id or alias][,[type value]][#[anchor, if integer = tt_content uid]]
394 * The extraction is based on how \TYPO3\CMS\Frontend\ContentObject::typolink() behaves.
395 *
396 * @param string $typolinkValue TypoLink value.
397 * @return array Array with the properties of the input link specified. The key "LINK_TYPE" will reveal the type. If that is blank it could not be determined.
398 * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::typolink(), setTypoLinkPartsElement()
399 */
400 public function getTypoLinkParts($typolinkValue)
401 {
402 $finalTagParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($typolinkValue);
403
404 $link_param = $finalTagParts['url'];
405 // we define various keys below, "url" might be misleading
406 unset($finalTagParts['url']);
407
408 // Parse URL:
409 $pU = @parse_url($link_param);
410
411 // If it's a mail address:
412 if (strstr($link_param, '@') && !$pU['scheme']) {
413 $link_param = preg_replace('/^mailto:/i', '', $link_param);
414 $finalTagParts['LINK_TYPE'] = 'mailto';
415 $finalTagParts['url'] = trim($link_param);
416 return $finalTagParts;
417 }
418
419 if ($pU['scheme'] === 't3' && $pU['host'] === LinkService::TYPE_RECORD) {
420 $finalTagParts['LINK_TYPE'] = LinkService::TYPE_RECORD;
421 $finalTagParts['url'] = $link_param;
422 }
423
424 list($linkHandlerKeyword, $linkHandlerValue) = explode(':', trim($link_param), 2);
425
426 // Dispatch available signal slots.
427 $linkHandlerFound = false;
428 list($linkHandlerFound, $finalTagParts) = $this->emitGetTypoLinkParts($linkHandlerFound, $finalTagParts, $linkHandlerKeyword, $linkHandlerValue);
429 if ($linkHandlerFound) {
430 return $finalTagParts;
431 }
432
433 // Check for FAL link-handler keyword
434 if ($linkHandlerKeyword === 'file') {
435 $finalTagParts['LINK_TYPE'] = 'file';
436 $finalTagParts['identifier'] = trim($link_param);
437 return $finalTagParts;
438 }
439
440 $isLocalFile = 0;
441 $fileChar = (int)strpos($link_param, '/');
442 $urlChar = (int)strpos($link_param, '.');
443
444 // Detects if a file is found in site-root and if so it will be treated like a normal file.
445 list($rootFileDat) = explode('?', rawurldecode($link_param));
446 $containsSlash = strstr($rootFileDat, '/');
447 $rFD_fI = pathinfo($rootFileDat);
448 $fileExtension = strtolower($rFD_fI['extension']);
449 if (!$containsSlash && trim($rootFileDat) && (@is_file(PATH_site . $rootFileDat) || $fileExtension === 'php' || $fileExtension === 'html' || $fileExtension === 'htm')) {
450 $isLocalFile = 1;
451 } elseif ($containsSlash) {
452 // Adding this so realurl directories are linked right (non-existing).
453 $isLocalFile = 2;
454 }
455 if ($pU['scheme'] || ($isLocalFile != 1 && $urlChar && (!$containsSlash || $urlChar < $fileChar))) { // url (external): If doubleSlash or if a '.' comes before a '/'.
456 $finalTagParts['LINK_TYPE'] = 'url';
457 $finalTagParts['url'] = $link_param;
458 } elseif ($containsSlash || $isLocalFile) { // file (internal)
459 $splitLinkParam = explode('?', $link_param);
460 if (file_exists(rawurldecode($splitLinkParam[0])) || $isLocalFile) {
461 $finalTagParts['LINK_TYPE'] = 'file';
462 $finalTagParts['filepath'] = rawurldecode($splitLinkParam[0]);
463 $finalTagParts['query'] = $splitLinkParam[1];
464 }
465 } else {
466 // integer or alias (alias is without slashes or periods or commas, that is
467 // 'nospace,alphanum_x,lower,unique' according to definition in $GLOBALS['TCA']!)
468 $finalTagParts['LINK_TYPE'] = 'page';
469
470 $link_params_parts = explode('#', $link_param);
471 // Link-data del
472 $link_param = trim($link_params_parts[0]);
473
474 if ((string)$link_params_parts[1] !== '') {
475 $finalTagParts['anchor'] = trim($link_params_parts[1]);
476 }
477
478 // Splitting the parameter by ',' and if the array counts more than 1 element it's a id/type/? pair
479 $pairParts = GeneralUtility::trimExplode(',', $link_param);
480 if (count($pairParts) > 1) {
481 $link_param = $pairParts[0];
482 $finalTagParts['type'] = $pairParts[1]; // Overruling 'type'
483 }
484
485 // Checking if the id-parameter is an alias.
486 if ((string)$link_param !== '') {
487 if (!\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($link_param)) {
488 $finalTagParts['alias'] = $link_param;
489 $link_param = $this->getPageIdFromAlias($link_param);
490 }
491
492 $finalTagParts['page_id'] = (int)$link_param;
493 }
494 }
495
496 return $finalTagParts;
497 }
498
499 /**
500 * Recompile a TypoLink value from the array of properties made with getTypoLinkParts() into an elements array
501 *
502 * @param array $tLP TypoLink properties
503 * @param array $elements Array of elements to be modified with substitution / information entries.
504 * @param string $content The content to process.
505 * @param int $idx Index value of the found element - user to make unique but stable tokenID
506 * @return string The input content, possibly containing tokens now according to the added substitution entries in $elements
507 * @see getTypoLinkParts()
508 */
509 public function setTypoLinkPartsElement($tLP, &$elements, $content, $idx)
510 {
511 // Initialize, set basic values. In any case a link will be shown
512 $tokenID = $this->makeTokenID('setTypoLinkPartsElement:' . $idx);
513 $elements[$tokenID . ':' . $idx] = [];
514 $elements[$tokenID . ':' . $idx]['matchString'] = $content;
515 // Based on link type, maybe do more:
516 switch ((string)$tLP['LINK_TYPE']) {
517 case 'mailto':
518
519 case 'url':
520 // Mail addresses and URLs can be substituted manually:
521 $elements[$tokenID . ':' . $idx]['subst'] = [
522 'type' => 'string',
523 'tokenID' => $tokenID,
524 'tokenValue' => $tLP['url']
525 ];
526 // Output content will be the token instead:
527 $content = '{softref:' . $tokenID . '}';
528 break;
529 case 'file':
530 // Process files referenced by their FAL uid
531 if ($tLP['identifier']) {
532 list($linkHandlerKeyword, $linkHandlerValue) = explode(':', trim($tLP['identifier']), 2);
533 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($linkHandlerValue)) {
534 // Token and substitute value
535 $elements[$tokenID . ':' . $idx]['subst'] = [
536 'type' => 'db',
537 'recordRef' => 'sys_file:' . $linkHandlerValue,
538 'tokenID' => $tokenID,
539 'tokenValue' => $tLP['identifier'],
540 ];
541 // Output content will be the token instead:
542 $content = '{softref:' . $tokenID . '}';
543 } else {
544 // This is a link to a folder...
545 return $content;
546 }
547 } else {
548 return $content;
549 }
550 break;
551 case 'page':
552 // Rebuild page reference typolink part:
553 $content = '';
554 // Set page id:
555 if ($tLP['page_id']) {
556 $content .= '{softref:' . $tokenID . '}';
557 $elements[$tokenID . ':' . $idx]['subst'] = [
558 'type' => 'db',
559 'recordRef' => 'pages:' . $tLP['page_id'],
560 'tokenID' => $tokenID,
561 'tokenValue' => $tLP['alias'] ? $tLP['alias'] : $tLP['page_id']
562 ];
563 }
564 // Add type if applicable
565 if ((string)$tLP['type'] !== '') {
566 $content .= ',' . $tLP['type'];
567 }
568 // Add anchor if applicable
569 if ((string)$tLP['anchor'] !== '') {
570 // Anchor is assumed to point to a content elements:
571 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($tLP['anchor'])) {
572 // Initialize a new entry because we have a new relation:
573 $newTokenID = $this->makeTokenID('setTypoLinkPartsElement:anchor:' . $idx);
574 $elements[$newTokenID . ':' . $idx] = [];
575 $elements[$newTokenID . ':' . $idx]['matchString'] = 'Anchor Content Element: ' . $tLP['anchor'];
576 $content .= '#{softref:' . $newTokenID . '}';
577 $elements[$newTokenID . ':' . $idx]['subst'] = [
578 'type' => 'db',
579 'recordRef' => 'tt_content:' . $tLP['anchor'],
580 'tokenID' => $newTokenID,
581 'tokenValue' => $tLP['anchor']
582 ];
583 } else {
584 // Anchor is a hardcoded string
585 $content .= '#' . $tLP['type'];
586 }
587 }
588 break;
589 case LinkService::TYPE_RECORD:
590 $elements[$tokenID . ':' . $idx]['subst'] = [
591 'type' => 'db',
592 'recordRef' => $tLP['table'] . ':' . $tLP['uid'],
593 'tokenID' => $tokenID,
594 'tokenValue' => $content,
595 ];
596
597 $content = '{softref:' . $tokenID . '}';
598 break;
599 default:
600 $linkHandlerFound = false;
601 list($linkHandlerFound, $tLP, $content, $newElements) = $this->emitSetTypoLinkPartsElement($linkHandlerFound, $tLP, $content, $elements, $idx, $tokenID);
602 // We need to merge the array, otherwise we would loose the reference.
603 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($elements, $newElements);
604
605 if (!$linkHandlerFound) {
606 $elements[$tokenID . ':' . $idx]['error'] = 'Couldn\'t decide typolink mode.';
607 return $content;
608 }
609 }
610 // Finally, for all entries that was rebuild with tokens, add target, class, title and additionalParams in the end:
611 $tLP['url'] = $content;
612 $content = GeneralUtility::makeInstance(TypoLinkCodecService::class)->encode($tLP);
613
614 // Return rebuilt typolink value:
615 return $content;
616 }
617
618 /**
619 * Look up and return page uid for alias
620 *
621 * @param string $link_param Page alias string value
622 * @return int Page uid corresponding to alias value.
623 */
624 public function getPageIdFromAlias($link_param)
625 {
626 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
627 $queryBuilder->getRestrictions()
628 ->removeAll()
629 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
630 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
631
632 $pageUid = $queryBuilder->select('uid')
633 ->from('pages')
634 ->where(
635 $queryBuilder->expr()->eq('alias', $queryBuilder->createNamedParameter($link_param, \PDO::PARAM_STR))
636 )
637 ->setMaxResults(1)
638 ->execute()
639 ->fetchColumn(0);
640
641 return (int)$pageUid;
642 }
643
644 /**
645 * Make Token ID for input index.
646 *
647 * @param string $index Suffix value.
648 * @return string Token ID
649 */
650 public function makeTokenID($index = '')
651 {
652 return md5($this->tokenID_basePrefix . ':' . $index);
653 }
654
655 /**
656 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
657 */
658 protected function getSignalSlotDispatcher()
659 {
660 return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class);
661 }
662
663 /**
664 * @param bool $linkHandlerFound
665 * @param array $finalTagParts
666 * @param string $linkHandlerKeyword
667 * @param string $linkHandlerValue
668 * @return array
669 */
670 protected function emitGetTypoLinkParts($linkHandlerFound, $finalTagParts, $linkHandlerKeyword, $linkHandlerValue)
671 {
672 return $this->getSignalSlotDispatcher()->dispatch(get_class($this), 'getTypoLinkParts', [$linkHandlerFound, $finalTagParts, $linkHandlerKeyword, $linkHandlerValue]);
673 }
674
675 /**
676 * @param bool $linkHandlerFound
677 * @param array $tLP
678 * @param string $content
679 * @param array $elements
680 * @param int $idx
681 * @param string $tokenID
682 * @return array
683 */
684 protected function emitSetTypoLinkPartsElement($linkHandlerFound, $tLP, $content, $elements, $idx, $tokenID)
685 {
686 return $this->getSignalSlotDispatcher()->dispatch(get_class($this), 'setTypoLinkPartsElement', [$linkHandlerFound, $tLP, $content, $elements, $idx, $tokenID, $this]);
687 }
688 }