Fixed issue #15601: Change all core PHP files to UTF8 encoding
[Packages/TYPO3.CMS.git] / typo3 / sysext / lowlevel / class.tx_lowlevel_cleaner_core.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2010 Kasper Skårhøj (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 * $Id$
31 *
32 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
33 */
34 /**
35 * [CLASS/FUNCTION INDEX of SCRIPT]
36 *
37 *
38 *
39 * 71: class tx_lowlevel_cleaner_core extends t3lib_cli
40 * 88: function tx_lowlevel_cleaner_core()
41 *
42 * SECTION: CLI functionality
43 * 134: function cli_main($argv)
44 * 193: function cli_referenceIndexCheck()
45 * 228: function cli_noExecutionCheck($matchString)
46 * 251: function cli_printInfo($header,$res)
47 *
48 * SECTION: Page tree traversal
49 * 331: function genTree($rootID,$depth=1000,$echoLevel=0,$callBack='')
50 * 369: function genTree_traverse($rootID,$depth,$echoLevel=0,$callBack='',$versionSwapmode='',$rootIsVersion=0,$accumulatedPath='')
51 *
52 * SECTION: Helper functions
53 * 554: function infoStr($rec)
54 *
55 * TOTAL FUNCTIONS: 8
56 * (This index is automatically created/updated by the extension "extdeveval")
57 *
58 */
59
60
61 /**
62 * Core functions for cleaning and analysing
63 *
64 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
65 * @package TYPO3
66 * @subpackage tx_lowlevel
67 */
68 class tx_lowlevel_cleaner_core extends t3lib_cli {
69
70 var $genTree_traverseDeleted = TRUE;
71 var $genTree_traverseVersions = TRUE;
72
73
74
75 var $label_infoString = 'The list of records is organized as [table]:[uid]:[field]:[flexpointer]:[softref_key]';
76 var $pagetreePlugins = array();
77 var $cleanerModules = array();
78
79 var $performanceStatistics = array();
80
81
82 /**
83 * Constructor
84 *
85 * @return void
86 */
87 function tx_lowlevel_cleaner_core() {
88
89 // Running parent class constructor
90 parent::t3lib_cli();
91
92 $this->cleanerModules = (array)$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['lowlevel']['cleanerModules'];
93
94 // Adding options to help archive:
95 $this->cli_options[] = array('-r', 'Execute this tool, otherwise help is shown');
96 $this->cli_options[] = array('-v level', 'Verbosity level 0-3', "The value of level can be:\n 0 = all output\n 1 = info and greater (default)\n 2 = warnings and greater\n 3 = errors");
97 $this->cli_options[] = array('--refindex mode', 'Mode for reference index handling for operations that require a clean reference index ("update"/"ignore")', 'Options are "check" (default), "update" and "ignore". By default, the reference index is checked before running analysis that require a clean index. If the check fails, the analysis is not run. You can choose to bypass this completely (using value "ignore") or ask to have the index updated right away before the analysis (using value "update")');
98 $this->cli_options[] = array('--AUTOFIX [testName]', 'Repairs errors that can be automatically fixed.', 'Only add this option after having run the test without it so you know what will happen when you add this option! The optional parameter "[testName]" works for some tool keys to limit the fixing to a particular test.');
99 $this->cli_options[] = array('--dryrun', 'With --AUTOFIX it will only simulate a repair process','You may like to use this to see what the --AUTOFIX option will be doing. It will output the whole process like if a fix really occurred but nothing is in fact happening');
100 $this->cli_options[] = array('--YES', 'Implicit YES to all questions','Use this with EXTREME care. The option "-i" is not affected by this option.');
101 $this->cli_options[] = array('-i', 'Interactive','Will ask you before running the AUTOFIX on each element.');
102 $this->cli_options[] = array('--filterRegex expr', 'Define an expression for preg_match() that must match the element ID in order to auto repair it','The element ID is the string in quotation marks when the text \'Cleaning ... in "ELEMENT ID"\'. "expr" is the expression for preg_match(). To match for example "Nature3.JPG" and "Holiday3.JPG" you can use "/.*3.JPG/". To match for example "Image.jpg" and "Image.JPG" you can use "/.*.jpg/i". Try a --dryrun first to see what the matches are!');
103 $this->cli_options[] = array('--showhowto', 'Displays HOWTO file for cleaner script.');
104
105 // Setting help texts:
106 $this->cli_help['name'] = 'lowlevel_cleaner -- Analysis and clean-up tools for TYPO3 installations';
107 $this->cli_help['synopsis'] = 'toolkey ###OPTIONS###';
108 $this->cli_help['description'] = "Dispatches to various analysis and clean-up tools which can plug into the API of this script. Typically you can run tests that will take longer than the usual max execution time of PHP. Such tasks could be checking for orphan records in the page tree or flushing all published versions in the system. For the complete list of options, please explore each of the 'toolkey' keywords below:\n\n ".implode("\n ",array_keys($this->cleanerModules));
109 $this->cli_help['examples'] = "/.../cli_dispatch.phpsh lowlevel_cleaner missing_files -s -r\nThis will show you missing files in the TYPO3 system and only report back if errors were found.";
110 $this->cli_help['author'] = "Kasper Skaarhoej, (c) 2006";
111 }
112
113
114
115
116
117
118
119
120
121 /**************************
122 *
123 * CLI functionality
124 *
125 *************************/
126
127 /**
128 * CLI engine
129 *
130 * @param array Command line arguments
131 * @return string
132 */
133 function cli_main($argv) {
134
135 // Force user to admin state and set workspace to "Live":
136 $GLOBALS['BE_USER']->user['admin'] = 1;
137 $GLOBALS['BE_USER']->setWorkspace(0);
138
139 // Print Howto:
140 if ($this->cli_isArg('--showhowto')) {
141 $howto = t3lib_div::getUrl(t3lib_extMgm::extPath('lowlevel').'HOWTO_clean_up_TYPO3_installations.txt');
142 echo wordwrap($howto,120).LF;
143 exit;
144 }
145
146 // Print help
147 $analysisType = (string)$this->cli_args['_DEFAULT'][1];
148 if (!$analysisType) {
149 $this->cli_validateArgs();
150 $this->cli_help();
151 exit;
152 }
153
154 // Analysis type:
155 switch((string)$analysisType) {
156 default:
157 if (is_array($this->cleanerModules[$analysisType])) {
158 $cleanerMode = t3lib_div::getUserObj($this->cleanerModules[$analysisType][0]);
159 $cleanerMode->cli_validateArgs();
160
161 if ($this->cli_isArg('-r')) { // Run it...
162 if (!$cleanerMode->checkRefIndex || $this->cli_referenceIndexCheck()) {
163 $res = $cleanerMode->main();
164 $this->cli_printInfo($analysisType, $res);
165
166 // Autofix...
167 if ($this->cli_isArg('--AUTOFIX')) {
168 if ($this->cli_isArg('--YES') || $this->cli_keyboardInput_yes("\n\nNOW Running --AUTOFIX on result. OK?".($this->cli_isArg('--dryrun')?' (--dryrun simulation)':''))) {
169 $cleanerMode->main_autofix($res);
170 } else {
171 $this->cli_echo("ABORTING AutoFix...\n",1);
172 }
173 }
174 }
175 } else { // Help only...
176 $cleanerMode->cli_help();
177 exit;
178 }
179 } else {
180 $this->cli_echo("ERROR: Analysis Type '".$analysisType."' is unknown.\n",1);
181 exit;
182 }
183 break;
184 }
185 }
186
187 /**
188 * Checks reference index
189 *
190 * @return boolean TRUE if reference index was OK (either OK, updated or ignored)
191 */
192 function cli_referenceIndexCheck() {
193
194 // Reference index option:
195 $refIndexMode = isset($this->cli_args['--refindex']) ? $this->cli_args['--refindex'][0] : 'check';
196 if (!t3lib_div::inList('update,ignore,check', $refIndexMode)) {
197 $this->cli_echo("ERROR: Wrong value for --refindex argument.\n",1);
198 exit;
199 }
200
201 switch($refIndexMode) {
202 case 'check':
203 case 'update':
204 $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
205 list($headerContent,$bodyContent,$errorCount) = $refIndexObj->updateIndex($refIndexMode=='check',$this->cli_echo());
206
207 if ($errorCount && $refIndexMode=='check') {
208 $ok = FALSE;
209 $this->cli_echo("ERROR: Reference Index Check failed! (run with '--refindex update' to fix)\n",1);
210 } else {
211 $ok = TRUE;
212 }
213 break;
214 case 'ignore':
215 $this->cli_echo("Reference Index Check: Bypassing reference index check...\n");
216 $ok = TRUE;
217 break;
218 }
219
220 return $ok;
221 }
222
223 /**
224 * @param [type] $matchString: ...
225 * @return string If string, it's the reason for not executing. Returning FALSE means it should execute.
226 */
227 function cli_noExecutionCheck($matchString) {
228
229 // Check for filter:
230 if ($this->cli_isArg('--filterRegex') && $regex = $this->cli_argValue('--filterRegex',0)) {
231 if (!preg_match($regex,$matchString)) return 'BYPASS: Filter Regex "'.$regex.'" did not match string "'.$matchString.'"';
232 }
233 // Check for interactive mode
234 if ($this->cli_isArg('-i')) {
235 if (!$this->cli_keyboardInput_yes(' EXECUTE?')) {
236 return 'BYPASS...';
237 }
238 }
239 // Check for
240 if ($this->cli_isArg('--dryrun')) return 'BYPASS: --dryrun set';
241 }
242
243 /**
244 * Formats a result array from a test so it fits output in the shell
245 *
246 * @param string name of the test (eg. function name)
247 * @param array Result array from an analyze function
248 * @return void Outputs with echo - capture content with output buffer if needed.
249 */
250 function cli_printInfo($header,$res) {
251
252 $detailLevel = t3lib_div::intInRange($this->cli_isArg('-v') ? $this->cli_argValue('-v') : 1,0,3);
253 $silent = !$this->cli_echo();
254
255 $severity = array(
256 0 => 'MESSAGE',
257 1 => 'INFO',
258 2 => 'WARNING',
259 3 => 'ERROR',
260 );
261
262 // Header output:
263 if ($detailLevel <= 1) {
264 $this->cli_echo(
265 "*********************************************\n".
266 $header.LF.
267 "*********************************************\n");
268 $this->cli_echo(wordwrap(trim($res['message'])).LF.LF);
269 }
270
271 // Traverse headers for output:
272 if (is_array($res['headers'])) {
273 foreach($res['headers'] as $key => $value) {
274
275 if ($detailLevel <= intval($value[2])) {
276 if (is_array($res[$key]) && (count($res[$key]) || !$silent)) {
277
278 // Header and explanaion:
279 $this->cli_echo('---------------------------------------------'.LF,1);
280 $this->cli_echo('['.$header.']'.LF,1);
281 $this->cli_echo($value[0].' ['.$severity[$value[2]].']'.LF,1);
282 $this->cli_echo('---------------------------------------------'.LF,1);
283 if (trim($value[1])) {
284 $this->cli_echo('Explanation: '.wordwrap(trim($value[1])).LF.LF,1);
285 }
286 }
287
288 // Content:
289 if (is_array($res[$key])) {
290 if (count($res[$key])) {
291 if ($this->cli_echo('',1)) { print_r($res[$key]); }
292 } else {
293 $this->cli_echo('(None)'.LF.LF);
294 }
295 } else {
296 $this->cli_echo($res[$key].LF.LF);
297 }
298 }
299 }
300 }
301 }
302
303
304
305
306
307
308
309
310
311
312
313
314 /**************************
315 *
316 * Page tree traversal
317 *
318 *************************/
319
320 /**
321 * Traverses the FULL/part of page tree, mainly to register ALL validly connected records (to find orphans) but also to register deleted records, versions etc.
322 * Output (in $this->recStats) can be useful for multiple purposes.
323 *
324 * @param integer Root page id from where to start traversal. Use "0" (zero) to have full page tree (necessary when spotting orphans, otherwise you can run it on parts only)
325 * @param integer Depth to traverse. zero is do not traverse at all. 1 = 1 sublevel, 1000= 1000 sublevels (all...)
326 * @param boolean If >0, will echo information about the traversal process.
327 * @param string Call back function (from this class or subclass)
328 * @return void
329 */
330 function genTree($rootID,$depth=1000,$echoLevel=0,$callBack='') {
331
332 $pt = t3lib_div::milliseconds();$this->performanceStatistics['genTree()']='';
333
334 // Initialize:
335 $this->workspaceIndex = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title','sys_workspace','1=1'.t3lib_BEfunc::deleteClause('sys_workspace'),'','','','uid');
336 $this->workspaceIndex[-1] = TRUE;
337 $this->workspaceIndex[0] = TRUE;
338
339 $this->recStats = array(
340 'all' => array(), // All records connected in tree including versions (the reverse are orphans). All Info and Warning categories below are included here (and therefore safe if you delete the reverse of the list)
341 'deleted' => array(), // Subset of "alL" that are deleted-flagged [Info]
342 'versions' => array(), // Subset of "all" which are offline versions (pid=-1). [Info]
343 'versions_published' => array(), // Subset of "versions" that is a count of 1 or more (has been published) [Info]
344 'versions_liveWS' => array(), // Subset of "versions" that exists in live workspace [Info]
345 'versions_lost_workspace' => array(), // Subset of "versions" that doesn't belong to an existing workspace [Warning: Fix by move to live workspace]
346 'versions_inside_versioned_page' => array(), // Subset of "versions" This is versions of elements found inside an already versioned branch / page. In real life this can work out, but is confusing and the backend should prevent this from happening to people. [Warning: Fix by deleting those versions (or publishing them)]
347 'illegal_record_under_versioned_page' => array(), // If a page is "element" or "page" version and records are found attached to it, they might be illegally attached, so this will tell you. [Error: Fix by deleting orphans since they are not registered in "all" category]
348 'misplaced_at_rootlevel' => array(), // Subset of "all": Those that should not be at root level but are. [Warning: Fix by moving record into page tree]
349 'misplaced_inside_tree' => array(), // Subset of "all": Those that are inside page tree but should be at root level [Warning: Fix by setting PID to zero]
350 );
351
352 // Start traversal:
353 $pt2 = t3lib_div::milliseconds();$this->performanceStatistics['genTree_traverse()']=''; $this->performanceStatistics['genTree_traverse():TraverseTables']='';
354 $this->genTree_traverse($rootID,$depth,$echoLevel,$callBack);
355 $this->performanceStatistics['genTree_traverse()'] = t3lib_div::milliseconds()-$pt2;
356
357 // Sort recStats (for diff'able displays)
358 foreach($this->recStats as $kk => $vv) {
359 foreach($this->recStats[$kk] as $tables => $recArrays) {
360 ksort($this->recStats[$kk][$tables]);
361 }
362 ksort($this->recStats[$kk]);
363 }
364
365 if ($echoLevel>0) echo LF.LF;
366
367
368 // Processing performance statistics:
369 $this->performanceStatistics['genTree()'] = t3lib_div::milliseconds()-$pt;
370
371 // Count records:
372 foreach($GLOBALS['TCA'] as $tableName => $cfg) {
373 // Select all records belonging to page:
374 $resSub = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
375 'count(*)',
376 $tableName,
377 ''
378 );
379 $countRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($resSub);
380 $this->performanceStatistics['MySQL_count'][$tableName]=$countRow['count(*)'];
381 $this->performanceStatistics['CSV'].=LF.$tableName.','.
382 $this->performanceStatistics['genTree_traverse():TraverseTables:']['MySQL'][$tableName].','.
383 $this->performanceStatistics['genTree_traverse():TraverseTables:']['Proc'][$tableName].','.
384 $this->performanceStatistics['MySQL_count'][$tableName];
385 }
386
387 $this->performanceStatistics['recStats_size']['(ALL)']=strlen(serialize($this->recStats));
388 foreach($this->recStats as $key => $arrcontent) {
389 $this->performanceStatistics['recStats_size'][$key]=strlen(serialize($arrcontent));
390 }
391 }
392
393 /**
394 * Recursive traversal of page tree:
395 *
396 * @param integer Page root id (must be online, valid page record - or zero for page tree root)
397 * @param integer Depth
398 * @param integer Echo Level
399 * @param string Call back function (from this class or subclass)
400 * @param string DON'T set from outside, internal. (indicates we are inside a version of a page)
401 * @param integer DON'T set from outside, internal. (1: Indicates that rootID is a version of a page, 2: ...that it is even a version of a version (which triggers a warning!)
402 * @param string Internal string that accumulates the path
403 * @return void
404 * @access private
405 */
406 function genTree_traverse($rootID,$depth,$echoLevel=0,$callBack='',$versionSwapmode='',$rootIsVersion=0,$accumulatedPath='') {
407
408 // Register page:
409 $this->recStats['all']['pages'][$rootID] = $rootID;
410 $pageRecord = t3lib_BEfunc::getRecordRaw('pages','uid='.intval($rootID),'deleted,title,t3ver_count,t3ver_wsid');
411 $accumulatedPath.='/'.$pageRecord['title'];
412
413 // Register if page is deleted:
414 if ($pageRecord['deleted']) {
415 $this->recStats['deleted']['pages'][$rootID] = $rootID;
416 }
417 // If rootIsVersion is set it means that the input rootID is that of a version of a page. See below where the recursive call is made.
418 if ($rootIsVersion) {
419 $this->recStats['versions']['pages'][$rootID] = $rootID;
420 if ($pageRecord['t3ver_count']>=1 && $pageRecord['t3ver_wsid']==0) { // If it has been published and is in archive now...
421 $this->recStats['versions_published']['pages'][$rootID] = $rootID;
422 }
423 if ($pageRecord['t3ver_wsid']==0) { // If it has been published and is in archive now...
424 $this->recStats['versions_liveWS']['pages'][$rootID] = $rootID;
425 }
426 if (!isset($this->workspaceIndex[$pageRecord['t3ver_wsid']])) { // If it doesn't belong to a workspace...
427 $this->recStats['versions_lost_workspace']['pages'][$rootID] = $rootID;
428 }
429 if ($rootIsVersion==2) { // In case the rootID is a version inside a versioned page
430 $this->recStats['versions_inside_versioned_page']['pages'][$rootID] = $rootID;
431 }
432 }
433
434 if ($echoLevel>0)
435 echo LF.$accumulatedPath.' ['.$rootID.']'.
436 ($pageRecord['deleted'] ? ' (DELETED)':'').
437 ($this->recStats['versions_published']['pages'][$rootID] ? ' (PUBLISHED)':'')
438 ;
439 if ($echoLevel>1 && $this->recStats['versions_lost_workspace']['pages'][$rootID])
440 echo LF.' ERROR! This version belongs to non-existing workspace ('.$pageRecord['t3ver_wsid'].')!';
441 if ($echoLevel>1 && $this->recStats['versions_inside_versioned_page']['pages'][$rootID])
442 echo LF.' WARNING! This version is inside an already versioned page or branch!';
443
444 // Call back:
445 if ($callBack) {
446 $this->$callBack('pages',$rootID,$echoLevel,$versionSwapmode,$rootIsVersion);
447 }
448
449 $pt3 = t3lib_div::milliseconds();
450
451 // Traverse tables of records that belongs to page:
452 foreach($GLOBALS['TCA'] as $tableName => $cfg) {
453 if ($tableName!='pages') {
454
455 // Select all records belonging to page:
456 $pt4=t3lib_div::milliseconds();
457 $resSub = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
458 'uid'.($GLOBALS['TCA'][$tableName]['ctrl']['delete']?','.$GLOBALS['TCA'][$tableName]['ctrl']['delete']:''),
459 $tableName,
460 'pid='.intval($rootID).
461 ($this->genTree_traverseDeleted ? '' : t3lib_BEfunc::deleteClause($tableName))
462 );
463 $this->performanceStatistics['genTree_traverse():TraverseTables:']['MySQL']['(ALL)']+= t3lib_div::milliseconds()-$pt4;
464 $this->performanceStatistics['genTree_traverse():TraverseTables:']['MySQL'][$tableName]+= t3lib_div::milliseconds()-$pt4;
465
466 $pt5=t3lib_div::milliseconds();
467 $count = $GLOBALS['TYPO3_DB']->sql_num_rows($resSub);
468 if ($count) {
469 if ($echoLevel==2) echo LF.' \-'.$tableName.' ('.$count.')';
470 }
471
472 while ($rowSub = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($resSub)) {
473 if ($echoLevel==3) echo LF.' \-'.$tableName.':'.$rowSub['uid'];
474
475 // If the rootID represents an "element" or "page" version type, we must check if the record from this table is allowed to belong to this:
476 if ($versionSwapmode=='SWAPMODE:-1' || ($versionSwapmode=='SWAPMODE:0' && !$GLOBALS['TCA'][$tableName]['ctrl']['versioning_followPages'])) {
477 // This is illegal records under a versioned page - therefore not registered in $this->recStats['all'] so they should be orphaned:
478 $this->recStats['illegal_record_under_versioned_page'][$tableName][$rowSub['uid']] = $rowSub['uid'];
479 if ($echoLevel>1) echo LF.' ERROR! Illegal record ('.$tableName.':'.$rowSub['uid'].') under versioned page!';
480 } else {
481 $this->recStats['all'][$tableName][$rowSub['uid']] = $rowSub['uid'];
482
483 // Register deleted:
484 if ($GLOBALS['TCA'][$tableName]['ctrl']['delete'] && $rowSub[$GLOBALS['TCA'][$tableName]['ctrl']['delete']]) {
485 $this->recStats['deleted'][$tableName][$rowSub['uid']] = $rowSub['uid'];
486 if ($echoLevel==3) echo ' (DELETED)';
487 }
488
489 // Check location of records regarding tree root:
490 if (!$GLOBALS['TCA'][$tableName]['ctrl']['rootLevel'] && $rootID==0) {
491 $this->recStats['misplaced_at_rootlevel'][$tableName][$rowSub['uid']] = $rowSub['uid'];
492 if ($echoLevel>1) echo LF.' ERROR! Misplaced record ('.$tableName.':'.$rowSub['uid'].') on rootlevel!';
493 }
494 if ($GLOBALS['TCA'][$tableName]['ctrl']['rootLevel']==1 && $rootID>0) {
495 $this->recStats['misplaced_inside_tree'][$tableName][$rowSub['uid']] = $rowSub['uid'];
496 if ($echoLevel>1) echo LF.' ERROR! Misplaced record ('.$tableName.':'.$rowSub['uid'].') inside page tree!';
497 }
498
499 // Traverse plugins:
500 if ($callBack) {
501 $this->$callBack($tableName,$rowSub['uid'],$echoLevel,$versionSwapmode,$rootIsVersion);
502 }
503
504 // Add any versions of those records:
505 if ($this->genTree_traverseVersions) {
506 $versions = t3lib_BEfunc::selectVersionsOfRecord($tableName, $rowSub['uid'], 'uid,t3ver_wsid,t3ver_count'.($GLOBALS['TCA'][$tableName]['ctrl']['delete']?','.$GLOBALS['TCA'][$tableName]['ctrl']['delete']:''), 0, TRUE);
507 if (is_array($versions)) {
508 foreach($versions as $verRec) {
509 if (!$verRec['_CURRENT_VERSION']) {
510 if ($echoLevel==3) echo LF.' \-[#OFFLINE VERSION: WS#'.$verRec['t3ver_wsid'].'/Cnt:'.$verRec['t3ver_count'].'] '.$tableName.':'.$verRec['uid'].')';
511 $this->recStats['all'][$tableName][$verRec['uid']] = $verRec['uid'];
512
513 // Register deleted:
514 if ($GLOBALS['TCA'][$tableName]['ctrl']['delete'] && $verRec[$GLOBALS['TCA'][$tableName]['ctrl']['delete']]) {
515 $this->recStats['deleted'][$tableName][$verRec['uid']] = $verRec['uid'];
516 if ($echoLevel==3) echo ' (DELETED)';
517 }
518
519 // Register version:
520 $this->recStats['versions'][$tableName][$verRec['uid']] = $verRec['uid'];
521 if ($verRec['t3ver_count']>=1 && $verRec['t3ver_wsid']==0) { // Only register published versions in LIVE workspace (published versions in draft workspaces are allowed)
522 $this->recStats['versions_published'][$tableName][$verRec['uid']] = $verRec['uid'];
523 if ($echoLevel==3) echo ' (PUBLISHED)';
524 }
525 if ($verRec['t3ver_wsid']==0) {
526 $this->recStats['versions_liveWS'][$tableName][$verRec['uid']] = $verRec['uid'];
527 }
528 if (!isset($this->workspaceIndex[$verRec['t3ver_wsid']])) {
529 $this->recStats['versions_lost_workspace'][$tableName][$verRec['uid']] = $verRec['uid'];
530 if ($echoLevel>1) echo LF.' ERROR! Version ('.$tableName.':'.$verRec['uid'].') belongs to non-existing workspace ('.$verRec['t3ver_wsid'].')!';
531 }
532 if ($versionSwapmode) { // In case we are inside a versioned branch, there should not exists versions inside that "branch".
533 $this->recStats['versions_inside_versioned_page'][$tableName][$verRec['uid']] = $verRec['uid'];
534 if ($echoLevel>1) echo LF.' ERROR! This version ('.$tableName.':'.$verRec['uid'].') is inside an already versioned page or branch!';
535 }
536
537 // Traverse plugins:
538 if ($callBack) {
539 $this->$callBack($tableName,$verRec['uid'],$echoLevel,$versionSwapmode,$rootIsVersion);
540 }
541 }
542 }
543 }
544 unset($versions);
545 }
546 }
547 }
548
549 $this->performanceStatistics['genTree_traverse():TraverseTables:']['Proc']['(ALL)']+= t3lib_div::milliseconds()-$pt5;
550 $this->performanceStatistics['genTree_traverse():TraverseTables:']['Proc'][$tableName]+= t3lib_div::milliseconds()-$pt5;
551
552 }
553 }
554 unset($resSub);
555 unset($rowSub);
556 $this->performanceStatistics['genTree_traverse():TraverseTables']+= t3lib_div::milliseconds()-$pt3;
557
558 // Find subpages to root ID and traverse (only when rootID is not a version or is a branch-version):
559 if (!$versionSwapmode || $versionSwapmode=='SWAPMODE:1') {
560 if ($depth>0) {
561 $depth--;
562 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
563 'uid',
564 'pages',
565 'pid='.intval($rootID).
566 ($this->genTree_traverseDeleted ? '' : t3lib_BEfunc::deleteClause('pages')),
567 '',
568 'sorting'
569 );
570 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
571 $this->genTree_traverse($row['uid'],$depth,$echoLevel,$callBack,$versionSwapmode,0,$accumulatedPath);
572 }
573 }
574
575 // Add any versions of pages
576 if ($rootID>0 && $this->genTree_traverseVersions) {
577 $versions = t3lib_BEfunc::selectVersionsOfRecord('pages', $rootID, 'uid,t3ver_oid,t3ver_wsid,t3ver_count,t3ver_swapmode', 0, TRUE);
578 if (is_array($versions)) {
579 foreach($versions as $verRec) {
580 if (!$verRec['_CURRENT_VERSION']) {
581 $this->genTree_traverse($verRec['uid'],$depth,$echoLevel,$callBack,'SWAPMODE:'.t3lib_div::intInRange($verRec['t3ver_swapmode'],-1,1),$versionSwapmode?2:1,$accumulatedPath.' [#OFFLINE VERSION: WS#'.$verRec['t3ver_wsid'].'/Cnt:'.$verRec['t3ver_count'].']');
582 }
583 }
584 }
585 }
586 }
587 }
588
589
590
591
592
593
594
595
596 /**************************
597 *
598 * Helper functions
599 *
600 *************************/
601
602 /**
603 * Compile info-string
604 *
605 * @param array Input record from sys_refindex
606 * @return string String identifying the main record of the reference
607 */
608 function infoStr($rec) {
609 return $rec['tablename'].':'.$rec['recuid'].':'.$rec['field'].':'.$rec['flexpointer'].':'.$rec['softref_key'].($rec['deleted'] ? ' (DELETED)':'');
610 }
611 }
612
613 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/lowlevel/class.tx_lowlevel_cleaner.php']) {
614 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/lowlevel/class.tx_lowlevel_cleaner.php']);
615 }
616
617 ?>