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