5a525e0f4d970f448b0ace5d1fde16a2660ca063
[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 (Exception\UnknownUrnException $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 * @throws Exception\UnknownLinkHandlerException
93 * @throws Exception\UnknownUrnException
94 */
95 public function resolveByStringRepresentation(string $urn): array
96 {
97 // linking to any t3:// syntax
98 if (stripos($urn, 't3://') === 0) {
99 // lets parse the urn
100 $urnParsed = parse_url($urn);
101 $type = $urnParsed['host'];
102 if (isset($urnParsed['query'])) {
103 parse_str(htmlspecialchars_decode($urnParsed['query']), $data);
104 } else {
105 $data = [];
106 }
107 $fragment = $urnParsed['fragment'] ?? null;
108
109 if (is_object($this->handlers[$type])) {
110 $result = $this->handlers[$type]->resolveHandlerData($data);
111 $result['type'] = $type;
112 } else {
113 throw new Exception\UnknownLinkHandlerException('LinkHandler for ' . $type . ' was not registered', 1460581769);
114 }
115 // this was historically named "section"
116 if ($fragment) {
117 $result['fragment'] = $fragment;
118 }
119 } elseif (stripos($urn, '://') && $this->handlers[self::TYPE_URL]) {
120 $result = $this->handlers[self::TYPE_URL]->resolveHandlerData(['url' => $urn]);
121 $result['type'] = self::TYPE_URL;
122 } elseif (stripos($urn, 'mailto:') === 0 && $this->handlers[self::TYPE_EMAIL]) {
123 $result = $this->handlers[self::TYPE_EMAIL]->resolveHandlerData(['email' => $urn]);
124 $result['type'] = self::TYPE_EMAIL;
125 } else {
126 throw new Exception\UnknownUrnException('No valid URN to resolve found', 1457177667);
127 }
128
129 return $result;
130 }
131
132 /**
133 * Returns a string interpretation of the link target, something like
134 *
135 * - t3://page?uid=23&my=value#cool
136 * - https://www.typo3.org/
137 * - t3://file?uid=13
138 * - t3://folder?storage=2&identifier=/my/folder/
139 * - mailto:mac@safe.com
140 *
141 * @param array $parameters
142 * @return string
143 * @throws Exception\UnknownLinkHandlerException
144 */
145 public function asString(array $parameters): string
146 {
147 if (is_object($this->handlers[$parameters['type']])) {
148 return $this->handlers[$parameters['type']]->asString($parameters);
149 } elseif (isset($parameters['url']) && !empty($parameters['url'])) {
150 // This usually happens for tel: or other types where a URL is available and the
151 // legacy link service could resolve at least something
152 return $parameters['url'];
153 }
154 throw new Exception\UnknownLinkHandlerException('No valid handlers found for type: ' . $parameters['type'], 1460629247);
155 }
156 }