TYPO3 CMS  TYPO3_6-2
VersionsCommand.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Lowlevel;
3 
20 
27 
33  public function __construct() {
34  parent::__construct();
35  // Setting up help:
36  $this->cli_options[] = array('--echotree level', 'When "level" is set to 1 or higher you will see the page of the page tree outputted as it is traversed. A value of 2 for "level" will show even more information.');
37  $this->cli_options[] = array('--pid id', 'Setting start page in page tree. Default is the page tree root, 0 (zero)');
38  $this->cli_options[] = array('--depth int', 'Setting traversal depth. 0 (zero) will only analyse start page (see --pid), 1 will traverse one level of subpages etc.');
39  $this->cli_options[] = array('--flush-live', 'If set, not only published versions from Live workspace are flushed, but ALL versions from Live workspace (which are offline of course)');
40  $this->cli_help['name'] = 'versions -- To find information about versions and workspaces in the system';
41  $this->cli_help['description'] = trim('
42 Traversing page tree and finding versions, categorizing them by various properties.
43 Published versions from the Live workspace are registered. So are all offline versions from Live workspace in general. Further, versions in non-existing workspaces are found.
44 
45 Automatic Repair:
46 - Deleting (completely) published versions from LIVE workspace OR _all_ offline versions from Live workspace (toogle by --flush-live)
47 - Resetting workspace for versions where workspace is deleted. (You might want to run this tool again after this operation to clean out those new elements in the Live workspace)
48 - Deleting unused placeholders
49 ');
50  $this->cli_help['examples'] = '';
51  }
52 
60  public function main() {
61  global $TYPO3_DB;
62  // Initialize result array:
63  $resultArray = array(
64  'message' => $this->cli_help['name'] . LF . LF . $this->cli_help['description'],
65  'headers' => array(
66  'versions' => array('All versions', 'Showing all versions of records found', 0),
67  'versions_published' => array('All published versions', 'This is all records that has been published and can therefore be removed permanently', 1),
68  'versions_liveWS' => array('All versions in Live workspace', 'This is all records that are offline versions in the Live workspace. You may wish to flush these if you only use workspaces for versioning since then you might find lots of versions piling up in the live workspace which have simply been disconnected from the workspace before they were published.', 1),
69  'versions_lost_workspace' => array('Versions outside a workspace', 'Versions that has lost their connection to a workspace in TYPO3.', 3),
70  'versions_inside_versioned_page' => array('Versions in versions', 'Versions inside an already versioned page. Something that is confusing to users and therefore should not happen but is technically possible.', 2),
71  'versions_unused_placeholders' => array('Unused placeholder records', 'Placeholder records which are not used anymore by offline versions.', 2),
72  'versions_move_placeholders_ok' => array('Move placeholders', 'Move-to placeholder records which has good integrity', 0),
73  'versions_move_placeholders_bad' => array('Move placeholders with bad integrity', 'Move-to placeholder records which has bad integrity', 2),
74  'versions_move_id_check' => array('Checking if t3ver_move_id is correct', 't3ver_move_id must only be set with online records having t3ver_state=3.', 2)
75  ),
76  'versions' => array()
77  );
78  $startingPoint = $this->cli_isArg('--pid') ? \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($this->cli_argValue('--pid'), 0) : 0;
79  $depth = $this->cli_isArg('--depth') ? \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($this->cli_argValue('--depth'), 0) : 1000;
80  $this->genTree($startingPoint, $depth, (int)$this->cli_argValue('--echotree'));
81  $resultArray['versions'] = $this->recStats['versions'];
82  $resultArray['versions_published'] = $this->recStats['versions_published'];
83  $resultArray['versions_liveWS'] = $this->recStats['versions_liveWS'];
84  $resultArray['versions_lost_workspace'] = $this->recStats['versions_lost_workspace'];
85  $resultArray['versions_inside_versioned_page'] = $this->recStats['versions_inside_versioned_page'];
86  // Finding all placeholders with no records attached!
87  $resultArray['versions_unused_placeholders'] = array();
88  foreach ($GLOBALS['TCA'] as $table => $cfg) {
89  if ($cfg['ctrl']['versioningWS']) {
90  $placeHolders = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
91  'uid,pid',
92  $table,
93  't3ver_state=' . new VersionState(VersionState::NEW_PLACEHOLDER) . ' AND pid>=0' . BackendUtility::deleteClause($table)
94  );
95  foreach ($placeHolders as $phrec) {
96  if (count(BackendUtility::selectVersionsOfRecord($table, $phrec['uid'], 'uid', '*', NULL)) <= 1) {
97  $resultArray['versions_unused_placeholders'][GeneralUtility::shortmd5($table . ':' . $phrec['uid'])] = $table . ':' . $phrec['uid'];
98  }
99  }
100  }
101  }
102  asort($resultArray['versions_unused_placeholders']);
103  // Finding all move placeholders with inconsistencies:
104  $resultArray['versions_move_placeholders_ok'] = array();
105  $resultArray['versions_move_placeholders_bad'] = array();
106  foreach ($GLOBALS['TCA'] as $table => $cfg) {
107  if ((int)$cfg['ctrl']['versioningWS'] >= 2) {
108  $placeHolders = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
109  'uid,pid,t3ver_move_id,t3ver_wsid,t3ver_state',
110  $table,
111  't3ver_state=' . new VersionState(VersionState::MOVE_PLACEHOLDER) . ' AND pid>=0' . BackendUtility::deleteClause($table)
112  );
113  foreach ($placeHolders as $phrec) {
114  $shortID = GeneralUtility::shortmd5($table . ':' . $phrec['uid']);
115  if ((int)$phrec['t3ver_wsid'] != 0) {
116  $phrecCopy = $phrec;
117  if (BackendUtility::movePlhOL($table, $phrec)) {
118  if ($wsAlt = BackendUtility::getWorkspaceVersionOfRecord($phrecCopy['t3ver_wsid'], $table, $phrec['uid'], 'uid,pid,t3ver_state')) {
119  if (!VersionState::cast($wsAlt['t3ver_state'])->equals(VersionState::MOVE_POINTER)) {
120  $resultArray['versions_move_placeholders_bad'][$shortID] = array($table . ':' . $phrec['uid'], 'State for version was not "4" as it should be!', $phrecCopy);
121  } else {
122  $resultArray['versions_move_placeholders_ok'][$shortID] = array(
123  $table . ':' . $phrec['uid'],
124  'PLH' => $phrecCopy,
125  'online' => $phrec,
126  'PNT' => $wsAlt
127  );
128  }
129  } else {
130  $resultArray['versions_move_placeholders_bad'][$shortID] = array($table . ':' . $phrec['uid'], 'No version was found for online record to be moved. A version must exist.', $phrecCopy);
131  }
132  } else {
133  $resultArray['versions_move_placeholders_bad'][$shortID] = array($table . ':' . $phrec['uid'], 'Did not find online record for "t3ver_move_id" value ' . $phrec['t3ver_move_id'], $phrec);
134  }
135  } else {
136  $resultArray['versions_move_placeholders_bad'][$shortID] = array($table . ':' . $phrec['uid'], 'Placeholder was not assigned a workspace value in t3ver_wsid.', $phrec);
137  }
138  }
139  }
140  }
141  ksort($resultArray['versions_move_placeholders_ok']);
142  ksort($resultArray['versions_move_placeholders_bad']);
143  // Finding move_id_check inconsistencies:
144  $resultArray['versions_move_id_check'] = array();
145  foreach ($GLOBALS['TCA'] as $table => $cfg) {
146  if ((int)$cfg['ctrl']['versioningWS'] >= 2) {
147  $placeHolders = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,pid,t3ver_move_id,t3ver_wsid,t3ver_state', $table, 't3ver_move_id<>0' . BackendUtility::deleteClause($table));
148  foreach ($placeHolders as $phrec) {
149  if (VersionState::cast($phrec['t3ver_state'])->equals(VersionState::MOVE_PLACEHOLDER)) {
150  if ($phrec['pid'] != -1) {
151 
152  } else {
153  $resultArray['versions_move_id_check'][] = array($table . ':' . $phrec['uid'], 'Record was offline, must not be!', $phrec);
154  }
155  } else {
156  $resultArray['versions_move_id_check'][] = array($table . ':' . $phrec['uid'], 'Record had t3ver_move_id set to "' . $phrec['t3ver_move_id'] . '" while having t3ver_state=' . $phrec['t3ver_state'], $phrec);
157  }
158  }
159  }
160  }
161  return $resultArray;
162  }
163 
172  public function main_autoFix($resultArray) {
173  $kk = $this->cli_isArg('--flush-live') ? 'versions_liveWS' : 'versions_published';
174  // Putting "pages" table in the bottom:
175  if (isset($resultArray[$kk]['pages'])) {
176  $_pages = $resultArray[$kk]['pages'];
177  unset($resultArray[$kk]['pages']);
178  $resultArray[$kk]['pages'] = $_pages;
179  }
180  // Traversing records:
181  foreach ($resultArray[$kk] as $table => $list) {
182  echo 'Flushing published records from table "' . $table . '":' . LF;
183  foreach ($list as $uid) {
184  echo ' Flushing record "' . $table . ':' . $uid . '": ';
185  if ($bypass = $this->cli_noExecutionCheck($table . ':' . $uid)) {
186  echo $bypass;
187  } else {
188  // Execute CMD array:
189  $tce = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
190  $tce->stripslashes_values = FALSE;
191  $tce->start(array(), array());
192  $tce->deleteEl($table, $uid, TRUE, TRUE);
193  // Return errors if any:
194  if (count($tce->errorLog)) {
195  echo ' ERROR from "TCEmain":' . LF . 'TCEmain:' . implode((LF . 'TCEmain:'), $tce->errorLog);
196  } else {
197  echo 'DONE';
198  }
199  }
200  echo LF;
201  }
202  }
203  // Traverse workspace:
204  foreach ($resultArray['versions_lost_workspace'] as $table => $list) {
205  echo 'Resetting workspace to zero for records from table "' . $table . '":' . LF;
206  foreach ($list as $uid) {
207  echo ' Flushing record "' . $table . ':' . $uid . '": ';
208  if ($bypass = $this->cli_noExecutionCheck($table . ':' . $uid)) {
209  echo $bypass;
210  } else {
211  $fields_values = array(
212  't3ver_wsid' => 0
213  );
214  $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . (int)$uid, $fields_values);
215  echo 'DONE';
216  }
217  echo LF;
218  }
219  }
220  // Delete unused placeholders
221  foreach ($resultArray['versions_unused_placeholders'] as $recID) {
222  list($table, $uid) = explode(':', $recID);
223  echo 'Deleting unused placeholder (soft) "' . $table . ':' . $uid . '": ';
224  if ($bypass = $this->cli_noExecutionCheck($table . ':' . $uid)) {
225  echo $bypass;
226  } else {
227  // Execute CMD array:
228  $tce = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
229  $tce->stripslashes_values = FALSE;
230  $tce->start(array(), array());
231  $tce->deleteAction($table, $uid);
232  // Return errors if any:
233  if (count($tce->errorLog)) {
234  echo ' ERROR from "TCEmain":' . LF . 'TCEmain:' . implode((LF . 'TCEmain:'), $tce->errorLog);
235  } else {
236  echo 'DONE';
237  }
238  }
239  echo LF;
240  }
241  }
242 
243 }
static getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields=' *')
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
$uid
Definition: server.php:36
static selectVersionsOfRecord($table, $uid, $fields=' *', $workspace=0, $includeDeletedRecords=FALSE, $row=NULL)
genTree($rootID, $depth=1000, $echoLevel=0, $callBack='')
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
static deleteClause($table, $tableAlias='')