[!!!][TASK] Remove sys_domain and LegacyDomains
[Packages/TYPO3.CMS.git] / typo3 / sysext / linkvalidator / Classes / Linktype / InternalLinktype.php
1 <?php
2 namespace TYPO3\CMS\Linkvalidator\Linktype;
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\ConnectionPool;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19
20 /**
21 * This class provides Check Internal Links plugin implementation
22 */
23 class InternalLinktype extends AbstractLinktype
24 {
25 /**
26 * @var string
27 */
28 const DELETED = 'deleted';
29
30 /**
31 * @var string
32 */
33 const HIDDEN = 'hidden';
34
35 /**
36 * @var string
37 */
38 const MOVED = 'moved';
39
40 /**
41 * @var string
42 */
43 const NOTEXISTING = 'notExisting';
44
45 /**
46 * Result of the check, if the current page uid is valid or not
47 *
48 * @var bool
49 */
50 protected $responsePage = true;
51
52 /**
53 * Result of the check, if the current content uid is valid or not
54 *
55 * @var bool
56 */
57 protected $responseContent = true;
58
59 /**
60 * Checks a given URL + /path/filename.ext for validity
61 *
62 * @param string $url Url to check as page-id or page-id#anchor (if anchor is present)
63 * @param array $softRefEntry: The soft reference entry which builds the context of that url
64 * @param \TYPO3\CMS\Linkvalidator\LinkAnalyzer $reference Parent instance
65 * @return bool TRUE on success or FALSE on error
66 */
67 public function checkLink($url, $softRefEntry, $reference)
68 {
69 $anchor = '';
70 $this->responseContent = true;
71 // Might already contain values - empty it
72 unset($this->errorParams);
73 // Only check pages records. Content elements will also be checked
74 // as we extract the anchor in the next step.
75 [$table, $uid] = explode(':', $softRefEntry['substr']['recordRef']);
76 if (!in_array($table, ['pages', 'tt_content'], true)) {
77 return true;
78 }
79 // Defines the linked page and anchor (if any).
80 if (strpos($url, '#c') !== false) {
81 $parts = explode('#c', $url);
82 $page = $parts[0];
83 $anchor = $parts[1];
84 } elseif (
85 $table === 'tt_content'
86 && strpos($softRefEntry['row'][$softRefEntry['field']], 't3://') === 0
87 ) {
88 $parsedTypoLinkUrl = @parse_url($softRefEntry['row'][$softRefEntry['field']]);
89 if ($parsedTypoLinkUrl['host'] === 'page') {
90 parse_str($parsedTypoLinkUrl['query'], $query);
91 if (isset($query['uid'])) {
92 $page = (int)$query['uid'];
93 $anchor = (int)$url;
94 }
95 }
96 } else {
97 $page = $url;
98 }
99 // Check if the linked page is OK
100 $this->responsePage = $this->checkPage($page);
101 // Check if the linked content element is OK
102 if ($anchor) {
103 // Check if the content element is OK
104 $this->responseContent = $this->checkContent($page, $anchor);
105 }
106 if (
107 is_array($this->errorParams['page']) && !$this->responsePage
108 || is_array($this->errorParams['content']) && !$this->responseContent
109 ) {
110 $this->setErrorParams($this->errorParams);
111 }
112
113 return $this->responsePage && $this->responseContent;
114 }
115
116 /**
117 * Checks a given page uid for validity
118 *
119 * @param string $page Page uid to check
120 * @return bool TRUE on success or FALSE on error
121 */
122 protected function checkPage($page)
123 {
124 // Get page ID on which the content element in fact is located
125 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
126 $queryBuilder->getRestrictions()->removeAll();
127 $row = $queryBuilder
128 ->select('uid', 'title', 'deleted', 'hidden', 'starttime', 'endtime')
129 ->from('pages')
130 ->where(
131 $queryBuilder->expr()->eq(
132 'uid',
133 $queryBuilder->createNamedParameter($page, \PDO::PARAM_INT)
134 )
135 )
136 ->execute()
137 ->fetch();
138 $this->responsePage = true;
139 if ($row) {
140 if ($row['deleted'] == '1') {
141 $this->errorParams['errorType']['page'] = self::DELETED;
142 $this->errorParams['page']['title'] = $row['title'];
143 $this->errorParams['page']['uid'] = $row['uid'];
144 $this->responsePage = false;
145 } elseif ($row['hidden'] == '1'
146 || $GLOBALS['EXEC_TIME'] < (int)$row['starttime']
147 || $row['endtime'] && (int)$row['endtime'] < $GLOBALS['EXEC_TIME']
148 ) {
149 $this->errorParams['errorType']['page'] = self::HIDDEN;
150 $this->errorParams['page']['title'] = $row['title'];
151 $this->errorParams['page']['uid'] = $row['uid'];
152 $this->responsePage = false;
153 }
154 } else {
155 $this->errorParams['errorType']['page'] = self::NOTEXISTING;
156 $this->errorParams['page']['uid'] = (int)$page;
157 $this->responsePage = false;
158 }
159 return $this->responsePage;
160 }
161
162 /**
163 * Checks a given content uid for validity
164 *
165 * @param string $page Uid of the page to which the link is pointing
166 * @param string $anchor Uid of the content element to check
167 * @return bool TRUE on success or FALSE on error
168 */
169 protected function checkContent($page, $anchor)
170 {
171 // Get page ID on which the content element in fact is located
172 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
173 $queryBuilder->getRestrictions()->removeAll();
174 $row = $queryBuilder
175 ->select('uid', 'pid', 'header', 'deleted', 'hidden', 'starttime', 'endtime')
176 ->from('tt_content')
177 ->where(
178 $queryBuilder->expr()->eq(
179 'uid',
180 $queryBuilder->createNamedParameter($anchor, \PDO::PARAM_INT)
181 )
182 )
183 ->execute()
184 ->fetch();
185 $this->responseContent = true;
186 // this content element exists
187 if ($row) {
188 $page = (int)$page;
189 // page ID on which this CE is in fact located.
190 $correctPageID = (int)$row['pid'];
191 // Check if the element is on the linked page
192 // (The element might have been moved to another page)
193 if ($correctPageID !== $page) {
194 $this->errorParams['errorType']['content'] = self::MOVED;
195 $this->errorParams['content']['uid'] = (int)$anchor;
196 $this->errorParams['content']['wrongPage'] = $page;
197 $this->errorParams['content']['rightPage'] = $correctPageID;
198 $this->responseContent = false;
199 } else {
200 // The element is located on the page to which the link is pointing
201 if ($row['deleted'] == '1') {
202 $this->errorParams['errorType']['content'] = self::DELETED;
203 $this->errorParams['content']['title'] = $row['header'];
204 $this->errorParams['content']['uid'] = $row['uid'];
205 $this->responseContent = false;
206 } elseif ($row['hidden'] == '1' || $GLOBALS['EXEC_TIME'] < (int)$row['starttime'] || $row['endtime'] && (int)$row['endtime'] < $GLOBALS['EXEC_TIME']) {
207 $this->errorParams['errorType']['content'] = self::HIDDEN;
208 $this->errorParams['content']['title'] = $row['header'];
209 $this->errorParams['content']['uid'] = $row['uid'];
210 $this->responseContent = false;
211 }
212 }
213 } else {
214 // The content element does not exist
215 $this->errorParams['errorType']['content'] = self::NOTEXISTING;
216 $this->errorParams['content']['uid'] = (int)$anchor;
217 $this->responseContent = false;
218 }
219 return $this->responseContent;
220 }
221
222 /**
223 * Generates the localized error message from the error params saved from the parsing
224 *
225 * @param array $errorParams All parameters needed for the rendering of the error message
226 * @return string Validation error message
227 */
228 public function getErrorMessage($errorParams)
229 {
230 $lang = $this->getLanguageService();
231 $errorType = $errorParams['errorType'];
232 if (is_array($errorParams['page'])) {
233 switch ($errorType['page']) {
234 case self::DELETED:
235 $errorPage = str_replace(
236 [
237 '###title###',
238 '###uid###'
239 ],
240 [
241 $errorParams['page']['title'],
242 $errorParams['page']['uid']
243 ],
244 $lang->getLL('list.report.pagedeleted')
245 );
246 break;
247 case self::HIDDEN:
248 $errorPage = str_replace(
249 [
250 '###title###',
251 '###uid###'
252 ],
253 [
254 $errorParams['page']['title'],
255 $errorParams['page']['uid']
256 ],
257 $lang->getLL('list.report.pagenotvisible')
258 );
259 break;
260 default:
261 $errorPage = str_replace(
262 '###uid###',
263 $errorParams['page']['uid'],
264 $lang->getLL('list.report.pagenotexisting')
265 );
266 }
267 }
268 if (is_array($errorParams['content'])) {
269 switch ($errorType['content']) {
270 case self::DELETED:
271 $errorContent = str_replace(
272 [
273 '###title###',
274 '###uid###'
275 ],
276 [
277 $errorParams['content']['title'],
278 $errorParams['content']['uid']
279 ],
280 $lang->getLL('list.report.contentdeleted')
281 );
282 break;
283 case self::HIDDEN:
284 $errorContent = str_replace(
285 [
286 '###title###',
287 '###uid###'
288 ],
289 [
290 $errorParams['content']['title'],
291 $errorParams['content']['uid']
292 ],
293 $lang->getLL('list.report.contentnotvisible')
294 );
295 break;
296 case self::MOVED:
297 $errorContent = str_replace(
298 [
299 '###title###',
300 '###uid###',
301 '###wrongpage###',
302 '###rightpage###'
303 ],
304 [
305 $errorParams['content']['title'],
306 $errorParams['content']['uid'],
307 $errorParams['content']['wrongPage'],
308 $errorParams['content']['rightPage']
309 ],
310 $lang->getLL('list.report.contentmoved')
311 );
312 break;
313 default:
314 $errorContent = str_replace('###uid###', $errorParams['content']['uid'], $lang->getLL('list.report.contentnotexisting'));
315 }
316 }
317 if (isset($errorPage) && isset($errorContent)) {
318 $response = $errorPage . LF . $errorContent;
319 } elseif (isset($errorPage)) {
320 $response = $errorPage;
321 } elseif (isset($errorContent)) {
322 $response = $errorContent;
323 } else {
324 // This should not happen
325 $response = $lang->getLL('list.report.noinformation');
326 }
327 return $response;
328 }
329
330 /**
331 * Constructs a valid Url for browser output
332 *
333 * @param array $row Broken link record
334 * @return string Parsed broken url
335 */
336 public function getBrokenUrl($row)
337 {
338 $domain = rtrim(GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), '/');
339 return $domain . '/index.php?id=' . $row['url'];
340 }
341 }