Fixed #11430: Performance improvement: use $GLOBALS['EXEC_TIME'] instead of time...
[Packages/TYPO3.CMS.git] / typo3 / sysext / lowlevel / clmods / class.versions.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2009 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 * Cleaner module: Versions of records
29 * User function called from tx_lowlevel_cleaner_core configured in ext_localconf.php
30 *
31 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
32 */
33 /**
34 * [CLASS/FUNCTION INDEX of SCRIPT]
35 *
36 *
37 *
38 * 56: class tx_lowlevel_versions extends tx_lowlevel_cleaner_core
39 * 63: function tx_lowlevel_versions()
40 * 88: function main()
41 * 122: function main_autoFix($resultArray)
42 *
43 * TOTAL FUNCTIONS: 3
44 * (This index is automatically created/updated by the extension "extdeveval")
45 *
46 */
47
48
49 /**
50 * Looking for versions of records
51 *
52 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
53 * @package TYPO3
54 * @subpackage tx_lowlevel
55 */
56 class tx_lowlevel_versions extends tx_lowlevel_cleaner_core {
57
58 /**
59 * Constructor
60 *
61 * @return void
62 */
63 function tx_lowlevel_versions() {
64 parent::tx_lowlevel_cleaner_core();
65
66 // Setting up help:
67 $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.');
68 $this->cli_options[] = array('--pid id', 'Setting start page in page tree. Default is the page tree root, 0 (zero)');
69 $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.');
70
71 $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)');
72
73 $this->cli_help['name'] = 'versions -- To find information about versions and workspaces in the system';
74 $this->cli_help['description'] = trim('
75 Traversing page tree and finding versions, categorizing them by various properties.
76 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.
77
78 Automatic Repair:
79 - Deleting (completely) published versions from LIVE workspace OR _all_ offline versions from Live workspace (toogle by --flush-live)
80 - 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)
81 - Deleting unused placeholders
82 ');
83
84 $this->cli_help['examples'] = '';
85 }
86
87 /**
88 * Find orphan records
89 * VERY CPU and memory intensive since it will look up the whole page tree!
90 *
91 * @return array
92 */
93 function main() {
94 global $TYPO3_DB;
95
96 // Initialize result array:
97 $resultArray = array(
98 'message' => $this->cli_help['name'].chr(10).chr(10).$this->cli_help['description'],
99 'headers' => array(
100 'versions' => array('All versions','Showing all versions of records found',0),
101 'versions_published' => array('All published versions','This is all records that has been published and can therefore be removed permanently',1),
102 '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),
103 'versions_lost_workspace' => array('Versions outside a workspace','Versions that has lost their connection to a workspace in TYPO3.',3),
104 '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),
105 'versions_unused_placeholders' => array('Unused placeholder records','Placeholder records which are not used anymore by offline versions.',2),
106 'versions_move_placeholders_ok' => array('Move placeholders','Move-to placeholder records which has good integrity',0),
107 'versions_move_placeholders_bad' => array('Move placeholders with bad integrity','Move-to placeholder records which has bad integrity',2),
108 '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),
109 ),
110 'versions' => array(),
111 );
112
113 $startingPoint = $this->cli_isArg('--pid') ? t3lib_div::intInRange($this->cli_argValue('--pid'),0) : 0;
114 $depth = $this->cli_isArg('--depth') ? t3lib_div::intInRange($this->cli_argValue('--depth'),0) : 1000;
115 $this->genTree($startingPoint,$depth,(int)$this->cli_argValue('--echotree'));
116
117 $resultArray['versions'] = $this->recStats['versions'];
118 $resultArray['versions_published'] = $this->recStats['versions_published'];
119 $resultArray['versions_liveWS'] = $this->recStats['versions_liveWS'];
120 $resultArray['versions_lost_workspace'] = $this->recStats['versions_lost_workspace'];
121 $resultArray['versions_inside_versioned_page'] = $this->recStats['versions_inside_versioned_page'];
122
123 // Finding all placeholders with no records attached!
124 $resultArray['versions_unused_placeholders'] = array();
125 foreach($GLOBALS['TCA'] as $table => $cfg) {
126 if ($cfg['ctrl']['versioningWS']) {
127 $placeHolders = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,pid',$table,'t3ver_state=1 AND pid>=0'.t3lib_BEfunc::deleteClause($table));
128 foreach($placeHolders as $phrec) {
129 if (count(t3lib_BEfunc::selectVersionsOfRecord($table, $phrec['uid'], 'uid'))<=1) {
130 $resultArray['versions_unused_placeholders'][t3lib_div::shortmd5($table.':'.$phrec['uid'])] = $table.':'.$phrec['uid'];
131 }
132 }
133 }
134 }
135 asort($resultArray['versions_unused_placeholders']);
136
137 // Finding all move placeholders with inconsistencies:
138 $resultArray['versions_move_placeholders_ok'] = array();
139 $resultArray['versions_move_placeholders_bad'] = array();
140 foreach($GLOBALS['TCA'] as $table => $cfg) {
141 if ((int)$cfg['ctrl']['versioningWS']>=2) {
142 $placeHolders = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,pid,t3ver_move_id,t3ver_wsid,t3ver_state',$table,'t3ver_state=3 AND pid>=0'.t3lib_BEfunc::deleteClause($table));
143 foreach($placeHolders as $phrec) {
144 $shortID = t3lib_div::shortmd5($table.':'.$phrec['uid']);
145 if ((int)$phrec['t3ver_wsid']!=0) {
146 $phrecCopy = $phrec;
147 if (t3lib_BEfunc::movePlhOL($table,$phrec)) {
148 if ($wsAlt = t3lib_BEfunc::getWorkspaceVersionOfRecord($phrecCopy['t3ver_wsid'], $table, $phrec['uid'], 'uid,pid,t3ver_state')) {
149 if ($wsAlt['t3ver_state']!=4) {
150 $resultArray['versions_move_placeholders_bad'][$shortID] = array($table.':'.$phrec['uid'],'State for version was not "4" as it should be!',$phrecCopy);
151 } else {
152 $resultArray['versions_move_placeholders_ok'][$shortID] = array(
153 $table.':'.$phrec['uid'],
154 'PLH' => $phrecCopy,
155 'online' => $phrec,
156 'PNT' => $wsAlt
157 );
158 }
159 } else {
160 $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);
161 }
162 } else {
163 $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);
164 }
165 } else {
166 $resultArray['versions_move_placeholders_bad'][$shortID] = array($table.':'.$phrec['uid'],'Placeholder was not assigned a workspace value in t3ver_wsid.',$phrec);
167 }
168 }
169 }
170 }
171
172 ksort($resultArray['versions_move_placeholders_ok']);
173 ksort($resultArray['versions_move_placeholders_bad']);
174
175 // Finding move_id_check inconsistencies:
176 $resultArray['versions_move_id_check'] = array();
177 foreach($GLOBALS['TCA'] as $table => $cfg) {
178 if ((int)$cfg['ctrl']['versioningWS']>=2) {
179 $placeHolders = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,pid,t3ver_move_id,t3ver_wsid,t3ver_state',$table,'t3ver_move_id!=0'.t3lib_BEfunc::deleteClause($table));
180 foreach($placeHolders as $phrec) {
181 if ((int)$phrec['t3ver_state']==3) {
182 if ($phrec['pid']!=-1) {
183 // OK
184 } else {
185 $resultArray['versions_move_id_check'][] = array($table.':'.$phrec['uid'],'Record was offline, must not be!',$phrec);
186 }
187 } else {
188 $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);
189 }
190 }
191 }
192 }
193
194 return $resultArray;
195 }
196
197 /**
198 * Mandatory autofix function
199 * Will run auto-fix on the result array. Echos status during processing.
200 *
201 * @param array Result array from main() function
202 * @return void
203 */
204 function main_autoFix($resultArray) {
205
206 $kk = $this->cli_isArg('--flush-live') ? 'versions_liveWS' : 'versions_published';
207
208 // Putting "pages" table in the bottom:
209 if (isset($resultArray[$kk]['pages'])) {
210 $_pages = $resultArray[$kk]['pages'];
211 unset($resultArray[$kk]['pages']);
212 $resultArray[$kk]['pages'] = $_pages;
213 }
214
215 // Traversing records:
216 foreach($resultArray[$kk] as $table => $list) {
217 echo 'Flushing published records from table "'.$table.'":'.chr(10);
218 foreach($list as $uid) {
219 echo ' Flushing record "'.$table.':'.$uid.'": ';
220
221 if ($bypass = $this->cli_noExecutionCheck($table.':'.$uid)) {
222 echo $bypass;
223 } else {
224
225 // Execute CMD array:
226 $tce = t3lib_div::makeInstance('t3lib_TCEmain');
227 $tce->stripslashes_values = FALSE;
228 $tce->start(array(),array());
229 $tce->deleteEl($table,$uid, TRUE, TRUE);
230
231 // Return errors if any:
232 if (count($tce->errorLog)) {
233 echo ' ERROR from "TCEmain":'.chr(10).'TCEmain:'.implode(chr(10).'TCEmain:',$tce->errorLog);
234 } else echo 'DONE';
235 }
236 echo chr(10);
237 }
238 }
239
240 // Traverse workspace:
241 foreach($resultArray['versions_lost_workspace'] as $table => $list) {
242 echo 'Resetting workspace to zero for records from table "'.$table.'":'.chr(10);
243 foreach($list as $uid) {
244 echo ' Flushing record "'.$table.':'.$uid.'": ';
245 if ($bypass = $this->cli_noExecutionCheck($table.':'.$uid)) {
246 echo $bypass;
247 } else {
248 $fields_values = array(
249 't3ver_wsid' => 0
250 );
251 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($uid),$fields_values);
252 echo 'DONE';
253 }
254 echo chr(10);
255 }
256 }
257
258 // Delete unused placeholders
259 foreach($resultArray['versions_unused_placeholders'] as $recID) {
260 list($table,$uid) = explode(':',$recID);
261 echo 'Deleting unused placeholder (soft) "'.$table.':'.$uid.'": ';
262 if ($bypass = $this->cli_noExecutionCheck($table.':'.$uid)) {
263 echo $bypass;
264 } else {
265
266 // Execute CMD array:
267 $tce = t3lib_div::makeInstance('t3lib_TCEmain');
268 $tce->stripslashes_values = FALSE;
269 $tce->start(array(),array());
270 $tce->deleteAction($table, $uid);
271
272 // Return errors if any:
273 if (count($tce->errorLog)) {
274 echo ' ERROR from "TCEmain":'.chr(10).'TCEmain:'.implode(chr(10).'TCEmain:',$tce->errorLog);
275 } else echo 'DONE';
276 }
277 echo chr(10);
278 }
279 }
280 }
281
282 ?>