1d0fe4acab94204dadc089e9bce4669efcb41e03
[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 // 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 * Check if any table contains draft-workspace records
147 *
148 * @return bool
149 */
150 protected function isDraftWorkspaceUsed() {
151 $foundDraftRecords = FALSE;
152
153 $tables = array_keys($GLOBALS['TCA']);
154 foreach ($tables as $table) {
155 $versioningVer = t3lib_div::intInRange($GLOBALS['TCA'][$table]['ctrl']['versioningWS'], 0, 2, 0);
156 if ($versioningVer > 0) {
157 if ($this->hasElementsOnWorkspace($table, -1)) {
158 $foundDraftRecords = TRUE;
159 break;
160 }
161 }
162 }
163
164 return $foundDraftRecords;
165 }
166
167 /**
168 * Create a new stage for the given workspace
169 *
170 * @param integer Workspace ID
171 * @param string The label of the new stage
172 * @param string The users or groups which are authorized for that stage
173 * @return integer The id of the new stage
174 */
175 protected function createReviewStageForWorkspace($workspaceId, $stageLabel, $stageMembers) {
176 $data = array(
177 'parentid' => $workspaceId,
178 'parenttable' => 'sys_workspace',
179 'title' => $stageLabel,
180 'responsible_persons' => $stageMembers
181 );
182 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_workspace_stage', $data);
183 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
184 return $GLOBALS['TYPO3_DB']->sql_insert_id();
185 }
186
187 /**
188 * Updates the stages of placeholder records within the given workspace from $oldId to $newId
189 *
190 * @param integer Workspace ID
191 * @param integer Old stage od
192 * @param integer New stage od
193 * @return void
194 */
195 protected function migrateOldRecordsToStage($workspaceId, $oldStageId, $newStageId) {
196 $tables = array_keys($GLOBALS['TCA']);
197
198 $where = 't3ver_wsid = ' . intval($workspaceId) . ' AND t3ver_stage = ' . intval($oldStageId) . ' AND pid = -1';
199 $values = array(
200 't3ver_stage' => intval($newStageId)
201 );
202 foreach($tables as $table) {
203 $versioningVer = t3lib_div::intInRange($GLOBALS['TCA'][$table]['ctrl']['versioningWS'], 0, 2, 0);
204 if ($versioningVer > 0) {
205 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, $where, $values);
206 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
207 }
208 }
209 }
210
211
212 /**
213 * Check if there's any workspace which doesn't support the new admin-field format yet
214 * @return bool
215 */
216 protected function isOldStyleAdminFieldUsed() {
217 $where = 'adminusers != "" AND adminusers NOT LIKE "%be_users%" AND adminusers NOT LIKE "%be_groups%" AND deleted=0';
218 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', 'sys_workspace', $where);
219 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
220 return $count > 0;
221 }
222
223 /**
224 * Create a real workspace named "Draft"
225 *
226 * @return integer
227 */
228 protected function createWorkspace() {
229 // @todo who are the reviewers and owners for this workspace?
230 // In previous versions this was defined in be_groups/be_users with the setting "Edit in Draft"
231 $data = array(
232 'title' => 'Draft'
233 );
234 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_workspace', $data);
235 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
236 return $GLOBALS['TYPO3_DB']->sql_insert_id();
237 }
238
239 /**
240 * Migrates all elements from the old draft workspace to the new one.
241 *
242 * @param integer $wsId
243 * @return void
244 */
245 protected function migrateDraftWorkspaceRecordsToWorkspace($wsId) {
246 $tables = array_keys($GLOBALS['TCA']);
247 $where = 't3ver_wsid=-1';
248 $values = array(
249 't3ver_wsid' => intval($wsId)
250 );
251 foreach($tables as $table) {
252 $versioningVer = t3lib_div::intInRange($GLOBALS['TCA'][$table]['ctrl']['versioningWS'], 0, 2, 0);
253 if ($versioningVer > 0 && $this->hasElementsOnWorkspace($table, -1)) {
254 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, $where, $values);
255 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
256 }
257 }
258 }
259
260 /**
261 * Migrate all workspace adminusers fields to support groups aswell,
262 * this means that the old comma separated list of uids (referring to be_users)
263 * is updated to be a list of uids with the tablename as prefix
264 *
265 * @return void
266 */
267 protected function migrateAdminFieldToNewStyle() {
268 $where = 'adminusers != "" AND adminusers NOT LIKE "%be_users%" AND adminusers NOT LIKE "%be_groups%" AND deleted=0';
269 $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, adminusers', 'sys_workspace', $where);
270 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
271 foreach ($workspaces as $workspace) {
272 $updateArray = array(
273 'adminusers' => 'be_users_' . implode(',be_users_', t3lib_div::trimExplode(',', $workspace['adminusers'], TRUE)),
274 );
275 $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
276 'sys_workspace',
277 'uid = "' . $workspace['uid'] . '"',
278 $updateArray
279 );
280 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
281 }
282 }
283
284 /**
285 * Includes the TCA definition of installed extensions.
286 *
287 * This method is used because usually the TCA is included within the init.php script, this doesn't happen
288 * if the install-tool is used, therefore this has to be done by hand.
289 *
290 * @return void
291 */
292 protected function includeTCA() {
293 global $TCA; // this is relevant because it's used within the included ext_tables.php files - do NOT remove it
294
295 include_once(TYPO3_tables_script ? PATH_typo3conf . TYPO3_tables_script : PATH_t3lib . 'stddb/tables.php');
296 // Extension additions
297 if ($GLOBALS['TYPO3_LOADED_EXT']['_CACHEFILE']) {
298 include_once(PATH_typo3conf . $GLOBALS['TYPO3_LOADED_EXT']['_CACHEFILE'] . '_ext_tables.php');
299 } else {
300 include_once(PATH_t3lib . 'stddb/load_ext_tables.php');
301 }
302 }
303
304 /**
305 * Determines whether a table has elements in a particular workspace.
306 *
307 * @param string $table Name of the table
308 * @param integer $workspaceId Id of the workspace
309 * @return boolean
310 */
311 protected function hasElementsOnWorkspace($table, $workspaceId) {
312 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
313 'uid',
314 $table,
315 't3ver_wsid=' . intval($workspaceId)
316 );
317
318 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;
319
320 return ($count > 0);
321 }
322 }
323 ?>