|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2010-2011 Tolleiv Nietsch <info@tolleiv.de> 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * A copy is found in the textfile GPL.txt and important notices to the license 00017 * from the author is found in LICENSE.txt distributed with these scripts. 00018 * 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00027 00028 00029 /** 00030 * Migrates workspaces from TYPO3 versions below 4.5. 00031 * 00032 * @author Tolleiv Nietsch <info@tolleiv.de> 00033 * @version $Id$ 00034 */ 00035 class tx_coreupdates_migrateworkspaces extends tx_coreupdates_installsysexts { 00036 protected $title = 'Versioning and Workspaces'; 00037 00038 public $sqlQueries; 00039 00040 /** 00041 * Checks if an update is needed 00042 * 00043 * @param string &$description: The description for the update, which will be updated with a description of the script's purpose 00044 * @return boolean whether an update is needed (true) or not (false) 00045 */ 00046 public function checkForUpdate(&$description) { 00047 $result = FALSE; 00048 $description = 'Migrates the old hardcoded draft workspace to be a real workspace record, 00049 updates workspace owner fields to support either users or groups and 00050 migrates the old-style workspaces with fixed workflow to a custom-stage workflow. If required 00051 the extbase, fluid, version and workspaces extensions are installed.'; 00052 00053 $reason = ''; 00054 // TYPO3 version 4.5 and above 00055 if ($this->versionNumber >= 4005000) { 00056 // If neither version nor workspaces is installed, we're not doing a migration 00057 // Present the user with the choice of activating versioning and workspaces 00058 if (!t3lib_extMgm::isLoaded('version') && !t3lib_extMgm::isLoaded('workspaces')) { 00059 $result = TRUE; 00060 // Override the default description 00061 $description = 'Activates the usage of workspaces in your installation. Workspaces let you edit elements 00062 without the changes being visible on the live web site right away. Modified elements can then go 00063 through a validation process and eventually be published.<br /><br />'; 00064 $description .= 'This wizard will install system extensions "version" and "workspaces" (and may 00065 install "fluid" and "extbase" too, as they are used by the "workspaces" extension).'; 00066 } else { 00067 00068 $this->includeTCA(); 00069 00070 if (!t3lib_extMgm::isLoaded('version') || !t3lib_extMgm::isLoaded('workspaces')) { 00071 $result = TRUE; 00072 $reason .= ' Both extensions "version" and "workspaces" need to be 00073 present to use the entire versioning and workflow featureset of TYPO3.'; 00074 } 00075 00076 $tables = array_keys($GLOBALS['TYPO3_DB']->admin_get_tables()); 00077 // sys_workspace table might not exists if version extension was never installed 00078 if (!in_array('sys_workspace', $tables) || !in_array('sys_workspace_stage', $tables)) { 00079 $result = TRUE; 00080 $reason .= ' The database tables for the workspace functionality are missing.'; 00081 } elseif ($this->isOldStyleAdminFieldUsed() || $this->isOldStyleWorkspace()) { 00082 $wsCount = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', 'sys_workspace', ''); 00083 $result |= $wsCount > 0; 00084 $reason .= ' The existing workspaces will be checked for compatibility with the new features.'; 00085 } 00086 00087 $draftWorkspaceTestResult = $this->isDraftWorkspaceUsed(); 00088 if ($draftWorkspaceTestResult) { 00089 $reason .= ' The old style draft workspace is used. 00090 Related records will be moved into a full featured workspace.'; 00091 $result = TRUE; 00092 } 00093 00094 $description .= '<br /><strong>Why do you need this wizard?</strong><br />' . $reason; 00095 } 00096 } 00097 00098 return $result; 00099 } 00100 00101 /** 00102 * This method requests input from the user about the upgrade process, if needed 00103 * 00104 * @param string $inputPrefix 00105 * @return void 00106 */ 00107 public function getUserInput($inputPrefix) { 00108 $content = ''; 00109 00110 if (!t3lib_extMgm::isLoaded('version') && !t3lib_extMgm::isLoaded('workspaces')) { 00111 // We need feedback only if versioning is not activated at all 00112 // In such a case we want to leave the user with the choice of not activating the stuff at all 00113 $content = ' 00114 <fieldset> 00115 <ol> 00116 '; 00117 00118 $content .= ' 00119 <li class="labelAfter"> 00120 <input type="checkbox" id="versioning" name="' . $inputPrefix . '[versioning]" value="1" checked="checked" /> 00121 <label for="versioning">Activate workspaces?</label> 00122 </li> 00123 '; 00124 00125 $content .= ' 00126 </ol> 00127 </fieldset> 00128 '; 00129 00130 } else { 00131 // No feedback needed, just include the update flag as a hidden field 00132 $content = '<input type="hidden" id="versioning" name="' . $inputPrefix . '[versioning]" value="1" />'; 00133 } 00134 00135 return $content; 00136 } 00137 00138 /** 00139 * Performs the database update. Changes existing workspaces to use the new custom workspaces 00140 * 00141 * @param array &$databaseQueries: queries done in this update 00142 * @param mixed &$customMessages: custom messages 00143 * @return boolean whether it worked (true) or not (false) 00144 */ 00145 public function performUpdate(array &$databaseQueries, &$customMessages) { 00146 $result = TRUE; 00147 00148 // TYPO3 version below 4.5 00149 if ($this->versionNumber < 4005000) { 00150 return FALSE; 00151 } 00152 // Wizard skipped by the user 00153 if (empty($this->pObj->INSTALL['update']['migrateWorkspaces']['versioning'])) { 00154 return TRUE; 00155 } 00156 00157 // There's no TCA available yet 00158 $this->includeTCA(); 00159 00160 // install version and workspace extension (especially when updating from very old TYPO3 versions 00161 $this->installExtensions(array('extbase', 'fluid', 'version', 'workspaces')); 00162 00163 // migrate all workspaces to support groups and be_users 00164 if ($this->isOldStyleAdminFieldUsed()) { 00165 $this->migrateAdminFieldToNewStyle(); 00166 } 00167 00168 // create a new dedicated "Draft" workspace and move all records to that new workspace 00169 if ($this->isDraftWorkspaceUsed()) { 00170 $draftWorkspaceId = $this->createWorkspace(); 00171 if (is_integer($draftWorkspaceId)) { 00172 $this->migrateDraftWorkspaceRecordsToWorkspace($draftWorkspaceId); 00173 } 00174 } 00175 00176 $workspaces = $this->getWorkspacesWithoutStages(); 00177 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery; 00178 $label = 'Review'; 00179 foreach($workspaces as $workspace) { 00180 // Find all workspaces and add "review" stage record 00181 // Add review users and groups to the new IRRE record 00182 $reviewStageId = $this->createReviewStageForWorkspace($workspace['uid'], $label, $workspace['reviewers']); 00183 // Update all "review" state records in the database to point to the new state 00184 $this->migrateOldRecordsToStage($workspace['uid'], 1, $reviewStageId); 00185 // Update all "ready to publish" records in the database to point to the new ready to publish state 00186 $this->migrateOldRecordsToStage($workspace['uid'], 10, -99); 00187 } 00188 00189 if (is_array($this->sqlQueries) && is_array($databaseQueries)) { 00190 $databaseQueries = array_merge($databaseQueries, $this->sqlQueries); 00191 } 00192 00193 return $result; 00194 } 00195 00196 /** 00197 * Check if any table contains draft-workspace records 00198 * 00199 * @return bool 00200 */ 00201 protected function isDraftWorkspaceUsed() { 00202 $foundDraftRecords = FALSE; 00203 00204 $tables = array_keys($GLOBALS['TCA']); 00205 foreach ($tables as $table) { 00206 $versioningVer = t3lib_div::intInRange($GLOBALS['TCA'][$table]['ctrl']['versioningWS'], 0, 2, 0); 00207 if ($versioningVer > 0) { 00208 if ($this->hasElementsOnWorkspace($table, -1)) { 00209 $foundDraftRecords = TRUE; 00210 break; 00211 } 00212 } 00213 } 00214 00215 return $foundDraftRecords; 00216 } 00217 00218 /** 00219 * Find workspaces which have no sys_workspace_state(s) but have records using states 00220 * If " 00221 * 00222 * @return bool 00223 */ 00224 protected function isOldStyleWorkspace() { 00225 $foundOldStyleStages = FALSE; 00226 $workspaces = $this->getWorkspacesWithoutStages(); 00227 $workspacesWithReviewers = 0; 00228 $workspaceUids = array(); 00229 foreach ($workspaces as $workspace) { 00230 $workspaceUids[] = $workspace['uid']; 00231 if ($workspace['reviewers']) { 00232 $workspacesWithReviewers++; 00233 } 00234 } 00235 if (!$workspacesWithReviewers && !empty($workspaceUids)) { 00236 $tables = array_keys($GLOBALS['TCA']); 00237 foreach ($tables as $table) { 00238 $versioningVer = t3lib_div::intInRange($GLOBALS['TCA'][$table]['ctrl']['versioningWS'], 0, 2, 0); 00239 if ($versioningVer > 0) { 00240 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows( 00241 'uid', 00242 $table, 00243 't3ver_wsid IN (' . implode(',', $workspaceUids) . ') AND t3ver_stage IN (-1,1,10) AND pid = -1' 00244 ); 00245 if ($count > 0) { 00246 $foundOldStyleStages = TRUE; 00247 break; 00248 } 00249 } 00250 } 00251 } 00252 return $foundOldStyleStages || $workspacesWithReviewers; 00253 } 00254 00255 /** 00256 * Create a new stage for the given workspace 00257 * 00258 * @param integer Workspace ID 00259 * @param string The label of the new stage 00260 * @param string The users or groups which are authorized for that stage 00261 * @return integer The id of the new stage 00262 */ 00263 protected function createReviewStageForWorkspace($workspaceId, $stageLabel, $stageMembers) { 00264 $data = array( 00265 'parentid' => $workspaceId, 00266 'parenttable' => 'sys_workspace', 00267 'title' => $stageLabel, 00268 'responsible_persons' => $stageMembers 00269 ); 00270 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_workspace_stage', $data); 00271 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery; 00272 return $GLOBALS['TYPO3_DB']->sql_insert_id(); 00273 } 00274 00275 /** 00276 * Updates the stages of placeholder records within the given workspace from $oldId to $newId 00277 * 00278 * @param integer Workspace ID 00279 * @param integer Old stage od 00280 * @param integer New stage od 00281 * @return void 00282 */ 00283 protected function migrateOldRecordsToStage($workspaceId, $oldStageId, $newStageId) { 00284 $tables = array_keys($GLOBALS['TCA']); 00285 00286 $where = 't3ver_wsid = ' . intval($workspaceId) . ' AND t3ver_stage = ' . intval($oldStageId) . ' AND pid = -1'; 00287 $values = array( 00288 't3ver_stage' => intval($newStageId) 00289 ); 00290 foreach($tables as $table) { 00291 $versioningVer = t3lib_div::intInRange($GLOBALS['TCA'][$table]['ctrl']['versioningWS'], 0, 2, 0); 00292 if ($versioningVer > 0) { 00293 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, $where, $values); 00294 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery; 00295 } 00296 } 00297 } 00298 00299 00300 /** 00301 * Check if there's any workspace which doesn't support the new admin-field format yet 00302 * @return bool 00303 */ 00304 protected function isOldStyleAdminFieldUsed() { 00305 $where = 'adminusers != "" AND adminusers NOT LIKE "%be_users%" AND adminusers NOT LIKE "%be_groups%" AND deleted=0'; 00306 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', 'sys_workspace', $where); 00307 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery; 00308 return $count > 0; 00309 } 00310 00311 /** 00312 * Create a real workspace named "Draft" 00313 * 00314 * @return integer 00315 */ 00316 protected function createWorkspace() { 00317 // @todo who are the reviewers and owners for this workspace? 00318 // In previous versions this was defined in be_groups/be_users with the setting "Edit in Draft" 00319 $data = array( 00320 'title' => 'Draft' 00321 ); 00322 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_workspace', $data); 00323 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery; 00324 return $GLOBALS['TYPO3_DB']->sql_insert_id(); 00325 } 00326 00327 /** 00328 * Migrates all elements from the old draft workspace to the new one. 00329 * 00330 * @param integer $wsId 00331 * @return void 00332 */ 00333 protected function migrateDraftWorkspaceRecordsToWorkspace($wsId) { 00334 $tables = array_keys($GLOBALS['TCA']); 00335 $where = 't3ver_wsid=-1'; 00336 $values = array( 00337 't3ver_wsid' => intval($wsId) 00338 ); 00339 foreach($tables as $table) { 00340 $versioningVer = t3lib_div::intInRange($GLOBALS['TCA'][$table]['ctrl']['versioningWS'], 0, 2, 0); 00341 if ($versioningVer > 0 && $this->hasElementsOnWorkspace($table, -1)) { 00342 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, $where, $values); 00343 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery; 00344 } 00345 } 00346 } 00347 00348 /** 00349 * Migrate all workspace adminusers fields to support groups aswell, 00350 * this means that the old comma separated list of uids (referring to be_users) 00351 * is updated to be a list of uids with the tablename as prefix 00352 * 00353 * @return void 00354 */ 00355 protected function migrateAdminFieldToNewStyle() { 00356 $where = 'adminusers != "" AND adminusers NOT LIKE "%be_users%" AND adminusers NOT LIKE "%be_groups%" AND deleted=0'; 00357 $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, adminusers', 'sys_workspace', $where); 00358 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery; 00359 foreach ($workspaces as $workspace) { 00360 $updateArray = array( 00361 'adminusers' => 'be_users_' . implode(',be_users_', t3lib_div::trimExplode(',', $workspace['adminusers'], TRUE)), 00362 ); 00363 $GLOBALS['TYPO3_DB']->exec_UPDATEquery( 00364 'sys_workspace', 00365 'uid = "' . $workspace['uid'] . '"', 00366 $updateArray 00367 ); 00368 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery; 00369 } 00370 } 00371 00372 /** 00373 * Includes the TCA definition of installed extensions. 00374 * 00375 * This method is used because usually the TCA is included within the init.php script, this doesn't happen 00376 * if the install-tool is used, therefore this has to be done by hand. 00377 * 00378 * @return void 00379 */ 00380 protected function includeTCA() { 00381 global $TCA; // this is relevant because it's used within the included ext_tables.php files - do NOT remove it 00382 00383 include_once(TYPO3_tables_script ? PATH_typo3conf . TYPO3_tables_script : PATH_t3lib . 'stddb/tables.php'); 00384 // Extension additions 00385 if ($GLOBALS['TYPO3_LOADED_EXT']['_CACHEFILE']) { 00386 include_once(PATH_typo3conf . $GLOBALS['TYPO3_LOADED_EXT']['_CACHEFILE'] . '_ext_tables.php'); 00387 } else { 00388 include_once(PATH_t3lib . 'stddb/load_ext_tables.php'); 00389 } 00390 } 00391 00392 /** 00393 * Determines whether a table has elements in a particular workspace. 00394 * 00395 * @param string $table Name of the table 00396 * @param integer $workspaceId Id of the workspace 00397 * @return boolean 00398 */ 00399 protected function hasElementsOnWorkspace($table, $workspaceId) { 00400 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows( 00401 'uid', 00402 $table, 00403 't3ver_wsid=' . intval($workspaceId) 00404 ); 00405 00406 $this->sqlQueries[] = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery; 00407 00408 return ($count > 0); 00409 } 00410 00411 /** 00412 * Returns all sys_workspace records which are not referenced by any sys_workspace_stages record 00413 * 00414 * @return array 00415 */ 00416 protected function getWorkspacesWithoutStages() { 00417 $stages = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('parentid', 'sys_workspace_stage', 'parenttable="sys_workspace"'); 00418 $wsWhitelist = array(); 00419 foreach ($stages as $stage) { 00420 $wsWhitelist[] = $stage['parentid']; 00421 } 00422 $where = 'deleted=0'; 00423 $where .= (!empty($wsWhitelist) ? ' AND uid NOT IN (' . implode(',', $wsWhitelist) . ')' : ''); 00424 return $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'sys_workspace', $where); 00425 } 00426 } 00427 ?>
1.8.0