Todays special
authorKasper Skårhøj <kasper@typo3.org>
Mon, 31 Oct 2005 17:20:38 +0000 (17:20 +0000)
committerKasper Skårhøj <kasper@typo3.org>
Mon, 31 Oct 2005 17:20:38 +0000 (17:20 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@835 709f56b5-9817-0410-a4d7-c38de5d9e867

TODO.txt
t3lib/class.t3lib_tcemain.php
typo3/mod/user/ws/class.wslib.php
typo3/mod/user/ws/index.php
typo3/stylesheet.css
typo3/sysext/cms/ext_tables.php
typo3/sysext/version/cm1/index.php

index 998c8d1..ea793e3 100755 (executable)
--- a/TODO.txt
+++ b/TODO.txt
@@ -297,35 +297,16 @@ SQLengine (t3lib_sqlengine):
   - Open Office / CSV tests (proof-of-concept)
 
 
+
+
+
 *******************************
 Versioning/Workspaces:
 *******************************
-- delete:
-       - something twice? / delete a version, not just unlink it?
-       - "Delete versions" link in versioning module.
-
----------------------------------------
-Monday:
-
-Versioning management TODO:
-       - Versioning Mgm:
-               - Implement more of recent "t3ver_*" fields in display.
-               - Select on workspace also
-       - Workspace Mgm:
-               - Diff view by default
-               - display change log
-               - Show admin links, stage etc. incl. log for stage.
-
-Workspace Manager TODO:
-- TODO:
-       - Workspace list and change
-       - Workspace Admin
-
-----------------------------------------------
-Tuesday:
+Wednesday:
 
 Set up TemplaVoila package for testing.
-
+--------------------------------------
 - Backend challenges yet to solve:
        - FlexForms: t3lib_BEfunc::getFlexFormDS(), workspaceOL/fixVersioningPid ??
        - Page and (User) TSconfig?
@@ -349,7 +330,9 @@ Backend preview:
 
 - Documentation "Backend challenges"
 
+
 ----------------------------------
+Friday:
 - copy/move:
        - copying versions:
                - with references in workspace?
@@ -364,6 +347,7 @@ Backend preview:
        - ?
 
 ----------------------------
+Thursday:
 
 Frontend Preview:
 - ONLINE PREVIEW: Versionized pages should be previewed just by their UID (which will select the offline version!) - only in "Live" space, disabled at the moment inside tslib_fe!
@@ -391,6 +375,7 @@ Frontend Preview:
        t3lib_page::getPageOverlay() - because pages_language_overlay records have "versioning_followPages" set this should be observed when selecting language overlay records. But should it be hardcoded or something better?
 - Documentation. As frontend preview implementation is dealt with, describe any persisting incompatibilities in "TYPO3 Core API"
 
+---------------------------
 
 Versioning API:
        - (Support for) swapping using temporary file (instead of using negative ID)
@@ -415,6 +400,9 @@ Documentation:
 Check Various:
 - Auto publishing: Might not work with "Publish only "Publish" stage" because swapped elements will be in editing stage or so!
 
+Workspace Manager TODO:
+- Workspace list
+
 -----LATER:
 
 - Branch versioning:
index 8379a3a..511bfaf 100755 (executable)
@@ -717,7 +717,7 @@ class t3lib_TCEmain {
                                                        } elseif ($this->checkSimilar) {        // Removing fields which are equal to the current value:
                                                                $fieldArray = $this->compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray);
                                                        }
-                                                       if ($TCA[$table]['ctrl']['tstamp'])     {
+                                                       if ($TCA[$table]['ctrl']['tstamp'] && count($fieldArray))       {
                                                                $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
                                                                if ($createNewVersion)  $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
                                                        }
@@ -754,13 +754,17 @@ class t3lib_TCEmain       {
                                                                                        if ($table==='pages') {         // Swap mode set to "branch" so we can build branches for pages.
                                                                                                $fieldArray['t3ver_swapmode'] = $versioningType;
                                                                                        }
-                                                                                       $this->insertDB($table,$id,$fieldArray,TRUE,0,TRUE);    // When inserted, $this->substNEWwithIDs[$id] will be changed to the uid of THIS version and so the interface will pick it up just nice!
+                                                                                       $phShadowId = $this->insertDB($table,$id,$fieldArray,TRUE,0,TRUE);      // When inserted, $this->substNEWwithIDs[$id] will be changed to the uid of THIS version and so the interface will pick it up just nice!
+                                                                                       if ($phShadowId)        {
+                                                                                               $this->placeholderShadowing($table,$phShadowId);
+                                                                                       }
                                                                                } else $this->newlog('Versioning type "'.$versioningType.'" was not allowed, so could not create new record.',1);
                                                                        } else {
                                                                                $this->insertDB($table,$id,$fieldArray,FALSE,$incomingFieldArray['uid']);
                                                                        }
                                                                } else {
                                                                        $this->updateDB($table,$id,$fieldArray);
+                                                                       $this->placeholderShadowing($table,$id);
                                                                }
                                                        }
 
@@ -779,6 +783,36 @@ class t3lib_TCEmain        {
                $this->removeRegisteredFiles();
        }
 
+       /**
+        * Fix shadowing of data in case we are editing a offline version of a live "New" placeholder record:
+        *
+        * @param       string          Table name
+        * @param       integer         Record uid
+        * @return      void
+        */
+       function placeholderShadowing($table,$id)       {
+               global $TCA;
+
+               if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'*'))    {
+                       if ((int)$liveRec['t3ver_state']===1)   {
+                               $justStoredRecord = t3lib_BEfunc::getRecord($table,$id);
+                               $newRecord = array();
+
+                               $shadowColumns = t3lib_div::trimExplode(',', $TCA[$table]['ctrl']['shadowColumnsForNewPlaceholders'],1);
+                               foreach($shadowColumns as $fieldName)   {
+                                       if (strcmp($justStoredRecord[$fieldName],$liveRec[$fieldName])) {
+                                               $newRecord[$fieldName] = $justStoredRecord[$fieldName];
+                                       }
+                               }
+
+                               if (count($newRecord))  {
+                                       $this->newlog('Shadowing done on fields '.implode(',',array_keys($newRecord)).' in Placeholder record '.$table.':'.$liveRec['uid'].' (offline version UID='.$id.')');
+                                       $this->updateDB($table,$liveRec['uid'],$newRecord);
+                               }
+                       }
+               }
+       }
+
        /**
         * Filling in the field array
         * $this->exclude_array is used to filter fields if needed.
@@ -2084,21 +2118,7 @@ class t3lib_TCEmain      {
                                                                }
                                                        break;
                                                        case 'delete':
-                                                               $delRec = t3lib_BEfunc::getRecord($table, $id);
-                                                               if (is_array($delRec))  {       // Record asked to be deleted was found:
-
-                                                                               // Look, if record is "online" or in a versionized branch, then delete directly.
-                                                                       if ($res = $this->BE_USER->workspaceAllowLiveRecordsInPID($delRec['pid'], $table))      {       // Directly delete:
-                                                                               if ($res>0)     {
-                                                                                       $this->deleteEl($table, $id);
-                                                                               } else {
-                                                                                       $this->newlog('Stage of root point did not allow for deletion',1);
-                                                                               }
-                                                                       } else {
-                                                                               // Otherwise, try to delete by versionization:
-                                                                               $this->versionizeRecord($table,$id,'DELETED!',TRUE);
-                                                                       }
-                                                               }
+                                                               $this->deleteAction($table, $id);
                                                        break;
                                                }
 
@@ -2866,6 +2886,244 @@ class t3lib_TCEmain     {
 
 
 
+
+
+
+
+       /*********************************************
+        *
+        * Cmd: Deleting
+        *
+        ********************************************/
+
+       /**
+        * Delete CMD action
+        *
+        * @param       string          Table name
+        * @param       integer         Record UID
+        * @return      void
+        */
+       function deleteAction($table, $id)      {
+               global $TCA;
+
+               $delRec = t3lib_BEfunc::getRecord($table, $id);
+
+               if (is_array($delRec))  {       // Record asked to be deleted was found:
+
+                               // For Live version, try if there is a workspace version because if so, rather "delete" that instead
+                       if ($delRec['pid']!=-1) {       // Look, if record is an offline version, then delete directly:
+                               if ($wsVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $id))     {
+                                       $delRec = $wsVersion;
+                                       $id = $delRec['uid'];
+                               }
+                       }
+
+                       if ($delRec['pid']==-1) {       // Look, if record is an offline version, then delete directly:
+                               if ($TCA[$table]['ctrl']['versioningWS'])       {
+                                       if ($this->BE_USER->workspace==0 || (int)$delRec['t3ver_wsid']==$this->BE_USER->workspace)      {       // In Live workspace, delete any. In other workspaces there must be match.
+                                               $liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'uid,t3ver_state');
+
+                                               if ($delRec['t3ver_wsid']==0 || (int)$liveRec['t3ver_state']!==1)       {       // Delete those in WS 0 + if their live records state was not "Placeholder".
+                                                       $this->deleteEl($table, $id);
+                                               } else {        // If live record was placeholder, rather clear it from workspace (because it clears both version and placeholder). Yes, of course we would most like to just delete them, BUT what if placeholder has other versions in other workspaces...
+                                                       $this->version_clearWSID($table,$id);
+                                               }
+                                       } else $this->newlog('Tried to delete record from another workspace',1);
+                               } else $this->newlog('Versioning not enabled for record with PID = -1!',2);
+                       } elseif ($res = $this->BE_USER->workspaceAllowLiveRecordsInPID($delRec['pid'], $table))        {       // Look, if record is "online" or in a versionized branch, then delete directly.
+                               if ($res>0)     {
+                                       $this->deleteEl($table, $id);
+                               } else $this->newlog('Stage of root point did not allow for deletion',1);
+                       } else {
+                               // Otherwise, try to delete by versionization:
+                               $this->versionizeRecord($table,$id,'DELETED!',TRUE);
+                       }
+               }
+       }
+
+       /**
+        * Delete element from any table
+        *
+        * @param       string          Table name
+        * @param       integer         Record UID
+        * @param       boolean         Flag: If $noRecordCheck is set, then the function does not check permission to delete record
+        * @param       boolean         If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
+        * @return      void
+        */
+       function deleteEl($table, $uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE)   {
+               if ($table == 'pages')  {
+                       $this->deletePages($uid, $noRecordCheck, $forceHardDelete);
+               } else {
+                       $this->deleteRecord($table, $uid, $noRecordCheck, $forceHardDelete);
+               }
+       }
+
+       /**
+        * Deleting a record
+        * This function may not be used to delete pages-records unless the underlying records are already deleted
+        * Deletes a record regardless of versioning state (live of offline, doesn't matter, the uid decides)
+        * If both $noRecordCheck and $forceHardDelete are set it could even delete a "deleted"-flagged record!
+        *
+        * @param       string          Table name
+        * @param       integer         Record UID
+        * @param       boolean         Flag: If $noRecordCheck is set, then the function does not check permission to delete record
+        * @param       boolean         If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
+        * @return      void
+        */
+       function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE)        {
+               global $TCA;
+
+               $uid = intval($uid);
+               if ($TCA[$table] && $uid)       {
+                       if ($noRecordCheck || $this->doesRecordExist($table,$uid,'delete'))     {
+                               $deleteRow = $TCA[$table]['ctrl']['delete'];
+                               if ($deleteRow && !$forceHardDelete)    {
+                                       $updateFields = array(
+                                               $deleteRow => 1
+                                       );
+
+                                               // If the table is sorted, then the sorting number is set very high
+                                       if ($TCA[$table]['ctrl']['sortby'])     {
+                                               $updateFields[$TCA[$table]['ctrl']['sortby']] = 1000000000;
+                                       }
+
+                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
+                               } else {
+
+                                               // Fetches all fields that holds references to files
+                                       $fileFieldArr = $this->extFileFields($table);
+                                       if (count($fileFieldArr))       {
+                                               $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',',$fileFieldArr), $table, 'uid='.intval($uid));
+                                               if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))        {
+                                                       $fArray = $fileFieldArr;
+                                                       foreach($fArray as $theField)   {       // MISSING: Support for MM file relations!
+                                                               $this->extFileFunctions($table,$theField,$row[$theField],'deleteAll');          // This deletes files that belonged to this record.
+                                                       }
+                                               } else {
+                                                       $this->log($table,$uid,3,0,100,'Delete: Zero rows in result when trying to read filenames from record which should be deleted');
+                                               }
+                                       }
+
+                                       $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($uid));
+                               }
+
+                               if (!$GLOBALS['TYPO3_DB']->sql_error()) {
+                                       $this->log($table,$uid,3,0,0,'');
+                               } else {
+                                       $this->log($table,$uid,3,0,100,$GLOBALS['TYPO3_DB']->sql_error());
+                               }
+
+                               $this->clear_cache($table,$uid);        // clear cache
+                       } else $this->log($table,$uid,3,0,1,'Attempt to delete record without delete-permissions');
+               }
+       }
+
+       /**
+        * Used to delete page because it will check for branch below pages and unallowed tables on the page as well.
+        *
+        * @param       integer         Page id
+        * @param       boolean         If TRUE, pages are not checked for permission.
+        * @param       boolean         If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
+        * @return      void
+        */
+       function deletePages($uid,$force=FALSE,$forceHardDelete=FALSE)  {
+                       // Getting list of pages to delete:
+               if ($force)     {
+                       $brExist = $this->doesBranchExist('',$uid,0,1);         // returns the branch WITHOUT permission checks (0 secures that)
+                       $res = t3lib_div::trimExplode(',',$brExist.$uid,1);
+               } else {
+                       $res = $this->canDeletePage($uid);
+               }
+
+                       // Perform deletion if not error:
+               if (is_array($res))     {
+                       foreach($res as $deleteId)      {
+                               $this->deleteSpecificPage($deleteId,$forceHardDelete);
+                       }
+               } else {
+                       $this->newlog($res,1);
+               }
+       }
+
+       /**
+        * Delete a page and all records on it.
+        *
+        * @param       integer         Page id
+        * @param       boolean         If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
+        * @return      void
+        * @access private
+        * @see deletePages()
+        */
+       function deleteSpecificPage($uid,$forceHardDelete=FALSE)        {
+               global $TCA;
+               reset ($TCA);
+               $uid = intval($uid);
+               if ($uid)       {
+                       while (list($table)=each($TCA)) {
+                               if ($table!='pages')    {
+                                       $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table));
+                                       while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))     {
+                                               $this->deleteRecord($table,$row['uid'], TRUE, $forceHardDelete);
+                                       }
+                               }
+                       }
+                       $this->deleteRecord('pages',$uid, TRUE, $forceHardDelete);
+               }
+       }
+
+       /**
+        * Used to evaluate if a page can be deleted
+        *
+        * @param       integer         Page id
+        * @return      mixed           If array: List of page uids to traverse and delete (means OK), if string: error code.
+        */
+       function canDeletePage($uid)    {
+               if ($this->doesRecordExist('pages',$uid,'delete'))      {       // If we may at all delete this page
+                       if ($this->deleteTree)  {
+                               $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1);     // returns the branch
+                               if ($brExist != -1)     {       // Checks if we had permissions
+                                       if ($this->noRecordsFromUnallowedTables($brExist.$uid)) {
+                                               return t3lib_div::trimExplode(',',$brExist.$uid,1);
+                                       } else return 'Attempt to delete records from disallowed tables';
+                               } else return 'Attempt to delete pages in branch without permissions';
+                       } else {
+                               $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1);     // returns the branch
+                               if ($brExist == '')     {       // Checks if branch exists
+                                       if ($this->noRecordsFromUnallowedTables($uid))  {
+                                               return array($uid);
+                                       } else return 'Attempt to delete records from disallowed tables';
+                               } else return 'Attempt to delete page which has subpages';
+                       }
+               } else return 'Attempt to delete page without permissions';
+       }
+
+       /**
+        * Returns true if record CANNOT be deleted, otherwise false. Used to check before the versioning API allows a record to be marked for deletion.
+        *
+        * @param       string          Record Table
+        * @param       integer         Record UID
+        * @return      string          Returns a string IF there is an error (error string explaining). FALSE means record can be deleted
+        */
+       function cannotDeleteRecord($table,$id) {
+               if ($table==='pages')   {
+                       $res = $this->canDeletePage($id);
+                       return is_array($res) ? FALSE : $res;
+               } else {
+                       return $this->doesRecordExist($table,$id,'delete') ? FALSE : 'No permission to delete record';
+               }
+       }
+
+
+
+
+
+
+
+
+
+
+
+
        /*********************************************
         *
         * Cmd: Versioning
@@ -3247,196 +3505,6 @@ $this->log($table,$id,6,0,0,'Stage raised...',-1,array('comment'=>$comment,'stag
 
 
 
-
-       /*********************************************
-        *
-        * Cmd: Deleting
-        *
-        ********************************************/
-
-       /**
-        * Delete element from any table
-        *
-        * @param       string          Table name
-        * @param       integer         Record UID
-        * @param       boolean         Flag: If $noRecordCheck is set, then the function does not check permission to delete record
-        * @param       boolean         If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
-        * @return      void
-        */
-       function deleteEl($table, $uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE)   {
-               if ($table == 'pages')  {
-                       $this->deletePages($uid, $noRecordCheck, $forceHardDelete);
-               } else {
-                       $this->deleteRecord($table, $uid, $noRecordCheck, $forceHardDelete);
-               }
-       }
-
-       /**
-        * Deleting a record
-        * This function may not be used to delete pages-records unless the underlying records are already deleted
-        * Deletes a record regardless of versioning state (live of offline, doesn't matter, the uid decides)
-        * If both $noRecordCheck and $forceHardDelete are set it could even delete a "deleted"-flagged record!
-        *
-        * @param       string          Table name
-        * @param       integer         Record UID
-        * @param       boolean         Flag: If $noRecordCheck is set, then the function does not check permission to delete record
-        * @param       boolean         If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
-        * @return      void
-        */
-       function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE)        {
-               global $TCA;
-
-               $uid = intval($uid);
-               if ($TCA[$table] && $uid)       {
-                       if ($noRecordCheck || $this->doesRecordExist($table,$uid,'delete'))     {
-                               $deleteRow = $TCA[$table]['ctrl']['delete'];
-                               if ($deleteRow && !$forceHardDelete)    {
-                                       $updateFields = array(
-                                               $deleteRow => 1
-                                       );
-
-                                               // If the table is sorted, then the sorting number is set very high
-                                       if ($TCA[$table]['ctrl']['sortby'])     {
-                                               $updateFields[$TCA[$table]['ctrl']['sortby']] = 1000000000;
-                                       }
-
-                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
-                               } else {
-
-                                               // Fetches all fields that holds references to files
-                                       $fileFieldArr = $this->extFileFields($table);
-                                       if (count($fileFieldArr))       {
-                                               $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',',$fileFieldArr), $table, 'uid='.intval($uid));
-                                               if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))        {
-                                                       $fArray = $fileFieldArr;
-                                                       foreach($fArray as $theField)   {       // MISSING: Support for MM file relations!
-                                                               $this->extFileFunctions($table,$theField,$row[$theField],'deleteAll');          // This deletes files that belonged to this record.
-                                                       }
-                                               } else {
-                                                       $this->log($table,$uid,3,0,100,'Delete: Zero rows in result when trying to read filenames from record which should be deleted');
-                                               }
-                                       }
-
-                                       $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($uid));
-                               }
-
-                               if (!$GLOBALS['TYPO3_DB']->sql_error()) {
-                                       $this->log($table,$uid,3,0,0,'');
-                               } else {
-                                       $this->log($table,$uid,3,0,100,$GLOBALS['TYPO3_DB']->sql_error());
-                               }
-
-                               $this->clear_cache($table,$uid);        // clear cache
-                       } else $this->log($table,$uid,3,0,1,'Attempt to delete record without delete-permissions');
-               }
-       }
-
-       /**
-        * Used to delete page because it will check for branch below pages and unallowed tables on the page as well.
-        *
-        * @param       integer         Page id
-        * @param       boolean         If TRUE, pages are not checked for permission.
-        * @param       boolean         If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
-        * @return      void
-        */
-       function deletePages($uid,$force=FALSE,$forceHardDelete=FALSE)  {
-                       // Getting list of pages to delete:
-               if ($force)     {
-                       $brExist = $this->doesBranchExist('',$uid,0,1);         // returns the branch WITHOUT permission checks (0 secures that)
-                       $res = t3lib_div::trimExplode(',',$brExist.$uid,1);
-               } else {
-                       $res = $this->canDeletePage($uid);
-               }
-
-                       // Perform deletion if not error:
-               if (is_array($res))     {
-                       foreach($res as $deleteId)      {
-                               $this->deleteSpecificPage($deleteId,$forceHardDelete);
-                       }
-               } else {
-                       $this->newlog($res,1);
-               }
-       }
-
-       /**
-        * Delete a page and all records on it.
-        *
-        * @param       integer         Page id
-        * @param       boolean         If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
-        * @return      void
-        * @access private
-        * @see deletePages()
-        */
-       function deleteSpecificPage($uid,$forceHardDelete=FALSE)        {
-               global $TCA;
-               reset ($TCA);
-               $uid = intval($uid);
-               if ($uid)       {
-                       while (list($table)=each($TCA)) {
-                               if ($table!='pages')    {
-                                       $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table));
-                                       while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))     {
-                                               $this->deleteRecord($table,$row['uid'], TRUE, $forceHardDelete);
-                                       }
-                               }
-                       }
-                       $this->deleteRecord('pages',$uid, TRUE, $forceHardDelete);
-               }
-       }
-
-       /**
-        * Used to evaluate if a page can be deleted
-        *
-        * @param       integer         Page id
-        * @return      mixed           If array: List of page uids to traverse and delete (means OK), if string: error code.
-        */
-       function canDeletePage($uid)    {
-               if ($this->doesRecordExist('pages',$uid,'delete'))      {       // If we may at all delete this page
-                       if ($this->deleteTree)  {
-                               $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1);     // returns the branch
-                               if ($brExist != -1)     {       // Checks if we had permissions
-                                       if ($this->noRecordsFromUnallowedTables($brExist.$uid)) {
-                                               return t3lib_div::trimExplode(',',$brExist.$uid,1);
-                                       } else return 'Attempt to delete records from disallowed tables';
-                               } else return 'Attempt to delete pages in branch without permissions';
-                       } else {
-                               $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1);     // returns the branch
-                               if ($brExist == '')     {       // Checks if branch exists
-                                       if ($this->noRecordsFromUnallowedTables($uid))  {
-                                               return array($uid);
-                                       } else return 'Attempt to delete records from disallowed tables';
-                               } else return 'Attempt to delete page which has subpages';
-                       }
-               } else return 'Attempt to delete page without permissions';
-       }
-
-       /**
-        * Returns true if record CANNOT be deleted, otherwise false. Used to check before the versioning API allows a record to be marked for deletion.
-        *
-        * @param       string          Record Table
-        * @param       integer         Record UID
-        * @return      string          Returns a string IF there is an error (error string explaining). FALSE means record can be deleted
-        */
-       function cannotDeleteRecord($table,$id) {
-               if ($table==='pages')   {
-                       $res = $this->canDeletePage($id);
-                       return is_array($res) ? FALSE : $res;
-               } else {
-                       return $this->doesRecordExist($table,$id,'delete') ? FALSE : 'No permission to delete record';
-               }
-       }
-
-
-
-
-
-
-
-
-
-
-
-
        /*********************************************
         *
         * Cmd: Helper functions
@@ -4139,7 +4207,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',-1,array('comment'=>$comment,'stag
         * @param       boolean         Set to true if new version is created.
         * @param       integer         Suggested UID value for the inserted record. See the array $this->suggestedInsertUids; Admin-only feature
         * @param       boolean         If true, the ->substNEWwithIDs array is not updated. Only useful in very rare circumstances!
-        * @return      void
+        * @return      integer         Returns ID on success.
         */
        function insertDB($table,$id,$fieldArray,$newVersion=FALSE,$suggestedUid=0,$dontSetNewIdIndex=FALSE)    {
                global $TCA;
@@ -4192,6 +4260,8 @@ $this->log($table,$id,6,0,0,'Stage raised...',-1,array('comment'=>$comment,'stag
                                                        // Clear cache for relavant pages:
                                                $this->clear_cache($table,$id);
                                        }
+
+                                       return $id;
                                } else {
                                        $this->log($table,$id,1,0,2,"SQL error: '%s' (%s)",12,array($GLOBALS['TYPO3_DB']->sql_error(),$table.':'.$id));
                                }
index 1aef01d..7fa708a 100755 (executable)
@@ -120,9 +120,10 @@ class wslib {
         * @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)  {
+       function selectVersionsInWorkspace($wsid,$filter=0,$stage=-99,$pageId=-1)       {
                global $TCA;
 
                $wsid = intval($wsid);
@@ -139,6 +140,7 @@ class wslib {
                                        '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) : '').
index de42b46..269b199 100755 (executable)
@@ -616,7 +616,7 @@ class SC_mod_user_ws_index extends t3lib_SCbase {
                                                                        $multipleWarning = (!$mainCell && $GLOBALS['BE_USER']->workspace!==0? '<br/>'.$this->doc->icons(3).'<b>Multiple versions in same workspace!</b>' : '');
                                                                        $verWarning = $warnAboutVersions || ($warnAboutVersions_nonPages && $GLOBALS['TCA'][$table]['ctrl']['versioning_followPages'])? '<br/>'.$this->doc->icons(3).'<b>Version inside version!</b>' : '';
                                                                        $verElement = $icon.
-                                                                               '<a href="'.htmlspecialchars('index.php?details='.rawurlencode($table.':'.$rec_off['uid'])).'">'.
+                                                                               '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?id='.($table==='pages'?$rec_on['uid']:$rec_on['pid']).'&details='.rawurlencode($table.':'.$rec_off['uid']).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
                                                                                t3lib_BEfunc::getRecordTitle($table,$rec_off,TRUE).
                                                                                '</a>'.
                                                                                $versionsInOtherWSWarning.
@@ -932,7 +932,8 @@ class SC_mod_user_ws_index extends t3lib_SCbase {
                        return '<br/>
                                        <img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/ol/joinbottom.gif','width="18" height="16"').' align="top" alt="" title="" />'.
                                        ($origId ?
-                                               '<a href="index.php?details='.$uid.'"><span class="typo3-dimmed"><em>[Sub elements, click for details]</em><span></a>' :
+                                               '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?id='.$uid.'&details='.rawurlencode('pages:'.$uid).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
+                                               '<span class="typo3-dimmed"><em>[Sub elements, click for details]</em><span></a>' :
                                                '<span class="typo3-dimmed"><em>[Sub elements]</em><span>');
                } else {        // For an offline workspace, show sub elements:
 
@@ -1113,7 +1114,7 @@ class SC_mod_user_ws_index extends t3lib_SCbase {
                                                                        t3lib_iconWorks::getIconImage($tN, $rec, $this->doc->backPath,'') : '').
                                                                t3lib_BEfunc::getRecordTitle($tN, $rec, TRUE).
                                                        '</td>
-                                                       <td>'.
+                                                       <td class="cmdCell">'.
                                                                $this->displayWorkspaceOverview_commandLinksSub($tN,$rec,$origId).
                                                        '</td>'.($origId ? '<td class="diffCell">'.
                                                                $diffCode.
index 6e1e356..64f9df1 100755 (executable)
@@ -468,8 +468,8 @@ TABLE.ver-subtree DIV.diffLayer, DIV.logLayer { background-color: white; width:4
 TABLE.ver-subtree TR TD IMG { vertical-align: middle; }
 TABLE.ver-subtree, TABLE.ver-verElement TD.c-diffCell { border: 1px solid #666666; }
 TABLE.ver-verElement { width: 100%;}
-TABLE.ver-subtree TR.typo3-ver TD.iconTitle, TABLE.ver-subtree TR.typo3-ver TD.diffCell {background-color: #c5c1b5; }
-TABLE.ver-subtree TR.typo3-ver-hl TD.iconTitle, TABLE.ver-subtree TR.typo3-ver-hl TD.diffCell { background-color: #999999; }
+TABLE.ver-subtree TR.typo3-ver TD.iconTitle, TABLE.ver-subtree TR.typo3-ver TD.diffCell, TABLE.ver-subtree TR.typo3-ver TD.cmdCell {background-color: #c5c1b5; }
+TABLE.ver-subtree TR.typo3-ver-hl TD.iconTitle, TABLE.ver-subtree TR.typo3-ver-hl TD.diffCell, TABLE.ver-subtree TR.typo3-ver-hl TD.cmdCell { background-color: #999999; }
 TABLE.ver-subtree TR.typo3-ver-new TD { color: #666666; background-color: #eeeeee; }
 TABLE.ver-subtree TR.typo3-ver-noComp TD { font-style : italic; }
 
index 33ff2e9..d96bcb1 100755 (executable)
@@ -511,6 +511,7 @@ $TCA['tt_content'] = Array (
                'prependAtCopy' => 'LLL:EXT:lang/locallang_general.php:LGL.prependAtCopy',
                'copyAfterDuplFields' => 'colPos,sys_language_uid',
                'useColumnsForDefaultValues' => 'colPos,sys_language_uid',
+               'shadowColumnsForNewPlaceholders' => 'sys_language_uid,l18n_parent,colPos',
                'transOrigPointerField' => 'l18n_parent',
                'transOrigDiffSourceField' => 'l18n_diffsource',
                'languageField' => 'sys_language_uid',
@@ -629,6 +630,7 @@ $TCA['pages_language_overlay'] = Array (
                'transOrigPointerField' => 'pid',
                'transOrigPointerTable' => 'pages',
                'transOrigDiffSourceField' => 'l18n_diffsource',
+               'shadowColumnsForNewPlaceholders' => 'sys_language_uid',
                'languageField' => 'sys_language_uid',
                'mainpalette' => 1,
                'dynamicConfigFile' => t3lib_extMgm::extPath($_EXTKEY).'tbl_cms.php'
index 7d22cc6..35b6b2a 100755 (executable)
@@ -56,6 +56,8 @@ require_once (PATH_t3lib.'class.t3lib_scbase.php');
        // DEFAULT initialization of a module [END]
 
 require_once(PATH_t3lib.'class.t3lib_diff.php');
+require_once(PATH_typo3.'mod/user/ws/class.wslib.php');
+require_once(PATH_t3lib.'class.t3lib_pagetree.php');
 
 
 
@@ -68,6 +70,70 @@ require_once(PATH_t3lib.'class.t3lib_diff.php');
  */
 class tx_version_cm1 extends t3lib_SCbase {
 
+               // Default variables for backend modules
+       var $MCONF = array();                           // Module configuration
+       var $MOD_MENU = array();                        // Module menu items
+       var $MOD_SETTINGS = array();            // Module session settings
+       var $doc;                                                       // Document Template Object
+       var $content;                                           // Accumulated content
+
+
+               // Internal:
+       var $showWorkspaceCol = 0;
+       var $formatWorkspace_cache = array();
+       var $formatCount_cache = array();
+       var $targets = array();         // Accumulation of online targets.
+       var $pageModule = '';                   // Name of page module
+       var $publishAccess = FALSE;
+       var $be_user_Array = array();
+
+
+
+
+
+
+       /*********************************
+        *
+        * Standard module initialization
+        *
+        *********************************/
+
+       /**
+        * Initialize menu configuration
+        *
+        * @return      void
+        */
+       function menuConfig()   {
+
+                       // Menu items:
+               $this->MOD_MENU = array(
+                       'filter' => array(
+                               1 => 'Drafts',
+                               2 => 'Archive',
+                               0 => 'All',
+                       ),
+                       'display' => array(
+                               0 => '[ONLINE]',
+                               -98 => 'Workspaces',
+                               -99 => 'All',
+                               -1 => '[Offline]'
+                       ),
+                       'diff' => ''
+               );
+
+                       // Add workspaces:
+               if ($GLOBALS['BE_USER']->workspace===0) {       // Spend time on this only in online workspace because it might take time:
+                       $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title,adminusers,members,reviewers','sys_workspace','pid=0'.t3lib_BEfunc::deleteClause('sys_workspace'),'','title');
+                       foreach($workspaces as $rec)    {
+                               if ($GLOBALS['BE_USER']->checkWorkspace($rec))  {
+                                       $this->MOD_MENU['display'][$rec['uid']] = '['.$rec['uid'].'] '.$rec['title'];
+                               }
+                       }
+               }
+
+                       // CLEANSE SETTINGS
+               $this->MOD_SETTINGS = t3lib_BEfunc::getModuleData($this->MOD_MENU, t3lib_div::_GP('SET'), $this->MCONF['name'], 'ses');
+       }
 
        /**
         * Main function of the module. Write the content to $this->content
@@ -77,11 +143,20 @@ class tx_version_cm1 extends t3lib_SCbase {
        function main() {
                global $BE_USER,$LANG,$BACK_PATH,$TCA_DESCR,$TCA,$CLIENT,$TYPO3_CONF_VARS;
 
+                       // Setting module configuration:
+               $this->MCONF = $GLOBALS['MCONF'];
+
                        // Draw the header.
                $this->doc = t3lib_div::makeInstance('mediumDoc');
                $this->doc->backPath = $BACK_PATH;
                $this->doc->form='<form action="" method="post">';
 
+                       // Setting up the context sensitive menu:
+               $CMparts = $this->doc->getContextMenuCode();
+               $this->doc->JScode.= $CMparts[0];
+               $this->doc->bodyTagAdditions = $CMparts[1];
+               $this->doc->postCode.= $CMparts[2];
+
                        // Getting input data:
                $this->id = intval(t3lib_div::_GP('id'));               // Page id. If set, indicates activation from Web>Versioning module
                if (!$this->id) {
@@ -91,6 +166,11 @@ class tx_version_cm1 extends t3lib_SCbase {
                        $this->uid = $this->id;
                        $this->table = 'pages';
                }
+               $this->details = t3lib_div::_GP('details');             // Page id. If set, indicates activation from Web>Versioning module
+
+                       // Force this setting:
+               $this->MOD_SETTINGS['expandSubElements'] = TRUE;
+               $this->MOD_SETTINGS['diff'] = $this->details || $this->MOD_SETTINGS['diff']?1:0;
 
                        // Reading the record:
                $record = t3lib_BEfunc::getRecord($this->table,$this->uid);
@@ -116,9 +196,33 @@ class tx_version_cm1 extends t3lib_SCbase {
                                                function jumpToUrl(URL) {
                                                        document.location = URL;
                                                }
+
+                                               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\';
+                                                               }
+                                                       }
+                                               }
                                        </script>
                                ';
 
+                                       // If another page module was specified, replace the default Page module with the new one
+                               $newPageModule = trim($BE_USER->getTSConfigVal('options.overridePageModule'));
+                               $this->pageModule = t3lib_BEfunc::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
+
+                                       // Setting publish access permission for workspace:
+                               $this->publishAccess = $BE_USER->workspacePublishAccess($BE_USER->workspace);
+
+
                                $headerSection = $this->doc->getHeader('pages',$this->pageinfo,$this->pageinfo['_thePath']).'<br/>'.$LANG->sL('LLL:EXT:lang/locallang_core.php:labels.path').': '.t3lib_div::fixed_lgd_pre($this->pageinfo['_thePath'],50);
 
                                $this->content.=$this->doc->startPage($LANG->getLL('title'));
@@ -127,14 +231,14 @@ class tx_version_cm1 extends t3lib_SCbase {
                                $this->content.=$this->doc->section('',$headerSection);
                                $this->content.=$this->doc->divider(5);
 
-                               // Render content:
+                                       // Render content:
                                if ($this->id)  {
                                        $this->workspaceMgm();
                                } else {
                                        $this->versioningMgm();
                                }
 
-                               // ShortCut
+                                       // ShortCut
                                if ($BE_USER->mayMakeShortcut())        {
                                        $this->content.=$this->doc->spacer(20).$this->doc->section('',$this->doc->makeShortcutIcon('id',implode(',',array_keys($this->MOD_MENU)),$this->MCONF['name']));
                                }
@@ -245,9 +349,12 @@ class tx_version_cm1 extends t3lib_SCbase {
                                        <td>&nbsp;</td>
                                        <td>Title</td>
                                        <td>UID</td>
-                                       <td>t3ver_oid</td>
-                                       <td>t3ver_id</td>
-                                       <td>t3ver_wsid</td>
+                                       <td title="t3ver_oid - Reference to live version UID">oid</td>
+                                       <td title="t3ver_id - Version number, incremental integer">id</td>
+                                       <td title="t3ver_wsid - Workspace ID. There can be only one version of an element per ID (except ID zero).">wsid</td>
+                                       <td title="t3ver_state - Special states of a version: 1=Placeholder for "New". 2=Marked deleted.">state</td>
+                                       <td title="t3ver_stage - Publishing stage: Editing (0), review (1), publish (10), rejected (-1).">stage</td>
+                                       <td title="t3ver_count - Life cycle counter. Incremented each time element is unpublished.">count</td>
                                        <td>pid</td>
                                        <td>t3ver_label</td>
                                        <td colspan="2"><input type="submit" name="do_diff" value="Diff" /></td>
@@ -275,6 +382,9 @@ class tx_version_cm1 extends t3lib_SCbase {
                                        <td>'.$row['t3ver_oid'].'</td>
                                        <td>'.$row['t3ver_id'].'</td>
                                        <td>'.$row['t3ver_wsid'].'</td>
+                                       <td>'.$row['t3ver_state'].'</td>
+                                       <td>'.$row['t3ver_stage'].'</td>
+                                       <td>'.$row['t3ver_count'].'</td>
                                        <td>'.$row['pid'].'</td>
                                        <td nowrap="nowrap"><a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick('&edit['.$this->table.']['.$row['uid'].']=edit&columnsOnly=t3ver_label',$this->doc->backPath)).'"><img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/edit2.gif','width="11" height="12"').' alt="" title="Edit"/></a>'.htmlspecialchars($row['t3ver_label']).'</td>
                                        <td bgcolor="green"><input type="radio" name="diff_1" value="'.$row['uid'].'"'.($diff_1==$row['uid'] ? ' checked="checked"':'').'/></td>
@@ -290,7 +400,7 @@ class tx_version_cm1 extends t3lib_SCbase {
                                                <tr>
                                                        <td></td>
                                                        <td></td>
-                                                       <td colspan="7">'.$sub.'</td>
+                                                       <td colspan="10">'.$sub.'</td>
                                                        <td colspan="2"></td>
                                                </tr>';
                                }
@@ -406,10 +516,18 @@ class tx_version_cm1 extends t3lib_SCbase {
        function adminLinks($table,$row)        {
                global $BE_USER;
 
+                       // Edit link:
                $adminLink = '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick('&edit['.$table.']['.$row['uid'].']=edit',$this->doc->backPath)).'">'.
                                                '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/edit2.gif','width="11" height="12"').' alt="" title="Edit"/>'.
                                                '</a>';
 
+                       // Delete link:
+               $adminLink.= '<a href="'.htmlspecialchars($this->doc->issueCommand('&cmd['.$table.']['.$row['uid'].'][delete]=1')).'">'.
+                                               '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/garbage.gif','width="11" height="12"').' alt="" title="Delete"/>'.
+                                               '</a>';
+
+
+
                if ($table == 'pages')  {
 
                                // If another page module was specified, replace the default Page module with the new one
@@ -468,8 +586,957 @@ class tx_version_cm1 extends t3lib_SCbase {
         * @return      void
         */
        function workspaceMgm() {
+
+               $menu = '';
+               if ($GLOBALS['BE_USER']->workspace===0) {
+                       $menu.= t3lib_BEfunc::getFuncMenu($this->id,'SET[filter]',$this->MOD_SETTINGS['filter'],$this->MOD_MENU['filter']);
+                       $menu.= t3lib_BEfunc::getFuncMenu($this->id,'SET[display]',$this->MOD_SETTINGS['display'],$this->MOD_MENU['display']);
+               }
+               if (!$this->details && $GLOBALS['BE_USER']->workspace)  {
+                       $menu.= t3lib_BEfunc::getFuncCheck($this->id,'SET[diff]',$this->MOD_SETTINGS['diff']).' Show difference view';
+               }
+
+               if ($menu)      {
+                       $this->content.=$this->doc->section('',$menu,0,1);
+               }
+
                $this->content.=$this->doc->spacer(15);
-               $this->content.=$this->doc->section('Workspace management','... - ...',0,1);
+               $this->content.=$this->doc->section($this->details ? 'Details for version' : 'Workspace management', $this->displayWorkspaceOverview(),0,1);
+       }
+
+       /**
+        * Rendering the overview of versions in the current workspace
+        *
+        * @return      string          HTML (table)
+        * @see typo3/mod/user/ws/index.php for sister function!
+        */
+       function displayWorkspaceOverview()     {
+
+                       // Initialize variables:
+               $this->showWorkspaceCol = $GLOBALS['BE_USER']->workspace===0 && $this->MOD_SETTINGS['display']<=-98;
+
+                       // Get usernames and groupnames
+               $be_group_Array = t3lib_BEfunc::getListGroupNames('title,uid');
+               $groupArray = array_keys($be_group_Array);
+               $this->be_user_Array = t3lib_BEfunc::getUserNames();
+               if (!$GLOBALS['BE_USER']->isAdmin())            $this->be_user_Array = t3lib_BEfunc::blindUserNames($this->be_user_Array,$groupArray,1);
+
+                       // Initialize Workspace ID and filter-value:
+               if ($GLOBALS['BE_USER']->workspace===0) {
+                       $wsid = $this->details ? -99 : $this->MOD_SETTINGS['display'];          // Set wsid to the value from the menu (displaying content of other workspaces)
+                       $filter = $this->details ? 0 : $this->MOD_SETTINGS['filter'];
+               } else {
+                       $wsid = $GLOBALS['BE_USER']->workspace;
+                       $filter = 0;
+               }
+
+                       // Initialize workspace object and request all pending versions:
+               $wslibObj = t3lib_div::makeInstance('wslib');
+
+                       // Selecting ALL versions belonging to the workspace:
+               $versions = $wslibObj->selectVersionsInWorkspace($wsid, $filter, -99, $this->uid);      // $this->uid is the page id of LIVE record.
+
+                       // Traverse versions and build page-display array:
+               $pArray = array();
+               foreach($versions as $table => $records)        {
+                       foreach($records as $rec)       {
+                               $pageIdField = $table==='pages' ? 't3ver_oid' : 'realpid';
+                               $this->displayWorkspaceOverview_setInPageArray(
+                                       $pArray,
+                                       $table,
+                                       $rec
+                               );
+                       }
+               }
+
+                       // Make header of overview:
+               $tableRows = array();
+               if (count($pArray))     {
+                       $tableRows[] = '
+                               <tr class="bgColor5 tableheader">
+                                       <td nowrap="nowrap" colspan="2">Online Version:</td>
+                                       <td nowrap="nowrap" colspan="2">Offline Versions:</td>
+                                       <td nowrap="nowrap" colspan="4">Controls:</td>
+                               </tr>';
+
+                               // Add lines from overview:
+                       $tableRows = array_merge($tableRows, $this->displayWorkspaceOverview_list($pArray));
+
+                       $table = '<table border="0" cellpadding="0" cellspacing="1" class="lrPadding workspace-overview">'.implode('',$tableRows).'</table>';
+               } else $table = '';
+
+               $linkBack = t3lib_div::_GP('returnUrl') ? '<a href="'.htmlspecialchars(t3lib_div::_GP('returnUrl')).'" class="typo3-goBack"><img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/goback.gif','width="14" height="14"').' alt="" />Click here to go back</a><br/><br/>' : '';
+
+               $versionSelector = $GLOBALS['BE_USER']->workspace ? $this->doc->getVersionSelector($this->id) : '';
+
+               return $versionSelector.$linkBack.$table.$this->markupNewOriginals();
+       }
+
+       /**
+        * Rendering the content for the publish / review overview:
+        * (Made for internal recursive calling)
+        *
+        * @param       array           Storage of the elements to display (see displayWorkspaceOverview() / displayWorkspaceOverview_setInPageArray())
+        * @return      array           Table rows, see displayWorkspaceOverview()
+        */
+       function displayWorkspaceOverview_list($pArray) {
+               global $TCA;
+
+                       // If there ARE elements on this level, print them:
+               $warnAboutVersions_nonPages = FALSE;
+               $warnAboutVersions_page = FALSE;
+               if (is_array($pArray))  {
+                       foreach($pArray 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'], 1, '', '+edit,view,info,delete');
+                                       }
+
+                                               // Online version display:
+                                               // Create the main cells which will span over the number of versions there is.
+                                       $verLinkUrl = $TCA[$table]['ctrl']['versioningWS'];
+                                       $origElement = $icon.
+                                               ($verLinkUrl ? '<a href="'.htmlspecialchars('index.php?table='.$table.'&uid='.$rec_on['uid']).'">' : '').
+                                               t3lib_BEfunc::getRecordTitle($table,$rec_on,TRUE).
+                                               ($verLinkUrl ? '</a>' : '');
+                                       $mainCell_rowSpan = count($recs)>1 ? ' rowspan="'.count($recs).'"' : '';
+                                       $mainCell = '
+                                                               <td align="center"'.$mainCell_rowSpan.'>'.$this->formatVerId($rec_on['t3ver_id']).'</td>
+                                                               <td nowrap="nowrap"'.$mainCell_rowSpan.'>'.
+                                                                       $origElement.
+                                                                       '###SUB_ELEMENTS###'.   // For substitution with sub-elements, if any.
+                                                               '</td>';
+
+                                               // Offline versions display:
+                                               // Traverse the versions of the element
+                                       foreach($recs as $rec)  {
+
+                                                       // Get the offline version record and icon:
+                                               $rec_off = t3lib_BEfunc::getRecord($table,$rec['uid']);
+                                               $icon = t3lib_iconWorks::getIconImage($table, $rec_off, $this->doc->backPath, ' align="top" title="'.t3lib_BEfunc::getRecordIconAltText($rec_off,$table).'"');
+                                               $icon = $this->doc->wrapClickMenuOnIcon($icon, $table, $rec_off['uid'], 1, '', '+edit,view,info,delete');
+
+                                                       // Prepare diff-code:
+                                               if ($this->MOD_SETTINGS['diff'])        {
+                                                       if ($rec_on['t3ver_state']!=1)  {       // Not new record:
+                                                               list($diffHTML,$diffPct) = $this->createDiffView($table, $rec_off, $rec_on);
+                                                               $diffCode = ($diffPct<0 ? 'N/A' : ($diffPct ? $diffPct.'% change:' : '')).
+                                                                                       $diffHTML;
+                                                       } else {
+                                                               $diffCode = $this->doc->icons(1).'New element';
+                                                       }
+                                               } else $diffCode = '';
+
+                                                       // Prepare swap-mode values:
+                                               if ($table==='pages' && $rec_off['t3ver_swapmode']!=-1) {
+                                                       if ($rec_off['t3ver_swapmode']>0)       {
+                                                               $vType = 'branch';
+                                                       } else {
+                                                               $vType = 'page';
+                                                       }
+                                               } else {
+                                                       $vType = 'element';
+                                               }
+
+                                               switch($vType) {
+                                                       case 'element':
+                                                               $swapLabel = ' [Element]';
+                                                               $swapClass = 'ver-element';
+                                                               $warnAboutVersions_nonPages = $warnAboutVersions_page;  // Setting this if sub elements are found with a page+content (must be rendered prior to this of course!)
+                                                       break;
+                                                       case 'page':
+                                                               $swapLabel = ' [Page]';
+                                                               $swapClass = 'ver-page';
+                                                               $warnAboutVersions_page = !$this->showWorkspaceCol;             // This value is true only if multiple workspaces are shown and we need the opposite here.
+                                                       break;
+                                                       case 'branch':
+                                                               $swapLabel = ' [Branch]';
+                                                               $swapClass = 'ver-branch';
+                                                       break;
+                                               }
+
+                                                       // Modify main cell based on first version shown:
+                                               $subElements = array();
+                                               if ($table==='pages' && $rec_off['t3ver_swapmode']!=-1 && $mainCell)    {       // For "Page" and "Branch" swap modes where $mainCell is still carrying content (only first version)
+                                                       $subElements['on'] = $this->subElements($rec_on['uid'], $rec_off['t3ver_swapmode']);
+                                                       $subElements['off'] = $this->subElements($rec_off['uid'],$rec_off['t3ver_swapmode'],$rec_on['uid']);
+                                               }
+                                               $mainCell = str_replace('###SUB_ELEMENTS###', $subElements['on'], $mainCell);
+
+                                                       // Create version element:
+                                               $versionsInOtherWS = $this->versionsInOtherWS($table, $rec_on['uid']);
+                                               $versionsInOtherWSWarning = $versionsInOtherWS && $GLOBALS['BE_USER']->workspace!==0 ? '<br/>'.$this->doc->icons(2).'Other version(s) in workspace '.$versionsInOtherWS : '';
+                                               $multipleWarning = (!$mainCell && $GLOBALS['BE_USER']->workspace!==0? '<br/>'.$this->doc->icons(3).'<b>Multiple versions in same workspace!</b>' : '');
+                                               $verWarning = $warnAboutVersions || ($warnAboutVersions_nonPages && $GLOBALS['TCA'][$table]['ctrl']['versioning_followPages'])? '<br/>'.$this->doc->icons(3).'<b>Version inside version!</b>' : '';
+                                               $verElement = $icon.
+                                                       (!$this->details ? '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?id='.($table==='pages'?$rec_on['uid']:$rec_on['pid']).'&details='.rawurlencode($table.':'.$rec_off['uid']).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">' : '').
+                                                       t3lib_BEfunc::getRecordTitle($table,$rec_off,TRUE).
+                                                       (!$this->details ? '</a>' : '').
+                                                       $versionsInOtherWSWarning.
+                                                       $multipleWarning.
+                                                       $verWarning;
+
+                                               $ctrlTable = '
+                                                               <td nowrap="nowrap">'.$this->showStageChangeLog($table,$rec_off['uid'],$this->displayWorkspaceOverview_stageCmd($table,$rec_off)).'</td>
+                                                               <td nowrap="nowrap" class="'.$swapClass.'">'.
+                                                                       $this->displayWorkspaceOverview_commandLinks($table,$rec_on,$rec_off,$vType).
+                                                                       htmlspecialchars($swapLabel).
+                                                                       '&nbsp;&nbsp;</td>
+                                                               <td nowrap="nowrap"><b>Lifecycle:</b> '.htmlspecialchars($this->formatCount($rec_off['t3ver_count'])).'</td>'.          // Lifecycle
+                                                                       ($this->showWorkspaceCol ? '
+                                                               <td nowrap="nowrap">&nbsp;&nbsp;<b>Workspace:</b> '.htmlspecialchars($this->formatWorkspace($rec_off['t3ver_wsid'])).'</td>' : '');
+
+                                               if ($diffCode)  {
+                                                       $verElement = $verElement.'
+                                                       <br/><b>Difference to live element:</b>
+                                                       <table border="0" cellpadding="0" cellspacing="0" class="ver-verElement">
+                                                               <tr>
+                                                                       <td class="c-diffCell">'.$diffCode.'</td>
+                                                               </tr>
+                                                       </table>';
+                                               }
+
+
+                                                       // Create version cell:
+                                               $verCell = '
+                                                               <td align="center">'.$this->formatVerId($rec_off['t3ver_id']).'</td>
+                                                               <td nowrap="nowrap">'.
+                                                                       $verElement.
+                                                                       $subElements['off'].
+                                                                       '</td>
+                                                               '.$ctrlTable;
+
+                                                       // Compile table row:
+                                               $tableRows[] = '
+                                                       <tr class="bgColor4">
+                                                               '.$mainCell.$verCell.'
+                                                       </tr>';
+
+                                                       // Reset the main cell:
+                                               $mainCell = '';
+
+                                       }
+                               }
+                       }
+               }
+
+               return $tableRows;
+       }
+
+       /**
+        * Building up of the $pArray
+        * (Internal)
+        *
+        * @param       array           Array that is built up with the page tree structure
+        * @param       string          Table name
+        * @param       array           Table row
+        * @return      void            $pArray is passed by reference and modified internally
+        */
+       function displayWorkspaceOverview_setInPageArray(&$pArray,$table,$row)  {
+               if (!$this->details || $this->details==$table.':'.$row['uid'])  {
+                       $pArray[$table][$row['t3ver_oid']][] = $row;
+               }
+       }
+
+
+
+
+
+
+
+
+
+
+       /**************************************
+        *
+        * Helper functions (REDUNDANT FROM user/ws/index.php - someone could refactor this...)
+        *
+        *************************************/
+
+       /**
+        * 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] = '[Offline]';
+                               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];
+       }
+
+       /**
+        * 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)    {
+
+                       // Render, if not cached:
+               if (!isset($this->formatCount_cache[$count]))   {
+                       switch($count)  {
+                               case 0:
+                                       $this->formatCount_cache[$count] = 'Draft';
+                               break;
+                               case 1:
+                                       $this->formatCount_cache[$count] = 'Archive';
+                               break;
+                               default:
+                                       $this->formatCount_cache[$count] = 'Published '.$count.' times';
+                               break;
+                       }
+               }
+
+               return $this->formatCount_cache[$count];
+       }
+
+       /**
+        * 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).'
+                               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)  {
+               $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
+                       'log_data,tstamp,userid',
+                       'sys_log',
+                       'action=6
+                               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 = 'sent element to "Review"';
+                               break;
+                               case 10:
+                                       $text = 'approved for "Publish"';
+                               break;
+                               case -1:
+                                       $text = 'rejected element!';
+                               break;
+                               case 0:
+                                       $text = 'reset to "Editing"';
+                               break;
+                               default:
+                                       $text = '[undefined]';
+                               break;
+                       }
+                       $text = t3lib_BEfunc::dateTime($dat['tstamp']).': "'.$username.'" '.$text;
+                       $text.= ($data['comment']?'<br/>User Comment: <em>'.$data['comment'].'</em>':'');
+
+                       $entry[] = $text;
+               }
+
+               return count($entry) ? '<span onmouseover="document.getElementById(\'log_'.$table.$id.'\').style.visibility = \'visible\';" onmouseout="document.getElementById(\'log_'.$table.$id.'\').style.visibility = \'hidden\';">'.$stageCommands.' ('.count($entry).')</span>'.
+                               '<div class="logLayer" style="visibility: hidden; position: absolute;" id="log_'.$table.$id.'">'.implode('<hr/>',$entry).'</div>' : $stageCommands;
+       }
+
+       /**
+        * 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;
+
+               if (!$this->details && ($GLOBALS['BE_USER']->workspace===0 || !$this->MOD_SETTINGS['expandSubElements']))       {       // In online workspace we have a reduced view because otherwise it will bloat the listing:
+                       return '<br/>
+                                       <img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/ol/joinbottom.gif','width="18" height="16"').' align="top" alt="" title="" />'.
+                                       ($origId ?
+                                               '<a href="'.htmlspecialchars($this->doc->backPath.t3lib_extMgm::extRelPath('version').'cm1/index.php?id='.$uid.'&details='.rawurlencode('pages:'.$uid).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
+                                               '<span class="typo3-dimmed"><em>[Sub elements, click for details]</em><span></a>' :
+                                               '<span class="typo3-dimmed"><em>[Sub elements]</em><span>');
+               } 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 '
+                                       <!-- Sub-element tree for versions -->
+                                       <table border="0" cellpadding="0" cellspacing="1" class="ver-subtree">
+                                               '.implode('',$tCell).'
+                                       </table>';
+               }
+       }
+
+       /**
+        * 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:
+                                       $tdParams =  ' onmouseover="hlSubelements(\''.$origId.'_'.$tN.'_'.$rec[$origUidFields].'\', \''.$uid.'_'.$tN.'_'.$rec[$origUidFields].'\', 1, '.($this->MOD_SETTINGS['diff']==2?1:0).');"'.
+                                                               ' onmouseout="hlSubelements(\''.$origId.'_'.$tN.'_'.$rec[$origUidFields].'\', \''.$uid.'_'.$tN.'_'.$rec[$origUidFields].'\', 0, '.($this->MOD_SETTINGS['diff']==2?1:0).');"'.
+                                                               ' id="ver_'.$uid.'_'.$tN.'_'.$rec[$origUidFields].'" class="typo3-ver"';
+
+                                               // Create diff view:
+                                       if ($this->MOD_SETTINGS['diff'])        {
+                                               list($diffHTML,$diffPct) = $this->createDiffView($tN, $rec, $this->targets['orig_'.$origId.'_'.$tN.'_'.$rec[$origUidFields]]);
+
+                                               if ($this->MOD_SETTINGS['diff']==2)     {
+                                                       $diffCode =
+                                                               ($diffPct ? '<span class="nobr">'.$diffPct.'% change</span>' : '-').
+                                                               '<div style="visibility: hidden; position: absolute;" id="diff_'.$uid.'_'.$tN.'_'.$rec[$origUidFields].'" class="diffLayer">'.
+                                                               $diffHTML.
+                                                               '</div>';
+                                               } 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[] = '
+                                               <tr'.$tdParams.'>
+                                                       <td class="iconTitle">'.
+                                                               $HTMLdata.
+                                                               ($iconMode < 2 ?
+                                                                       '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/ol/join'.($iconMode ? 'bottom' : '').'.gif','width="18" height="16"').' alt="" />'.
+                                                                       t3lib_iconWorks::getIconImage($tN, $rec, $this->doc->backPath,'') : '').
+                                                               t3lib_BEfunc::getRecordTitle($tN, $rec, TRUE).
+                                                       '</td>
+                                                       <td class="cmdCell">'.
+                                                               $this->displayWorkspaceOverview_commandLinksSub($tN,$rec,$origId).
+                                                       '</td>'.($origId ? '<td class="diffCell">'.
+                                                               $diffCode.
+                                                       '</td>':'').'
+                                               </tr>';
+       }
+
+       /**
+        * 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);
+               }
+       }
+
+       /**
+        * 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;
+
+                       // 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[] = '
+                               <tr class="bgColor5 tableheader">
+                                       <td>Fieldname:</td>
+                                       <td width="98%" nowrap="nowrap">Colored diff-view:</td>
+                               </tr>
+                       ';
+
+                               // Initialize variables to pick up string lengths in:
+                       $allStrLen = 0;
+                       $diffStrLen = 0;
+
+                               // Traversing the first record and process all fields which are editable:
+                       foreach($diff_1_record as $fN => $fV)   {
+                               if ($TCA[$table]['columns'][$fN] && $TCA[$table]['columns'][$fN]['config']['type']!='passthrough' && !t3lib_div::inList('t3ver_label',$fN))     {
+
+                                               // Check if it is files:
+                                       $isFiles = FALSE;
+                                       if (strcmp(trim($diff_1_record[$fN]),trim($diff_2_record[$fN])) &&
+                                                       $TCA[$table]['columns'][$fN]['config']['type']=='group' &&
+                                                       $TCA[$table]['columns'][$fN]['config']['internal_type']=='file')        {
+
+                                                       // Initialize:
+                                               $uploadFolder = $TCA[$table]['columns'][$fN]['config']['uploadfolder'];
+                                               $files1 = array_flip(t3lib_div::trimExplode(',', $diff_1_record[$fN],1));
+                                               $files2 = array_flip(t3lib_div::trimExplode(',', $diff_2_record[$fN],1));
+
+                                                       // Traverse filenames and read their md5 sum:
+                                               foreach($files1 as $filename => $tmp)   {
+                                                       $files1[$filename] = @is_file(PATH_site.$uploadFolder.'/'.$filename) ? md5(t3lib_div::getUrl(PATH_site.$uploadFolder.'/'.$filename)) : $filename;
+                                               }
+                                               foreach($files2 as $filename => $tmp)   {
+                                                       $files2[$filename] = @is_file(PATH_site.$uploadFolder.'/'.$filename) ? md5(t3lib_div::getUrl(PATH_site.$uploadFolder.'/'.$filename)) : $filename;
+                                               }
+
+                                                       // Implode MD5 sums and set flag:
+                                               $diff_1_record[$fN] = implode(' ',$files1);
+                                               $diff_2_record[$fN] = implode(' ',$files2);
+                                               $isFiles = TRUE;
+                                       }
+
+                                               // If there is a change of value:
+                                       if (strcmp(trim($diff_1_record[$fN]),trim($diff_2_record[$fN])))        {
+
+
+                                                       // Get the best visual presentation of the value and present that:
+                                               $val1 = t3lib_BEfunc::getProcessedValue($table,$fN,$diff_2_record[$fN],0,1);
+                                               $val2 = t3lib_BEfunc::getProcessedValue($table,$fN,$diff_1_record[$fN],0,1);
+
+                                                       // Make diff result and record string lenghts:
+                                               $diffres = $t3lib_diff_Obj->makeDiffDisplay($val1,$val2,$isFiles?'div':'span');
+                                               $diffStrLen+= $t3lib_diff_Obj->differenceLgd;
+                                               $allStrLen+= strlen($val1.$val2);
+
+                                                       // If the compared values were files, substituted MD5 hashes:
+                                               if ($isFiles)   {
+                                                       $allFiles = array_merge($files1,$files2);
+                                                       foreach($allFiles as $filename => $token)       {
+                                                               if (strlen($token)==32 && strstr($diffres,$token))      {
+                                                                       $filename =
+                                                                               t3lib_BEfunc::thumbCode(array($fN=>$filename),$table,$fN,$this->doc->backPath).
+                                                                               $filename;
+                                                                       $diffres = str_replace($token,$filename,$diffres);
+                                                               }
+                                                       }
+                                               }
+
+                                                       // Add table row with result:
+                                               $tRows[] = '
+                                                       <tr class="bgColor4">
+                                                               <td>'.htmlspecialchars($GLOBALS['LANG']->sL(t3lib_BEfunc::getItemLabel($table,$fN))).'</td>
+                                                               <td width="98%">'.$diffres.'</td>
+                                                       </tr>
+                                               ';
+                                       } 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.= '<table border="0" cellpadding="1" cellspacing="1" class="diffTable">'.implode('',$tRows).'</table>';
+                       } else {
+                               $content.= '<span class="nobr">'.$this->doc->icons(1).'Complete match on editable fields.</span>';
+                       }
+               } else $content.= $this->doc->icons(3).'ERROR: Records could strangely not be found!';
+
+                       // Return value:
+               return array($content,$pctChange);
+       }
+
+       /**
+        * Links to stage change of a version
+        *
+        * @param       string          Table name
+        * @param       array           Offline record (version)
+        * @return      string          HTML content, mainly link tags and images.
+        */
+       function displayWorkspaceOverview_stageCmd($table,&$rec_off)    {
+#debug($rec_off['t3ver_stage']);
+               switch((int)$rec_off['t3ver_stage'])    {
+                       case 0:
+                               $sId = 1;
+                               $sLabel = 'Editing';
+                               $color = '#666666';
+                               $label = 'Comment for Reviewer:';
+                               $titleAttrib = 'Send to Review';
+                       break;
+                       case 1:
+                               $sId = 10;
+                               $sLabel = 'Review';
+                               $color = '#6666cc';
+                               $label = 'Comment for Publisher:';
+                               $titleAttrib = 'Approve for Publishing';
+                       break;
+                       case 10:
+                               $sLabel = 'Publish';
+                               $color = '#66cc66';
+                       break;
+                       case -1:
+                               $sLabel = $this->doc->icons(2).'Rejected';
+                               $sId = 0;
+                               $color = '#ff0000';
+                               $label = 'Comment:';
+                               $titleAttrib = 'Reset stage';
+                       break;
+                       default:
+                               $sLabel = 'Undefined';
+                               $sId = 0;
+                               $color = '';
+                       break;
+               }
+#debug($sId);
+
+               $raiseOk = !$GLOBALS['BE_USER']->workspaceCannotEditOfflineVersion($table,$rec_off);
+
+               if ($raiseOk && $rec_off['t3ver_stage']!=-1)    {
+                       $onClick = 'var commentTxt=window.prompt("Please explain why you reject:","");
+                                                       if (commentTxt!=null) {document.location="'.$this->doc->issueCommand(
+                                                       '&cmd['.$table.']['.$rec_off['uid'].'][version][action]=setStage'.
+                                                       '&cmd['.$table.']['.$rec_off['uid'].'][version][stageId]=-1'
+                                                       ).'&cmd['.$table.']['.$rec_off['uid'].'][version][comment]="+escape(commentTxt);}'.
+                                                       ' return false;';
+                               // Reject:
+                       $actionLinks.=
+                               '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
+                               '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/down.gif','width="14" height="14"').' alt="" align="top" title="Reject" />'.
+                               '</a>';
+               } else {
+                               // Reject:
+                       $actionLinks.=
+                               '<img src="'.$this->doc->backPath.'gfx/clear.gif" width="14" height="14" alt="" align="top" title="" />';
+               }
+
+               $actionLinks.= '<span style="background-color: '.$color.'; color: white;">'.$sLabel.'</span>';
+
+                       // Raise
+               if ($raiseOk)   {
+                       $onClick = 'var commentTxt=window.prompt("'.$label.'","");
+                                                       if (commentTxt!=null) {document.location="'.$this->doc->issueCommand(
+                                                       '&cmd['.$table.']['.$rec_off['uid'].'][version][action]=setStage'.
+                                                       '&cmd['.$table.']['.$rec_off['uid'].'][version][stageId]='.$sId
+                                                       ).'&cmd['.$table.']['.$rec_off['uid'].'][version][comment]="+escape(commentTxt);}'.
+                                                       ' return false;';
+                       if ($rec_off['t3ver_stage']!=10)        {
+                               $actionLinks.=
+                                       '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
+                                       '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/up.gif','width="14" height="14"').' alt="" align="top" title="'.htmlspecialchars($titleAttrib).'" />'.
+                                       '</a>';
+                       }
+               }
+               return $actionLinks;
+       }
+
+       /**
+        * 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)        {
+               if ($this->publishAccess && (!($GLOBALS['BE_USER']->workspaceRec['publish_access']&1) || (int)$rec_off['t3ver_stage']===10))    {
+                       $actionLinks =
+                               '<a href="'.htmlspecialchars($this->doc->issueCommand(
+                                               '&cmd['.$table.']['.$rec_on['uid'].'][version][action]=swap'.
+                                               '&cmd['.$table.']['.$rec_on['uid'].'][version][swapWith]='.$rec_off['uid']
+                                               )).'">'.
+                               '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/insert1.gif','width="14" height="14"').' alt="" align="top" title="Publish" />'.
+                               '</a>';
+                       if ($GLOBALS['BE_USER']->workspaceSwapAccess() && (int)$rec_on['t3ver_state']!==1 && (int)$rec_off['t3ver_state']!==2)  {
+                               $actionLinks.=
+                                       '<a href="'.htmlspecialchars($this->doc->issueCommand(
+                                                       '&cmd['.$table.']['.$rec_on['uid'].'][version][action]=swap'.
+                                                       '&cmd['.$table.']['.$rec_on['uid'].'][version][swapWith]='.$rec_off['uid'].
+                                                       '&cmd['.$table.']['.$rec_on['uid'].'][version][swapIntoWS]=1'
+                                                       )).'">'.
+                                       '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/swap.png','width="14" height="14"').' alt="" align="top" title="Swap" />'.
+                                       '</a>';
+                       }
+               }
+
+               if (!$GLOBALS['BE_USER']->workspaceCannotEditOfflineVersion($table,$rec_off))   {
+                               // Release
+                       $actionLinks.=
+                               '<a href="'.htmlspecialchars($this->doc->issueCommand('&cmd['.$table.']['.$rec_off['uid'].'][version][action]=clearWSID')).'" onclick="return confirm(\'Remove from workspace?\');">'.
+                               '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/group_clear.gif','width="14" height="14"').' alt="" align="top" title="Remove from workspace" />'.
+                               '</a>';
+
+                               // Edit
+                       if ($table==='pages' && $vType!=='element')     {
+                               $tempUid = ($vType==='branch' || $GLOBALS['BE_USER']->workspace===0 ? $rec_off['uid'] : $rec_on['uid']);
+                               $actionLinks.=
+                                       '<a href="#" onclick="top.loadEditId('.$tempUid.');top.goToModule(\''.$this->pageModule.'\'); return false;">'.
+                                       '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,t3lib_extMgm::extRelPath('cms').'layout/layout.gif','width="14" height="12"').' title="Edit page" alt="" />'.
+                                       '</a>';
+                       } else {
+                               $params = '&edit['.$table.']['.$rec_off['uid'].']=edit';
+                               $actionLinks.=
+                                       '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick($params,$this->doc->backPath)).'">'.
+                                       '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/edit2.gif','width="12" height="12"').' title="Edit element" alt="" />'.
+                                       '</a>';
+                       }
+               }
+
+                       // History/Log
+               $actionLinks.=
+                       '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$rec_off['uid']).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
+                       '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/history2.gif','width="13" height="12"').' title="Show Log" alt="" />'.
+                       '</a>';
+
+                       // View
+               if ($table==='pages')   {
+                       $tempUid = ($vType==='branch' || $GLOBALS['BE_USER']->workspace===0 ? $rec_off['uid'] : $rec_on['uid']);
+                       $actionLinks.=
+                               '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($tempUid,$this->doc->backPath,t3lib_BEfunc::BEgetRootLine($tempUid))).'">'.
+                               '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/zoom.gif','width="12" height="12"').' title="" alt="" />'.
+                               '</a>';
+               }
+
+               return $actionLinks;
+       }
+
+       /**
+        * Links to publishing etc of a version
+        *
+        * @param       string          Table name
+        * @param       array           Record
+        * @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.
+        * @return      string          HTML content, mainly link tags and images.
+        */
+       function displayWorkspaceOverview_commandLinksSub($table,$rec,$origId)  {
+               $uid = $rec['uid'];
+               if ($origId || $GLOBALS['BE_USER']->workspace===0)      {
+                       if (!$GLOBALS['BE_USER']->workspaceCannotEditRecord($table,$rec))       {
+                                       // Edit
+                               if ($table==='pages')   {
+                                       $actionLinks.=
+                                               '<a href="#" onclick="top.loadEditId('.$uid.');top.goToModule(\''.$this->pageModule.'\'); return false;">'.
+                                               '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,t3lib_extMgm::extRelPath('cms').'layout/layout.gif','width="14" height="12"').' title="Edit page" alt="" />'.
+                                               '</a>';
+                               } else {
+                                       $params = '&edit['.$table.']['.$uid.']=edit';
+                                       $actionLinks.=
+                                               '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::editOnClick($params,$this->doc->backPath)).'">'.
+                                               '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/edit2.gif','width="12" height="12"').' title="Edit element" alt="" />'.
+                                               '</a>';
+                               }
+                       }
+
+                               // History/Log
+                       $actionLinks.=
+                               '<a href="'.htmlspecialchars($this->doc->backPath.'show_rechis.php?element='.rawurlencode($table.':'.$uid).'&returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))).'">'.
+                               '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/history2.gif','width="13" height="12"').' title="Show Log" alt="" />'.
+                               '</a>';
+               }
+
+                       // View
+               if ($table==='pages')   {
+                       $actionLinks.=
+                               '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($uid,$this->doc->backPath,t3lib_BEfunc::BEgetRootLine($uid))).'">'.
+                               '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/zoom.gif','width="12" height="12"').' title="" alt="" />'.
+                               '</a>';
+               }
+
+               return $actionLinks;
        }
 }