‪TYPO3CMS  ‪main
InternalLinktype.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
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 
19 
20 use TYPO3\CMS\Backend\Utility\BackendUtility;
25 
30 {
34  public const ‪DELETED = 'deleted';
35 
39  public const ‪HIDDEN = 'hidden';
40 
44  public const ‪MOVED = 'moved';
45 
49  public const ‪NOTEXISTING = 'notExisting';
50 
56  protected ‪$responsePage = true;
57 
63  protected ‪$responseContent = true;
64 
65  protected string ‪$identifier = 'db';
66 
75  public function ‪checkLink(string ‪$url, array $softRefEntry, ‪LinkAnalyzer $reference): bool
76  {
77  $page = null;
78  $anchor = '';
79  $this->responseContent = true;
80  // Might already contain values - empty it
81  unset($this->errorParams);
82  // Only check pages records. Content elements will also be checked
83  // as we extract the anchor in the next step.
84  [$table] = explode(':', $softRefEntry['substr']['recordRef']);
85  if (!in_array($table, ['pages', 'tt_content'], true)) {
86  return true;
87  }
88  // Defines the linked page and anchor (if any).
89  if (str_contains(‪$url, '#c')) {
90  $parts = explode('#c', ‪$url);
91  $page = $parts[0];
92  $anchor = $parts[1];
93  } elseif (
94  $table === 'tt_content'
95  && str_starts_with($softRefEntry['row'][$softRefEntry['field']], 't3://')
96  ) {
97  $parsedTypoLinkUrl = @parse_url($softRefEntry['row'][$softRefEntry['field']]);
98  if ($parsedTypoLinkUrl['host'] === 'page') {
99  parse_str($parsedTypoLinkUrl['query'], $query);
100  if (isset($query['uid'])) {
101  $page = (int)$query['uid'];
102  $anchor = (int)‪$url;
103  }
104  }
105  } else {
106  $page = ‪$url;
107  }
108  // Check if the linked page is OK
109  $this->responsePage = $this->‪checkPage((int)$page);
110  // Check if the linked content element is OK
111  if ($anchor) {
112  // Check if the content element is OK
113  $this->responseContent = $this->‪checkContent((int)$page, (int)$anchor);
114  }
115  if (
116  (is_array($this->errorParams['page'] ?? false) && !$this->responsePage)
117  || (is_array($this->errorParams['content'] ?? false) && !$this->responseContent)
118  ) {
119  $this->‪setErrorParams($this->errorParams);
120  }
121 
122  return $this->responsePage && ‪$this->responseContent;
123  }
124 
131  protected function ‪checkPage($page)
132  {
133  // Get page ID on which the content element in fact is located
134  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
135  $queryBuilder->getRestrictions()->removeAll();
136  $row = $queryBuilder
137  ->select('uid', 'title', 'deleted', 'hidden', 'starttime', 'endtime')
138  ->from('pages')
139  ->where(
140  $queryBuilder->expr()->eq(
141  'uid',
142  $queryBuilder->createNamedParameter($page, ‪Connection::PARAM_INT)
143  )
144  )
145  ->executeQuery()
146  ->fetchAssociative();
147  $this->responsePage = true;
148  if ($row) {
149  if ($row['deleted'] == '1') {
150  $this->errorParams['errorType']['page'] = ‪self::DELETED;
151  $this->errorParams['page']['title'] = $row['title'];
152  $this->errorParams['page']['uid'] = $row['uid'];
153  $this->responsePage = false;
154  } elseif ($row['hidden'] == '1'
155  || ‪$GLOBALS['EXEC_TIME'] < (int)$row['starttime']
156  || $row['endtime'] && (int)$row['endtime'] < ‪$GLOBALS['EXEC_TIME']
157  ) {
158  $this->errorParams['errorType']['page'] = ‪self::HIDDEN;
159  $this->errorParams['page']['title'] = $row['title'];
160  $this->errorParams['page']['uid'] = $row['uid'];
161  $this->responsePage = false;
162  }
163  } else {
164  $this->errorParams['errorType']['page'] = ‪self::NOTEXISTING;
165  $this->errorParams['page']['uid'] = (int)$page;
166  $this->responsePage = false;
167  }
168  return ‪$this->responsePage;
169  }
170 
178  protected function ‪checkContent($page, $anchor)
179  {
180  // Get page ID on which the content element in fact is located
181  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
182  $queryBuilder->getRestrictions()->removeAll();
183  $row = $queryBuilder
184  ->select('uid', 'pid', 'header', 'deleted', 'hidden', 'starttime', 'endtime')
185  ->from('tt_content')
186  ->where(
187  $queryBuilder->expr()->eq(
188  'uid',
189  $queryBuilder->createNamedParameter($anchor, ‪Connection::PARAM_INT)
190  )
191  )
192  ->executeQuery()
193  ->fetchAssociative();
194  $this->responseContent = true;
195  // this content element exists
196  if ($row) {
197  $page = (int)$page;
198  // page ID on which this CE is in fact located.
199  $correctPageID = (int)$row['pid'];
200  // Check if the element is on the linked page
201  // (The element might have been moved to another page)
202  if ($correctPageID !== $page) {
203  $this->errorParams['errorType']['content'] = ‪self::MOVED;
204  $this->errorParams['content']['title'] = BackendUtility::getRecordTitle('tt_content', $row);
205  $this->errorParams['content']['uid'] = $row['uid'];
206  $this->errorParams['content']['wrongPage'] = $page;
207  $this->errorParams['content']['rightPage'] = $correctPageID;
208  $this->responseContent = false;
209  } else {
210  // The element is located on the page to which the link is pointing
211  if ($row['deleted'] == '1') {
212  $this->errorParams['errorType']['content'] = ‪self::DELETED;
213  $this->errorParams['content']['title'] = BackendUtility::getRecordTitle('tt_content', $row);
214  $this->errorParams['content']['uid'] = $row['uid'];
215  $this->responseContent = false;
216  } elseif ($row['hidden'] == '1' || ‪$GLOBALS['EXEC_TIME'] < (int)$row['starttime'] || $row['endtime'] && (int)$row['endtime'] < ‪$GLOBALS['EXEC_TIME']) {
217  $this->errorParams['errorType']['content'] = ‪self::HIDDEN;
218  $this->errorParams['content']['title'] = BackendUtility::getRecordTitle('tt_content', $row);
219  $this->errorParams['content']['uid'] = $row['uid'];
220  $this->responseContent = false;
221  }
222  }
223  } else {
224  // The content element does not exist
225  $this->errorParams['errorType']['content'] = ‪self::NOTEXISTING;
226  $this->errorParams['content']['uid'] = (int)$anchor;
227  $this->responseContent = false;
228  }
230  }
231 
238  public function ‪getErrorMessage(array ‪$errorParams): string
239  {
240  $lang = $this->‪getLanguageService();
241  $errorType = ‪$errorParams['errorType'] ?? '';
242  if ($errorType === '') {
243  return $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.report.noinformation');
244  }
245 
246  $errorPage = null;
247  $errorContent = null;
248  if (is_array(‪$errorParams['page'] ?? false)) {
249  switch ($errorType['page']) {
250  case ‪self::DELETED:
251  $errorPage = str_replace(
252  [
253  '###title###',
254  '###uid###',
255  ],
256  [
257  ‪$errorParams['page']['title'],
258  (string)‪$errorParams['page']['uid'],
259  ],
260  $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.report.pagedeleted')
261  );
262  break;
263  case ‪self::HIDDEN:
264  $errorPage = str_replace(
265  [
266  '###title###',
267  '###uid###',
268  ],
269  [
270  ‪$errorParams['page']['title'],
271  (string)‪$errorParams['page']['uid'],
272  ],
273  $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.report.pagenotvisible')
274  );
275  break;
276  default:
277  $errorPage = str_replace(
278  '###uid###',
279  (string)‪$errorParams['page']['uid'],
280  $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.report.pagenotexisting')
281  );
282  }
283  }
284  if (is_array(‪$errorParams['content'] ?? false)) {
285  switch ($errorType['content']) {
286  case ‪self::DELETED:
287  $errorContent = str_replace(
288  [
289  '###title###',
290  '###uid###',
291  ],
292  [
293  ‪$errorParams['content']['title'],
294  (string)‪$errorParams['content']['uid'],
295  ],
296  $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.report.contentdeleted')
297  );
298  break;
299  case ‪self::HIDDEN:
300  $errorContent = str_replace(
301  [
302  '###title###',
303  '###uid###',
304  ],
305  [
306  ‪$errorParams['content']['title'],
307  (string)‪$errorParams['content']['uid'],
308  ],
309  $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.report.contentnotvisible')
310  );
311  break;
312  case ‪self::MOVED:
313  $errorContent = str_replace(
314  [
315  '###title###',
316  '###uid###',
317  '###wrongpage###',
318  '###rightpage###',
319  ],
320  [
321  ‪$errorParams['content']['title'],
322  (string)‪$errorParams['content']['uid'],
323  (string)‪$errorParams['content']['wrongPage'],
324  (string)‪$errorParams['content']['rightPage'],
325  ],
326  $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.report.contentmoved')
327  );
328  break;
329  default:
330  $errorContent = str_replace('###uid###', (string)‪$errorParams['content']['uid'], $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.report.contentnotexisting'));
331  }
332  }
333  if (isset($errorPage) && isset($errorContent)) {
334  $response = $errorPage . LF . $errorContent;
335  } elseif (isset($errorPage)) {
336  $response = $errorPage;
337  } elseif (isset($errorContent)) {
338  $response = $errorContent;
339  } else {
340  // This should not happen
341  $response = $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.report.noinformation');
342  }
343  return $response;
344  }
345 
352  public function ‪getBrokenUrl(array $row): string
353  {
354  $domain = rtrim(‪$GLOBALS['TYPO3_REQUEST']->getAttribute('normalizedParams')->getSiteUrl(), '/');
355  return $domain . '/index.php?id=' . $row['url'];
356  }
357 }
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Linkvalidator\Linktype\AbstractLinktype\$errorParams
‪array $errorParams
Definition: AbstractLinktype.php:32
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\checkLink
‪bool checkLink(string $url, array $softRefEntry, LinkAnalyzer $reference)
Definition: InternalLinktype.php:73
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\$identifier
‪string $identifier
Definition: InternalLinktype.php:63
‪TYPO3\CMS\Linkvalidator\Linktype\AbstractLinktype
Definition: AbstractLinktype.php:26
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\MOVED
‪const MOVED
Definition: InternalLinktype.php:44
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\getErrorMessage
‪string getErrorMessage(array $errorParams)
Definition: InternalLinktype.php:236
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\$responseContent
‪bool $responseContent
Definition: InternalLinktype.php:61
‪TYPO3\CMS\Linkvalidator\Linktype
Definition: AbstractLinktype.php:18
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype
Definition: InternalLinktype.php:30
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\getBrokenUrl
‪string getBrokenUrl(array $row)
Definition: InternalLinktype.php:350
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\DELETED
‪const DELETED
Definition: InternalLinktype.php:34
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\checkPage
‪bool checkPage($page)
Definition: InternalLinktype.php:129
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\HIDDEN
‪const HIDDEN
Definition: InternalLinktype.php:39
‪TYPO3\CMS\Linkvalidator\Linktype\AbstractLinktype\setErrorParams
‪setErrorParams($value)
Definition: AbstractLinktype.php:71
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\checkContent
‪bool checkContent($page, $anchor)
Definition: InternalLinktype.php:176
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\NOTEXISTING
‪const NOTEXISTING
Definition: InternalLinktype.php:49
‪TYPO3\CMS\Linkvalidator\Linktype\InternalLinktype\$responsePage
‪bool $responsePage
Definition: InternalLinktype.php:55
‪TYPO3\CMS\Linkvalidator\Linktype\AbstractLinktype\getLanguageService
‪getLanguageService()
Definition: AbstractLinktype.php:97