[TASK] BACK_PATH DocumentTemplate
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Integrity / DatabaseIntegrityCheck.php
1 <?php
2 namespace TYPO3\CMS\Core\Integrity;
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\Backend\Utility\BackendUtility;
18
19 /**
20 * This class holds functions used by the TYPO3 backend to check the integrity of the database (The DBint module, 'lowlevel' extension)
21 *
22 * Depends on: Depends on \TYPO3\CMS\Core\Database\RelationHandler
23 *
24 * @todo Need to really extend this class when the tcemain library has been updated and the whole API is better defined. There are some known bugs in this library. Further it would be nice with a facility to not only analyze but also clean up!
25 * @see \TYPO3\CMS\Lowlevel\View\DatabaseIntegrityView::func_relations(), \TYPO3\CMS\Lowlevel\View\DatabaseIntegrityView::func_records()
26 */
27 class DatabaseIntegrityCheck {
28
29 /**
30 * @var bool If set, genTree() includes deleted pages. This is default.
31 */
32 public $genTree_includeDeleted = TRUE;
33
34 /**
35 * @var bool If set, genTree() includes versionized pages/records. This is default.
36 */
37 public $genTree_includeVersions = TRUE;
38
39 /**
40 * @var bool If set, genTree() includes records from pages.
41 */
42 public $genTree_includeRecords = FALSE;
43
44 /**
45 * @var string Extra where-clauses for the tree-selection
46 */
47 public $perms_clause = '';
48
49 /**
50 * @var array Will hold id/rec pairs from genTree()
51 */
52 public $page_idArray = array();
53
54 /**
55 * @var array
56 */
57 public $rec_idArray = array();
58
59 /**
60 * @var array
61 */
62 public $checkFileRefs = array();
63
64 /**
65 * @var array From the select-fields
66 */
67 public $checkSelectDBRefs = array();
68
69 /**
70 * @var array From the group-fields
71 */
72 public $checkGroupDBRefs = array();
73
74 /**
75 * @var array Statistics
76 */
77 public $recStats = array(
78 'allValid' => array(),
79 'published_versions' => array(),
80 'deleted' => array()
81 );
82
83 /**
84 * @var array
85 */
86 public $lRecords = array();
87
88 /**
89 * @var string
90 */
91 public $lostPagesList = '';
92
93 /**
94 * Generates a list of Page-uid's that corresponds to the tables in the tree.
95 * This list should ideally include all records in the pages-table.
96 *
97 * @param int $theID a pid (page-record id) from which to start making the tree
98 * @param string $depthData HTML-code (image-tags) used when this function calls itself recursively.
99 * @param bool $versions Internal variable, don't set from outside!
100 * @return void
101 */
102 public function genTree($theID, $depthData = '', $versions = FALSE) {
103 if ($versions) {
104 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,title,doktype,deleted,t3ver_wsid,t3ver_id,t3ver_count,hidden', 'pages', 'pid=-1 AND t3ver_oid=' . (int)$theID . ' ' . (!$this->genTree_includeDeleted ? 'AND deleted=0' : '') . $this->perms_clause, '', 'sorting');
105 } else {
106 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,title,doktype,deleted,hidden', 'pages', 'pid=' . (int)$theID . ' ' . (!$this->genTree_includeDeleted ? 'AND deleted=0' : '') . $this->perms_clause, '', 'sorting');
107 }
108 // Traverse the records selected:
109 $a = 0;
110 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
111 $a++;
112 $newID = $row['uid'];
113 // Register various data for this item:
114 $this->page_idArray[$newID] = $row;
115 $this->recStats['all_valid']['pages'][$newID] = $newID;
116 if ($row['deleted']) {
117 $this->recStats['deleted']['pages'][$newID] = $newID;
118 }
119 if ($versions && $row['t3ver_count'] >= 1) {
120 $this->recStats['published_versions']['pages'][$newID] = $newID;
121 }
122 if ($row['deleted']) {
123 $this->recStats['deleted']++;
124 }
125 if ($row['hidden']) {
126 $this->recStats['hidden']++;
127 }
128 $this->recStats['doktype'][$row['doktype']]++;
129 // If all records should be shown, do so:
130 if ($this->genTree_includeRecords) {
131 foreach ($GLOBALS['TCA'] as $tableName => $cfg) {
132 if ($tableName != 'pages') {
133 $this->genTree_records($newID, '', $tableName);
134 }
135 }
136 }
137 // Add sub pages:
138 $this->genTree($newID);
139 // If versions are included in the tree, add those now:
140 if ($this->genTree_includeVersions) {
141 $this->genTree($newID, '', TRUE);
142 }
143 }
144 $GLOBALS['TYPO3_DB']->sql_free_result($res);
145 }
146
147 /**
148 * @param int $theID a pid (page-record id) from which to start making the tree
149 * @param string $_ Unused parameter
150 * @param string $table Table to get the records from
151 * @param bool $versions Internal variable, don't set from outside!
152 * @return void
153 */
154 public function genTree_records($theID, $_ = '', $table = '', $versions = FALSE) {
155 if ($versions) {
156 // Select all records from table pointing to this page:
157 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
158 BackendUtility::getCommonSelectFields($table),
159 $table,
160 'pid=-1 AND t3ver_oid=' . (int)$theID . (!$this->genTree_includeDeleted ? BackendUtility::deleteClause($table) : '')
161 );
162 } else {
163 // Select all records from table pointing to this page:
164 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
165 BackendUtility::getCommonSelectFields($table),
166 $table,
167 'pid=' . (int)$theID . (!$this->genTree_includeDeleted ? BackendUtility::deleteClause($table) : '')
168 );
169 }
170 // Traverse selected:
171 $a = 0;
172 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
173 $a++;
174 $newID = $row['uid'];
175 // Register various data for this item:
176 $this->rec_idArray[$table][$newID] = $row;
177 $this->recStats['all_valid'][$table][$newID] = $newID;
178 if ($row['deleted']) {
179 $this->recStats['deleted'][$table][$newID] = $newID;
180 }
181 if ($versions && $row['t3ver_count'] >= 1 && $row['t3ver_wsid'] == 0) {
182 $this->recStats['published_versions'][$table][$newID] = $newID;
183 }
184 // Select all versions of this record:
185 if ($this->genTree_includeVersions && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
186 $this->genTree_records($newID, '', $table, TRUE);
187 }
188 }
189 $GLOBALS['TYPO3_DB']->sql_free_result($res);
190 }
191
192 /**
193 * Fills $this->lRecords with the records from all tc-tables that are not attached to a PID in the pid-list.
194 *
195 * @param string $pid_list list of pid's (page-record uid's). This list is probably made by genTree()
196 * @return void
197 */
198 public function lostRecords($pid_list) {
199 $this->lostPagesList = '';
200 if ($pid_list) {
201 foreach ($GLOBALS['TCA'] as $table => $tableConf) {
202 $pid_list_tmp = $pid_list;
203 if (!isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) || !$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
204 // Remove preceding "-1," for non-versioned tables
205 $pid_list_tmp = preg_replace('/^\\-1,/', '', $pid_list_tmp);
206 }
207 $garbage = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid,' . $GLOBALS['TCA'][$table]['ctrl']['label'], $table, 'pid NOT IN (' . $pid_list_tmp . ')');
208 $lostIdList = array();
209 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($garbage)) {
210 $this->lRecords[$table][$row['uid']] = array(
211 'uid' => $row['uid'],
212 'pid' => $row['pid'],
213 'title' => strip_tags(BackendUtility::getRecordTitle($table, $row))
214 );
215 $lostIdList[] = $row['uid'];
216 }
217 $GLOBALS['TYPO3_DB']->sql_free_result($garbage);
218 if ($table == 'pages') {
219 $this->lostPagesList = implode(',', $lostIdList);
220 }
221 }
222 }
223 }
224
225 /**
226 * Fixes lost record from $table with uid $uid by setting the PID to zero.
227 * If there is a disabled column for the record that will be set as well.
228 *
229 * @param string $table Database tablename
230 * @param int $uid The uid of the record which will have the PID value set to 0 (zero)
231 * @return bool TRUE if done.
232 */
233 public function fixLostRecord($table, $uid) {
234 if ($table && $GLOBALS['TCA'][$table] && $uid && is_array($this->lRecords[$table][$uid]) && $GLOBALS['BE_USER']->user['admin']) {
235 $updateFields = array();
236 $updateFields['pid'] = 0;
237 // If possible a lost record restored is hidden as default
238 if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']) {
239 $updateFields[$GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']] = 1;
240 }
241 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . (int)$uid, $updateFields);
242 return TRUE;
243 } else {
244 return FALSE;
245 }
246 }
247
248 /**
249 * Counts records from $GLOBALS['TCA']-tables that ARE attached to an existing page.
250 *
251 * @param string $pid_list list of pid's (page-record uid's). This list is probably made by genTree()
252 * @return array an array with the number of records from all $GLOBALS['TCA']-tables that are attached to a PID in the pid-list.
253 */
254 public function countRecords($pid_list) {
255 $list = array();
256 $list_n = array();
257 if ($pid_list) {
258 foreach ($GLOBALS['TCA'] as $table => $tableConf) {
259 $pid_list_tmp = $pid_list;
260 if (!isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) || !$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
261 // Remove preceding "-1," for non-versioned tables
262 $pid_list_tmp = preg_replace('/^\\-1,/', '', $pid_list_tmp);
263 }
264 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', $table, 'pid IN (' . $pid_list_tmp . ')');
265 if ($count) {
266 $list[$table] = $count;
267 }
268 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', $table, 'pid IN (' . $pid_list_tmp . ')' . BackendUtility::deleteClause($table));
269 if ($count) {
270 $list_n[$table] = $count;
271 }
272 }
273 }
274 return array('all' => $list, 'non_deleted' => $list_n);
275 }
276
277 /**
278 * Finding relations in database based on type 'group' (files or database-uid's in a list)
279 *
280 * @param string $mode $mode = file, $mode = db, $mode = '' (all...)
281 * @return array An array with all fields listed that somehow are references to other records (foreign-keys) or files
282 */
283 public function getGroupFields($mode) {
284 $result = array();
285 foreach ($GLOBALS['TCA'] as $table => $tableConf) {
286 $cols = $GLOBALS['TCA'][$table]['columns'];
287 foreach ($cols as $field => $config) {
288 if ($config['config']['type'] == 'group') {
289 if ((!$mode || $mode == 'file') && $config['config']['internal_type'] == 'file' || (!$mode || $mode == 'db') && $config['config']['internal_type'] == 'db') {
290 $result[$table][] = $field;
291 }
292 }
293 if ((!$mode || $mode == 'db') && $config['config']['type'] == 'select' && $config['config']['foreign_table']) {
294 $result[$table][] = $field;
295 }
296 }
297 if ($result[$table]) {
298 $result[$table] = implode(',', $result[$table]);
299 }
300 }
301 return $result;
302 }
303
304 /**
305 * Finds all fields that hold filenames from uploadfolder
306 *
307 * @param string $uploadfolder Path to uploadfolder
308 * @return array An array with all fields listed that have references to files in the $uploadfolder
309 */
310 public function getFileFields($uploadfolder) {
311 $result = array();
312 foreach ($GLOBALS['TCA'] as $table => $tableConf) {
313 $cols = $GLOBALS['TCA'][$table]['columns'];
314 foreach ($cols as $field => $config) {
315 if ($config['config']['type'] == 'group' && $config['config']['internal_type'] == 'file' && $config['config']['uploadfolder'] == $uploadfolder) {
316 $result[] = array($table, $field);
317 }
318 }
319 }
320 return $result;
321 }
322
323 /**
324 * Returns an array with arrays of table/field pairs which are allowed to hold references to the input table name - according to $GLOBALS['TCA']
325 *
326 * @param string $theSearchTable Table name
327 * @return array
328 */
329 public function getDBFields($theSearchTable) {
330 $result = array();
331 foreach ($GLOBALS['TCA'] as $table => $tableConf) {
332 $cols = $GLOBALS['TCA'][$table]['columns'];
333 foreach ($cols as $field => $config) {
334 if ($config['config']['type'] == 'group' && $config['config']['internal_type'] == 'db') {
335 if (trim($config['config']['allowed']) == '*' || strstr($config['config']['allowed'], $theSearchTable)) {
336 $result[] = array($table, $field);
337 }
338 } elseif ($config['config']['type'] == 'select' && $config['config']['foreign_table'] == $theSearchTable) {
339 $result[] = array($table, $field);
340 }
341 }
342 }
343 return $result;
344 }
345
346 /**
347 * This selects non-empty-records from the tables/fields in the fkey_array generated by getGroupFields()
348 *
349 * @param array $fkey_arrays Array with tables/fields generated by getGroupFields()
350 * @return void
351 * @see getGroupFields()
352 */
353 public function selectNonEmptyRecordsWithFkeys($fkey_arrays) {
354 if (is_array($fkey_arrays)) {
355 foreach ($fkey_arrays as $table => $field_list) {
356 if ($GLOBALS['TCA'][$table] && trim($field_list)) {
357 $fieldArr = explode(',', $field_list);
358 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('dbal')) {
359 $fields = $GLOBALS['TYPO3_DB']->admin_get_fields($table);
360 $field = array_shift($fieldArr);
361 $cl_fl = $GLOBALS['TYPO3_DB']->MetaType($fields[$field]['type'], $table) == 'I' || $GLOBALS['TYPO3_DB']->MetaType($fields[$field]['type'], $table) == 'N' || $GLOBALS['TYPO3_DB']->MetaType($fields[$field]['type'], $table) == 'R' ? $field . '<>0' : $field . '<>\'\'';
362 foreach ($fieldArr as $field) {
363 $cl_fl .= $GLOBALS['TYPO3_DB']->MetaType($fields[$field]['type'], $table) == 'I' || $GLOBALS['TYPO3_DB']->MetaType($fields[$field]['type'], $table) == 'N' || $GLOBALS['TYPO3_DB']->MetaType($fields[$field]['type'], $table) == 'R' ? ' OR ' . $field . '<>0' : ' OR ' . $field . '<>\'\'';
364 }
365 unset($fields);
366 } else {
367 $cl_fl = implode('<>\'\' OR ', $fieldArr) . '<>\'\'';
368 }
369 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,' . $field_list, $table, $cl_fl);
370 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
371 foreach ($fieldArr as $field) {
372 if (trim($row[$field])) {
373 $fieldConf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
374 if ($fieldConf['type'] == 'group') {
375 if ($fieldConf['internal_type'] == 'file') {
376 // Files...
377 if ($fieldConf['MM']) {
378 $tempArr = array();
379 /** @var $dbAnalysis \TYPO3\CMS\Core\Database\RelationHandler */
380 $dbAnalysis = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
381 $dbAnalysis->start('', 'files', $fieldConf['MM'], $row['uid']);
382 foreach ($dbAnalysis->itemArray as $somekey => $someval) {
383 if ($someval['id']) {
384 $tempArr[] = $someval['id'];
385 }
386 }
387 } else {
388 $tempArr = explode(',', trim($row[$field]));
389 }
390 foreach ($tempArr as $file) {
391 $file = trim($file);
392 if ($file) {
393 $this->checkFileRefs[$fieldConf['uploadfolder']][$file] += 1;
394 }
395 }
396 }
397 if ($fieldConf['internal_type'] == 'db') {
398 /** @var $dbAnalysis \TYPO3\CMS\Core\Database\RelationHandler */
399 $dbAnalysis = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
400 $dbAnalysis->start($row[$field], $fieldConf['allowed'], $fieldConf['MM'], $row['uid'], $table, $fieldConf);
401 foreach ($dbAnalysis->itemArray as $tempArr) {
402 $this->checkGroupDBRefs[$tempArr['table']][$tempArr['id']] += 1;
403 }
404 }
405 }
406 if ($fieldConf['type'] == 'select' && $fieldConf['foreign_table']) {
407 /** @var $dbAnalysis \TYPO3\CMS\Core\Database\RelationHandler */
408 $dbAnalysis = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
409 $dbAnalysis->start($row[$field], $fieldConf['foreign_table'], $fieldConf['MM'], $row['uid'], $table, $fieldConf);
410 foreach ($dbAnalysis->itemArray as $tempArr) {
411 if ($tempArr['id'] > 0) {
412 $this->checkGroupDBRefs[$fieldConf['foreign_table']][$tempArr['id']] += 1;
413 }
414 }
415 }
416 }
417 }
418 }
419 $GLOBALS['TYPO3_DB']->sql_free_result($mres);
420 }
421 }
422 }
423 }
424
425 /**
426 * Depends on selectNonEmpty.... to be executed first!!
427 *
428 * @return array Report over files; keys are "moreReferences", "noReferences", "noFile", "error
429 */
430 public function testFileRefs() {
431 $output = array();
432 // Handle direct references with upload folder setting (workaround)
433 $newCheckFileRefs = array();
434 foreach ($this->checkFileRefs as $folder => $files) {
435 // Only direct references without a folder setting
436 if ($folder !== '') {
437 $newCheckFileRefs[$folder] = $files;
438 continue;
439 }
440 foreach ($files as $file => $references) {
441 // Direct file references have often many references (removes occurrences in the moreReferences section of the result array)
442 if ($references > 1) {
443 $references = 1;
444 }
445 // The directory must be empty (prevents checking of the root directory)
446 $directory = dirname($file);
447 if ($directory !== '') {
448 $newCheckFileRefs[$directory][basename($file)] = $references;
449 }
450 }
451 }
452 $this->checkFileRefs = $newCheckFileRefs;
453 foreach ($this->checkFileRefs as $folder => $fileArr) {
454 $path = PATH_site . $folder;
455 if (@is_dir($path) && @is_readable($path)) {
456 $d = dir($path);
457 while ($entry = $d->read()) {
458 if (@is_file(($path . '/' . $entry))) {
459 if (isset($fileArr[$entry])) {
460 if ($fileArr[$entry] > 1) {
461 $temp = $this->whereIsFileReferenced($folder, $entry);
462 $tempList = '';
463 foreach ($temp as $inf) {
464 $tempList .= '[' . $inf['table'] . '][' . $inf['uid'] . '][' . $inf['field'] . '] (pid:' . $inf['pid'] . ') - ';
465 }
466 $output['moreReferences'][] = array($path, $entry, $fileArr[$entry], $tempList);
467 }
468 unset($fileArr[$entry]);
469 } else {
470 // Contains workaround for direct references
471 if (!strstr($entry, 'index.htm') && !preg_match(('/^' . preg_quote($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') . '/'), $folder)) {
472 $output['noReferences'][] = array($path, $entry);
473 }
474 }
475 }
476 }
477 $d->close();
478 $tempCounter = 0;
479 foreach ($fileArr as $file => $value) {
480 // Workaround for direct file references
481 if (preg_match('/^' . preg_quote($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') . '/', $folder)) {
482 $file = $folder . '/' . $file;
483 $folder = '';
484 $path = substr(PATH_site, 0, -1);
485 }
486 $temp = $this->whereIsFileReferenced($folder, $file);
487 $tempList = '';
488 foreach ($temp as $inf) {
489 $tempList .= '[' . $inf['table'] . '][' . $inf['uid'] . '][' . $inf['field'] . '] (pid:' . $inf['pid'] . ') - ';
490 }
491 $tempCounter++;
492 $output['noFile'][substr($path, -3) . '_' . substr($file, 0, 3) . '_' . $tempCounter] = array($path, $file, $tempList);
493 }
494 } else {
495 $output['error'][] = array($path);
496 }
497 }
498 return $output;
499 }
500
501 /**
502 * Depends on selectNonEmpty.... to be executed first!!
503 *
504 * @param array $theArray Table with key/value pairs being table names and arrays with uid numbers
505 * @return string HTML Error message
506 */
507 public function testDBRefs($theArray) {
508 $result = '';
509 foreach ($theArray as $table => $dbArr) {
510 if ($GLOBALS['TCA'][$table]) {
511 $idlist = array_keys($dbArr);
512 $theList = implode(',', $idlist);
513 if ($theList) {
514 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'uid IN (' . $theList . ')' . BackendUtility::deleteClause($table));
515 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
516 if (isset($dbArr[$row['uid']])) {
517 unset($dbArr[$row['uid']]);
518 } else {
519 $result .= 'Strange Error. ...<br />';
520 }
521 }
522 $GLOBALS['TYPO3_DB']->sql_free_result($mres);
523 foreach ($dbArr as $theId => $theC) {
524 $result .= 'There are ' . $theC . ' records pointing to this missing or deleted record; [' . $table . '][' . $theId . ']<br />';
525 }
526 }
527 } else {
528 $result .= 'Codeerror. Table is not a table...<br />';
529 }
530 }
531 return $result;
532 }
533
534 /**
535 * Finding all references to record based on table/uid
536 *
537 * @param string $searchTable Table name
538 * @param int $id Uid of database record
539 * @return array Array with other arrays containing information about where references was found
540 */
541 public function whereIsRecordReferenced($searchTable, $id) {
542 // Gets tables / Fields that reference to files
543 $fileFields = $this->getDBFields($searchTable);
544 $theRecordList = array();
545 foreach ($fileFields as $info) {
546 $table = $info[0];
547 $field = $info[1];
548 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid,' . $GLOBALS['TCA'][$table]['ctrl']['label'] . ',' . $field, $table, $field . ' LIKE \'%' . $GLOBALS['TYPO3_DB']->quoteStr($id, $table) . '%\'');
549 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
550 // Now this is the field, where the reference COULD come from. But we're not garanteed, so we must carefully examine the data.
551 $fieldConf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
552 $allowedTables = $fieldConf['type'] == 'group' ? $fieldConf['allowed'] : $fieldConf['foreign_table'];
553 /** @var $dbAnalysis \TYPO3\CMS\Core\Database\RelationHandler */
554 $dbAnalysis = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
555 $dbAnalysis->start($row[$field], $allowedTables, $fieldConf['MM'], $row['uid'], $table, $fieldConf);
556 foreach ($dbAnalysis->itemArray as $tempArr) {
557 if ($tempArr['table'] == $searchTable && $tempArr['id'] == $id) {
558 $theRecordList[] = array('table' => $table, 'uid' => $row['uid'], 'field' => $field, 'pid' => $row['pid']);
559 }
560 }
561 }
562 $GLOBALS['TYPO3_DB']->sql_free_result($mres);
563 }
564 return $theRecordList;
565 }
566
567 /**
568 * Finding all references to file based on uploadfolder / filename
569 *
570 * @param string $uploadfolder Upload folder where file is found
571 * @param string $filename Filename to search for
572 * @return array Array with other arrays containing information about where references was found
573 */
574 public function whereIsFileReferenced($uploadfolder, $filename) {
575 // Gets tables / Fields that reference to files
576 $fileFields = $this->getFileFields($uploadfolder);
577 $theRecordList = array();
578 foreach ($fileFields as $info) {
579 $table = $info[0];
580 $field = $info[1];
581 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid,' . $GLOBALS['TCA'][$table]['ctrl']['label'] . ',' . $field, $table, $field . ' LIKE \'%' . $GLOBALS['TYPO3_DB']->quoteStr($filename, $table) . '%\'');
582 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
583 // Now this is the field, where the reference COULD come from.
584 // But we're not guaranteed, so we must carefully examine the data.
585 $tempArr = explode(',', trim($row[$field]));
586 foreach ($tempArr as $file) {
587 $file = trim($file);
588 if ($file == $filename) {
589 $theRecordList[] = array('table' => $table, 'uid' => $row['uid'], 'field' => $field, 'pid' => $row['pid']);
590 }
591 }
592 }
593 $GLOBALS['TYPO3_DB']->sql_free_result($mres);
594 }
595 return $theRecordList;
596 }
597
598 }