';
$workspaceMenu[] = '';
Index: typo3/sysext/version/cm1/index.php
===================================================================
--- typo3/sysext/version/cm1/index.php (revision 8193)
+++ typo3/sysext/version/cm1/index.php (working copy)
@@ -84,7 +84,7 @@
$LANG->includeLLFile('EXT:version/locallang.xml');
// DEFAULT initialization of a module [END]
-require_once(PATH_typo3.'mod/user/ws/class.wslib.php');
+require_once('../ws/class.wslib.php');
@@ -762,7 +762,7 @@
* Rendering the overview of versions in the current workspace
*
* @return string HTML (table)
- * @see typo3/mod/user/ws/index.php for sister function!
+ * @see ws/index.php for sister function!
*/
function displayWorkspaceOverview() {
Index: typo3/sysext/version/tasks/class.tx_version_tasks_autopublish.php
===================================================================
--- typo3/sysext/version/tasks/class.tx_version_tasks_autopublish.php (revision 8193)
+++ typo3/sysext/version/tasks/class.tx_version_tasks_autopublish.php (working copy)
@@ -43,7 +43,7 @@
*/
public function execute() {
// Load the workspace library class and instatiate it
- require_once(PATH_typo3 . 'mod/user/ws/class.wslib.php');
+ require_once(t3lib_extMgm::extPath('version') . 'ws/class.wslib.php');
$autopubObj = t3lib_div::makeInstance('wslib');
// Publish the workspaces that need to be
$autopubObj->autoPublishWorkspaces();
Index: typo3/sysext/version/ws/class.wslib.php
===================================================================
--- typo3/sysext/version/ws/class.wslib.php (revision 0)
+++ typo3/sysext/version/ws/class.wslib.php (revision 0)
@@ -0,0 +1,240 @@
+
+ */
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ *
+ *
+ * 68: class wslib
+ * 81: function getCmdArrayForPublishWS($wsid, $doSwap,$pageId=0)
+ * 127: function selectVersionsInWorkspace($wsid,$filter=0,$stage=-99,$pageId=-1)
+ *
+ * SECTION: CLI functions
+ * 183: function CLI_main()
+ * 193: function autoPublishWorkspaces()
+ *
+ * TOTAL FUNCTIONS: 4
+ * (This index is automatically created/updated by the extension "extdeveval")
+ *
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Library with Workspace related functionality
+ *
+ * @author Kasper Skaarhoj
+ * @package TYPO3
+ * @subpackage core
+ */
+class wslib {
+
+
+
+
+ /**
+ * Building tcemain CMD-array for swapping all versions in a workspace.
+ *
+ * @param integer Real workspace ID, cannot be ONLINE (zero).
+ * @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.
+ * @param [type] $pageId: ...
+ * @return array Command array for tcemain
+ */
+ function getCmdArrayForPublishWS($wsid, $doSwap,$pageId=0) {
+
+ $wsid = intval($wsid);
+ $cmd = array();
+
+ if ($wsid>=-1 && $wsid!==0) {
+
+ // Define stage to select:
+ $stage = -99;
+ if ($wsid>0) {
+ $workspaceRec = t3lib_BEfunc::getRecord('sys_workspace',$wsid);
+ if ($workspaceRec['publish_access']&1) {
+ $stage = 10;
+ }
+ }
+
+ // Select all versions to swap:
+ $versions = $this->selectVersionsInWorkspace($wsid,0,$stage,($pageId?$pageId:-1));
+
+ // Traverse the selection to build CMD array:
+ foreach($versions as $table => $records) {
+ foreach($records as $rec) {
+
+ // Build the cmd Array:
+ $cmd[$table][$rec['t3ver_oid']]['version'] = array(
+ 'action' => 'swap',
+ 'swapWith' => $rec['uid'],
+ 'swapIntoWS' => $doSwap ? 1 : 0
+ );
+ }
+ }
+ }
+ return $cmd;
+ }
+
+ /**
+ * Select all records from workspace pending for publishing
+ * Used from backend to display workspace overview
+ * User for auto-publishing for selecting versions for publication
+ *
+ * @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
+ * @param integer Lifecycle filter: 1 = select all drafts (never-published), 2 = select all published one or more times (archive/multiple), anything else selects all.
+ * @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"
+ * @param integer Page id: Live page for which to find versions in workspace!
+ * @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 REAL pid of the online record is found as "realpid"
+ */
+ function selectVersionsInWorkspace($wsid,$filter=0,$stage=-99,$pageId=-1) {
+ global $TCA;
+
+ $wsid = intval($wsid);
+ $filter = intval($filter);
+ $output = array();
+
+ // Traversing all tables supporting versioning:
+ foreach($TCA as $table => $cfg) {
+ if ($TCA[$table]['ctrl']['versioningWS']) {
+
+ // Select all records from this table in the database from the workspace
+ // This joins the online version with the offline version as tables A and B
+ $recs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows (
+ 'A.uid, A.t3ver_oid,'.($table==='pages' ? ' A.t3ver_swapmode,':'').' B.pid AS realpid',
+ $table.' A,'.$table.' B',
+ 'A.pid=-1'. // Table A is the offline version and pid=-1 defines offline
+ ($pageId!=-1 ? ($table==='pages' ? ' AND B.uid='.intval($pageId) : ' AND B.pid='.intval($pageId)) : '').
+ ($wsid>-98 ? ' AND A.t3ver_wsid='.$wsid : ($wsid===-98 ? ' AND A.t3ver_wsid!=0' : '')). // For "real" workspace numbers, select by that. If = -98, select all that are NOT online (zero). Anything else below -1 will not select on the wsid and therefore select all!
+ ($filter===1 ? ' AND A.t3ver_count=0' : ($filter===2 ? ' AND A.t3ver_count>0' : '')). // lifecycle filter: 1 = select all drafts (never-published), 2 = select all published one or more times (archive/multiple)
+ ($stage!=-99 ? ' AND A.t3ver_stage='.intval($stage) : '').
+ ' AND B.pid>=0'. // Table B (online) must have PID >= 0 to signify being online.
+ ' AND A.t3ver_oid=B.uid'. // ... and finally the join between the two tables.
+ t3lib_BEfunc::deleteClause($table,'A').
+ t3lib_BEfunc::deleteClause($table,'B'),
+ '',
+ 'B.uid' // Order by UID, mostly to have a sorting in the backend overview module which doesn't "jump around" when swapping.
+ );
+ if (count($recs)) {
+ $output[$table] = $recs;
+ }
+ }
+ }
+
+ return $output;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ /****************************
+ *
+ * Scheduler methods
+ *
+ ****************************/
+
+ /**
+ * Main function to call from cli-script
+ *
+ * @return void
+ * @deprecated since TYPO3 4.5 - This was meant for an obsolete CLI script. You shouldn't be calling this.
+ */
+ function CLI_main() {
+ self::logDeprecatedFunction();
+ $this->autoPublishWorkspaces();
+ }
+
+ /**
+ * This method is called by the Scheduler task that triggers
+ * the autopublication process
+ * It searches for workspaces whose publication date is in the past
+ * and publishes them
+ *
+ * @return void
+ */
+ function autoPublishWorkspaces() {
+ global $TYPO3_CONF_VARS;
+
+ // Temporarily set high power...
+ $GLOBALS['BE_USER']->user['admin'] = 1;
+
+ // Select all workspaces that needs to be published / unpublished:
+ $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
+ 'uid,swap_modes,publish_time,unpublish_time',
+ 'sys_workspace',
+ 'pid=0
+ AND
+ ((publish_time!=0 AND publish_time<='.intval($GLOBALS['EXEC_TIME']).')
+ OR (publish_time=0 AND unpublish_time!=0 AND unpublish_time<='.intval($GLOBALS['EXEC_TIME']).'))'.
+ t3lib_BEfunc::deleteClause('sys_workspace')
+ );
+
+ foreach($workspaces as $rec) {
+
+ // First, clear start/end time so it doesn't get select once again:
+ $fieldArray = $rec['publish_time']!=0 ? array('publish_time'=>0) : array('unpublish_time'=>0);
+ $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_workspace','uid='.intval($rec['uid']),$fieldArray);
+
+ // Get CMD array:
+ $cmd = $this->getCmdArrayForPublishWS($rec['uid'], $rec['swap_modes']==1); // $rec['swap_modes']==1 means that auto-publishing will swap versions, not just publish and empty the workspace.
+
+ // Execute CMD array:
+ $tce = t3lib_div::makeInstance('t3lib_TCEmain');
+ $tce->stripslashes_values = 0;
+ $tce->start(array(),$cmd);
+ $tce->process_cmdmap();
+ }
+ }
+}
+
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/mod/user/ws/class.wslib.php']) {
+ include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/mod/user/ws/class.wslib.php']);
+}
+?>
\ No newline at end of file
Index: typo3/sysext/version/ws/class.wslib_gui.php
===================================================================
--- typo3/sysext/version/ws/class.wslib_gui.php (revision 0)
+++ typo3/sysext/version/ws/class.wslib_gui.php (revision 0)
@@ -0,0 +1,1309 @@
+
+ * @author Dmitry Dulepov
+ */
+/**
+ * [CLASS/FUNCTION INDEX of SCRIPT]
+ *
+ *
+ *
+ * 84: class wslib_gui
+ *
+ * SECTION: Public functions
+ * 128: function getWorkspaceOverview(&$doc, $wsid = null, $filter = 0, $pageId = -1)
+ * 192: function hlSubelements(origId, verId, over, diffLayer)
+ *
+ * SECTION: Private functions (do not use outside of this class!)
+ * 224: function initVars()
+ * 253: function displayWorkspaceOverview_setInPageArray(&$pArray, $rlArr, $table, $row)
+ * 284: function markupNewOriginals()
+ * 309: function displayWorkspaceOverview_list($pArray, $tableRows=array(), $c=0, $warnAboutVersions=FALSE)
+ * 504: function displayWorkspaceOverview_pageTreeIconTitle($pageUid, $title, $indentCount)
+ * 518: function formatVerId($verId)
+ * 529: function formatWorkspace($wsid)
+ * 559: function createDiffView($table, $diff_1_record, $diff_2_record)
+ * 676: function versionsInOtherWS($table, $uid)
+ * 705: function showStageChangeLog($table,$id,$stageCommands)
+ * 757: function displayWorkspaceOverview_commandLinks($table,&$rec_on,&$rec_off,$vType)
+ * 830: function formatCount($count)
+ * 860: function subElements($uid,$treeLevel,$origId=0)
+ * 963: function subElements_getNonPageRecords($tN, $uid, &$recList)
+ * 993: function subElements_renderItem(&$tCell,$tN,$uid,$rec,$origId,$iconMode,$HTMLdata)
+ * 1066: function displayWorkspaceOverview_commandLinksSub($table,$rec,$origId)
+ * 1113: function displayWorkspaceOverview_stageCmd($table,&$rec_off)
+ *
+ * TOTAL FUNCTIONS: 19
+ * (This index is automatically created/updated by the extension "extdeveval")
+ *
+ */
+
+require_once('class.wslib.php');
+$LANG->includeLLFile('EXT:lang/locallang_mod_user_ws.xml');
+$LANG->includeLLFile('EXT:lang/locallang_misc.xml');
+
+/**
+ * Library with Workspace GUI related functionality. It is used by main workspace
+ * module but also can be used from extensions. Originally 99.9%% of the code
+ * was written by Kasper and only transfered here by Dmitry.
+ *
+ * @author Kasper Skaarhoj
+ * @author Dmitry Dulepov
+ * @package TYPO3
+ * @subpackage core
+ */
+class wslib_gui {
+
+ // Static:
+ var $pageTreeIndent = 8;
+ var $pageTreeIndent_titleLgd = 30;
+
+ // Options
+ var $diff = false;
+ var $expandSubElements = false;
+ var $alwaysDisplayHeader = false;
+
+ // Internal
+ var $showWorkspaceCol = 0;
+ var $doc;
+ var $formatWorkspace_cache = array();
+ var $targets = array(); // Accumulation of online targets.
+ var $be_user_Array = array();
+ var $pageModule = '';
+ var $formatCount_cache = array();
+ var $stageIndex = array();
+ var $addHlSubelementsScript = false;
+
+ /*********************************
+ *
+ * Public functions
+ *
+ *********************************/
+
+ /**
+ * Creates HTML to display workspace overview. Can be used to display overview for all possible records or only for single page.
+ *
+ * The following code is required in BE module when this function is used:
+ *
+ * $this->doc->getContextMenuCode();
+ *
+ * or click-menu will not be generated properly!
+ *
+ * @param object $doc Document (to use for formatting)
+ * @param int $wsid Workspace ID, If null, the value is obtained from current BE user
+ * @param int $filter If 0, than no filtering, if 10 than select for publishing, otherwise stage value
+ * @param int $pageId If greater than zero, than it is UID of page in LIVE workspaces to select records for
+ * @return string Generated HTML
+ */
+ function getWorkspaceOverview(&$doc, $wsid = null, $filter = 0, $pageId = -1) {
+ global $LANG;
+
+ // Setup
+ $this->workspaceId = (!is_null($wsid) ? $wsid : $GLOBALS['BE_USER']->workspace);
+ $this->showWorkspaceCol = $GLOBALS['BE_USER']->workspace == 0 && $this->workspaceId <= -98;
+ $this->doc = $doc;
+ $this->initVars();
+
+ // Initialize workspace object and request all pending versions:
+ $wslibObj = t3lib_div::makeInstance('wslib');
+
+ // Selecting ALL versions belonging to the workspace:
+ $versions = $wslibObj->selectVersionsInWorkspace($this->workspaceId, $filter, -99, $pageId);
+
+ // Traverse versions and build page-display array:
+ $pArray = array();
+ $wmArray = array(); // is page in web mount?
+ $rlArray = array(); // root line of page
+ $pagePermsClause = $GLOBALS['BE_USER']->getPagePermsClause(1);
+ foreach($versions as $table => $records) {
+ if (is_array($records)) {
+ foreach($records as $rec) {
+ $pageIdField = $table==='pages' ? 't3ver_oid' : 'realpid';
+ $recPageId = $rec[$pageIdField];
+ if (!isset($wmArray[$recPageId])) {
+ $wmArray[$recPageId] = $GLOBALS['BE_USER']->isInWebMount($recPageId,$pagePermsClause);
+ }
+ if ($wmArray[$recPageId]) {
+ if (!isset($rlArray[$recPageId])) {
+ $rlArray[$recPageId] = t3lib_BEfunc::BEgetRootLine($recPageId, 'AND 1=1');
+ }
+ $this->displayWorkspaceOverview_setInPageArray(
+ $pArray,
+ $rlArray[$recPageId],
+ $table,
+ $rec
+ );
+ }
+ }
+ }
+ }
+
+ // Page-browser:
+ $resultsPerPage = 50;
+ $pointer = t3lib_div::_GP('browsePointer');
+ $browseStat = $this->cropWorkspaceOverview_list($pArray,$pointer,$resultsPerPage);
+ $browse = '';
+ $browse .= '
Showing ' . $browseStat['begin'] . ' to ' . ($browseStat['end'] ? $browseStat['end'] . ' out of ' . $browseStat['allItems'] : $browseStat['allItems']) . ' versions:
';
+ if (!($browseStat['begin']==1 && !$browseStat['end'])) {
+ for($a=0;$a':'').'['.($a+1).']'.($a==(int)$pointer?'':'').' ';
+ }
+ }
+
+ $workspaceOverviewList = $this->displayWorkspaceOverview_list($pArray);
+ if ($workspaceOverviewList || $this->alwaysDisplayHeader) {
+ // Make header of overview:
+ $tableRows = array();
+ $tableHeader = '
+
';
+
+ // script
+ if ($this->addHlSubelementsScript && !strstr($this->doc->JScode, 'function hlSubelements(')) {
+ $table = $this->doc->wrapScriptTags('
+ function hlSubelements(origId, verId, over, diffLayer) {
+ if (over) {
+ document.getElementById(\'orig_\'+origId).attributes.getNamedItem("class").nodeValue = \'typo3-ver-hl\';
+ document.getElementById(\'ver_\'+verId).attributes.getNamedItem("class").nodeValue = \'typo3-ver-hl\';
+ if (diffLayer) {
+ document.getElementById(\'diff_\'+verId).style.visibility = \'visible\';
+ }
+ } else {
+ document.getElementById(\'orig_\'+origId).attributes.getNamedItem("class").nodeValue = \'typo3-ver\';
+ document.getElementById(\'ver_\'+verId).attributes.getNamedItem("class").nodeValue = \'typo3-ver\';
+ if (diffLayer) {
+ document.getElementById(\'diff_\'+verId).style.visibility = \'hidden\';
+ }
+ }
+ }
+ ') . $table;
+ }
+
+ return $browse . $table . $this->markupNewOriginals();
+ }
+ return '';
+ }
+
+ /*********************************
+ *
+ * Private functions (do not use outside of this class!)
+ *
+ *********************************/
+
+ /**
+ * Initializes several class variables
+ *
+ * @return void
+ */
+ function initVars() {
+ // Init users
+ $be_group_Array = t3lib_BEfunc::getListGroupNames('title,uid');
+ $groupArray = array_keys($be_group_Array);
+ // Need 'admin' field for t3lib_iconWorks::getIconImage()
+ $this->be_user_Array = t3lib_BEfunc::getUserNames('username,usergroup,usergroup_cached_list,uid,admin,workspace_perms');
+ if (!$GLOBALS['BE_USER']->isAdmin()) {
+ $this->be_user_Array = t3lib_BEfunc::blindUserNames($this->be_user_Array,$groupArray,1);
+ }
+
+ // If another page module was specified, replace the default Page module with the new one
+ $newPageModule = trim($GLOBALS['BE_USER']->getTSConfigVal('options.overridePageModule'));
+ $this->pageModule = t3lib_BEfunc::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
+
+ // Setting publish access permission for workspace:
+ $this->publishAccess = $GLOBALS['BE_USER']->workspacePublishAccess($GLOBALS['BE_USER']->workspace); // FIXME Should be $this->workspaceId here?
+ }
+
+ /**
+ * Building up of the $pArray variable which is a hierarchical storage of table-rows arranged according to the level in the rootline the element was found
+ * (Internal)
+ * Made for recursive calling
+ *
+ * @param array Array that is built up with the page tree structure
+ * @param array Root line for element (table / row); The element is stored in pArray according to this root line.
+ * @param string Table name
+ * @param array Table row
+ * @return void $pArray is passed by reference and modified internally
+ */
+ function displayWorkspaceOverview_setInPageArray(&$pArray, $rlArr, $table, $row) {
+ // Initialize:
+ ksort($rlArr);
+ reset($rlArr);
+ if (!$rlArr[0]['uid']) {
+ array_shift($rlArr);
+ }
+
+ // Get and remove first element in root line:
+ $cEl = current($rlArr);
+ $pUid = $cEl['t3ver_oid'] ? $cEl['t3ver_oid'] : $cEl['uid']; // Done to pile up "false versions" in the right branch...
+
+ $pArray[$pUid] = $cEl['title'];
+ array_shift($rlArr);
+
+ // If there are elements left in the root line, call this function recursively (to build $pArray in depth)
+ if (count($rlArr)) {
+ if (!isset($pArray[$pUid.'.'])) {
+ $pArray[$pUid.'.'] = array();
+ }
+ $this->displayWorkspaceOverview_setInPageArray($pArray[$pUid.'.'], $rlArr, $table, $row);
+ } else { // If this was the last element, set the value:
+ $pArray[$pUid.'_'][$table][$row['t3ver_oid']][] = $row;
+ }
+ }
+
+ /**
+ * JavaScript code to mark up new records that are online (in sub element lists)
+ *
+ * @return string HTML javascript section
+ */
+ function markupNewOriginals() {
+
+ if (count($this->targets)) {
+ $scriptCode = '';
+ foreach($this->targets as $key => $rec) {
+ $scriptCode.='
+ document.getElementById(\''.$key.'\').attributes.getNamedItem("class").nodeValue = \'typo3-ver-new\';
+ ';
+ }
+
+ return $this->doc->wrapScriptTags($scriptCode);
+ }
+ }
+
+
+ /**
+ * Rendering the content for the publish / review overview:
+ * (Made for internal recursive calling)
+ *
+ * @param array Hierarchical storage of the elements to display (see displayWorkspaceOverview() / displayWorkspaceOverview_setInPageArray())
+ * @param array Existing array of table rows to add to
+ * @param array Depth counter
+ * @param boolean If set, a warning is shown if versions are found (internal flag)
+ * @return array Table rows, see displayWorkspaceOverview()
+ */
+ function displayWorkspaceOverview_list($pArray, $tableRows=array(), $c=0, $warnAboutVersions=FALSE) {
+ global $TCA, $LANG;
+
+ // Initialize:
+ $fullColSpan = $this->showWorkspaceCol ? 10 : 9;
+
+ // Traverse $pArray
+ if (is_array($pArray)) {
+ foreach($pArray as $k => $v) {
+ if (t3lib_div::testInt($k)) {
+
+ // If there are elements on this level, output a divider row which just creates a little visual space.
+ if (is_array($pArray[$k.'_'])) {
+ $tableRows[] = '
+
+
+
';
+ }
+
+ // Printing a level from the page tree with no additional content:
+ // If there are NO elements on this level OR if there are NO pages on a level with elements, then show page tree icon and title (otherwise it is shown with the display of the elements)
+ if (!is_array($pArray[$k.'_']) || !is_array($pArray[$k.'_']['pages'])) {
+ $tableRows[] = '
+
';
+ }
+
+ // If there ARE elements on this level, print them:
+ $warnAboutVersions_next = $warnAboutVersions;
+ $warnAboutVersions_nonPages = FALSE;
+ $warnAboutVersions_page = FALSE;
+ if (is_array($pArray[$k.'_'])) {
+ foreach($pArray[$k.'_'] as $table => $oidArray) {
+ foreach($oidArray as $oid => $recs) {
+
+ // Get CURRENT online record and icon based on "t3ver_oid":
+ $rec_on = t3lib_BEfunc::getRecord($table,$oid);
+ $icon = t3lib_iconWorks::getIconImage($table, $rec_on, $this->doc->backPath,' align="top" title="'.t3lib_BEfunc::getRecordIconAltText($rec_on,$table).'"');
+ if ($GLOBALS['BE_USER']->workspace===0) { // Only edit online records if in ONLINE workspace:
+ $icon = $this->doc->wrapClickMenuOnIcon($icon, $table, $rec_on['uid'], 2, '', '+edit,view,info,delete');
+ }
+
+ // MAIN CELL / Online version display:
+ // Create the main cells which will span over the number of versions there is. If the table is "pages" then it will show the page tree icon and title (which was not shown by the code above)
+ $verLinkUrl = t3lib_extMgm::isLoaded('version') && $TCA[$table]['ctrl']['versioningWS'];
+ $origElement = $icon.
+ ($verLinkUrl ? '' : '').
+ t3lib_BEfunc::getRecordTitle($table,$rec_on,TRUE).
+ ($verLinkUrl ? '' : '');
+ $mainCell_rowSpan = count($recs)>1 ? ' rowspan="'.count($recs).'"' : '';
+ $mainCell = $table==='pages' ? '
+
';
+
+ // Reset the main cell:
+ $mainCell = '';
+ }
+ }
+ }
+ }
+ // Call recursively for sub-rows:
+ $tableRows = $this->displayWorkspaceOverview_list($pArray[$k.'.'], $tableRows, $c+1, $warnAboutVersions_next);
+ }
+ }
+ }
+ return $tableRows;
+ }
+
+ /**
+ * Filtering out items in pArray according to pointer and result-per-page setting
+ *
+ * @param array Hierarchical storage of the elements to display (see displayWorkspaceOverview() / displayWorkspaceOverview_setInPageArray())
+ * @return array Returns statistics about the pointer state.
+ */
+ function cropWorkspaceOverview_list(&$pArray,$pointer=0,$resPerPage=50,$stat=array()) {
+
+ // Traverse $pArray
+ if (is_array($pArray)) {
+ foreach($pArray as $k => $v) {
+ if (t3lib_div::testInt($k)) {
+
+ if (is_array($pArray[$k.'_'])) {
+ foreach($pArray[$k.'_'] as $table => $oidArray) {
+ foreach($oidArray as $oid => $recs) {
+
+ // Check, if the item count has reached the point where we want to set the in-point.
+ $beginWasSet = FALSE;
+ if (!isset($stat['begin']) && (int)$stat['allItems'] >= $pointer*$resPerPage) {
+ $stat['begin']=(int)$stat['allItems']+1;
+ $beginWasSet = TRUE;
+ }
+
+ // If in-point is not set, unset the previous items.
+ if (!isset($stat['begin'])) {
+ unset($pArray[$k.'_'][$table][$oid]);
+ }
+
+ // Increase counter:
+ $stat['allItems']+=count($recs);
+
+ // Check if end-point is reached:
+ if (!$beginWasSet && !isset($stat['end']) && $stat['allItems'] > ($pointer+1)*$resPerPage) {
+ $stat['end']=$stat['allItems']-1;
+ }
+
+ // If end-point is reached, unset following items.
+ if (isset($stat['end'])) {
+ unset($pArray[$k.'_'][$table][$oid]);
+ }
+ }
+
+ // Clean-up if no more items:
+ if (!count($pArray[$k.'_'][$table])) {
+ unset($pArray[$k.'_'][$table]);
+ }
+ }
+
+ // Clean-up if no more items:
+ if (!count($pArray[$k.'_'])) {
+ unset($pArray[$k.'_']);
+ }
+ }
+
+ // Call recursively for sub-rows:
+ if (is_array($pArray[$k.'.'])) {
+ $stat = $this->cropWorkspaceOverview_list($pArray[$k.'.'],$pointer,$resPerPage,$stat);
+ }
+ }
+ }
+ }
+ return $stat;
+ }
+
+ /**
+ * Create indentation, icon and title for the page tree identification for the list.
+ *
+ * @param integer Page UID (record will be looked up again)
+ * @param string Page title
+ * @param integer Depth counter from displayWorkspaceOverview_list() used to indent the icon and title
+ * @return string HTML content
+ */
+ function displayWorkspaceOverview_pageTreeIconTitle($pageUid, $title, $indentCount) {
+ $pRec = t3lib_BEfunc::getRecord('pages',$pageUid);
+ return ''. // Indenting page tree
+ t3lib_iconWorks::getIconImage('pages',$pRec,$this->doc->backPath,' align="top" title="'.t3lib_BEfunc::getRecordIconAltText($pRec,'pages').'"').
+ htmlspecialchars(t3lib_div::fixed_lgd_cs($title,$this->pageTreeIndent_titleLgd)).
+ ' ';
+ }
+
+ /**
+ * Formatting the version number for HTML output
+ *
+ * @param integer Version number
+ * @return string Version number for output
+ */
+ function formatVerId($verId) {
+ return '1.'.$verId;
+ }
+
+
+ /**
+ * Formatting workspace ID into a visual label
+ *
+ * @param integer Workspace ID
+ * @return string Workspace title
+ */
+ function formatWorkspace($wsid) {
+
+ // Render, if not cached:
+ if (!isset($this->formatWorkspace_cache[$wsid])) {
+ switch($wsid) {
+ case -1:
+ $this->formatWorkspace_cache[$wsid] = '[Draft]';
+ break;
+ case 0:
+ $this->formatWorkspace_cache[$wsid] = ''; // Does not output anything for ONLINE because it might confuse people to think that the elemnet IS online which is not the case - only that it exists as an offline version in the online workspace...
+ break;
+ default:
+ list($titleRec) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('title','sys_workspace','uid='.intval($wsid).t3lib_BEfunc::deleteClause('sys_workspace'));
+ $this->formatWorkspace_cache[$wsid] = '['.$wsid.'] '.$titleRec['title'];
+ break;
+ }
+ }
+
+ return $this->formatWorkspace_cache[$wsid];
+ }
+
+
+ /**
+ * Create visual difference view of two records. Using t3lib_diff library
+ *
+ * @param string Table name
+ * @param array New version record (green)
+ * @param array Old version record (red)
+ * @return array Array with two keys (0/1) with HTML content / percentage integer (if -1, then it means N/A) indicating amount of change
+ */
+ function createDiffView($table, $diff_1_record, $diff_2_record) {
+ global $TCA, $LANG;
+
+ // Initialize:
+ $pctChange = 'N/A';
+
+ // Check that records are arrays:
+ if (is_array($diff_1_record) && is_array($diff_2_record)) {
+
+ // Load full table description and initialize diff-object:
+ t3lib_div::loadTCA($table);
+ $t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff');
+
+ // Add header row:
+ $tRows = array();
+ $tRows[] = '
+
+ ';
+ } else {
+ // Add string lengths even if value matched - in this was the change percentage is not high if only a single field is changed:
+ $allStrLen+=strlen($diff_1_record[$fN].$diff_2_record[$fN]);
+ }
+ }
+ }
+
+ // Calculate final change percentage:
+ $pctChange = $allStrLen ? ceil($diffStrLen*100/$allStrLen) : -1;
+
+ // Create visual representation of result:
+ if (count($tRows)>1) {
+ $content.= '
'.implode('',$tRows).'
';
+ } else {
+ $content.= ''.$this->doc->icons(1).$LANG->getLL('diffview_complete_match').'';
+ }
+ } else $content.= $this->doc->icons(3).$LANG->getLL('diffview_cannot_find_records');
+
+ // Return value:
+ return array($content,$pctChange);
+ }
+
+ /**
+ * Looking for versions of a record in other workspaces than the current
+ *
+ * @param string Table name
+ * @param integer Record uid
+ * @return string List of other workspace IDs
+ */
+ function versionsInOtherWS($table, $uid) {
+ // Check for duplicates:
+ // Select all versions of record NOT in this workspace:
+ $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
+ 't3ver_wsid',
+ $table,
+ 'pid=-1
+ AND t3ver_oid='.intval($uid).'
+ AND t3ver_wsid!='.intval($GLOBALS['BE_USER']->workspace).// TODO should be $this->workspaceId here???
+ ' AND (t3ver_wsid=-1 OR t3ver_wsid>0)'.
+ t3lib_BEfunc::deleteClause($table),
+ '',
+ 't3ver_wsid',
+ '',
+ 't3ver_wsid'
+ );
+ if (count($rows)) {
+ return implode(',',array_keys($rows));
+ }
+ }
+
+ /**
+ * Looks up stage changes for version and displays a formatted view on mouseover.
+ *
+ * @param string Table name
+ * @param integer Record ID
+ * @param string HTML string to wrap the mouseover around (should be stage change links)
+ * @return string HTML code.
+ */
+ function showStageChangeLog($table,$id,$stageCommands) {
+ global $LANG;
+
+ $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
+ 'log_data,tstamp,userid',
+ 'sys_log',
+ 'action=6 and details_nr=30
+ AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table,'sys_log').'
+ AND recuid='.intval($id)
+ );
+
+ $entry = array();
+ foreach($rows as $dat) {
+ $data = unserialize($dat['log_data']);
+ $username = $this->be_user_Array[$dat['userid']] ? $this->be_user_Array[$dat['userid']]['username'] : '['.$dat['userid'].']';
+
+ switch($data['stage']) {
+ case 1:
+ $text = $LANG->getLL('stage_sent_to_review');
+ break;
+ case 10:
+ $text = $LANG->getLL('stage_approved_for_publish');
+ break;
+ case -1:
+ $text = $LANG->getLL('stage_rejected');
+ break;
+ case 0:
+ $text = $LANG->getLL('stage_reset_to_editing');
+ break;
+ default:
+ $text = $LANG->getLL('stage_undefined');
+ break;
+ }
+ $text = t3lib_BEfunc::datetime($dat['tstamp']).': ' . sprintf($text, htmlspecialchars($username));
+ $text.= ($data['comment'] ? ' ' . $LANG->getLL('stage_label_user_comment') . ' ' . htmlspecialchars($data['comment']) . '' : '');
+
+ $entry[] = $text;
+ }
+
+ return count($entry) ? ''.$stageCommands.' ('.count($entry).')'.
+ '
'.implode('',$entry).'
' : $stageCommands;
+ }
+
+ /**
+ * Links to publishing etc of a version
+ *
+ * @param string Table name
+ * @param array Online record
+ * @param array Offline record (version)
+ * @param string Swap type, "branch", "page" or "element"
+ * @return string HTML content, mainly link tags and images.
+ */
+ function displayWorkspaceOverview_commandLinks($table,&$rec_on,&$rec_off,$vType) {
+ global $LANG;
+
+ if ($this->publishAccess && (!($GLOBALS['BE_USER']->workspaceRec['publish_access']&1) || (int)$rec_off['t3ver_stage']===10)) {
+ $actionLinks =
+ ''.
+ t3lib_iconWorks::getSpriteIcon('actions-version-swap-version') .
+ '';
+ if ($GLOBALS['BE_USER']->workspaceSwapAccess()) {
+ $actionLinks.=
+ ''.
+ t3lib_iconWorks::getSpriteIcon('actions-version-swap-workspace') .
+ '';
+ }
+ }
+
+ if (!$GLOBALS['BE_USER']->workspaceCannotEditOfflineVersion($table,$rec_off)) {
+ if ($GLOBALS['BE_USER']->workspace!==0) {
+ // Release
+ $confirm = $LANG->JScharCode($LANG->getLL('remove_from_ws_confirmation'));
+ $actionLinks.=
+ ''.
+ t3lib_iconWorks::getSpriteIcon('actions-version-document-remove') .
+ '';
+ }
+
+ // Edit
+ if ($table==='pages' && $vType!=='element') {
+ $tempUid = ($vType==='branch' || $GLOBALS['BE_USER']->workspace===0 ? $rec_off['uid'] : $rec_on['uid']);
+ $actionLinks.=
+ ''.
+ t3lib_iconWorks::getSpriteIcon('actions-page-open') .
+ '';
+ } else {
+ $params = '&edit['.$table.']['.$rec_off['uid'].']=edit';
+ $actionLinks.=
+ ''.
+ t3lib_iconWorks::getSpriteIcon('actions-document-open') .
+ '';
+ }
+ }
+
+ // History/Log
+ $actionLinks.=
+ '' .
+ t3lib_iconWorks::getSpriteIcon('actions-document-history-open') .
+ '';
+
+ // View
+ if ($table==='pages') {
+ $tempUid = ($vType==='branch' || $GLOBALS['BE_USER']->workspace===0 ? $rec_off['uid'] : $rec_on['uid']);
+ $actionLinks.=
+ ''.
+ t3lib_iconWorks::getSpriteIcon('actions-document-view') .
+ '';
+ }
+
+ return $actionLinks;
+ }
+
+ /**
+ * Format publishing count for version (lifecycle state)
+ *
+ * @param integer t3ver_count value (number of times it has been online)
+ * @return string String translation of count.
+ */
+ function formatCount($count) {
+ global $LANG;
+
+ // Render, if not cached:
+ if (!isset($this->formatCount_cache[$count])) {
+ switch($count) {
+ case 0:
+ $this->formatCount_cache[$count] = $LANG->getLL('workspace_list_publishing_count_draft');
+ break;
+ case 1:
+ $this->formatCount_cache[$count] = $LANG->getLL('workspace_list_publishing_count_archive');
+ break;
+ default:
+ $this->formatCount_cache[$count] = sprintf($LANG->getLL('workspace_list_publishing_count'), $count);
+ break;
+ }
+ }
+
+ return $this->formatCount_cache[$count];
+ }
+
+
+ /**
+ * Creates display of sub elements of a page when the swap mode is either "Page" or "Branch" (0 / ALL)
+ *
+ * @param integer Page uid (for either online or offline version, but it MUST have swapmode/treeLevel set to >0 (not -1 indicating element versioning)
+ * @param integer The treeLevel value, >0 indicates "branch" while 0 means page+content. (-1 would have meant element versioning, but that should never happen for a call to this function!)
+ * @param integer For offline versions; This is t3ver_oid, the original ID of the online page.
+ * @return string HTML content.
+ */
+ function subElements($uid,$treeLevel,$origId=0) {
+ global $TCA, $LANG;
+
+ if ($GLOBALS['BE_USER']->workspace===0 || !$this->expandSubElements) { // In online workspace we have a reduced view because otherwise it will bloat the listing:
+ return '
+ doc->backPath,'gfx/ol/joinbottom.gif','width="18" height="16"').' align="top" alt="" title="" />'.
+ ($origId ?
+ ''.
+ '['.$LANG->getLL('label_subelementsdetails').']' :
+ '['.$LANG->getLL('label_subelements').']');
+ } else { // For an offline workspace, show sub elements:
+
+ $tCell = array();
+
+ // Find records that follow pages when swapping versions:
+ $recList = array();
+ foreach($TCA as $tN => $tCfg) {
+ if ($tN!='pages' && ($treeLevel>0 || $TCA[$tN]['ctrl']['versioning_followPages'])) {
+ $this->subElements_getNonPageRecords($tN, $uid, $recList);
+ }
+ }
+
+ // Render records collected above:
+ $elCount = count($recList)-1;
+ foreach($recList as $c => $comb) {
+ list($tN,$rec) = $comb;
+
+ $this->subElements_renderItem(
+ $tCell,
+ $tN,
+ $uid,
+ $rec,
+ $origId,
+ $c==$elCount && $treeLevel==0 ? 1 : 0, // If true, will show bottom-join icon.
+ ''
+ );
+ }
+
+ // For branch, dive into the subtree:
+ if ($treeLevel>0) {
+
+ // Drawing tree:
+ $tree = t3lib_div::makeInstance('t3lib_pageTree');
+ $tree->init('AND '.$GLOBALS['BE_USER']->getPagePermsClause(1));
+ $tree->makeHTML = 2; // 2=Also rendering depth-data into the result array
+ $tree->getTree($uid, 99, '');
+
+ // Traverse page tree:
+ foreach($tree->tree as $data) {
+
+ // Render page in table cell:
+ $this->subElements_renderItem(
+ $tCell,
+ 'pages',
+ $uid,
+ t3lib_BEfunc::getRecord('pages',$data['row']['uid']), // Needs all fields, at least more than what is given in $data['row']...
+ $origId,
+ 2, // 2=the join icon and icon for the record is not rendered for pages (where all is in $data['HTML']
+ $data['HTML']
+ );
+
+ // Find all records from page and collect in $recList:
+ $recList = array();
+ foreach($TCA as $tN => $tCfg) {
+ if ($tN!=='pages') {
+ $this->subElements_getNonPageRecords($tN, $data['row']['uid'], $recList);
+ }
+ }
+
+ // Render records collected above:
+ $elCount = count($recList)-1;
+ foreach($recList as $c => $comb) {
+ list($tN,$rec) = $comb;
+
+ $this->subElements_renderItem(
+ $tCell,
+ $tN,
+ $uid,
+ $rec,
+ $origId,
+ $c==$elCount?1:0, // If true, will show bottom-join icon.
+ $data['HTML_depthData']
+ );
+ }
+ }
+ }
+
+ return '
+
+
+ '.implode('',$tCell).'
+
';
+ }
+ }
+
+ /**
+ * Select records from a table and add them to recList
+ *
+ * @param string Table name (from TCA)
+ * @param integer PID to select records from
+ * @param array Array where records are accumulated, passed by reference
+ * @return void
+ */
+ function subElements_getNonPageRecords($tN, $uid, &$recList) {
+ global $TCA;
+
+ $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
+ '*',
+ $tN,
+ 'pid='.intval($uid).
+ ($TCA[$tN]['ctrl']['versioningWS'] ? ' AND t3ver_state=0' : '').
+ t3lib_BEfunc::deleteClause($tN),
+ '',
+ $TCA[$tN]['ctrl']['sortby'] ? $TCA[$tN]['ctrl']['sortby'] : $GLOBALS['TYPO3_DB']->stripOrderBy($TCA[$tN]['ctrl']['default_sortby'])
+ );
+
+ foreach($records as $rec) {
+ $recList[] = array($tN,$rec);
+ }
+ }
+
+ /**
+ * Render a single item in a subelement list into a table row:
+ *
+ * @param array Table rows, passed by reference
+ * @param string Table name
+ * @param integer Page uid for which the subelements are selected/shown
+ * @param array Row of element in list
+ * @param integer The uid of the online version of $uid. If zero it means we are drawing a row for the online version itself while a value means we are drawing display for an offline version.
+ * @param integer Mode of icon display: 0=not the last, 1= is the last in list (make joinbottom icon then), 2=do not shown icons are all (for pages from the page tree already rendered)
+ * @param string Prefix HTML data (icons for tree rendering)
+ * @return void (Content accumulated in $tCell!)
+ */
+ function subElements_renderItem(&$tCell,$tN,$uid,$rec,$origId,$iconMode,$HTMLdata) {
+ global $TCA;
+
+ // Initialize:
+ $origUidFields = $TCA[$tN]['ctrl']['origUid'];
+ $diffCode = '';
+
+ if ($origUidFields) { // If there is a field for this table with original uids we will use that to connect records:
+ if (!$origId) { // In case we are displaying the online originals:
+ $this->targets['orig_'.$uid.'_'.$tN.'_'.$rec['uid']] = $rec; // Build up target array (important that
+ $tdParams = ' id="orig_'.$uid.'_'.$tN.'_'.$rec['uid'].'" class="typo3-ver"'; // Setting ID of the table row
+ } else { // Version branch:
+ if ($this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]) { // If there IS a corresponding original record...:
+
+ // Prepare Table row parameters:
+ $this->addHlSubelementsScript = true;
+ $tdParams = ' onmouseover="hlSubelements(\''.$origId.'_'.$tN.'_'.$rec[$origUidFields].'\', \''.$uid.'_'.$tN.'_'.$rec[$origUidFields].'\', 1, '.($this->diff==2?1:0).');"'.
+ ' onmouseout="hlSubelements(\''.$origId.'_'.$tN.'_'.$rec[$origUidFields].'\', \''.$uid.'_'.$tN.'_'.$rec[$origUidFields].'\', 0, '.($this->diff==2?1:0).');"'.
+ ' id="ver_'.$uid.'_'.$tN.'_'.$rec[$origUidFields].'" class="typo3-ver"';
+
+ // Create diff view:
+ if ($this->diff) {
+ list($diffHTML,$diffPct) = $this->createDiffView($tN, $rec, $this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]);
+
+ if ($this->diff==2) {
+ $diffCode =
+ ($diffPct ? ''.$diffPct.'% change' : '-').
+ '
'.
+ $diffHTML.
+ '
';
+ } else {
+ $diffCode =
+ ($diffPct<0 ? 'N/A' : ($diffPct ? $diffPct.'% change:' : '')).
+ $diffHTML;
+ }
+ }
+
+ // Unsetting the target fields allows us to mark all originals without a version in the subtree (see ->markupNewOriginals())
+ unset($this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]);
+ } else { // No original record, so must be new:
+ $tdParams = ' class="typo3-ver-new"';
+ }
+ }
+ } else { // If no original uid column is supported for this table we are forced NOT to display any diff or highlighting.
+ $tdParams = ' class="typo3-ver-noComp"';
+ }
+
+ // Compile the cell:
+ $tCell[] = '
+