* Merging all changes from TYPO3_4-0 branch back into HEAD
[Packages/TYPO3.CMS.git] / typo3 / sysext / lowlevel / class.tx_lowlevel_cleaner_core.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2005 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 * Core functions for cleaning and analysing
29 *
30 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
31 */
32 /**
33 * [CLASS/FUNCTION INDEX of SCRIPT]
34 *
35 *
36 *
37 * 71: class tx_lowlevel_cleaner_core
38 *
39 * SECTION: Analyse functions
40 * 94: function missing_files_analyze()
41 * 161: function missing_relations_analyze($filter='')
42 * 258: function double_files_analyze()
43 * 348: function RTEmagic_files_analyze()
44 * 437: function clean_lost_files_analyze()
45 * 525: function orphan_records_analyze()
46 *
47 * SECTION: Helper functions
48 * 572: function html_printInfo($header,$res,$silent=FALSE,$detailLevel=0)
49 *
50 * SECTION: CLI functionality
51 * 642: function cli_main($argv)
52 * 693: function cli_printInfo($header,$res,$silent=FALSE,$detailLevel=0)
53 *
54 * TOTAL FUNCTIONS: 9
55 * (This index is automatically created/updated by the extension "extdeveval")
56 *
57 */
58
59
60 require_once(PATH_t3lib.'class.t3lib_admin.php');
61
62
63
64 /**
65 * Core functions for cleaning and analysing
66 *
67 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
68 * @package TYPO3
69 * @subpackage tx_lowlevel
70 */
71 class tx_lowlevel_cleaner_core {
72
73 var $label_infoString = 'The list of records is organized as [table]:[uid]:[field]:[flexpointer]:[softref_key]';
74
75
76
77
78
79
80
81
82
83 /**************************
84 *
85 * Analyse functions
86 *
87 *************************/
88
89 /**
90 * Find missing files
91 *
92 * @return array
93 */
94 function missing_files_analyze() {
95 global $TYPO3_DB;
96
97 $listExplain = ' Shows the relative filename of missing file as header and under a list of record fields in which the references are found. '.$this->label_infoString;
98
99 // Initialize result array:
100 $resultArray = array(
101 'message' => '
102 Objective: Find all file references from non-deleted records pointing to a missing (non-existing) file.
103
104 Assumptions:
105 - a perfect integrity of the reference index table (always update the reference index table before using this tool!)
106 - relevant soft reference parsers applied everywhere file references are used inline
107
108 Files may be missing for these reasons (except software bugs):
109 - someone manually deleted the file inside fileadmin/ or another user maintained folder. If the reference was a soft reference (opposite to a TCEmain managed file relation from "group" type fields), technically it is not an error although it might be a mistake that someone did so.
110 - someone manually deleted the file inside the uploads/ folder (typically containing managed files) which is an error since no user interaction should take place there.
111
112 NOTICE: Uses the Reference Index Table (sys_refindex) for analysis. Update it before use!',
113 'headers' => array(
114 'managedFilesMissing' => array('List of missing files managed by TCEmain', $listExplain, 3),
115 'softrefFilesMissing' => array('List of missing files registered as a soft reference', $listExplain, 3),
116 'warnings' => array('Warnings, if any','',2)
117 ),
118 'managedFilesMissing' => array(),
119 'softrefFilesMissing' => array(),
120 'warnings' => array()
121 );
122
123 // Select all files in the reference table
124 $recs = $TYPO3_DB->exec_SELECTgetRows(
125 '*',
126 'sys_refindex',
127 'ref_table='.$TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex').
128 ' AND deleted=0' // Check only for records which are not deleted (we don't care about missing files in deleted-flagged records)
129 );
130
131 // Traverse the files and put into a large table:
132 if (is_array($recs)) {
133 foreach($recs as $rec) {
134
135 // Compile info string for location of reference:
136 $infoString = $rec['tablename'].':'.$rec['recuid'].':'.$rec['field'].':'.$rec['flexpointer'].':'.$rec['softref_key'];
137
138 // Handle missing file:
139 if (!@is_file(PATH_site.$rec['ref_string'])) {
140
141 if ((string)$rec['softref_key']=='') {
142 $resultArrayIndex = 'managedFilesMissing';
143 } else {
144 $resultArrayIndex = 'softrefFilesMissing';
145 }
146
147 $resultArray[$resultArrayIndex][$rec['ref_string']][$rec['hash']] = $infoString;
148 }
149 }
150 }
151
152 return $resultArray;
153 }
154
155 /**
156 * Missing relations to database records
157 *
158 * @param string Filter selection, options: "softref", "managed", "" (all)
159 * @return array
160 */
161 function missing_relations_analyze($filter='') {
162 global $TYPO3_DB;
163
164 $listExplain = ' Shows the missing record as header and underneath a list of record fields in which the references are found. '.$this->label_infoString;
165
166 // Initialize result array:
167 $resultArray = array(
168 'message' => '
169 Objective: Find all record references pointing to a missing (non-existing or deleted-flagged) record.
170 Assumptions:
171 - a perfect integrity of the reference index table (always update the reference index table before using this tool!)
172 - all database references to check are integers greater than zero
173 - does not check if a referenced record is inside an offline branch, another workspace etc. which could make the reference useless in reality or otherwise question integrity
174 Records may be missing for these reasons (except software bugs):
175 - someone deleted the record which is technically not an error although it might be a mistake that someone did so.
176 - after flushing published versions and/or deleted-flagged records a number of new missing references might appear; those were pointing to records just flushed.
177 NOTICE: Uses the Reference Index Table (sys_refindex) for analysis. Update it before use!',
178 'headers' => array(
179 'offlineVersionRecords' => array('Offline version records','These records are offline versions having a pid=-1 and references should never occur directly to their uids.'.$listExplain,3),
180 'deletedRecords' => array('Deleted-flagged records','These records are deleted with a flag but references are still pointing at them. Keeping the references is useful if you undelete the referenced records later, otherwise the references are lost completely when the deleted records are flushed at some point.'.$listExplain,2),
181 'nonExistingRecords' => array('Non-existing records to which there are references','These references can safely be removed since there is no record found in the database at all.'.$listExplain,3), // 3 = error
182 'uniqueReferencesToTables' => array('Unique references to various tables','For each listed table, this shows how many different records had references pointing to them. More references to the same record counts only 1, hence it is the number of unique referenced records you see. The count includes both valid and invalid references.',1), // 1 = info
183 'warnings' => array('Warnings picked up','',2) // 2 = warning
184 ),
185 'offlineVersionRecords' => array(),
186 'deletedRecords' => array(),
187 'nonExistingRecords' => array(),
188 'uniqueReferencesToTables' => array(),
189 'warnings' => array()
190 );
191
192 // Create clause to filter by:
193 $filterClause = '';
194 if ($filter==='softref') {
195 $filterClause = ' AND softref_key!='.$TYPO3_DB->fullQuoteStr('', 'sys_refindex');
196 }
197 if ($filter==='managed') {
198 $filterClause = ' AND softref_key='.$TYPO3_DB->fullQuoteStr('', 'sys_refindex');
199 }
200
201 // Select all files in the reference table not found by a soft reference parser (thus TCA configured)
202 $recs = $TYPO3_DB->exec_SELECTgetRows(
203 '*',
204 'sys_refindex',
205 'ref_table!='.$TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex'). // Assuming that any other key will be a table name!
206 ' AND ref_uid>0'.
207 $filterClause.
208 ' AND deleted=0' // Check only for records which are not deleted (we don't care about missing relations in deleted-flagged records)
209 );
210
211 // Traverse the files and put into a large table:
212 $tempExists = array();
213 if (is_array($recs)) {
214 foreach($recs as $rec) {
215 $idx = $rec['ref_table'].':'.$rec['ref_uid'];
216
217 if (!isset($tempExists[$idx])) {
218
219 // Select all files in the reference table not found by a soft reference parser (thus TCA configured)
220 if (isset($GLOBALS['TCA'][$rec['ref_table']])) {
221 $recs = $TYPO3_DB->exec_SELECTgetRows(
222 'uid,pid'.($GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete'] ? ','.$GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete'] : ''),
223 $rec['ref_table'],
224 'uid='.intval($rec['ref_uid'])
225 );
226
227 $tempExists[$idx] = count($recs) ? TRUE : FALSE;
228 } else {
229 $tempExists[$idx] = FALSE;
230 }
231 $resultArray['uniqueReferencesToTables'][$rec['ref_table']]++;
232 }
233
234 // Compile info string for location of reference:
235 $infoString = $rec['tablename'].':'.$rec['recuid'].':'.$rec['field'].':'.$rec['flexpointer'].':'.$rec['softref_key'];
236
237 // Handle missing file:
238 if ($tempExists[$idx]) {
239 if ($recs[0]['pid']==-1) {
240 $resultArray['offlineVersionRecords'][$idx][$rec['hash']] = $infoString;
241 } elseif ($GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete'] && $recs[0][$GLOBALS['TCA'][$rec['ref_table']]['ctrl']['delete']]) {
242 $resultArray['deletedRecords'][$idx][$rec['hash']] = $infoString;
243 }
244 } else {
245 $resultArray['nonExistingRecords'][$idx][$rec['hash']] = $infoString;
246 }
247 }
248 }
249
250 return $resultArray;
251 }
252
253 /**
254 * Find managed files which are referred to more than one time
255 *
256 * @return array
257 */
258 function double_files_analyze() {
259 global $TYPO3_DB;
260
261 // Initialize result array:
262 $resultArray = array(
263 'message' => '
264 Objective: Looking for files from TYPO3 managed records which are referenced more than one time (only one time allowed)
265 Assumptions:
266 - a perfect integrity of the reference index table (always update the reference index table before using this tool!)
267 - files found in deleted records are included (otherwise you would see a false list of lost files)
268 ' .
269 'Files attached to records in TYPO3 using a "group" type configuration in TCA or FlexForm DataStructure are managed exclusively by the system and there must always exist a 1-1 reference between the file and the reference in the record.' .
270 'This tool will expose when such files are referenced from multiple locations which is considered an integrity error. ' .
271 'If a multi-reference is found it was typically created because the record was copied or modified outside of TCEmain which will otherwise maintain the relations correctly. ' .
272 'Multi-references should be resolved to 1-1 references as soon as possible. The danger of keeping multi-references is that if the file is removed from one of the refering records it will actually be deleted in the file system, leaving missing files for the remaining referers!
273
274 NOTICE: Uses the Reference Index Table (sys_refindex) for analysis. Update it before use!',
275 'headers' => array(
276 'multipleReferencesList_count' => array('Number of multi-reference files','(See below)',1),
277 'singleReferencesList_count' => array('Number of files correctly referenced','The amount of correct 1-1 references',1),
278 'multipleReferencesList' => array('Entries with files having multiple references','These are serious problems that should be resolved ASAP to prevent data loss! '.$this->label_infoString,3),
279 'dirname_registry' => array('Registry of directories in which files are found.','Registry includes which table/field pairs store files in them plus how many files their store.',1),
280 'missingFiles' => array('Tracking missing files','(Extra feature, not related to tracking of double references. Further, the list may include more files than found in the missing_files()-test because this list includes missing files from deleted records.)',0),
281 'warnings' => array('Warnings picked up','',2)
282 ),
283 'multipleReferencesList_count' => 0,
284 'singleReferencesList_count' => 0,
285 'multipleReferencesList' => array(),
286 'dirname_registry' => array(),
287 'missingFiles' => array(),
288 'warnings' => array()
289 );
290
291 // Select all files in the reference table not found by a soft reference parser (thus TCA configured)
292 $recs = $TYPO3_DB->exec_SELECTgetRows(
293 '*',
294 'sys_refindex',
295 'ref_table='.$TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex').
296 ' AND softref_key='.$TYPO3_DB->fullQuoteStr('', 'sys_refindex')
297 );
298
299 // Traverse the files and put into a large table:
300 $tempCount = array();
301 if (is_array($recs)) {
302 foreach($recs as $rec) {
303
304 // Compile info string for location of reference:
305 $infoString = $rec['tablename'].':'.$rec['recuid'].':'.$rec['field'].':'.$rec['flexpointer'].':';
306
307 // Registering occurencies in directories:
308 $resultArray['dirname_registry'][dirname($rec['ref_string'])][$rec['tablename'].':'.$rec['field']]++;
309
310 // Handle missing file:
311 if (!@is_file(PATH_site.$rec['ref_string'])) {
312 $resultArray['missingFiles'][$rec['ref_string']][$rec['hash']] = $infoString;
313 }
314
315 // Add entry if file has multiple references pointing to it:
316 if (isset($tempCount[$rec['ref_string']])) {
317 if (!is_array($resultArray['multipleReferencesList'][$rec['ref_string']])) {
318 $resultArray['multipleReferencesList'][$rec['ref_string']] = array();
319 $resultArray['multipleReferencesList'][$rec['ref_string']][$tempCount[$rec['ref_string']][1]] = $tempCount[$rec['ref_string']][0];
320 }
321 $resultArray['multipleReferencesList'][$rec['ref_string']][$rec['hash']] = $infoString;
322 } else {
323 $tempCount[$rec['ref_string']] = array($infoString,$rec['hash']);
324 }
325 }
326 }
327
328 // Add count for multi-references:
329 $resultArray['multipleReferencesList_count'] = count($resultArray['multipleReferencesList']);
330 $resultArray['singleReferencesList_count'] = count($tempCount) - $resultArray['multipleReferencesList_count'];
331
332 // Sort dirname registry and add warnings for directories outside uploads/
333 ksort($resultArray['dirname_registry']);
334 foreach($resultArray['dirname_registry'] as $dir => $temp) {
335 if (!t3lib_div::isFirstPartOfStr($dir,'uploads/')) {
336 $resultArray['warnings'][] = 'Directory "'.$dir.'" was outside uploads/ which is unusual practice in TYPO3 although not forbidden. Directory used by the following table:field pairs: '.implode(',',array_keys($temp));
337 }
338 }
339
340 return $resultArray;
341 }
342
343 /**
344 * Analyse situation with RTE magic images.
345 *
346 * @return array
347 */
348 function RTEmagic_files_analyze() {
349 global $TYPO3_DB;
350
351 // Initialize result array:
352 $resultArray = array(
353 'message' => '
354 Objective: Looking up all occurencies of RTEmagic images in the database and check existence of parent and copy files on the file system plus report possibly lost files of this type.
355 Assumptions:
356 - a perfect integrity of the reference index table (always update the reference index table before using this tool!)
357 - that all RTEmagic image files in the database are registered with the soft reference parser "images"
358 - images found in deleted records are included (means that you might find lost RTEmagic images after flushing deleted records)
359 The assumptions are not requirements by the TYPO3 API but reflects the de facto implementation of most TYPO3 installations. ' .
360 'However, many custom fields using an RTE will probably not have the "images" soft reference parser registered and so the index will be incomplete and not listing all RTEmagic image files. ' .
361 'The consequence of this limitation is that you should be careful if you wish to delete lost RTEmagic images - they could be referenced from a field not parsed by the "images" soft reference parser!' .
362 'Another limitation: In theory a RTEmagic image should be used from only one record, however TCEmain does not support this (unfortunately!) so when a record is copied or versionized no new version will be produced. This leads to a usage count of more than one for many RTEmagic images which is also shown in the overview. At this point in time its not considered a bug and there is no fix for it.
363
364 NOTICE: Uses the Reference Index Table (sys_refindex) for analysis. Update it before use!',
365 'headers' => array(
366 'completeFileList' => array('Complete list of used RTEmagic files','Both parent and copy are listed here including usage count (which should in theory all be "1")',1),
367 'RTEmagicFilePairs' => array('Statistical info about RTEmagic files','(copy used as index)',0),
368 'missingFiles' => array('Missing RTEmagic image files','Have either their parent or copy missing (look that up in RTEmagicFilePairs)',3),
369 'lostFiles' => array('Lost RTEmagic files from uploads/','These files you might be able to deleted but only if _all_ RTEmagic images are found by the soft reference parser. If you are using the RTE in third-party extensions it is likely that the soft reference parser is not applied correctly to their RTE and thus these "lost" files actually represent valid RTEmagic images, just not registered.',2),
370 'warnings' => array('Warnings picked up','',2)
371 ),
372 'RTEmagicFilePairs' => array(),
373 'completeFileList' => array(),
374 'missingFiles' => array(),
375 'lostFiles' => array(),
376 'warnings' => array()
377 );
378
379 // Select all RTEmagic files in the reference table (only from soft references of course)
380 $recs = $TYPO3_DB->exec_SELECTgetRows(
381 '*',
382 'sys_refindex',
383 'ref_table='.$TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex').
384 ' AND ref_string LIKE '.$TYPO3_DB->fullQuoteStr('%/RTEmagic%', 'sys_refindex').
385 ' AND softref_key='.$TYPO3_DB->fullQuoteStr('images', 'sys_refindex')
386 );
387
388 // Traverse the files and put into a large table:
389 if (is_array($recs)) {
390 foreach($recs as $rec) {
391 $filename = basename($rec['ref_string']);
392 if (t3lib_div::isFirstPartOfStr($filename,'RTEmagicC_')) {
393 $original = 'RTEmagicP_'.ereg_replace('\.[[:alnum:]]+$','',substr($filename,10));
394 $infoString = $rec['tablename'].':'.$rec['recuid'].':'.$rec['field'].':'.$rec['flexpointer'].':'.$rec['softref_key'];
395
396 // Build index:
397 $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['exists'] = @is_file(PATH_site.$rec['ref_string']);
398 $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original'] = substr($rec['ref_string'],0,-strlen($filename)).$original;
399 $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original_exists'] = @is_file(PATH_site.$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original']);
400 $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['count']++;
401 $resultArray['RTEmagicFilePairs'][$rec['ref_string']]['usedIn'][$rec['hash']] = $infoString;
402
403 $resultArray['completeFileList'][$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original']]++;
404 $resultArray['completeFileList'][$rec['ref_string']]++;
405
406 // Missing files:
407 if (!$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['exists'] || !$resultArray['RTEmagicFilePairs'][$rec['ref_string']]['original_exists']) {
408 $resultArray['missingFiles'][$rec['ref_string']] = $rec['ref_string'];
409 }
410 }
411 }
412 }
413
414 // Now, ask for RTEmagic files inside uploads/ folder:
415 $resLostFiles = $this->clean_lost_files_analyze();
416
417 if (is_array($resLostFiles['RTEmagicFiles'])) {
418 foreach($resLostFiles['RTEmagicFiles'] as $fileName) {
419 if (!isset($resultArray['completeFileList'][$fileName])) {
420 $resultArray['lostFiles'][] = $fileName;
421 }
422 }
423 }
424
425 return $resultArray;
426 }
427
428 /**
429 * Find lost files in uploads/ folder
430 *
431 * TODO: Add parameter to exclude filepath
432 * TODO: Add parameter to list more file names/patterns to ignore
433 * TODO: Add parameter to include RTEmagic images
434 *
435 * @return void
436 */
437 function clean_lost_files_analyze() {
438 global $TYPO3_DB;
439
440 // Initialize result array:
441 $resultArray = array(
442 'message' => '
443 Objective: Looking for files in the uploads/ folder which does not have a reference in TYPO3 managed records.
444 Assumptions:
445 - a perfect integrity of the reference index table (always update the reference index table before using this tool!)
446 - that all contents in the uploads folder are files attached to TCA records and exclusively managed by TCEmain through "group" type fields
447 - exceptions are: index.html and .htaccess files (ignored)
448 - exceptions are: RTEmagic* image files (ignored)
449 - files found in deleted records are included (otherwise you would see a false list of lost files)
450 The assumptions are not requirements by the TYPO3 API but reflects the de facto implementation of most TYPO3 installations and therefore a practical approach to cleaning up the uploads/ folder. ' .
451 'Therefore, if all "group" type fields in TCA and flexforms are positioned inside the uploads/ folder and if no files inside are managed manually it should be safe to clean out files with no relations found in the system. ' .
452 'Under such circumstances there should theoretically be no lost files in the uploads/ folder since TCEmain should have managed relations automatically including adding and deleting files. ' .
453 'However, there is at least one reason known to why files might be found lost and that is when FlexForms are used. In such a case a change in the data structure used for the flexform could leave lost files behind. ' .
454 'Another scenario could of course be de-installation of extensions which managed files in the uploads/ folders.
455
456 NOTICE: Uses the Reference Index Table (sys_refindex) for analysis. Update it before use!',
457 'headers' => array(
458 'managedFiles' => array('Files related to TYPO3 records and managed by TCEmain','These files you definitely want to keep.',0),
459 'ignoredFiles' => array('Ignored files (index.html, .htaccess etc.)','These files are allowed in uploads/ folder',0),
460 'RTEmagicFiles' => array('RTE magic images - those found (and ignored)','These files are also allowed in some uploads/ folders as RTEmagic images.',0),
461 'lostFiles' => array('Lost files - those you can delete','You can delete these files!',3),
462 'warnings' => array('Warnings picked up','',2)
463 ),
464 'managedFiles' => array(),
465 'ignoredFiles' => array(),
466 'RTEmagicFiles' => array(),
467 'lostFiles' => array(),
468 'warnings' => array()
469 );
470
471 // Get all files:
472 $fileArr = array();
473 $fileArr = t3lib_div::getAllFilesAndFoldersInPath($fileArr,PATH_site.'uploads/');
474 $fileArr = t3lib_div::removePrefixPathFromList($fileArr,PATH_site);
475
476 // Traverse files and for each, look up if its found in the reference index.
477 foreach($fileArr as $key => $value) {
478
479 // First, allow "index.html", ".htaccess" files since they are often used for good reasons
480 if (substr($value,-11) == '/index.html' || substr($value,-10) == '/.htaccess') {
481 unset($fileArr[$key]) ;
482 $resultArray['ignoredFiles'][] = $value;
483 } else {
484 // Looking for a reference from a field which is NOT a soft reference (thus, only fields with a proper TCA/Flexform configuration)
485 $recs = $TYPO3_DB->exec_SELECTgetRows(
486 '*',
487 'sys_refindex',
488 'ref_table='.$TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex').
489 ' AND ref_string='.$TYPO3_DB->fullQuoteStr($value, 'sys_refindex').
490 ' AND softref_key='.$TYPO3_DB->fullQuoteStr('', 'sys_refindex')
491 );
492
493 // If found, unset entry:
494 if (count($recs)) {
495 unset($fileArr[$key]) ;
496 $resultArray['managedFiles'][] = $value;
497 if (count($recs)>1) {
498 $resultArray['warnings'][]='Warning: File "'.$value.'" had '.count($recs).' references from group-fields, should have only one!';
499 }
500 } else {
501 // When here it means the file was not found. So we test if it has a RTEmagic-image name and if so, we allow it:
502 if (ereg('^RTEmagic[P|C]_',basename($value))) {
503 unset($fileArr[$key]) ;
504 $resultArray['RTEmagicFiles'][] = $value;
505 } else {
506 // We conclude that the file is lost...:
507 unset($fileArr[$key]) ;
508 $resultArray['lostFiles'][] = $value;
509 }
510 }
511 }
512 }
513
514 // $fileArr variable should now be empty with all contents transferred to the result array keys.
515
516 return $resultArray;
517 }
518
519 /**
520 * Find orphan records
521 * VERY CPU and memory intensive since it will look up the whole page tree!
522 *
523 * @return void
524 */
525 function orphan_records_analyze() {
526 global $TYPO3_DB;
527
528 $adminObj = t3lib_div::makeInstance('t3lib_admin');
529
530 $adminObj->genTree_includeDeleted = TRUE; // if set, genTree() includes deleted pages. This is default.
531 $adminObj->genTree_includeVersions = TRUE; // if set, genTree() includes verisonized pages/records. This is default.
532 $adminObj->genTree_includeRecords = TRUE; // if set, genTree() includes records from pages.
533 $adminObj->perms_clause = ''; // extra where-clauses for the tree-selection
534 $adminObj->genTree_makeHTML = 0; // if set, genTree() generates HTML, that visualizes the tree.
535
536 $pt = t3lib_div::milliseconds();
537 $adminObj->genTree(1,'');
538
539 print_r($adminObj->recStats);
540 echo strlen(serialize($adminObj->recStats)).chr(10);
541 echo (t3lib_div::milliseconds()-$pt).' milliseconds';
542 exit;
543
544 return $resultArray;
545 }
546
547
548
549
550
551
552
553
554
555
556
557 /**************************
558 *
559 * Helper functions
560 *
561 *************************/
562
563 /**
564 * Formats a result array from a test so it fits HTML output
565 *
566 * @param string name of the test (eg. function name)
567 * @param array Result array from an analyze function
568 * @param boolean Silent flag, if set, will only output when the result array contains data in arrays.
569 * @param integer Detail level: 0=all, 1=info and greater, 2=warnings and greater, 3=errors
570 * @return string HTML
571 */
572 function html_printInfo($header,$res,$silent=FALSE,$detailLevel=0) {
573
574 if (!$silent) {
575 // Name:
576 $output.= '<h3>'.htmlspecialchars($header).'</h3>';
577
578 // Message:
579 $output.= nl2br(htmlspecialchars(trim($res['message']))).'<hr/>';
580 }
581
582 // Traverse headers for output:
583 if (is_array($res['headers'])) {
584 foreach($res['headers'] as $key => $value) {
585
586 if ($detailLevel <= intval($value[2])) {
587 if (!$silent || (is_array($res[$key]) && count($res[$key]))) {
588 // Header and explanaion:
589 $output.= '<b>'.
590 ($silent ? '<i>'.htmlspecialchars($header).'</i><br/>' : '').
591 (is_array($res[$key]) && count($res[$key]) ? $GLOBALS['SOBE']->doc->icons($value[2]) : '').
592 htmlspecialchars($value[0]).
593 '</b><br/>';
594 if (trim($value[1])) {
595 $output.= '<em>'.htmlspecialchars(trim($value[1])).'</em><br/>';
596 }
597 $output.='<br/>';
598 }
599
600 // Content:
601 if (is_array($res[$key])) {
602 if (count($res[$key])) {
603 $output.= t3lib_div::view_array($res[$key]).'<br/><br/>';
604 } else {
605 if (!$silent) $output.= '(None)'.'<br/><br/>';
606 }
607 } else {
608 if (!$silent) $output.= htmlspecialchars($res[$key]).'<br/><br/>';
609 }
610 }
611 }
612 }
613
614 return $output;
615 }
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630 /**************************
631 *
632 * CLI functionality
633 *
634 *************************/
635
636 /**
637 * CLI engine
638 *
639 * @param array Command line arguments
640 * @return string
641 */
642 function cli_main($argv) {
643
644 if (in_array('-h',$argv)) {
645 echo "
646 Options:
647 -h = This help screen.
648 ";
649 exit;
650 }
651
652
653 # $silentFlag = TRUE;
654 $filter = 1;
655
656 // Missing files:
657 # $res = $this->missing_files_analyze();
658 # $this->cli_printInfo('missing_files_analyze()', $res, $silentFlag, $filter);
659
660 // Missing relations:
661 # $res = $this->missing_relations_analyze();
662 # $this->cli_printInfo('missing_relations_analyze()', $res, $silentFlag, $filter);
663
664 // Double references
665 # $res = $this->double_files_analyze();
666 # $this->cli_printInfo('double_files_analyze()', $res, $silentFlag, $filter);
667
668 // RTE images
669 # $res = $this->RTEmagic_files_analyze();
670 # $this->cli_printInfo('RTEmagic_files_analyze()', $res, $silentFlag, $filter);
671
672 // Lost files:
673 # $res = $this->clean_lost_files_analyze();
674 # $this->cli_printInfo('clean_lost_files_analyze()', $res, $silentFlag, $filter);
675
676 $res = $this->orphan_records_analyze();
677 $this->cli_printInfo('orphan_records_analyze()', $res, $silentFlag, $filter);
678
679 # ob_start();
680 # $output.= ob_get_contents().chr(10);
681 # ob_end_clean();
682 }
683
684 /**
685 * Formats a result array from a test so it fits output in the shell
686 *
687 * @param string name of the test (eg. function name)
688 * @param array Result array from an analyze function
689 * @param boolean Silent flag, if set, will only output when the result array contains data in arrays.
690 * @param integer Detail level: 0=all, 1=info and greater, 2=warnings and greater, 3=errors
691 * @return void Outputs with echo - capture content with output buffer if needed.
692 */
693 function cli_printInfo($header,$res,$silent=FALSE,$detailLevel=0) {
694
695 if (!$silent) {
696 // Name:
697 echo chr(10).'*********************************************'.chr(10).$header.chr(10).'*********************************************'.chr(10);
698
699 // Message:
700 echo trim($res['message']).chr(10).chr(10);
701 }
702
703 // Traverse headers for output:
704 if (is_array($res['headers'])) {
705 foreach($res['headers'] as $key => $value) {
706
707 if ($detailLevel <= intval($value[2])) {
708 if (!$silent || (is_array($res[$key]) && count($res[$key]))) {
709 // Header and explanaion:
710 echo '---------------------------------------------'.chr(10).
711 ($silent ? '['.$header.']'.chr(10) : '').
712 $value[0].' ['.$value[2].']'.chr(10).
713 '---------------------------------------------'.chr(10);
714 if (trim($value[1])) {
715 echo '[Explanation: '.trim($value[1]).']'.chr(10);
716 }
717 }
718
719 // Content:
720 if (is_array($res[$key])) {
721 if (count($res[$key])) {
722 print_r($res[$key]);
723 } else {
724 if (!$silent) echo '(None)'.chr(10).chr(10);
725 }
726 } else {
727 if (!$silent) echo $res[$key].chr(10).chr(10);
728 }
729 }
730 }
731 }
732 }
733 }
734
735 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/lowlevel/class.tx_lowlevel_cleaner.php']) {
736 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/lowlevel/class.tx_lowlevel_cleaner.php']);
737 }
738 ?>