[BUGFIX] Add initialization of DataMapper on QueryResult object wakeup
[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\Core\Environment;
18 use TYPO3\CMS\Core\LinkHandling\LinkService;
19 use TYPO3\CMS\Core\Resource\File;
20 use TYPO3\CMS\Core\SingletonInterface;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
23
24 /**
25 * Soft Reference processing class
26 * "Soft References" are references to database elements, files, email addresses, URls etc.
27 * which are found in-text in content. The <link [page_id]> tag from typical bodytext fields
28 * are an example of this.
29 * This class contains generic parsers for the most well-known types
30 * which are default for most TYPO3 installations. Soft References can also be userdefined.
31 * The Soft Reference parsers are used by the system to find these references and process them accordingly in import/export actions and copy operations.
32 *
33 * Example of usage
34 * Soft References:
35 * if ($conf['softref'] && (strong)$value !== '')) { // Check if a TCA configured field has softreferences defined (see TYPO3 Core API document)
36 * $softRefs = \TYPO3\CMS\Backend\Utility\BackendUtility::explodeSoftRefParserList($conf['softref']); // Explode the list of softreferences/parameters
37 * if ($softRefs !== FALSE) { // If there are soft references
38 * foreach($softRefs as $spKey => $spParams) { // Traverse soft references
39 * $softRefObj = \TYPO3\CMS\Backend\Utility\BackendUtility::softRefParserObj($spKey); // create / get object
40 * if (is_object($softRefObj)) { // If there was an object returned...:
41 * $resultArray = $softRefObj->findRef($table, $field, $uid, $softRefValue, $spKey, $spParams); // Do processing
42 *
43 * Result Array:
44 * The Result array should contain two keys: "content" and "elements".
45 * "content" is a string containing the input content but possibly with tokens inside.
46 * Tokens are strings like {softref:[tokenID]} which is a placeholder for a value extracted by a softref parser
47 * 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.
48 * "elements" is an array where the keys are insignificant, but the values are arrays with these keys:
49 * "matchString" => The value of the match. This is only for informational purposes to show what was found.
50 * "error" => An error message can be set here, like "file not found" etc.
51 * "subst" => array( // If this array is found there MUST be a token in the output content as well!
52 * "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.
53 * "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.
54 * "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)
55 * "relFileName" => (for "file" type): Relative filename. May not necessarily exist. This could be noticed in the error key.
56 * "recordRef" => (for "db" type) : Reference to DB record on the form [table]:[uid]. May not necessarily exist.
57 * "title" => Title of element (for backend information)
58 * "description" => Description of element (for backend information)
59 * )
60 */
61 /**
62 * Class for processing of the default soft reference types for CMS:
63 *
64 * - 'substitute' : A full field value targeted for manual substitution (for import /export features)
65 * - 'notify' : Just report if a value is found, nothing more.
66 * - 'images' : HTML <img> tags for RTE images
67 * - 'typolink' : references to page id or file, possibly with anchor/target, possibly commaseparated list.
68 * - 'typolink_tag' : As typolink, but searching for <link> tag to encapsulate it.
69 * - 'email' : Email highlight
70 * - 'url' : URL highlights (with a scheme)
71 */
72 class SoftReferenceIndex implements SingletonInterface
73 {
74 /**
75 * @var string
76 */
77 public $tokenID_basePrefix = '';
78
79 /**
80 * Main function through which all processing happens
81 *
82 * @param string $table Database table name
83 * @param string $field Field name for which processing occurs
84 * @param int $uid UID of the record
85 * @param string $content The content/value of the field
86 * @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.
87 * @param array $spParams Parameters of the softlink parser. Basically this is the content inside optional []-brackets after the softref keys. Parameters are exploded by ";
88 * @param string $structurePath If running from inside a FlexForm structure, this is the path of the tag.
89 * @return array|bool Result array on positive matches, see description above. Otherwise FALSE
90 */
91 public function findRef($table, $field, $uid, $content, $spKey, $spParams, $structurePath = '')
92 {
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 'typolink':
124 $retVal = $this->findRef_typolink($content, $spParams);
125 break;
126 case 'typolink_tag':
127 $retVal = $this->findRef_typolink_tag($content);
128 break;
129 case 'ext_fileref':
130 $retVal = $this->findRef_extension_fileref($content);
131 break;
132 case 'email':
133 $retVal = $this->findRef_email($content, $spParams);
134 break;
135 case 'url':
136 $retVal = $this->findRef_url($content, $spParams);
137 break;
138 default:
139 $retVal = false;
140 }
141 return $retVal;
142 }
143
144 /**
145 * TypoLink value processing.
146 * Will process input value as a TypoLink value.
147 *
148 * @param string $content The input content to analyze
149 * @param array $spParams Parameters set for the softref parser key in TCA/columns. value "linkList" will split the string by comma before processing.
150 * @return array Result array on positive matches, see description above. Otherwise FALSE
151 * @see \TYPO3\CMS\Frontend\ContentObject::typolink()
152 * @see getTypoLinkParts()
153 */
154 public function findRef_typolink($content, $spParams)
155 {
156 // First, split the input string by a comma if the "linkList" parameter is set.
157 // 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.
158 if (is_array($spParams) && in_array('linkList', $spParams)) {
159 // Preserving whitespace on purpose.
160 $linkElement = explode(',', $content);
161 } else {
162 // If only one element, just set in this array to make it easy below.
163 $linkElement = [$content];
164 }
165 // Traverse the links now:
166 $elements = [];
167 foreach ($linkElement as $k => $typolinkValue) {
168 $tLP = $this->getTypoLinkParts($typolinkValue);
169 $linkElement[$k] = $this->setTypoLinkPartsElement($tLP, $elements, $typolinkValue, $k);
170 }
171 // Return output:
172 if (!empty($elements)) {
173 $resultArray = [
174 'content' => implode(',', $linkElement),
175 'elements' => $elements
176 ];
177 return $resultArray;
178 }
179 }
180
181 /**
182 * TypoLink tag processing.
183 * Will search for <link ...> and <a> tags in the content string and process any found.
184 *
185 * @param string $content The input content to analyze
186 * @return array Result array on positive matches, see description above. Otherwise FALSE
187 * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::typolink()
188 * @see getTypoLinkParts()
189 */
190 public function findRef_typolink_tag($content)
191 {
192 // Parse string for special TYPO3 <link> tag:
193 $htmlParser = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Html\HtmlParser::class);
194 $linkService = GeneralUtility::makeInstance(LinkService::class);
195 $linkTags = $htmlParser->splitTags('a', $content);
196 // Traverse result:
197 $elements = [];
198 foreach ($linkTags as $key => $foundValue) {
199 if ($key % 2) {
200 if (preg_match('/href="([^"]+)"/', $foundValue, $matches)) {
201 try {
202 $linkDetails = $linkService->resolve($matches[1]);
203 if ($linkDetails['type'] === LinkService::TYPE_FILE && preg_match('/file\?uid=(\d+)/', $matches[1], $fileIdMatch)) {
204 $token = $this->makeTokenID($key);
205 $linkTags[$key] = str_replace($matches[1], '{softref:' . $token . '}', $linkTags[$key]);
206 $elements[$key]['subst'] = [
207 'type' => 'db',
208 'recordRef' => 'sys_file:' . $fileIdMatch[1],
209 'tokenID' => $token,
210 'tokenValue' => 'file:' . ($linkDetails['file'] instanceof File ? $linkDetails['file']->getUid() : $fileIdMatch[1])
211 ];
212 } elseif ($linkDetails['type'] === LinkService::TYPE_PAGE && preg_match('/page\?uid=(\d+)#?(\d+)?/', $matches[1], $pageAndAnchorMatches)) {
213 $token = $this->makeTokenID($key);
214 $linkTags[$key] = str_replace($matches[1], '{softref:' . $token . '}', $linkTags[$key]);
215 $elements[$key]['subst'] = [
216 'type' => 'db',
217 'recordRef' => 'pages:' . $linkDetails['pageuid'] . (isset($pageAndAnchorMatches[2]) ? '#c' . $pageAndAnchorMatches[2] : ''),
218 'tokenID' => $token,
219 'tokenValue' => $linkDetails['pageuid'] . (isset($pageAndAnchorMatches[2]) ? '#c' . $pageAndAnchorMatches[2] : '')
220 ];
221 } elseif ($linkDetails['type'] === LinkService::TYPE_URL) {
222 $token = $this->makeTokenID($key);
223 $linkTags[$key] = str_replace($matches[1], '{softref:' . $token . '}', $linkTags[$key]);
224 $elements[$key]['subst'] = [
225 'type' => 'external',
226 'tokenID' => $token,
227 'tokenValue' => $linkDetails['url']
228 ];
229 }
230 } catch (\Exception $e) {
231 // skip invalid links
232 }
233 } else {
234 // keep the legacy code for now
235 $typolinkValue = preg_replace('/<LINK[[:space:]]+/i', '', substr($foundValue, 0, -1));
236 $tLP = $this->getTypoLinkParts($typolinkValue);
237 $linkTags[$k] = '<LINK ' . $this->setTypoLinkPartsElement($tLP, $elements, $typolinkValue, $k) . '>';
238 }
239 }
240 }
241 // Return output:
242 if (!empty($elements)) {
243 $resultArray = [
244 'content' => implode('', $linkTags),
245 'elements' => $elements
246 ];
247 return $resultArray;
248 }
249 }
250
251 /**
252 * Finding email addresses in content and making them substitutable.
253 *
254 * @param string $content The input content to analyze
255 * @param array $spParams Parameters set for the softref parser key in TCA/columns
256 * @return array Result array on positive matches, see description above. Otherwise FALSE
257 */
258 public function findRef_email($content, $spParams)
259 {
260 // Email:
261 $parts = preg_split('/([^[:alnum:]]+)([A-Za-z0-9\\._-]+[@][A-Za-z0-9\\._-]+[\\.].[A-Za-z0-9]+)/', ' ' . $content . ' ', 10000, PREG_SPLIT_DELIM_CAPTURE);
262 foreach ($parts as $idx => $value) {
263 if ($idx % 3 == 2) {
264 $tokenID = $this->makeTokenID($idx);
265 $elements[$idx] = [];
266 $elements[$idx]['matchString'] = $value;
267 if (is_array($spParams) && in_array('subst', $spParams)) {
268 $parts[$idx] = '{softref:' . $tokenID . '}';
269 $elements[$idx]['subst'] = [
270 'type' => 'string',
271 'tokenID' => $tokenID,
272 'tokenValue' => $value
273 ];
274 }
275 }
276 }
277 // Return output:
278 if (!empty($elements)) {
279 $resultArray = [
280 'content' => substr(implode('', $parts), 1, -1),
281 'elements' => $elements
282 ];
283 return $resultArray;
284 }
285 }
286
287 /**
288 * Finding URLs in content
289 *
290 * @param string $content The input content to analyze
291 * @param array $spParams Parameters set for the softref parser key in TCA/columns
292 * @return array Result array on positive matches, see description above. Otherwise FALSE
293 */
294 public function findRef_url($content, $spParams)
295 {
296 // URLs
297 $parts = preg_split('/([^[:alnum:]"\']+)((https?|ftp):\\/\\/[^[:space:]"\'<>]*)([[:space:]])/', ' ' . $content . ' ', 10000, PREG_SPLIT_DELIM_CAPTURE);
298 foreach ($parts as $idx => $value) {
299 if ($idx % 5 == 3) {
300 unset($parts[$idx]);
301 }
302 if ($idx % 5 == 2) {
303 $tokenID = $this->makeTokenID($idx);
304 $elements[$idx] = [];
305 $elements[$idx]['matchString'] = $value;
306 if (is_array($spParams) && in_array('subst', $spParams)) {
307 $parts[$idx] = '{softref:' . $tokenID . '}';
308 $elements[$idx]['subst'] = [
309 'type' => 'string',
310 'tokenID' => $tokenID,
311 'tokenValue' => $value
312 ];
313 }
314 }
315 }
316 // Return output:
317 if (!empty($elements)) {
318 $resultArray = [
319 'content' => substr(implode('', $parts), 1, -1),
320 'elements' => $elements
321 ];
322 return $resultArray;
323 }
324 }
325
326 /**
327 * Finding reference to files from extensions in content, but only to notify about their existence. No substitution
328 *
329 * @param string $content The input content to analyze
330 * @return array Result array on positive matches, see description above. Otherwise FALSE
331 */
332 public function findRef_extension_fileref($content)
333 {
334 // Files starting with EXT:
335 $parts = preg_split('/([^[:alnum:]"\']+)(EXT:[[:alnum:]_]+\\/[^[:space:]"\',]*)/', ' ' . $content . ' ', 10000, PREG_SPLIT_DELIM_CAPTURE);
336 foreach ($parts as $idx => $value) {
337 if ($idx % 3 == 2) {
338 $this->makeTokenID($idx);
339 $elements[$idx] = [];
340 $elements[$idx]['matchString'] = $value;
341 }
342 }
343 // Return output:
344 if (!empty($elements)) {
345 $resultArray = [
346 'content' => substr(implode('', $parts), 1, -1),
347 'elements' => $elements
348 ];
349 return $resultArray;
350 }
351 }
352
353 /*************************
354 *
355 * Helper functions
356 *
357 *************************/
358
359 /**
360 * Analyze content as a TypoLink value and return an array with properties.
361 * TypoLinks format is: <link [typolink] [browser target] [css class] [title attribute] [additionalParams]>.
362 * See TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::typolink()
363 * The syntax of the [typolink] part is: [typolink] = [page id][,[type value]][#[anchor, if integer = tt_content uid]]
364 * The extraction is based on how \TYPO3\CMS\Frontend\ContentObject::typolink() behaves.
365 *
366 * @param string $typolinkValue TypoLink value.
367 * @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.
368 * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::typolink()
369 * @see setTypoLinkPartsElement()
370 */
371 public function getTypoLinkParts($typolinkValue)
372 {
373 $finalTagParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($typolinkValue);
374
375 $link_param = $finalTagParts['url'];
376 // we define various keys below, "url" might be misleading
377 unset($finalTagParts['url']);
378
379 if (stripos(rawurldecode(trim($link_param)), 'phar://') === 0) {
380 throw new \RuntimeException(
381 'phar scheme not allowed as soft reference target',
382 1530030672
383 );
384 }
385
386 // Parse URL:
387 $pU = @parse_url($link_param);
388
389 // If it's a mail address:
390 if (strpos($link_param, '@') !== false && !$pU['scheme']) {
391 $link_param = preg_replace('/^mailto:/i', '', $link_param);
392 $finalTagParts['LINK_TYPE'] = 'mailto';
393 $finalTagParts['url'] = trim($link_param);
394 return $finalTagParts;
395 }
396
397 if ($pU['scheme'] === 't3' && $pU['host'] === LinkService::TYPE_RECORD) {
398 $finalTagParts['LINK_TYPE'] = LinkService::TYPE_RECORD;
399 $finalTagParts['url'] = $link_param;
400 }
401
402 list($linkHandlerKeyword, $linkHandlerValue) = explode(':', trim($link_param), 2);
403
404 // Dispatch available signal slots.
405 $linkHandlerFound = false;
406 list($linkHandlerFound, $finalTagParts) = $this->emitGetTypoLinkParts($linkHandlerFound, $finalTagParts, $linkHandlerKeyword, $linkHandlerValue);
407 if ($linkHandlerFound) {
408 return $finalTagParts;
409 }
410
411 // Check for FAL link-handler keyword
412 if ($linkHandlerKeyword === 'file') {
413 $finalTagParts['LINK_TYPE'] = 'file';
414 $finalTagParts['identifier'] = trim($link_param);
415 return $finalTagParts;
416 }
417
418 $isLocalFile = 0;
419 $fileChar = (int)strpos($link_param, '/');
420 $urlChar = (int)strpos($link_param, '.');
421
422 // Detects if a file is found in site-root and if so it will be treated like a normal file.
423 list($rootFileDat) = explode('?', rawurldecode($link_param));
424 $containsSlash = strstr($rootFileDat, '/');
425 $rFD_fI = pathinfo($rootFileDat);
426 $fileExtension = strtolower($rFD_fI['extension']);
427 if (!$containsSlash && trim($rootFileDat) && (@is_file(Environment::getPublicPath() . '/' . $rootFileDat) || $fileExtension === 'php' || $fileExtension === 'html' || $fileExtension === 'htm')) {
428 $isLocalFile = 1;
429 } elseif ($containsSlash) {
430 // Adding this so realurl directories are linked right (non-existing).
431 $isLocalFile = 2;
432 }
433 if ($pU['scheme'] || ($isLocalFile != 1 && $urlChar && (!$containsSlash || $urlChar < $fileChar))) { // url (external): If doubleSlash or if a '.' comes before a '/'.
434 $finalTagParts['LINK_TYPE'] = 'url';
435 $finalTagParts['url'] = $link_param;
436 } elseif ($containsSlash || $isLocalFile) { // file (internal)
437 $splitLinkParam = explode('?', $link_param);
438 if (file_exists(rawurldecode($splitLinkParam[0])) || $isLocalFile) {
439 $finalTagParts['LINK_TYPE'] = 'file';
440 $finalTagParts['filepath'] = rawurldecode($splitLinkParam[0]);
441 $finalTagParts['query'] = $splitLinkParam[1];
442 }
443 } else {
444 // integer or alias (alias is without slashes or periods or commas, that is
445 // 'nospace,alphanum_x,lower,unique' according to definition in $GLOBALS['TCA']!)
446 $finalTagParts['LINK_TYPE'] = 'page';
447
448 $link_params_parts = explode('#', $link_param);
449 // Link-data del
450 $link_param = trim($link_params_parts[0]);
451
452 if ((string)$link_params_parts[1] !== '') {
453 $finalTagParts['anchor'] = trim($link_params_parts[1]);
454 }
455
456 // Splitting the parameter by ',' and if the array counts more than 1 element it's an id/type/? pair
457 $pairParts = GeneralUtility::trimExplode(',', $link_param);
458 if (count($pairParts) > 1) {
459 $link_param = $pairParts[0];
460 $finalTagParts['type'] = $pairParts[1]; // Overruling 'type'
461 }
462 $finalTagParts['page_id'] = (int)$link_param;
463 }
464
465 return $finalTagParts;
466 }
467
468 /**
469 * Recompile a TypoLink value from the array of properties made with getTypoLinkParts() into an elements array
470 *
471 * @param array $tLP TypoLink properties
472 * @param array $elements Array of elements to be modified with substitution / information entries.
473 * @param string $content The content to process.
474 * @param int $idx Index value of the found element - user to make unique but stable tokenID
475 * @return string The input content, possibly containing tokens now according to the added substitution entries in $elements
476 * @see getTypoLinkParts()
477 */
478 public function setTypoLinkPartsElement($tLP, &$elements, $content, $idx)
479 {
480 // Initialize, set basic values. In any case a link will be shown
481 $tokenID = $this->makeTokenID('setTypoLinkPartsElement:' . $idx);
482 $elements[$tokenID . ':' . $idx] = [];
483 $elements[$tokenID . ':' . $idx]['matchString'] = $content;
484 // Based on link type, maybe do more:
485 switch ((string)$tLP['LINK_TYPE']) {
486 case 'mailto':
487
488 case 'url':
489 // Mail addresses and URLs can be substituted manually:
490 $elements[$tokenID . ':' . $idx]['subst'] = [
491 'type' => 'string',
492 'tokenID' => $tokenID,
493 'tokenValue' => $tLP['url']
494 ];
495 // Output content will be the token instead:
496 $content = '{softref:' . $tokenID . '}';
497 break;
498 case 'file':
499 // Process files referenced by their FAL uid
500 if ($tLP['identifier']) {
501 list($linkHandlerKeyword, $linkHandlerValue) = explode(':', trim($tLP['identifier']), 2);
502 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($linkHandlerValue)) {
503 // Token and substitute value
504 $elements[$tokenID . ':' . $idx]['subst'] = [
505 'type' => 'db',
506 'recordRef' => 'sys_file:' . $linkHandlerValue,
507 'tokenID' => $tokenID,
508 'tokenValue' => $tLP['identifier'],
509 ];
510 // Output content will be the token instead:
511 $content = '{softref:' . $tokenID . '}';
512 } else {
513 // This is a link to a folder...
514 return $content;
515 }
516 } else {
517 return $content;
518 }
519 break;
520 case 'page':
521 // Rebuild page reference typolink part:
522 $content = '';
523 // Set page id:
524 if ($tLP['page_id']) {
525 $content .= '{softref:' . $tokenID . '}';
526 $elements[$tokenID . ':' . $idx]['subst'] = [
527 'type' => 'db',
528 'recordRef' => 'pages:' . $tLP['page_id'],
529 'tokenID' => $tokenID,
530 'tokenValue' => $tLP['page_id']
531 ];
532 }
533 // Add type if applicable
534 if ((string)$tLP['type'] !== '') {
535 $content .= ',' . $tLP['type'];
536 }
537 // Add anchor if applicable
538 if ((string)$tLP['anchor'] !== '') {
539 // Anchor is assumed to point to a content elements:
540 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($tLP['anchor'])) {
541 // Initialize a new entry because we have a new relation:
542 $newTokenID = $this->makeTokenID('setTypoLinkPartsElement:anchor:' . $idx);
543 $elements[$newTokenID . ':' . $idx] = [];
544 $elements[$newTokenID . ':' . $idx]['matchString'] = 'Anchor Content Element: ' . $tLP['anchor'];
545 $content .= '#{softref:' . $newTokenID . '}';
546 $elements[$newTokenID . ':' . $idx]['subst'] = [
547 'type' => 'db',
548 'recordRef' => 'tt_content:' . $tLP['anchor'],
549 'tokenID' => $newTokenID,
550 'tokenValue' => $tLP['anchor']
551 ];
552 } else {
553 // Anchor is a hardcoded string
554 $content .= '#' . $tLP['type'];
555 }
556 }
557 break;
558 case LinkService::TYPE_RECORD:
559 $elements[$tokenID . ':' . $idx]['subst'] = [
560 'type' => 'db',
561 'recordRef' => $tLP['table'] . ':' . $tLP['uid'],
562 'tokenID' => $tokenID,
563 'tokenValue' => $content,
564 ];
565
566 $content = '{softref:' . $tokenID . '}';
567 break;
568 default:
569 $linkHandlerFound = false;
570 list($linkHandlerFound, $tLP, $content, $newElements) = $this->emitSetTypoLinkPartsElement($linkHandlerFound, $tLP, $content, $elements, $idx, $tokenID);
571 // We need to merge the array, otherwise we would loose the reference.
572 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($elements, $newElements);
573
574 if (!$linkHandlerFound) {
575 $elements[$tokenID . ':' . $idx]['error'] = 'Couldn\'t decide typolink mode.';
576 return $content;
577 }
578 }
579 // Finally, for all entries that was rebuild with tokens, add target, class, title and additionalParams in the end:
580 $tLP['url'] = $content;
581 $content = GeneralUtility::makeInstance(TypoLinkCodecService::class)->encode($tLP);
582
583 // Return rebuilt typolink value:
584 return $content;
585 }
586
587 /**
588 * Make Token ID for input index.
589 *
590 * @param string $index Suffix value.
591 * @return string Token ID
592 */
593 public function makeTokenID($index = '')
594 {
595 return md5($this->tokenID_basePrefix . ':' . $index);
596 }
597
598 /**
599 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
600 */
601 protected function getSignalSlotDispatcher()
602 {
603 return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class);
604 }
605
606 /**
607 * @param bool $linkHandlerFound
608 * @param array $finalTagParts
609 * @param string $linkHandlerKeyword
610 * @param string $linkHandlerValue
611 * @return array
612 */
613 protected function emitGetTypoLinkParts($linkHandlerFound, $finalTagParts, $linkHandlerKeyword, $linkHandlerValue)
614 {
615 return $this->getSignalSlotDispatcher()->dispatch(static::class, 'getTypoLinkParts', [$linkHandlerFound, $finalTagParts, $linkHandlerKeyword, $linkHandlerValue]);
616 }
617
618 /**
619 * @param bool $linkHandlerFound
620 * @param array $tLP
621 * @param string $content
622 * @param array $elements
623 * @param int $idx
624 * @param string $tokenID
625 * @return array
626 */
627 protected function emitSetTypoLinkPartsElement($linkHandlerFound, $tLP, $content, $elements, $idx, $tokenID)
628 {
629 return $this->getSignalSlotDispatcher()->dispatch(static::class, 'setTypoLinkPartsElement', [$linkHandlerFound, $tLP, $content, $elements, $idx, $tokenID, $this]);
630 }
631 }