[TASK] Remove comments for revised in TYPO3 3.6
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Integrity / DatabaseIntegrityCheck.php
1 <?php
2 namespace TYPO3\CMS\Core\Integrity;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 1999-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 /**
31 * This class holds functions used by the TYPO3 backend to check the integrity of the database (The DBint module, 'lowlevel' extension)
32 *
33 * Depends on: Depends on \TYPO3\CMS\Core\Database\RelationHandler
34 *
35 * @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!
36 * @see SC_mod_tools_dbint_index::func_relations(), SC_mod_tools_dbint_index::func_records()
37 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
38 */
39 class DatabaseIntegrityCheck {
40
41 /**
42 * @var boolean If set, genTree() includes deleted pages. This is default.
43 * @todo Define visibility
44 */
45 public $genTree_includeDeleted = TRUE;
46
47 /**
48 * @var boolean If set, genTree() includes versionized pages/records. This is default.
49 * @todo Define visibility
50 */
51 public $genTree_includeVersions = TRUE;
52
53 /**
54 * @var boolean If set, genTree() includes records from pages.
55 * @todo Define visibility
56 */
57 public $genTree_includeRecords = FALSE;
58
59 /**
60 * @var string Extra where-clauses for the tree-selection
61 * @todo Define visibility
62 */
63 public $perms_clause = '';
64
65 /**
66 * @var int If set, genTree() generates HTML, that visualizes the tree.
67 * @todo Define visibility
68 */
69 public $genTree_makeHTML = 0;
70
71 // Internal
72 /**
73 * @var array Will hold id/rec pairs from genTree()
74 * @todo Define visibility
75 */
76 public $page_idArray = array();
77
78 /**
79 * @var array
80 * @todo Define visibility
81 */
82 public $rec_idArray = array();
83
84 /**
85 * @var string Will hold the HTML-code visualising the tree. genTree()
86 * @todo Define visibility
87 */
88 public $genTree_HTML = '';
89
90 /**
91 * @var string
92 * @todo Define visibility
93 */
94 public $backPath = '';
95
96 // Internal
97 /**
98 * @var array
99 * @todo Define visibility
100 */
101 public $checkFileRefs = array();
102
103 /**
104 * @var array From the select-fields
105 * @todo Define visibility
106 */
107 public $checkSelectDBRefs = array();
108
109 /**
110 * @var array From the group-fields
111 * @todo Define visibility
112 */
113 public $checkGroupDBRefs = array();
114
115 /**
116 * @var array Statistics
117 * @todo Define visibility
118 */
119 public $recStats = array(
120 'allValid' => array(),
121 'published_versions' => array(),
122 'deleted' => array()
123 );
124
125 /**
126 * @var array
127 * @todo Define visibility
128 */
129 public $lRecords = array();
130
131 /**
132 * @var string
133 * @todo Define visibility
134 */
135 public $lostPagesList = '';
136
137 /**
138 * Generates a list of Page-uid's that corresponds to the tables in the tree.
139 * This list should ideally include all records in the pages-table.
140 *
141 * @param integer $theID a pid (page-record id) from which to start making the tree
142 * @param string $depthData HTML-code (image-tags) used when this function calls itself recursively.
143 * @param boolean $versions Internal variable, don't set from outside!
144 * @return void
145 * @todo Define visibility
146 */
147 public function genTree($theID, $depthData, $versions = FALSE) {
148 if ($versions) {
149 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,title,doktype,deleted,t3ver_wsid,t3ver_id,t3ver_count' . (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('cms') ? ',hidden' : ''), 'pages', 'pid=-1 AND t3ver_oid=' . intval($theID) . ' ' . (!$this->genTree_includeDeleted ? 'AND deleted=0' : '') . $this->perms_clause, '', 'sorting');
150 } else {
151 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,title,doktype,deleted' . (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('cms') ? ',hidden' : ''), 'pages', 'pid=' . intval($theID) . ' ' . (!$this->genTree_includeDeleted ? 'AND deleted=0' : '') . $this->perms_clause, '', 'sorting');
152 }
153 // Traverse the records selected:
154 $a = 0;
155 $c = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
156 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
157 // Prepare the additional label used in the HTML output in case of versions:
158 if ($versions) {
159 $versionLabel = '[v1.' . $row['t3ver_id'] . '; WS#' . $row['t3ver_wsid'] . ']';
160 } else {
161 $versionLabel = '';
162 }
163 $a++;
164 $newID = $row['uid'];
165 // Build HTML output:
166 if ($this->genTree_makeHTML) {
167 $this->genTree_HTML .= LF . '<div><span class="nobr">';
168 $PM = 'join';
169 $LN = $a == $c ? 'blank' : 'line';
170 $BTM = $a == $c ? 'bottom' : '';
171 $this->genTree_HTML .= $depthData . '<img' . \TYPO3\CMS\Backend\Utility\IconUtility::skinImg($this->backPath, ('gfx/ol/' . $PM . $BTM . '.gif'), 'width="18" height="16"') . ' align="top" alt="" />' . $versionLabel . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconForRecord('pages', $row) . htmlspecialchars(($row['uid'] . ': ' . \TYPO3\CMS\Core\Utility\GeneralUtility::fixed_lgd_cs(strip_tags($row['title']), 50))) . '</span></div>';
172 }
173 // Register various data for this item:
174 $this->page_idArray[$newID] = $row;
175 $this->recStats['all_valid']['pages'][$newID] = $newID;
176 if ($row['deleted']) {
177 $this->recStats['deleted']['pages'][$newID] = $newID;
178 }
179 if ($versions && $row['t3ver_count'] >= 1) {
180 $this->recStats['published_versions']['pages'][$newID] = $newID;
181 }
182 if ($row['deleted']) {
183 $this->recStats['deleted']++;
184 }
185 if ($row['hidden']) {
186 $this->recStats['hidden']++;
187 }
188 $this->recStats['doktype'][$row['doktype']]++;
189 // Create the HTML code prefix for recursive call:
190 $genHTML = $depthData . '<img' . \TYPO3\CMS\Backend\Utility\IconUtility::skinImg($this->backPath, ('gfx/ol/' . $LN . '.gif'), 'width="18" height="16"') . ' align="top" alt="" />' . $versionLabel;
191 // If all records should be shown, do so:
192 if ($this->genTree_includeRecords) {
193 foreach ($GLOBALS['TCA'] as $tableName => $cfg) {
194 if ($tableName != 'pages') {
195 $this->genTree_records($newID, $this->genTree_HTML ? $genHTML : '', $tableName);
196 }
197 }
198 }
199 // Add sub pages:
200 $this->genTree($newID, $this->genTree_HTML ? $genHTML : '');
201 // If versions are included in the tree, add those now:
202 if ($this->genTree_includeVersions) {
203 $this->genTree($newID, $this->genTree_HTML ? $genHTML : '', TRUE);
204 }
205 }
206 $GLOBALS['TYPO3_DB']->sql_free_result($res);
207 }
208
209 /**
210 * @param integer $theID a pid (page-record id) from which to start making the tree
211 * @param string $depthData HTML-code used when this function calls itself recursively.
212 * @param string $table Table to get the records from
213 * @param boolean $versions Internal variable, don't set from outside!
214 * @return void
215 * @todo Define visibility
216 */
217 public function genTree_records($theID, $depthData, $table = '', $versions = FALSE) {
218 if ($versions) {
219 // Select all records from table pointing to this page:
220 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(\TYPO3\CMS\Backend\Utility\BackendUtility::getCommonSelectFields($table), $table, 'pid=-1 AND t3ver_oid=' . intval($theID) . (!$this->genTree_includeDeleted ? \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table) : ''));
221 } else {
222 // Select all records from table pointing to this page:
223 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(\TYPO3\CMS\Backend\Utility\BackendUtility::getCommonSelectFields($table), $table, 'pid=' . intval($theID) . (!$this->genTree_includeDeleted ? \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table) : ''));
224 }
225 // Traverse selected:
226 $a = 0;
227 $c = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
228 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
229 // Prepare the additional label used in the HTML output in case of versions:
230 if ($versions) {
231 $versionLabel = '[v1.' . $row['t3ver_id'] . '; WS#' . $row['t3ver_wsid'] . ']';
232 } else {
233 $versionLabel = '';
234 }
235 $a++;
236 $newID = $row['uid'];
237 // Build HTML output:
238 if ($this->genTree_makeHTML) {
239 $this->genTree_HTML .= LF . '<div><span class="nobr">';
240 $PM = 'join';
241 $LN = $a == $c ? 'blank' : 'line';
242 $BTM = $a == $c ? 'bottom' : '';
243 $this->genTree_HTML .= $depthData . '<img' . \TYPO3\CMS\Backend\Utility\IconUtility::skinImg($this->backPath, ('gfx/ol/' . $PM . $BTM . '.gif'), 'width="18" height="16"') . ' align="top" alt="" />' . $versionLabel . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconForRecord($table, $row, array('title' => $table)) . htmlspecialchars(($row['uid'] . ': ' . \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle($table, $row))) . '</span></div>';
244 }
245 // Register various data for this item:
246 $this->rec_idArray[$table][$newID] = $row;
247 $this->recStats['all_valid'][$table][$newID] = $newID;
248 if ($row['deleted']) {
249 $this->recStats['deleted'][$table][$newID] = $newID;
250 }
251 if ($versions && $row['t3ver_count'] >= 1 && $row['t3ver_wsid'] == 0) {
252 $this->recStats['published_versions'][$table][$newID] = $newID;
253 }
254 // Select all versions of this record:
255 if ($this->genTree_includeVersions && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
256 $genHTML = $depthData . '<img' . \TYPO3\CMS\Backend\Utility\IconUtility::skinImg($this->backPath, ('gfx/ol/' . $LN . '.gif'), 'width="18" height="16"') . ' align="top" alt="" />';
257 $this->genTree_records($newID, $genHTML, $table, TRUE);
258 }
259 }
260 $GLOBALS['TYPO3_DB']->sql_free_result($res);
261 }
262
263 /**
264 * Generates tree and returns statistics
265 *
266 * @param integer $root
267 * @return array Record statistics
268 * @deprecated and unused since 6.0, will be removed two versions later
269 * @todo Define visibility
270 */
271 public function genTreeStatus($root = 0) {
272 \TYPO3\CMS\Core\Utility\GeneralUtility::logDeprecatedFunction();
273 $this->genTree_includeDeleted = TRUE;
274 // if set, genTree() includes deleted pages. This is default.
275 $this->genTree_includeVersions = TRUE;
276 // if set, genTree() includes verisonized pages/records. This is default.
277 $this->genTree_includeRecords = TRUE;
278 // if set, genTree() includes records from pages.
279 $this->perms_clause = '';
280 // extra where-clauses for the tree-selection
281 $this->genTree_makeHTML = 0;
282 // if set, genTree() generates HTML, that visualizes the tree.
283 $this->genTree($root, '');
284 return $this->recStats;
285 }
286
287 /**
288 * Fills $this->lRecords with the records from all tc-tables that are not attached to a PID in the pid-list.
289 *
290 * @param string $pid_list list of pid's (page-record uid's). This list is probably made by genTree()
291 * @return void
292 * @todo Define visibility
293 */
294 public function lostRecords($pid_list) {
295 $this->lostPagesList = '';
296 if ($pid_list) {
297 foreach ($GLOBALS['TCA'] as $table => $tableConf) {
298 $pid_list_tmp = $pid_list;
299 if (!isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) || !$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
300 // Remove preceding "-1," for non-versioned tables
301 $pid_list_tmp = preg_replace('/^\\-1,/', '', $pid_list_tmp);
302 }
303 $garbage = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid,' . $GLOBALS['TCA'][$table]['ctrl']['label'], $table, 'pid NOT IN (' . $pid_list_tmp . ')');
304 $lostIdList = array();
305 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($garbage)) {
306 $this->lRecords[$table][$row['uid']] = array(
307 'uid' => $row['uid'],
308 'pid' => $row['pid'],
309 'title' => strip_tags(\TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle($table, $row))
310 );
311 $lostIdList[] = $row['uid'];
312 }
313 $GLOBALS['TYPO3_DB']->sql_free_result($garbage);
314 if ($table == 'pages') {
315 $this->lostPagesList = implode(',', $lostIdList);
316 }
317 }
318 }
319 }
320
321 /**
322 * Fixes lost record from $table with uid $uid by setting the PID to zero.
323 * If there is a disabled column for the record that will be set as well.
324 *
325 * @param string $table Database tablename
326 * @param integer $uid The uid of the record which will have the PID value set to 0 (zero)
327 * @return boolean TRUE if done.
328 * @todo Define visibility
329 */
330 public function fixLostRecord($table, $uid) {
331 if ($table && $GLOBALS['TCA'][$table] && $uid && is_array($this->lRecords[$table][$uid]) && $GLOBALS['BE_USER']->user['admin']) {
332 $updateFields = array();
333 $updateFields['pid'] = 0;
334 // If possible a lost record restored is hidden as default
335 if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']) {
336 $updateFields[$GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']] = 1;
337 }
338 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($uid), $updateFields);
339 return TRUE;
340 } else {
341 return FALSE;
342 }
343 }
344
345 /**
346 * Counts records from $GLOBALS['TCA']-tables that ARE attached to an existing page.
347 *
348 * @param string $pid_list list of pid's (page-record uid's). This list is probably made by genTree()
349 * @return array an array with the number of records from all $GLOBALS['TCA']-tables that are attached to a PID in the pid-list.
350 * @todo Define visibility
351 */
352 public function countRecords($pid_list) {
353 $list = array();
354 $list_n = array();
355 if ($pid_list) {
356 foreach ($GLOBALS['TCA'] as $table => $tableConf) {
357 $pid_list_tmp = $pid_list;
358 if (!isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) || !$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
359 // Remove preceding "-1," for non-versioned tables
360 $pid_list_tmp = preg_replace('/^\\-1,/', '', $pid_list_tmp);
361 }
362 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', $table, 'pid IN (' . $pid_list_tmp . ')');
363 if ($count) {
364 $list[$table] = $count;
365 }
366 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', $table, 'pid IN (' . $pid_list_tmp . ')' . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table));
367 if ($count) {
368 $list_n[$table] = $count;
369 }
370 }
371 }
372 return array('all' => $list, 'non_deleted' => $list_n);
373 }
374
375 /**
376 * Finding relations in database based on type 'group' (files or database-uid's in a list)
377 *
378 * @param string $mode $mode = file, $mode = db, $mode = '' (all...)
379 * @return array An array with all fields listed that somehow are references to other records (foreign-keys) or files
380 * @todo Define visibility
381 */
382 public function getGroupFields($mode) {
383 $result = array();
384 foreach ($GLOBALS['TCA'] as $table => $tableConf) {
385 $cols = $GLOBALS['TCA'][$table]['columns'];
386 foreach ($cols as $field => $config) {
387 if ($config['config']['type'] == 'group') {
388 if ((!$mode || $mode == 'file') && $config['config']['internal_type'] == 'file' || (!$mode || $mode == 'db') && $config['config']['internal_type'] == 'db') {
389 $result[$table][] = $field;
390 }
391 }
392 if ((!$mode || $mode == 'db') && $config['config']['type'] == 'select' && $config['config']['foreign_table']) {
393 $result[$table][] = $field;
394 }
395 }
396 if ($result[$table]) {
397 $result[$table] = implode(',', $result[$table]);
398 }
399 }
400 return $result;
401 }
402
403 /**
404 * Finds all fields that hold filenames from uploadfolder
405 *
406 * @param string $uploadfolder Path to uploadfolder
407 * @return array An array with all fields listed that have references to files in the $uploadfolder
408 * @todo Define visibility
409 */
410 public function getFileFields($uploadfolder) {
411 $result = array();
412 foreach ($GLOBALS['TCA'] as $table => $tableConf) {
413 $cols = $GLOBALS['TCA'][$table]['columns'];
414 foreach ($cols as $field => $config) {
415 if ($config['config']['type'] == 'group' && $config['config']['internal_type'] == 'file' && $config['config']['uploadfolder'] == $uploadfolder) {
416 $result[] = array($table, $field);
417 }
418 }
419 }
420 return $result;
421 }
422
423 /**
424 * Returns an array with arrays of table/field pairs which are allowed to hold references to the input table name - according to $GLOBALS['TCA']
425 *
426 * @param string $theSearchTable Table name
427 * @return array
428 * @todo Define visibility
429 */
430 public function getDBFields($theSearchTable) {
431 $result = array();
432 foreach ($GLOBALS['TCA'] as $table => $tableConf) {
433 $cols = $GLOBALS['TCA'][$table]['columns'];
434 foreach ($cols as $field => $config) {
435 if ($config['config']['type'] == 'group' && $config['config']['internal_type'] == 'db') {
436 if (trim($config['config']['allowed']) == '*' || strstr($config['config']['allowed'], $theSearchTable)) {
437 $result[] = array($table, $field);
438 }
439 } elseif ($config['config']['type'] == 'select' && $config['config']['foreign_table'] == $theSearchTable) {
440 $result[] = array($table, $field);
441 }
442 }
443 }
444 return $result;
445 }
446
447 /**
448 * This selects non-empty-records from the tables/fields in the fkey_array generated by getGroupFields()
449 *
450 * @param array $fkey_arrays Array with tables/fields generated by getGroupFields()
451 * @return void
452 * @see getGroupFields()
453 * @todo Define visibility
454 */
455 public function selectNonEmptyRecordsWithFkeys($fkey_arrays) {
456 if (is_array($fkey_arrays)) {
457 foreach ($fkey_arrays as $table => $field_list) {
458 if ($GLOBALS['TCA'][$table] && trim($field_list)) {
459 $fieldArr = explode(',', $field_list);
460 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('dbal')) {
461 $fields = $GLOBALS['TYPO3_DB']->admin_get_fields($table);
462 $field = array_shift($fieldArr);
463 $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 . '<>\'\'';
464 foreach ($fieldArr as $field) {
465 $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 . '<>\'\'';
466 }
467 unset($fields);
468 } else {
469 $cl_fl = implode('<>\'\' OR ', $fieldArr) . '<>\'\'';
470 }
471 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,' . $field_list, $table, $cl_fl);
472 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
473 foreach ($fieldArr as $field) {
474 if (trim($row[$field])) {
475 $fieldConf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
476 if ($fieldConf['type'] == 'group') {
477 if ($fieldConf['internal_type'] == 'file') {
478 // Files...
479 if ($fieldConf['MM']) {
480 $tempArr = array();
481 /** @var $dbAnalysis \TYPO3\CMS\Core\Database\RelationHandler */
482 $dbAnalysis = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
483 $dbAnalysis->start('', 'files', $fieldConf['MM'], $row['uid']);
484 foreach ($dbAnalysis->itemArray as $somekey => $someval) {
485 if ($someval['id']) {
486 $tempArr[] = $someval['id'];
487 }
488 }
489 } else {
490 $tempArr = explode(',', trim($row[$field]));
491 }
492 foreach ($tempArr as $file) {
493 $file = trim($file);
494 if ($file) {
495 $this->checkFileRefs[$fieldConf['uploadfolder']][$file] += 1;
496 }
497 }
498 }
499 if ($fieldConf['internal_type'] == 'db') {
500 /** @var $dbAnalysis \TYPO3\CMS\Core\Database\RelationHandler */
501 $dbAnalysis = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
502 $dbAnalysis->start($row[$field], $fieldConf['allowed'], $fieldConf['MM'], $row['uid'], $table, $fieldConf);
503 foreach ($dbAnalysis->itemArray as $tempArr) {
504 $this->checkGroupDBRefs[$tempArr['table']][$tempArr['id']] += 1;
505 }
506 }
507 }
508 if ($fieldConf['type'] == 'select' && $fieldConf['foreign_table']) {
509 /** @var $dbAnalysis \TYPO3\CMS\Core\Database\RelationHandler */
510 $dbAnalysis = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
511 $dbAnalysis->start($row[$field], $fieldConf['foreign_table'], $fieldConf['MM'], $row['uid'], $table, $fieldConf);
512 foreach ($dbAnalysis->itemArray as $tempArr) {
513 if ($tempArr['id'] > 0) {
514 $this->checkGroupDBRefs[$fieldConf['foreign_table']][$tempArr['id']] += 1;
515 }
516 }
517 }
518 }
519 }
520 }
521 $GLOBALS['TYPO3_DB']->sql_free_result($mres);
522 }
523 }
524 }
525 }
526
527 /**
528 * Depends on selectNonEmpty.... to be executed first!!
529 *
530 * @return array Report over files; keys are "moreReferences", "noReferences", "noFile", "error
531 * @todo Define visibility
532 */
533 public function testFileRefs() {
534 $output = array();
535 // Handle direct references with upload folder setting (workaround)
536 $newCheckFileRefs = array();
537 foreach ($this->checkFileRefs as $folder => $files) {
538 // Only direct references without a folder setting
539 if ($folder !== '') {
540 $newCheckFileRefs[$folder] = $files;
541 continue;
542 }
543 foreach ($files as $file => $references) {
544 // Direct file references have often many references (removes occurences in the moreReferences section of the result array)
545 if ($references > 1) {
546 $references = 1;
547 }
548 // The directory must be empty (prevents checking of the root directory)
549 $directory = dirname($file);
550 if ($directory !== '') {
551 $newCheckFileRefs[$directory][basename($file)] = $references;
552 }
553 }
554 }
555 $this->checkFileRefs = $newCheckFileRefs;
556 foreach ($this->checkFileRefs as $folder => $fileArr) {
557 $path = PATH_site . $folder;
558 if (@is_dir($path)) {
559 $d = dir($path);
560 while ($entry = $d->read()) {
561 if (@is_file(($path . '/' . $entry))) {
562 if (isset($fileArr[$entry])) {
563 if ($fileArr[$entry] > 1) {
564 $temp = $this->whereIsFileReferenced($folder, $entry);
565 $tempList = '';
566 foreach ($temp as $inf) {
567 $tempList .= '[' . $inf['table'] . '][' . $inf['uid'] . '][' . $inf['field'] . '] (pid:' . $inf['pid'] . ') - ';
568 }
569 $output['moreReferences'][] = array($path, $entry, $fileArr[$entry], $tempList);
570 }
571 unset($fileArr[$entry]);
572 } else {
573 // Contains workaround for direct references
574 if (!strstr($entry, 'index.htm') && !preg_match(('/^' . preg_quote($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') . '/'), $folder)) {
575 $output['noReferences'][] = array($path, $entry);
576 }
577 }
578 }
579 }
580 $d->close();
581 $tempCounter = 0;
582 foreach ($fileArr as $file => $value) {
583 // Workaround for direct file references
584 if (preg_match('/^' . preg_quote($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') . '/', $folder)) {
585 $file = $folder . '/' . $file;
586 $folder = '';
587 $path = substr(PATH_site, 0, -1);
588 }
589 $temp = $this->whereIsFileReferenced($folder, $file);
590 $tempList = '';
591 foreach ($temp as $inf) {
592 $tempList .= '[' . $inf['table'] . '][' . $inf['uid'] . '][' . $inf['field'] . '] (pid:' . $inf['pid'] . ') - ';
593 }
594 $tempCounter++;
595 $output['noFile'][substr($path, -3) . '_' . substr($file, 0, 3) . '_' . $tempCounter] = array($path, $file, $tempList);
596 }
597 } else {
598 $output['error'][] = array($path);
599 }
600 }
601 return $output;
602 }
603
604 /**
605 * Depends on selectNonEmpty.... to be executed first!!
606 *
607 * @param array $theArray Table with key/value pairs being table names and arrays with uid numbers
608 * @return string HTML Error message
609 * @todo Define visibility
610 */
611 public function testDBRefs($theArray) {
612 $result = '';
613 foreach ($theArray as $table => $dbArr) {
614 if ($GLOBALS['TCA'][$table]) {
615 $idlist = array_keys($dbArr);
616 $theList = implode(',', $idlist);
617 if ($theList) {
618 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'uid IN (' . $theList . ')' . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table));
619 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
620 if (isset($dbArr[$row['uid']])) {
621 unset($dbArr[$row['uid']]);
622 } else {
623 $result .= 'Strange Error. ...<br />';
624 }
625 }
626 $GLOBALS['TYPO3_DB']->sql_free_result($mres);
627 foreach ($dbArr as $theId => $theC) {
628 $result .= 'There are ' . $theC . ' records pointing to this missing or deleted record; [' . $table . '][' . $theId . ']<br />';
629 }
630 }
631 } else {
632 $result .= 'Codeerror. Table is not a table...<br />';
633 }
634 }
635 return $result;
636 }
637
638 /**
639 * Finding all references to record based on table/uid
640 *
641 * @param string $searchTable Table name
642 * @param integer $id Uid of database record
643 * @return array Array with other arrays containing information about where references was found
644 * @todo Define visibility
645 */
646 public function whereIsRecordReferenced($searchTable, $id) {
647 // Gets tables / Fields that reference to files
648 $fileFields = $this->getDBFields($searchTable);
649 $theRecordList = array();
650 foreach ($fileFields as $info) {
651 $table = $info[0];
652 $field = $info[1];
653 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid,' . $GLOBALS['TCA'][$table]['ctrl']['label'] . ',' . $field, $table, $field . ' LIKE \'%' . $GLOBALS['TYPO3_DB']->quoteStr($id, $table) . '%\'');
654 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
655 // Now this is the field, where the reference COULD come from. But we're not garanteed, so we must carefully examine the data.
656 $fieldConf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
657 $allowedTables = $fieldConf['type'] == 'group' ? $fieldConf['allowed'] : $fieldConf['foreign_table'];
658 /** @var $dbAnalysis \TYPO3\CMS\Core\Database\RelationHandler */
659 $dbAnalysis = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
660 $dbAnalysis->start($row[$field], $allowedTables, $fieldConf['MM'], $row['uid'], $table, $fieldConf);
661 foreach ($dbAnalysis->itemArray as $tempArr) {
662 if ($tempArr['table'] == $searchTable && $tempArr['id'] == $id) {
663 $theRecordList[] = array('table' => $table, 'uid' => $row['uid'], 'field' => $field, 'pid' => $row['pid']);
664 }
665 }
666 }
667 $GLOBALS['TYPO3_DB']->sql_free_result($mres);
668 }
669 return $theRecordList;
670 }
671
672 /**
673 * Finding all references to file based on uploadfolder / filename
674 *
675 * @param string $uploadfolder Upload folder where file is found
676 * @param string $filename Filename to search for
677 * @return array Array with other arrays containing information about where references was found
678 * @todo Define visibility
679 */
680 public function whereIsFileReferenced($uploadfolder, $filename) {
681 // Gets tables / Fields that reference to files
682 $fileFields = $this->getFileFields($uploadfolder);
683 $theRecordList = array();
684 foreach ($fileFields as $info) {
685 $table = $info[0];
686 $field = $info[1];
687 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid,' . $GLOBALS['TCA'][$table]['ctrl']['label'] . ',' . $field, $table, $field . ' LIKE \'%' . $GLOBALS['TYPO3_DB']->quoteStr($filename, $table) . '%\'');
688 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
689 // Now this is the field, where the reference COULD come from.
690 // But we're not guaranteed, so we must carefully examine the data.
691 $tempArr = explode(',', trim($row[$field]));
692 foreach ($tempArr as $file) {
693 $file = trim($file);
694 if ($file == $filename) {
695 $theRecordList[] = array('table' => $table, 'uid' => $row['uid'], 'field' => $field, 'pid' => $row['pid']);
696 }
697 }
698 }
699 $GLOBALS['TYPO3_DB']->sql_free_result($mres);
700 }
701 return $theRecordList;
702 }
703
704 }
705
706
707 ?>