[BUGFIX] Ensures configuration for resolveAnchorLink is an array
[Packages/TYPO3.CMS.git] / typo3 / sysext / linkvalidator / Classes / Linktype / LinkHandler.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\Database\Query\Restriction\DeletedRestriction;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * This class provides Check Link Handler plugin implementation
23 */
24 class LinkHandler extends AbstractLinktype
25 {
26 /**
27 * @var string
28 */
29 const DELETED = 'deleted';
30
31 /**
32 * @var string
33 */
34 const DISABLED = 'disabled';
35
36 /**
37 * Checks a given URL for validity
38 *
39 * @param string $url Url to check
40 * @param array $softRefEntry The soft reference entry which builds the context of that url
41 * @param \TYPO3\CMS\Linkvalidator\LinkAnalyzer $reference Parent instance
42 * @return bool TRUE on success or FALSE on error
43 */
44 public function checkLink($url, $softRefEntry, $reference)
45 {
46 $response = true;
47 $errorType = '';
48 $errorParams = [];
49 $parts = explode(':', $url);
50 if (count($parts) !== 3) {
51 return $response;
52 }
53
54 list(, $tableName, $rowid) = $parts;
55 $rowid = (int)$rowid;
56
57 $row = null;
58 $tsConfig = $reference->getTSConfig();
59 $reportHiddenRecords = (bool)$tsConfig['linkhandler.']['reportHiddenRecords'];
60
61 // First check, if we find a non disabled record if the check
62 // for hidden records is enabled.
63 if ($reportHiddenRecords) {
64 $row = $this->getRecordRow($tableName, $rowid, 'disabled');
65 if ($row === false) {
66 $response = false;
67 $errorType = self::DISABLED;
68 }
69 }
70
71 // If no enabled record was found or we did not check that see
72 // if we can find a non deleted record.
73 if ($row === null) {
74 $row = $this->getRecordRow($tableName, $rowid, 'deleted');
75 if ($row === false) {
76 $response = false;
77 $errorType = self::DELETED;
78 }
79 }
80
81 // If we did not find a non deleted record, check if we find a
82 // deleted one.
83 if ($row === null) {
84 $row = $this->getRecordRow($tableName, $rowid, 'all');
85 if ($row === false) {
86 $response = false;
87 $errorType = '';
88 }
89 }
90
91 if (!$response) {
92 $errorParams['errorType'] = $errorType;
93 $errorParams['tablename'] = $tableName;
94 $errorParams['uid'] = $rowid;
95 $this->setErrorParams($errorParams);
96 }
97
98 return $response;
99 }
100
101 /**
102 * Type fetching method, based on the type that softRefParserObj returns
103 *
104 * @param array $value Reference properties
105 * @param string $type Current type
106 * @param string $key Validator hook name
107 * @return string fetched type
108 */
109 public function fetchType($value, $type, $key)
110 {
111 if ($value['type'] === 'string' && strpos(strtolower($value['tokenValue']), 'record:') === 0) {
112 $type = 'linkhandler';
113 }
114 return $type;
115 }
116
117 /**
118 * Generate the localized error message from the error params saved from the parsing
119 *
120 * @param array $errorParams All parameters needed for the rendering of the error message
121 * @return string Validation error message
122 */
123 public function getErrorMessage($errorParams)
124 {
125 $errorType = $errorParams['errorType'];
126 $tableName = $errorParams['tablename'];
127 if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['title'])) {
128 $title = $this->getLanguageService()->sL($GLOBALS['TCA'][$tableName]['ctrl']['title']);
129 } else {
130 $title = $tableName;
131 }
132 switch ($errorType) {
133 case self::DISABLED:
134 $response = $this->getTranslatedErrorMessage('list.report.rownotvisible', $errorParams['uid'], $title);
135 break;
136 case self::DELETED:
137 $response = $this->getTranslatedErrorMessage('list.report.rowdeleted', $errorParams['uid'], $title);
138 break;
139 default:
140 $response = $this->getTranslatedErrorMessage('list.report.rownotexisting', $errorParams['uid']);
141 }
142 return $response;
143 }
144
145 /**
146 * Fetches the translation with the given key and replaces the ###uid### and ###title### markers
147 *
148 * @param string $translationKey
149 * @param int $uid
150 * @param string $title
151 * @return string
152 */
153 protected function getTranslatedErrorMessage($translationKey, $uid, $title = null)
154 {
155 $message = $this->getLanguageService()->getLL($translationKey);
156 $message = str_replace('###uid###', (int)$uid, $message);
157 if (isset($title)) {
158 $message = str_replace('###title###', htmlspecialchars($title), $message);
159 }
160 return $message;
161 }
162
163 /**
164 * Fetches the record with the given UID from the given table.
165 *
166 * The filter option accepts two values:
167 *
168 * "disabled" will filter out disabled and deleted records.
169 * "deleted" filters out deleted records but will return disabled records.
170 * If nothing is specified all records will be returned (including deleted).
171 *
172 * @param string $tableName The name of the table from which the record should be fetched.
173 * @param int $uid The UID of the record that should be fetched.
174 * @param string $filter A filter setting, can be empty or "disabled" or "deleted".
175 * @return array|bool The result row as associative array or false if nothing is found.
176 */
177 protected function getRecordRow($tableName, $uid, $filter = '')
178 {
179 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName);
180
181 switch ($filter) {
182 case self::DISABLED:
183 // All default restrictions for the QueryBuilder stay active
184 break;
185 case self::DELETED:
186 $queryBuilder->getRestrictions()
187 ->removeAll()
188 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
189 break;
190 default:
191 $queryBuilder->getRestrictions()->removeAll();
192 }
193
194 $row = $queryBuilder
195 ->select('*')
196 ->from($tableName)
197 ->where(
198 $queryBuilder->expr()->eq(
199 'uid',
200 $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
201 )
202 )
203 ->execute()
204 ->fetch();
205
206 return $row;
207 }
208 }