*** empty log message ***
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_admin.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2006 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Contains a class for evaluation of database integrity according to $TCA
29 *
30 * $Id$
31 * Revised for TYPO3 3.6 July/2003 by Kasper Skaarhoj
32 * XHTML compliant
33 *
34 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
35 */
36 /**
37 * [CLASS/FUNCTION INDEX of SCRIPT]
38 *
39 *
40 *
41 * 90: class t3lib_admin
42 * 116: function genTree($theID, $depthData)
43 * 156: function lostRecords($pid_list)
44 * 187: function fixLostRecord($table,$uid)
45 * 208: function countRecords($pid_list)
46 * 236: function getGroupFields($mode)
47 * 270: function getFileFields($uploadfolder)
48 * 293: function getDBFields($theSearchTable)
49 * 321: function selectNonEmptyRecordsWithFkeys($fkey_arrays)
50 * 410: function testFileRefs ()
51 * 461: function testDBRefs($theArray)
52 * 499: function whereIsRecordReferenced($searchTable,$id)
53 * 536: function whereIsFileReferenced($uploadfolder,$filename)
54 *
55 * TOTAL FUNCTIONS: 12
56 * (This index is automatically created/updated by the extension "extdeveval")
57 *
58 */
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 /**
80 * This class holds functions used by the TYPO3 backend to check the integrity of the database (The DBint module, 'lowlevel' extension)
81 *
82 * Depends on: Depends on loaddbgroup from t3lib/
83 *
84 * @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!
85 * @see SC_mod_tools_dbint_index::func_relations(), SC_mod_tools_dbint_index::func_records()
86 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
87 * @package TYPO3
88 * @subpackage t3lib
89 */
90 class t3lib_admin {
91 var $genTree_includeDeleted = 1; // if set, genTree() includes deleted pages. This is default.
92 var $perms_clause=''; // extra where-clauses for the tree-selection
93 var $genTree_makeHTML = 0; // if set, genTree() generates HTML, that visualizes the tree.
94 // internal
95 var $genTree_idlist = ''; // Will hold the id-list from genTree()
96 var $getTree_HTML = ''; // Will hold the HTML-code visualising the tree. genTree()
97 var $backPath='';
98
99 // internal
100 var $checkFileRefs = Array();
101 var $checkSelectDBRefs = Array(); // From the select-fields
102 var $checkGroupDBRefs = Array(); // From the group-fields
103
104 var $page_idArray=Array();
105 var $recStat = Array();
106 var $lRecords = Array();
107 var $lostPagesList = '';
108
109 /**
110 * Generates a list of Page-uid's that corresponds to the tables in the tree. This list should ideally include all records in the pages-table.
111 *
112 * @param integer a pid (page-record id) from which to start making the tree
113 * @param string HTML-code (image-tags) used when this function calls itself recursively.
114 * @return integer Number of $GLOBALS['TYPO3_DB']->sql_num_rows (most recent query)
115 */
116 function genTree($theID, $depthData) {
117 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
118 'uid,title,doktype,deleted'.(t3lib_extMgm::isLoaded('cms')?',hidden':''),
119 'pages',
120 'pid='.intval($theID).' '.((!$this->genTree_includeDeleted)?'AND deleted=0':'').$this->perms_clause,
121 '',
122 'sorting'
123 );
124 $a=0;
125 $c = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
126 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
127 $a++;
128 $newID =$row['uid'];
129 if ($this->genTree_makeHTML) {
130 $this->genTree_HTML.=chr(10).'<div><span class="nobr">';
131 $PM = 'join';
132 $LN = ($a==$c)?'blank':'line';
133 $BTM = ($a==$c)?'bottom':'';
134 $this->genTree_HTML.= $depthData.'<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/ol/'.$PM.$BTM.'.gif','width="18" height="16"').' align="top" alt="" />'.t3lib_iconWorks::getIconImage('pages',$row,$this->backPath,'align="top"').htmlspecialchars($row['uid'].': '.t3lib_div::fixed_lgd_cs(strip_tags($row['title']),50)).'</span></div>';
135 }
136
137 if (isset($page_idlist[$newID])) {
138 $this->recStat['doublePageID'][]=$newID;
139 }
140 $this->page_idArray[$newID]=$newID;
141 if ($row['deleted']) {$this->recStat['deleted']++;}
142 if ($row['hidden']) {$this->recStat['hidden']++;}
143 $this->recStat['doktype'][$row['doktype']]++;
144
145 $this->genTree($newID,$this->genTree_HTML ? $depthData.'<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/ol/'.$LN.'.gif','width="18" height="16"').' align="top" alt="" />' : '');
146 }
147 return $GLOBALS['TYPO3_DB']->sql_num_rows($res);
148 }
149
150 /**
151 * Fills $this->lRecords with the records from all tc-tables that are not attached to a PID in the pid-list.
152 *
153 * @param string list of pid's (page-record uid's). This list is probably made by genTree()
154 * @return void
155 */
156 function lostRecords($pid_list) {
157 global $TCA;
158 reset($TCA);
159 $this->lostPagesList='';
160 if ($pid_list) {
161 while (list($table)=each($TCA)) {
162 t3lib_div::loadTCA($table);
163 $garbage = $GLOBALS['TYPO3_DB']->exec_SELECTquery (
164 'uid,pid,'.$TCA[$table]['ctrl']['label'],
165 $table,
166 'pid NOT IN ('.$pid_list.')'
167 );
168 $lostIdList=Array();
169 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($garbage)) {
170 $this->lRecords[$table][$row['uid']]=Array('uid'=>$row['uid'], 'pid'=>$row['pid'], 'title'=> strip_tags($row[$TCA[$table]['ctrl']['label']]) );
171 $lostIdList[]=$row['uid'];
172 }
173 if ($table=='pages') {
174 $this->lostPagesList=implode(',',$lostIdList);
175 }
176 }
177 }
178 }
179
180 /**
181 * Fixes lost record from $table with uid $uid by setting the PID to zero. If there is a disabled column for the record that will be set as well.
182 *
183 * @param string Database tablename
184 * @param integer The uid of the record which will have the PID value set to 0 (zero)
185 * @return boolean True if done.
186 */
187 function fixLostRecord($table,$uid) {
188 if ($table && $GLOBALS['TCA'][$table] && $uid && is_array($this->lRecords[$table][$uid]) && $GLOBALS['BE_USER']->user['admin']) {
189
190 $updateFields = array();
191 $updateFields['pid'] = 0;
192 if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']) { // If possible a lost record restored is hidden as default
193 $updateFields[$GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']] = 1;
194 }
195
196 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
197
198 return TRUE;
199 } else return FALSE;
200 }
201
202 /**
203 * Counts records from $TCA-tables that ARE attached to an existing page.
204 *
205 * @param string list of pid's (page-record uid's). This list is probably made by genTree()
206 * @return array an array with the number of records from all $TCA-tables that are attached to a PID in the pid-list.
207 */
208 function countRecords($pid_list) {
209 global $TCA;
210 reset($TCA);
211 $list=Array();
212 $list_n=Array();
213 if ($pid_list) {
214 while (list($table)=each($TCA)) {
215 t3lib_div::loadTCA($table);
216 $count = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid IN ('.$pid_list.')');
217 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_row($count)) {
218 $list[$table]=$row[0];
219 }
220
221 $count = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid IN ('.$pid_list.')'.t3lib_BEfunc::deleteClause($table));
222 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_row($count)) {
223 $list_n[$table]=$row[0];
224 }
225 }
226 }
227 return array('all' => $list, 'non_deleted' => $list_n);
228 }
229
230 /**
231 * Finding relations in database based on type 'group' (files or database-uid's in a list)
232 *
233 * @param string $mode = file, $mode = db, $mode = '' (all...)
234 * @return array An array with all fields listed that somehow are references to other records (foreign-keys) or files
235 */
236 function getGroupFields($mode) {
237 global $TCA;
238 reset ($TCA);
239 $result = Array();
240 while (list($table)=each($TCA)) {
241 t3lib_div::loadTCA($table);
242 $cols = $TCA[$table]['columns'];
243 reset ($cols);
244 while (list($field,$config)=each($cols)) {
245 if ($config['config']['type']=='group') {
246 if (
247 ((!$mode||$mode=='file') && $config['config']['internal_type']=='file') ||
248 ((!$mode||$mode=='db') && $config['config']['internal_type']=='db')
249 ) {
250 $result[$table][]=$field;
251 }
252 }
253 if ( (!$mode||$mode=='db') && $config['config']['type']=='select' && $config['config']['foreign_table']) {
254 $result[$table][]=$field;
255 }
256 }
257 if ($result[$table]) {
258 $result[$table] = implode(',',$result[$table]);
259 }
260 }
261 return $result;
262 }
263
264 /**
265 * Finds all fields that hold filenames from uploadfolder
266 *
267 * @param string Path to uploadfolder
268 * @return array An array with all fields listed that have references to files in the $uploadfolder
269 */
270 function getFileFields($uploadfolder) {
271 global $TCA;
272 reset ($TCA);
273 $result = Array();
274 while (list($table)=each($TCA)) {
275 t3lib_div::loadTCA($table);
276 $cols = $TCA[$table]['columns'];
277 reset ($cols);
278 while (list($field,$config)=each($cols)) {
279 if ($config['config']['type']=='group' && $config['config']['internal_type']=='file' && $config['config']['uploadfolder']==$uploadfolder) {
280 $result[]=Array($table,$field);
281 }
282 }
283 }
284 return $result;
285 }
286
287 /**
288 * Returns an array with arrays of table/field pairs which are allowed to hold references to the input table name - according to $TCA
289 *
290 * @param string Table name
291 * @return array
292 */
293 function getDBFields($theSearchTable) {
294 global $TCA;
295 $result = Array();
296 reset ($TCA);
297 while (list($table)=each($TCA)) {
298 t3lib_div::loadTCA($table);
299 $cols = $TCA[$table]['columns'];
300 reset ($cols);
301 while (list($field,$config)=each($cols)) {
302 if ($config['config']['type']=='group' && $config['config']['internal_type']=='db') {
303 if (trim($config['config']['allowed'])=='*' || strstr($config['config']['allowed'],$theSearchTable)) {
304 $result[]=Array($table,$field);
305 }
306 } else if ($config['config']['type']=='select' && $config['config']['foreign_table']==$theSearchTable) {
307 $result[]=Array($table,$field);
308 }
309 }
310 }
311 return $result;
312 }
313
314 /**
315 * This selects non-empty-records from the tables/fields in the fkey_array generated by getGroupFields()
316 *
317 * @param array Array with tables/fields generated by getGroupFields()
318 * @return void
319 * @see getGroupFields()
320 */
321 function selectNonEmptyRecordsWithFkeys($fkey_arrays) {
322 global $TCA;
323 if (is_array($fkey_arrays)) {
324 reset($fkey_arrays);
325 while (list($table,$field_list)=each($fkey_arrays)) {
326 if ($TCA[$table] && trim($field_list)) {
327 t3lib_div::loadTCA($table);
328 $fieldArr = explode(',',$field_list);
329
330 if(t3lib_extMgm::isLoaded('dbal')) {
331 $fields = $GLOBALS['TYPO3_DB']->admin_get_fields($table);
332 reset($fields);
333 list(,$field)=each($fieldArr);
334 $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') ?
335 $field.'!=0' : $field.'!=\'\'';
336 while (list(,$field)=each($fieldArr)) {
337 $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') ?
338 ' OR '.$field.'!=0' : ' OR '.$field.'!=\'\'';
339 }
340 unset($fields);
341 }
342 else {
343 $cl_fl = implode ('!="" OR ',$fieldArr). '!=""';
344 }
345
346 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,'.$field_list, $table, $cl_fl);
347 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
348 reset($fieldArr);
349 while (list(,$field)=each($fieldArr)) {
350 if (trim($row[$field])) {
351 $fieldConf = $TCA[$table]['columns'][$field]['config'];
352 if ($fieldConf['type']=='group') {
353 if ($fieldConf['internal_type']=='file') {
354 // files...
355 if ($fieldConf['MM']) {
356 $tempArr=array();
357 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
358 $dbAnalysis->start('','files',$fieldConf['MM'],$row['uid']);
359 reset($dbAnalysis->itemArray);
360 while (list($somekey,$someval)=each($dbAnalysis->itemArray)) {
361 if ($someval['id']) {
362 $tempArr[]=$someval['id'];
363 }
364 }
365 } else {
366 $tempArr = explode(',',trim($row[$field]));
367 }
368 reset($tempArr);
369 while (list(,$file)=each($tempArr)) {
370 $file = trim($file);
371 if ($file) {
372 $this->checkFileRefs[$fieldConf['uploadfolder']][$file]+=1;
373 }
374 }
375 }
376 if ($fieldConf['internal_type']=='db') {
377 // dbs - group
378 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
379 $dbAnalysis->start($row[$field],$fieldConf['allowed'],$fieldConf['MM'],$row['uid']);
380 reset($dbAnalysis->itemArray);
381 while (list(,$tempArr)=each($dbAnalysis->itemArray)) {
382 $this->checkGroupDBRefs[$tempArr['table']][$tempArr['id']]+=1;
383 }
384 }
385 }
386 if ($fieldConf['type']=='select' && $fieldConf['foreign_table']) {
387 // dbs - select
388 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
389 $dbAnalysis->start($row[$field],$fieldConf['foreign_table'],$fieldConf['MM'],$row['uid']);
390 reset($dbAnalysis->itemArray);
391 while (list(,$tempArr)=each($dbAnalysis->itemArray)) {
392 if ($tempArr['id']>0) {
393 $this->checkGroupDBRefs[$fieldConf['foreign_table']][$tempArr['id']]+=1;
394 }
395 }
396 }
397 }
398 }
399 }
400 }
401 }
402 }
403 }
404
405 /**
406 * Depends on selectNonEmpty.... to be executed first!!
407 *
408 * @return array Report over files; keys are "moreReferences", "noReferences", "noFile", "error"
409 */
410 function testFileRefs () {
411 $output=Array();
412 reset($this->checkFileRefs);
413 while(list($folder,$fileArr)=each($this->checkFileRefs)) {
414 $path = PATH_site.$folder;
415 if (@is_dir($path)) {
416 $d = dir($path);
417 while($entry=$d->read()) {
418 if (@is_file($path.'/'.$entry)) {
419 if (isset($fileArr[$entry])) {
420 if ($fileArr[$entry] > 1) {
421 $temp = $this->whereIsFileReferenced($folder,$entry);
422 $tempList = '';
423 while(list(,$inf)=each($temp)) {
424 $tempList.='['.$inf['table'].']['.$inf['uid'].']['.$inf['field'].'] (pid:'.$inf['pid'].') - ';
425 }
426 $output['moreReferences'][] = Array($path,$entry,$fileArr[$entry],$tempList);
427 }
428 unset($fileArr[$entry]);
429 } else {
430 if (!strstr($entry,'index.htm')) {
431 $output['noReferences'][] = Array($path,$entry);
432 }
433 }
434 }
435 }
436 $d->close();
437 reset($fileArr);
438 $tempCounter=0;
439 while(list($file,)=each($fileArr)) {
440 $temp = $this->whereIsFileReferenced($folder,$file);
441 $tempList = '';
442 while(list(,$inf)=each($temp)) {
443 $tempList.='['.$inf['table'].']['.$inf['uid'].']['.$inf['field'].'] (pid:'.$inf['pid'].') - ';
444 }
445 $tempCounter++;
446 $output['noFile'][substr($path,-3).'_'.substr($file,0,3).'_'.$tempCounter] = Array($path,$file,$tempList);
447 }
448 } else {
449 $output['error'][] = Array($path);
450 }
451 }
452 return $output;
453 }
454
455 /**
456 * Depends on selectNonEmpty.... to be executed first!!
457 *
458 * @param array Table with key/value pairs being table names and arrays with uid numbers
459 * @return string HTML Error message
460 */
461 function testDBRefs($theArray) {
462 global $TCA;
463 reset($theArray);
464 while(list($table,$dbArr)=each($theArray)) {
465 if ($TCA[$table]) {
466 $idlist = Array();
467 while(list($id,)=each($dbArr)) {
468 $idlist[]=$id;
469 }
470 $theList = implode(',',$idlist);
471 if ($theList) {
472 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'uid IN ('.$theList.')'.t3lib_BEfunc::deleteClause($table));
473 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
474 if (isset($dbArr[$row['uid']])) {
475 unset ($dbArr[$row['uid']]);
476 } else {
477 $result.='Strange Error. ...<br />';
478 }
479 }
480 reset($dbArr);
481 while (list($theId,$theC)=each($dbArr)) {
482 $result.='There are '.$theC.' records pointing to this missing or deleted record; ['.$table.']['.$theId.']<br />';
483 }
484 }
485 } else {
486 $result.='Codeerror. Table is not a table...<br />';
487 }
488 }
489 return $result;
490 }
491
492 /**
493 * Finding all references to record based on table/uid
494 *
495 * @param string Table name
496 * @param integer Uid of database record
497 * @return array Array with other arrays containing information about where references was found
498 */
499 function whereIsRecordReferenced($searchTable,$id) {
500 global $TCA;
501 $fileFields = $this->getDBFields($searchTable); // Gets tables / Fields that reference to files...
502 $theRecordList=Array();
503 while (list(,$info)=each($fileFields)) {
504 $table=$info[0]; $field=$info[1];
505 t3lib_div::loadTCA($table);
506 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
507 'uid,pid,'.$TCA[$table]['ctrl']['label'].','.$field,
508 $table,
509 $field.' LIKE \'%'.$GLOBALS['TYPO3_DB']->quoteStr($id, $table).'%\''
510 );
511 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
512 // Now this is the field, where the reference COULD come from. But we're not garanteed, so we must carefully examine the data.
513 $fieldConf = $TCA[$table]['columns'][$field]['config'];
514 $allowedTables = ($fieldConf['type']=='group') ? $fieldConf['allowed'] : $fieldConf['foreign_table'];
515
516 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
517 $dbAnalysis->start($row[$field],$allowedTables,$fieldConf['MM'],$row['uid']);
518 reset($dbAnalysis->itemArray);
519 while (list(,$tempArr)=each($dbAnalysis->itemArray)) {
520 if ($tempArr['table']==$searchTable && $tempArr['id']==$id) {
521 $theRecordList[]=Array('table'=>$table,'uid'=>$row['uid'],'field'=>$field,'pid'=>$row['pid']);
522 }
523 }
524 }
525 }
526 return $theRecordList;
527 }
528
529 /**
530 * Finding all references to file based on uploadfolder / filename
531 *
532 * @param string Upload folder where file is found
533 * @param string Filename to search for
534 * @return array Array with other arrays containing information about where references was found
535 */
536 function whereIsFileReferenced($uploadfolder,$filename) {
537 global $TCA;
538 $fileFields = $this->getFileFields($uploadfolder); // Gets tables / Fields that reference to files...
539 $theRecordList=Array();
540 while (list(,$info)=each($fileFields)) {
541 $table=$info[0]; $field=$info[1];
542 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
543 'uid,pid,'.$TCA[$table]['ctrl']['label'].','.$field,
544 $table,
545 $field.' LIKE \'%'.$GLOBALS['TYPO3_DB']->quoteStr($filename, $table).'%\''
546 );
547 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
548 // Now this is the field, where the reference COULD come from. But we're not garanteed, so we must carefully examine the data.
549 $tempArr = explode(',',trim($row[$field]));
550 while (list(,$file)=each($tempArr)) {
551 $file = trim($file);
552 if ($file==$filename) {
553 $theRecordList[]=Array('table'=>$table,'uid'=>$row['uid'],'field'=>$field,'pid'=>$row['pid']);
554 }
555 }
556 }
557 }
558 return $theRecordList;
559 }
560 }
561
562
563 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_admin.php']) {
564 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_admin.php']);
565 }
566 ?>