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