<?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( );