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