[BUGFIX] Show correct record title
[Packages/TYPO3.CMS.git] / typo3 / class.show_rechis.inc
1 <?php
2 /***************************************************************
3 *  Copyright notice
4 *
5 *  (c) 1999-2009 Kasper Skårhøj (kasperYYYY@typo3.com)
6 *  (c) 2006-2009 Sebastian Kurfürst (sebastian@garbage-group.de)
7 *  All rights reserved
8 *
9 *  This script is part of the TYPO3 project. The TYPO3 project is
10 *  free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License as published by
12 *  the Free Software Foundation; either version 2 of the License, or
13 *  (at your option) any later version.
14 *
15 *  The GNU General Public License can be found at
16 *  http://www.gnu.org/copyleft/gpl.html.
17 *  A copy is found in the textfile GPL.txt and important notices to the license
18 *  from the author is found in LICENSE.txt distributed with these scripts.
19 *
20 *
21 *  This script is distributed in the hope that it will be useful,
22 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
23 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 *  GNU General Public License for more details.
25 *
26 *  This copyright notice MUST APPEAR in all copies of the script!
27 ***************************************************************/
28 /**
29  * Class for the record history display script (show_rechis.php)
30  *
31  * XHTML Compliant
32  *
33  * @author      Sebastian Kurfürst <sebastian@garbage-group.de>
34  */
35
36
37 /**
38  * Class for the record history display script (show_rechis.php)
39  *
40  * @author      Sebastian Kurfürst <sebastian@garbage-group.de>
41  * @package TYPO3
42  * @subpackage core
43  */
44 class recordHistory {
45                 // External, static:
46         var $maxSteps=20;               // Maximum number of sys_history steps to show.
47         var $showDiff=1;                // display diff or not (0-no diff, 1-inline)
48         var $showSubElements=1;         // on a pages table - show sub elements as well.
49         var $showInsertDelete=1;                // show inserts and deletes as well
50
51                 // Internal, GPvars
52         var $element;                   // Element reference, syntax [tablename]:[uid]
53         var $lastSyslogId;              // syslog ID which is not shown anymore
54         var $returnUrl;
55
56                 // Internal
57         var $changeLog;
58         var $showMarked=FALSE;
59         /**
60          * Constructor for the class
61          *
62          * @return      void
63          */
64         function __construct()  {
65                         // GPvars:
66                 $this->element = t3lib_div::_GP('element');
67                 $this->returnUrl = t3lib_div::sanitizeLocalUrl(t3lib_div::_GP('returnUrl'));
68                 $this->lastSyslogId = t3lib_div::_GP('diff');
69                 $this->rollbackFields = t3lib_div::_GP('rollbackFields');
70                         // resolve sh_uid if set
71                 $this->resolveShUid();
72         }
73
74         /**
75          * Main function for the listing of history.
76          * It detects incoming variables like element reference, history element uid etc. and renders the correct screen.
77          *
78          * @return      HTML            content for the module
79          */
80         function main() {
81                 $content = '';
82
83                         // single-click rollback
84                 if (t3lib_div::_GP('revert') && t3lib_div::_GP('sumUp'))        {
85                         $this->rollbackFields = t3lib_div::_GP('revert');
86                         $this->showInsertDelete = 0;
87                         $this->showSubElements = 0;
88
89                         $element = explode(':',$this->element);
90                         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*','sys_history', 'tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($element[0], 'sys_history').' AND recuid='.intval($element[1]), '', 'uid DESC', '1');
91                         $record = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
92                         $this->lastSyslogId = $record['sys_log_uid'];
93
94                         $this->createChangeLog();
95                         $completeDiff = $this->createMultipleDiff();
96                         $this->performRollback($completeDiff);
97                         t3lib_utility_Http::redirect($this->returnUrl);
98                 }
99
100                         // save snapshot
101                 if (t3lib_div::_GP('highlight') && !t3lib_div::_GP('settings')) {
102                         $this->toggleHighlight(t3lib_div::_GP('highlight'));
103                 }
104
105                 $content .= $this->displaySettings();
106                 if ($this->createChangeLog())   {
107                         if ($this->rollbackFields)      {
108                                 $completeDiff = $this->createMultipleDiff();
109                                 $content .= $this->performRollback($completeDiff);
110
111                         }
112                         if ($this->lastSyslogId)        {
113                                 $completeDiff = $this->createMultipleDiff();
114                                 $content .= $this->displayMultipleDiff($completeDiff);
115                         }
116                         if ($this->element)     {
117                                 $content .= $this->displayHistory();
118                         }
119                 }
120                 return $content;
121         }
122
123         /*******************************
124          *
125          * database actions
126          *
127          *******************************/
128
129         /**
130          * toggles highlight state of record
131          *
132          * @param       integer         uid of sys_history entry
133          * @return      [type]          ...
134          */
135         function toggleHighlight($uid)  {
136                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('snapshot','sys_history','uid='.intval($uid));
137                 $tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
138                 if ($tmp['snapshot'])   {
139                         $tmp = 0;
140                 } else {
141                         $tmp = 1;
142                 }
143                 $updateFields = array('snapshot' => $tmp);
144                 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_history','uid='.intval($uid),$updateFields);
145         }
146
147         /**
148          * perform rollback
149          *
150          * @param       array           diff array to rollback
151          * @return      void
152          * @access private
153          */
154         function performRollback($diff) {
155                 if (!$this->rollbackFields)     {
156                         return 0;
157                 }
158
159                 $reloadPageFrame=0;
160                 $rollbackData = explode(':',$this->rollbackFields);
161
162                         // PROCESS INSERTS AND DELETES
163                         // rewrite inserts and deletes
164                 $cmdmapArray = array();
165                 if ($diff['insertsDeletes'])    {
166
167                         switch (count($rollbackData))   {
168                                 case 1: // all tables
169                                         $data = $diff['insertsDeletes'];
170                                         break;
171                                 case 2: // one record
172                                         if ($diff['insertsDeletes'][$this->rollbackFields]) {
173                                                 $data[$this->rollbackFields] = $diff['insertsDeletes'][$this->rollbackFields];
174                                         }
175                                         break;
176                                 case 3: // one field in one record -- ignore!
177                                         break;
178                         }
179                         if ($data)      {
180                                 foreach ($data as $key => $action)      {
181                                         $elParts = explode(':',$key);
182                                         if ($action == 1)       {       // inserted records should be deleted
183                                                 $cmdmapArray[$elParts[0]][$elParts[1]]['delete'] = 1;
184                                                         // when the record is deleted, the contents of the record do not need to be updated
185                                                 unset($diff['oldData'][$key]);
186                                                 unset($diff['newData'][$key]);
187                                         } elseif ($action == -1) {      // deleted records should be inserted again
188                                                 $cmdmapArray[$elParts[0]][$elParts[1]]['undelete'] = 1;
189                                         }
190                                 }
191                         }
192                 }
193                         // Writes the data:
194                 if ($cmdmapArray)       {
195                         $tce = t3lib_div::makeInstance('t3lib_TCEmain');
196                         $tce->stripslashes_values=0;
197                         $tce->debug=0;
198                         $tce->dontProcessTransformations=1;
199                         $tce->start(array(),$cmdmapArray);
200                         $tce->process_cmdmap();
201                         unset($tce);
202                         if (isset($cmdmapArray['pages']))       {
203                                 $reloadPageFrame=1;
204                         }
205                 }
206
207                         // PROCESS CHANGES
208                         // create an array for process_datamap
209                 $diff_modified = array();
210                 foreach ($diff['oldData'] as $key => $value)    {
211                         $splitKey = explode(':',$key);
212                         $diff_modified[$splitKey[0]][$splitKey[1]] = $value;
213                 }
214                 switch (count($rollbackData))   {
215                         case 1: // all tables
216                                 $data = $diff_modified;
217                                 break;
218                         case 2: // one record
219                                 $data[$rollbackData[0]][$rollbackData[1]] = $diff_modified[$rollbackData[0]][$rollbackData[1]];
220                                 break;
221                         case 3: // one field in one record
222                                 $data[$rollbackData[0]][$rollbackData[1]][$rollbackData[2]] = $diff_modified[$rollbackData[0]][$rollbackData[1]][$rollbackData[2]];
223                                 break;
224                 }
225                         // Removing fields:
226                 $data = $this->removeFilefields($rollbackData[0],$data);
227
228                         // Writes the data:
229                 $tce = t3lib_div::makeInstance('t3lib_TCEmain');
230                 $tce->stripslashes_values=0;
231                 $tce->debug=0;
232                 $tce->dontProcessTransformations=1;
233                 $tce->start($data,array());
234                 $tce->process_datamap();
235                 unset($tce);
236                 if (isset($data['pages']))      {
237                         $reloadPageFrame=1;
238                 }
239
240                         // return to normal operation
241                 $this->lastSyslogId = FALSE;
242                 $this->rollbackFields = FALSE;
243                 $this->createChangeLog();
244
245                         // reload page frame if necessary
246                 if ($reloadPageFrame)   {
247                         return '<script type="text/javascript">
248                         /*<![CDATA[*/
249                         if (top.content && top.content.nav_frame && top.content.nav_frame.refresh_nav)  {
250                                 top.content.nav_frame.refresh_nav();
251                         }
252                         /*]]>*/
253                         </script>';
254                 }
255         }
256
257         /*******************************
258          *
259          * Display functions
260          *
261          *******************************/
262
263         /**
264          * Displays settings
265          *
266          * @return      string          HTML code to modify settings
267          */
268         function displaySettings()      {
269                         // get current selection from UC, merge data, write it back to UC
270                 $currentSelection = is_array($GLOBALS['BE_USER']->uc['moduleData']['history']) ? $GLOBALS['BE_USER']->uc['moduleData']['history'] : array('maxSteps' => '', 'showDiff' => 1, 'showSubElements' => 1, 'showInsertDelete' => 1);
271
272                 $currentSelectionOverride = t3lib_div::_GP('settings');
273                 if ($currentSelectionOverride)  {
274                         $currentSelection = array_merge($currentSelection,$currentSelectionOverride);
275                         $GLOBALS['BE_USER']->uc['moduleData']['history'] = $currentSelection;
276                         $GLOBALS['BE_USER']->writeUC($GLOBALS['BE_USER']->uc);
277                 }
278
279                         // display selector for number of history entries
280                 $selector['maxSteps'] = array(
281                         10 => 10,
282                         20 => 20,
283                         50 => 50,
284                         100 => 100,
285                         '' => 'maxSteps_all',
286                         'marked' => 'maxSteps_marked'
287                 );
288                 $selector['showDiff'] = array(
289                         0 => 'showDiff_no',
290                         1 => 'showDiff_inline'
291                 );
292                 $selector['showSubElements'] = array(
293                         0 => 'no',
294                         1 => 'yes',
295                 );
296                 $selector['showInsertDelete'] = array(
297                         0 => 'no',
298                         1 => 'yes',
299                 );
300                         // render selectors
301                 $displayCode = '';
302                 foreach ($selector as $key => $values)  {
303                         $displayCode .= '<tr><td>'.$GLOBALS['LANG']->getLL($key,1).'</td><td><select name="settings['.$key.']" onChange="document.settings.submit()" style="width:100px">';
304                         foreach ($values as $singleKey => $singleVal) {
305                                 $caption = $GLOBALS['LANG']->getLL($singleVal,1)?$GLOBALS['LANG']->getLL($singleVal,1):$singleVal;
306                                 $displayCode .= '<option value="'.$singleKey.'" '.(($singleKey ==  $currentSelection[$key])?'selected':'').'> '.$caption.'</option>';
307                         }
308                         $displayCode .= '</select></td></tr>';
309                 }
310                         // set values correctly
311                 if ($currentSelection['maxSteps'] != 'marked')  {
312                         $this->maxSteps = $currentSelection['maxSteps']?intval($currentSelection['maxSteps']):'';
313                 } else {
314                         $this->showMarked = TRUE;
315                         $this->maxSteps = FALSE;
316                 }
317                 $this->showDiff = intval($currentSelection['showDiff']);
318                 $this->showSubElements = intval($currentSelection['showSubElements']);
319                 $this->showInsertDelete = intval($currentSelection['showInsertDelete']);
320
321                 $content = '';
322                         // get link to page history if the element history is shown
323                 $elParts = explode(':',$this->element);
324                 if ($elParts[0] != 'pages')     {
325                         $content .= '<strong>'.$GLOBALS['LANG']->getLL('elementHistory',1).'</strong><br />';
326                         $pid = t3lib_BEfunc::getRecordRaw($elParts[0],'uid='.intval($elParts[1]));
327                         $content .= $this->linkPage($GLOBALS['LANG']->getLL('elementHistory_link',1),array('element' => 'pages:'.$pid['pid']));
328                 }
329                 $content .= '<form name="settings" action="'.t3lib_div::getIndpEnv('TYPO3_REQUEST_URL').'" method="post"><table>'.$displayCode.'</table></form>';
330                 return $GLOBALS['SOBE']->doc->section($GLOBALS['LANG']->getLL('settings',1),$content,0,1,0,0);
331
332         }
333
334         /**
335          * Shows the full change log
336          *
337          * @return      string          HTML for list, wrapped in a table.
338          */
339         function displayHistory()       {
340                 $lines=array();
341
342                         // Initialize:
343                 $lines[] = '<tr class="t3-row-header">
344                                 <td> </td>
345                                 <td>'.$GLOBALS['LANG']->getLL('time',1).'</td>
346                                 <td>'.$GLOBALS['LANG']->getLL('age',1).'</td>
347                                 <td>'.$GLOBALS['LANG']->getLL('user',1).'</td>
348                                 <td>'.$GLOBALS['LANG']->getLL('tableUid',1).'</td>
349                                 <td>'.$GLOBALS['LANG']->getLL('differences',1).'</td>
350                                 <td>&nbsp;</td>
351                         </tr>';
352
353                         // get default page TSconfig expiration time
354                 $elParts = explode(':',$this->element);
355                 if ($elParts[0] != 'pages')     {
356                         $tmp = t3lib_BEfunc::getRecordRaw($elParts[0],'uid='.intval($elParts[1]));
357                         $pid = $tmp['pid'];
358                 } else {
359                         $pid = $elParts[1];
360                 }
361                 $tmpTsConfig = $GLOBALS['BE_USER']->getTSConfig('TCEMAIN',t3lib_BEfunc::getPagesTSconfig($pid));
362                 $expirationTime = isset($tmpTsConfig['properties']['default.']['history.']['maxAgeDays']) ? $tmpTsConfig['properties']['default.']['history.']['maxAgeDays'] : 30;
363
364                 $expirationTimestamp = $expirationTime ? ($GLOBALS['EXEC_TIME'] - 60 * 60 * 24 * $expirationTime) : 0;
365                 $expirationWarning = 0;
366
367                 $be_user_array = t3lib_BEfunc::getUserNames();
368
369                         // Traverse changelog array:
370                 if (!$this->changeLog)  {
371                         return 0;
372                 }
373                 $i = 0;
374                 foreach ($this->changeLog as $sysLogUid => $entry)      {
375                                 // stop after maxSteps
376                         if ($i > $this->maxSteps && $this->maxSteps)    {
377                                 break;
378                         }
379
380                                 // display inconsistency warning
381                         if ($entry['tstamp'] < $expirationTimestamp && !$expirationWarning)     {
382                                 $expirationWarning = 1;
383
384                                 $lines[] = '
385                                 <tr class="c-headLine">
386                                         <td colspan="7"><strong>'.$GLOBALS['LANG']->getLL('consistenceWarning',1).'</strong></td>
387                                 </tr>';
388                         }
389
390                                 // show only marked states
391                         if (!$entry['snapshot'] && $this->showMarked)   {
392                                 continue;
393                         }
394                         $i++;
395                                 // get user names
396                         $userName = ($entry['user']?$be_user_array[$entry['user']]['username']:$GLOBALS['LANG']->getLL('externalChange',1));
397
398                                 // build up single line
399                         $singleLine = array();
400
401                                 // diff link
402                         $image = t3lib_iconWorks::getSpriteIcon('actions-view-go-forward', array('title' => $GLOBALS['LANG']->getLL('sumUpChanges', TRUE)));
403                         $singleLine[] = '<span>'.$this->linkPage($image,array('diff' => $sysLogUid)).'</span>'; // remove first link
404
405                         $singleLine[] = htmlspecialchars(t3lib_BEfunc::datetime($entry['tstamp']));     // add time
406                         $singleLine[] = htmlspecialchars(t3lib_BEfunc::calcAge($GLOBALS['EXEC_TIME'] - $entry['tstamp'], $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears')));        // add age
407                         $singleLine[] = htmlspecialchars($userName);    // add user name
408                         $singleLine[] = $this->linkPage($this->generateTitle($entry['tablename'],$entry['recuid']),array('element' => $entry['tablename'].':'.$entry['recuid']),'',$GLOBALS['LANG']->getLL('linkRecordHistory',1));     // add record UID
409
410                                 // show insert/delete/diff/changed field names
411                         if ($entry['action'])   {       // insert or delete of element
412                                 $singleLine[] = '<strong>'.htmlspecialchars($GLOBALS['LANG']->getLL($entry['action'],1)).'</strong>';
413                         } else {
414                                 if (!$this->showDiff)   {       // display field names instead of full diff
415                                         // re-write field names with labels
416                                 $tmpFieldList = explode(',',$entry['fieldlist']);
417                                 foreach ($tmpFieldList as $key => $value)       {
418                                         $tmp = str_replace(':','',$GLOBALS['LANG']->sl(t3lib_BEfunc::getItemLabel($entry['tablename'],$value),1));
419                                         if($tmp)        $tmpFieldList[$key] = $tmp;
420                                         else    unset($tmpFieldList[$key]);     // remove fields if no label available
421                                 }
422                                 $singleLine[] = htmlspecialchars(implode(',',$tmpFieldList));
423                                 } else {        // display diff
424                                         $diff = $this->renderDiff($entry,$entry['tablename']);
425                                         $singleLine[] = $diff;
426                                 }
427                         }
428                                 // show link to mark/unmark state
429                         if (!$entry['action'])  {
430                                 if ($entry['snapshot']) {
431                                         $image = '<img'.t3lib_iconWorks::skinImg('','gfx/unmarkstate.gif').' align="top" alt="'.$GLOBALS['LANG']->getLL('unmarkState',1).'" title="'.$GLOBALS['LANG']->getLL('unmarkState',1).'" />';
432                                 } else {
433                                         $image = '<img'.t3lib_iconWorks::skinImg('','gfx/markstate.gif').' align="top" alt="'.$GLOBALS['LANG']->getLL('markState',1).'" title="'.$GLOBALS['LANG']->getLL('markState',1).'" />';
434                                 }
435                                 $singleLine[] = $this->linkPage($image,array('highlight' => $entry['uid']));
436                         } else {
437                                 $singleLine[] = '';
438                         }
439
440                                 // put line together
441                         $lines[] = '
442                                 <tr class="db_list_normal">
443                                         <td>' . implode('</td><td>', $singleLine) . '</td>
444                                 </tr>';
445                 }
446
447                         // Finally, put it all together:
448                 $theCode = '
449                         <!--
450                                 History (list):
451                         -->
452                         <table class="typo3-dblist" border="0" cellpadding="0" cellspacing="0" id="typo3-history">
453                                 ' . implode('', $lines) . '
454                         </table>';
455
456                 if ($this->lastSyslogId)        {
457                         $theCode .= '<br />' .  $this->linkPage(t3lib_iconWorks::getSpriteIcon('actions-move-to-bottom', array('title' => $GLOBALS['LANG']->getLL('fullView', TRUE))), array('diff' => ''));
458                 }
459
460                         // Add message about the difference view.
461                 $flashMessage = t3lib_div::makeInstance(
462                         't3lib_FlashMessage',
463                         $GLOBALS['LANG']->getLL('differenceMsg'),
464                         '',
465                         t3lib_FlashMessage::INFO
466                 );
467
468                 $theCode .= '<br /><br />' . $flashMessage->render() . '<br />';
469
470                         // Add CSH:
471                 $theCode .= t3lib_BEfunc::cshItem('xMOD_csh_corebe', 'history_'.($this->sumUp ? 'sum' : 'log'), $GLOBALS['BACK_PATH'],'');
472
473                         // Add the whole content as a module section:
474                 return $GLOBALS['SOBE']->doc->section($GLOBALS['LANG']->getLL('changes'),$theCode,0,1);
475         }
476
477         /**
478          * Displays a diff over multiple fields including rollback links
479          *
480          * @param       array           difference array
481          * @return      string          HTML output
482          */
483         function displayMultipleDiff($diff)     {
484                 $content = '';
485
486                         // get all array keys needed
487                 $arrayKeys = array_merge(array_keys($diff['newData']),array_keys($diff['insertsDeletes']),array_keys($diff['oldData']));
488                 $arrayKeys = array_unique($arrayKeys);
489
490                 if ($arrayKeys) {
491                         foreach ($arrayKeys as $key)    {
492                                 $record = '';
493                                 $elParts = explode(':',$key);
494                                         // turn around diff because it should be a "rollback preview"
495                                 if ($diff['insertsDeletes'][$key] == 1) {       // insert
496                                         $record .= '<strong>'.$GLOBALS['LANG']->getLL('delete',1).'</strong>';
497                                         $record .= '<br />';
498                                 } elseif ($diff['insertsDeletes'][$key] == -1)  {
499                                         $record .= '<strong>'.$GLOBALS['LANG']->getLL('insert',1).'</strong>';
500                                         $record .= '<br />';
501                                 }
502                                         // build up temporary diff array
503                                         // turn around diff because it should be a "rollback preview"
504                                 if ($diff['newData'][$key])     {
505                                         $tmpArr['newRecord'] = $diff['oldData'][$key];
506                                         $tmpArr['oldRecord'] = $diff['newData'][$key];
507                                         $record .= $this->renderDiff($tmpArr, $elParts[0],$elParts[1]);
508                                 }
509
510                                 $elParts = explode(':',$key);
511                                 $titleLine = $this->createRollbackLink($key, $GLOBALS['LANG']->getLL('revertRecord',1),1) . $this->generateTitle($elParts[0],$elParts[1]);
512                                 $record = '<div style="margin-left:10px;padding-left:5px;border-left:1px solid black;border-bottom:1px dotted black;padding-bottom:2px;">'.$record.'</div>';
513
514                                 $content .= $GLOBALS['SOBE']->doc->section($titleLine,$record,0,0,0,1);
515                         }
516                         $content = $this->createRollbackLink('ALL', $GLOBALS['LANG']->getLL('revertAll',1),0)  . '<div style="margin-left:10px;padding-left:5px;border-left:1px solid black;border-bottom:1px dotted black;padding-bottom:2px;">'.$content.'</div>';
517                 } else {
518                         $content = $GLOBALS['LANG']->getLL('noDifferences',1);
519                 }
520                 return $GLOBALS['SOBE']->doc->section($GLOBALS['LANG']->getLL('mergedDifferences',1),$content,0,1,0,1);
521         }
522
523         /**
524          * Renders HTML table-rows with the comparison information of an sys_history entry record
525          *
526          * @param       array           sys_history entry record.
527          * @param       string          The table name
528          * @param       integer         If set to UID of record, display rollback links
529          * @return      string          HTML table
530          * @access private
531          */
532         function renderDiff($entry,$table,$rollbackUid=0)       {
533                 $lines=array();
534                 if (is_array($entry['newRecord']))      {
535
536                         $t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff');
537
538                         $fieldsToDisplay = array_keys($entry['newRecord']);
539                         foreach($fieldsToDisplay as $fN)        {
540                                 t3lib_div::loadTCA($table);
541                                 if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type']!='passthrough')      {
542
543                                                 // Create diff-result:
544                                         $diffres = $t3lib_diff_Obj->makeDiffDisplay(
545                                                 t3lib_BEfunc::getProcessedValue($table,$fN,$entry['oldRecord'][$fN],0,1),
546                                                 t3lib_BEfunc::getProcessedValue($table,$fN,$entry['newRecord'][$fN],0,1)
547                                         );
548                                         $lines[]='
549                                                 <tr class="bgColor4">
550                                                 '.($rollbackUid?'<td style="width:33px">'.$this->createRollbackLink($table.':'.$rollbackUid.':'.$fN, $GLOBALS['LANG']->getLL('revertField',1),2).'</td>':'').'
551                                                         <td style="width:90px"><em>'.$GLOBALS['LANG']->sl(t3lib_BEfunc::getItemLabel($table,$fN),1).'</em></td>
552                                                         <td style="width:300px">'.nl2br($diffres).'</td>
553                                                 </tr>';
554                                 }
555                         }
556                 }
557                 if ($lines)     {
558                         $content = '<table border="0" cellpadding="2" cellspacing="2" id="typo3-history-item">
559                                         '.implode('',$lines).'
560                                 </table>';
561                         return $content;
562                 }
563                 return NULL;    // error fallback
564         }
565
566         /*******************************
567          *
568          * build up history
569          *
570          *******************************/
571
572         /**
573          * Creates a diff between the current version of the records and the selected version
574          *
575          * @return      array           diff for many elements
576          */
577         function createMultipleDiff()   {
578                 $insertsDeletes = array();
579                 $newArr = array();
580                 $differences = array();
581                 if (!$this->changeLog)  {
582                         return 0;
583                 }
584
585                         // traverse changelog array
586                 foreach ($this->changeLog as $key => $value)    {
587                         $field = $value['tablename'].':'.$value['recuid'];
588                                 // inserts / deletes
589                         if ($value['action'])   {
590                                 if (!$insertsDeletes[$field])   {
591                                         $insertsDeletes[$field] = 0;
592                                 }
593                                 if ($value['action'] == 'insert')       {
594                                         $insertsDeletes[$field]++;
595                                 } else {
596                                         $insertsDeletes[$field]--;
597                                 }
598                                         // unset not needed fields
599                                 if ($insertsDeletes[$field] == 0)       {
600                                         unset($insertsDeletes[$field]);
601                                 }
602                         } else {
603                                         // update fields
604                                 if (!isset($newArr[$field]))    {       // first row of field
605                                         $newArr[$field] = $value['newRecord'];
606                                         $differences[$field] = $value['oldRecord'];
607                                 } else { // standard
608                                         $differences[$field] = array_merge($differences[$field],$value['oldRecord']);
609                                 }
610                         }
611                 }
612
613                         // remove entries where there were no changes effectively
614                 foreach ($newArr as $record => $value)  {
615                         foreach ($value as $key => $innerVal)   {
616                                 if ($newArr[$record][$key] == $differences[$record][$key])      {
617                                         unset($newArr[$record][$key]);
618                                         unset($differences[$record][$key]);
619                                 }
620                         }
621                         if (empty($newArr[$record]) && empty($differences[$record]))    {
622                                 unset($newArr[$record]);
623                                 unset($differences[$record]);
624                         }
625                 }
626                 return array(
627                         'newData' => $newArr,
628                         'oldData' => $differences,
629                         'insertsDeletes' => $insertsDeletes
630                 );
631         }
632
633         /**
634          * Creates change log including sub-elements, filling $this->changeLog
635          *
636          * @return      [type]          ...
637          */
638         function createChangeLog()      {
639                 $elParts = explode(':',$this->element);
640                 $changeLog = $this->getHistoryData($elParts[0],$elParts[1]);
641
642                         // get history of tables of this page and merge it into changelog
643                 if ($elParts[0] == 'pages' && $this->showSubElements)   {
644                         foreach ($GLOBALS['TCA'] as $tablename => $value)       {
645                                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid',$tablename,'pid='.intval($elParts[1]));     // check if there are records on the page
646                                 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))      {
647                                         if ($newChangeLog = $this->getHistoryData($tablename, $row['uid']))     {       // if there is history data available, merge it into changelog
648                                                 foreach ($newChangeLog as $key => $value)       {
649                                                         $changeLog[$key] = $value;
650                                                 }
651                                         }
652                                 }
653                         }
654                 }
655                 if(!$changeLog) {
656                         return 0;
657                 }
658
659                 krsort($changeLog);
660                 $this->changeLog = $changeLog;
661
662                 return 1;
663         }
664
665         /**
666          * Gets history and delete/insert data from sys_log and sys_history
667          *
668          * @param       string          DB table name
669          * @param       integer         UID of record
670          * @return      array           history data of the record
671          */
672         function getHistoryData($table,$uid)    {
673                 $uid = $this->resolveElement($table,$uid);
674                         // If table is found in $GLOBALS['TCA']:
675                 if ($GLOBALS['TCA'][$table])    {
676                                 // Selecting the $this->maxSteps most recent states:
677                         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
678                                                 'sys_history.*,sys_log.userid',
679                                                 'sys_history,sys_log',
680                                                 'sys_history.sys_log_uid=sys_log.uid
681                                                         AND sys_history.tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history').'
682                                                         AND sys_history.recuid='.intval($uid),
683                                                 '',
684                                                 'sys_log.uid DESC',
685                                                 $this->maxSteps
686                                         );
687
688                                 // Traversing the result, building up changesArray / changeLog:
689                         #$changesArray=array(); // used temporarily to track intermedia changes
690                         $changeLog=array();
691                         while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))      {
692                                         // only history until a certain syslog ID needed
693                                 if ($row['sys_log_uid'] < $this->lastSyslogId && $this->lastSyslogId)   {
694                                         continue;
695                                 }
696                                 $hisDat = unserialize($row['history_data']);
697                                 if (is_array($hisDat['newRecord']) && is_array($hisDat['oldRecord']))   {
698
699                                                 // Add hisDat to the changeLog
700                                         $hisDat['uid']=$row['uid'];
701                                         $hisDat['tstamp']=$row['tstamp'];
702                                         $hisDat['user']=$row['userid'];
703                                         $hisDat['snapshot']=$row['snapshot'];
704                                         $hisDat['fieldlist']=$row['fieldlist'];
705                                         $hisDat['tablename']=$row['tablename'];
706                                         $hisDat['recuid']=$row['recuid'];
707
708                                         $changeLog[$row['sys_log_uid']]=$hisDat;
709
710                                                 // Update change array
711                                                 // This is used to detect if any intermedia changes have been made.
712                                         #$changesArray = array_merge($changesArray,$hisDat['oldRecord']);
713                                 } else {
714                                         debug('ERROR: [getHistoryData]');
715                                         return 0;       // error fallback
716                                 }
717                         }
718                                 // SELECT INSERTS/DELETES
719                         if ($this->showInsertDelete)    {
720                                         // Select most recent inserts and deletes // WITHOUT snapshots
721                                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
722                                                 'uid,userid,action,tstamp',
723                                                 'sys_log',
724                                                 'type=1
725                                                         AND ( action=1 OR action=3 )
726                                                         AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_log').'
727                                                         AND recuid='.intval($uid),
728                                                 '',
729                                                 'uid DESC',
730                                                 $this->maxSteps
731                                         );
732                                 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))      {
733
734                                         if ($row['uid'] < $this->lastSyslogId && $this->lastSyslogId)   {
735                                                 continue;
736                                         }
737                                         $hisDat = array();
738
739                                         switch ($row['action']) {
740                                                 case 1: // Insert
741                                                         $hisDat['action'] = 'insert';
742                                                         break;
743                                                 case 3: // Delete
744                                                         $hisDat['action'] = 'delete';
745                                                         break;
746                                         }
747                                         $hisDat['tstamp']=$row['tstamp'];
748                                         $hisDat['user']=$row['userid'];
749                                         $hisDat['tablename'] = $table;
750                                         $hisDat['recuid'] = $uid;
751                                         $changeLog[$row['uid']] = $hisDat;
752                                 }
753                         }
754                         return $changeLog;
755                 }
756                 return 0;       // error fallback
757         }
758
759
760         /*******************************
761          *
762          * Various helper functions
763          *
764          *******************************/
765
766         /**
767          * generates the title and puts the record title behind
768          *
769          * @param       [type]          $table: ...
770          * @param       [type]          $uid: ...
771          * @return      [type]          ...
772          */
773         function generateTitle($table, $uid)    {
774                 $out = $table.':'.$uid;
775                 if ($labelField = $GLOBALS['TCA'][$table]['ctrl']['label'])     {
776                         $record = t3lib_BEfunc::getRecordRaw($table, 'uid='.intval($uid));
777                         $out .= ' ('.t3lib_BEfunc::getRecordTitle($table, $record, TRUE).')';
778                 }
779                 return $out;
780         }
781         /**
782          * creates a link for the rollback
783          *
784          * @param       sting           parameter which is set to rollbackFields
785          * @param       string          optional, alternative label and title tag of image
786          * @param       integer         optional, type of rollback: 0 - ALL; 1 - element; 2 - field
787          * @return      string          HTML output
788          */
789         function createRollbackLink($key, $alt='', $type=0)     {
790                  return $this->linkPage('<img '.t3lib_iconWorks::skinImg('','gfx/revert_'.$type.'.gif','width="33" height="33"').' alt="'.$alt.'" title="'.$alt.'" align="middle" />',array('rollbackFields'=>$key));
791          }
792
793         /**
794          * Creates a link to the same page.
795          *
796          * @param       string          String to wrap in <a> tags (must be htmlspecialchars()'ed prior to calling function)
797          * @param       array           Array of key/value pairs to override the default values with.
798          * @param       string          Possible anchor value.
799          * @param       string          Possible title.
800          * @return      string          Link.
801          * @access private
802          */
803         function linkPage($str,$inparams=array(),$anchor='',$title='')  {
804
805                         // Setting default values based on GET parameters:
806                 $params['element']=$this->element;
807                 $params['returnUrl']=$this->returnUrl;
808                 $params['diff']=$this->lastSyslogId;
809                         // Mergin overriding values:
810                 $params = array_merge($params,$inparams);
811
812                         // Make the link:
813                 $Ahref = 'show_rechis.php?'.t3lib_div::implodeArrayForUrl('',$params).($anchor?'#'.$anchor:'');
814                 $link = '<a href="'.htmlspecialchars($Ahref).'"'.($title?' title="'.$title.'"':'').'>'.$str.'</a>';
815
816                         // Return link:
817                 return $link;
818         }
819
820         /**
821          * Will traverse the field names in $dataArray and look in $GLOBALS['TCA'] if the fields are of types which cannot be handled by the sys_history (that is currently group types with internal_type set to "file")
822          *
823          * @param       string          Table name
824          * @param       array           The data array
825          * @return      array           The modified data array
826          * @access private
827          */
828         function removeFilefields($table,$dataArray) {
829                 if ($GLOBALS['TCA'][$table])    {
830                         t3lib_div::loadTCA($table);
831
832                         foreach($GLOBALS['TCA'][$table]['columns'] as $field => $config)        {
833                                 if ($config['config']['type']=='group' && $config['config']['internal_type']=='file')   {
834                                         unset($dataArray[$field]);
835                                 }
836                         }
837                 }
838                 return $dataArray;
839         }
840
841         /**
842          * Convert input element reference to workspace version if any.
843          *
844          * @param       string          table of input element
845          * @param       integer         UID of record
846          * @return      integer         converted UID of record
847          */
848         function resolveElement($table,$uid)    {
849                 if (isset($GLOBALS['TCA'][$table]))     {
850                         if ($workspaceVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $table, $uid, 'uid')) {
851                                 $uid = $workspaceVersion['uid'];
852                         }
853                 }
854                 return $uid;
855         }
856
857         /**
858          * resolve sh_uid (used from log)
859          *
860          * @return      [type]          ...
861          */
862         function resolveShUid() {
863                 if (t3lib_div::_GP('sh_uid'))   {
864                         $sh_uid = t3lib_div::_GP('sh_uid');
865                         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*','sys_history', 'uid='.intval($sh_uid));
866                         $record = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
867                         $this->element = $record['tablename'].':'.$record['recuid'];
868                         $this->lastSyslogId = $record['sys_log_uid']-1;
869                 }
870         }
871 }
872 ?>