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