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