|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2010-2011 Workspaces Team (http://forge.typo3.org/projects/show/typo3v4-workspaces) 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 * @author Workspaces Team (http://forge.typo3.org/projects/show/typo3v4-workspaces) 00030 * @package Workspaces 00031 * @subpackage Service 00032 */ 00033 class tx_Workspaces_Service_Workspaces { 00034 const TABLE_WORKSPACE = 'sys_workspace'; 00035 const SELECT_ALL_WORKSPACES = -98; 00036 const LIVE_WORKSPACE_ID = 0; 00037 const DRAFT_WORKSPACE_ID = -1; 00038 00039 /** 00040 * retrieves the available workspaces from the database and checks whether 00041 * they're available to the current BE user 00042 * 00043 * @return array array of worspaces available to the current user 00044 */ 00045 public function getAvailableWorkspaces() { 00046 $availableWorkspaces = array(); 00047 00048 // add default workspaces 00049 if ($GLOBALS['BE_USER']->checkWorkspace(array('uid' => (string) self::LIVE_WORKSPACE_ID))) { 00050 $availableWorkspaces[self::LIVE_WORKSPACE_ID] = self::getWorkspaceTitle(self::LIVE_WORKSPACE_ID); 00051 } 00052 if ($GLOBALS['BE_USER']->checkWorkspace(array('uid' => (string) self::DRAFT_WORKSPACE_ID))) { 00053 $availableWorkspaces[self::DRAFT_WORKSPACE_ID] = self::getWorkspaceTitle(self::DRAFT_WORKSPACE_ID); 00054 } 00055 00056 // add custom workspaces (selecting all, filtering by BE_USER check): 00057 $customWorkspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, title, adminusers, members', 'sys_workspace', 'pid = 0' . t3lib_BEfunc::deleteClause('sys_workspace'), '', 'title'); 00058 if (count($customWorkspaces)) { 00059 foreach ($customWorkspaces as $workspace) { 00060 if ($GLOBALS['BE_USER']->checkWorkspace($workspace)) { 00061 $availableWorkspaces[$workspace['uid']] = htmlspecialchars($workspace['title']); 00062 } 00063 } 00064 } 00065 00066 return $availableWorkspaces; 00067 } 00068 00069 00070 /** 00071 * Find the title for the requested workspace. 00072 * 00073 * @param integer $wsId 00074 * @return string 00075 */ 00076 public static function getWorkspaceTitle($wsId) { 00077 $title = FALSE; 00078 switch ($wsId) { 00079 case self::LIVE_WORKSPACE_ID: 00080 $title = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_misc.xml:shortcut_onlineWS'); 00081 break; 00082 case self::DRAFT_WORKSPACE_ID: 00083 $title = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_misc.xml:shortcut_offlineWS'); 00084 break; 00085 default: 00086 $labelField = $GLOBALS['TCA']['sys_workspace']['ctrl']['label']; 00087 $wsRecord = t3lib_beFunc::getRecord('sys_workspace', $wsId, 'uid,' . $labelField); 00088 if (is_array($wsRecord)) { 00089 $title = $wsRecord[$labelField]; 00090 } 00091 } 00092 00093 if ($title === FALSE) { 00094 throw new InvalidArgumentException('No such workspace defined'); 00095 } 00096 00097 return $title; 00098 } 00099 00100 00101 /** 00102 * Building tcemain CMD-array for swapping all versions in a workspace. 00103 * 00104 * @param integer Real workspace ID, cannot be ONLINE (zero). 00105 * @param boolean If set, then the currently online versions are swapped into the workspace in exchange for the offline versions. Otherwise the workspace is emptied. 00106 * @param integer $pageId: ... 00107 * @return array Command array for tcemain 00108 */ 00109 public function getCmdArrayForPublishWS($wsid, $doSwap, $pageId = 0) { 00110 00111 $wsid = intval($wsid); 00112 $cmd = array(); 00113 00114 if ($wsid >= -1 && $wsid!==0) { 00115 00116 // Define stage to select: 00117 $stage = -99; 00118 if ($wsid > 0) { 00119 $workspaceRec = t3lib_BEfunc::getRecord('sys_workspace', $wsid); 00120 if ($workspaceRec['publish_access'] & 1) { 00121 $stage = Tx_Workspaces_Service_Stages::STAGE_PUBLISH_ID; 00122 } 00123 } 00124 00125 // Select all versions to swap: 00126 $versions = $this->selectVersionsInWorkspace($wsid, 0, $stage, ($pageId ? $pageId : -1)); 00127 00128 // Traverse the selection to build CMD array: 00129 foreach ($versions as $table => $records) { 00130 foreach ($records as $rec) { 00131 // Build the cmd Array: 00132 $cmd[$table][$rec['t3ver_oid']]['version'] = array('action' => 'swap', 'swapWith' => $rec['uid'], 'swapIntoWS' => $doSwap ? 1 : 0); 00133 } 00134 } 00135 } 00136 return $cmd; 00137 } 00138 00139 00140 /** 00141 * Building tcemain CMD-array for releasing all versions in a workspace. 00142 * 00143 * @param integer Real workspace ID, cannot be ONLINE (zero). 00144 * @param boolean Run Flush (true) or ClearWSID (false) command 00145 * @param integer $pageId: ... 00146 * @return array Command array for tcemain 00147 */ 00148 public function getCmdArrayForFlushWS($wsid, $flush = TRUE, $pageId = 0) { 00149 00150 $wsid = intval($wsid); 00151 $cmd = array(); 00152 00153 if ($wsid >= -1 && $wsid!==0) { 00154 // Define stage to select: 00155 $stage = -99; 00156 00157 // Select all versions to swap: 00158 $versions = $this->selectVersionsInWorkspace($wsid, 0, $stage, ($pageId ? $pageId : -1)); 00159 00160 // Traverse the selection to build CMD array: 00161 foreach ($versions as $table => $records) { 00162 foreach ($records as $rec) { 00163 // Build the cmd Array: 00164 $cmd[$table][$rec['uid']]['version'] = array('action' => ($flush ? 'flush' : 'clearWSID')); 00165 } 00166 } 00167 } 00168 return $cmd; 00169 } 00170 00171 00172 /** 00173 * Select all records from workspace pending for publishing 00174 * Used from backend to display workspace overview 00175 * User for auto-publishing for selecting versions for publication 00176 * 00177 * @param integer Workspace ID. If -99, will select ALL versions from ANY workspace. If -98 will select all but ONLINE. >=-1 will select from the actual workspace 00178 * @param integer Lifecycle filter: 1 = select all drafts (never-published), 2 = select all published one or more times (archive/multiple), anything else selects all. 00179 * @param integer Stage filter: -99 means no filtering, otherwise it will be used to select only elements with that stage. For publishing, that would be "10" 00180 * @param integer Page id: Live page for which to find versions in workspace! 00181 * @param integer Recursion Level - select versions recursive - parameter is only relevant if $pageId != -1 00182 * @return array Array of all records uids etc. First key is table name, second key incremental integer. Records are associative arrays with uid, t3ver_oid and t3ver_swapmode fields. The pid of the online record is found as "livepid" the pid of the offline record is found in "wspid" 00183 */ 00184 public function selectVersionsInWorkspace($wsid, $filter = 0, $stage = -99, $pageId = -1, $recursionLevel = 0) { 00185 00186 $wsid = intval($wsid); 00187 $filter = intval($filter); 00188 $output = array(); 00189 00190 // Contains either nothing or a list with live-uids 00191 if ($pageId != -1 && $recursionLevel > 0) { 00192 $pageList = $this->getTreeUids($pageId, $wsid, $recursionLevel); 00193 } else if ($pageId != -1) { 00194 $pageList = $pageId; 00195 } else { 00196 $pageList = ''; 00197 } 00198 00199 // Traversing all tables supporting versioning: 00200 foreach ($GLOBALS['TCA'] as $table => $cfg) { 00201 if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) { 00202 00203 $recs = $this->selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage); 00204 if (intval($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) === 2) { 00205 $moveRecs = $this->getMoveToPlaceHolderFromPages($table, $pageList, $wsid, $filter, $stage); 00206 $recs = array_merge($recs, $moveRecs); 00207 } 00208 $recs = $this->filterPermittedElements($recs, $table); 00209 if (count($recs)) { 00210 $output[$table] = $recs; 00211 } 00212 } 00213 } 00214 return $output; 00215 } 00216 00217 /** 00218 * Find all versionized elements except moved records. 00219 * 00220 * @param string $table 00221 * @param string $pageList 00222 * @param integer $filter 00223 * @param integer $stage 00224 * @return array 00225 */ 00226 protected function selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage) { 00227 00228 $fields = 'A.uid, A.t3ver_oid,' . ($table==='pages' ? ' A.t3ver_swapmode,' : '') . 'B.pid AS wspid, B.pid AS livepid'; 00229 $from = $table . ' A,' . $table . ' B'; 00230 00231 // Table A is the offline version and pid=-1 defines offline 00232 $where = 'A.pid=-1 AND A.t3ver_state!=4'; 00233 if ($pageList) { 00234 $pidField = ($table==='pages' ? 'uid' : 'pid'); 00235 $pidConstraint = strstr($pageList, ',') ? ' IN (' . $pageList . ')' : '=' . $pageList; 00236 $where .= ' AND B.' . $pidField . $pidConstraint; 00237 } 00238 00239 /** 00240 * For "real" workspace numbers, select by that. 00241 * If = -98, select all that are NOT online (zero). 00242 * Anything else below -1 will not select on the wsid and therefore select all! 00243 */ 00244 if ($wsid > self::SELECT_ALL_WORKSPACES) { 00245 $where .= ' AND A.t3ver_wsid=' . $wsid; 00246 } else if ($wsid === self::SELECT_ALL_WORKSPACES) { 00247 $where .= ' AND A.t3ver_wsid!=0'; 00248 } 00249 00250 /** 00251 * lifecycle filter: 00252 * 1 = select all drafts (never-published), 00253 * 2 = select all published one or more times (archive/multiple) 00254 */ 00255 if ($filter===1 || $filter===2) { 00256 $where .= ' AND A.t3ver_count ' . ($filter === 1 ? '= 0' : '> 0'); 00257 } 00258 00259 if ($stage != -99) { 00260 $where .= ' AND A.t3ver_stage=' . intval($stage); 00261 } 00262 00263 // Table B (online) must have PID >= 0 to signify being online. 00264 $where .= ' AND B.pid>=0'; 00265 // ... and finally the join between the two tables. 00266 $where .= ' AND A.t3ver_oid=B.uid'; 00267 $where .= t3lib_BEfunc::deleteClause($table, 'A'); 00268 $where .= t3lib_BEfunc::deleteClause($table, 'B'); 00269 00270 /** 00271 * Select all records from this table in the database from the workspace 00272 * This joins the online version with the offline version as tables A and B 00273 * Order by UID, mostly to have a sorting in the backend overview module which doesn't "jump around" when swapping. 00274 */ 00275 $res = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows($fields, $from, $where, '', 'B.uid'); 00276 return is_array($res) ? $res : array(); 00277 } 00278 00279 /** 00280 * Find all moved records at their new position. 00281 * 00282 * @param string $table 00283 * @param string $pageList 00284 * @param integer $wsid 00285 * @return array 00286 */ 00287 protected function getMoveToPlaceHolderFromPages($table, $pageList, $wsid, $filter, $stage) { 00288 00289 /** 00290 * Aliases: 00291 * A - moveTo placeholder 00292 * B - online record 00293 * C - moveFrom placeholder 00294 */ 00295 $fields = 'A.pid AS wspid, B.uid AS t3ver_oid, C.uid AS uid, B.pid AS livepid'; 00296 $from = $table . ' A, ' . $table . ' B,' . $table . ' C'; 00297 $where = 'A.t3ver_state=3 AND B.pid>0 AND B.t3ver_state=0 AND B.t3ver_wsid=0 AND C.pid=-1 AND C.t3ver_state=4'; 00298 00299 if ($wsid > self::SELECT_ALL_WORKSPACES) { 00300 $where .= ' AND A.t3ver_wsid=' . $wsid . ' AND C.t3ver_wsid=' . $wsid; 00301 } else if ($wsid === self::SELECT_ALL_WORKSPACES) { 00302 $where .= ' AND A.t3ver_wsid!=0 AND C.t3ver_wsid!=0 '; 00303 } 00304 00305 /** 00306 * lifecycle filter: 00307 * 1 = select all drafts (never-published), 00308 * 2 = select all published one or more times (archive/multiple) 00309 */ 00310 if ($filter===1 || $filter===2) { 00311 $where .= ' AND C.t3ver_count ' . ($filter === 1 ? '= 0' : '> 0'); 00312 } 00313 00314 if ($stage != -99) { 00315 $where .= ' AND C.t3ver_stage=' . intval($stage); 00316 } 00317 00318 if ($pageList) { 00319 $pidField = ($table==='pages' ? 'B.uid' : 'A.pid'); 00320 $pidConstraint = strstr($pageList, ',') ? ' IN (' . $pageList . ')' : '=' . $pageList; 00321 $where .= ' AND ' . $pidField . $pidConstraint; 00322 } 00323 00324 $where .= ' AND A.t3ver_move_id = B.uid AND B.uid = C.t3ver_oid'; 00325 $where .= t3lib_BEfunc::deleteClause($table, 'A'); 00326 $where .= t3lib_BEfunc::deleteClause($table, 'B'); 00327 $where .= t3lib_BEfunc::deleteClause($table, 'C'); 00328 $res = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows($fields, $from, $where, '', 'A.uid'); 00329 00330 return is_array($res) ? $res : array(); 00331 } 00332 00333 00334 /** 00335 * Find all page uids recursive starting from a specific page 00336 * 00337 * @param integer $pageId 00338 * @param integer $wsid 00339 * @param integer $recursionLevel 00340 * @return string Comma sep. uid list 00341 */ 00342 protected function getTreeUids($pageId, $wsid, $recursionLevel) { 00343 /** 00344 * Reusing existing functionality with the drawback that 00345 * mount points are not covered yet 00346 **/ 00347 $perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1); 00348 $searchObj = t3lib_div::makeInstance('t3lib_fullsearch'); 00349 $pageList = FALSE; 00350 if ($pageId > 0) { 00351 $pageList = $searchObj->getTreeList($pageId, $recursionLevel, 0, $perms_clause); 00352 } else { 00353 $mountPoints = $GLOBALS['BE_USER']->uc['pageTree_temporaryMountPoint']; 00354 if (!is_array($mountPoints) || empty($mountPoints)) { 00355 $mountPoints = array_map('intval', $GLOBALS['BE_USER']->returnWebmounts()); 00356 $mountPoints = array_unique($mountPoints); 00357 } 00358 $newList = array(); 00359 foreach($mountPoints as $mountPoint) { 00360 $newList[] = $searchObj->getTreeList($mountPoint, $recursionLevel, 0, $perms_clause); 00361 } 00362 $pageList = implode(',', $newList); 00363 } 00364 unset($searchObj); 00365 if (intval($GLOBALS['TCA']['pages']['ctrl']['versioningWS']) === 2 && $pageList) { 00366 if ($pageList) { 00367 // Remove the "subbranch" if a page was moved away 00368 $movedAwayPages = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, pid, t3ver_move_id', 'pages', 't3ver_move_id IN (' . $pageList . ') AND t3ver_wsid=' . $wsid . t3lib_BEfunc::deleteClause($table), '', 'uid', '', 't3ver_move_id'); 00369 $pageIds = t3lib_div::intExplode(',', $pageList, TRUE); 00370 00371 // move all pages away 00372 $newList = array_diff($pageIds, array_keys($movedAwayPages)); 00373 00374 // move back in if still connected to the "remaining" pages 00375 do { 00376 $changed = FALSE; 00377 foreach ($movedAwayPages as $uid => $rec) { 00378 if (in_array($rec['pid'], $newList) && !in_array($uid, $newList)) { 00379 $newList[] = $uid; 00380 $changed = TRUE; 00381 } 00382 } 00383 } while ($changed); 00384 00385 $pageList = implode(',', $newList); 00386 } 00387 // In case moving pages is enabled we need to replace all move-to pointer with their origin 00388 $pages = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, t3ver_move_id', 'pages', 'uid IN (' . $pageList . ')' . t3lib_BEfunc::deleteClause($table), '', 'uid', '', 'uid'); 00389 00390 $newList = array(); 00391 $pageIds = t3lib_div::intExplode(',', $pageList, TRUE); 00392 if (!in_array($pageId, $pageIds)) { 00393 $pageIds[] = $pageId; 00394 } 00395 foreach ($pageIds as $pageId) { 00396 if (intval($pages[$pageId]['t3ver_move_id']) > 0) { 00397 $newList[] = intval($pages[$pageId]['t3ver_move_id']); 00398 } else { 00399 $newList[] = $pageId; 00400 } 00401 } 00402 $pageList = implode(',', $newList); 00403 } 00404 return $pageList; 00405 } 00406 00407 /** 00408 * Remove all records which are not permitted for the user 00409 * 00410 * @param array $recs 00411 * @param string $table 00412 * @return array 00413 */ 00414 protected function filterPermittedElements($recs, $table) { 00415 $checkField = ($table == 'pages') ? 'uid' : 'wspid'; 00416 $permittedElements = array(); 00417 if (is_array($recs)) { 00418 foreach ($recs as $rec) { 00419 $page = t3lib_beFunc::getRecord('pages', $rec[$checkField], 'uid,pid,perms_userid,perms_user,perms_groupid,perms_group,perms_everybody'); 00420 if ($GLOBALS['BE_USER']->doesUserHaveAccess($page, 1)) { 00421 $permittedElements[] = $rec; 00422 } 00423 } 00424 } 00425 return $permittedElements; 00426 } 00427 00428 00429 /** 00430 * Trivial check to see if the user already migrated his workspaces 00431 * to the new style (either manually or with the migrator scripts) 00432 * 00433 * @return bool 00434 */ 00435 public static function isOldStyleWorkspaceUsed() { 00436 $oldStyleWorkspaceIsUsed = FALSE; 00437 $cacheKey = 'workspace-oldstyleworkspace-notused'; 00438 $cacheResult = $GLOBALS['BE_USER']->getSessionData($cacheKey); 00439 if (!$cacheResult) { 00440 $where = 'adminusers != "" AND adminusers NOT LIKE "%be_users%" AND adminusers NOT LIKE "%be_groups%" AND deleted=0'; 00441 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', 'sys_workspace', $where); 00442 $oldStyleWorkspaceIsUsed = $count > 0; 00443 $GLOBALS['BE_USER']->setAndSaveSessionData($cacheKey, !$oldStyleWorkspaceIsUsed); 00444 } else { 00445 $oldStyleWorkspaceIsUsed = !$cacheResult; 00446 } 00447 return $oldStyleWorkspaceIsUsed; 00448 } 00449 00450 /** 00451 * Determine whether a specific page is new and not yet available in the LIVE workspace 00452 * 00453 * @static 00454 * @param $id Primary key of the page to check 00455 * @param $language Language for which to check the page 00456 * @return bool 00457 */ 00458 public static function isNewPage($id, $language = 0) { 00459 $isNewPage = FALSE; 00460 // If the language is not default, check state of overlay 00461 if ($language > 0) { 00462 $whereClause = 'pid = ' . $id; 00463 $whereClause .= ' AND ' .$GLOBALS['TCA']['pages_language_overlay']['ctrl']['languageField'] . ' = ' . $language; 00464 $whereClause .= ' AND t3ver_wsid = ' . $GLOBALS['BE_USER']->workspace; 00465 $whereClause .= t3lib_BEfunc::deleteClause('pages_language_overlay'); 00466 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('t3ver_state', 'pages_language_overlay', $whereClause); 00467 if (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) { 00468 $isNewPage = (int) $row['t3ver_state'] === 1; 00469 } 00470 00471 // Otherwise check state of page itself 00472 } else { 00473 $rec = t3lib_BEfunc::getRecord('pages', $id, 't3ver_state'); 00474 if (is_array($rec)) { 00475 $isNewPage = (int) $rec['t3ver_state'] === 1; 00476 } 00477 } 00478 return $isNewPage; 00479 } 00480 00481 /** 00482 * Generates a view link for a page. 00483 * 00484 * @static 00485 * @param $table 00486 * @param $uid 00487 * @param $record 00488 * @return string 00489 */ 00490 public static function viewSingleRecord($table, $uid, $record=null) { 00491 $viewUrl = ''; 00492 if ($table == 'pages') { 00493 $viewUrl = t3lib_BEfunc::viewOnClick(t3lib_BEfunc::getLiveVersionIdOfRecord('pages', $uid)); 00494 } elseif ($table == 'pages_language_oderlay' || $table == 'tt_content') { 00495 $elementRecord = is_array($record) ? $record : t3lib_BEfunc::getLiveVersionOfRecord($table, $uid); 00496 $viewUrl = t3lib_BEfunc::viewOnClick($elementRecord['pid']); 00497 } else { 00498 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['workspaces']['viewSingleRecord'])) { 00499 $_params = array('table' => $table, 'uid' => $uid, 'record' => $record); 00500 $_funcRef = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['workspaces']['viewSingleRecord']; 00501 $viewUrl = t3lib_div::callUserFunction($_funcRef, $_params, null); 00502 } 00503 } 00504 return $viewUrl; 00505 } 00506 } 00507 00508 00509 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/workspaces/Classes/Service/Workspaces.php'])) { 00510 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/workspaces/Classes/Service/Workspaces.php']); 00511 } 00512 ?>
1.8.0