[BUGFIX] Fix PHP warning in EXT:beuser
[Packages/TYPO3.CMS.git] / typo3 / class.show_rechis.inc
old mode 100755 (executable)
new mode 100644 (file)
index b44c406..57d7307
@@ -1,22 +1,23 @@
 <?php
 /***************************************************************
 *  Copyright notice
-*  
-*  (c) 1999-2003 Kasper Skaarhoj (kasper@typo3.com)
+*
+*  (c) 1999-2009 Kasper Skårhøj (kasperYYYY@typo3.com)
+*  (c) 2006-2009 Sebastian Kurfürst (sebastian@garbage-group.de)
 *  All rights reserved
 *
-*  This script is part of the TYPO3 project. The TYPO3 project is 
+*  This script is part of the TYPO3 project. The TYPO3 project is
 *  free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
-* 
+*
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
-*  A copy is found in the textfile GPL.txt and important notices to the license 
+*  A copy is found in the textfile GPL.txt and important notices to the license
 *  from the author is found in LICENSE.txt distributed with these scripts.
 *
-* 
+*
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/
-/** 
+
+/**
  * Class for the record history display script (show_rechis.php)
  *
- * $Id$
+ * XHTML Compliant
  *
- * @author     Kasper Skaarhoj <kasper@typo3.com>
+ * @author Sebastian Kurfürst <sebastian@garbage-group.de>
  */
+
 /**
- * [CLASS/FUNCTION INDEX of SCRIPT]
- *
- *
- *
- *   77: class recordHistory 
- *   88:     function getChangesSinceRecord($element,$hisUid=0,$hisUid_Stop=0) 
- *  133:     function nextHisUid($element,$hisUid)     
- *  167:     function compareChangesWithCurrent($element,$changeRec,$discardFileLists=0)       
- *  203:     function readFieldTypes($table,$id)       
- *  226:     function cmp($changeStatus,$oldRecord)    
- *  245:     function revertToPreviousValues($element,$field)  
- *  298:     function saveState($element,$sumUp)       
- *  343:     function removeFilefields($table,$dataArray)      
- *  363:     function displayHistory($element) 
- *  487:     function renderEntry($entry,$table)       
- *  537:     function displaySysHistoryEntry($sh_uid)  
- *  573:     function listHeader()     
- *  613:     function linkPage($str,$inparams=array(),$anchor="")      
- *
- * TOTAL FUNCTIONS: 13
- * (This index is automatically created/updated by the extension "extdeveval")
+ * Class for the record history display script (show_rechis.php)
  *
+ * @author Sebastian Kurfürst <sebastian@garbage-group.de>
+ * @package TYPO3
+ * @subpackage core
  */
+class recordHistory {
+               // External, static:
+               // Maximum number of sys_history steps to show.
+       var $maxSteps = 20;
+               // display diff or not (0-no diff, 1-inline)
+       var $showDiff = 1;
+               // on a pages table - show sub elements as well.
+       var $showSubElements = 1;
+               // show inserts and deletes as well
+       var $showInsertDelete = 1;
+
+               // Internal, GPvars
+               // Element reference, syntax [tablename]:[uid]
+       var $element;
+               // syslog ID which is not shown anymore
+       var $lastSyslogId;
+       var $returnUrl;
+
+               // Internal
+       var $changeLog;
+       var $showMarked=FALSE;
 
+       /**
+        * Constructor for the class
+        */
+       function __construct() {
+                       // GPvars:
+               $this->element = t3lib_div::_GP('element');
+               $this->returnUrl = t3lib_div::sanitizeLocalUrl(t3lib_div::_GP('returnUrl'));
+               $this->lastSyslogId = t3lib_div::_GP('diff');
+               $this->rollbackFields = t3lib_div::_GP('rollbackFields');
+                       // Resolve sh_uid if set
+               $this->resolveShUid();
+       }
 
+       /**
+        * Main function for the listing of history.
+        * It detects incoming variables like element reference, history element uid etc. and renders the correct screen.
+        *
+        * @return HTML content for the module
+        */
+       function main() {
+               $content = '';
+
+                       // Single-click rollback
+               if (t3lib_div::_GP('revert') && t3lib_div::_GP('sumUp')) {
+                       $this->rollbackFields = t3lib_div::_GP('revert');
+                       $this->showInsertDelete = 0;
+                       $this->showSubElements = 0;
+
+                       $element = explode(':', $this->element);
+                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                               '*',
+                               'sys_history',
+                               'tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($element[0], 'sys_history') . ' AND recuid=' . intval($element[1]),
+                               '',
+                               'uid DESC',
+                               '1'
+                       );
+                       $record = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
+                       $this->lastSyslogId = $record['sys_log_uid'];
+
+                       $this->createChangeLog();
+                       $completeDiff = $this->createMultipleDiff();
+                       $this->performRollback($completeDiff);
+                       t3lib_utility_Http::redirect($this->returnUrl);
+               }
 
+                       // Save snapshot
+               if (t3lib_div::_GP('highlight') && !t3lib_div::_GP('settings')) {
+                       $this->toggleHighlight(t3lib_div::_GP('highlight'));
+               }
 
+               $content .= $this->displaySettings();
+               if ($this->createChangeLog()) {
+                       if ($this->rollbackFields) {
+                               $completeDiff = $this->createMultipleDiff();
+                               $content .= $this->performRollback($completeDiff);
+                       }
 
+                       if ($this->lastSyslogId) {
+                               $completeDiff = $this->createMultipleDiff();
+                               $content .= $this->displayMultipleDiff($completeDiff);
+                       }
 
+                       if ($this->element) {
+                               $content .= $this->displayHistory();
+                       }
+               }
+               return $content;
+       }
 
+       /*******************************
+        *
+        * database actions
+        *
+        *******************************/
 
+       /**
+        * Toggles highlight state of record
+        *
+        * @param integer $uid Uid of sys_history entry
+        * @return void
+        */
+       function toggleHighlight($uid) {
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('snapshot', 'sys_history', 'uid=' . intval($uid));
+               $tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
+               $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_history', 'uid=' . intval($uid), array('snapshot' => !$tmp['snapshot']));
+       }
 
+       /**
+        * perform rollback
+        *
+        * @param array $diff Diff array to rollback
+        * @return void
+        * @access private
+        */
+       function performRollback($diff) {
+               if (!$this->rollbackFields) {
+                       return 0;
+               }
 
+               $reloadPageFrame = 0;
+               $rollbackData = explode(':', $this->rollbackFields);
 
+                       // PROCESS INSERTS AND DELETES
+                       // rewrite inserts and deletes
+               $cmdmapArray = array();
+               if ($diff['insertsDeletes']) {
 
-/**
- * Class for the record history display script (show_rechis.php)
- * 
- * @author     Kasper Skaarhoj <kasper@typo3.com>
- * @package TYPO3
- * @subpackage core
- */
-class recordHistory {
-       var $listType = 0;
-       var $maxSteps=20;
-       
-       
-       /**
-        * @param       [type]          $element: ...
-        * @param       [type]          $hisUid: ...
-        * @param       [type]          $hisUid_Stop: ...
-        * @return      [type]          ...
-        */
-       function getChangesSinceRecord($element,$hisUid=0,$hisUid_Stop=0)       {
-               global $TCA;
-
-               $sumChangesArray=array();
-               $sumChangesArray["changes"]=array();
-               $elParts = explode(":",$element);
-               if ($TCA[$elParts[0]])  {
-                       $addWhere = " AND sys_history.uid>=".$hisUid;
-                       if ($hisUid_Stop)       $addWhere.= " AND sys_history.uid<=".$hisUid_Stop;
-                       $query="SELECT sys_history.*,sys_log.userid FROM sys_history,sys_log WHERE 
-                               sys_history.sys_log_uid=sys_log.uid
-                               AND sys_history.tablename='".$elParts[0]."' 
-                               AND sys_history.recuid=".intval($elParts[1]).$addWhere." 
-                               ORDER BY uid DESC";
-
-                       $res = mysql(TYPO3_db,$query);
-                       $times=array();
-                       while ($newRow = mysql_fetch_assoc($res))       {
-                               $hisDat = unserialize($newRow["history_data"]);
-                               if (is_array($hisDat["newRecord"]) && is_array($hisDat["oldRecord"]))   {
-//                                     if ($newRow["uid"]!=$hisUid)    {       // Overlay the old-record values... 
-//debug($hisDat["oldRecord"]);
-                                               $sumChangesArray["changes"]=array_merge($sumChangesArray["changes"],$hisDat["oldRecord"]);
-       //                              } else {        // ... unless the record is the one we're waiting for..
-               //                              $sumChangesArray["changes"]=array_merge($sumChangesArray["changes"],$hisDat["newRecord"]);
-                       //              }
-                                       $sumChangesArray["counter"]++;
-                                       $sumChangesArray["tstamp"][]=$newRow["tstamp"];
-                                       $sumChangesArray["userList"][]=$newRow["userid"];
-                               } else {
-                                       debug("ERROR!");
+                       switch (count($rollbackData)) {
+                               case 1: // all tables
+                                       $data = $diff['insertsDeletes'];
+                                       break;
+
+                               case 2: // one record
+                                       if ($diff['insertsDeletes'][$this->rollbackFields]) {
+                                               $data[$this->rollbackFields] = $diff['insertsDeletes'][$this->rollbackFields];
+                                       }
+                                       break;
+
+                               case 3: // one field in one record -- ignore!
+                                       break;
+                       }
+                       if ($data) {
+                               foreach ($data as $key => $action) {
+                                       $elParts = explode(':', $key);
+                                       if ($action == 1) {     // inserted records should be deleted
+                                               $cmdmapArray[$elParts[0]][$elParts[1]]['delete'] = 1;
+                                                       // When the record is deleted, the contents of the record do not need to be updated
+                                               unset($diff['oldData'][$key]);
+                                               unset($diff['newData'][$key]);
+                                       } elseif ($action == -1) {      // deleted records should be inserted again
+                                               $cmdmapArray[$elParts[0]][$elParts[1]]['undelete'] = 1;
+                                       }
                                }
-                               
                        }
-               }       
-               return $sumChangesArray;
+               }
+
+                       // Writes the data:
+               if ($cmdmapArray) {
+                       $tce = t3lib_div::makeInstance('t3lib_TCEmain');
+                       $tce->stripslashes_values = 0;
+                       $tce->debug = 0;
+                       $tce->dontProcessTransformations = 1;
+                       $tce->start(array(), $cmdmapArray);
+                       $tce->process_cmdmap();
+                       unset($tce);
+                       if (isset($cmdmapArray['pages'])) {
+                               $reloadPageFrame = 1;
+                       }
+               }
+
+                       // PROCESS CHANGES
+                       // create an array for process_datamap
+               $diff_modified = array();
+               foreach ($diff['oldData'] as $key => $value) {
+                       $splitKey = explode(':', $key);
+                       $diff_modified[$splitKey[0]][$splitKey[1]] = $value;
+               }
+               switch (count($rollbackData)) {
+                       case 1: // all tables
+                               $data = $diff_modified;
+                               break;
+                       case 2: // one record
+                               $data[$rollbackData[0]][$rollbackData[1]] = $diff_modified[$rollbackData[0]][$rollbackData[1]];
+                               break;
+                       case 3: // one field in one record
+                               $data[$rollbackData[0]][$rollbackData[1]][$rollbackData[2]] = $diff_modified[$rollbackData[0]][$rollbackData[1]][$rollbackData[2]];
+                               break;
+               }
+
+                       // Removing fields:
+               $data = $this->removeFilefields($rollbackData[0], $data);
+
+                       // Writes the data:
+               $tce = t3lib_div::makeInstance('t3lib_TCEmain');
+               $tce->stripslashes_values = 0;
+               $tce->debug = 0;
+               $tce->dontProcessTransformations = 1;
+               $tce->start($data, array());
+               $tce->process_datamap();
+               unset($tce);
+               if (isset($data['pages'])) {
+                       $reloadPageFrame = 1;
+               }
+
+                       // Return to normal operation
+               $this->lastSyslogId = FALSE;
+               $this->rollbackFields = FALSE;
+               $this->createChangeLog();
+
+                       // Reload page frame if necessary
+               if ($reloadPageFrame) {
+                       return '<script type="text/javascript">
+                       /*<![CDATA[*/
+                       if (top.content && top.content.nav_frame && top.content.nav_frame.refresh_nav) {
+                               top.content.nav_frame.refresh_nav();
+                       }
+                       /*]]>*/
+                       </script>';
+               }
        }
 
+       /*******************************
+        *
+        * Display functions
+        *
+        *******************************/
+
        /**
-        * [Describe function...]
-        * 
-        * @param       [type]          $element: ...
-        * @param       [type]          $hisUid: ...
-        * @return      [type]          ...
+        * Displays settings
+        *
+        * @return string HTML code to modify settings
         */
-       function nextHisUid($element,$hisUid)   {
-               $elParts = explode(":",$element);
-               // Prev:
-                       $query="SELECT uid FROM sys_history WHERE 
-                               tablename='".$elParts[0]."' 
-                               AND recuid=".intval($elParts[1])." 
-                               AND uid<".intval($hisUid)."
-                               ORDER BY uid DESC LIMIT 1";
-                       $res = mysql(TYPO3_db,$query);
-                       if ($row = mysql_fetch_assoc($res))     {
-                               $prevUid=$row["uid"];
-                       }
-               // Next         
-                       $query="SELECT uid FROM sys_history WHERE 
-                               tablename='".$elParts[0]."' 
-                               AND recuid=".intval($elParts[1])." 
-                               AND uid>".intval($hisUid)."
-                               ORDER BY uid LIMIT 1";
-                       $res = mysql(TYPO3_db,$query);
-                       if ($row = mysql_fetch_assoc($res))     {
-                               $nextUid=$row["uid"];
+       function displaySettings() {
+                       // Get current selection from UC, merge data, write it back to UC
+               $currentSelection = is_array($GLOBALS['BE_USER']->uc['moduleData']['history']) ? $GLOBALS['BE_USER']->uc['moduleData']['history'] : array('maxSteps' => '', 'showDiff' => 1, 'showSubElements' => 1, 'showInsertDelete' => 1);
+
+               $currentSelectionOverride = t3lib_div::_GP('settings');
+               if ($currentSelectionOverride) {
+                       $currentSelection = array_merge($currentSelection, $currentSelectionOverride);
+                       $GLOBALS['BE_USER']->uc['moduleData']['history'] = $currentSelection;
+                       $GLOBALS['BE_USER']->writeUC($GLOBALS['BE_USER']->uc);
+               }
+
+                       // Display selector for number of history entries
+               $selector['maxSteps'] = array(
+                       10 => 10,
+                       20 => 20,
+                       50 => 50,
+                       100 => 100,
+                       '' => 'maxSteps_all',
+                       'marked' => 'maxSteps_marked'
+               );
+               $selector['showDiff'] = array(
+                       0 => 'showDiff_no',
+                       1 => 'showDiff_inline'
+               );
+               $selector['showSubElements'] = array(
+                       0 => 'no',
+                       1 => 'yes',
+               );
+               $selector['showInsertDelete'] = array(
+                       0 => 'no',
+                       1 => 'yes',
+               );
+
+                       // render selectors
+               $displayCode = '';
+               foreach ($selector as $key => $values) {
+                       $displayCode .= '<tr><td>' . $GLOBALS['LANG']->getLL($key, 1) . '</td>';
+                       $displayCode .= '<td><select name="settings[' . $key . ']" onChange="document.settings.submit()" style="width:100px">';
+                       foreach ($values as $singleKey => $singleVal) {
+                               $caption = ($GLOBALS['LANG']->getLL($singleVal, 1) ? $GLOBALS['LANG']->getLL($singleVal, 1) : $singleVal);
+                               $displayCode .= '<option value="' . $singleKey . '"' . (($singleKey == $currentSelection[$key]) ? ' selected="selected"' : '').'> ' . $caption . '</option>';
                        }
+                       $displayCode .= '</select></td></tr>';
+               }
+
+                       // set values correctly
+               if ($currentSelection['maxSteps'] != 'marked') {
+                       $this->maxSteps = $currentSelection['maxSteps'] ? intval($currentSelection['maxSteps']) : '';
+               } else {
+                       $this->showMarked = TRUE;
+                       $this->maxSteps = FALSE;
+               }
+               $this->showDiff = intval($currentSelection['showDiff']);
+               $this->showSubElements = intval($currentSelection['showSubElements']);
+               $this->showInsertDelete = intval($currentSelection['showInsertDelete']);
+
+               $content = '';
+                       // Get link to page history if the element history is shown
+               $elParts = explode(':', $this->element);
+               if ($elParts[0] != 'pages') {
+                       $content .= '<strong>' . $GLOBALS['LANG']->getLL('elementHistory', 1) . '</strong><br />';
+                       $pid = t3lib_BEfunc::getRecordRaw($elParts[0], 'uid=' . intval($elParts[1]));
+                       $content .= $this->linkPage($GLOBALS['LANG']->getLL('elementHistory_link', 1), array('element' => 'pages:' . $pid['pid']));
+               }
+               $content .= '<form name="settings" action="' . t3lib_div::getIndpEnv('TYPO3_REQUEST_URL') . '" method="post"><table>' . $displayCode . '</table></form>';
+               return $GLOBALS['SOBE']->doc->section($GLOBALS['LANG']->getLL('settings', 1), $content, FALSE, TRUE, FALSE, FALSE);
 
-               return array($prevUid,$nextUid);
        }
 
        /**
-        * [Describe function...]
-        * 
-        * @param       [type]          $element: ...
-        * @param       [type]          $changeRec: ...
-        * @param       [type]          $discardFileLists: ...
-        * @return      [type]          ...
+        * Shows the full change log
+        *
+        * @return string HTML for list, wrapped in a table.
         */
-       function compareChangesWithCurrent($element,$changeRec,$discardFileLists=0)     {
-               global $TCA;
-
-               $sumChangesArray=array();
-               $elParts = explode(":",$element);
-               $newChangeRec=array();
-               if ($TCA[$elParts[0]])  {
-                       $currentRecord = t3lib_BEfunc::getRecord($elParts[0],$elParts[1]);
-                                                       // list($currentRecord,$DB_fieldTypes) = $this->readFieldTypes($elParts[0],$elParts[1]);
-                                               // || ($cRecTypes[$col]=="int" && $currentRecord[$col]==0 && !strcmp($val,"")
-                       if (is_array($currentRecord) && is_array($changeRec["changes"]))        {
-                               reset($changeRec["changes"]);
-                               while(list($fN,$fV)=each($changeRec["changes"]))        {
-                                       if (strcmp($fV,$currentRecord[$fN]))    {
-                                               $newChangeRec["oldRecord"][$fN]=$fV;
-                                               $newChangeRec["newRecord"][$fN]=$currentRecord[$fN];
+       function displayHistory() {
+               $lines = array();
+
+                       // Initialize:
+               $lines[] = '<tr class="t3-row-header">
+                               <td> </td>
+                               <td>' . $GLOBALS['LANG']->getLL('time', 1) . '</td>
+                               <td>' . $GLOBALS['LANG']->getLL('age', 1) . '</td>
+                               <td>' . $GLOBALS['LANG']->getLL('user', 1) . '</td>
+                               <td>' . $GLOBALS['LANG']->getLL('tableUid', 1) . '</td>
+                               <td>' . $GLOBALS['LANG']->getLL('differences', 1) . '</td>
+                               <td>&nbsp;</td>
+                       </tr>';
+
+               $be_user_array = t3lib_BEfunc::getUserNames();
+
+                       // Traverse changelog array:
+               if (!$this->changeLog) {
+                       return 0;
+               }
+               $i = 0;
+               foreach ($this->changeLog as $sysLogUid => $entry) {
+                               // stop after maxSteps
+                       if ($i > $this->maxSteps && $this->maxSteps) {
+                               break;
+                       }
+
+                               // Show only marked states
+                       if (!$entry['snapshot'] && $this->showMarked) {
+                               continue;
+                       }
+                       $i++;
+
+                               // Get user names
+                       $userName = ($entry['user']?$be_user_array[$entry['user']]['username']:$GLOBALS['LANG']->getLL('externalChange', 1));
+
+                               // Build up single line
+                       $singleLine = array();
+
+                               // Diff link
+                       $image = t3lib_iconWorks::getSpriteIcon('actions-view-go-forward', array('title' => $GLOBALS['LANG']->getLL('sumUpChanges', TRUE)));
+                       $singleLine[] = '<span>' . $this->linkPage($image, array('diff' => $sysLogUid)) . '</span>'; // remove first link
+
+                       $singleLine[] = htmlspecialchars(t3lib_BEfunc::datetime($entry['tstamp']));     // add time
+                       $singleLine[] = htmlspecialchars(t3lib_BEfunc::calcAge($GLOBALS['EXEC_TIME'] - $entry['tstamp'], $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears')));        // add age
+                       $singleLine[] = htmlspecialchars($userName);    // add user name
+                       $singleLine[] = $this->linkPage($this->generateTitle($entry['tablename'], $entry['recuid']), array('element' => $entry['tablename'] . ':' . $entry['recuid']), '', $GLOBALS['LANG']->getLL('linkRecordHistory', 1));    // add record UID
+
+                               // Show insert/delete/diff/changed field names
+                       if ($entry['action']) { // insert or delete of element
+                               $singleLine[] = '<strong>' . htmlspecialchars($GLOBALS['LANG']->getLL($entry['action'], 1)) . '</strong>';
+                       } else {
+                                       // Display field names instead of full diff
+                               if (!$this->showDiff) {
+                                               // Re-write field names with labels
+                                       $tmpFieldList = explode(',', $entry['fieldlist']);
+                                       foreach ($tmpFieldList as $key => $value) {
+                                               $tmp = str_replace(':', '', $GLOBALS['LANG']->sl(t3lib_BEfunc::getItemLabel($entry['tablename'], $value), 1));
+
+                                               if ($tmp) {
+                                                       $tmpFieldList[$key] = $tmp;
+                                               } else {
+                                                               // remove fields if no label available
+                                                       unset($tmpFieldList[$key]);
+                                               }
                                        }
+                                       $singleLine[] = htmlspecialchars(implode(',', $tmpFieldList));
+                               } else {        // Display diff
+                                       $diff = $this->renderDiff($entry, $entry['tablename']);
+                                       $singleLine[] = $diff;
                                }
-                               $newChangeRec["tstamp"]=min($changeRec["tstamp"]);
-                               $newChangeRec["counter"]=$changeRec["counter"];
-                               $newChangeRec["userList"]=array_unique($changeRec["userList"]);
-                       } else {
-                               return false;   // No arrays, possibly no record
                        }
-               }       
-               
-               return $newChangeRec;
-       }
 
-       /**
-        * [Describe function...]
-        * 
-        * @param       [type]          $table: ...
-        * @param       [type]          $id: ...
-        * @return      [type]          ...
-        */
-       function readFieldTypes($table,$id)     {
-               $res = mysql(TYPO3_db,"SELECT * FROM $table WHERE uid=".intval($id));
-                       // Fetch the types of the fields.
-               if (mysql_num_rows($res))       {
-                       $currentRecord = mysql_fetch_assoc($res);
-                       $c=0;
-                       reset($currentRecord);
-                       $cRecTypes=array();
-                       while (list($col,$val)=each($currentRecord))    {
-                               $cRecTypes[$col]=mysql_field_type($res,$c);
-                               $c++;
+                               // Show link to mark/unmark state
+                       if (!$entry['action']) {
+                               if ($entry['snapshot']) {
+                                       $image = '<img' . t3lib_iconWorks::skinImg('', 'gfx/unmarkstate.gif') . ' align="top" alt="' . $GLOBALS['LANG']->getLL('unmarkState', 1) . '" title="' . $GLOBALS['LANG']->getLL('unmarkState', 1) . '" />';
+                               } else {
+                                       $image = '<img' . t3lib_iconWorks::skinImg('', 'gfx/markstate.gif') . ' align="top" alt="' . $GLOBALS['LANG']->getLL('markState', 1) . '" title="' . $GLOBALS['LANG']->getLL('markState', 1) . '" />';
+                               }
+                               $singleLine[] = $this->linkPage($image, array('highlight' => $entry['uid']));
+                       } else {
+                               $singleLine[] = '';
                        }
+
+                               // put line together
+                       $lines[] = '
+                               <tr class="db_list_normal">
+                                       <td>' . implode('</td><td>', $singleLine) . '</td>
+                               </tr>';
+               }
+
+                       // Finally, put it all together:
+               $theCode = '
+                       <!--
+                               History (list):
+                       -->
+                       <table class="typo3-dblist" border="0" cellpadding="0" cellspacing="0" id="typo3-history">
+                               ' . implode('', $lines) . '
+                       </table>';
+
+               if ($this->lastSyslogId) {
+                       $theCode .= '<br />' .  $this->linkPage(t3lib_iconWorks::getSpriteIcon('actions-move-to-bottom', array('title' => $GLOBALS['LANG']->getLL('fullView', TRUE))), array('diff' => ''));
                }
-               return array($currentRecord,$cRecTypes);
+
+                       // Add message about the difference view.
+               $flashMessage = t3lib_div::makeInstance(
+                       't3lib_FlashMessage',
+                       $GLOBALS['LANG']->getLL('differenceMsg'),
+                       '',
+                       t3lib_FlashMessage::INFO
+               );
+
+               $theCode .= '<br /><br />' . $flashMessage->render() . '<br />';
+
+                       // Add the whole content as a module section:
+               return $GLOBALS['SOBE']->doc->section($GLOBALS['LANG']->getLL('changes'), $theCode, FALSE, TRUE);
        }
 
        /**
-        * [Describe function...]
-        * 
-        * @param       [type]          $changeStatus: ...
-        * @param       [type]          $oldRecord: ...
-        * @return      [type]          ...
+        * Displays a diff over multiple fields including rollback links
+        *
+        * @param array $diff Difference array
+        * @return string HTML output
         */
-       function cmp($changeStatus,$oldRecord)  {
-               reset($oldRecord);
-               $changes=array();
-               while(list($fN,$fV)=each($oldRecord))   {
-                       if (isset($changeStatus[$fN]) && strcmp($fV,$changeStatus[$fN]))        {
-                               $changes["oldRecord"][$fN]=$changeStatus[$fN];
-                               $changes["newRecord"][$fN]=$fV;
+       function displayMultipleDiff($diff) {
+               $content = '';
+
+                       // Get all array keys needed
+               $arrayKeys = array_merge(array_keys($diff['newData']), array_keys($diff['insertsDeletes']), array_keys($diff['oldData']));
+               $arrayKeys = array_unique($arrayKeys);
+
+               if ($arrayKeys) {
+                       foreach ($arrayKeys as $key) {
+                               $record = '';
+                               $elParts = explode(':', $key);
+                                       // Turn around diff because it should be a "rollback preview"
+                               if ($diff['insertsDeletes'][$key] == 1) {       // insert
+                                       $record .= '<strong>' . $GLOBALS['LANG']->getLL('delete', 1) . '</strong>';
+                                       $record .= '<br />';
+                               } elseif ($diff['insertsDeletes'][$key] == -1) {
+                                       $record .= '<strong>' . $GLOBALS['LANG']->getLL('insert', 1) . '</strong>';
+                                       $record .= '<br />';
+                               }
+                                       // Build up temporary diff array
+                                       // turn around diff because it should be a "rollback preview"
+                               if ($diff['newData'][$key]) {
+                                       $tmpArr['newRecord'] = $diff['oldData'][$key];
+                                       $tmpArr['oldRecord'] = $diff['newData'][$key];
+                                       $record .= $this->renderDiff($tmpArr, $elParts[0], $elParts[1]);
+                               }
+
+                               $elParts = explode(':', $key);
+                               $titleLine = $this->createRollbackLink($key, $GLOBALS['LANG']->getLL('revertRecord', 1), 1) . $this->generateTitle($elParts[0], $elParts[1]);
+                               $record = '<div style="margin-left:10px;padding-left:5px;border-left:1px solid black;border-bottom:1px dotted black;padding-bottom:2px;">' . $record . '</div>';
+
+                               $content .= $GLOBALS['SOBE']->doc->section($titleLine, $record, FALSE, FALSE, FALSE, TRUE);
                        }
+                       $content = $this->createRollbackLink('ALL', $GLOBALS['LANG']->getLL('revertAll', 1), 0)  . '<div style="margin-left:10px;padding-left:5px;border-left:1px solid black;border-bottom:1px dotted black;padding-bottom:2px;">' . $content . '</div>';
+               } else {
+                       $content = $GLOBALS['LANG']->getLL('noDifferences', 1);
                }
-               return $changes;
+               return $GLOBALS['SOBE']->doc->section($GLOBALS['LANG']->getLL('mergedDifferences', 1), $content, FALSE, TRUE, FALSE, TRUE);
        }
 
        /**
-        * [Describe function...]
-        * 
-        * @param       [type]          $element: ...
-        * @param       [type]          $field: ...
-        * @return      [type]          ...
+        * Renders HTML table-rows with the comparison information of an sys_history entry record
+        *
+        * @param array $entry sys_history entry record.
+        * @param string $table The table name
+        * @param integer $rollbackUid If set to UID of record, display rollback links
+        * @return string HTML table
+        * @access private
         */
-       function revertToPreviousValues($element,$field)        {
-               $sumUp = t3lib_div::GPvar("sumUp");
-
-               $elParts = explode(":",$element);
-               $redirect=intval(t3lib_div::GPvar("doReturn"));
-               if ($sumUp==-1) {       // Undo/Redo
-                       $query="SELECT uid FROM sys_history WHERE 
-                               sys_history.tablename='".$elParts[0]."' 
-                               AND sys_history.recuid=".intval($elParts[1])." 
-                               ORDER BY uid DESC LIMIT 1";
-                       $res = mysql(TYPO3_db,$query);
-                       if ($row = mysql_fetch_assoc($res))     {
-                               $sumUp=$row["uid"];
-                       }
-                       $redirect=1;
-               }
-               if ($sumUp!=-1) {
-                       $changeRec=$this->compareChangesWithCurrent($element,$this->getChangesSinceRecord($element,$sumUp));
-
-                       $data =array();
-                       if (t3lib_BEfunc::getRecord($elParts[0],$elParts[1]))   {
-                               if ($field=="ALL_FIELDS")       {
-                                       $data=$changeRec["oldRecord"];
-                               } elseif(substr($field,0,6)=="field:") {
-                                       $data[substr($field,6)]=$changeRec["oldRecord"][substr($field,6)];
-                               }
-                               $data = $this->removeFilefields($elParts[0],$data);
-                               if (count($data))       {
-                                       $inData=array();
-                                       $inData[$elParts[0]][$elParts[1]]=$data;
-                                       $tce = t3lib_div::makeInstance("t3lib_TCEmain");
-                                       $tce->stripslashes_values=0;
-                                       $tce->debug=0;
-                                       $tce->dontProcessTransformations=1;
-                                       $tce->start($inData,array());
-                                       $tce->process_datamap();
+       function renderDiff($entry, $table, $rollbackUid = 0) {
+               $lines = array();
+               if (is_array($entry['newRecord'])) {
+
+                       $t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff');
+
+                       $fieldsToDisplay = array_keys($entry['newRecord']);
+                       foreach ($fieldsToDisplay as $fN) {
+                               t3lib_div::loadTCA($table);
+                               if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type'] != 'passthrough') {
+
+                                               // Create diff-result:
+                                       $diffres = $t3lib_diff_Obj->makeDiffDisplay(
+                                               t3lib_BEfunc::getProcessedValue($table, $fN, $entry['oldRecord'][$fN], 0, 1),
+                                               t3lib_BEfunc::getProcessedValue($table, $fN, $entry['newRecord'][$fN], 0, 1)
+                                       );
+                                       $lines[] = '
+                                               <tr class="bgColor4">
+                                               ' . ($rollbackUid ? '<td style="width:33px">' . $this->createRollbackLink($table . ':' . $rollbackUid . ':' . $fN, $GLOBALS['LANG']->getLL('revertField', 1), 2) . '</td>' : '') . '
+                                                       <td style="width:90px"><em>' . $GLOBALS['LANG']->sl(t3lib_BEfunc::getItemLabel($table, $fN), 1) . '</em></td>
+                                                       <td style="width:300px">' . nl2br($diffres) . '</td>
+                                               </tr>';
                                }
                        }
                }
-
-               if ($redirect)  {
-                       Header ("Location: ".t3lib_div::locationHeaderUrl(t3lib_div::GPvar("returnUrl")));
-                       exit;
+               if ($lines) {
+                       $content = '<table border="0" cellpadding="2" cellspacing="2" id="typo3-history-item">
+                                       ' . implode('', $lines) . '
+                               </table>';
+                       return $content;
                }
+                       // error fallback
+               return NULL;
        }
 
+       /*******************************
+        *
+        * build up history
+        *
+        *******************************/
+
        /**
-        * [Describe function...]
-        * 
-        * @param       [type]          $element: ...
-        * @param       [type]          $sumUp: ...
-        * @return      [type]          ...
+        * Creates a diff between the current version of the records and the selected version
+        *
+        * @return array Diff for many elements, 0 if no changelog is found
         */
-       function saveState($element,$sumUp)     {
-               $elParts = explode(":",$element);
-
-               $changeRec = $this->getChangesSinceRecord($element,$sumUp);
-
-               $query="SELECT history_data FROM sys_history WHERE 
-                       sys_history.tablename='".$elParts[0]."' 
-                       AND sys_history.recuid=".intval($elParts[1])." 
-                       ORDER BY uid DESC LIMIT 1";
-               $res = mysql(TYPO3_db,$query);
-               if ($row = mysql_fetch_assoc($res))     {
-                       $lastestData = unserialize($row["history_data"]);
-//                     debug($lastestData);
+       function createMultipleDiff() {
+               $insertsDeletes = array();
+               $newArr = array();
+               $differences = array();
+               if (!$this->changeLog) {
+                       return 0;
                }
 
+                       // traverse changelog array
+               foreach ($this->changeLog as $key => $value) {
+                       $field = $value['tablename'] . ':' . $value['recuid'];
 
-               $historyRecords=array();
-               $historyRecords["oldRecord"] = $changeRec["changes"];
-               $historyRecords["newRecord"] = array();
-               reset($historyRecords["oldRecord"]);
-               while(list($kk)=each($historyRecords["oldRecord"]))     {
-                       $historyRecords["newRecord"][$kk]=$lastestData["newRecord"][$kk];
+                               // inserts / deletes
+                       if ($value['action']) {
+                               if (!$insertsDeletes[$field]) {
+                                       $insertsDeletes[$field] = 0;
+                               }
+                               if ($value['action'] == 'insert') {
+                                       $insertsDeletes[$field]++;
+                               } else {
+                                       $insertsDeletes[$field]--;
+                               }
+                                       // unset not needed fields
+                               if ($insertsDeletes[$field] == 0) {
+                                       unset($insertsDeletes[$field]);
+                               }
+                       } else {
+                                       // update fields
+                                       // first row of field
+                               if (!isset($newArr[$field])) {
+                                       $newArr[$field] = $value['newRecord'];
+                                       $differences[$field] = $value['oldRecord'];
+                               } else { // standard
+                                       $differences[$field] = array_merge($differences[$field], $value['oldRecord']);
+                               }
+                       }
                }
 
-               $updateID = $GLOBALS["BE_USER"]->writelog(3,1,0,1,"Saved state","");
-               
-               $fields_values=array();
-               $fields_values["history_data"]=serialize($historyRecords);
-               $fields_values["fieldlist"]=implode(",",array_keys($historyRecords["oldRecord"]));
-               $fields_values["tstamp"]=time();
-               $fields_values["tablename"]=$elParts[0];
-               $fields_values["recuid"]=$elParts[1];
-               $fields_values["sys_log_uid"]=$updateID;
-               $fields_values["snapshot"]=1;
-               $query = t3lib_BEfunc::DBcompileInsert("sys_history",$fields_values);
-               $res = mysql(TYPO3_db,$query);
+                       // remove entries where there were no changes effectively
+               foreach ($newArr as $record => $value) {
+                       foreach ($value as $key => $innerVal) {
+                               if ($newArr[$record][$key] == $differences[$record][$key]) {
+                                       unset($newArr[$record][$key]);
+                                       unset($differences[$record][$key]);
+                               }
+                       }
+                       if (empty($newArr[$record]) && empty($differences[$record])) {
+                               unset($newArr[$record]);
+                               unset($differences[$record]);
+                       }
+               }
+               return array(
+                       'newData' => $newArr,
+                       'oldData' => $differences,
+                       'insertsDeletes' => $insertsDeletes
+               );
        }
 
        /**
-        * [Describe function...]
-        * 
-        * @param       [type]          $table: ...
-        * @param       [type]          $dataArray: ...
-        * @return      [type]          ...
+        * Creates change log including sub-elements, filling $this->changeLog
+        *
+        * @return integer
         */
-       function removeFilefields($table,$dataArray)    {
-               global $TCA;
-               if ($TCA[$table])       {
-                       t3lib_div::loadTCA($table);
-                       reset($TCA[$table]["columns"]);
-                       while(list($field,$config)=each($TCA[$table]["columns"]))       {
-                               if ($config["config"]["type"]=="group" && $config["config"]["internal_type"]=="file")   {
-                                       unset($dataArray[$field]);
+       function createChangeLog() {
+               $elParts = explode(':', $this->element);
+               $changeLog = $this->getHistoryData($elParts[0], $elParts[1]);
+
+                       // get history of tables of this page and merge it into changelog
+               if ($elParts[0] == 'pages' && $this->showSubElements) {
+                       foreach ($GLOBALS['TCA'] as $tablename => $value) {
+                                       // check if there are records on the page
+                               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $tablename, 'pid=' . intval($elParts[1]));
+                               while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+                                               // if there is history data available, merge it into changelog
+                                       if ($newChangeLog = $this->getHistoryData($tablename, $row['uid']))     {
+                                               foreach ($newChangeLog as $key => $value) {
+                                                       $changeLog[$key] = $value;
+                                               }
+                                       }
                                }
                        }
                }
-               return $dataArray;
+               if (!$changeLog) {
+                       return 0;
+               }
+
+               krsort($changeLog);
+               $this->changeLog = $changeLog;
+
+               return 1;
        }
 
        /**
-        * [Describe function...]
-        * 
-        * @param       [type]          $element: ...
-        * @return      [type]          ...
+        * Gets history and delete/insert data from sys_log and sys_history
+        *
+        * @param string $table DB table name
+        * @param integer $uid UID of record
+        * @return array history data of the record
         */
-       function displayHistory($element)       {
-               global $SOBE, $LANG, $TCA;
-               
-               $elParts = explode(":",$element);
-               $table = $elParts[0];
-               if ($TCA[$table])       {
-                       $mainQBody = "FROM sys_history,sys_log WHERE 
-                               sys_history.sys_log_uid=sys_log.uid
-                               AND sys_history.tablename='".$table."' 
-                               AND sys_history.recuid=".intval($elParts[1]);
-                       $query="SELECT count(*) ".$mainQBody; 
-                       $res = mysql(TYPO3_db,$query);
-                       list($Rcount)=mysql_fetch_row($res);
-
-                       $max = $this->maxSteps; // How many steps back...
-                       $query="SELECT sys_history.*,sys_log.userid ".$mainQBody." 
-                               ORDER BY uid LIMIT ".t3lib_div::intInRange($Rcount-$max,0).",".$max;
-                       $res = mysql(TYPO3_db,$query);
-
-                       $changesArray=array();
-                       $changeLog=array();
-                       while ($newRow = mysql_fetch_assoc($res))       {
-                               $hisDat = unserialize($newRow["history_data"]);
-                               if (is_array($hisDat["newRecord"]) && is_array($hisDat["oldRecord"]))   {
-                                               // If intermedia changes:
-                                       $intermediaChanges = $this->cmp($changesArray,$hisDat["oldRecord"]);
-                                       if (count($intermediaChanges) && !$newRow["snapshot"])  {
-                                               $changeLog[]=$intermediaChanges;
-                                       }
+       function getHistoryData($table, $uid) {
+               $uid = $this->resolveElement($table, $uid);
+
+                       // If table is found in $GLOBALS['TCA']:
+               if ($GLOBALS['TCA'][$table]) {
+
+                               // Selecting the $this->maxSteps most recent states:
+                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                                               'sys_history.*, sys_log.userid',
+                                               'sys_history, sys_log',
+                                               'sys_history.sys_log_uid = sys_log.uid
+                                                       AND sys_history.tablename = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history') . '
+                                                       AND sys_history.recuid = '.intval($uid),
+                                               '',
+                                               'sys_log.uid DESC',
+                                               $this->maxSteps
+                                       );
+
+                               // Traversing the result, building up changesArray / changeLog:
+                       $changeLog = array();
+
+                       while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+                                       // Only history until a certain syslog ID needed
+                               if ($row['sys_log_uid'] < $this->lastSyslogId && $this->lastSyslogId) {
+                                       continue;
+                               }
+                               $hisDat = unserialize($row['history_data']);
+
+                               if (is_array($hisDat['newRecord']) && is_array($hisDat['oldRecord'])) {
 
                                                // Add hisDat to the changeLog
-                                       $hisDat["uid"]=$newRow["uid"];
-                                       $hisDat["tstamp"]=$newRow["tstamp"];
-                                       $hisDat["user"]=$newRow["userid"];
-                                       $hisDat["snapshot"]=$newRow["snapshot"];
-                                       $changeLog[]=$hisDat;
-
-                                               // Update change array
-                                               // this is used to detect if any intermedia changes has been made.
-                                       $changesArray = array_merge($changesArray,$hisDat["newRecord"]);
+                                       $hisDat['uid'] = $row['uid'];
+                                       $hisDat['tstamp'] = $row['tstamp'];
+                                       $hisDat['user'] = $row['userid'];
+                                       $hisDat['snapshot'] = $row['snapshot'];
+                                       $hisDat['fieldlist'] = $row['fieldlist'];
+                                       $hisDat['tablename'] = $row['tablename'];
+                                       $hisDat['recuid'] = $row['recuid'];
+
+                                       $changeLog[$row['sys_log_uid']] = $hisDat;
                                } else {
-                                       debug("ERROR: [displayHistory]");
+                                       debug('ERROR: [getHistoryData]');
+                                               // error fallback
+                                       return 0;
                                }
                        }
-                       
-                       $lines=array();
-                       $darkerBgColor = t3lib_div::modifyHTMLcolor($SOBE->doc->bgColor4,-30,-30,-30);
-                       $darkerBgColor_interM = "#cccccc";
-                       if (t3lib_div::GPvar("sumUp"))  {
-                               
-                               $changeLog=array();
-                               $changeLog[]=$this->compareChangesWithCurrent($element,$this->getChangesSinceRecord($element,t3lib_div::GPvar("sumUp")));
-                               $this->listType=2;
-                               $lines[]=$this->listHeader();
-                               $be_users = t3lib_BEfunc::getUserNames();
-                               
-                               list($prevHisUid,$nextHisUid) = $this->nextHisUid($element,t3lib_div::GPvar("sumUp"));
-                               
-                               $linkPack=      ($prevHisUid ? $this->linkPage('<img src="gfx/pilup.gif" width="14" height="14" hspace=1 border="0" title="'.$LANG->getLL("prev").'">', array("sumUp"=>$prevHisUid)) : "").
-                               ($nextHisUid ? $this->linkPage('<img src="gfx/pildown.gif" width="14" height="14" hspace=1 border="0" title="'.$LANG->getLL("next").'">', array("sumUp"=>$nextHisUid)) : "").
-                               '<BR>'.$this->linkPage('<img src="gfx/history2.gif" width="13" height="12" hspace=1 vspace=3 border="0" title="'.$LANG->getLL("historyList").'">', array("sumUp"=>""), "uid_".t3lib_div::GPvar("sumUp")).
-                               
-                               $this->linkPage('<img src="gfx/savesnapshot.gif" width="17" height="12" hspace=1 vspace=3 border="0" title="'.$LANG->getLL("saveState").'">', array("saveState"=>t3lib_div::GPvar("sumUp"),"sumUp"=>""), "latest");
-                               
-                               reset($changeLog);
-                               while(list(,$entry)=each($changeLog))   {
-                                       if (!is_array($entry["userList"]))      $entry["userList"]=array();
-                                       reset($entry["userList"]);
-                                       while(list($uLk,$uV)=each($entry["userList"]))  {
-                                               $entry["userList"][$uLk]=$be_users[$uV]["username"];
-                                       }
-                                       
-                                       $lines[]='<tr bgColor="'.$darkerBgColor.'">
-                                               <td valign=top>'.$linkPack.'</td>
-                                               <td colspan=4><B>'.$LANG->getLL("time").':</B> '.t3lib_BEfunc::datetime($entry["tstamp"]).', '.t3lib_BEfunc::calcAge(time()-$entry["tstamp"],$GLOBALS["LANG"]->sL("LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears")).' &nbsp; - &nbsp; <B>'.$LANG->getLL("changeCount").':</B> '.$entry["counter"].'<BR>
-                                                       <B>'.$LANG->getLL("users").':</B> '.implode(", ",$entry["userList"]).' 
-                                               </td>
-                                       </tr>';
-                                       if (isset($entry["oldRecord"]) && isset($entry["newRecord"]))   {
-                                               $lines = array_merge($lines,$this->renderEntry($entry,$table));
-                                       } else {
-                                               $lines[]='<tr bgColor="'.$SOBE->doc->bgColor4.'"><td colspan=5 align=center><BR><B>'.$LANG->getLL("similar").'</B><BR><BR></td></tr>';
+                               // SELECT INSERTS/DELETES
+                       if ($this->showInsertDelete) {
+                                       // Select most recent inserts and deletes // WITHOUT snapshots
+                               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                                               'uid, userid, action, tstamp',
+                                               'sys_log',
+                                               'type = 1
+                                                       AND (action=1 OR action=3)
+                                                       AND tablename = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_log') . '
+                                                       AND recuid = ' . intval($uid),
+                                               '',
+                                               'uid DESC',
+                                               $this->maxSteps
+                                       );
+                               while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+
+                                       if ($row['uid'] < $this->lastSyslogId && $this->lastSyslogId) {
+                                               continue;
                                        }
-                               }
-                       } else {                        
-                                       // Change Log:
-                               $this->listType=1;
-                               $be_users = t3lib_BEfunc::getUserNames();
-                               $lines[]=$this->listHeader();
-                               reset($changeLog);
-                               
-                               while(list($c,$entry)=each($changeLog)) {
-                                       $lines[]='<tr><td colspan=3>&nbsp;</td></tr>';
-                                       $lastAnchor = ($c+1==count($changeLog)?'<a name="latest"></a>':"");
-                                       if ($entry["uid"])      {
-                                               $theTime = $this->linkPage(t3lib_BEfunc::datetime($entry["tstamp"]),array("sh_uid"=>$entry["uid"],"element"=>""));
-                                               $lines[]='<tr bgColor="'.($entry["snapshot"]?$SOBE->doc->bgColor2:$darkerBgColor).'">
-                                                       <td colspan=2>'.$lastAnchor.'<a name="uid_'.$entry["uid"].'"></a>'.($entry["snapshot"]?'<img src="gfx/napshot.gif" width="12" height="12" border="0" align=top>':'').'<B>'.$LANG->getLL("time").':</B> '.$theTime.', '.t3lib_BEfunc::calcAge(time()-$entry["tstamp"],$GLOBALS["LANG"]->sL("LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears")).' &nbsp; - &nbsp; <B>'.$LANG->getLL("user").':</B> '.$be_users[$entry["user"]]["username"].'</td>
-                                                       <td>'.
-                                                       $this->linkPage('<img src="gfx/history.gif" width="13" height="12" hspace=2 border="0" title="'.$LANG->getLL("revertAllFields").'">', array("revert"=>"ALL_FIELDS","sumUp"=>$entry["uid"],"doReturn"=>1)).
-                                                       $this->linkPage('<img src="gfx/history_details.gif" width="12" height="12" hspace=2 border="0" title="'.$LANG->getLL("sumUpChanges").'">', array("sumUp"=>$entry["uid"])).'</td>
-                                               </tr>';
-                                       } else {
-                                               $lines[]='<tr bgColor="'.$darkerBgColor_interM.'">
-                                                       <td colspan=3><strong>'.$LANG->getLL("externalChange").'</strong></td>
-                                                       </tr>';
+                                       $hisDat = array();
+
+                                       switch ($row['action']) {
+                                               case 1: // Insert
+                                                       $hisDat['action'] = 'insert';
+                                                       break;
+                                               case 3: // Delete
+                                                       $hisDat['action'] = 'delete';
+                                                       break;
                                        }
-                                       $lines = array_merge($lines,$this->renderEntry($entry,$table));
+                                       $hisDat['tstamp'] = $row['tstamp'];
+                                       $hisDat['user'] = $row['userid'];
+                                       $hisDat['tablename'] = $table;
+                                       $hisDat['recuid'] = $uid;
+                                       $changeLog[$row['uid']] = $hisDat;
                                }
                        }
-                       
-                       $theCode='<table border=0 cellpadding=2 cellspacing=2>'.implode("",$lines).'</table>';
-                       $theCode.='<BR><img src="gfx/icon_note.gif" width="18" height="16" border="0" align="top">'.$LANG->getLL("differenceMsg")."<BR><BR>";
-                       return $SOBE->doc->section($LANG->getLL("changes"),$theCode,0,1);
+                       return $changeLog;
                }
+                       // error fallback
+               return 0;
        }
 
+
+       /*******************************
+        *
+        * Various helper functions
+        *
+        *******************************/
+
        /**
-        * [Describe function...]
-        * 
-        * @param       [type]          $entry: ...
-        * @param       [type]          $table: ...
-        * @return      [type]          ...
+        * Generates the title and puts the record title behind
+        *
+        * @param string $table
+        * @param string $uid
+        * @return string
         */
-       function renderEntry($entry,$table)     {
-               global $SOBE, $LANG, $TCA;
-               $lines=array();
-               if (is_array($entry["newRecord"]))      {
-                       $fieldsToDisplay = array_keys($entry["newRecord"]);
-                       reset($fieldsToDisplay);
-                       $t3lib_diff_Obj = t3lib_div::makeInstance("t3lib_diff");
-                       while(list(,$fN)=each($fieldsToDisplay))        {
-                               $diffres = $t3lib_diff_Obj->makeDiffDisplay(
-                                       t3lib_BEfunc::getProcessedValue($table,$fN,$entry["oldRecord"][$fN],0,1),
-                                       t3lib_BEfunc::getProcessedValue($table,$fN,$entry["newRecord"][$fN],0,1)
-                               );
-
-                               switch($this->listType) {
-                                       case 1:
-                                               $lines[]='<tr bgColor="'.$SOBE->doc->bgColor4.'">
-                                                       <td valign=top>'.$LANG->sl(t3lib_BEfunc::getItemLabel($table,$fN,"<i>|</i>")).'</td>
-                                                       <td valign=top>'.nl2br($diffres).'</td>
-                                                       <td valign=top>&nbsp;</td>
-                                               </tr>';
-                                       break;
-                                       case 2:
-                                               $lines[]='<tr bgColor="'.$SOBE->doc->bgColor4.'">
-                                                       <td valign=top>'.$LANG->sl(t3lib_BEfunc::getItemLabel($table,$fN,"<i>|</i>")).'</td>
-                                                       <td valign=top>'.t3lib_BEfunc::getProcessedValue($table,$fN,$entry["oldRecord"][$fN]).'</td>
-                                                       <td valign=top>'.$this->linkPage('<img src="gfx/history.gif" width="13" height="12" border="0" title="'.$LANG->getLL("revertField").'">', array("revert"=>"field:".$fN)).'</td>
-                                                       <td valign=top>'.t3lib_BEfunc::getProcessedValue($table,$fN,$entry["newRecord"][$fN]).'</td>
-                                                       <td valign=top>'.nl2br($diffres).'</td>
-                                               </tr>';
-                                       break;
-                                       default:
-                                               $lines[]='<tr bgColor="'.$SOBE->doc->bgColor4.'">
-                                                       <td valign=top>'.$LANG->sl(t3lib_BEfunc::getItemLabel($table,$fN,"<i>|</i>")).'</td>
-                                                       <td valign=top>'.t3lib_BEfunc::getProcessedValue($table,$fN,$entry["oldRecord"][$fN]).'</td>
-                                                       <td valign=top>'.t3lib_BEfunc::getProcessedValue($table,$fN,$entry["newRecord"][$fN]).'</td>
-                                                       <td valign=top>'.nl2br($diffres).'</td>
-                                               </tr>';
-                                       break;
-                               }
-                       }
+       function generateTitle($table, $uid) {
+               $out = $table . ':' . $uid;
+               if ($labelField = $GLOBALS['TCA'][$table]['ctrl']['label']) {
+                       $record = t3lib_BEfunc::getRecordRaw($table, 'uid=' . intval($uid));
+                       $out .= ' (' . t3lib_BEfunc::getRecordTitle($table, $record, TRUE) . ')';
                }
-               return $lines;
+               return $out;
+       }
+       /**
+        * Creates a link for the rollback
+        *
+        * @param string $key Parameter which is set to rollbackFields
+        * @param string $alt Optional, alternative label and title tag of image
+        * @param integer $type Optional, type of rollback: 0 - ALL; 1 - element; 2 - field
+        * @return string HTML output
+        */
+       function createRollbackLink($key, $alt = '', $type = 0) {
+               return $this->linkPage('<img ' . t3lib_iconWorks::skinImg('', 'gfx/revert_' . $type . '.gif', 'width="33" height="33"') . ' alt="' . $alt . '" title="' . $alt . '" align="middle" />', array('rollbackFields' => $key));
        }
 
        /**
-        * [Describe function...]
-        * 
-        * @param       [type]          $sh_uid: ...
-        * @return      [type]          ...
+        * Creates a link to the same page.
+        *
+        * @param string $str String to wrap in <a> tags (must be htmlspecialchars()'ed prior to calling function)
+        * @param array $inparams Array of key/value pairs to override the default values with.
+        * @param string $anchor Possible anchor value.
+        * @param string $title Possible title.
+        * @return string Link.
+        * @access private
         */
-       function displaySysHistoryEntry($sh_uid)        {
-               global $SOBE, $LANG, $TCA;
-
-               $query="SELECT * FROM sys_history WHERE uid=".intval($sh_uid);
-               $res = mysql(TYPO3_db,$query);
-               $newRow = mysql_fetch_assoc($res);
-               if (is_array($newRow))  {
-                       $this->listType=0;
-                       $lines=array();
-
-                       $recIdentString = $LANG->sL($TCA[$newRow["tablename"]]["ctrl"]["title"])."/".$newRow["recuid"];
-                       $recIdentString = $this->linkPage($recIdentString,array("sh_uid"=>"","element"=>$newRow["tablename"].":".$newRow["recuid"]),"uid_".$sh_uid);
-                       $lines[]='<tr bgColor="'.$SOBE->doc->bgColor5.'">
-                               <td colspan=4>
-                                       <strong>'.$LANG->getLL("tableUid").':</strong> '.$recIdentString.'<br>
-                                       <strong>'.$LANG->getLL("time").':</strong> '.t3lib_BEfunc::datetime($newRow["tstamp"]).', '.t3lib_BEfunc::calcAge(time()-$newRow["tstamp"],$GLOBALS["LANG"]->sL("LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears")).'<br>
-                                       </td>
-                       </tr>';
+       function linkPage($str, $inparams = array(), $anchor = '', $title = '') {
+
+                       // Setting default values based on GET parameters:
+               $params['element'] = $this->element;
+               $params['returnUrl'] = $this->returnUrl;
+               $params['diff'] = $this->lastSyslogId;
+                       // Mergin overriding values:
+               $params = array_merge($params, $inparams);
+
+                       // Make the link:
+               $link = 'show_rechis.php?' . t3lib_div::implodeArrayForUrl('', $params) . ($anchor ? '#' . $anchor : '');
+               return '<a href="' . htmlspecialchars($link) . '"' . ($title ? ' title="' . $title . '"' : '') . '>' . $str . '</a>';
+       }
 
-                       $lines[]=$this->listHeader();
+       /**
+        * Will traverse the field names in $dataArray and look in $GLOBALS['TCA'] if the fields are of types which cannot be handled by the sys_history (that is currently group types with internal_type set to "file")
+        *
+        * @param string $table Table name
+        * @param array $dataArray The data array
+        * @return array The modified data array
+        * @access private
+        */
+       function removeFilefields($table, $dataArray) {
+               if ($GLOBALS['TCA'][$table]) {
+                       t3lib_div::loadTCA($table);
 
-                       $historyData = unserialize($newRow["history_data"]);
-                       $lines = array_merge($lines,$this->renderEntry($historyData,$newRow["tablename"]));
-                       
-                       $theCode='<table border=0 cellpadding=2 cellspacing=2>'.implode("",$lines).'</table>';
-                       $theCode.='<BR><img src="gfx/icon_note.gif" width="18" height="16" border="0" align="top">'.$LANG->getLL("differenceMsg")."<BR><BR>";
-                       $content.=$SOBE->doc->section($LANG->getLL("changes"),$theCode,0,1);
+                       foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $config) {
+                               if ($config['config']['type'] == 'group' && $config['config']['internal_type'] == 'file') {
+                                       unset($dataArray[$field]);
+                               }
+                       }
                }
-               return $content;
+               return $dataArray;
        }
 
        /**
-        * [Describe function...]
-        * 
-        * @return      [type]          ...
+        * Convert input element reference to workspace version if any.
+        *
+        * @param string $table Table of input element
+        * @param integer $uid UID of record
+        * @return integer converted UID of record
         */
-       function listHeader()   {
-               global $SOBE, $LANG, $TCA;
-
-               switch($this->listType) {
-                       case 1:
-                               $out='<tr bgColor="'.$SOBE->doc->bgColor5.'">
-                                       <td valign=top nowrap><strong>'.$LANG->getLL("fieldName").':</strong></td>
-                                       <td valign=top nowrap><strong>'.$LANG->getLL("difference").':</strong></td>
-                                       <td valign=top>&nbsp;</td>
-                               </tr>'; 
-                       break;
-                       case 2:
-                               $out='<tr bgColor="'.$SOBE->doc->bgColor5.'">
-                                       <td valign=top nowrap><strong>'.$LANG->getLL("fieldName").':</strong></td>
-                                       <td valign=top nowrap><strong>'.$LANG->getLL("oldValue").':</strong></td>
-                                       <td valign=top nowrap><strong>'.$this->linkPage('<img src="gfx/history.gif" width="13" height="12" border="0" title="'.$LANG->getLL("revertAllFields").'">', array("revert"=>"ALL_FIELDS")).'</strong></td>
-                                       <td valign=top nowrap><strong>'.$LANG->getLL("currentValue").':</strong></td>
-                                       <td valign=top nowrap><strong>'.$LANG->getLL("difference").':</strong></td>
-                               </tr>'; 
-                       break;
-                       default:
-                               $out='<tr bgColor="'.$SOBE->doc->bgColor5.'">
-                                       <td valign=top nowrap><strong>'.$LANG->getLL("fieldName").':</strong></td>
-                                       <td valign=top nowrap><strong>'.$LANG->getLL("oldValue").':</strong></td>
-                                       <td valign=top nowrap><strong>'.$LANG->getLL("newValue").':</strong></td>
-                                       <td valign=top nowrap><strong>'.$LANG->getLL("difference").':</strong></td>
-                               </tr>'; 
-                       break;
+       function resolveElement($table, $uid) {
+               if (isset($GLOBALS['TCA'][$table])) {
+                       if ($workspaceVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $table, $uid, 'uid')) {
+                               $uid = $workspaceVersion['uid'];
+                       }
                }
-               return $out;
+               return $uid;
        }
 
        /**
-        * [Describe function...]
-        * 
-        * @param       [type]          $str: ...
-        * @param       [type]          $inparams: ...
-        * @param       [type]          $anchor: ...
-        * @return      [type]          ...
+        * Resolve sh_uid (used from log)
+        *
+        * @return void
         */
-       function linkPage($str,$inparams=array(),$anchor="")    {
-               $params["sh_uid"]=t3lib_div::GPvar("sh_uid");
-               $params["sumUp"]=t3lib_div::GPvar("sumUp");
-               $params["element"]=t3lib_div::GPvar("element");
-               $params["returnUrl"]=t3lib_div::GPvar("returnUrl");
-               $params = array_merge($params,$inparams);                       // Candidate for t3lib_div::array_merge() if integer-keys will some day make trouble...
-               $link = '<a href="show_rechis.php?'.t3lib_div::implodeArrayForUrl("",$params).($anchor?"#".$anchor:"").'">'.$str.'</a>';
-               return $link;
+       function resolveShUid() {
+               if (t3lib_div::_GP('sh_uid')) {
+                       $sh_uid = t3lib_div::_GP('sh_uid');
+                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'sys_history', 'uid=' . intval($sh_uid));
+                       $record = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
+                       $this->element = $record['tablename'] . ':' . $record['recuid'];
+                       $this->lastSyslogId = $record['sys_log_uid'] - 1;
+               }
        }
 }
-
-if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/class.show_rechis.inc'])    {
-       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/class.show_rechis.inc']);
-}
 ?>
\ No newline at end of file