Fixed bug #16936: wizardtitle "Versioning and Workspaces: Migrate "draft" workspace...
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / updates / class.tx_coreupdates_migrateworkspaces.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2010 Tolleiv Nietsch <info@tolleiv.de>
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
29 /**
30 * Migrates workspaces from TYPO3 versions below 4.5.
31 *
32 * @author Tolleiv Nietsch <info@tolleiv.de>
33 * @version $Id$
34 */
35 class tx_coreupdates_migrateworkspaces extends tx_coreupdates_installsysexts {
36 protected $title = 'Versioning and Workspaces';
37
38 public $sqlQueries;
39
40 /**
41 * Checks if an update is needed
42 *
43 * @param string &$description: The description for the update, which will be updated with a description of the script's purpose
44 * @return boolean whether an update is needed (true) or not (false)
45 */
46 public function checkForUpdate(&$description) {
47 $result = FALSE;
48 $description = 'Migrates the old hardcoded draft workspace to be a real workspace record,
49 updates workspace owner fields to support either users or groups and
50 migrates the old-style workspaces with fixed workflow to a custom-stage workflow. If required
51 the extbase, fluid, version and workspaces extensions are installed.';
52
53 // TYPO3 version 4.5 and above
54 if ($this->versionNumber >= 4005000) {
55 $tables = array_keys($GLOBALS['TYPO3_DB']->admin_get_tables());
56 // sys_workspace table might not exists if version extension was never installed
57 if (in_array('sys_workspace', $tables)) {
58 $wsCount = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', 'sys_workspace', '');
59 $result = $wsCount > 0;
60 }
61
62 if (!$result) {
63 $this->includeTCA();
64 $result = $this->isDraftWorkspaceUsed();
65 }
66 }
67
68 return $result;
69 }
70
71 /**
72 * If there's any user-input which we'd love to process, this method allows to specify what
73 * kind of input we need.
74 *
75 * Since we don't need input this is left empty. It's still here to avoid that this is inherited.
76 *
77 * @param string $inputPrefix
78 * @return void
79 */
80 public function getUserInput($inputPrefix) {
81 return;
82 }
83
84 /**
85 * Performs the database update. Changes existing workspaces to use the new custom workspaces
86 *
87 * @param array &$databaseQueries: queries done in this update
88 * @param mixed &$customMessages: custom messages
89 * @return boolean whether it worked (true) or not (false)
90 */
91 public function performUpdate(array &$databaseQueries, &$customMessages) {
92 $result = TRUE;
93
94 // TYPO3 version below 4.5
95 if ($this->versionNumber < 4005000) {
96 return FALSE;
97 }
98
99 // There's no TCA available yet
100 $this->includeTCA();
101
102 // install version and workspace extension (especially when updating from very old TYPO3 versions
103 $this->installExtensions(array('extbase', 'fluid', 'version', 'workspaces'));
104
105 // migrate all workspaces to support groups and be_users
106 if ($this->isOldStyleAdminFieldUsed()) {
107 $this->migrateAdminFieldToNewStyle();
108 }
109
110 // create a new dedicated "Draft" workspace and move all records to that new workspace
111 if ($this->isDraftWorkspaceUsed()) {
112 $draftWorkspaceId = $this->createWorkspace();
113 if (is_integer($draftWorkspaceId)) {
114 $this->migrateDraftWorkspaceRecordsToWorkspace($draftWorkspaceId);
115 }
116 }
117
118 $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, reviewers', 'sys_workspace', '', '', '', '1');
119 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
120 $label = 'Review';
121 foreach($workspaces as $workspace) {
122 $where = 'parentid=' . $workspace['uid'] . ' AND parenttable="sys_workspace" AND deleted=0';
123 $stageCount = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', 'sys_workspace_stage', $where);
124 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
125 if ($stageCount == 0) {
126 // Find all workspaces and add "review" stage record
127 // Add review users and groups to the new IRRE record
128 $reviewStageId = $this->createReviewStageForWorkspace($workspace['uid'], $label, $workspace['reviewers']);
129 // Update all "review" state records in the database to point to the new state
130 $this->migrateOldRecordsToStage($workspace['uid'], 1, $reviewStageId);
131 // Update all "ready to publish" records in the database to point to the new ready to publish state
132 $this->migrateOldRecordsToStage($workspace['uid'], 10, -99);
133 } else {
134 // this workspace has stages already at this point we might break something if we continue
135 }
136 }
137
138 if (is_array($this->sqlQueries) && is_array($databaseQueries)) {
139 $databaseQueries = array_merge($databaseQueries, $this->sqlQueries);
140 }
141
142 return $result;
143 }
144
145 /**
146 * Install a extensions
147 *
148 * @param array List of extension keys
149 * @return boolean Determines whether this was successful or not
150 */
151 protected function installExtensions($extensionKeys) {
152 if (!is_array($extensionKeys)) {
153 return FALSE;
154 }
155
156 $result = FALSE;
157 $extList = $this->addExtToList($extensionKeys);
158 if ($extList) {
159 $this->writeNewExtensionList($extList);
160 $result = TRUE;
161 }
162 return $result;
163 }
164
165 /**
166 * Check if any table contains draft-workspace records
167 *
168 * @return bool
169 */
170 protected function isDraftWorkspaceUsed() {
171 $foundDraftRecords = FALSE;
172
173 $tables = array_keys($GLOBALS['TCA']);
174 foreach ($tables as $table) {
175 $versioningVer = t3lib_div::intInRange($GLOBALS['TCA'][$table]['ctrl']['versioningWS'], 0, 2, 0);
176 if ($versioningVer > 0) {
177 if ($this->hasElementsOnWorkspace($table, -1)) {
178 $foundDraftRecords = TRUE;
179 break;
180 }
181 }
182 }
183
184 return $foundDraftRecords;
185 }
186
187 /**
188 * Create a new stage for the given workspace
189 *
190 * @param integer Workspace ID
191 * @param string The label of the new stage
192 * @param string The users or groups which are authorized for that stage
193 * @return integer The id of the new stage
194 */
195 protected function createReviewStageForWorkspace($workspaceId, $stageLabel, $stageMembers) {
196 $data = array(
197 'parentid' => $workspaceId,
198 'parenttable' => 'sys_workspace',
199 'title' => $stageLabel,
200 'responsible_persons' => $stageMembers
201 );
202 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_workspace_stage', $data);
203 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
204 return $GLOBALS['TYPO3_DB']->sql_insert_id();
205 }
206
207 /**
208 * Updates the stages of placeholder records within the given workspace from $oldId to $newId
209 *
210 * @param integer Workspace ID
211 * @param integer Old stage od
212 * @param integer New stage od
213 * @return void
214 */
215 protected function migrateOldRecordsToStage($workspaceId, $oldStageId, $newStageId) {
216 $tables = array_keys($GLOBALS['TCA']);
217
218 $where = 't3ver_wsid = ' . intval($workspaceId) . ' AND t3ver_stage = ' . intval($oldStageId) . ' AND pid = -1';
219 $values = array(
220 't3ver_stage' => intval($newStageId)
221 );
222 foreach($tables as $table) {
223 $versioningVer = t3lib_div::intInRange($GLOBALS['TCA'][$table]['ctrl']['versioningWS'], 0, 2, 0);
224 if ($versioningVer > 0) {
225 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, $where, $values);
226 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
227 }
228 }
229 }
230
231
232 /**
233 * Check if there's any workspace which doesn't support the new admin-field format yet
234 * @return bool
235 */
236 protected function isOldStyleAdminFieldUsed() {
237 $where = 'adminusers != "" AND adminusers NOT LIKE "%be_users%" AND adminusers NOT LIKE "%be_groups%" AND deleted=0';
238 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', 'sys_workspace', $where);
239 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
240 return $count > 0;
241 }
242
243 /**
244 * Create a real workspace named "Draft"
245 *
246 * @return integer
247 */
248 protected function createWorkspace() {
249 // @todo who are the reviewers and owners for this workspace?
250 // In previous versions this was defined in be_groups/be_users with the setting "Edit in Draft"
251 $data = array(
252 'title' => 'Draft'
253 );
254 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_workspace', $data);
255 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
256 return $GLOBALS['TYPO3_DB']->sql_insert_id();
257 }
258
259 /**
260 * Migrates all elements from the old draft workspace to the new one.
261 *
262 * @param integer $wsId
263 * @return void
264 */
265 protected function migrateDraftWorkspaceRecordsToWorkspace($wsId) {
266 $tables = array_keys($GLOBALS['TCA']);
267 $where = 't3ver_wsid=-1';
268 $values = array(
269 't3ver_wsid' => intval($wsId)
270 );
271 foreach($tables as $table) {
272 $versioningVer = t3lib_div::intInRange($GLOBALS['TCA'][$table]['ctrl']['versioningWS'], 0, 2, 0);
273 if ($versioningVer > 0 && $this->hasElementsOnWorkspace($table, -1)) {
274 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, $where, $values);
275 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
276 }
277 }
278 }
279
280 /**
281 * Migrate all workspace adminusers fields to support groups aswell,
282 * this means that the old comma separated list of uids (referring to be_users)
283 * is updated to be a list of uids with the tablename as prefix
284 *
285 * @return void
286 */
287 protected function migrateAdminFieldToNewStyle() {
288 $where = 'adminusers != "" AND adminusers NOT LIKE "%be_users%" AND adminusers NOT LIKE "%be_groups%" AND deleted=0';
289 $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, adminusers', 'sys_workspace', $where);
290 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
291 foreach ($workspaces as $workspace) {
292 $updateArray = array(
293 'adminusers' => 'be_users_' . implode(',be_users_', t3lib_div::trimExplode(',', $workspace['adminusers'], TRUE)),
294 );
295 $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
296 'sys_workspace',
297 'uid = "' . $workspace['uid'] . '"',
298 $updateArray
299 );
300 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
301 }
302 }
303
304 /**
305 * Includes the TCA definition of installed extensions.
306 *
307 * This method is used because usually the TCA is included within the init.php script, this doesn't happen
308 * if the install-tool is used, therefore this has to be done by hand.
309 *
310 * @return void
311 */
312 protected function includeTCA() {
313 global $TCA; // this is relevant because it's used within the included ext_tables.php files - do NOT remove it
314
315 include_once(TYPO3_tables_script ? PATH_typo3conf . TYPO3_tables_script : PATH_t3lib . 'stddb/tables.php');
316 // Extension additions
317 if ($GLOBALS['TYPO3_LOADED_EXT']['_CACHEFILE']) {
318 include_once(PATH_typo3conf . $GLOBALS['TYPO3_LOADED_EXT']['_CACHEFILE'] . '_ext_tables.php');
319 } else {
320 include_once(PATH_t3lib . 'stddb/load_ext_tables.php');
321 }
322 }
323
324 /**
325 * Determines whether a table has elements in a particular workspace.
326 *
327 * @param string $table Name of the table
328 * @param integer $workspaceId Id of the workspace
329 * @return boolean
330 */
331 protected function hasElementsOnWorkspace($table, $workspaceId) {
332 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
333 'uid',
334 $table,
335 't3ver_wsid=' . intval($workspaceId)
336 );
337
338 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
339
340 return ($count > 0);
341 }
342 }
343 ?>