TYPO3 CMS  TYPO3_7-6
CleanerCommand.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Lowlevel;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
19 
24 {
28  public $genTree_traverseDeleted = true;
29 
34 
38  public $label_infoString = 'The list of records is organized as [table]:[uid]:[field]:[flexpointer]:[softref_key]';
39 
43  public $pagetreePlugins = [];
44 
48  public $cleanerModules = [];
49 
54 
58  protected $workspaceIndex = [];
59 
63  public function __construct()
64  {
65  // Running parent class constructor
66  parent::__construct();
67  $this->cleanerModules = (array)$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['lowlevel']['cleanerModules'];
68  // Adding options to help archive:
69  $this->cli_options[] = ['-r', 'Execute this tool, otherwise help is shown'];
70  $this->cli_options[] = ['-v level', 'Verbosity level 0-3', 'The value of level can be:
71  0 = all output
72  1 = info and greater (default)
73  2 = warnings and greater
74  3 = errors'];
75  $this->cli_options[] = ['--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")'];
76  $this->cli_options[] = ['--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.'];
77  $this->cli_options[] = ['--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'];
78  $this->cli_options[] = ['--YES', 'Implicit YES to all questions', 'Use this with EXTREME care. The option "-i" is not affected by this option.'];
79  $this->cli_options[] = ['-i', 'Interactive', 'Will ask you before running the AUTOFIX on each element.'];
80  $this->cli_options[] = ['--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!'];
81  $this->cli_options[] = ['--showhowto', 'Displays HOWTO file for cleaner script.'];
82  // Setting help texts:
83  $this->cli_help['name'] = 'lowlevel_cleaner -- Analysis and clean-up tools for TYPO3 installations';
84  $this->cli_help['synopsis'] = 'toolkey ###OPTIONS###';
85  $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:
86 
87  ' . implode('
88  ', array_keys($this->cleanerModules));
89  $this->cli_help['examples'] = '/.../cli_dispatch.phpsh lowlevel_cleaner missing_files -s -r
90 This will show you missing files in the TYPO3 system and only report back if errors were found.';
91  $this->cli_help['author'] = 'Kasper Skaarhoej, (c) 2006';
92  }
93 
94  /**************************
95  *
96  * CLI functionality
97  *
98  *************************/
105  public function cli_main($argv)
106  {
107  $this->cli_setArguments($argv);
108 
109  // Force user to admin state and set workspace to "Live":
110  $GLOBALS['BE_USER']->user['admin'] = 1;
111  $GLOBALS['BE_USER']->setWorkspace(0);
112  // Print Howto:
113  if ($this->cli_isArg('--showhowto')) {
114  $howto = GeneralUtility::getUrl(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('lowlevel') . 'README.rst');
115  echo wordwrap($howto, 120) . LF;
116  die;
117  }
118  // Print help
119  $analysisType = (string)$this->cli_args['_DEFAULT'][1];
120  if (!$analysisType) {
121  $this->cli_validateArgs();
122  $this->cli_help();
123  die;
124  }
125 
126  if (is_array($this->cleanerModules[$analysisType])) {
127  $cleanerMode = GeneralUtility::getUserObj($this->cleanerModules[$analysisType][0]);
128  $cleanerMode->cli_validateArgs();
129  // Run it...
130  if ($this->cli_isArg('-r')) {
131  if (!$cleanerMode->checkRefIndex || $this->cli_referenceIndexCheck()) {
132  $res = $cleanerMode->main();
133  $this->cli_printInfo($analysisType, $res);
134  // Autofix...
135  if ($this->cli_isArg('--AUTOFIX')) {
136  if ($this->cli_isArg('--YES') || $this->cli_keyboardInput_yes('
137 
138 NOW Running --AUTOFIX on result. OK?' . ($this->cli_isArg('--dryrun') ? ' (--dryrun simulation)' : ''))) {
139  $cleanerMode->main_autofix($res);
140  } else {
141  $this->cli_echo('ABORTING AutoFix...
142 ', 1);
143  }
144  }
145  }
146  } else {
147  // Help only...
148  $cleanerMode->cli_help();
149  die;
150  }
151  } else {
152  $this->cli_echo('ERROR: Analysis Type \'' . $analysisType . '\' is unknown.
153 ', 1);
154  die;
155  }
156  }
157 
163  public function cli_referenceIndexCheck()
164  {
165  // Reference index option:
166  $refIndexMode = isset($this->cli_args['--refindex']) ? $this->cli_args['--refindex'][0] : 'check';
167  switch ($refIndexMode) {
168  case 'check':
169 
170  case 'update':
171  $refIndexObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ReferenceIndex::class);
172  list($headerContent, $bodyContent, $errorCount) = $refIndexObj->updateIndex($refIndexMode === 'check', $this->cli_echo());
173  if ($errorCount && $refIndexMode === 'check') {
174  $ok = false;
175  $this->cli_echo('ERROR: Reference Index Check failed! (run with \'--refindex update\' to fix)
176 ', 1);
177  } else {
178  $ok = true;
179  }
180  break;
181  case 'ignore':
182  $this->cli_echo('Reference Index Check: Bypassing reference index check...
183 ');
184  $ok = true;
185  break;
186  default:
187  $this->cli_echo('ERROR: Wrong value for --refindex argument.
188 ', 1);
189  die();
190 
191  }
192  return $ok;
193  }
194 
199  public function cli_noExecutionCheck($matchString)
200  {
201  // Check for filter:
202  if ($this->cli_isArg('--filterRegex') && ($regex = $this->cli_argValue('--filterRegex', 0))) {
203  if (!preg_match($regex, $matchString)) {
204  return 'BYPASS: Filter Regex "' . $regex . '" did not match string "' . $matchString . '"';
205  }
206  }
207  // Check for interactive mode
208  if ($this->cli_isArg('-i')) {
209  if (!$this->cli_keyboardInput_yes(' EXECUTE?')) {
210  return 'BYPASS...';
211  }
212  }
213  // Check for
214  if ($this->cli_isArg('--dryrun')) {
215  return 'BYPASS: --dryrun set';
216  }
217  }
218 
226  public function cli_printInfo($header, $res)
227  {
228  $detailLevel = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($this->cli_isArg('-v') ? $this->cli_argValue('-v') : 1, 0, 3);
229  $silent = !$this->cli_echo();
230  $severity = [
231  0 => 'MESSAGE',
232  1 => 'INFO',
233  2 => 'WARNING',
234  3 => 'ERROR'
235  ];
236  // Header output:
237  if ($detailLevel <= 1) {
238  $this->cli_echo('*********************************************
239 ' . $header . LF . '*********************************************
240 ');
241  $this->cli_echo(wordwrap(trim($res['message'])) . LF . LF);
242  }
243  // Traverse headers for output:
244  if (is_array($res['headers'])) {
245  foreach ($res['headers'] as $key => $value) {
246  if ($detailLevel <= (int)$value[2]) {
247  if (is_array($res[$key]) && (count($res[$key]) || !$silent)) {
248  // Header and explanaion:
249  $this->cli_echo('---------------------------------------------' . LF, 1);
250  $this->cli_echo('[' . $header . ']' . LF, 1);
251  $this->cli_echo($value[0] . ' [' . $severity[$value[2]] . ']' . LF, 1);
252  $this->cli_echo('---------------------------------------------' . LF, 1);
253  if (trim($value[1])) {
254  $this->cli_echo('Explanation: ' . wordwrap(trim($value[1])) . LF . LF, 1);
255  }
256  }
257  // Content:
258  if (is_array($res[$key])) {
259  if (count($res[$key])) {
260  if ($this->cli_echo('', 1)) {
261  print_r($res[$key]);
262  }
263  } else {
264  $this->cli_echo('(None)' . LF . LF);
265  }
266  } else {
267  $this->cli_echo($res[$key] . LF . LF);
268  }
269  }
270  }
271  }
272  }
273 
274  /**************************
275  *
276  * Page tree traversal
277  *
278  *************************/
289  public function genTree($rootID, $depth = 1000, $echoLevel = 0, $callBack = '')
290  {
292  $this->performanceStatistics['genTree()'] = '';
293  // Initialize:
294  if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
295  $this->workspaceIndex = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title', 'sys_workspace', '1=1' . BackendUtility::deleteClause('sys_workspace'), '', '', '', 'uid');
296  }
297  $this->workspaceIndex[-1] = true;
298  $this->workspaceIndex[0] = true;
299  $this->recStats = [
300  'all' => [],
301  // 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)
302  'deleted' => [],
303  // Subset of "alL" that are deleted-flagged [Info]
304  'versions' => [],
305  // Subset of "all" which are offline versions (pid=-1). [Info]
306  'versions_published' => [],
307  // Subset of "versions" that is a count of 1 or more (has been published) [Info]
308  'versions_liveWS' => [],
309  // Subset of "versions" that exists in live workspace [Info]
310  'versions_lost_workspace' => [],
311  // Subset of "versions" that doesn't belong to an existing workspace [Warning: Fix by move to live workspace]
312  'versions_inside_versioned_page' => [],
313  // 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)]
314  'illegal_record_under_versioned_page' => [],
315  // 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]
316  'misplaced_at_rootlevel' => [],
317  // Subset of "all": Those that should not be at root level but are. [Warning: Fix by moving record into page tree]
318  'misplaced_inside_tree' => []
319  ];
320  // Start traversal:
322  $this->performanceStatistics['genTree_traverse()'] = '';
323  $this->performanceStatistics['genTree_traverse():TraverseTables'] = '';
324  $this->genTree_traverse($rootID, $depth, $echoLevel, $callBack);
325  $this->performanceStatistics['genTree_traverse()'] = GeneralUtility::milliseconds() - $pt2;
326  // Sort recStats (for diff'able displays)
327  foreach ($this->recStats as $kk => $vv) {
328  foreach ($this->recStats[$kk] as $tables => $recArrays) {
329  ksort($this->recStats[$kk][$tables]);
330  }
331  ksort($this->recStats[$kk]);
332  }
333  if ($echoLevel > 0) {
334  echo LF . LF;
335  }
336  // Processing performance statistics:
337  $this->performanceStatistics['genTree()'] = GeneralUtility::milliseconds() - $pt;
338  // Count records:
339  foreach ($GLOBALS['TCA'] as $tableName => $cfg) {
340  // Select all records belonging to page:
341  $resSub = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $tableName, '');
342  $countRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($resSub);
343  $this->performanceStatistics['MySQL_count'][$tableName] = $countRow['count(*)'];
344  $this->performanceStatistics['CSV'] .= LF . $tableName . ',' . $this->performanceStatistics['genTree_traverse():TraverseTables:']['MySQL'][$tableName] . ',' . $this->performanceStatistics['genTree_traverse():TraverseTables:']['Proc'][$tableName] . ',' . $this->performanceStatistics['MySQL_count'][$tableName];
345  }
346  $this->performanceStatistics['recStats_size']['(ALL)'] = strlen(serialize($this->recStats));
347  foreach ($this->recStats as $key => $arrcontent) {
348  $this->performanceStatistics['recStats_size'][$key] = strlen(serialize($arrcontent));
349  }
350  }
351 
366  public function genTree_traverse($rootID, $depth, $echoLevel = 0, $callBack = '', $versionSwapmode = '', $rootIsVersion = 0, $accumulatedPath = '')
367  {
368  // Register page:
369  $this->recStats['all']['pages'][$rootID] = $rootID;
370  $pageRecord = BackendUtility::getRecordRaw('pages', 'uid=' . (int)$rootID, 'deleted,title,t3ver_count,t3ver_wsid');
371  $accumulatedPath .= '/' . $pageRecord['title'];
372  // Register if page is deleted:
373  if ($pageRecord['deleted']) {
374  $this->recStats['deleted']['pages'][$rootID] = $rootID;
375  }
376  // 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.
377  if ($rootIsVersion) {
378  $this->recStats['versions']['pages'][$rootID] = $rootID;
379  // If it has been published and is in archive now...
380  if ($pageRecord['t3ver_count'] >= 1 && $pageRecord['t3ver_wsid'] == 0) {
381  $this->recStats['versions_published']['pages'][$rootID] = $rootID;
382  }
383  // If it has been published and is in archive now...
384  if ($pageRecord['t3ver_wsid'] == 0) {
385  $this->recStats['versions_liveWS']['pages'][$rootID] = $rootID;
386  }
387  // If it doesn't belong to a workspace...
388  if (!isset($this->workspaceIndex[$pageRecord['t3ver_wsid']])) {
389  $this->recStats['versions_lost_workspace']['pages'][$rootID] = $rootID;
390  }
391  // In case the rootID is a version inside a versioned page
392  if ($rootIsVersion == 2) {
393  $this->recStats['versions_inside_versioned_page']['pages'][$rootID] = $rootID;
394  }
395  }
396  if ($echoLevel > 0) {
397  echo LF . $accumulatedPath . ' [' . $rootID . ']' . ($pageRecord['deleted'] ? ' (DELETED)' : '') . ($this->recStats['versions_published']['pages'][$rootID] ? ' (PUBLISHED)' : '');
398  }
399  if ($echoLevel > 1 && $this->recStats['versions_lost_workspace']['pages'][$rootID]) {
400  echo LF . ' ERROR! This version belongs to non-existing workspace (' . $pageRecord['t3ver_wsid'] . ')!';
401  }
402  if ($echoLevel > 1 && $this->recStats['versions_inside_versioned_page']['pages'][$rootID]) {
403  echo LF . ' WARNING! This version is inside an already versioned page or branch!';
404  }
405  // Call back:
406  if ($callBack) {
407  $this->{$callBack}('pages', $rootID, $echoLevel, $versionSwapmode, $rootIsVersion);
408  }
410  // Traverse tables of records that belongs to page:
411  foreach ($GLOBALS['TCA'] as $tableName => $cfg) {
412  if ($tableName != 'pages') {
413  // Select all records belonging to page:
415  $resSub = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid' . ($GLOBALS['TCA'][$tableName]['ctrl']['delete'] ? ',' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] : ''), $tableName, 'pid=' . (int)$rootID . ($this->genTree_traverseDeleted ? '' : BackendUtility::deleteClause($tableName)));
416  $this->performanceStatistics['genTree_traverse():TraverseTables:']['MySQL']['(ALL)'] += GeneralUtility::milliseconds() - $pt4;
417  $this->performanceStatistics['genTree_traverse():TraverseTables:']['MySQL'][$tableName] += GeneralUtility::milliseconds() - $pt4;
419  $count = $GLOBALS['TYPO3_DB']->sql_num_rows($resSub);
420  if ($count) {
421  if ($echoLevel == 2) {
422  echo LF . ' \\-' . $tableName . ' (' . $count . ')';
423  }
424  }
425  while ($rowSub = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($resSub)) {
426  if ($echoLevel == 3) {
427  echo LF . ' \\-' . $tableName . ':' . $rowSub['uid'];
428  }
429  // 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:
430  if ($versionSwapmode == 'SWAPMODE:-1' || $versionSwapmode == 'SWAPMODE:0' && !$GLOBALS['TCA'][$tableName]['ctrl']['versioning_followPages']) {
431  // This is illegal records under a versioned page - therefore not registered in $this->recStats['all'] so they should be orphaned:
432  $this->recStats['illegal_record_under_versioned_page'][$tableName][$rowSub['uid']] = $rowSub['uid'];
433  if ($echoLevel > 1) {
434  echo LF . ' ERROR! Illegal record (' . $tableName . ':' . $rowSub['uid'] . ') under versioned page!';
435  }
436  } else {
437  $this->recStats['all'][$tableName][$rowSub['uid']] = $rowSub['uid'];
438  // Register deleted:
439  if ($GLOBALS['TCA'][$tableName]['ctrl']['delete'] && $rowSub[$GLOBALS['TCA'][$tableName]['ctrl']['delete']]) {
440  $this->recStats['deleted'][$tableName][$rowSub['uid']] = $rowSub['uid'];
441  if ($echoLevel == 3) {
442  echo ' (DELETED)';
443  }
444  }
445  // Check location of records regarding tree root:
446  if (!$GLOBALS['TCA'][$tableName]['ctrl']['rootLevel'] && $rootID == 0) {
447  $this->recStats['misplaced_at_rootlevel'][$tableName][$rowSub['uid']] = $rowSub['uid'];
448  if ($echoLevel > 1) {
449  echo LF . ' ERROR! Misplaced record (' . $tableName . ':' . $rowSub['uid'] . ') on rootlevel!';
450  }
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) {
455  echo LF . ' ERROR! Misplaced record (' . $tableName . ':' . $rowSub['uid'] . ') inside page tree!';
456  }
457  }
458  // Traverse plugins:
459  if ($callBack) {
460  $this->{$callBack}($tableName, $rowSub['uid'], $echoLevel, $versionSwapmode, $rootIsVersion);
461  }
462  // Add any versions of those records:
463  if ($this->genTree_traverseVersions) {
464  $versions = BackendUtility::selectVersionsOfRecord($tableName, $rowSub['uid'], 'uid,t3ver_wsid,t3ver_count' . ($GLOBALS['TCA'][$tableName]['ctrl']['delete'] ? ',' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] : ''), null, true);
465  if (is_array($versions)) {
466  foreach ($versions as $verRec) {
467  if (!$verRec['_CURRENT_VERSION']) {
468  if ($echoLevel == 3) {
469  echo LF . ' \\-[#OFFLINE VERSION: WS#' . $verRec['t3ver_wsid'] . '/Cnt:' . $verRec['t3ver_count'] . '] ' . $tableName . ':' . $verRec['uid'] . ')';
470  }
471  $this->recStats['all'][$tableName][$verRec['uid']] = $verRec['uid'];
472  // Register deleted:
473  if ($GLOBALS['TCA'][$tableName]['ctrl']['delete'] && $verRec[$GLOBALS['TCA'][$tableName]['ctrl']['delete']]) {
474  $this->recStats['deleted'][$tableName][$verRec['uid']] = $verRec['uid'];
475  if ($echoLevel == 3) {
476  echo ' (DELETED)';
477  }
478  }
479  // Register version:
480  $this->recStats['versions'][$tableName][$verRec['uid']] = $verRec['uid'];
481  if ($verRec['t3ver_count'] >= 1 && $verRec['t3ver_wsid'] == 0) {
482  // Only register published versions in LIVE workspace (published versions in draft workspaces are allowed)
483  $this->recStats['versions_published'][$tableName][$verRec['uid']] = $verRec['uid'];
484  if ($echoLevel == 3) {
485  echo ' (PUBLISHED)';
486  }
487  }
488  if ($verRec['t3ver_wsid'] == 0) {
489  $this->recStats['versions_liveWS'][$tableName][$verRec['uid']] = $verRec['uid'];
490  }
491  if (!isset($this->workspaceIndex[$verRec['t3ver_wsid']])) {
492  $this->recStats['versions_lost_workspace'][$tableName][$verRec['uid']] = $verRec['uid'];
493  if ($echoLevel > 1) {
494  echo LF . ' ERROR! Version (' . $tableName . ':' . $verRec['uid'] . ') belongs to non-existing workspace (' . $verRec['t3ver_wsid'] . ')!';
495  }
496  }
497  // In case we are inside a versioned branch, there should not exists versions inside that "branch".
498  if ($versionSwapmode) {
499  $this->recStats['versions_inside_versioned_page'][$tableName][$verRec['uid']] = $verRec['uid'];
500  if ($echoLevel > 1) {
501  echo LF . ' ERROR! This version (' . $tableName . ':' . $verRec['uid'] . ') is inside an already versioned page or branch!';
502  }
503  }
504  // Traverse plugins:
505  if ($callBack) {
506  $this->{$callBack}($tableName, $verRec['uid'], $echoLevel, $versionSwapmode, $rootIsVersion);
507  }
508  }
509  }
510  }
511  unset($versions);
512  }
513  }
514  }
515  $this->performanceStatistics['genTree_traverse():TraverseTables:']['Proc']['(ALL)'] += GeneralUtility::milliseconds() - $pt5;
516  $this->performanceStatistics['genTree_traverse():TraverseTables:']['Proc'][$tableName] += GeneralUtility::milliseconds() - $pt5;
517  }
518  }
519  unset($resSub);
520  unset($rowSub);
521  $this->performanceStatistics['genTree_traverse():TraverseTables'] += GeneralUtility::milliseconds() - $pt3;
522  // Find subpages to root ID and traverse (only when rootID is not a version or is a branch-version):
523  if (!$versionSwapmode || $versionSwapmode == 'SWAPMODE:1') {
524  if ($depth > 0) {
525  $depth--;
526  $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', 'pid=' . (int)$rootID . ($this->genTree_traverseDeleted ? '' : BackendUtility::deleteClause('pages')), '', 'sorting');
527  while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
528  $this->genTree_traverse($row['uid'], $depth, $echoLevel, $callBack, $versionSwapmode, 0, $accumulatedPath);
529  }
530  }
531  // Add any versions of pages
532  if ($rootID > 0 && $this->genTree_traverseVersions) {
533  $versions = BackendUtility::selectVersionsOfRecord('pages', $rootID, 'uid,t3ver_oid,t3ver_wsid,t3ver_count', null, true);
534  if (is_array($versions)) {
535  foreach ($versions as $verRec) {
536  if (!$verRec['_CURRENT_VERSION']) {
537  $this->genTree_traverse($verRec['uid'], $depth, $echoLevel, $callBack, 'SWAPMODE:-1', $versionSwapmode ? 2 : 1, $accumulatedPath . ' [#OFFLINE VERSION: WS#' . $verRec['t3ver_wsid'] . '/Cnt:' . $verRec['t3ver_count'] . ']');
538  }
539  }
540  }
541  }
542  }
543  }
544 
545  /**************************
546  *
547  * Helper functions
548  *
549  *************************/
556  public function infoStr($rec)
557  {
558  return $rec['tablename'] . ':' . $rec['recuid'] . ':' . $rec['field'] . ':' . $rec['flexpointer'] . ':' . $rec['softref_key'] . ($rec['deleted'] ? ' (DELETED)' : '');
559  }
560 }
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
static getRecordRaw($table, $where='', $fields=' *')
genTree($rootID, $depth=1000, $echoLevel=0, $callBack='')
static getUrl($url, $includeHeader=0, $requestHeaders=false, &$report=null)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static selectVersionsOfRecord($table, $uid, $fields=' *', $workspace=0, $includeDeletedRecords=false, $row=null)
static deleteClause($table, $tableAlias='')
genTree_traverse($rootID, $depth, $echoLevel=0, $callBack='', $versionSwapmode='', $rootIsVersion=0, $accumulatedPath='')