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