Updated copyright notices to show "2004"
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_admin.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2004 Kasper Skaarhoj (kasper@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 <kasper@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 * 150: function lostRecords($pid_list)
44 * 179: function fixLostRecord($table,$uid)
45 * 197: function countRecords($pid_list)
46 * 227: function getGroupFields($mode)
47 * 261: function getFileFields($uploadfolder)
48 * 284: function getDBFields($theSearchTable)
49 * 312: function selectNonEmptyRecordsWithFkeys($fkey_arrays)
50 * 387: function testFileRefs ()
51 * 438: function testDBRefs($theArray)
52 * 477: function whereIsRecordReferenced($searchTable,$id)
53 * 511: 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 <kasper@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 mysql_num_rows (most recent query)
115 */
116 function genTree($theID, $depthData) {
117 $res = mysql(TYPO3_db, 'SELECT uid,title,doktype,deleted'.(t3lib_extMgm::isLoaded('cms')?',hidden':'').' FROM pages WHERE pid="'.$theID.'" '.((!$this->genTree_includeDeleted)?'AND NOT deleted':'').$this->perms_clause.' ORDER BY sorting');
118 $a=0;
119 $c=mysql_num_rows($res);
120 while ($row = mysql_fetch_assoc($res)) {
121 $a++;
122 $newID =$row['uid'];
123 if ($this->genTree_makeHTML) {
124 $this->genTree_HTML.=chr(10).'<div><span class="nobr">';
125 $PM = 'join';
126 $LN = ($a==$c)?'blank':'line';
127 $BTM = ($a==$c)?'bottom':'';
128 $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(strip_tags($row['title']),50)).'</span></div>';
129 }
130
131 if (isset($page_idlist[$newID])) {
132 $this->recStat['doublePageID'][]=$newID;
133 }
134 $this->page_idArray[$newID]=$newID;
135 if ($row['deleted']) {$this->recStat['deleted']++;}
136 if ($row['hidden']) {$this->recStat['hidden']++;}
137 $this->recStat['doktype'][$row['doktype']]++;
138
139 $this->genTree($newID,$this->genTree_HTML ? $depthData.'<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/ol/'.$LN.'.gif','width="18" height="16"').' align="top" alt="" />' : '');
140 }
141 return (mysql_num_rows($res));
142 }
143
144 /**
145 * Fills $this->lRecords with the records from all tc-tables that are not attached to a PID in the pid-list.
146 *
147 * @param string list of pid's (page-record uid's). This list is probably made by genTree()
148 * @return void
149 */
150 function lostRecords($pid_list) {
151 global $TCA;
152 reset($TCA);
153 $this->lostPagesList='';
154 if ($pid_list) {
155 while (list($table)=each($TCA)) {
156 t3lib_div::loadTCA($table);
157 $query = 'SELECT uid,pid,'.$TCA[$table]['ctrl']['label'].' FROM '.$table.' WHERE pid NOT IN ('.$pid_list.')';
158 $garbage = mysql(TYPO3_db,$query);
159 echo mysql_error();
160 $lostIdList=Array();
161 while ($row = mysql_fetch_assoc($garbage)) {
162 $this->lRecords[$table][$row['uid']]=Array('uid'=>$row['uid'], 'pid'=>$row['pid'], 'title'=> strip_tags($row[$TCA[$table]['ctrl']['label']]) );
163 $lostIdList[]=$row['uid'];
164 }
165 if ($table=='pages') {
166 $this->lostPagesList=implode($lostIdList,',');
167 }
168 }
169 }
170 }
171
172 /**
173 * 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.
174 *
175 * @param string Database tablename
176 * @param integer The uid of the record which will have the PID value set to 0 (zero)
177 * @return boolean True if done.
178 */
179 function fixLostRecord($table,$uid) {
180 if ($table && $GLOBALS['TCA'][$table] && $uid && is_array($this->lRecords[$table][$uid]) && $GLOBALS['BE_USER']->user['admin']) {
181 $extra='';
182 if ($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']) { // If possible a lost record restored is hidden as default
183 $extra.=','.$GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'].'=1';
184 }
185 $fixQuery = 'UPDATE '.$table.' SET pid=0'.$extra.' WHERE uid="'.$uid.'"';
186 $fixQ = mysql(TYPO3_db,$fixQuery);
187 return true;
188 } else return false;
189 }
190
191 /**
192 * Counts records from $TCA-tables that ARE attached to an existing page.
193 *
194 * @param string list of pid's (page-record uid's). This list is probably made by genTree()
195 * @return array an array with the number of records from all $TCA-tables that are attached to a PID in the pid-list.
196 */
197 function countRecords($pid_list) {
198 global $TCA;
199 reset($TCA);
200 $list=Array();
201 $list_n=Array();
202 if ($pid_list) {
203 while (list($table)=each($TCA)) {
204 t3lib_div::loadTCA($table);
205 $query = 'SELECT count(*) FROM '.$table.' WHERE pid IN ('.$pid_list.')';
206 $count = mysql(TYPO3_db,$query);
207 if ($row = mysql_fetch_row($count)) {
208 $list[$table]=$row[0];
209 }
210
211 $query = 'SELECT count(*) FROM '.$table.' WHERE pid IN ('.$pid_list.')'.t3lib_BEfunc::deleteClause($table);
212 $count = mysql(TYPO3_db,$query);
213 if ($row = mysql_fetch_row($count)) {
214 $list_n[$table]=$row[0];
215 }
216 }
217 }
218 return array('all' => $list, 'non_deleted' => $list_n);
219 }
220
221 /**
222 * Finding relations in database based on type 'group' (files or database-uid's in a list)
223 *
224 * @param string $mode = file, $mode = db, $mode = '' (all...)
225 * @return array An array with all fields listed that somehow are references to other records (foreign-keys) or files
226 */
227 function getGroupFields($mode) {
228 global $TCA;
229 reset ($TCA);
230 $result = Array();
231 while (list($table)=each($TCA)) {
232 t3lib_div::loadTCA($table);
233 $cols = $TCA[$table]['columns'];
234 reset ($cols);
235 while (list($field,$config)=each($cols)) {
236 if ($config['config']['type']=='group') {
237 if (
238 ((!$mode||$mode=='file') && $config['config']['internal_type']=='file') ||
239 ((!$mode||$mode=='db') && $config['config']['internal_type']=='db')
240 ) {
241 $result[$table][]=$field;
242 }
243 }
244 if ( (!$mode||$mode=='db') && $config['config']['type']=='select' && $config['config']['foreign_table']) {
245 $result[$table][]=$field;
246 }
247 }
248 if ($result[$table]) {
249 $result[$table] = implode(',',$result[$table]);
250 }
251 }
252 return $result;
253 }
254
255 /**
256 * Finds all fields that hold filenames from uploadfolder
257 *
258 * @param string Path to uploadfolder
259 * @return array An array with all fields listed that have references to files in the $uploadfolder
260 */
261 function getFileFields($uploadfolder) {
262 global $TCA;
263 reset ($TCA);
264 $result = Array();
265 while (list($table)=each($TCA)) {
266 t3lib_div::loadTCA($table);
267 $cols = $TCA[$table]['columns'];
268 reset ($cols);
269 while (list($field,$config)=each($cols)) {
270 if ($config['config']['type']=='group' && $config['config']['internal_type']=='file' && $config['config']['uploadfolder']==$uploadfolder) {
271 $result[]=Array($table,$field);
272 }
273 }
274 }
275 return $result;
276 }
277
278 /**
279 * Returns an array with arrays of table/field pairs which are allowed to hold references to the input table name - according to $TCA
280 *
281 * @param string Table name
282 * @return array
283 */
284 function getDBFields($theSearchTable) {
285 global $TCA;
286 $result = Array();
287 reset ($TCA);
288 while (list($table)=each($TCA)) {
289 t3lib_div::loadTCA($table);
290 $cols = $TCA[$table]['columns'];
291 reset ($cols);
292 while (list($field,$config)=each($cols)) {
293 if ($config['config']['type']=='group' && $config['config']['internal_type']=='db') {
294 if (trim($config['config']['allowed'])=='*' || strstr($config['config']['allowed'],$theSearchTable)) {
295 $result[]=Array($table,$field);
296 }
297 } else if ($config['config']['type']=='select' && $config['config']['foreign_table']==$theSearchTable) {
298 $result[]=Array($table,$field);
299 }
300 }
301 }
302 return $result;
303 }
304
305 /**
306 * This selects non-empty-records from the tables/fields in the fkey_array generated by getGroupFields()
307 *
308 * @param array Array with tables/fields generated by getGroupFields()
309 * @return void
310 * @see getGroupFields()
311 */
312 function selectNonEmptyRecordsWithFkeys($fkey_arrays) {
313 global $TCA;
314 if (is_array($fkey_arrays)) {
315 reset($fkey_arrays);
316 while (list($table,$field_list)=each($fkey_arrays)) {
317 if ($TCA[$table] && trim($field_list)) {
318 t3lib_div::loadTCA($table);
319 $fieldArr = explode(',',$field_list);
320 $cl_fl = implode ('!="" OR ',$fieldArr). '!=""';
321 $query = 'SELECT uid,'.$field_list.' FROM '.$table.' WHERE '.$cl_fl;
322 $mres = mysql(TYPO3_db,$query);
323 echo mysql_error();
324 while ($row=mysql_fetch_assoc($mres)) {
325 reset($fieldArr);
326 while (list(,$field)=each($fieldArr)) {
327 if (trim($row[$field])) {
328 $fieldConf = $TCA[$table]['columns'][$field]['config'];
329 if ($fieldConf['type']=='group') {
330 if ($fieldConf['internal_type']=='file') {
331 // files...
332 if ($fieldConf['MM']) {
333 $tempArr=array();
334 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
335 $dbAnalysis->start('','files',$fieldConf['MM'],$row['uid']);
336 reset($dbAnalysis->itemArray);
337 while (list($somekey,$someval)=each($dbAnalysis->itemArray)) {
338 if ($someval['id']) {
339 $tempArr[]=$someval['id'];
340 }
341 }
342 } else {
343 $tempArr = explode(',',trim($row[$field]));
344 }
345 reset($tempArr);
346 while (list(,$file)=each($tempArr)) {
347 $file = trim($file);
348 if ($file) {
349 $this->checkFileRefs[$fieldConf['uploadfolder']][$file]+=1;
350 }
351 }
352 }
353 if ($fieldConf['internal_type']=='db') {
354 // dbs - group
355 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
356 $dbAnalysis->start($row[$field],$fieldConf['allowed'],$fieldConf['MM'],$row['uid']);
357 reset($dbAnalysis->itemArray);
358 while (list(,$tempArr)=each($dbAnalysis->itemArray)) {
359 $this->checkGroupDBRefs[$tempArr['table']][$tempArr['id']]+=1;
360 }
361 }
362 }
363 if ($fieldConf['type']=='select' && $fieldConf['foreign_table']) {
364 // dbs - select
365 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
366 $dbAnalysis->start($row[$field],$fieldConf['foreign_table'],$fieldConf['MM'],$row['uid']);
367 reset($dbAnalysis->itemArray);
368 while (list(,$tempArr)=each($dbAnalysis->itemArray)) {
369 if ($tempArr['id']>0) {
370 $this->checkGroupDBRefs[$fieldConf['foreign_table']][$tempArr['id']]+=1;
371 }
372 }
373 }
374 }
375 }
376 }
377 }
378 }
379 }
380 }
381
382 /**
383 * Depends on selectNonEmpty.... to be executed first!!
384 *
385 * @return array Report over files; keys are "moreReferences", "noReferences", "noFile", "error"
386 */
387 function testFileRefs () {
388 $output=Array();
389 reset($this->checkFileRefs);
390 while(list($folder,$fileArr)=each($this->checkFileRefs)) {
391 $path = PATH_site.$folder;
392 if (@is_dir($path)) {
393 $d = dir($path);
394 while($entry=$d->read()) {
395 if (@is_file($path.'/'.$entry)) {
396 if (isset($fileArr[$entry])) {
397 if ($fileArr[$entry] > 1) {
398 $temp = $this->whereIsFileReferenced($folder,$entry);
399 $tempList = '';
400 while(list(,$inf)=each($temp)) {
401 $tempList.='['.$inf['table'].']['.$inf['uid'].']['.$inf['field'].'] (pid:'.$inf['pid'].') - ';
402 }
403 $output['moreReferences'][] = Array($path,$entry,$fileArr[$entry],$tempList);
404 }
405 unset($fileArr[$entry]);
406 } else {
407 if (!strstr($entry,'index.htm')) {
408 $output['noReferences'][] = Array($path,$entry);
409 }
410 }
411 }
412 }
413 $d->close();
414 reset($fileArr);
415 $tempCounter=0;
416 while(list($file,)=each($fileArr)) {
417 $temp = $this->whereIsFileReferenced($folder,$file);
418 $tempList = '';
419 while(list(,$inf)=each($temp)) {
420 $tempList.='['.$inf['table'].']['.$inf['uid'].']['.$inf['field'].'] (pid:'.$inf['pid'].') - ';
421 }
422 $tempCounter++;
423 $output['noFile'][substr($path,-3).'_'.substr($file,0,3).'_'.$tempCounter] = Array($path,$file,$tempList);
424 }
425 } else {
426 $output['error'][] = Array($path);
427 }
428 }
429 return $output;
430 }
431
432 /**
433 * Depends on selectNonEmpty.... to be executed first!!
434 *
435 * @param array Table with key/value pairs being table names and arrays with uid numbers
436 * @return string HTML Error message
437 */
438 function testDBRefs($theArray) {
439 global $TCA;
440 reset($theArray);
441 while(list($table,$dbArr)=each($theArray)) {
442 if ($TCA[$table]) {
443 $idlist = Array();
444 while(list($id,)=each($dbArr)) {
445 $idlist[]=$id;
446 }
447 $theList = implode($idlist,',');
448 if ($theList) {
449 $query='SELECT uid FROM '.$table.' WHERE uid IN ('.$theList.') '.t3lib_BEfunc::deleteClause($table);
450 $mres = mysql(TYPO3_db,$query);
451 while($row=mysql_fetch_assoc($mres)) {
452 if (isset($dbArr[$row['uid']])) {
453 unset ($dbArr[$row['uid']]);
454 } else {
455 $result.='Strange Error. ...<br />';
456 }
457 }
458 reset($dbArr);
459 while (list($theId,$theC)=each($dbArr)) {
460 $result.='There are '.$theC.' records pointing to this missing or deleted record; ['.$table.']['.$theId.']<br />';
461 }
462 }
463 } else {
464 $result.='Codeerror. Table is not a table...<br />';
465 }
466 }
467 return $result;
468 }
469
470 /**
471 * Finding all references to record based on table/uid
472 *
473 * @param string Table name
474 * @param integer Uid of database record
475 * @return array Array with other arrays containing information about where references was found
476 */
477 function whereIsRecordReferenced($searchTable,$id) {
478 global $TCA;
479 $fileFields = $this->getDBFields($searchTable); // Gets tables / Fields that reference to files...
480 $theRecordList=Array();
481 while (list(,$info)=each($fileFields)) {
482 $table=$info[0]; $field=$info[1];
483 t3lib_div::loadTCA($table);
484 $query = 'SELECT uid,pid,'.$TCA[$table]['ctrl']['label'].','.$field.' FROM '.$table.' WHERE '.$field.' LIKE "%'.addSlashes($id).'%"';
485 $mres = mysql(TYPO3_db,$query);
486 while ($row = mysql_fetch_assoc($mres)) {
487 // Now this is the field, where the reference COULD come from. But we're not garanteed, so we must carefully examine the data.
488 $fieldConf = $TCA[$table]['columns'][$field]['config'];
489 $allowedTables = ($fieldConf['type']=='group') ? $fieldConf['allowed'] : $fieldConf['foreign_table'];
490
491 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
492 $dbAnalysis->start($row[$field],$allowedTables,$fieldConf['MM'],$row['uid']);
493 reset($dbAnalysis->itemArray);
494 while (list(,$tempArr)=each($dbAnalysis->itemArray)) {
495 if ($tempArr['table']==$searchTable && $tempArr['id']==$id) {
496 $theRecordList[]=Array('table'=>$table,'uid'=>$row['uid'],'field'=>$field,'pid'=>$row['pid']);
497 }
498 }
499 }
500 }
501 return $theRecordList;
502 }
503
504 /**
505 * Finding all references to file based on uploadfolder / filename
506 *
507 * @param string Upload folder where file is found
508 * @param string Filename to search for
509 * @return array Array with other arrays containing information about where references was found
510 */
511 function whereIsFileReferenced($uploadfolder,$filename) {
512 global $TCA;
513 $fileFields = $this->getFileFields($uploadfolder); // Gets tables / Fields that reference to files...
514 $theRecordList=Array();
515 while (list(,$info)=each($fileFields)) {
516 $table=$info[0]; $field=$info[1];
517 $query = 'SELECT uid,pid,'.$TCA[$table]['ctrl']['label'].','.$field.' FROM '.$table.' WHERE '.$field.' LIKE "%'.addSlashes($filename).'%"';
518 $mres = mysql(TYPO3_db,$query);
519 while ($row = mysql_fetch_assoc($mres)) {
520 // Now this is the field, where the reference COULD come from. But we're not garanteed, so we must carefully examine the data.
521 $tempArr = explode(',',trim($row[$field]));
522 while (list(,$file)=each($tempArr)) {
523 $file = trim($file);
524 if ($file==$filename) {
525 $theRecordList[]=Array('table'=>$table,'uid'=>$row['uid'],'field'=>$field,'pid'=>$row['pid']);
526 }
527 }
528 }
529 }
530 return $theRecordList;
531 }
532 }
533
534
535 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_admin.php']) {
536 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_admin.php']);
537 }
538 ?>