TYPO3 API  SVNRelease
class.show_rechis.inc
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 1999-2009 Kasper Skårhøj (kasperYYYY@typo3.com)
00006 *  (c) 2006-2009 Sebastian Kurfürst (sebastian@garbage-group.de)
00007 *  All rights reserved
00008 *
00009 *  This script is part of the TYPO3 project. The TYPO3 project is
00010 *  free software; you can redistribute it and/or modify
00011 *  it under the terms of the GNU General Public License as published by
00012 *  the Free Software Foundation; either version 2 of the License, or
00013 *  (at your option) any later version.
00014 *
00015 *  The GNU General Public License can be found at
00016 *  http://www.gnu.org/copyleft/gpl.html.
00017 *  A copy is found in the textfile GPL.txt and important notices to the license
00018 *  from the author is found in LICENSE.txt distributed with these scripts.
00019 *
00020 *
00021 *  This script is distributed in the hope that it will be useful,
00022 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00023 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00024 *  GNU General Public License for more details.
00025 *
00026 *  This copyright notice MUST APPEAR in all copies of the script!
00027 ***************************************************************/
00028 /**
00029  * Class for the record history display script (show_rechis.php)
00030  *
00031  * $Id: class.show_rechis.inc 8742 2010-08-30 18:55:32Z baschny $
00032  * XHTML Compliant
00033  *
00034  * @author  Sebastian Kurfürst <sebastian@garbage-group.de>
00035  */
00036 
00037 
00038 /**
00039  * Class for the record history display script (show_rechis.php)
00040  *
00041  * @author  Sebastian Kurfürst <sebastian@garbage-group.de>
00042  * @package TYPO3
00043  * @subpackage core
00044  */
00045 class recordHistory {
00046         // External, static:
00047     var $maxSteps=20;       // Maximum number of sys_history steps to show.
00048     var $showDiff=1;        // display diff or not (0-no diff, 1-inline)
00049     var $showSubElements=1;     // on a pages table - show sub elements as well.
00050     var $showInsertDelete=1;        // show inserts and deletes as well
00051 
00052         // Internal, GPvars
00053     var $element;           // Element reference, syntax [tablename]:[uid]
00054     var $lastSyslogId;      // syslog ID which is not shown anymore
00055     var $returnUrl;
00056 
00057         // Internal
00058     var $changeLog;
00059     var $showMarked=FALSE;
00060     /**
00061      * Constructor for the class
00062      *
00063      * @return  void
00064      */
00065     function recordHistory()    {
00066             // GPvars:
00067         $this->element = t3lib_div::_GP('element');
00068         $this->returnUrl = t3lib_div::sanitizeLocalUrl(t3lib_div::_GP('returnUrl'));
00069         $this->lastSyslogId = t3lib_div::_GP('diff');
00070         $this->rollbackFields = t3lib_div::_GP('rollbackFields');
00071             // resolve sh_uid if set
00072         $this->resolveShUid();
00073     }
00074 
00075     /**
00076      * Main function for the listing of history.
00077      * It detects incoming variables like element reference, history element uid etc. and renders the correct screen.
00078      *
00079      * @return  HTML        content for the module
00080      */
00081     function main() {
00082         $content = '';
00083 
00084             // single-click rollback
00085         if (t3lib_div::_GP('revert') && t3lib_div::_GP('sumUp'))    {
00086             $this->rollbackFields = t3lib_div::_GP('revert');
00087             $this->showInsertDelete = 0;
00088             $this->showSubElements = 0;
00089 
00090             $element = explode(':',$this->element);
00091             $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');
00092             $record = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
00093             $this->lastSyslogId = $record['sys_log_uid'];
00094 
00095             $this->createChangeLog();
00096             $completeDiff = $this->createMultipleDiff();
00097             $this->performRollback($completeDiff);
00098             t3lib_utility_Http::redirect($this->returnUrl);
00099         }
00100 
00101             // save snapshot
00102         if (t3lib_div::_GP('highlight') && !t3lib_div::_GP('settings')) {
00103             $this->toggleHighlight(t3lib_div::_GP('highlight'));
00104         }
00105 
00106         $content .= $this->displaySettings();
00107         if ($this->createChangeLog())   {
00108             if ($this->rollbackFields)  {
00109                 $completeDiff = $this->createMultipleDiff();
00110                 $content .= $this->performRollback($completeDiff);
00111 
00112             }
00113             if ($this->lastSyslogId)    {
00114                 $completeDiff = $this->createMultipleDiff();
00115                 $content .= $this->displayMultipleDiff($completeDiff);
00116             }
00117             if ($this->element) {
00118                 $content .= $this->displayHistory();
00119             }
00120         }
00121         return $content;
00122     }
00123 
00124     /*******************************
00125      *
00126      * database actions
00127      *
00128      *******************************/
00129 
00130     /**
00131      * toggles highlight state of record
00132      *
00133      * @param   integer     uid of sys_history entry
00134      * @return  [type]      ...
00135      */
00136     function toggleHighlight($uid)  {
00137         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('snapshot','sys_history','uid='.intval($uid));
00138         $tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
00139         if ($tmp['snapshot'])   {
00140             $tmp = 0;
00141         } else {
00142             $tmp = 1;
00143         }
00144         $updateFields = array('snapshot' => $tmp);
00145         $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_history','uid='.intval($uid),$updateFields);
00146     }
00147 
00148     /**
00149      * perform rollback
00150      *
00151      * @param   array       diff array to rollback
00152      * @return  void
00153      * @access private
00154      */
00155     function performRollback($diff) {
00156         if (!$this->rollbackFields) {
00157             return 0;
00158         }
00159 
00160         $reloadPageFrame=0;
00161         $rollbackData = explode(':',$this->rollbackFields);
00162 
00163             // PROCESS INSERTS AND DELETES
00164             // rewrite inserts and deletes
00165         $cmdmapArray = array();
00166         if ($diff['insertsDeletes'])    {
00167 
00168             switch (count($rollbackData))   {
00169                 case 1: // all tables
00170                     $data = $diff['insertsDeletes'];
00171                     break;
00172                 case 2: // one record
00173                     if ($diff['insertsDeletes'][$this->rollbackFields]) {
00174                         $data[$this->rollbackFields] = $diff['insertsDeletes'][$this->rollbackFields];
00175                     }
00176                     break;
00177                 case 3: // one field in one record -- ignore!
00178                     break;
00179             }
00180             if ($data)  {
00181                 foreach ($data as $key => $action)  {
00182                     $elParts = explode(':',$key);
00183                     if ($action == 1)   {   // inserted records should be deleted
00184                         $cmdmapArray[$elParts[0]][$elParts[1]]['delete'] = 1;
00185                             // when the record is deleted, the contents of the record do not need to be updated
00186                         unset($diff['oldData'][$key]);
00187                         unset($diff['newData'][$key]);
00188                     } elseif ($action == -1) {  // deleted records should be inserted again
00189                         $cmdmapArray[$elParts[0]][$elParts[1]]['undelete'] = 1;
00190                     }
00191                 }
00192             }
00193         }
00194             // Writes the data:
00195         if ($cmdmapArray)   {
00196             $tce = t3lib_div::makeInstance('t3lib_TCEmain');
00197             $tce->stripslashes_values=0;
00198             $tce->debug=0;
00199             $tce->dontProcessTransformations=1;
00200             $tce->start(array(),$cmdmapArray);
00201             $tce->process_cmdmap();
00202             unset($tce);
00203             if (isset($cmdmapArray['pages']))   {
00204                 $reloadPageFrame=1;
00205             }
00206         }
00207 
00208             // PROCESS CHANGES
00209             // create an array for process_datamap
00210         $diff_modified = array();
00211         foreach ($diff['oldData'] as $key => $value)    {
00212             $splitKey = explode(':',$key);
00213             $diff_modified[$splitKey[0]][$splitKey[1]] = $value;
00214         }
00215         switch (count($rollbackData))   {
00216             case 1: // all tables
00217                 $data = $diff_modified;
00218                 break;
00219             case 2: // one record
00220                 $data[$rollbackData[0]][$rollbackData[1]] = $diff_modified[$rollbackData[0]][$rollbackData[1]];
00221                 break;
00222             case 3: // one field in one record
00223                 $data[$rollbackData[0]][$rollbackData[1]][$rollbackData[2]] = $diff_modified[$rollbackData[0]][$rollbackData[1]][$rollbackData[2]];
00224                 break;
00225         }
00226             // Removing fields:
00227         $data = $this->removeFilefields($rollbackData[0],$data);
00228 
00229             // Writes the data:
00230         $tce = t3lib_div::makeInstance('t3lib_TCEmain');
00231         $tce->stripslashes_values=0;
00232         $tce->debug=0;
00233         $tce->dontProcessTransformations=1;
00234         $tce->start($data,array());
00235         $tce->process_datamap();
00236         unset($tce);
00237         if (isset($data['pages']))  {
00238             $reloadPageFrame=1;
00239         }
00240 
00241             // return to normal operation
00242         $this->lastSyslogId = FALSE;
00243         $this->rollbackFields = FALSE;
00244         $this->createChangeLog();
00245 
00246             // reload page frame if necessary
00247         if ($reloadPageFrame)   {
00248             return '<script type="text/javascript">
00249             /*<![CDATA[*/
00250             if (top.content && top.content.nav_frame && top.content.nav_frame.refresh_nav)  {
00251                 top.content.nav_frame.refresh_nav();
00252             }
00253             /*]]>*/
00254             </script>';
00255         }
00256     }
00257 
00258     /*******************************
00259      *
00260      * Display functions
00261      *
00262      *******************************/
00263 
00264     /**
00265      * Displays settings
00266      *
00267      * @return  string      HTML code to modify settings
00268      */
00269     function displaySettings()  {
00270         global $BE_USER, $LANG, $SOBE;
00271             // get current selection from UC, merge data, write it back to UC
00272         $currentSelection = is_array($BE_USER->uc['moduleData']['history']) ? $BE_USER->uc['moduleData']['history'] : array('maxSteps' => '', 'showDiff' => 1, 'showSubElements' => 1, 'showInsertDelete' => 1);
00273 
00274         $currentSelectionOverride = t3lib_div::_GP('settings');
00275         if ($currentSelectionOverride)  {
00276             $currentSelection = array_merge($currentSelection,$currentSelectionOverride);
00277             $BE_USER->uc['moduleData']['history'] = $currentSelection;
00278             $BE_USER->writeUC($BE_USER->uc);
00279         }
00280 
00281             // display selector for number of history entries
00282         $selector['maxSteps'] = array(
00283             10 => 10,
00284             20 => 20,
00285             50 => 50,
00286             100 => 100,
00287             '' => 'maxSteps_all',
00288             'marked' => 'maxSteps_marked'
00289         );
00290         $selector['showDiff'] = array(
00291             0 => 'showDiff_no',
00292             1 => 'showDiff_inline'
00293         );
00294         $selector['showSubElements'] = array(
00295             0 => 'no',
00296             1 => 'yes',
00297         );
00298         $selector['showInsertDelete'] = array(
00299             0 => 'no',
00300             1 => 'yes',
00301         );
00302             // render selectors
00303         $displayCode = '';
00304         foreach ($selector as $key => $values)  {
00305             $displayCode .= '<tr><td>'.$LANG->getLL($key,1).'</td><td><select name="settings['.$key.']" onChange="document.settings.submit()" style="width:100px">';
00306             foreach ($values as $singleKey => $singleVal) {
00307                 $caption = $LANG->getLL($singleVal,1)?$LANG->getLL($singleVal,1):$singleVal;
00308                 $displayCode .= '<option value="'.$singleKey.'" '.(($singleKey ==  $currentSelection[$key])?'selected':'').'> '.$caption.'</option>';
00309             }
00310             $displayCode .= '</select></td></tr>';
00311         }
00312             // set values correctly
00313         if ($currentSelection['maxSteps'] != 'marked')  {
00314             $this->maxSteps = $currentSelection['maxSteps']?intval($currentSelection['maxSteps']):'';
00315         } else {
00316             $this->showMarked = TRUE;
00317             $this->maxSteps = FALSE;
00318         }
00319         $this->showDiff = intval($currentSelection['showDiff']);
00320         $this->showSubElements = intval($currentSelection['showSubElements']);
00321         $this->showInsertDelete = intval($currentSelection['showInsertDelete']);
00322 
00323         $content = '';
00324             // get link to page history if the element history is shown
00325         $elParts = explode(':',$this->element);
00326         if ($elParts[0] != 'pages') {
00327             $content .= '<strong>'.$LANG->getLL('elementHistory',1).'</strong><br />';
00328             $pid = t3lib_BEfunc::getRecordRaw($elParts[0],'uid='.intval($elParts[1]));
00329             $content .= $this->linkPage($LANG->getLL('elementHistory_link',1),array('element' => 'pages:'.$pid['pid']));
00330         }
00331         $content .= '<form name="settings" action="'.t3lib_div::getIndpEnv('TYPO3_REQUEST_URL').'" method="post"><table>'.$displayCode.'</table></form>';
00332         return $SOBE->doc->section($LANG->getLL('settings',1),$content,0,1,0,0);
00333 
00334     }
00335 
00336     /**
00337      * Shows the full change log
00338      *
00339      * @return  string      HTML for list, wrapped in a table.
00340      */
00341     function displayHistory()   {
00342         global $LANG;
00343         global $SOBE;
00344         global $TCA;
00345 
00346         $lines=array();
00347 
00348             // Initialize:
00349         $lines[] = '<tr class="t3-row-header">
00350                 <td> </td>
00351                 <td>'.$LANG->getLL('time',1).'</td>
00352                 <td>'.$LANG->getLL('age',1).'</td>
00353                 <td>'.$LANG->getLL('user',1).'</td>
00354                 <td>'.$LANG->getLL('tableUid',1).'</td>
00355                 <td>'.$LANG->getLL('differences',1).'</td>
00356                 <td>&nbsp;</td>
00357             </tr>';
00358 
00359             // get default page TSconfig expiration time
00360         $elParts = explode(':',$this->element);
00361         if ($elParts[0] != 'pages') {
00362             $tmp = t3lib_BEfunc::getRecordRaw($elParts[0],'uid='.intval($elParts[1]));
00363             $pid = $tmp['pid'];
00364         } else {
00365             $pid = $elParts[1];
00366         }
00367         $tmpTsConfig = $GLOBALS['BE_USER']->getTSConfig('TCEMAIN',t3lib_BEfunc::getPagesTSconfig($pid));
00368         $expirationTime = isset($tmpTsConfig['properties']['default.']['history.']['maxAgeDays']) ? $tmpTsConfig['properties']['default.']['history.']['maxAgeDays'] : 30;
00369 
00370         $expirationTimestamp = $expirationTime ? ($GLOBALS['EXEC_TIME'] - 60 * 60 * 24 * $expirationTime) : 0;
00371         $expirationWarning = 0;
00372 
00373         $be_user_array = t3lib_BEfunc::getUserNames();
00374 
00375             // Traverse changelog array:
00376         if (!$this->changeLog)  {
00377             return 0;
00378         }
00379         $i = 0;
00380         foreach ($this->changeLog as $sysLogUid => $entry)  {
00381                 // stop after maxSteps
00382             if ($i > $this->maxSteps && $this->maxSteps)    {
00383                 break;
00384             }
00385 
00386                 // display inconsistency warning
00387             if ($entry['tstamp'] < $expirationTimestamp && !$expirationWarning) {
00388                 $expirationWarning = 1;
00389 
00390                 $lines[] = '
00391                 <tr class="c-headLine">
00392                     <td colspan="7"><strong>'.$LANG->getLL('consistenceWarning',1).'</strong></td>
00393                 </tr>';
00394             }
00395 
00396                 // show only marked states
00397             if (!$entry['snapshot'] && $this->showMarked)   {
00398                 continue;
00399             }
00400             $i++;
00401                 // get user names
00402             $userName = ($entry['user']?$be_user_array[$entry['user']]['username']:$LANG->getLL('externalChange',1));
00403 
00404                 // build up single line
00405             $singleLine = array();
00406 
00407                 // diff link
00408             $image = t3lib_iconWorks::getSpriteIcon('actions-view-go-forward', array('title' => $LANG->getLL('sumUpChanges', TRUE)));
00409             $singleLine[] = '<span>'.$this->linkPage($image,array('diff' => $sysLogUid)).'</span>'; // remove first link
00410 
00411             $singleLine[] = htmlspecialchars(t3lib_BEfunc::datetime($entry['tstamp'])); // add time
00412             $singleLine[] = htmlspecialchars(t3lib_BEfunc::calcAge($GLOBALS['EXEC_TIME'] - $entry['tstamp'], $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears')));    // add age
00413             $singleLine[] = htmlspecialchars($userName);    // add user name
00414             $singleLine[] = $this->linkPage($this->generateTitle($entry['tablename'],$entry['recuid']),array('element' => $entry['tablename'].':'.$entry['recuid']),'',$LANG->getLL('linkRecordHistory',1));    // add record UID
00415 
00416                 // show insert/delete/diff/changed field names
00417             if ($entry['action'])   {   // insert or delete of element
00418                 $singleLine[] = '<strong>'.htmlspecialchars($LANG->getLL($entry['action'],1)).'</strong>';
00419             } else {
00420                 if (!$this->showDiff)   {   // display field names instead of full diff
00421                     // re-write field names with labels
00422                 $tmpFieldList = explode(',',$entry['fieldlist']);
00423                 foreach ($tmpFieldList as $key => $value)   {
00424                     $tmp = str_replace(':','',$LANG->sl(t3lib_BEfunc::getItemLabel($entry['tablename'],$value),1));
00425                     if($tmp)    $tmpFieldList[$key] = $tmp;
00426                     else    unset($tmpFieldList[$key]); // remove fields if no label available
00427                 }
00428                 $singleLine[] = htmlspecialchars(implode(',',$tmpFieldList));
00429                 } else {    // display diff
00430                     $diff = $this->renderDiff($entry,$entry['tablename']);
00431                     $singleLine[] = $diff;
00432                 }
00433             }
00434                 // show link to mark/unmark state
00435             if (!$entry['action'])  {
00436                 if ($entry['snapshot']) {
00437                     $image = '<img'.t3lib_iconWorks::skinImg('','gfx/unmarkstate.gif').' align="top" alt="'.$LANG->getLL('unmarkState',1).'" title="'.$LANG->getLL('unmarkState',1).'" />';
00438                 } else {
00439                     $image = '<img'.t3lib_iconWorks::skinImg('','gfx/markstate.gif').' align="top" alt="'.$LANG->getLL('markState',1).'" title="'.$LANG->getLL('markState',1).'" />';
00440                 }
00441                 $singleLine[] = $this->linkPage($image,array('highlight' => $entry['uid']));
00442             } else {
00443                 $singleLine[] = '';
00444             }
00445 
00446                 // put line together
00447             $lines[] = '
00448                 <tr class="db_list_normal">
00449                     <td>' . implode('</td><td>', $singleLine) . '</td>
00450                 </tr>';
00451         }
00452 
00453             // Finally, put it all together:
00454         $theCode = '
00455             <!--
00456                 History (list):
00457             -->
00458             <table class="typo3-dblist" border="0" cellpadding="0" cellspacing="0" id="typo3-history">
00459                 ' . implode('', $lines) . '
00460             </table>';
00461 
00462         if ($this->lastSyslogId)    {
00463             $theCode .= '<br />' .  $this->linkPage(t3lib_iconWorks::getSpriteIcon('actions-move-to-bottom', array('title' => $LANG->getLL('fullView', TRUE))), array('diff' => ''));
00464         }
00465 
00466             // Add message about the difference view.
00467         $flashMessage = t3lib_div::makeInstance(
00468             't3lib_FlashMessage',
00469             $GLOBALS['LANG']->getLL('differenceMsg'),
00470             '',
00471             t3lib_FlashMessage::INFO
00472         );
00473 
00474         $theCode .= '<br /><br />' . $flashMessage->render() . '<br />';
00475 
00476             // Add CSH:
00477         $theCode .= t3lib_BEfunc::cshItem('xMOD_csh_corebe', 'history_'.($this->sumUp ? 'sum' : 'log'), $GLOBALS['BACK_PATH'],'');
00478 
00479             // Add the whole content as a module section:
00480         return $SOBE->doc->section($LANG->getLL('changes'),$theCode,0,1);
00481     }
00482 
00483     /**
00484      * Displays a diff over multiple fields including rollback links
00485      *
00486      * @param   array       difference array
00487      * @return  string      HTML output
00488      */
00489     function displayMultipleDiff($diff) {
00490         global $SOBE, $LANG;
00491         $content = '';
00492 
00493             // get all array keys needed
00494         $arrayKeys = array_merge(array_keys($diff['newData']),array_keys($diff['insertsDeletes']),array_keys($diff['oldData']));
00495         $arrayKeys = array_unique($arrayKeys);
00496 
00497         if ($arrayKeys) {
00498             foreach ($arrayKeys as $key)    {
00499                 $record = '';
00500                 $elParts = explode(':',$key);
00501                     // turn around diff because it should be a "rollback preview"
00502                 if ($diff['insertsDeletes'][$key] == 1) {   // insert
00503                     $record .= '<strong>'.$LANG->getLL('delete',1).'</strong>';
00504                     $record .= '<br />';
00505                 } elseif ($diff['insertsDeletes'][$key] == -1)  {
00506                     $record .= '<strong>'.$LANG->getLL('insert',1).'</strong>';
00507                     $record .= '<br />';
00508                 }
00509                     // build up temporary diff array
00510                     // turn around diff because it should be a "rollback preview"
00511                 if ($diff['newData'][$key]) {
00512                     $tmpArr['newRecord'] = $diff['oldData'][$key];
00513                     $tmpArr['oldRecord'] = $diff['newData'][$key];
00514                     $record .= $this->renderDiff($tmpArr, $elParts[0],$elParts[1]);
00515                 }
00516 
00517                 $elParts = explode(':',$key);
00518                 $titleLine = $this->createRollbackLink($key, $LANG->getLL('revertRecord',1),1) . $this->generateTitle($elParts[0],$elParts[1]);
00519                 $record = '<div style="margin-left:10px;padding-left:5px;border-left:1px solid black;border-bottom:1px dotted black;padding-bottom:2px;">'.$record.'</div>';
00520 
00521                 $content .= $SOBE->doc->section($titleLine,$record,0,0,0,1);
00522             }
00523             $content = $this->createRollbackLink('ALL', $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>';
00524         } else {
00525             $content = $LANG->getLL('noDifferences',1);
00526         }
00527         return $SOBE->doc->section($LANG->getLL('mergedDifferences',1),$content,0,1,0,1);
00528     }
00529 
00530     /**
00531      * Renders HTML table-rows with the comparison information of an sys_history entry record
00532      *
00533      * @param   array       sys_history entry record.
00534      * @param   string      The table name
00535      * @param   integer     If set to UID of record, display rollback links
00536      * @return  string      HTML table
00537      * @access private
00538      */
00539     function renderDiff($entry,$table,$rollbackUid=0)   {
00540         global $SOBE, $LANG, $TCA;
00541         $lines=array();
00542         if (is_array($entry['newRecord']))  {
00543 
00544             $t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff');
00545 
00546             $fieldsToDisplay = array_keys($entry['newRecord']);
00547             foreach($fieldsToDisplay as $fN)    {
00548                 t3lib_div::loadTCA($table);
00549                 if (is_array($TCA[$table]['columns'][$fN]) && $TCA[$table]['columns'][$fN]['config']['type']!='passthrough')    {
00550 
00551                         // Create diff-result:
00552                     $diffres = $t3lib_diff_Obj->makeDiffDisplay(
00553                         t3lib_BEfunc::getProcessedValue($table,$fN,$entry['oldRecord'][$fN],0,1),
00554                         t3lib_BEfunc::getProcessedValue($table,$fN,$entry['newRecord'][$fN],0,1)
00555                     );
00556                     $lines[]='
00557                         <tr class="bgColor4">
00558                         '.($rollbackUid?'<td style="width:33px">'.$this->createRollbackLink($table.':'.$rollbackUid.':'.$fN, $LANG->getLL('revertField',1),2).'</td>':'').'
00559                             <td style="width:90px"><em>'.$LANG->sl(t3lib_BEfunc::getItemLabel($table,$fN),1).'</em></td>
00560                             <td style="width:300px">'.nl2br($diffres).'</td>
00561                         </tr>';
00562                 }
00563             }
00564         }
00565         if ($lines) {
00566             $content = '<table border="0" cellpadding="2" cellspacing="2" id="typo3-history-item">
00567                     '.implode('',$lines).'
00568                 </table>';
00569             return $content;
00570         }
00571         return NULL;    // error fallback
00572     }
00573 
00574     /*******************************
00575      *
00576      * build up history
00577      *
00578      *******************************/
00579 
00580     /**
00581      * Creates a diff between the current version of the records and the selected version
00582      *
00583      * @return  array       diff for many elements
00584      */
00585     function createMultipleDiff()   {
00586         $insertsDeletes = array();
00587         $newArr = array();
00588         $differences = array();
00589         if (!$this->changeLog)  {
00590             return 0;
00591         }
00592 
00593             // traverse changelog array
00594         foreach ($this->changeLog as $key => $value)    {
00595             $field = $value['tablename'].':'.$value['recuid'];
00596                 // inserts / deletes
00597             if ($value['action'])   {
00598                 if (!$insertsDeletes[$field])   {
00599                     $insertsDeletes[$field] = 0;
00600                 }
00601                 if ($value['action'] == 'insert')   {
00602                     $insertsDeletes[$field]++;
00603                 } else {
00604                     $insertsDeletes[$field]--;
00605                 }
00606                     // unset not needed fields
00607                 if ($insertsDeletes[$field] == 0)   {
00608                     unset($insertsDeletes[$field]);
00609                 }
00610             } else {
00611                     // update fields
00612                 if (!isset($newArr[$field]))    {   // first row of field
00613                     $newArr[$field] = $value['newRecord'];
00614                     $differences[$field] = $value['oldRecord'];
00615                 } else { // standard
00616                     $differences[$field] = array_merge($differences[$field],$value['oldRecord']);
00617                 }
00618             }
00619         }
00620 
00621             // remove entries where there were no changes effectively
00622         foreach ($newArr as $record => $value)  {
00623             foreach ($value as $key => $innerVal)   {
00624                 if ($newArr[$record][$key] == $differences[$record][$key])  {
00625                     unset($newArr[$record][$key]);
00626                     unset($differences[$record][$key]);
00627                 }
00628             }
00629             if (empty($newArr[$record]) && empty($differences[$record]))    {
00630                 unset($newArr[$record]);
00631                 unset($differences[$record]);
00632             }
00633         }
00634         return array(
00635             'newData' => $newArr,
00636             'oldData' => $differences,
00637             'insertsDeletes' => $insertsDeletes
00638         );
00639     }
00640 
00641     /**
00642      * Creates change log including sub-elements, filling $this->changeLog
00643      *
00644      * @return  [type]      ...
00645      */
00646     function createChangeLog()  {
00647 
00648         global $TCA;
00649         $elParts = explode(':',$this->element);
00650         $changeLog = $this->getHistoryData($elParts[0],$elParts[1]);
00651 
00652             // get history of tables of this page and merge it into changelog
00653         if ($elParts[0] == 'pages' && $this->showSubElements)   {
00654             foreach ($TCA as $tablename => $value)  {
00655                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid',$tablename,'pid='.intval($elParts[1])); // check if there are records on the page
00656                 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))  {
00657                     if ($newChangeLog = $this->getHistoryData($tablename, $row['uid'])) {   // if there is history data available, merge it into changelog
00658                         foreach ($newChangeLog as $key => $value)   {
00659                             $changeLog[$key] = $value;
00660                         }
00661                     }
00662                 }
00663             }
00664         }
00665         if(!$changeLog) {
00666             return 0;
00667         }
00668 
00669         krsort($changeLog);
00670         $this->changeLog = $changeLog;
00671 
00672         return 1;
00673     }
00674 
00675     /**
00676      * Gets history and delete/insert data from sys_log and sys_history
00677      *
00678      * @param   string      DB table name
00679      * @param   integer     UID of record
00680      * @return  array       history data of the record
00681      */
00682     function getHistoryData($table,$uid)    {
00683         global $TCA;
00684         $uid = $this->resolveElement($table,$uid);
00685             // If table is found in $TCA:
00686         if ($TCA[$table])   {
00687                 // Selecting the $this->maxSteps most recent states:
00688             $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
00689                         'sys_history.*,sys_log.userid',
00690                         'sys_history,sys_log',
00691                         'sys_history.sys_log_uid=sys_log.uid
00692                             AND sys_history.tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history').'
00693                             AND sys_history.recuid='.intval($uid),
00694                         '',
00695                         'sys_log.uid DESC',
00696                         $this->maxSteps
00697                     );
00698 
00699                 // Traversing the result, building up changesArray / changeLog:
00700             #$changesArray=array(); // used temporarily to track intermedia changes
00701             $changeLog=array();
00702             while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))  {
00703                     // only history until a certain syslog ID needed
00704                 if ($row['sys_log_uid'] < $this->lastSyslogId && $this->lastSyslogId)   {
00705                     continue;
00706                 }
00707                 $hisDat = unserialize($row['history_data']);
00708                 if (is_array($hisDat['newRecord']) && is_array($hisDat['oldRecord']))   {
00709 
00710                         // Add hisDat to the changeLog
00711                     $hisDat['uid']=$row['uid'];
00712                     $hisDat['tstamp']=$row['tstamp'];
00713                     $hisDat['user']=$row['userid'];
00714                     $hisDat['snapshot']=$row['snapshot'];
00715                     $hisDat['fieldlist']=$row['fieldlist'];
00716                     $hisDat['tablename']=$row['tablename'];
00717                     $hisDat['recuid']=$row['recuid'];
00718 
00719                     $changeLog[$row['sys_log_uid']]=$hisDat;
00720 
00721                         // Update change array
00722                         // This is used to detect if any intermedia changes have been made.
00723                     #$changesArray = array_merge($changesArray,$hisDat['oldRecord']);
00724                 } else {
00725                     debug('ERROR: [getHistoryData]');
00726                     return 0;   // error fallback
00727                 }
00728             }
00729                 // SELECT INSERTS/DELETES
00730             if ($this->showInsertDelete)    {
00731                     // Select most recent inserts and deletes // WITHOUT snapshots
00732                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
00733                         'uid,userid,action,tstamp',
00734                         'sys_log',
00735                         'type=1
00736                             AND ( action=1 OR action=3 )
00737                             AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_log').'
00738                             AND recuid='.intval($uid),
00739                         '',
00740                         'uid DESC',
00741                         $this->maxSteps
00742                     );
00743                 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))  {
00744 
00745                     if ($row['uid'] < $this->lastSyslogId && $this->lastSyslogId)   {
00746                         continue;
00747                     }
00748                     $hisDat = array();
00749 
00750                     switch ($row['action']) {
00751                         case 1: // Insert
00752                             $hisDat['action'] = 'insert';
00753                             break;
00754                         case 3: // Delete
00755                             $hisDat['action'] = 'delete';
00756                             break;
00757                     }
00758                     $hisDat['tstamp']=$row['tstamp'];
00759                     $hisDat['user']=$row['userid'];
00760                     $hisDat['tablename'] = $table;
00761                     $hisDat['recuid'] = $uid;
00762                     $changeLog[$row['uid']] = $hisDat;
00763                 }
00764             }
00765             return $changeLog;
00766         }
00767         return 0;   // error fallback
00768     }
00769 
00770 
00771     /*******************************
00772      *
00773      * Various helper functions
00774      *
00775      *******************************/
00776 
00777     /**
00778      * generates the title and puts the record title behind
00779      *
00780      * @param   [type]      $table: ...
00781      * @param   [type]      $uid: ...
00782      * @return  [type]      ...
00783      */
00784     function generateTitle($table, $uid)    {
00785         global $TCA;
00786 
00787         $out = $table.':'.$uid;
00788         if ($labelField = $TCA[$table]['ctrl']['label'])    {
00789             $record = t3lib_BEfunc::getRecordRaw($table, 'uid='.intval($uid));
00790             $out .= ' ('.t3lib_BEfunc::getRecordTitle($table, $record, TRUE).')';
00791         }
00792         return $out;
00793     }
00794     /**
00795      * creates a link for the rollback
00796      *
00797      * @param   sting       parameter which is set to rollbackFields
00798      * @param   string      optional, alternative label and title tag of image
00799      * @param   integer     optional, type of rollback: 0 - ALL; 1 - element; 2 - field
00800      * @return  string      HTML output
00801      */
00802     function createRollbackLink($key, $alt='', $type=0) {
00803          global $LANG;
00804 
00805          return $this->linkPage('<img '.t3lib_iconWorks::skinImg('','gfx/revert_'.$type.'.gif','width="33" height="33"').' alt="'.$alt.'" title="'.$alt.'" align="middle" />',array('rollbackFields'=>$key));
00806      }
00807 
00808     /**
00809      * Creates a link to the same page.
00810      *
00811      * @param   string      String to wrap in <a> tags (must be htmlspecialchars()'ed prior to calling function)
00812      * @param   array       Array of key/value pairs to override the default values with.
00813      * @param   string      Possible anchor value.
00814      * @param   string      Possible title.
00815      * @return  string      Link.
00816      * @access private
00817      */
00818     function linkPage($str,$inparams=array(),$anchor='',$title='')  {
00819 
00820             // Setting default values based on GET parameters:
00821         $params['element']=$this->element;
00822         $params['returnUrl']=$this->returnUrl;
00823         $params['diff']=$this->lastSyslogId;
00824             // Mergin overriding values:
00825         $params = array_merge($params,$inparams);
00826 
00827             // Make the link:
00828         $Ahref = 'show_rechis.php?'.t3lib_div::implodeArrayForUrl('',$params).($anchor?'#'.$anchor:'');
00829         $link = '<a href="'.htmlspecialchars($Ahref).'"'.($title?' title="'.$title.'"':'').'>'.$str.'</a>';
00830 
00831             // Return link:
00832         return $link;
00833     }
00834 
00835     /**
00836      * Will traverse the field names in $dataArray and look in $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")
00837      *
00838      * @param   string      Table name
00839      * @param   array       The data array
00840      * @return  array       The modified data array
00841      * @access private
00842      */
00843     function removeFilefields($table,$dataArray)    {
00844         global $TCA;
00845 
00846         if ($TCA[$table])   {
00847             t3lib_div::loadTCA($table);
00848 
00849             foreach($TCA[$table]['columns'] as $field => $config)   {
00850                 if ($config['config']['type']=='group' && $config['config']['internal_type']=='file')   {
00851                     unset($dataArray[$field]);
00852                 }
00853             }
00854         }
00855         return $dataArray;
00856     }
00857 
00858     /**
00859      * Convert input element reference to workspace version if any.
00860      *
00861      * @param   string      table of input element
00862      * @param   integer     UID of record
00863      * @return  integer     converted UID of record
00864      */
00865     function resolveElement($table,$uid)    {
00866         if (isset($GLOBALS['TCA'][$table])) {
00867             if ($workspaceVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $table, $uid, 'uid')) {
00868                 $uid = $workspaceVersion['uid'];
00869             }
00870         }
00871         return $uid;
00872     }
00873 
00874     /**
00875      * resolve sh_uid (used from log)
00876      *
00877      * @return  [type]      ...
00878      */
00879     function resolveShUid() {
00880         if (t3lib_div::_GP('sh_uid'))   {
00881             $sh_uid = t3lib_div::_GP('sh_uid');
00882             $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*','sys_history', 'uid='.intval($sh_uid));
00883             $record = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
00884             $this->element = $record['tablename'].':'.$record['recuid'];
00885             $this->lastSyslogId = $record['sys_log_uid']-1;
00886         }
00887     }
00888 }
00889 ?>