<?php
/*
LabWiki 1.2.1, 22 August 2012, by Santosh Patnaik, MD, PhD. Based on QwikiWiki 1.5 of David Barrett
*/
////// BACKUP MAINTAINANCE
//////// Requires edit.php be such that backups are made in
//////// document specific subdirectories
//////// inside backups folders
//////////// Also requires appropriate Javascript in
//////////// template files and show_backup.php and show_source.php files.
////// See - https://sourceforge.net/tracker/?func=browse&group_id=80406&atid=625194
include( "_global.php" );
QWDisableCaching( );
// set extra javascript so buttomn pressing and not 'enter' key works
$extra_javascript =
'function checkCR(evt)
{
var evt = (evt) ? evt : ((event) ? event : null);
var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
if ((evt.keyCode == 13) && (node.type=="text")) {return false;}
}
document.onkeypress = checkCR;';
// checks
if(!$QW['requestPage'])
{QWRedirect( "index.php" );exit;}
if(!$QW['userIsAuthenticated'])
{QWRedirect("login.php?page=".htlmspecialchars($QW['page']).$QW['URLSuffix']);exit;}
if(!is_file($QW['pagePath']))
{QWRedirect( "index.php" );exit;}
// Update the list of recently viewed pages
$recentlyViewedPages[] = $QW['page'];
if( isset( $QW['cookieRecentPageList'] ) )
{$QW['cookieRecentPageList'] = array_merge( array( $QW['page'] ), $QW['cookieRecentPageList'] );}
else
{$QW['cookieRecentPageList'] = array( $QW['page'] );}
$QW['cookieRecentPageList'] = QWUnduplicateArray( $QW['cookieRecentPageList'] );
QWTruncateArrayInPlace( $QW['cookieRecentPageList'], 20 ); // only keep a history of 20 pages
setcookie( "recentpages", serialize( $QW['cookieRecentPageList'] ), time()+60*60*24*30 );
// QWTBackupFormatTitle
$titlePrefix = "Backup maintainance | Document: ";
$QW_TEMPLATE['getTitle'] = "QWTBackupFormatTitle";
function QWTBackupFormatTitle( )
{global $titlePrefix, $QW;
return $titlePrefix . QWCleanQwikiPageName( $QW['page'] );
}
//////////////////// Delete specific backup file if requested
if (isset($_GET['delete_page']) && strcasecmp(substr($_GET['delete_page'], -6), '.qwiki') == 0)
{
$pagename = $QW['page'];
$file_to_delete = 'backups/'.$pagename.'/'.$_GET['delete_page'];
if (is_file($file_to_delete))
{
unlink ($file_to_delete);
}
$message = 'The specified file was deleted.';
}
//////////////////// Restore current document with backup file if requested
if (isset($_GET['restore_page']) && strcasecmp(substr($_GET['restore_page'], -6), '.qwiki') == 0)
{
// don't do if lock exists
if (!QWLockExists($QW['lockPath']))
{
// Make backup of current version
$pagename = $QW['page'];
$backupDir = 'backups/'.$pagename;
$c = 0;
do
{
// Increment the version number
$backupPath = sprintf( "$backupDir/$QW[page].%04d.qwiki", $c++ );
}
while( is_file( $backupPath ) );
if( is_file( $QW['pagePath'] ) )
{copy( $QW['pagePath'], $backupPath );
}
// Delete the current version
unlink ( $QW['pagePath'] );
// Restore with designated backup file and then delete it
$backup_to_copy = $backupDir.'/'.$_GET['restore_page'];
copy( $backup_to_copy , $QW['pagePath'] );
unlink ($backup_to_copy);
// Notify
$message = '<p>The wiki document has been restored with data from <em>' . htmlspecialchars($_GET['restore_page']) . '</em>. You can log out now.</p>';
}
else
{
$message = '<p>Restoration cannot be performed as the wiki document is being edited by someone. Please try later. You can log out now.</p>';
}
}
/////////////////////////////// Directory removal function
function remove_dir($dir)
{
global $total_count;
if(!is_dir($dir)){return false;}
$handle = opendir($dir);
while (false!==($item = readdir($handle)))
{
if($item != '.' && $item != '..')
{if(is_dir($dir.'/'.$item))
{remove_dir($dir.'/'.$item);}
else
{unlink($dir.'/'.$item); $total_count++;}
}
}
closedir($handle);
$success=false;
if(rmdir($dir))
{$success = true;}
return $success;
}
////////////////////////////// Delete all backups for this document
if (isset($_POST['this_all']))
{
$total_count = 0;
$pagename = $QW['page'];
$dir_to_delete = 'backups/'.$pagename;
remove_dir($dir_to_delete);
$message = '<p>All backup files - a total of ' . $total_count . ' - for this document have been deleted. You may log out now.</p>';
}
///////////////////////////// Delete all backups for entire wiki
if (isset($_POST['all_all']))
{
$total_count = 0;
$dir_to_delete = 'backups';
remove_dir($dir_to_delete);
$backup_dir = $dir_to_delete;
// recreate backups directory as it was deleted
QWVerifyDirectory( $backup_dir );
chmod ($backup_dir, 0755);
$message = '<p>All backup files - a total of ' . $total_count . ' - for the entire wiki have been deleted. You may log out now.</p>';
}
///////////////// Delete certain number of backups for this document by age
if (isset($_POST['this_days']) && isset($_POST['this_days_number']))
{
$n = $_POST['this_days_number'];
if (!preg_match("/^([0-9]+)$/",$n))
{
$message = '<p>Please input a positive number, such as 2, 5, etc.</p>';
}
else
{
// convert no. of days to seconds
$n = 86400 * $n;
// sort the files
$number_deleted = 0;
$pagename = $QW['page'];
$dir_to_use = 'backups/'.$pagename;
$handle = opendir($dir_to_use);
while (false!==($itemname = readdir($handle)))
{
if(!is_dir($dir_to_use.'/'.$itemname) && strcasecmp(substr($itemname, -6), '.qwiki') == 0)
{
$the_path = $dir_to_use.'/'.$itemname;
$age = time() - filemtime($the_path);
if ($age > $n)
{
unlink ($the_path);
$number_deleted++;
}
}
}
closedir($handle);
if ($number_deleted == 0)
{
$message = '<p>No backup files had to be deleted. You may log out now.</p>';
}
else
{
if ($number_deleted == 1)
{
$message = '<p>One backup file was deleted. You may log out now.</p>';
}
else
{
$message = '<p>'.$number_deleted.' backup files were deleted. You may log out now.</p>';
}
}
}
}
/////////////// Delete certain number of backups for all of wiki by age
if (isset($_POST['all_days']) && isset($_POST['all_days_number']))
{
$n = $_POST['all_days_number'];
if (!preg_match("/^([0-9]+)$/",$n))
{
$message = '<p>Please input a positive number, such as 2, 5, etc.</p>';
}
else
{
// convert no. of days to seconds
$n = 86400 * $n;
// get all directories
$handle = opendir(backups);
while (false!==($item_name = readdir($handle)))
{
if($item_name != '.' && $item_name != '..')
{
if(is_dir('backups/'.$item_name))
{
$dir_list[] = 'backups/'.$item_name;
}
}
}
closedir($handle);
$number_deleted = 0;
$total_count = 0;
// start deleting if aged
foreach ($dir_list as $key=>$value)
{
$handle = opendir($value);
while (false!==($itemname = readdir($handle)))
{
if(!is_dir($value.'/'.$itemname) && strcasecmp(substr($itemname, -6), '.qwiki') == 0)
{
$total_count++;
$the_path = $value.'/'.$itemname;
$age = time() - filemtime($the_path);
if ($age > $n)
{
unlink ($the_path);
$number_deleted++;
// to show what was deleted
$list_item = ' '.$itemname . '<br />';
$list_deleted .= $list_item;
}
}
}
closedir($handle);
}
if ($number_deleted == 0)
{
$message = '<p>No backup file from a total of ' . $total_count . ' had to be deleted. You may log out now.</p>';
}
else
{
if ($number_deleted == 1)
{
$message = '<p>One backup file from a total of ' . $total_count .' was deleted:<br /><br />'.$list_deleted.' <br />You may log out now.</p>';
}
else
{
$message = '<p>These '. $number_deleted.' backup files from a total of ' . $total_count . ' were deleted:<br /><br />'.$list_deleted.' <br />You may log out now.</p>';
}
}
}
}
//////////////////// Delete certain number of backups for this document by version
if (isset($_POST['this_versions']) && isset($_POST['this_versions_number']))
{
$n = $_POST['this_versions_number'];
if (!preg_match("/^([0-9]+)$/",$n))
{
$message = '<p>Please input a positive number, such as 2, 5, etc.</p>';
}
else
{
// get the files
$number_deleted = 0;
$pagename = $QW['page'];
$dir_to_use = 'backups/'.$pagename;
$handle = opendir($dir_to_use);
while (false!==($itemname = readdir($handle)))
{
if(!is_dir($dir_to_use.'/'.$itemname) && strcasecmp(substr($itemname, -6), '.qwiki') == 0)
{
$file_list['path'][] = $dir_to_use.'/'.$itemname;
$file_list['age'][] = filemtime($dir_to_use.'/'.$itemname);
}
}
closedir($handle);
// sort the files by age - youngest first
arsort($file_list['age']);
$i=0;
while (list ($key, $val) = each ($file_list['age']))
{
$sorted_list[$i]['path'] = $file_list['path'][$key];
$sorted_list[$i]['age'] = $file_list['age'][$key];
$i++;
}
// get age of latest file that will be deleted
$age_limit = $sorted_list[$n]['age'];
// delete files with age equalling or more than age_limit
foreach ($sorted_list as $key => $value)
{
if (!($sorted_list[$key]['age'] > $age_limit))
{
unlink ($sorted_list[$key]['path']);
$number_deleted++;
}
}
if ($number_deleted == 0)
{
$message = '<p>No backup files had to be deleted. You may log out now.</p>';
}
else
{
if ($number_deleted == 1)
{
$message = '<p>One backup file was deleted. You may log out now.</p>';
}
else
{
$message = '<p>'.$number_deleted.' backup files were deleted. You may log out now.</p>';
}
}
}
}
//////////////////// Delete certain number of backups for entire wiki by version
if (isset($_POST['all_versions']) && isset($_POST['all_versions_number']))
{
$n = $_POST['all_versions_number'];
if (!preg_match("/^([0-9]+)$/",$n))
{
$message = '<p>Please input a positive number, such as 2, 5, etc.</p>';
}
else
{
$number_deleted = 0;
$total_count = 0;
// get all directories
$handle = opendir(backups);
while (false!==($item_name = readdir($handle)))
{
if($item_name != '.' && $item_name != '..')
{
if(is_dir('backups/'.$item_name))
{
$dir_list[] = 'backups/'.$item_name;
}
}
}
closedir($handle);
// get files from all directories
foreach ($dir_list as $key=>$value)
{
$handle2 = opendir($value);
while (false!==($itemname = readdir($handle2)))
{
if(!is_dir($value.'/'.$itemname) && strcasecmp(substr($itemname, -6), '.qwiki') == 0)
{
$total_count++;
$file_list['path'][] = $value.'/'.$itemname;
$file_list['age'][] = filemtime($value.'/'.$itemname);
// sort the files by age - youngest first
arsort($file_list['age']);
$i=0;
while (list ($key2, $value2) = each ($file_list['age']))
{
$sorted_list[$i]['path'] = $file_list['path'][$key2];
$sorted_list[$i]['age'] = $file_list['age'][$key2];
$i++;
}
}
}
// further proceed if targeted backups present
if (count($sorted_list) > $n)
{
// get age of latest file that will be deleted
// e.g., if n=3, this will be 4th youngest file, 1st to 3rd being younger
$age_limit = $sorted_list[$n]['age'];
// make list of files with age equalling or more than age_limit
foreach ($sorted_list as $key3 => $value3)
{
if (!($sorted_list[$key3]['age'] > $age_limit))
{
$to_delete_list[] = $sorted_list[$key3]['path'];
}
}
}
// reset the arrays except to_delete_list
$file_list = array();
$sorted_list = array();
closedir($handle2);
}
// start deleting
$to_delete_list = array_unique($to_delete_list);
foreach ($to_delete_list as $key4 => $value4)
{
if (file_exists($value4))
{
unlink ($value4);
$number_deleted++;
// to show what was deleted
$list_item = ' '.$value4. '<br />';
$list_deleted .= $list_item;
}
}
if ($number_deleted == 0)
{
$message = '<p>No backup file from a total of ' . $total_count . ' had to be deleted. You may log out now.</p>';
}
else
{
if ($number_deleted == 1)
{
$message = '<p>One backup file from a total of ' . $total_count .' was deleted:<br /><br />'.$list_deleted.' <br />You may log out now.</p>';
}
else
{
$message = '<p>These '. $number_deleted.' backup files from a total of ' . $total_count . ' were deleted:<br /><br />'.$list_deleted.' <br />You may log out now.</p>';
}
}
}
}
//////////////////////////////////////// QWTBackupInjectBody
$QW_TEMPLATE['injectBody'] = "QWTBackupInjectBody";
function QWTBackupInjectBody( )
{
global $QW, $QW_CONFIG, $errorHTML, $message, $total_count;
if( $errorHTML ) {echo QWFormatMessage( $errorHTML );}
$pagename = $QW['page'];
$backup_dir = 'backups/'.$pagename.'/';
// output result of actions
echo ('<table summary="none" width="790" ><tr align="center"><td align="center">');
echo ("<p><i>" . $message . "</i></p>");
// output title of document
echo "<p><b>Wiki document</b> - " . htmlspecialchars(QWCleanQwikiPageName( $QW['page'] )) . "</b></p>";
// checks if backup directory exists or not
if (!is_dir($backup_dir))
{ echo "<p>Backups of previous versions of this document do not exist.</p>"; }
// get list of files if it exists
else
{
$handle = opendir($backup_dir);
$list= array();
while (false!==($item = readdir($handle)))
{
// See if it's a qwiki file
if(!is_dir( $item) && strcasecmp(substr($item, -6), '.qwiki') == 0)
{$list[] = $item;}
}
closedir($handle);
// if list empty
$number = count ($list);
if ($number == 0)
{
echo "<p>Backups of previous versions of this document do not exist.</p>";
}
else
{
if ($number ==1)
{
echo "<p>One previous version of this document is available as backup.</p>";
}
else
{
echo "<p>" . $number ." previous versions of this document are available as backups.</p>";
}
echo '<table class="QWInnerSection" summary="list" cellpadding="2">
<tr class="QWInnerSectionTitle">
<td align="left">Filename</td>
<td align="left">Generated - date & time</td>
<td align="left">Size - bytes</td>
<td align="left">Action</td>
</tr>';
foreach ($list as $item)
{
$path = $backup_dir.'/'.$item;
$size = filesize ($path);
$creation_time = filemtime ($path);
$time = date ("F d, Y: Hi", $creation_time);
echo '
<tr>
<td align="left">
' . htmlspecialchars($item) . '
</td>
<td align="left">
' . $time . ' h
</td>
<td align="left">
' . $size . '
</td>
<td align="left">
<a href="show_backup.php?page='.htmlspecialchars($item).'" onclick="window.open(this.href); return false;" onkeypress="window.open(this.href); return false;">
View
</a>
|
<a href="show_source.php?page='.htmlspecialchars($item).'" onclick="window.open(this.href); return false;" onkeypress="window.open(this.href); return false;">
Code
</a>
|
<a href="javascript:confirmdelete(\'backups.php?page='.htmlspecialchars($QW['page']).'&delete_page='.htmlspecialchars($item).'\')">
Delete
</a>
|
<a href="javascript:confirmdelete(\'backups.php?page='.htmlspecialchars($QW['page']).'&restore_page='.htmlspecialchars($item).'\')">
Restore
</a>
</td>
</tr>
';
}
echo "</table>";
}
}
// code form for various options
echo ('<form class="QWForm" method="post" action="backups.php?page='.htmlspecialchars($QW['page']).'">
<input type="hidden" name="debug" value="'.htmlspecialchars($QW['requestDebug']).'" />
<input type="hidden" name="help" value="'.htmlspecialchars($QW['requestHelp']).'" />');
// show document-specific options if backups present
if (!empty($number))
{echo ('
<p>Delete all backups for this document -
<input type="submit" name="this_all" action="backups.php?page='.htmlspecialchars($QW['page']).'" value="Delete" onclick="return confirmsubmit()" />
<br />
Delete backups older than these many days -
<input type="text" name="this_days_number" maxlength="3" size="3" value="30">
<input type="submit" name="this_days" action="backups.php?page='.htmlspecialchars($QW['page']).'" value="Delete" onclick="return confirmsubmit()" />
<br />
Delete backups older than these many versions -
<input type="text" name="this_versions_number" maxlength="3" size="3" value="3">
<input type="submit" name="this_versions" action="backups.php?page='.htmlspecialchars($QW['page']).'" value="Delete" onclick="return confirmsubmit()" />
</p>');
}
// show options for entire wiki
echo ('
<p><strong>Entire wiki</strong></p>
<p>Delete all backups for entire wiki -
<input type="submit" name="all_all" action="backups.php?page='.htmlspecialchars($QW['page']).'" value="Delete" onclick="return confirmsubmit()" />
<br />
Delete backups older than these many days for entire wiki -
<input type="text" name="all_days_number" maxlength="3" size="3" value="30">
<input type="submit" name="all_days" action="backups.php?page='.htmlspecialchars($QW['page']).'" value="Delete" onclick="return confirmsubmit()" />
<br />
Delete backups older than these many versions for entire wiki -
<input type="text" name="all_versions_number" maxlength="3" size="3" value="3">
<input type="submit" name="all_versions" action="backups.php?page='.htmlspecialchars($QW['page']).'" value="Delete" onclick="return confirmsubmit()" />
</p>
</form>');
// help
QWFormatHelp( "Everytime a wiki document is edited, a backup of the older version is generated. These backups can accummulate to a large number over time. To trim them, a number of options are provided here. Before deleting a backup, its contents can be viewed as formatted (view) or without formatting (code). The latter allows easy copying and pasting. The current wiki file may be restored with a specific backup file (restore). Note that backups contain only the text and not any attached image files, etc. Restoring with a backup will therefore not restore any attached files that were deleted. Similarly, when viewing a backup file, attached files will not be displayed.", "backups.php?page=".htmlspecialchars($QW['page']) );
echo ('</td></tr></table>');
}
//////////////////////////////////////////// QWTBackupFormatCommandList
$QW_TEMPLATE['commandList'] = "QWTBackupFormatCommandList";
function QWTBackupFormatCommandList( )
{global $QW, $QW_CONFIG;
if($QW['userIsAuthenticated']){$commandList[]='<a href="logout.php?page='.htmlspecialchars($QW['page']).$QW['URLSuffix'].'">Admin logout</a>';}
else{$commandList[]='<a href="login.php?page='.htmlspecialchars($QW['page']).$QW['URLSuffix'].'">Admin login</a>';}
$commandList[] = '<a href="index.php?page='.htmlspecialchars($QW['page']).$QW['URLSuffix'].'">Back to '.htmlspecialchars(QWCleanQwikiPageName( $QW['page'] )) .'</a>';
if($QW['requestFrom']){$fromSuffix="&from=".htmlspecialchars($QW['requestFrom']);}
else{$fromSuffix="";}
return $commandList;
}
/////////////////////////////////////////// Include the template
include( $QW_CONFIG['templateFilename'] );
// Output debugging, if requested
if( $QW_CONFIG['enableDebugging'] && $QW['requestDebug'] ) echo QWFormatDebug( );