First CVS update in a long time - see the top of changelog. In particular the foundat...
[Packages/TYPO3.CMS.git] / typo3 / class.show_rechis.inc
1 <?php
2 /***************************************************************
3 *  Copyright notice
4 *  
5 *  (c) 1999-2004 Kasper Skaarhoj (kasper@typo3.com)
6 *  All rights reserved
7 *
8 *  This script is part of the TYPO3 project. The TYPO3 project is 
9 *  free software; you can redistribute it and/or modify
10 *  it under the terms of the GNU General Public License as published by
11 *  the Free Software Foundation; either version 2 of the License, or
12 *  (at your option) any later version.
13
14 *  The GNU General Public License can be found at
15 *  http://www.gnu.org/copyleft/gpl.html.
16 *  A copy is found in the textfile GPL.txt and important notices to the license 
17 *  from the author is found in LICENSE.txt distributed with these scripts.
18 *
19
20 *  This script is distributed in the hope that it will be useful,
21 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 *  GNU General Public License for more details.
24 *
25 *  This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /** 
28  * Class for the record history display script (show_rechis.php)
29  *
30  * $Id$
31  * Revised for TYPO3 3.6 November/2003 by Kasper Skaarhoj
32  * XHTML Compliant  
33  *
34  * @author      Kasper Skaarhoj <kasper@typo3.com>
35  */
36 /**
37  * [CLASS/FUNCTION INDEX of SCRIPT]
38  *
39  *
40  *
41  *   83: class recordHistory 
42  *  106:     function recordHistory()   
43  *  124:     function main()    
44  *  155:     function displaySysHistoryEntry($sh_uid)   
45  *  215:     function revertToPreviousValues($element,$field)   
46  *  281:     function saveState($element,$sumUp)        
47  *  335:     function displayHistory($element)  
48  *
49  *              SECTION: Various helper functions
50  *  533:     function nextHisUid($element,$hisUid)      
51  *  579:     function compareChangesWithCurrent($element,$changeRec)    
52  *  624:     function readFieldTypes($table,$id)        
53  *  653:     function cmp($changeStatus,$oldRecord)     
54  *  676:     function removeFilefields($table,$dataArray)       
55  *  699:     function renderEntry($entry,$table)        
56  *  757:     function listHeader()      
57  *  801:     function linkPage($str,$inparams=array(),$anchor='')       
58  *  829:     function getChangesSinceRecord($element,$hisUid=0,$hisUid_Stop=0)  
59  *
60  * TOTAL FUNCTIONS: 15
61  * (This index is automatically created/updated by the extension "extdeveval")
62  *
63  */
64
65
66
67
68
69
70
71
72
73
74
75
76 /**
77  * Class for the record history display script (show_rechis.php)
78  *
79  * @author      Kasper Skaarhoj <kasper@typo3.com>
80  * @package TYPO3
81  * @subpackage core
82  */
83 class recordHistory {
84                 
85                 // External, static:
86         var $maxSteps=20;               // Maximum number of sys_history steps to show.
87         
88                 // Internal, dynamic:
89         var $listType = 0;              // This value determines the kind of list build. The variable is used as a parameter from some functions.
90         
91                 // Internal, static. GPvars:
92         var $sh_uid;                    //      sh_uid is the id-number of the sys_history log item to SHOW
93         var $element;                   // Element reference, syntax [tablename]:[uid]
94         var $saveState;                 // Saving states: Points to a sys_history UID which should be saved.
95         var $returnUrl;                 // Return URL - kept in links, used to link back to calling module.
96         var $revert;                    // String identifying mode of reverting: Either all fields or only a single field to revert. See function revertToPreviousValues()
97         var $sumUp;                             // Generally used as a pointer to a sys_history uid as a state.
98         var $doReturn;                  // If set, function revertToPreviousValues() will perform a redirect to returnUrl
99
100
101         /**
102          * Constructor for the class
103          *
104          * @return      void
105          */
106         function recordHistory()        {
107                 
108                         // GPvars:
109                 $this->sh_uid = t3lib_div::_GP('sh_uid');       
110                 $this->element = t3lib_div::_GP('element');
111                 $this->saveState = t3lib_div::_GP('saveState');
112                 $this->returnUrl = t3lib_div::_GP('returnUrl');
113                 $this->revert = t3lib_div::_GP('revert');
114                 $this->sumUp = t3lib_div::_GP('sumUp');
115                 $this->doReturn = t3lib_div::_GP('doReturn');
116         }
117
118         /**
119          * Main function for the listing of history.
120          * It detects incoming variables like element reference, history element uid etc. and renders the correct screen.
121          *
122          * @return      void
123          */
124         function main() {
125
126                 $content='';
127
128                         // If link from sys log:
129                         // sh_uid is the id-number of the sys_history log item
130                 if ($this->sh_uid)      {
131                         $content.=$this->displaySysHistoryEntry($this->sh_uid);
132                 }
133                 
134                         // If link to element:
135                 if ($this->element)     {
136                         if ($this->revert && $this->sumUp)      {
137                                 $content.=$this->revertToPreviousValues($this->element,$this->revert);
138                         }
139                         if ($this->saveState)   {
140                                 $content.=$this->saveState($this->element,$this->saveState);
141                         }
142                         $content.=$this->displayHistory($this->element);
143                 }       
144                 
145                         // Return content variable:
146                 return $content;
147         }
148         
149         /**
150          * Displays a specific entry from the sys_history table
151          *
152          * @param       integer         UID of sys_history table entry
153          * @return      string          HTML content
154          */
155         function displaySysHistoryEntry($sh_uid)        {
156                 global $SOBE, $LANG, $TCA;
157
158                         // Select the entry from the table:
159                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'sys_history', 'uid='.intval($sh_uid));
160                 $newRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
161
162                         // If an entry was found:               
163                 if (is_array($newRow))  {
164                 
165                                 // Init:
166                         $this->listType=0;
167                         $lines=array();
168
169                                 // Create header:
170                         $recIdentString = $LANG->sL($TCA[$newRow['tablename']]['ctrl']['title']).'/'.$newRow['recuid'];
171                         $recIdentString = $this->linkPage(htmlspecialchars($recIdentString),array('sh_uid'=>'','element'=>$newRow['tablename'].':'.$newRow['recuid']),'uid_'.$sh_uid);
172                         $theTime = t3lib_BEfunc::datetime($newRow['tstamp']).', '.t3lib_BEfunc::calcAge(time()-$newRow['tstamp'],$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears'));
173                         $lines[]='
174                                 <tr class="bgColor5">
175                                         <td colspan="4">
176                                                 <strong>'.$LANG->getLL('tableUid',1).':</strong> '.$recIdentString.'<br />
177                                                 <strong>'.$LANG->getLL('time',1).':</strong> '.htmlspecialchars($theTime).'<br />
178                                         </td>
179                                 </tr>';
180
181                                 // Add header to accumulation:
182                         $lines[]=$this->listHeader();
183
184                                 // Get the entry data and add it:
185                         $historyData = unserialize($newRow['history_data']);
186                         $lines = array_merge($lines,$this->renderEntry($historyData,$newRow['tablename']));
187                         
188                                 // Combine all content into a table for layout:
189                         $theCode='
190                         
191                                 <!-- 
192                                         History for item:
193                                 -->
194                                 <table border="0" cellpadding="2" cellspacing="2" id="typo3-history-item">
195                                         '.implode('',$lines).'
196                                 </table>';
197                         $theCode.='
198                                 <br /><img'.t3lib_iconWorks::skinImg('','gfx/icon_note.gif','width="18" height="16"').' align="top" alt="" />'.$LANG->getLL('differenceMsg').'<br /><br />';
199                         
200                                 // Create the module section:
201                         $content.=$SOBE->doc->section($LANG->getLL('changes'),$theCode,0,1);
202                 }
203
204                         // Return content:
205                 return $content;
206         }
207
208         /**
209          * Return to previous values for element
210          *
211          * @param       string          Element reference, syntax "[table]:[uid]"
212          * @param       string          Tells which field to restore. A single field (eg named "myField") is defined as "field:myField" while ALL fields is indicated by the string "ALL_FIELDS"
213          * @return      void            The function writes through tceMain and ends with a header-location, if instructed to.
214          */
215         function revertToPreviousValues($element,$field)        {
216                 $sumUp = $this->sumUp;  // sys_history uid from which to get previous values
217
218                 $elParts = explode(':',$element);
219                 $redirect = intval($this->doReturn);
220                 if ($sumUp==-1) {       // Undo/Redo
221                         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
222                                                 'uid', 
223                                                 'sys_history', 
224                                                 'sys_history.tablename="'.$GLOBALS['TYPO3_DB']->quoteStr($elParts[0], 'sys_history').'" 
225                                                         AND sys_history.recuid='.intval($elParts[1]),
226                                                 '',
227                                                 'uid DESC',
228                                                 '1'
229                                         );
230                         if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
231                                 $sumUp=$row['uid'];
232                         }
233                         $redirect = 1;
234                 }
235                 if ($sumUp!=-1) {
236                         $changeRec=$this->compareChangesWithCurrent($element,$this->getChangesSinceRecord($element,$sumUp));
237
238                         $data =array();
239                         if (t3lib_BEfunc::getRecord($elParts[0],$elParts[1]))   {
240
241                                         // Fields field(s) to restore:
242                                 if ($field=='ALL_FIELDS')       {
243                                         $data=$changeRec['oldRecord'];
244                                 } elseif(substr($field,0,6)=='field:') {
245                                         $data[substr($field,6)]=$changeRec['oldRecord'][substr($field,6)];
246                                 }
247                                         // Removing fields:
248                                 $data = $this->removeFilefields($elParts[0],$data);
249                                 
250                                         // If there are fields to write:
251                                 if (count($data))       {
252
253                                                 // Setting data right:
254                                         $inData=array();
255                                         $inData[$elParts[0]][$elParts[1]]=$data;
256                                         
257                                                 // Writes the data:
258                                         $tce = t3lib_div::makeInstance('t3lib_TCEmain');
259                                         $tce->stripslashes_values=0;
260                                         $tce->debug=0;
261                                         $tce->dontProcessTransformations=1;
262                                         $tce->start($inData,array());
263                                         $tce->process_datamap();
264                                 }
265                         }
266                 }
267
268                 if ($redirect)  {
269                         Header ('Location: '.t3lib_div::locationHeaderUrl($this->returnUrl));
270                         exit;
271                 }
272         }
273
274         /**
275          * Will save state uid $sumUp of element
276          *
277          * @param       string          Element reference, syntax "[table]:[uid]"
278          * @param       integer         sys_history uid from which to get previous values
279          * @return      void
280          */
281         function saveState($element,$sumUp)     {
282                 $elParts = explode(':',$element);
283
284                         // Find the changes since $sumUp sys_history uid                        
285                 $changeRec = $this->getChangesSinceRecord($element,$sumUp);
286
287                         // Select most recent sys_history record for the element:
288                 $lastestData = array();
289                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
290                                         'history_data', 
291                                         'sys_history', 
292                                         'sys_history.tablename="'.$GLOBALS['TYPO3_DB']->quoteStr($elParts[0], 'sys_history').'" 
293                                                 AND sys_history.recuid='.intval($elParts[1]),
294                                         '',
295                                         'uid DESC',
296                                         '1'
297                                 );
298                 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
299                         $lastestData = unserialize($row['history_data']);
300                 }
301
302                         // Create forged history data from the most recent state and the previous state to save:
303                 $historyRecords=array();
304                 $historyRecords['oldRecord'] = $changeRec['changes'];
305                 $historyRecords['newRecord'] = array();
306
307                 reset($historyRecords['oldRecord']);
308                 while(list($kk)=each($historyRecords['oldRecord']))     {
309                         $historyRecords['newRecord'][$kk]=$lastestData['newRecord'][$kk];
310                 }
311
312                         // Update log:
313                 $updateID = $GLOBALS['BE_USER']->writelog(3,1,0,1,'Saved state','');
314                 
315                         // Create query for inserting into sys_history table:
316                 $fields_values = array(
317                         'history_data' => serialize($historyRecords),
318                         'fieldlist' => implode(',',array_keys($historyRecords['oldRecord'])),
319                         'tstamp' => time(),
320                         'tablename' => $elParts[0],
321                         'recuid' => $elParts[1],
322                         'sys_log_uid' => $updateID,
323                         'snapshot' => 1
324                 );
325                         // Save state by executing this query:
326                 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_history', $fields_values);
327         }
328
329         /**
330          * Displays the history states of an element
331          *
332          * @param       string          Element reference, syntax "[table]:[uid]"
333          * @return      string          HTML for list, wrapped in a table.
334          */
335         function displayHistory($element)       {
336                 global $SOBE, $LANG, $TCA;
337                 
338                         // Initialize:
339                 $elParts = explode(':',$element);
340                 $table = $elParts[0];
341                 
342                         // If table is found in $TCA:
343                 if ($TCA[$table])       {
344                 
345                                 // Counting number of states:
346                         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
347                                                 'count(*)', 
348                                                 'sys_history,sys_log', 
349                                                 'sys_history.sys_log_uid=sys_log.uid
350                                                         AND sys_history.tablename="'.$GLOBALS['TYPO3_DB']->quoteStr($table, 'sys_history').'" 
351                                                         AND sys_history.recuid='.intval($elParts[1])
352                                         );
353                         list($Rcount) = $GLOBALS['TYPO3_DB']->sql_fetch_row($res);
354
355                                 // Selecting the $this->maxSteps most recent states:
356                         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
357                                                 'sys_history.*,sys_log.userid', 
358                                                 'sys_history,sys_log', 
359                                                 'sys_history.sys_log_uid=sys_log.uid
360                                                         AND sys_history.tablename="'.$GLOBALS['TYPO3_DB']->quoteStr($table, 'sys_history').'" 
361                                                         AND sys_history.recuid='.intval($elParts[1]),
362                                                 '',
363                                                 'uid',
364                                                 t3lib_div::intInRange($Rcount-$this->maxSteps,0).','.$this->maxSteps
365                                         );
366
367                                 // Traversing the result, building up changesArray / changeLog:
368                         $changesArray=array();
369                         $changeLog=array();
370                         while ($newRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))   {
371                                 $hisDat = unserialize($newRow['history_data']);
372                                 if (is_array($hisDat['newRecord']) && is_array($hisDat['oldRecord']))   {
373                                                 // If intermedia changes:
374                                         $intermediaChanges = $this->cmp($changesArray,$hisDat['oldRecord']);
375                                         if (count($intermediaChanges) && !$newRow['snapshot'])  {
376                                                 $changeLog[]=$intermediaChanges;
377                                         }
378
379                                                 // Add hisDat to the changeLog
380                                         $hisDat['uid']=$newRow['uid'];
381                                         $hisDat['tstamp']=$newRow['tstamp'];
382                                         $hisDat['user']=$newRow['userid'];
383                                         $hisDat['snapshot']=$newRow['snapshot'];
384                                         $changeLog[]=$hisDat;
385
386                                                 // Update change array
387                                                 // This is used to detect if any intermedia changes has been made.
388                                         $changesArray = array_merge($changesArray,$hisDat['newRecord']);
389                                 } else {
390                                         debug('ERROR: [displayHistory]');
391                                 }
392                         }
393                 
394                                 
395                         $lines=array();
396                         $darkerBgColor_interM = '#cccccc';
397                         if ($this->sumUp)       {       // Show details for a single point in the list:
398
399                                         // Initialize:
400                                 $changeLog=array();             // array is reset here because we want to show only one item (and therefore we will build it all over again...)
401                                 $changeLog[]=$this->compareChangesWithCurrent($element,$this->getChangesSinceRecord($element,$this->sumUp));
402                                 $this->listType=2;
403                                 $lines[]=$this->listHeader();
404                                 $be_users = t3lib_BEfunc::getUserNames();
405
406                                         // Get the previous/next uids:
407                                 list($prevHisUid,$nextHisUid) = $this->nextHisUid($element,$this->sumUp);
408                                 
409                                         // Create the set of navigation links:
410                                 $linkPack = 
411                                         ($prevHisUid ? $this->linkPage('<img'.t3lib_iconWorks::skinImg('','gfx/pilup.gif','width="14" height="14"').' title="'.$LANG->getLL('prev',1).'" alt="" />', array('sumUp'=>$prevHisUid)) : '').        // previous
412                                         ($nextHisUid ? $this->linkPage('<img'.t3lib_iconWorks::skinImg('','gfx/pildown.gif','width="14" height="14"').' title="'.$LANG->getLL('next',1).'" alt="" />', array('sumUp'=>$nextHisUid)) : '').      // next
413                                         '<br />'.$this->linkPage('<img'.t3lib_iconWorks::skinImg('','gfx/history2.gif','width="13" height="12"').' title="'.$LANG->getLL('historyList',1).'" alt="" />', array('sumUp'=>''), 'uid_'.$this->sumUp).      // back to list
414                                         $this->linkPage('<img'.t3lib_iconWorks::skinImg('','gfx/savesnapshot.gif','width="17" height="12"').' title="'.$LANG->getLL('saveState',1).'" alt="" />', array('saveState'=>$this->sumUp,'sumUp'=>''), 'latest');      // save state
415
416                                         // Traverse changelog array:
417                                 foreach($changeLog as $entry)   {
418                                         
419                                                 // Set user-names:
420                                         if (!is_array($entry['userList']))      $entry['userList']=array();
421                                         foreach($entry['userList'] as $uLk => $uV)      {
422                                                 $entry['userList'][$uLk]=$be_users[$uV]['username'];
423                                         }
424                                         
425                                                 // Add the header:
426                                         $theTime = t3lib_BEfunc::datetime($entry['tstamp']).', '.t3lib_BEfunc::calcAge(time()-$entry['tstamp'],$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears'));
427                                         $lines[]='
428                                                 <tr class="bgColor4-20">
429                                                         <td valign="top">'.$linkPack.'</td>
430                                                         <td colspan="4"><b>'.$LANG->getLL('time',1).':</b> '.htmlspecialchars($theTime).' &nbsp; - &nbsp; <b>'.$LANG->getLL('changeCount',1).':</b> '.$entry['counter'].'<br />
431                                                                 <b>'.$LANG->getLL('users',1).':</b> '.implode(', ',$entry['userList']).' 
432                                                         </td>
433                                                 </tr>';
434                                         
435                                                 // Add content:
436                                         if (isset($entry['oldRecord']) && isset($entry['newRecord']))   {       // If there ARE differences to show, then add lines for each changed field:
437                                                 $lines = array_merge($lines,$this->renderEntry($entry,$table));
438                                         } else {        // Otherwise, if no changes - show a message about that!
439                                                 $lines[]='
440                                                         <tr class="bgColor4">
441                                                                 <td colspan="5" align="center"><br /><b>'.$LANG->getLL('similar',1).'</b><br /><br /></td>
442                                                         </tr>';
443                                         }
444                                 }
445                         } else {        // Show the full change Log:
446
447                                         // Initialize:
448                                 $this->listType=1;
449                                 $be_users = t3lib_BEfunc::getUserNames();
450                                 $lines[]=$this->listHeader();
451
452                                         // Traverse changelog array:
453                                 foreach($changeLog as $c => $entry)     {
454                                 
455                                                 // Add spacer line:
456                                         $lines[]='
457                                                 <tr>
458                                                         <td colspan="3">&nbsp;</td>
459                                                 </tr>';
460
461                                                 // Anchor to latest entry:
462                                         $lastAnchor = ($c+1==count($changeLog)?'<a name="latest"></a>':'');
463                                                 
464                                                 // Render state header:
465                                         if ($entry['uid'])      {       // This state was made by the backend:
466                                                 $theTime = $this->linkPage(t3lib_BEfunc::datetime($entry['tstamp']),array('sh_uid'=>$entry['uid'],'element'=>''));
467                                                 $theAge = ', '.t3lib_BEfunc::calcAge(time()-$entry['tstamp'],$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears'));
468                                                 $bgColorClass = $entry['snapshot'] ? 'bgColor2' : 'bgColor4-20';
469                                                 $lines[]='
470                                                         <tr class="'.$bgColorClass.'">
471                                                                 <td colspan="2">'.
472                                                                         $lastAnchor.
473                                                                         '<a name="uid_'.$entry['uid'].'"></a>'.
474                                                                         ($entry['snapshot'] ? '<img'.t3lib_iconWorks::skinImg('','gfx/snapshot.gif','width="12" height="12"').' alt="" />':'').
475                                                                         '<b>'.$LANG->getLL('time',1).':</b> '.$theTime.htmlspecialchars($theAge).' &nbsp; - &nbsp; <b>'.$LANG->getLL('user',1).':</b> '.$be_users[$entry['user']]['username'].
476                                                                 '</td>
477                                                                 <td>'.
478                                                                         $this->linkPage('<img'.t3lib_iconWorks::skinImg('','gfx/history.gif','width="13" height="12"').' title="'.$LANG->getLL('revertAllFields',1).'" alt="" />', array('revert'=>'ALL_FIELDS','sumUp'=>$entry['uid'],'doReturn'=>1)).
479                                                                         $this->linkPage('<img'.t3lib_iconWorks::skinImg('','gfx/history_details.gif','width="12" height="12"').' title="'.$LANG->getLL('sumUpChanges',1).'" alt="" />', array('sumUp'=>$entry['uid'])).
480                                                                 '</td>
481                                                         </tr>';
482                                         } else {        // This state must have been some external change:
483                                                 $lines[]='
484                                                         <tr bgcolor="'.$darkerBgColor_interM.'">
485                                                                 <td colspan="3"><strong>'.$LANG->getLL('externalChange',1).'</strong></td>
486                                                         </tr>';
487                                         }
488                                                 // Merge state header with all entries in the state:
489                                         $lines = array_merge($lines,$this->renderEntry($entry,$table));
490                                 }
491                         }
492                         
493                                 // Finally, put it all together:
494                         $theCode='
495                         
496                                 <!-- 
497                                         Item history (either list or single):
498                                 -->
499                                 <table border="0" cellpadding="2" cellspacing="2" id="typo3-history">
500                                         '.implode('',$lines).'
501                                 </table>';
502                         
503                                 // Add message about the difference view.
504                         $theCode.='<br /><img'.t3lib_iconWorks::skinImg('','gfx/icon_note.gif','width="18" height="16"').' align="top" alt="" />'.$LANG->getLL('differenceMsg').'<br /><br />';
505                         
506                                 // Add the whole content as a module section:
507                         return $SOBE->doc->section($LANG->getLL('changes'),$theCode,0,1);
508                 }
509         }       
510         
511         
512
513
514
515
516
517
518
519         /*******************************
520          *
521          * Various helper functions
522          *
523          *******************************/
524
525         /**
526          * Based on the uid of a sys_history record (a state) this method will find the uids of the previous and next state (if any)
527          *
528          * @param       string          Element reference, syntax "[table]:[uid]"
529          * @param       integer         Current state uid
530          * @return      array           Array with previous and next uid as key 0 / 1
531          * @access private
532          */
533         function nextHisUid($element,$hisUid)   {
534                 $elParts = explode(':',$element);
535
536                         // Prev:
537                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
538                                         'uid', 
539                                         'sys_history', 
540                                         'tablename="'.$GLOBALS['TYPO3_DB']->quoteStr($elParts[0], 'sys_history').'" 
541                                                 AND recuid='.intval($elParts[1]).' 
542                                                 AND uid<'.intval($hisUid),
543                                         '',
544                                         'uid DESC',
545                                         '1'
546                                 );
547                 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
548                         $prevUid = $row['uid'];
549                 }
550
551                         // Next:
552                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
553                                         'uid', 
554                                         'sys_history', 
555                                         'tablename="'.$GLOBALS['TYPO3_DB']->quoteStr($elParts[0], 'sys_history').'"
556                                                 AND recuid='.intval($elParts[1]).' 
557                                                 AND uid>'.intval($hisUid),
558                                         '',
559                                         'uid',
560                                         '1'
561                                 );
562                 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
563                         $nextUid = $row['uid'];
564                 }
565
566                         // Return next and previous ids:
567                 return array($prevUid,$nextUid);
568         }
569
570         /**
571          * This compares a certain sys_history state (given by the $changeRec array) with the current values of the element refered to by $element.
572          *
573          * @param       string          Element reference, syntax "[table]:[uid]"
574          * @param       array           Array with the state information from a certain state. This kind of array is produced by getChangesSinceRecord()
575          * @return      array           Array with the changes registered in.
576          * @access private
577          * @see getChangesSinceRecord()
578          */
579         function compareChangesWithCurrent($element,$changeRec) {
580                 global $TCA;
581
582                         // Initialize:
583                 $sumChangesArray=array();
584                 $elParts = explode(':',$element);
585                 $newChangeRec=array();
586                 
587                         // If tablename is found in $TCA:
588                 if ($TCA[$elParts[0]])  {
589                 
590                                 // Select current record content of element:
591                         $currentRecord = t3lib_BEfunc::getRecord($elParts[0],$elParts[1]);
592
593                                 // If that is found and the "changes" entry of the $changeRec is an array, then proceed:
594                         if (is_array($currentRecord) && is_array($changeRec['changes']))        {
595                                         
596                                         // For each entry in "changes" we compare the field content with the current and if there is a difference, it is tracked in the array $newChangeRec
597                                 foreach($changeRec['changes'] as $fN => $fV)    {
598                                         if (strcmp($fV,$currentRecord[$fN]))    {
599                                                 $newChangeRec['oldRecord'][$fN]=$fV;
600                                                 $newChangeRec['newRecord'][$fN]=$currentRecord[$fN];
601                                         }
602                                 }
603                                         // Finally, setting some general information fields:
604                                 $newChangeRec['tstamp']=min($changeRec['tstamp']);
605                                 $newChangeRec['counter']=$changeRec['counter'];
606                                 $newChangeRec['userList']=array_unique($changeRec['userList']);
607                         } else {
608                                 return false;   // No arrays, possibly no record
609                         }
610                 }       
611                 
612                         // Returns the array of changes detected:
613                 return $newChangeRec;
614         }
615
616         /**
617          * Returns the record of $table/$id along with the sql field types for each field
618          *
619          * @param       string          The table name
620          * @param       integer         The uid of the record
621          * @return      array           An array with two num keys; in 0 is the current record, in 1 is the field types for each field.
622          * @access private
623          */
624         function readFieldTypes($table,$id)     {
625                         
626                         // Select record:
627                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid='.intval($id));
628
629                         // Fetch the types of the fields.
630                 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res))   {
631                         $currentRecord = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
632                         $c=0;
633                         $cRecTypes=array();
634                         foreach($currentRecord as $col => $val) {
635                                 $cRecTypes[$col] = $GLOBALS['TYPO3_DB']->sql_field_type($res,$c);
636                                 $c++;
637                         }
638                 }
639                 
640                 $GLOBALS['TYPO3_DB']->sql_free_result($res);
641
642                 return array($currentRecord,$cRecTypes);
643         }
644
645         /**
646          * Compares the old record with the changed fields.
647          *
648          * @param       array           Record with field/value pairs (what has changed)
649          * @param       array           Record with field/value pairs
650          * @return      array           Comparison result.
651          * @access private
652          */
653         function cmp($changeStatus,$oldRecord)  {
654         
655                         // Initialize:
656                 $changes=array();
657                 
658                         // Traverse $oldRecord
659                 foreach($oldRecord as $fN => $fV)       {
660                         if (isset($changeStatus[$fN]) && strcmp($fV,$changeStatus[$fN]))        {
661                                 $changes['oldRecord'][$fN]=$changeStatus[$fN];
662                                 $changes['newRecord'][$fN]=$fV;
663                         }
664                 }
665                 return $changes;
666         }
667
668         /**
669          * 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")
670          *
671          * @param       string          Table name
672          * @param       array           The data array
673          * @return      array           The modified data array
674          * @access private
675          */
676         function removeFilefields($table,$dataArray)    {
677                 global $TCA;
678                 
679                 if ($TCA[$table])       {
680                         t3lib_div::loadTCA($table);
681                         
682                         foreach($TCA[$table]['columns'] as $field => $config)   {
683                                 if ($config['config']['type']=='group' && $config['config']['internal_type']=='file')   {
684                                         unset($dataArray[$field]);
685                                 }
686                         }
687                 }
688                 return $dataArray;
689         }
690
691         /**
692          * Renders HTML table-rows with the comparison information of an sys_history entry record
693          *
694          * @param       array           sys_history entry record.
695          * @param       string          The table name
696          * @return      array           HTML table rows in an array
697          * @access private
698          */
699         function renderEntry($entry,$table)     {
700                 global $SOBE, $LANG;
701
702                 $lines=array();
703                 if (is_array($entry['newRecord']))      {
704                                 
705                         $t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff');
706
707                         $fieldsToDisplay = array_keys($entry['newRecord']);
708                         foreach($fieldsToDisplay as $fN)        {
709
710                                         // Create diff-result:
711                                 $diffres = $t3lib_diff_Obj->makeDiffDisplay(
712                                         t3lib_BEfunc::getProcessedValue($table,$fN,$entry['oldRecord'][$fN],0,1),
713                                         t3lib_BEfunc::getProcessedValue($table,$fN,$entry['newRecord'][$fN],0,1)
714                                 );
715
716                                         // Depending on list type, we make the row:
717                                 switch($this->listType) {
718                                         case 1:
719                                                 $lines[]='
720                                                         <tr class="bgColor4">
721                                                                 <td><em>'.$LANG->sl(t3lib_BEfunc::getItemLabel($table,$fN),1).'</em></td>
722                                                                 <td>'.nl2br($diffres).'</td>
723                                                                 <td>&nbsp;</td>
724                                                         </tr>';
725                                         break;
726                                         case 2:
727                                                 $lines[]='
728                                                         <tr class="bgColor4">
729                                                                 <td><em>'.$LANG->sl(t3lib_BEfunc::getItemLabel($table,$fN)).'</em></td>
730                                                                 <td>'.htmlspecialchars(t3lib_BEfunc::getProcessedValue($table,$fN,$entry['oldRecord'][$fN])).'</td>
731                                                                 <td>'.$this->linkPage('<img'.t3lib_iconWorks::skinImg('','gfx/history.gif','width="13" height="12"').' title="'.$LANG->getLL('revertField',1).'" alt="" />', array('revert'=>'field:'.$fN)).'</td>
732                                                                 <td>'.htmlspecialchars(t3lib_BEfunc::getProcessedValue($table,$fN,$entry['newRecord'][$fN])).'</td>
733                                                                 <td>'.nl2br($diffres).'</td>
734                                                         </tr>';
735                                         break;
736                                         default:
737                                                 $lines[]='
738                                                         <tr class="bgColor4">
739                                                                 <td><em>'.$LANG->sl(t3lib_BEfunc::getItemLabel($table,$fN)).'</em></td>
740                                                                 <td>'.htmlspecialchars(t3lib_BEfunc::getProcessedValue($table,$fN,$entry['oldRecord'][$fN])).'</td>
741                                                                 <td>'.htmlspecialchars(t3lib_BEfunc::getProcessedValue($table,$fN,$entry['newRecord'][$fN])).'</td>
742                                                                 <td>'.nl2br($diffres).'</td>
743                                                         </tr>';
744                                         break;
745                                 }
746                         }
747                 }
748                 return $lines;
749         }
750
751         /**
752          * Creates a header row based on the value of $this->listType
753          *
754          * @return      string          HTML table header row
755          * @access private
756          */
757         function listHeader()   {
758                 global $SOBE, $LANG;
759
760                 switch($this->listType) {
761                         case 1:
762                                 $out='
763                                         <tr class="bgColor5 c-head">
764                                                 <td>'.$LANG->getLL('fieldName',1).':</td>
765                                                 <td>'.$LANG->getLL('difference',1).':</td>
766                                                 <td>&nbsp;</td>
767                                         </tr>'; 
768                         break;
769                         case 2:
770                                 $out='
771                                         <tr class="bgColor5 c-head">
772                                                 <td>'.$LANG->getLL('fieldName',1).':</td>
773                                                 <td>'.$LANG->getLL('oldValue',1).':</td>
774                                                 <td>'.$this->linkPage('<img'.t3lib_iconWorks::skinImg('','gfx/history.gif','width="13" height="12"').' title="'.$LANG->getLL('revertAllFields',1).'" alt="" />', array('revert'=>'ALL_FIELDS')).'</td>
775                                                 <td>'.$LANG->getLL('currentValue',1).':</td>
776                                                 <td>'.$LANG->getLL('difference',1).':</td>
777                                         </tr>'; 
778                         break;
779                         default:
780                                 $out='
781                                         <tr class="bgColor5 c-head">
782                                                 <td>'.$LANG->getLL('fieldName',1).':</td>
783                                                 <td>'.$LANG->getLL('oldValue',1).':</td>
784                                                 <td>'.$LANG->getLL('newValue',1).':</td>
785                                                 <td>'.$LANG->getLL('difference',1).':</td>
786                                         </tr>'; 
787                         break;
788                 }
789                 return $out;
790         }
791
792         /**
793          * Creates a link to the same page.
794          *
795          * @param       string          String to wrap in <a> tags (must be htmlspecialchars()'ed prior to calling function)
796          * @param       array           Array of key/value pairs to override the default values with.
797          * @param       string          Possible anchor value.
798          * @return      string          Link.
799          * @access private
800          */
801         function linkPage($str,$inparams=array(),$anchor='')    {
802
803                         // Setting default values based on GET parameters:
804                 $params['sh_uid']=$this->sh_uid;
805                 $params['sumUp']=$this->sumUp;
806                 $params['element']=$this->element;
807                 $params['returnUrl']=$this->returnUrl;
808                 
809                         // Mergin overriding values:
810                 $params = array_merge($params,$inparams);
811                 
812                         // Make the link:
813                 $Ahref = 'show_rechis.php?'.t3lib_div::implodeArrayForUrl('',$params).($anchor?'#'.$anchor:'');
814                 $link = '<a href="'.htmlspecialchars($Ahref).'">'.$str.'</a>';
815                 
816                         // Return link:
817                 return $link;
818         }
819         
820         /**
821          * This creates an array with the sum of differences between two points in the sys_history
822          *
823          * @param       string          Element reference, syntax "[table]:[uid]"
824          * @param       integer         sys_history uid from which to start the selection process
825          * @param       integer         optional sys_history uid at which to stop the selection (thus applying an upper limit)
826          * @return      array           Array with difference information
827          * @access private
828          */
829         function getChangesSinceRecord($element,$hisUid=0,$hisUid_Stop=0)       {
830                 global $TCA;
831
832                         // Init:
833                 $sumChangesArray=array();
834                 $sumChangesArray['changes']=array();
835                 $elParts = explode(':',$element);
836                 
837                         // If there is a table by the requested name:
838                 if ($TCA[$elParts[0]])  {
839                         $times = array();
840                                 
841                                 // Create query for selecting sys_history records in time interval:
842                         $addWhere = ' AND sys_history.uid>='.$hisUid;
843                         if ($hisUid_Stop)       $addWhere.= ' AND sys_history.uid<='.$hisUid_Stop;
844                                 // Make query:
845                         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
846                                                         'sys_history.*,sys_log.userid', 
847                                                         'sys_history,sys_log', 
848                                                         'sys_history.sys_log_uid=sys_log.uid
849                                                                 AND sys_history.tablename="'.$GLOBALS['TYPO3_DB']->quoteStr($elParts[0], 'sys_history').'" 
850                                                                 AND sys_history.recuid='.intval($elParts[1]).$addWhere,
851                                                         '',
852                                                         'uid DESC'
853                                                 );
854                         
855                                 // Travese results:
856                         while ($newRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))   {
857                                 $hisDat = unserialize($newRow['history_data']);
858                                 if (is_array($hisDat['newRecord']) && is_array($hisDat['oldRecord']))   {
859                                         $sumChangesArray['changes']=array_merge($sumChangesArray['changes'],$hisDat['oldRecord']);
860                                         $sumChangesArray['counter']++;
861                                         $sumChangesArray['tstamp'][]=$newRow['tstamp'];
862                                         $sumChangesArray['userList'][]=$newRow['userid'];
863                                 } else {
864                                         debug('ERROR!');
865                                 }
866                                 
867                         }
868                 }       
869                 return $sumChangesArray;
870         }
871 }
872
873 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/class.show_rechis.inc'])     {
874         include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/class.show_rechis.inc']);
875 }
876 ?>