29ec07ba53babee180b90e26a0eea109f74287a5
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / LinkHandling / LinkService.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Core\LinkHandling;
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\SingletonInterface;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * Class LinkService, responsible to find what kind of resource (type) is used
23 * to link to (email, external url, file, page etc)
24 * with the possibility to get a system-wide understandable "urn" to identify
25 * what type it actually is, based on the scheme or prefix.
26 */
27 class LinkService implements SingletonInterface
28 {
29 const TYPE_PAGE = 'page';
30 const TYPE_URL = 'url';
31 const TYPE_EMAIL = 'email';
32 const TYPE_FILE = 'file';
33 const TYPE_FOLDER = 'folder';
34 const TYPE_RECORD = 'record';
35 const TYPE_UNKNOWN = 'unknown';
36
37 // @TODO There needs to be an API to make these types extensible as the former 'typolinkLinkHandler' does not work anymore! forge #79647
38
39 /**
40 * All registered LinkHandlers
41 *
42 * @var LinkHandlingInterface[]
43 */
44 protected $handlers;
45
46 /**
47 * LinkService constructor initializes the registered handlers.
48 */
49 public function __construct()
50 {
51 if (!empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['linkHandler'])) {
52 foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['linkHandler'] as $type => $handler) {
53 if (!is_object($this->handlers[$type])) {
54 $this->handlers[$type] = GeneralUtility::makeInstance($handler);
55 }
56 }
57 }
58 }
59
60 /**
61 * Part of the typolink construction functionality, called by typoLink()
62 * Used to resolve "legacy"-based typolinks and URNs.
63 *
64 * Tries to get the type of the link from the link parameter
65 * could be
66 * - "mailto" an email address
67 * - "url" external URL
68 * - "file" a local file (checked AFTER getPublicUrl() is called)
69 * - "page" a page (integer or alias)
70 *
71 * Does NOT check if the page exists or the file exists.
72 *
73 * @param string $linkParameter could be "fileadmin/myfile.jpg", "info@typo3.org", "13" or "http://www.typo3.org"
74 * @return array
75 */
76 public function resolve(string $linkParameter): array
77 {
78 try {
79 // Check if the new syntax with "t3://" is used
80 return $this->resolveByStringRepresentation($linkParameter);
81 } catch (\InvalidArgumentException $e) {
82 $legacyLinkNotationConverter = GeneralUtility::makeInstance(LegacyLinkNotationConverter::class);
83 return $legacyLinkNotationConverter->resolve($linkParameter);
84 }
85 }
86
87 /**
88 * Returns a array with data interpretation of the link target, something like t3:blabla.
89 *
90 * @param string $urn
91 * @return array
92 */
93 public function resolveByStringRepresentation(string $urn): array
94 {
95 // linking to any t3:// syntax
96 if (stripos($urn, 't3://') === 0) {
97 // lets parse the urn
98 $urnParsed = parse_url($urn);
99 $type = $urnParsed['host'];
100 if (isset($urnParsed['query'])) {
101 parse_str(htmlspecialchars_decode($urnParsed['query']), $data);
102 } else {
103 $data = [];
104 }
105 $fragment = $urnParsed['fragment'] ?? null;
106
107 if (is_object($this->handlers[$type])) {
108 $result = $this->handlers[$type]->resolveHandlerData($data);
109 $result['type'] = $type;
110 } else {
111 throw new \InvalidArgumentException('LinkHandler for ' . $type . ' was not registered', 1460581769);
112 }
113 // this was historically named "section"
114 if ($fragment) {
115 $result['fragment'] = $fragment;
116 }
117 } elseif (stripos($urn, '://') && $this->handlers[self::TYPE_URL]) {
118 $result = $this->handlers[self::TYPE_URL]->resolveHandlerData(['url' => $urn]);
119 $result['type'] = self::TYPE_URL;
120 } elseif (stripos($urn, 'mailto:') === 0 && $this->handlers[self::TYPE_EMAIL]) {
121 $result = $this->handlers[self::TYPE_EMAIL]->resolveHandlerData(['email' => $urn]);
122 $result['type'] = self::TYPE_EMAIL;
123 } else {
124 throw new \InvalidArgumentException('No valid URN to resolve found', 1457177667);
125 }
126
127 return $result;
128 }
129
130 /**
131 * Returns a string interpretation of the link target, something like
132 *
133 * - t3://page?uid=23&my=value#cool
134 * - https://www.typo3.org/
135 * - t3://file?uid=13
136 * - t3://folder?storage=2&identifier=/my/folder/
137 * - mailto:mac@safe.com
138 *
139 * @param array $parameters
140 * @return string
141 * @throws \InvalidArgumentException
142 */
143 public function asString(array $parameters): string
144 {
145 if (is_object($this->handlers[$parameters['type']])) {
146 return $this->handlers[$parameters['type']]->asString($parameters);
147 }
148 throw new \InvalidArgumentException('No valid handlers found for type: ' . $parameters['type'], 1460629247);
149 }
150 }