<?php
defined('WikyBlog') or die("Not an entry point...");
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// HISTORY INSTRUCTIONS
//
function run_Instructions_main($source,$instructions){
$test = trim($instructions);
if( empty($test) ){
return $source;
}
// 1) Set sourceArray
$type = null;
if( is_object($source) ){
//$type = get_class( $source );
$resultArray = get_object_vars( $source );
}else{
$resultArray = $source;
}
// 2) Prep Instructions
if( function_exists('gzinflate') ){
$inflated = @gzinflate($instructions);
if( $inflated !== false){
$instructions = $inflated;
}
}
$instructions = unserialize($instructions);
// message(showArray($resultArray) );
// message(showArray($instructions) );
// 3) Run Instruction For each Key
if( is_array($instructions) ){
foreach($instructions as $key => $instruction){
$resultArray[$key] = run_Instructions($resultArray[$key],$instruction);
}
}
return $resultArray;
}
function run_Instructions($source,$instructions){
if( is_string($instructions) ){
return $instructions;
}else{
$source = stringToArray($source);
}
// message(showArray($source) );
// message(showArray($instructions) );
////////////////////////////////////////////////////////////////////////////////
//
// I) Prepare addRight and deleteLeft
//
// 1) Delete/Add
if( isset($instructions['d']) ){
$deleteLeft = $instructions['d'];
}
if( isset($instructions['a']) ){
$addRight = $instructions['a'];
}
// 2) Replace => Delete/Add
if( !empty($instructions['r']) ){
foreach($instructions['r'] as $replaceKey => $with){
$deleteLeft[$replaceKey] = '';
foreach($with as $withKey => $withValue){
$addRight[$replaceKey][$withKey] = $withValue;
}
}
}
// 3) Move => Delete/Add
if( !empty($instructions['m']) ){
$move = $instructions['m'];
foreach($move as $from => $to){
$deleteLeft[$from] = '';
list($leftIndex,$rightIndex) = explode('.',$to);
$addRight[$leftIndex][$rightIndex] = $source[$from];
}
}
// echo '<table><tr>';
// echo '<td style="vertical-align:top">Move<br />';
// echo showArray($move);
// echo '</td><td style="vertical-align:top">Delete<br />';
// echo showArray($deleteLeft);
// echo '</td><td style="vertical-align:top">Add<br />';
// echo showArray($addRight);
// echo '</td></table>';
//message(showArray($addRight) );
////////////////////////////////////////////////////////////////////////////////
//
// II) Apply add and delete
// i) reverse array
// ii) line by line
// iia) delete
// iib) add
// iii) reverse array
//
krsort($source);
//message( showArray($source) );
foreach($source as $sourceKey => $sourceValue){
//$message = $sourceKey;
if( !isset($deleteLeft[$sourceKey]) ){
//$finalText[$sourceKey] = $source[$sourceKey];// wrong, just treat it as a stack,
// otherwise it could result in writing over adds,
$finalText[] = $source[$sourceKey];
//$message .= ' Leave: '.wbHtmlspecialchars($source[$sourceKey]);
}else{
//$message .= ' Delete: '.wbHtmlspecialchars($source[$sourceKey]);
}
//message($message);
if( isset($addRight[$sourceKey]) ){
krsort($addRight[$sourceKey]); // sort because of additions to addRight from move
// krsort() because we're going backwards
$add = array();
foreach($addRight[$sourceKey] as $key => $addLine){
$finalText[] = $addLine;
}
}
}
$finalText = array_reverse($finalText);
// For additional added lines
$nextIndex = count($source);
if( isset($addRight[$nextIndex]) ){
foreach($addRight[$nextIndex] as $key => $addLine){
$finalText[] = $addLine;
}
}
return implode("\n",$finalText);
}
//
// HISTORY INSTRUCTIONS
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SINGLE REVISIONS
//
// $rowLimit could be used to limit the the number of rows to 1
// - this could be useful with the xmlHTTP saving where history elements will constantly be overwritten
// $user
// - limit the revisions to the user
// - this was a bit of a hack?... and I'm not using it any more
function getRevision($revNum,&$object){
global $dbObject,$wbTables;
if( wbStrtolower($revNum) != $object->revision){
if( !is_numeric($revNum) ){
message('INVALID_REVISION');
return false;
}
$query = ' SELECT * FROM '.$wbTables['all_history'].' WHERE ';
$query .= ' `file_id` = '.$object->file_id;
$query .= ' AND `revision` >= '.$revNum;
if( wbStrtolower($object->revision) != 'current'){
$query .= ' AND `revision` < '.$object->revision;
}
$query .= ' AND `modified` != "'.$object->modified.'" '; //we don't want the placeholder
$query .= ' ORDER BY `revision` DESC';
//$query .= ' LIMIT 100 OFFSET 1'; //we don't want the first one, it's just a place holder
//then just use a limit of 100
$result = wbDB::runQuery($query);
$num = mysql_num_rows($result);
if($num == 0){
//this just means we've selected the current placeholder
//message('NO_UNIQUE_REVISION');
return;
}
applyRevisionRows($result,$object);
}
}// end function
//nearly the same as above function other than this selects the lates revision using LIMIT 1
function getLastRevision(&$object,$checkTime=false){
global $wbTables;
if(!$object->exists){
return false;
}
$query = ' SELECT * FROM '.$wbTables['all_history'].' WHERE ';
$query .= ' `file_id` = '.$object->file_id;
$query .= ' AND `revision` > 0';
$query .= ' ORDER BY `revision` DESC';
$query .= ' LIMIT 1 OFFSET 1 ';
$result = wbDB::runQuery($query);
$num = mysql_num_rows($result);
if( $num == 0 ){
return false;
}
$row = mysql_fetch_object($result);
if( $checkTime !== false ){
if( $_SESSION['editPage'][$object->uniqStorage] != $row->modified ){
return false;
}
}
applyRevisionRow(&$row,&$object);
return true;
}
function applyRevisionRows(&$result,&$object){
while($row = mysql_fetch_object($result) ){
applyRevisionRow(&$row,&$object);
// unset($temp2);
// $temp2 = run_Instructions_main($object,$row->instructions);
// $object->setVariables($temp2,$object->userValues);
//
// //must set these values because they aren't part of ->userValues
// $object->revision = $row->revision;
// $object->modified = $row->modified;
// $object->summary = $row->summary;
// $object->username = $row->username;
// $object->ip = $row->ip;
//
// //
// $object->distanceToCurrent++;
}
}
function applyRevisionRow(&$row,&$object){
$temp2 = run_Instructions_main($object,$row->instructions);
$keys = $object->userValues + $object->dbValues;
$object->setVariables($temp2,$keys);
//$object->setVariables($temp2,$object->userValues);
//must set these values because they aren't part of ->userValues
$object->revision = $row->revision;
$object->modified = $row->modified;
$object->summary = $row->summary;
$object->username = $row->username;
$object->ip = $row->ip;
//
$object->distanceToCurrent++;
}
function showRevision($link=false){
global $page,$dbObject,$langA;
if( !$dbObject->exists ){
message('FILE_MUST_EXIST');
return false;
}
$revNum = (int)$_GET['revNum'];
getRevision($revNum,$dbObject);
if( !$dbObject){
return false;
}
$title = $langA['revision'].$dbObject->revision;
$revLink = '/Edit'.$dbObject->uniqLink.'?cmd=show&revNum='.$revNum;
$page->regLink($title,$revLink);
//!! this isn't quite right... how do we know that the message will be on the right tab..
$message = $langA['revision_as_of'].dbFromDate($dbObject->modified,3);
$message .= ' - ';
if( $link ){
$message .= $link;
}else{
$message .= wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=editRevision&revNum='.$revNum,$langA['edit_revision']);
if( isOwner() ){
$message .= ' - ';
$message .= wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=RevertEdit&revNum='.$revNum,$langA['revert_to_revision']);
}
}
//previous|next links
$showLinks = array();
if( $dbObject->revision > 1){
$prevNum = $dbObject->revision-1;
$showLinks[] = wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=show&revNum='.$prevNum,wbLang::text('show_prev_revision',$prevNum));
}
if($dbObject->distanceToCurrent === 1){
$showLinks[] = wbLinks::local($dbObject->uniqLink,'current_revision');
}else{
$nextNum = ($dbObject->revision+1);
$showLinks[] = wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=show&revNum='.$nextNum,wbLang::text('show_next_revision',$nextNum));
}
if(count($showLinks) > 0){
$message .= '<br/><span class="sm">'.implode(' | ',$showLinks).'</span>';
}
message($message);
// an older version might not be safe and history tables don't store that data
// so we just assume older versions aren't 'safe'
if( strpos($dbObject->flags,'safe') !== false ){
$dbObject->flags = str_replace('safe','',$dbObject->flags);
}
$dbObject->outputObj($revLink);
$dbObject->addFooter($revLink);
}//end function
function showDeleted(){
global $page,$dbObject,$langA,$wbTables;
if( !$dbObject->exists ){
message('FILE_MUST_EXIST');
return false;
}
$revNum =& $_GET['revNum'];
if( !is_numeric($revNum) ){
message('INVALID_REVISION');
return false;
}
message('langA["deleted"]');
$langA['deleted'] = 'Deleted';
$title = $langA['deleted'];
$link = $page->regLink($title, '/Edit'.$dbObject->uniqLink. '?cmd=deleted&revNum='.$revNum);
//get history rows
$query = ' SELECT `instructions`,`revision` FROM '.$wbTables['all_history'].' WHERE ';
$query .= ' `file_id` = '.$dbObject->file_id;
$query .= ' AND `revision` >= '.$revNum;
if( wbStrtolower($dbObject->revision) != 'current'){
$query .= ' AND `revision` < '.$dbObject->revision;
}
$query .= ' AND `modified` != "'.$dbObject->modified.'" '; //we don't want the placeholder
$query .= ' ORDER BY `revision` DESC';
$result = wbDB::runQuery($query);
//$num = mysql_num_rows($result);
ob_start();
while($row = mysql_fetch_assoc($result)){
$instructions =& $row['instructions'];
if( function_exists('gzinflate') ){
$inflated = @gzinflate($instructions);
if( $inflated !== false){
$instructions = $inflated;
}
}
$instructions = unserialize($instructions);
echo '<h3>'.$langA['revision'].': '.$row['revision'].'</h3>';
foreach($row['instructions'] as $area => $areaInstruct){
showDeletedRevision($area,$areaInstruct);
}
}
$page->contentB[$link] = wb::get_clean();
}
function showDeletedRevision(&$area,&$instructions){
$deleted = array();
echo showArray($instructions);
if( isset($instructions['a']) ){
foreach($instructions['a'] as $lines){
$deleted += $lines;
}
}
if( isset($instructions['r']) ){
foreach($instructions['r'] as $lines){
$deleted += $lines;
}
}
if( count($deleted) > 0 ){
echo '<h4>deleted from '.$area.'</h4>';
echo showarray($lines);
}
}
// Edit an older version, saving will make it the current version
//
// for this to pass the check in toolSavePage.php it needs to have the current `modified` value
// so I store `modified` in $mod then reassign it to $dbObject after getRevision();
//
function editRevision(){
global $page,$dbObject,$langA;
if( !$dbObject->exists ){
message('FILE_MUST_EXIST');
return false;
}
$mod = $dbObject->modified;
getRevision($_GET['revNum'],$dbObject);
if( !$dbObject){
return false;
}
$dbObject->modified = $mod;
$_POST['summary'] = $langA['reverted_to_rev'].$_GET['revNum'];
message('EDITING_REVISION');
includeFile('tool/EditPage.php');
initEdit::init();
}//end function
function RevertEdit($revNum){
global $page,$dbObject,$langA;
if( !isOwner() ){
message('NOT_OWNER');
return;
}
if( !$dbObject->exists ){
message('FILE_MUST_EXIST');
return false;
}
$newObject = php4_clone($dbObject);
getRevision($revNum,$newObject);
if( !$newObject){
return false;
}
$_POST['summary'] = $langA['reverted_to_rev'].$_GET['revNum'];
includeFile('tool/SavePage.php',false);
if( objectSave::saveControl($dbObject, $newObject) ){
//message('SAVED_FILE');
message($_POST['summary']);
}else{
message('00PS');
}
}
//
// SINGLE REVISIONS
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// COMPARISON
//
function compareRevisions(){
global $page,$dbObject;
if( !$dbObject->exists ){
message('FILE_MUST_EXIST');
return false;
}
if( isset($_GET['revNum1']) ){
$revNum1 = $_GET['revNum1'];
$revNum2 = $page->cmdArg[0];
}else{
$revNum1 = $_GET['rev1'];
$revNum2 = $_GET['rev2'];
}
//////// 1) Make sure times are set
if( empty($revNum1) || empty($revNum2) ){
message('SELECT_TWO_VERSIONS');
return;
}elseif( $revNum1 == $revNum2){
message('SELECT_TWO_VERSIONS');
return;
}
//////// 2) Find Min and Max times => get their events
if( (wbStrtolower($revNum1) == 'current') ){
$max = $revNum1;
$min = $revNum2;
}else{
$max = max($revNum1,$revNum2);
$min = min($revNum1,$revNum2);
}
getRevision($max,$dbObject);
$newObject = php4_clone($dbObject);
if( !$newObject){
return false;
}
getRevision($min,$dbObject);
if( !$dbObject){
return false;
}
//////// Do Comparison
compareTwo($dbObject,$newObject);
}
function compareTwo(&$oldObject,&$newObject,$title='',$oldHeader=false,$newHeader=false){
global $page,$dbObject,$langA,$diffClasses;
$isOwner = isOwner();
$page->css2 = true;
includeFile('tool/DifferenceEngine.php');
ob_start();
if( empty($title) ){
if( $newObject->revision == 'current'){
$temp = $langA['current'];
}else{
$temp = $newObject->revision;
}
$title = toDisplay($dbObject->title).' ('.$oldObject->revision.$langA['vs'].$temp.')';
$page->displayTitle = $title;
}else{
$page->displayTitle .= ' ('. $title.')';
}
if( $oldObject->distanceToCurrent === 1){
$title = $langA['diff'];
$link = $page->regLink($title, '/Edit'.$dbObject->uniqLink.'?cmd=difference'); //for compatibility with changelog links
}elseif($oldObject->distanceToCurrent == 0){
//case when comparing current changes
}else{
$link = $page->regLink($title, '/Edit'.$dbObject->uniqLink.'?cmd=compare&rev1='.$oldObject->revision.'&rev2='.$newObject->revision);
}
//Top of Differences
//
//
echo '<table cellspacing="3" cellpadding="1" border="0" width="98%">';
echo '<tr><td></td><td style="text-align:center;width:50%">';
// Old Header
//
$editLink = false;
if( $oldHeader ){
echo $oldHeader;
}else{
if( $oldObject->revision == 'current'){
echo '<h4 class="heading">'.wbLinks::local($dbObject->uniqLink,$langA['current_revision']).'</h4>';
}else{
echo '<h4 class="heading">';
$temp = wbLang::text('revision_num_as_of',$oldObject->revision,dbFromDate($oldObject->modified,3));
echo wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=show&revNum='.$oldObject->revision,$temp);
echo '</h4>';
$editLink = $page->findLink($dbObject->editLabel);
if( strpos($editLink,'javascript:') !== 0){
$editLink = wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=editRevision&revNum='.$oldObject->revision,$langA['edit_revision']);
if( $isOwner ){
$editLink .= ' - ';
$editLink .= wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=RevertEdit&revNum='.$oldObject->revision,$langA['revert_to_revision']);
}
}else{
$editLink = false;
}
}
if( !empty($oldObject->username) ){
echo wbLinks::user($oldObject->username);
$user = $oldObject->username;
}else{
echo $oldObject->ip;
$user = $oldObject->username;
}
if( isset($_SESSION['username']) && ($_SESSION['username'] != $user) ){
echo ' <span class="sm">('.wbLinks::special('Permissions?guest='.$user,'user permissions','title="'.$langA['SET_USER_PERMISSIONS'].$user.'"',$_SESSION['username']).')</span> ';
}
echo '<br/> '.$oldObject->summary;
if( $editLink ){
echo '<br/><span class="sm">'.$editLink.'</span>';
}
if( $oldObject->revision > 1){
echo '<br/><span class="sm">'.wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=compare&rev1='.($oldObject->revision-1).'&rev2='.$oldObject->revision,$langA['compare_with_prev']).' </span>';
}elseif($oldObject->revision == 'current'){
echo '<br/><span class="sm">'.wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=difference',$langA['compare_with_prev']).' </span>';
}
}
echo '</td><td></td>';
echo '<td style="text-align:center;width:50%">';
// New Header
//
$editLink = false;
if( $newHeader ){
echo $newHeader;
}else{
if( $newObject->revision == 'current'){
echo '<h4 class="heading">'.wbLinks::local($dbObject->uniqLink,$langA['current_revision']).'</h4>';
}else{
echo '<h4 class="heading">';
$temp = wbLang::text('revision_num_as_of',$newObject->revision,dbFromDate($newObject->modified,3));
echo wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=show&revNum='.$newObject->revision,$temp);
echo '</h4>';
$editLink = $page->findLink($dbObject->editLabel);
if( strpos($editLink,'javascript:') !== 0){
$editLink = wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=editRevision&revNum='.$newObject->revision,$langA['edit_revision']);
if( $isOwner ){
$editLink .= ' - ';
$editLink .= wbLinks::local('/Edit'.$dbObject->uniqLink.'?cmd=RevertEdit&revNum='.$newObject->revision,$langA['revert_to_revision']);
}
}else{
$editLink = false;
}
}
if( !empty($newObject->username) ){
echo wbLinks::user($newObject->username);
$user = $newObject->username;
}else{
echo $newObject->ip;
$user = $newObject->ip;
}
if( isset($_SESSION['username']) && ($_SESSION['username'] != $user) ){
echo ' <span class="sm">('.wbLinks::special('Permissions?guest='.$user,'user permissions','title="'.$langA['SET_USER_PERMISSIONS'].$user.'"',$_SESSION['username']).')</span> ';
}
echo '<br/> '.$newObject->summary;
if( $editLink ){
echo '<br/><span class="sm">'.$editLink.'</span>';
}
if( $newObject->distanceToCurrent > 0){
if( $newObject->distanceToCurrent === 1){
echo '<br/><span class="sm">'.wbLinks::local('/Edit'.$newObject->uniqLink.'?cmd=difference',$langA['compare_with_next']).' </span>';
}else{
echo '<br/><span class="sm">'.wbLinks::local('/Edit'.$newObject->uniqLink.'?cmd=compare&rev1='.$newObject->revision.'&rev2='.($newObject->revision+1),$langA['compare_with_next']).' </span>';
}
}
}
echo '</td></tr>';
//
// New Difference
//
$diffClasses['context'] = 'historyNeut';
$diffClasses['deletedline'] = 'historyDelete';
$diffClasses['addedline'] = 'historyAdd';
$diffClasses['diffchange'] = 'historyChange';
$formatter = new TableDiffFormatter();
$i= 0;
foreach($oldObject->userValues as $key => $nothing){
// We don't want to show difference when comparing NULL with '' (empty string)
// NULL != '' also works
//
// if( getType($oldObject->$key) == 'NULL'){
// $oldObject->$key = '';
// }
// if( getType($newObject->$key) == 'NULL'){
// $newObject->$key = '';
// }
if( $oldObject->$key == $newObject->$key){
continue;
}
$i++;
echo '<tr><td colspan="4">';
echo '<h3 class="heading underline">';
if( isset($langA[$key]) ){
echo $langA[$key];
}else{
echo $key;
}
echo '</h3>';
echo '</td></tr>';
$oldArray = stringToArray($oldObject->$key);
$newArray = stringToArray($newObject->$key);
$diffs = new Diff( $oldArray,$newArray );
echo $formatter->format( $diffs );
}
if( $i === 0){
echo '<tr><td colspan="4" style="text-align:center">'.$langA['NO_DIFFERENCES'].'</td></tr>';
}
echo '</table>';
if( isset($link) ){
$page->contentB[$link] = wb::get_clean();
}else{
$page->contentA[$title] = wb::get_clean();
}
}
function getLastDifference(){
global $page, $dbObject,$langA;
if( !$dbObject->exists ){
message('FILE_MUST_EXIST');
return false;
}
$current = php4_clone($dbObject);
if( !getLastRevision($dbObject) ){
message('NO_REVISIONS');
return false;
}
compareTwo($dbObject,$current,$langA['diff']);
}
function showLines($array){
$text = '<table border="0" cellpadding="7" ><tr><td><b>'.$langA['lines'].'</b></td><td><b>'.$langA['text'].'</b></td>';
$odd = null;
foreach($array as $key => $value){
$text .= "\n";
if( isset($odd) ){
unset($odd);
}else{
$odd = ' class="row2" ';
}
$text .= "<tr $odd><td>";
$text .= $key;
$text .= '</td><td>';
$text .= wbHtmlspecialchars($value);
$text .= '</td></tr>';
}
$text .= '</table>';
return $text;
}
//this needs toolEditPage if it's not an xmlHttp request
function showChanges(){
global $dbObject,$langA;
if( !$dbObject->exists ){
message('FILE_MUST_EXIST');
return false;
}
$current = php4_clone($dbObject);
$dbObject->setFromPost();
compareTwo($current,$dbObject,$langA['changes'],false,$langA['your_text'] );
if( !isset($_GET['wb']) ){
includeFile('tool/EditPage.php');
initEdit::init();
}
}
//
// COMPARISON
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// FLOW CONTROL
//
global $langA;
switch( $page->userCmd ){
case 'changes':
case wbStrtolower($langA['changes']):
showChanges();
break;
case 'compare':
case wbStrtolower($langA['compare']);
case 'compare versions':
compareRevisions();
break;
case 'editrevision':
editRevision();
break;
case 'revertedit':
RevertEdit($_GET['revNum']);
break;
case 'show':
case 'revision':
showRevision();
break;
case 'difference':
getLastDifference();
break;
case 'deleted';
showDeleted();
break;
}