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