updraftplus
Last commit date
includes
13 years ago
methods
13 years ago
example-decrypt.php
14 years ago
readme.txt
13 years ago
updraftplus.php
13 years ago
updraftplus.php
1870 lines
| 1 | <?php |
| 2 | /* |
| 3 | Plugin Name: UpdraftPlus - Backup/Restore |
| 4 | Plugin URI: http://wordpress.org/extend/plugins/updraftplus |
| 5 | Description: Uploads, themes, plugins, and your DB can be automatically backed up to Amazon S3, Google Drive, FTP, or emailed, on separate schedules. |
| 6 | Author: David Anderson. |
| 7 | Version: 1.1.13 |
| 8 | Donate link: http://david.dw-perspective.org.uk/donate |
| 9 | License: GPLv3 or later |
| 10 | Author URI: http://wordshell.net |
| 11 | */ |
| 12 | |
| 13 | /* |
| 14 | TODO |
| 15 | //Add DropBox and Microsoft Skydrive support |
| 16 | //improve error reporting / pretty up return messages in admin area |
| 17 | //?? On 'backup now', open up a Lightbox, count down 5 seconds, then start examining the log file (if it can be found) |
| 18 | |
| 19 | Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed |
| 20 | // Does not delete old custom directories upon a restore? |
| 21 | // Re-do making of zip files to allow resumption (every x files, store the state in a transient) |
| 22 | */ |
| 23 | |
| 24 | /* Portions copyright 2010 Paul Kehrer |
| 25 | Portions copyright 2011-12 David Anderson |
| 26 | Other portions copyright as indicated authors in the relevant files |
| 27 | Particular thanks to Sorin Iclanzan, author of the "Backup" plugin, from which much Google Drive code was taken under the GPLv3+ |
| 28 | |
| 29 | This program is free software; you can redistribute it and/or modify |
| 30 | it under the terms of the GNU General Public License as published by |
| 31 | the Free Software Foundation; either version 3 of the License, or |
| 32 | (at your option) any later version. |
| 33 | |
| 34 | This program is distributed in the hope that it will be useful, |
| 35 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 36 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 37 | GNU General Public License for more details. |
| 38 | |
| 39 | You should have received a copy of the GNU General Public License |
| 40 | along with this program; if not, write to the Free Software |
| 41 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 42 | */ |
| 43 | |
| 44 | // 15 minutes |
| 45 | @set_time_limit(900); |
| 46 | |
| 47 | if (!isset($updraftplus)) $updraftplus = new UpdraftPlus(); |
| 48 | |
| 49 | if (!$updraftplus->memory_check(192)) { |
| 50 | # TODO: Better solution is to split the backup set into manageable chunks based on this limit |
| 51 | @ini_set('memory_limit', '192M'); //up the memory limit for large backup files |
| 52 | } |
| 53 | |
| 54 | define('UPDRAFTPLUS_DIR', dirname(__FILE__)); |
| 55 | define('UPDRAFT_DEFAULT_OTHERS_EXCLUDE','upgrade,cache,updraft,index.php'); |
| 56 | |
| 57 | class UpdraftPlus { |
| 58 | |
| 59 | var $version = '1.1.13'; |
| 60 | |
| 61 | // Choices will be shown in the admin menu in the order used here |
| 62 | var $backup_methods = array ( |
| 63 | "s3" => "Amazon S3", |
| 64 | "googledrive" => "Google Drive", |
| 65 | "ftp" => "FTP", |
| 66 | "email" => "Email" |
| 67 | ); |
| 68 | |
| 69 | var $dbhandle; |
| 70 | var $errors = array(); |
| 71 | var $nonce; |
| 72 | var $cronrun_type = "none"; |
| 73 | var $logfile_name = ""; |
| 74 | var $logfile_handle = false; |
| 75 | var $backup_time; |
| 76 | |
| 77 | function __construct() { |
| 78 | // Initialisation actions - takes place on plugin load |
| 79 | # Create admin page |
| 80 | add_action('admin_menu', array($this,'add_admin_pages')); |
| 81 | add_action('admin_init', array($this,'admin_init')); |
| 82 | add_action('updraft_backup', array($this,'backup_files')); |
| 83 | add_action('updraft_backup_database', array($this,'backup_database')); |
| 84 | # backup_all is used by the manual "Backup Now" button |
| 85 | add_action('updraft_backup_all', array($this,'backup_all')); |
| 86 | # this is our runs-after-backup event, whose purpose is to see if it succeeded or failed, and resume/mom-up etc. |
| 87 | add_action('updraft_backup_resume', array($this,'backup_resume')); |
| 88 | add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup')); |
| 89 | # http://codex.wordpress.org/Plugin_API/Filter_Reference/cron_schedules |
| 90 | add_filter('cron_schedules', array($this,'modify_cron_schedules')); |
| 91 | add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2); |
| 92 | add_action('init', array($this, 'handle_url_actions')); |
| 93 | } |
| 94 | |
| 95 | // Handle actions passed on to method plugins; e.g. Google OAuth 2.0 - ?page=updraftplus&action=updraftmethod-googledrive-auth |
| 96 | // Also handle action=downloadlog |
| 97 | function handle_url_actions() { |
| 98 | // First, basic security check: must be an admin page, with ability to manage options, with the right parameters |
| 99 | if ( is_admin() && current_user_can('manage_options') && isset( $_GET['page'] ) && $_GET['page'] == 'updraftplus' && isset($_GET['action']) ) { |
| 100 | if (preg_match("/^updraftmethod-([a-z]+)-([a-z]+)$/", $_GET['action'], $matches) && file_exists(UPDRAFTPLUS_DIR.'/methods/'.$matches[1].'.php')) { |
| 101 | $method = $matches[1]; |
| 102 | require_once(UPDRAFTPLUS_DIR.'/methods/'.$method.'.php'); |
| 103 | $call_class = "UpdraftPlus_BackupModule_".$method; |
| 104 | $call_method = "action_".$matches[2]; |
| 105 | if (method_exists($call_class, $call_method)) call_user_func(array($call_class,$call_method)); |
| 106 | } elseif ($_GET['action'] == 'downloadlog' && isset($_GET['updraftplus_backup_nonce']) && preg_match("/^[0-9a-f]{12}$/",$_GET['updraftplus_backup_nonce'])) { |
| 107 | $updraft_dir = $this->backups_dir_location(); |
| 108 | $log_file = $updraft_dir.'/log.'.$_GET['updraftplus_backup_nonce'].'.txt'; |
| 109 | if (is_readable($log_file)) { |
| 110 | header('Content-type: text/plain'); |
| 111 | readfile($log_file); |
| 112 | exit; |
| 113 | } else { |
| 114 | add_action('admin_notices', array($this,'show_admin_warning_unreadablelog') ); |
| 115 | } |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | # Adds the settings link under the plugin on the plugin screen. |
| 121 | function plugin_action_links($links, $file) { |
| 122 | if ($file == plugin_basename(__FILE__)){ |
| 123 | $settings_link = '<a href="'.site_url().'/wp-admin/options-general.php?page=updraftplus">'.__("Settings", "UpdraftPlus").'</a>'; |
| 124 | array_unshift($links, $settings_link); |
| 125 | $settings_link = '<a href="http://david.dw-perspective.org.uk/donate">'.__("Donate","UpdraftPlus").'</a>'; |
| 126 | array_unshift($links, $settings_link); |
| 127 | } |
| 128 | return $links; |
| 129 | } |
| 130 | |
| 131 | function backup_time_nonce() { |
| 132 | $this->backup_time = time(); |
| 133 | $nonce = substr(md5(time().rand()), 20); |
| 134 | $this->nonce = $nonce; |
| 135 | // Short-lived, as we only use this for detecting a race condition |
| 136 | set_transient("updraftplus_runtype_$nonce", $this->cronrun_type, 300); |
| 137 | } |
| 138 | |
| 139 | # Logs the given line, adding date stamp and newline |
| 140 | function log($line) { |
| 141 | if ($this->logfile_handle) fwrite($this->logfile_handle,date('r')." ".$line."\n"); |
| 142 | } |
| 143 | |
| 144 | function backup_resume($resumption_no) { |
| 145 | @ignore_user_abort(true); |
| 146 | // This is scheduled for 5 minutes after a backup job starts |
| 147 | $bnonce = get_transient('updraftplus_backup_job_nonce'); |
| 148 | if (!$bnonce) return; |
| 149 | $this->nonce = $bnonce; |
| 150 | $this->logfile_open($bnonce); |
| 151 | $this->log("Resume backup ($resumption_no): begin run (will check for any remaining jobs)"); |
| 152 | $btime = get_transient('updraftplus_backup_job_time'); |
| 153 | if (!$btime) { |
| 154 | $this->log("Did not find stored time setting - aborting"); |
| 155 | return; |
| 156 | } |
| 157 | $this->log("Resuming backup: resumption=$resumption_no, nonce=$bnonce, begun at=$btime"); |
| 158 | // Schedule again, to run in 5 minutes again, in case we again fail |
| 159 | $resume_delay = 300; |
| 160 | // A different argument than before is needed otherwise the event is ignored |
| 161 | $next_resumption = $resumption_no+1; |
| 162 | if ($next_resumption < 10) { |
| 163 | wp_schedule_single_event(time()+$resume_delay, 'updraft_backup_resume' ,array($next_resumption)); |
| 164 | } else { |
| 165 | $this->log("This is our tenth attempt - will not try again"); |
| 166 | } |
| 167 | $this->backup_time = $btime; |
| 168 | |
| 169 | // Returns an array, most recent first, of backup sets |
| 170 | $backup_history = $this->get_backup_history(); |
| 171 | if (!isset($backup_history[$btime])) $this->log("Error: Could not find a record in the database of a backup with this timestamp"); |
| 172 | |
| 173 | $our_files=$backup_history[$btime]; |
| 174 | $undone_files = array(); |
| 175 | |
| 176 | // Potentially encrypt the database if it is not already |
| 177 | if (isset($our_files['db']) && !preg_match("/\.crypt$/", $our_files['db'])) { |
| 178 | $our_files['db'] = $this->encrypt_file($our_files['db']); |
| 179 | $this->save_backup_history($our_files); |
| 180 | } |
| 181 | |
| 182 | foreach ($our_files as $key => $file) { |
| 183 | |
| 184 | $hash = md5($file); |
| 185 | $fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 186 | if (get_transient('updraft_'.$hash) === "yes") { |
| 187 | $this->log("$file: $key: This file has been successfully uploaded in the last 3 hours"); |
| 188 | } elseif (is_file($fullpath)) { |
| 189 | $this->log("$file: $key: This file has NOT been successfully uploaded in the last 3 hours: will retry"); |
| 190 | $undone_files[$key] = $file; |
| 191 | } else { |
| 192 | $this->log("$file: Note: This file was not marked as successfully uploaded, but does not exist on the local filesystem"); |
| 193 | $this->uploaded_file($file); |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | if (count($undone_files) == 0) { |
| 198 | $this->log("There were no files that needed uploading; backup job is finished"); |
| 199 | return; |
| 200 | } |
| 201 | |
| 202 | $this->log("Requesting backup of the files that were not successfully uploaded"); |
| 203 | $this->cloud_backup($undone_files); |
| 204 | $this->cloud_backup_finish($undone_files); |
| 205 | |
| 206 | $this->log("Resume backup ($resumption_no): finish run"); |
| 207 | |
| 208 | $this->backup_finish($next_resumption, true); |
| 209 | |
| 210 | } |
| 211 | |
| 212 | function backup_all() { |
| 213 | $this->backup(true,true); |
| 214 | } |
| 215 | |
| 216 | function backup_files() { |
| 217 | # Note that the "false" for database gets over-ridden automatically if they turn out to have the same schedules |
| 218 | $this->cronrun_type = "files"; |
| 219 | $this->backup(true,false); |
| 220 | } |
| 221 | |
| 222 | function backup_database() { |
| 223 | # Note that nothing will happen if the file backup had the same schedule |
| 224 | $this->cronrun_type = "database"; |
| 225 | $this->backup(false,true); |
| 226 | } |
| 227 | |
| 228 | function logfile_open($nonce) { |
| 229 | //set log file name and open log file |
| 230 | $updraft_dir = $this->backups_dir_location(); |
| 231 | $this->logfile_name = $updraft_dir. "/log.$nonce.txt"; |
| 232 | // Use append mode in case it already exists |
| 233 | $this->logfile_handle = fopen($this->logfile_name, 'a'); |
| 234 | } |
| 235 | |
| 236 | function check_backup_race( $to_delete = false ) { |
| 237 | // Avoid caching |
| 238 | global $wpdb; |
| 239 | $row = $wpdb->get_row($wpdb->prepare("SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", "_transient_updraftplus_backup_job_nonce")); |
| 240 | $cur_trans = ( is_object( $row ) ) ? $row->option_value : ""; |
| 241 | // Check if another backup job ID is stored in the transient |
| 242 | if ($cur_trans != "" && $cur_trans != $this->nonce) { |
| 243 | // Also check if that job is of the same type as ours, as two cron jobs could legitimately fire at the same time |
| 244 | $otherjob_crontype = get_transient("updraftplus_runtype_".$cur_trans); |
| 245 | // $this->cronrun_type should be "files", "database" or blank (if we were not run via a cron job) |
| 246 | if ($otherjob_crontype == $this->cronrun_type) { |
| 247 | $this->log("Another backup job ($cur_trans) of the same type ($otherjob_crontype) appears to now be running - terminating our run (apparent race condition)"); |
| 248 | $bdir = $this->backups_dir_location(); |
| 249 | if (is_array($to_delete)) { |
| 250 | foreach ($to_delete as $key => $file) { |
| 251 | if (is_file($bdir.'/'.$file)) { |
| 252 | $this->log("Deleting the file we created: ".$file); |
| 253 | @unlink($bdir.'/'.$file); |
| 254 | } |
| 255 | } |
| 256 | } |
| 257 | exit; |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | function backup($backup_files, $backup_database) { |
| 263 | |
| 264 | @ignore_user_abort(true); |
| 265 | //generate backup information |
| 266 | $this->backup_time_nonce(); |
| 267 | |
| 268 | $this->logfile_open($this->nonce); |
| 269 | |
| 270 | // Log some information that may be helpful |
| 271 | global $wp_version; |
| 272 | $this->log("PHP version: ".phpversion()." (".php_uname().") WordPress version: ".$wp_version." Updraft version: ".$this->version." PHP Max Execution Time: ".ini_get("max_execution_time")." Backup files: $backup_files (schedule: ".get_option('updraft_interval','unset').") Backup DB: $backup_database (schedule: ".get_option('updraft_interval_database','unset').")"); |
| 273 | |
| 274 | # If the files and database schedules are the same, and if this the file one, then we rope in database too. |
| 275 | # On the other hand, if the schedules were the same and this was the database run, then there is nothing to do. |
| 276 | if (get_option('updraft_interval') == get_option('updraft_interval_database') || get_option('updraft_interval_database','xyz') == 'xyz' ) { |
| 277 | $backup_database = ($backup_files == true) ? true : false; |
| 278 | } |
| 279 | |
| 280 | $this->log("Processed schedules. Tasks now: Backup files: $backup_files Backup DB: $backup_database"); |
| 281 | |
| 282 | $clear_nonce_transient = false; |
| 283 | |
| 284 | # Possibly now nothing is to be done, except to close the log file |
| 285 | if ($backup_files || $backup_database) { |
| 286 | |
| 287 | $clear_nonce_transient = true; |
| 288 | |
| 289 | // Do not set the transient or schedule the resume event until now, when we know there is something to do - otherwise 'vacatated' runs (when the database is on the same schedule as the files, and they get combined, leading to an empty run) can over-write the resume event and prevent resumption (because it is 'successful' - there was nothing to do). |
| 290 | // If we don't finish in 3 hours, then we won't finish |
| 291 | // This transient indicates the identity of the current backup job (which can be used to find the files and logfile) |
| 292 | set_transient("updraftplus_backup_job_nonce",$this->nonce,3600*3); |
| 293 | set_transient("updraftplus_backup_job_time",$this->backup_time,3600*3); |
| 294 | // Schedule the even to run later, which checks on success and can resume the backup |
| 295 | // We save the time to a variable because it is needed for un-scheduling |
| 296 | $resume_delay = 300; |
| 297 | wp_schedule_single_event(time()+$resume_delay, 'updraft_backup_resume', array(1)); |
| 298 | $this->log("In case we run out of time, scheduled a resumption at: $resume_delay seconds from now"); |
| 299 | |
| 300 | $backup_contains = ""; |
| 301 | |
| 302 | $backup_array = array(); |
| 303 | |
| 304 | $this->check_backup_race(); |
| 305 | |
| 306 | //backup directories and return a numerically indexed array of file paths to the backup files |
| 307 | if ($backup_files) { |
| 308 | $this->log("Beginning backup of directories"); |
| 309 | $backup_array = $this->backup_dirs(); |
| 310 | $backup_contains = "Files only (no database)"; |
| 311 | } |
| 312 | |
| 313 | $this->check_backup_race($backup_array); |
| 314 | |
| 315 | //backup DB and return string of file path |
| 316 | if ($backup_database) { |
| 317 | $this->log("Beginning backup of database"); |
| 318 | $db_backup = $this->backup_db(); |
| 319 | // add db path to rest of files |
| 320 | if(is_array($backup_array)) $backup_array['db'] = $db_backup; |
| 321 | $backup_contains = ($backup_files) ? "Files and database" : "Database only (no files)"; |
| 322 | } |
| 323 | |
| 324 | $this->check_backup_race($backup_array); |
| 325 | set_transient("updraftplus_backupcontains", $backup_contains, 3600*3); |
| 326 | |
| 327 | //save this to our history so we can track backups for the retain feature |
| 328 | $this->log("Saving backup history"); |
| 329 | // This is done before cloud despatch, because we want a record of what *should* be in the backup. Whether it actually makes it there or not is not yet known. |
| 330 | $this->save_backup_history($backup_array); |
| 331 | |
| 332 | // Now encrypt the database, and re-save |
| 333 | if ($backup_database && isset($backup_array['db'])) { |
| 334 | $backup_array['db'] = $this->encrypt_file($backup_array['db']); |
| 335 | // Re-save with the possibly-altered database filename |
| 336 | $this->save_backup_history($backup_array); |
| 337 | } |
| 338 | |
| 339 | //cloud operations (S3,Google Drive,FTP,email,nothing) |
| 340 | //this also calls the retain (prune) feature at the end (done in this method to reuse existing cloud connections) |
| 341 | if(is_array($backup_array) && count($backup_array) >0) { |
| 342 | $this->log("Beginning dispatch of backup to remote"); |
| 343 | $this->cloud_backup($backup_array); |
| 344 | } |
| 345 | |
| 346 | //save the last backup info, including errors, if any |
| 347 | $this->log("Saving last backup information into WordPress db"); |
| 348 | $this->save_last_backup($backup_array); |
| 349 | |
| 350 | // Send the email |
| 351 | $this->cloud_backup_finish($backup_array, $clear_nonce_transient); |
| 352 | |
| 353 | } |
| 354 | |
| 355 | // Close log file; delete and also delete transients if not in debug mode |
| 356 | $this->backup_finish(1, $clear_nonce_transient); |
| 357 | |
| 358 | } |
| 359 | |
| 360 | // Encrypts the file if the option is set; returns the basename of the file (according to whether it was encrypted or nto) |
| 361 | function encrypt_file($file) { |
| 362 | $encryption = get_option('updraft_encryptionphrase'); |
| 363 | if (strlen($encryption) > 0) { |
| 364 | $this->log("$file: applying encryption"); |
| 365 | $encryption_error = 0; |
| 366 | require_once(UPDRAFTPLUS_DIR.'/includes/Rijndael.php'); |
| 367 | $rijndael = new Crypt_Rijndael(); |
| 368 | $rijndael->setKey($encryption); |
| 369 | $updraft_dir = $this->backups_dir_location(); |
| 370 | $in_handle = @fopen($updraft_dir.'/'.$file,'r'); |
| 371 | $buffer = ""; |
| 372 | while (!feof ($in_handle)) { |
| 373 | $buffer .= fread($in_handle, 16384); |
| 374 | } |
| 375 | fclose ($in_handle); |
| 376 | $out_handle = @fopen($updraft_dir.'/'.$file.'.crypt','w'); |
| 377 | if (!fwrite($out_handle, $rijndael->encrypt($buffer))) {$encryption_error = 1;} |
| 378 | fclose ($out_handle); |
| 379 | if (0 == $encryption_error) { |
| 380 | $this->log("$file: encryption successful"); |
| 381 | # Delete unencrypted file |
| 382 | @unlink($updraft_dir.'/'.$file); |
| 383 | return basename($file.'.crypt'); |
| 384 | } else { |
| 385 | $this->log("Encryption error occurred when encrypting database. Encryption aborted."); |
| 386 | $this->error("Encryption error occurred when encrypting database. Encryption aborted."); |
| 387 | return basename($file); |
| 388 | } |
| 389 | } else { |
| 390 | return basename($file); |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | function backup_finish($cancel_event, $clear_nonce_transient) { |
| 395 | |
| 396 | // In fact, leaving the hook to run (if debug is set) is harmless, as the resume job should only do tasks that were left unfinished, which at this stage is none. |
| 397 | if (empty($this->errors)) { |
| 398 | if ($clear_nonce_transient) { |
| 399 | $this->log("There were no errors in the uploads, so the 'resume' event is being unscheduled"); |
| 400 | wp_clear_scheduled_hook('updraft_backup_resume', array($cancel_event)); |
| 401 | delete_transient("updraftplus_backup_job_nonce"); |
| 402 | delete_transient("updraftplus_backup_job_time"); |
| 403 | } |
| 404 | } else { |
| 405 | $this->log("There were errors in the uploads, so the 'resume' event is remaining scheduled"); |
| 406 | } |
| 407 | |
| 408 | @fclose($this->logfile_handle); |
| 409 | |
| 410 | // Don't delete the log file now; delete it upon rotation |
| 411 | //if (!get_option('updraft_debug_mode')) @unlink($this->logfile_name); |
| 412 | |
| 413 | } |
| 414 | |
| 415 | function cloud_backup_finish($backup_array) { |
| 416 | |
| 417 | // Send the results email if requested |
| 418 | if(get_option('updraft_email') != "" && get_option('updraft_service') != 'email') $this->send_results_email(); |
| 419 | |
| 420 | } |
| 421 | |
| 422 | function send_results_email() { |
| 423 | |
| 424 | $sendmail_to = get_option('updraft_email'); |
| 425 | |
| 426 | $this->log("Sending email report to: ".$sendmail_to); |
| 427 | |
| 428 | $append_log = (get_option('updraft_debug_mode') && $this->logfile_name != "") ? "\r\nLog contents:\r\n".file_get_contents($this->logfile_name) : "" ; |
| 429 | |
| 430 | wp_mail($sendmail_to,'Backed up: '.get_bloginfo('name').' (UpdraftPlus '.$this->version.') '.date('Y-m-d H:i',time()),'Site: '.site_url()."\r\nUpdraftPlus WordPress backup is complete.\r\nBackup contains: ".get_transient("updraftplus_backupcontains")."\r\n\r\n".$this->wordshell_random_advert(0)."\r\n".$append_log); |
| 431 | |
| 432 | } |
| 433 | |
| 434 | function save_last_backup($backup_array) { |
| 435 | $success = (empty($this->errors)) ? 1 : 0; |
| 436 | |
| 437 | $last_backup = array('backup_time'=>$this->backup_time, 'backup_array'=>$backup_array, 'success'=>$success, 'errors'=>$this->errors, 'backup_nonce' => $this->nonce); |
| 438 | |
| 439 | update_option('updraft_last_backup', $last_backup); |
| 440 | } |
| 441 | |
| 442 | // This should be called whenever a file is successfully uploaded |
| 443 | function uploaded_file($file, $id = false) { |
| 444 | # We take an MD5 hash because set_transient wants a name of 45 characters or less |
| 445 | $hash = md5($file); |
| 446 | $this->log("$file: $hash: recording as successfully uploaded"); |
| 447 | set_transient("updraft_".$hash, "yes", 3600*4); |
| 448 | if ($id) { |
| 449 | $ids = get_option('updraft_file_ids', array() ); |
| 450 | $ids[$file] = $id; |
| 451 | update_option('updraft_file_ids',$ids); |
| 452 | $this->log("Stored file<->id correlation in database ($file <-> $id)"); |
| 453 | } |
| 454 | // Delete local files if the option is set |
| 455 | $this->delete_local($file); |
| 456 | |
| 457 | } |
| 458 | |
| 459 | // Dispatch to the relevant function |
| 460 | function cloud_backup($backup_array) { |
| 461 | $service = get_option('updraft_service'); |
| 462 | $this->log("Cloud backup selection: ".$service); |
| 463 | @set_time_limit(900); |
| 464 | |
| 465 | $method_include = UPDRAFTPLUS_DIR.'/methods/'.$service.'.php'; |
| 466 | if (file_exists($method_include)) require_once($method_include); |
| 467 | |
| 468 | $objname = "UpdraftPlus_BackupModule_${service}"; |
| 469 | if (method_exists($objname, "backup")) { |
| 470 | // New style - external, allowing more plugability |
| 471 | $remote_obj = new $objname; |
| 472 | $remote_obj->backup($backup_array); |
| 473 | } else { |
| 474 | $this->prune_retained_backups("local", null, null); |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | function prune_file($updraft_service, $dofile, $method_object = null, $object_passback = null ) { |
| 479 | $this->log("Delete this file: $dofile, service=$updraft_service"); |
| 480 | $fullpath = trailingslashit(get_option('updraft_dir')).$dofile; |
| 481 | // delete it if it's locally available |
| 482 | if (file_exists($fullpath)) { |
| 483 | $this->log("Deleting local copy ($fullpath)"); |
| 484 | @unlink($fullpath); |
| 485 | } |
| 486 | |
| 487 | // Despatch to the particular method's deletion routine |
| 488 | if (!is_null($method_object)) $method_object->delete($dofile, $object_passback); |
| 489 | } |
| 490 | |
| 491 | // Carries out retain behaviour. Pass in a valid S3 or FTP object and path if relevant. |
| 492 | function prune_retained_backups($updraft_service, $backup_method_object = null, $backup_passback = null) { |
| 493 | |
| 494 | $this->log("Retain: beginning examination of existing backup sets"); |
| 495 | |
| 496 | // Number of backups to retain |
| 497 | $updraft_retain = get_option('updraft_retain', 1); |
| 498 | $retain = (is_numeric($updraft_retain)) ? $updraft_retain : 1; |
| 499 | $this->log("Retain: user setting: number to retain = $retain"); |
| 500 | |
| 501 | // Returns an array, most recent first, of backup sets |
| 502 | $backup_history = $this->get_backup_history(); |
| 503 | $db_backups_found = 0; |
| 504 | $file_backups_found = 0; |
| 505 | $this->log("Number of backup sets in history: ".count($backup_history)); |
| 506 | |
| 507 | foreach ($backup_history as $backup_datestamp => $backup_to_examine) { |
| 508 | // $backup_to_examine is an array of file names, keyed on db/plugins/themes/uploads |
| 509 | // The new backup_history array is saved afterwards, so remember to unset the ones that are to be deleted |
| 510 | $this->log("Examining backup set with datestamp: $backup_datestamp"); |
| 511 | |
| 512 | if (isset($backup_to_examine['db'])) { |
| 513 | $db_backups_found++; |
| 514 | $this->log("$backup_datestamp: this set includes a database (".$backup_to_examine['db']."); db count is now $db_backups_found"); |
| 515 | if ($db_backups_found > $retain) { |
| 516 | $this->log("$backup_datestamp: over retain limit; will delete this database"); |
| 517 | $dofile = $backup_to_examine['db']; |
| 518 | if (!empty($dofile)) $this->prune_file($updraft_service, $dofile, $backup_method_object, $backup_passback); |
| 519 | unset($backup_to_examine['db']); |
| 520 | } |
| 521 | } |
| 522 | if (isset($backup_to_examine['plugins']) || isset($backup_to_examine['themes']) || isset($backup_to_examine['uploads']) || isset($backup_to_examine['others'])) { |
| 523 | $file_backups_found++; |
| 524 | $this->log("$backup_datestamp: this set includes files; fileset count is now $file_backups_found"); |
| 525 | if ($file_backups_found > $retain) { |
| 526 | $this->log("$backup_datestamp: over retain limit; will delete this file set"); |
| 527 | $file = isset($backup_to_examine['plugins']) ? $backup_to_examine['plugins'] : ""; |
| 528 | $file2 = isset($backup_to_examine['themes']) ? $backup_to_examine['themes'] : ""; |
| 529 | $file3 = isset($backup_to_examine['uploads']) ? $backup_to_examine['uploads'] : ""; |
| 530 | $file4 = isset($backup_to_examine['others']) ? $backup_to_examine['others'] : ""; |
| 531 | foreach (array($file, $file2, $file3, $file4) as $dofile) { |
| 532 | if (!empty($dofile)) $this->prune_file($updraft_service, $dofile, $backup_method_object, $backup_passback); |
| 533 | } |
| 534 | unset($backup_to_examine['plugins']); |
| 535 | unset($backup_to_examine['themes']); |
| 536 | unset($backup_to_examine['uploads']); |
| 537 | unset($backup_to_examine['others']); |
| 538 | } |
| 539 | } |
| 540 | // Delete backup set completely if empty, o/w just remove DB |
| 541 | if (count($backup_to_examine) == 0 || (count($backup_to_examine) == 1 && isset($backup_to_examine['nonce']))) { |
| 542 | $this->log("$backup_datestamp: this backup set is now empty; will remove from history"); |
| 543 | unset($backup_history[$backup_datestamp]); |
| 544 | if (isset($backup_to_examine['nonce'])) { |
| 545 | $fullpath = trailingslashit(get_option('updraft_dir')).'log.'.$backup_to_examine['nonce'].'.txt'; |
| 546 | if (is_file($fullpath)) { |
| 547 | $this->log("$backup_datestamp: deleting log file (log.".$backup_to_examine['nonce'].".txt)"); |
| 548 | @unlink($fullpath); |
| 549 | } else { |
| 550 | $this->log("$backup_datestamp: corresponding log file not found - must have already been deleted"); |
| 551 | } |
| 552 | } else { |
| 553 | $this->log("$backup_datestamp: no nonce record found in the backup set, so cannot delete any remaining log file"); |
| 554 | } |
| 555 | } else { |
| 556 | $this->log("$backup_datestamp: this backup set remains non-empty; will retain in history"); |
| 557 | $backup_history[$backup_datestamp] = $backup_to_examine; |
| 558 | } |
| 559 | } |
| 560 | $this->log("Retain: saving new backup history (sets now: ".count($backup_history).") and finishing retain operation"); |
| 561 | update_option('updraft_backup_history',$backup_history); |
| 562 | } |
| 563 | |
| 564 | function delete_local($file) { |
| 565 | if(get_option('updraft_delete_local')) { |
| 566 | $this->log("Deleting local file: $file"); |
| 567 | //need error checking so we don't delete what isn't successfully uploaded? |
| 568 | $fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 569 | return unlink($fullpath); |
| 570 | } |
| 571 | return true; |
| 572 | } |
| 573 | |
| 574 | function backup_dirs() { |
| 575 | if(!$this->backup_time) $this->backup_time_nonce(); |
| 576 | $wp_themes_dir = WP_CONTENT_DIR.'/themes'; |
| 577 | $wp_upload_dir = wp_upload_dir(); |
| 578 | $wp_upload_dir = $wp_upload_dir['basedir']; |
| 579 | $wp_plugins_dir = WP_PLUGIN_DIR; |
| 580 | |
| 581 | if(!class_exists('PclZip')) require_once(ABSPATH.'/wp-admin/includes/class-pclzip.php'); |
| 582 | |
| 583 | $updraft_dir = $this->backups_dir_location(); |
| 584 | if(!is_writable($updraft_dir)) $this->error('Backup directory is not writable, or does not exist.','fatal'); |
| 585 | |
| 586 | //get the blog name and rip out all non-alphanumeric chars other than _ |
| 587 | $blog_name = str_replace(' ','_',get_bloginfo()); |
| 588 | $blog_name = preg_replace('/[^A-Za-z0-9_]/','', $blog_name); |
| 589 | if(!$blog_name) $blog_name = 'non_alpha_name'; |
| 590 | |
| 591 | $backup_file_base = $updraft_dir.'/backup_'.date('Y-m-d-Hi',$this->backup_time).'_'.$blog_name.'_'.$this->nonce; |
| 592 | |
| 593 | $backup_array = array(); |
| 594 | |
| 595 | # Plugins |
| 596 | @set_time_limit(900); |
| 597 | if (get_option('updraft_include_plugins', true)) { |
| 598 | $this->log("Beginning backup of plugins"); |
| 599 | $full_path = $backup_file_base.'-plugins.zip'; |
| 600 | $plugins = new PclZip($full_path); |
| 601 | # The paths in the zip should then begin with 'plugins', having removed WP_CONTENT_DIR from the front |
| 602 | if (!$plugins->create($wp_plugins_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) { |
| 603 | $this->error('Could not create plugins zip. Error was '.$php_errmsg,'fatal'); |
| 604 | $this->log('ERROR: PclZip failure: Could not create plugins zip'); |
| 605 | } else { |
| 606 | $this->log("Created plugins zip - file size is ".filesize($full_path)." bytes"); |
| 607 | } |
| 608 | $backup_array['plugins'] = basename($full_path); |
| 609 | } else { |
| 610 | $this->log("No backup of plugins: excluded by user's options"); |
| 611 | } |
| 612 | |
| 613 | $this->check_backup_race($backup_array); |
| 614 | |
| 615 | # Themes |
| 616 | @set_time_limit(900); |
| 617 | if (get_option('updraft_include_themes', true)) { |
| 618 | $this->log("Beginning backup of themes"); |
| 619 | $full_path = $backup_file_base.'-themes.zip'; |
| 620 | $themes = new PclZip($full_path); |
| 621 | if (!$themes->create($wp_themes_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) { |
| 622 | $this->error('Could not create themes zip. Error was '.$php_errmsg,'fatal'); |
| 623 | $this->log('ERROR: PclZip failure: Could not create themes zip'); |
| 624 | } else { |
| 625 | $this->log("Created themes zip - file size is ".filesize($full_path)." bytes"); |
| 626 | } |
| 627 | $backup_array['themes'] = basename($full_path); |
| 628 | } else { |
| 629 | $this->log("No backup of themes: excluded by user's options"); |
| 630 | } |
| 631 | |
| 632 | $this->check_backup_race($backup_array); |
| 633 | |
| 634 | # Uploads |
| 635 | @set_time_limit(900); |
| 636 | if (get_option('updraft_include_uploads', true)) { |
| 637 | $this->log("Beginning backup of uploads"); |
| 638 | $full_path = $backup_file_base.'-uploads.zip'; |
| 639 | $uploads = new PclZip($full_path); |
| 640 | if (!$uploads->create($wp_upload_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) { |
| 641 | $this->error('Could not create uploads zip. Error was '.$php_errmsg,'fatal'); |
| 642 | $this->log('ERROR: PclZip failure: Could not create uploads zip'); |
| 643 | } else { |
| 644 | $this->log("Created uploads zip - file size is ".filesize($full_path)." bytes"); |
| 645 | } |
| 646 | $backup_array['uploads'] = basename($full_path); |
| 647 | } else { |
| 648 | $this->log("No backup of uploads: excluded by user's options"); |
| 649 | } |
| 650 | |
| 651 | $this->check_backup_race($backup_array); |
| 652 | |
| 653 | # Others |
| 654 | @set_time_limit(900); |
| 655 | if (get_option('updraft_include_others', true)) { |
| 656 | $this->log("Beginning backup of other directories found in the content directory"); |
| 657 | $full_path=$backup_file_base.'-others.zip'; |
| 658 | $others = new PclZip($full_path); |
| 659 | // http://www.phpconcept.net/pclzip/user-guide/53 |
| 660 | /* First parameter to create is: |
| 661 | An array of filenames or dirnames, |
| 662 | or |
| 663 | A string containing the filename or a dirname, |
| 664 | or |
| 665 | A string containing a list of filename or dirname separated by a comma. |
| 666 | */ |
| 667 | // First, see what we can find. We always want to exclude these: |
| 668 | $wp_themes_dir = WP_CONTENT_DIR.'/themes'; |
| 669 | $wp_upload_dir = wp_upload_dir(); |
| 670 | $wp_upload_dir = $wp_upload_dir['basedir']; |
| 671 | $wp_plugins_dir = WP_PLUGIN_DIR; |
| 672 | $updraft_dir = untrailingslashit(get_option('updraft_dir')); |
| 673 | |
| 674 | # Initialise |
| 675 | $other_dirlist = array(); |
| 676 | |
| 677 | $others_skip = preg_split("/,/",get_option('updraft_include_others_exclude',UPDRAFT_DEFAULT_OTHERS_EXCLUDE)); |
| 678 | # Make the values into the keys |
| 679 | $others_skip = array_flip($others_skip); |
| 680 | |
| 681 | $this->log('Looking for candidates to back up in: '.WP_CONTENT_DIR); |
| 682 | if ($handle = opendir(WP_CONTENT_DIR)) { |
| 683 | while (false !== ($entry = readdir($handle))) { |
| 684 | $candidate = WP_CONTENT_DIR.'/'.$entry; |
| 685 | if ($entry == "." || $entry == "..") { ; } |
| 686 | elseif ($candidate == $updraft_dir) { $this->log("$entry: skipping: this is the updraft directory"); } |
| 687 | elseif ($candidate == $wp_themes_dir) { $this->log("$entry: skipping: this is the themes directory"); } |
| 688 | elseif ($candidate == $wp_upload_dir) { $this->log("$entry: skipping: this is the uploads directory"); } |
| 689 | elseif ($candidate == $wp_plugins_dir) { $this->log("$entry: skipping: this is the plugins directory"); } |
| 690 | elseif (isset($others_skip[$entry])) { $this->log("$entry: skipping: excluded by options"); } |
| 691 | else { $this->log("$entry: adding to list"); array_push($other_dirlist,$candidate); } |
| 692 | } |
| 693 | } else { |
| 694 | $this->log('ERROR: Could not read the content directory: '.WP_CONTENT_DIR); |
| 695 | } |
| 696 | |
| 697 | if (count($other_dirlist)>0) { |
| 698 | if (!$others->create($other_dirlist,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) { |
| 699 | $this->error('Could not create other zip. Error was '.$php_errmsg,'fatal'); |
| 700 | $this->log('ERROR: PclZip failure: Could not create other zip'); |
| 701 | } else { |
| 702 | $this->log("Created other directories zip - file size is ".filesize($full_path)." bytes"); |
| 703 | } |
| 704 | $backup_array['others'] = basename($full_path); |
| 705 | } else { |
| 706 | $this->log("No backup of other directories: there was nothing found to back up"); |
| 707 | } |
| 708 | } else { |
| 709 | $this->log("No backup of other directories: excluded by user's options"); |
| 710 | } |
| 711 | return $backup_array; |
| 712 | } |
| 713 | |
| 714 | function save_backup_history($backup_array) { |
| 715 | if(is_array($backup_array)) { |
| 716 | $backup_history = get_option('updraft_backup_history'); |
| 717 | $backup_history = (is_array($backup_history)) ? $backup_history : array(); |
| 718 | $backup_array['nonce'] = $this->nonce; |
| 719 | $backup_history[$this->backup_time] = $backup_array; |
| 720 | update_option('updraft_backup_history',$backup_history); |
| 721 | } else { |
| 722 | $this->error('Could not save backup history because we have no backup array. Backup probably failed.'); |
| 723 | } |
| 724 | } |
| 725 | |
| 726 | function get_backup_history() { |
| 727 | //$backup_history = get_option('updraft_backup_history'); |
| 728 | //by doing a raw DB query to get the most up-to-date data from this option we slightly narrow the window for the multiple-cron race condition |
| 729 | global $wpdb; |
| 730 | $backup_history = @unserialize($wpdb->get_var($wpdb->prepare("SELECT option_value from $wpdb->options WHERE option_name='updraft_backup_history'"))); |
| 731 | if(is_array($backup_history)) { |
| 732 | krsort($backup_history); //reverse sort so earliest backup is last on the array. this way we can array_pop |
| 733 | } else { |
| 734 | $backup_history = array(); |
| 735 | } |
| 736 | return $backup_history; |
| 737 | } |
| 738 | |
| 739 | |
| 740 | /*START OF WB-DB-BACKUP BLOCK*/ |
| 741 | |
| 742 | function backup_db() { |
| 743 | |
| 744 | $total_tables = 0; |
| 745 | |
| 746 | global $table_prefix, $wpdb; |
| 747 | if(!$this->backup_time) $this->backup_time_nonce(); |
| 748 | |
| 749 | $all_tables = $wpdb->get_results("SHOW TABLES", ARRAY_N); |
| 750 | $all_tables = array_map(create_function('$a', 'return $a[0];'), $all_tables); |
| 751 | |
| 752 | $updraft_dir = $this->backups_dir_location(); |
| 753 | //get the blog name and rip out all non-alphanumeric chars other than _ |
| 754 | $blog_name = str_replace(' ','_',get_bloginfo()); |
| 755 | $blog_name = preg_replace('/[^A-Za-z0-9_]/','', $blog_name); |
| 756 | if (!$blog_name) $blog_name = 'non_alpha_name'; |
| 757 | |
| 758 | $backup_file_base = $updraft_dir.'/backup_'.date('Y-m-d-Hi',$this->backup_time).'_'.$blog_name.'_'.$this->nonce; |
| 759 | if (is_writable($updraft_dir)) { |
| 760 | if (function_exists('gzopen')) { |
| 761 | $this->dbhandle = @gzopen($backup_file_base.'-db.gz','w'); |
| 762 | } else { |
| 763 | $this->dbhandle = @fopen($backup_file_base.'-db.gz', 'w'); |
| 764 | } |
| 765 | if(!$this->dbhandle) { |
| 766 | //$this->error(__('Could not open the backup file for writing!','wp-db-backup')); |
| 767 | } |
| 768 | } else { |
| 769 | //$this->error(__('The backup directory is not writable!','wp-db-backup')); |
| 770 | } |
| 771 | |
| 772 | //Begin new backup of MySql |
| 773 | $this->stow("# " . __('WordPress MySQL database backup','wp-db-backup') . "\n"); |
| 774 | $this->stow("#\n"); |
| 775 | $this->stow("# " . sprintf(__('Generated: %s','wp-db-backup'),date("l j. F Y H:i T")) . "\n"); |
| 776 | $this->stow("# " . sprintf(__('Hostname: %s','wp-db-backup'),DB_HOST) . "\n"); |
| 777 | $this->stow("# " . sprintf(__('Database: %s','wp-db-backup'),$this->backquote(DB_NAME)) . "\n"); |
| 778 | $this->stow("# --------------------------------------------------------\n"); |
| 779 | |
| 780 | |
| 781 | if (defined("DB_CHARSET")) { |
| 782 | $this->stow("/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n"); |
| 783 | $this->stow("/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n"); |
| 784 | $this->stow("/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n"); |
| 785 | $this->stow("/*!40101 SET NAMES " . DB_CHARSET . " */;\n"); |
| 786 | } |
| 787 | $this->stow("/*!40101 SET foreign_key_checks = 0 */;\n"); |
| 788 | |
| 789 | foreach ($all_tables as $table) { |
| 790 | $total_tables++; |
| 791 | // Increase script execution time-limit to 15 min for every table. |
| 792 | if ( !ini_get('safe_mode') || strtolower(ini_get('safe_mode')) == "off") @set_time_limit(15*60); |
| 793 | # === is needed, otherwise 'false' matches (i.e. prefix does not match) |
| 794 | if ( strpos($table, $table_prefix) === 0 ) { |
| 795 | // Create the SQL statements |
| 796 | $this->stow("# --------------------------------------------------------\n"); |
| 797 | $this->stow("# " . sprintf(__('Table: %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 798 | $this->stow("# --------------------------------------------------------\n"); |
| 799 | $this->backup_table($table); |
| 800 | } else { |
| 801 | $this->stow("# --------------------------------------------------------\n"); |
| 802 | $this->stow("# " . sprintf(__('Skipping non-WP table: %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 803 | $this->stow("# --------------------------------------------------------\n"); |
| 804 | } |
| 805 | } |
| 806 | |
| 807 | if (defined("DB_CHARSET")) { |
| 808 | $this->stow("/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n"); |
| 809 | $this->stow("/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n"); |
| 810 | $this->stow("/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n"); |
| 811 | } |
| 812 | |
| 813 | $this->close($this->dbhandle); |
| 814 | |
| 815 | if (count($this->errors)) { |
| 816 | return false; |
| 817 | } else { |
| 818 | # We no longer encrypt here - because the operation can take long, we made it resumable and moved it to the upload loop |
| 819 | $this->log("Total database tables backed up: $total_tables"); |
| 820 | return basename($backup_file_base.'-db.gz'); |
| 821 | } |
| 822 | |
| 823 | } //wp_db_backup |
| 824 | |
| 825 | /** |
| 826 | * Taken partially from phpMyAdmin and partially from |
| 827 | * Alain Wolf, Zurich - Switzerland |
| 828 | * Website: http://restkultur.ch/personal/wolf/scripts/db_backup/ |
| 829 | * Modified by Scott Merrill (http://www.skippy.net/) |
| 830 | * to use the WordPress $wpdb object |
| 831 | * @param string $table |
| 832 | * @param string $segment |
| 833 | * @return void |
| 834 | */ |
| 835 | function backup_table($table, $segment = 'none') { |
| 836 | global $wpdb; |
| 837 | |
| 838 | $total_rows = 0; |
| 839 | |
| 840 | $table_structure = $wpdb->get_results("DESCRIBE $table"); |
| 841 | if (! $table_structure) { |
| 842 | //$this->error(__('Error getting table details','wp-db-backup') . ": $table"); |
| 843 | return false; |
| 844 | } |
| 845 | |
| 846 | if(($segment == 'none') || ($segment == 0)) { |
| 847 | // Add SQL statement to drop existing table |
| 848 | $this->stow("\n\n"); |
| 849 | $this->stow("#\n"); |
| 850 | $this->stow("# " . sprintf(__('Delete any existing table %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 851 | $this->stow("#\n"); |
| 852 | $this->stow("\n"); |
| 853 | $this->stow("DROP TABLE IF EXISTS " . $this->backquote($table) . ";\n"); |
| 854 | |
| 855 | // Table structure |
| 856 | // Comment in SQL-file |
| 857 | $this->stow("\n\n"); |
| 858 | $this->stow("#\n"); |
| 859 | $this->stow("# " . sprintf(__('Table structure of table %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 860 | $this->stow("#\n"); |
| 861 | $this->stow("\n"); |
| 862 | |
| 863 | $create_table = $wpdb->get_results("SHOW CREATE TABLE $table", ARRAY_N); |
| 864 | if (false === $create_table) { |
| 865 | $err_msg = sprintf(__('Error with SHOW CREATE TABLE for %s.','wp-db-backup'), $table); |
| 866 | //$this->error($err_msg); |
| 867 | $this->stow("#\n# $err_msg\n#\n"); |
| 868 | } |
| 869 | $this->stow($create_table[0][1] . ' ;'); |
| 870 | |
| 871 | if (false === $table_structure) { |
| 872 | $err_msg = sprintf(__('Error getting table structure of %s','wp-db-backup'), $table); |
| 873 | //$this->error($err_msg); |
| 874 | $this->stow("#\n# $err_msg\n#\n"); |
| 875 | } |
| 876 | |
| 877 | // Comment in SQL-file |
| 878 | $this->stow("\n\n"); |
| 879 | $this->stow("#\n"); |
| 880 | $this->stow('# ' . sprintf(__('Data contents of table %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 881 | $this->stow("#\n"); |
| 882 | } |
| 883 | |
| 884 | if(($segment == 'none') || ($segment >= 0)) { |
| 885 | $defs = array(); |
| 886 | $ints = array(); |
| 887 | foreach ($table_structure as $struct) { |
| 888 | if ( (0 === strpos($struct->Type, 'tinyint')) || |
| 889 | (0 === strpos(strtolower($struct->Type), 'smallint')) || |
| 890 | (0 === strpos(strtolower($struct->Type), 'mediumint')) || |
| 891 | (0 === strpos(strtolower($struct->Type), 'int')) || |
| 892 | (0 === strpos(strtolower($struct->Type), 'bigint')) ) { |
| 893 | $defs[strtolower($struct->Field)] = ( null === $struct->Default ) ? 'NULL' : $struct->Default; |
| 894 | $ints[strtolower($struct->Field)] = "1"; |
| 895 | } |
| 896 | } |
| 897 | |
| 898 | |
| 899 | // Batch by $row_inc |
| 900 | if ( ! defined('ROWS_PER_SEGMENT') ) { |
| 901 | define('ROWS_PER_SEGMENT', 100); |
| 902 | } |
| 903 | |
| 904 | if($segment == 'none') { |
| 905 | $row_start = 0; |
| 906 | $row_inc = ROWS_PER_SEGMENT; |
| 907 | } else { |
| 908 | $row_start = $segment * ROWS_PER_SEGMENT; |
| 909 | $row_inc = ROWS_PER_SEGMENT; |
| 910 | } |
| 911 | do { |
| 912 | |
| 913 | if ( !ini_get('safe_mode') || strtolower(ini_get('safe_mode')) == "off") @set_time_limit(15*60); |
| 914 | $table_data = $wpdb->get_results("SELECT * FROM $table $where LIMIT {$row_start}, {$row_inc}", ARRAY_A); |
| 915 | $entries = 'INSERT INTO ' . $this->backquote($table) . ' VALUES ('; |
| 916 | // \x08\\x09, not required |
| 917 | $search = array("\x00", "\x0a", "\x0d", "\x1a"); |
| 918 | $replace = array('\0', '\n', '\r', '\Z'); |
| 919 | if($table_data) { |
| 920 | foreach ($table_data as $row) { |
| 921 | $total_rows++; |
| 922 | $values = array(); |
| 923 | foreach ($row as $key => $value) { |
| 924 | if ($ints[strtolower($key)]) { |
| 925 | // make sure there are no blank spots in the insert syntax, |
| 926 | // yet try to avoid quotation marks around integers |
| 927 | $value = ( null === $value || '' === $value) ? $defs[strtolower($key)] : $value; |
| 928 | $values[] = ( '' === $value ) ? "''" : $value; |
| 929 | } else { |
| 930 | $values[] = "'" . str_replace($search, $replace, $this->sql_addslashes($value)) . "'"; |
| 931 | } |
| 932 | } |
| 933 | $this->stow(" \n" . $entries . implode(', ', $values) . ');'); |
| 934 | } |
| 935 | $row_start += $row_inc; |
| 936 | } |
| 937 | } while((count($table_data) > 0) and ($segment=='none')); |
| 938 | } |
| 939 | |
| 940 | if(($segment == 'none') || ($segment < 0)) { |
| 941 | // Create footer/closing comment in SQL-file |
| 942 | $this->stow("\n"); |
| 943 | $this->stow("#\n"); |
| 944 | $this->stow("# " . sprintf(__('End of data contents of table %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 945 | $this->stow("# --------------------------------------------------------\n"); |
| 946 | $this->stow("\n"); |
| 947 | } |
| 948 | $this->log("Table $table: Total rows added: $total_rows"); |
| 949 | |
| 950 | } // end backup_table() |
| 951 | |
| 952 | |
| 953 | function stow($query_line) { |
| 954 | if (function_exists('gzopen')) { |
| 955 | if(! @gzwrite($this->dbhandle, $query_line)) { |
| 956 | //$this->error(__('There was an error writing a line to the backup script:','wp-db-backup') . ' ' . $query_line . ' ' . $php_errormsg); |
| 957 | } |
| 958 | } else { |
| 959 | if(false === @fwrite($this->dbhandle, $query_line)) { |
| 960 | //$this->error(__('There was an error writing a line to the backup script:','wp-db-backup') . ' ' . $query_line . ' ' . $php_errormsg); |
| 961 | } |
| 962 | } |
| 963 | } |
| 964 | |
| 965 | |
| 966 | function close($handle) { |
| 967 | if (function_exists('gzopen')) { |
| 968 | gzclose($handle); |
| 969 | } else { |
| 970 | fclose($handle); |
| 971 | } |
| 972 | } |
| 973 | |
| 974 | function error($error,$severity='') { |
| 975 | $this->errors[] = $error; |
| 976 | return true; |
| 977 | } |
| 978 | |
| 979 | /** |
| 980 | * Add backquotes to tables and db-names in |
| 981 | * SQL queries. Taken from phpMyAdmin. |
| 982 | */ |
| 983 | function backquote($a_name) { |
| 984 | if (!empty($a_name) && $a_name != '*') { |
| 985 | if (is_array($a_name)) { |
| 986 | $result = array(); |
| 987 | reset($a_name); |
| 988 | while(list($key, $val) = each($a_name)) |
| 989 | $result[$key] = '`' . $val . '`'; |
| 990 | return $result; |
| 991 | } else { |
| 992 | return '`' . $a_name . '`'; |
| 993 | } |
| 994 | } else { |
| 995 | return $a_name; |
| 996 | } |
| 997 | } |
| 998 | |
| 999 | /** |
| 1000 | * Better addslashes for SQL queries. |
| 1001 | * Taken from phpMyAdmin. |
| 1002 | */ |
| 1003 | function sql_addslashes($a_string = '', $is_like = false) { |
| 1004 | if ($is_like) $a_string = str_replace('\\', '\\\\\\\\', $a_string); |
| 1005 | else $a_string = str_replace('\\', '\\\\', $a_string); |
| 1006 | return str_replace('\'', '\\\'', $a_string); |
| 1007 | } |
| 1008 | |
| 1009 | /*END OF WP-DB-BACKUP BLOCK */ |
| 1010 | |
| 1011 | /* |
| 1012 | this function is both the backup scheduler and ostensibly a filter callback for saving the option. |
| 1013 | it is called in the register_setting for the updraft_interval, which means when the admin settings |
| 1014 | are saved it is called. it returns the actual result from wp_filter_nohtml_kses (a sanitization filter) |
| 1015 | so the option can be properly saved. |
| 1016 | */ |
| 1017 | function schedule_backup($interval) { |
| 1018 | //clear schedule and add new so we don't stack up scheduled backups |
| 1019 | wp_clear_scheduled_hook('updraft_backup'); |
| 1020 | switch($interval) { |
| 1021 | case 'every4hours': |
| 1022 | case 'every8hours': |
| 1023 | case 'twicedaily': |
| 1024 | case 'daily': |
| 1025 | case 'weekly': |
| 1026 | case 'fortnightly': |
| 1027 | case 'monthly': |
| 1028 | wp_schedule_event(time()+30, $interval, 'updraft_backup'); |
| 1029 | break; |
| 1030 | } |
| 1031 | return wp_filter_nohtml_kses($interval); |
| 1032 | } |
| 1033 | |
| 1034 | function schedule_backup_database($interval) { |
| 1035 | //clear schedule and add new so we don't stack up scheduled backups |
| 1036 | wp_clear_scheduled_hook('updraft_backup_database'); |
| 1037 | switch($interval) { |
| 1038 | case 'every4hours': |
| 1039 | case 'every8hours': |
| 1040 | case 'twicedaily': |
| 1041 | case 'daily': |
| 1042 | case 'weekly': |
| 1043 | case 'fortnightly': |
| 1044 | case 'monthly': |
| 1045 | wp_schedule_event(time()+30, $interval, 'updraft_backup_database'); |
| 1046 | break; |
| 1047 | } |
| 1048 | return wp_filter_nohtml_kses($interval); |
| 1049 | } |
| 1050 | |
| 1051 | //wp-cron only has hourly, daily and twicedaily, so we need to add some of our own |
| 1052 | function modify_cron_schedules($schedules) { |
| 1053 | $schedules['weekly'] = array( 'interval' => 604800, 'display' => 'Once Weekly' ); |
| 1054 | $schedules['fortnightly'] = array( 'interval' => 1209600, 'display' => 'Once Each Fortnight' ); |
| 1055 | $schedules['monthly'] = array( 'interval' => 2592000, 'display' => 'Once Monthly' ); |
| 1056 | $schedules['every4hours'] = array( 'interval' => 14400, 'display' => 'Every 4 hours' ); |
| 1057 | $schedules['every8hours'] = array( 'interval' => 28800, 'display' => 'Every 8 hours' ); |
| 1058 | return $schedules; |
| 1059 | } |
| 1060 | |
| 1061 | function backups_dir_location() { |
| 1062 | $updraft_dir = untrailingslashit(get_option('updraft_dir')); |
| 1063 | $default_backup_dir = WP_CONTENT_DIR.'/updraft'; |
| 1064 | //if the option isn't set, default it to /backups inside the upload dir |
| 1065 | $updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir; |
| 1066 | //check for the existence of the dir and an enumeration preventer. |
| 1067 | if(!is_dir($updraft_dir) || !is_file($updraft_dir.'/index.html') || !is_file($updraft_dir.'/.htaccess')) { |
| 1068 | @mkdir($updraft_dir,0777,true); //recursively create the dir with 0777 permissions. 0777 is default for php creation. not ideal, but I'll get back to this |
| 1069 | @file_put_contents($updraft_dir.'/index.html','Nothing to see here.'); |
| 1070 | @file_put_contents($updraft_dir.'/.htaccess','deny from all'); |
| 1071 | } |
| 1072 | return $updraft_dir; |
| 1073 | } |
| 1074 | |
| 1075 | function updraft_download_backup() { |
| 1076 | $type = $_POST['type']; |
| 1077 | $timestamp = (int)$_POST['timestamp']; |
| 1078 | $backup_history = $this->get_backup_history(); |
| 1079 | $file = $backup_history[$timestamp][$type]; |
| 1080 | $fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 1081 | if(!is_readable($fullpath)) { |
| 1082 | //if the file doesn't exist and they're using one of the cloud options, fetch it down from the cloud. |
| 1083 | $this->download_backup($file); |
| 1084 | } |
| 1085 | if(@is_readable($fullpath) && is_file($fullpath)) { |
| 1086 | $len = filesize($fullpath); |
| 1087 | |
| 1088 | $filearr = explode('.',$file); |
| 1089 | // //we've only got zip and gz...for now |
| 1090 | $file_ext = array_pop($filearr); |
| 1091 | if($file_ext == 'zip') { |
| 1092 | header('Content-type: application/zip'); |
| 1093 | } else { |
| 1094 | // This catches both when what was popped was 'crypt' (*-db.gz.crypt) and when it was 'gz' (unencrypted) |
| 1095 | header('Content-type: application/x-gzip'); |
| 1096 | } |
| 1097 | header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 |
| 1098 | header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past |
| 1099 | header("Content-Length: $len;"); |
| 1100 | if ($file_ext == 'crypt') { |
| 1101 | header("Content-Disposition: attachment; filename=\"".substr($file,0,-6)."\";"); |
| 1102 | } else { |
| 1103 | header("Content-Disposition: attachment; filename=\"$file\";"); |
| 1104 | } |
| 1105 | ob_end_flush(); |
| 1106 | if ($file_ext == 'crypt') { |
| 1107 | $encryption = get_option('updraft_encryptionphrase'); |
| 1108 | if ($encryption == "") { |
| 1109 | $this->error('Decryption of database failed: the database file is encrypted, but you have no encryption key entered.'); |
| 1110 | } else { |
| 1111 | require_once(dirname(__FILE__).'/includes/Rijndael.php'); |
| 1112 | $rijndael = new Crypt_Rijndael(); |
| 1113 | $rijndael->setKey($encryption); |
| 1114 | $in_handle = fopen($fullpath,'r'); |
| 1115 | $ciphertext = ""; |
| 1116 | while (!feof ($in_handle)) { |
| 1117 | $ciphertext .= fread($in_handle, 16384); |
| 1118 | } |
| 1119 | fclose ($in_handle); |
| 1120 | print $rijndael->decrypt($ciphertext); |
| 1121 | } |
| 1122 | } else { |
| 1123 | readfile($fullpath); |
| 1124 | } |
| 1125 | $this->delete_local($file); |
| 1126 | exit; //we exit immediately because otherwise admin-ajax appends an additional zero to the end |
| 1127 | } else { |
| 1128 | echo 'Download failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then remote retrieval may have failed.'; |
| 1129 | } |
| 1130 | } |
| 1131 | |
| 1132 | function download_backup($file) { |
| 1133 | $service = get_option('updraft_service'); |
| 1134 | |
| 1135 | $method_include = UPDRAFTPLUS_DIR.'/methods/'.$service.'.php'; |
| 1136 | if (file_exists($method_include)) require_once($method_include); |
| 1137 | |
| 1138 | $objname = "UpdraftPlus_BackupModule_${service}"; |
| 1139 | if (method_exists($objname, "download")) { |
| 1140 | $remote_obj = new $objname; |
| 1141 | $remote_obj->download($file); |
| 1142 | } else { |
| 1143 | $this->error('Automatic backup restoration is not available with the method: $service.'); |
| 1144 | } |
| 1145 | |
| 1146 | } |
| 1147 | |
| 1148 | function restore_backup($timestamp) { |
| 1149 | global $wp_filesystem; |
| 1150 | $backup_history = get_option('updraft_backup_history'); |
| 1151 | if(!is_array($backup_history[$timestamp])) { |
| 1152 | echo '<p>This backup does not exist in the backup history - restoration aborted. Timestamp: '.$timestamp.'</p><br/>'; |
| 1153 | return false; |
| 1154 | } |
| 1155 | |
| 1156 | $credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_restore&backup_timestamp=$timestamp"); |
| 1157 | WP_Filesystem($credentials); |
| 1158 | if ( $wp_filesystem->errors->get_error_code() ) { |
| 1159 | foreach ( $wp_filesystem->errors->get_error_messages() as $message ) |
| 1160 | show_message($message); |
| 1161 | exit; |
| 1162 | } |
| 1163 | |
| 1164 | //if we make it this far then WP_Filesystem has been instantiated and is functional (tested with ftpext, what about suPHP and other situations where direct may work?) |
| 1165 | echo '<span style="font-weight:bold">Restoration Progress </span><div id="updraft-restore-progress">'; |
| 1166 | |
| 1167 | $updraft_dir = trailingslashit(get_option('updraft_dir')); |
| 1168 | foreach($backup_history[$timestamp] as $type=>$file) { |
| 1169 | $fullpath = $updraft_dir.$file; |
| 1170 | if(!is_readable($fullpath) && $type != 'db') { |
| 1171 | $this->download_backup($file); |
| 1172 | } |
| 1173 | # Types: uploads, themes, plugins, others, db |
| 1174 | if(is_readable($fullpath) && $type != 'db') { |
| 1175 | if(!class_exists('WP_Upgrader')) { |
| 1176 | require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' ); |
| 1177 | } |
| 1178 | require_once(UPDRAFTPLUS_DIR.'/includes/updraft-restorer.php'); |
| 1179 | $restorer = new Updraft_Restorer(); |
| 1180 | $val = $restorer->restore_backup($fullpath,$type); |
| 1181 | if(is_wp_error($val)) { |
| 1182 | print_r($val); |
| 1183 | echo '</div>'; //close the updraft_restore_progress div even if we error |
| 1184 | return false; |
| 1185 | } |
| 1186 | } |
| 1187 | } |
| 1188 | echo '</div>'; //close the updraft_restore_progress div |
| 1189 | # The 'off' check is for badly configured setups - http://wordpress.org/support/topic/plugin-wp-super-cache-warning-php-safe-mode-enabled-but-safe-mode-is-off |
| 1190 | if(ini_get('safe_mode') && strtolower(ini_get('safe_mode')) != "off") { |
| 1191 | echo "<p>DB could not be restored because PHP safe_mode is active on your server. You will need to manually restore the file via phpMyAdmin or another method.</p><br/>"; |
| 1192 | return false; |
| 1193 | } |
| 1194 | return true; |
| 1195 | } |
| 1196 | |
| 1197 | //deletes the -old directories that are created when a backup is restored. |
| 1198 | function delete_old_dirs() { |
| 1199 | global $wp_filesystem; |
| 1200 | $credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_delete_old_dirs"); |
| 1201 | WP_Filesystem($credentials); |
| 1202 | if ( $wp_filesystem->errors->get_error_code() ) { |
| 1203 | foreach ( $wp_filesystem->errors->get_error_messages() as $message ) |
| 1204 | show_message($message); |
| 1205 | exit; |
| 1206 | } |
| 1207 | |
| 1208 | $to_delete = array('themes-old','plugins-old','uploads-old','others-old'); |
| 1209 | |
| 1210 | foreach($to_delete as $name) { |
| 1211 | //recursively delete |
| 1212 | if(!$wp_filesystem->delete(WP_CONTENT_DIR.'/'.$name, true)) { |
| 1213 | return false; |
| 1214 | } |
| 1215 | } |
| 1216 | return true; |
| 1217 | } |
| 1218 | |
| 1219 | //scans the content dir to see if any -old dirs are present |
| 1220 | function scan_old_dirs() { |
| 1221 | $dirArr = scandir(WP_CONTENT_DIR); |
| 1222 | foreach($dirArr as $dir) { |
| 1223 | if(strpos($dir,'-old') !== false) { |
| 1224 | return true; |
| 1225 | } |
| 1226 | } |
| 1227 | return false; |
| 1228 | } |
| 1229 | |
| 1230 | |
| 1231 | function retain_range($input) { |
| 1232 | $input = (int)$input; |
| 1233 | if($input > 0 && $input < 3650) { |
| 1234 | return $input; |
| 1235 | } else { |
| 1236 | return 1; |
| 1237 | } |
| 1238 | } |
| 1239 | |
| 1240 | function create_backup_dir() { |
| 1241 | global $wp_filesystem; |
| 1242 | $credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_create_backup_dir"); |
| 1243 | WP_Filesystem($credentials); |
| 1244 | if ( $wp_filesystem->errors->get_error_code() ) { |
| 1245 | foreach ( $wp_filesystem->errors->get_error_messages() as $message ) show_message($message); |
| 1246 | exit; |
| 1247 | } |
| 1248 | |
| 1249 | $updraft_dir = untrailingslashit(get_option('updraft_dir')); |
| 1250 | $default_backup_dir = WP_CONTENT_DIR.'/updraft'; |
| 1251 | $updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir; |
| 1252 | |
| 1253 | //chmod the backup dir to 0777. ideally we'd rather chgrp it but i'm not sure if it's possible to detect the group apache is running under (or what if it's not apache...) |
| 1254 | if(!$wp_filesystem->mkdir($updraft_dir, 0777)) return false; |
| 1255 | |
| 1256 | return true; |
| 1257 | } |
| 1258 | |
| 1259 | function memory_check_current() { |
| 1260 | # Returns in megabytes |
| 1261 | $memory_limit = ini_get('memory_limit'); |
| 1262 | $memory_unit = $memory_limit[strlen($memory_limit)-1]; |
| 1263 | $memory_limit = substr($memory_limit,0,strlen($memory_limit)-1); |
| 1264 | switch($memory_unit) { |
| 1265 | case 'K': |
| 1266 | $memory_limit = $memory_limit/1024; |
| 1267 | break; |
| 1268 | case 'G': |
| 1269 | $memory_limit = $memory_limit*1024; |
| 1270 | break; |
| 1271 | case 'M': |
| 1272 | //assumed size, no change needed |
| 1273 | break; |
| 1274 | } |
| 1275 | return $memory_limit; |
| 1276 | } |
| 1277 | |
| 1278 | function memory_check($memory) { |
| 1279 | $memory_limit = $this->memory_check_current(); |
| 1280 | return ($memory_limit >= $memory)?true:false; |
| 1281 | } |
| 1282 | |
| 1283 | function execution_time_check($time) { |
| 1284 | return (ini_get('max_execution_time') >= $time)?true:false; |
| 1285 | } |
| 1286 | |
| 1287 | function admin_init() { |
| 1288 | if(get_option('updraft_debug_mode')) { |
| 1289 | ini_set('display_errors',1); |
| 1290 | error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED); |
| 1291 | ini_set('track_errors',1); |
| 1292 | } |
| 1293 | wp_enqueue_script('jquery'); |
| 1294 | register_setting( 'updraft-options-group', 'updraft_interval', array($this,'schedule_backup') ); |
| 1295 | register_setting( 'updraft-options-group', 'updraft_interval_database', array($this,'schedule_backup_database') ); |
| 1296 | register_setting( 'updraft-options-group', 'updraft_retain', array($this,'retain_range') ); |
| 1297 | register_setting( 'updraft-options-group', 'updraft_encryptionphrase', 'wp_filter_nohtml_kses' ); |
| 1298 | register_setting( 'updraft-options-group', 'updraft_service', 'wp_filter_nohtml_kses' ); |
| 1299 | |
| 1300 | register_setting( 'updraft-options-group', 'updraft_s3_login' ); |
| 1301 | register_setting( 'updraft-options-group', 'updraft_s3_pass' ); |
| 1302 | register_setting( 'updraft-options-group', 'updraft_s3_remote_path', 'wp_filter_nohtml_kses' ); |
| 1303 | register_setting( 'updraft-options-group', 'updraft_googledrive_clientid', 'wp_filter_nohtml_kses' ); |
| 1304 | register_setting( 'updraft-options-group', 'updraft_googledrive_secret' ); |
| 1305 | register_setting( 'updraft-options-group', 'updraft_googledrive_remotepath', 'wp_filter_nohtml_kses' ); |
| 1306 | register_setting( 'updraft-options-group', 'updraft_ftp_login' ); |
| 1307 | register_setting( 'updraft-options-group', 'updraft_ftp_pass' ); |
| 1308 | register_setting( 'updraft-options-group', 'updraft_ftp_remote_path' ); |
| 1309 | register_setting( 'updraft-options-group', 'updraft_server_address', 'wp_filter_nohtml_kses' ); |
| 1310 | register_setting( 'updraft-options-group', 'updraft_dir' ); |
| 1311 | register_setting( 'updraft-options-group', 'updraft_email', 'wp_filter_nohtml_kses' ); |
| 1312 | register_setting( 'updraft-options-group', 'updraft_delete_local', 'absint' ); |
| 1313 | register_setting( 'updraft-options-group', 'updraft_debug_mode', 'absint' ); |
| 1314 | register_setting( 'updraft-options-group', 'updraft_include_plugins', 'absint' ); |
| 1315 | register_setting( 'updraft-options-group', 'updraft_include_themes', 'absint' ); |
| 1316 | register_setting( 'updraft-options-group', 'updraft_include_uploads', 'absint' ); |
| 1317 | register_setting( 'updraft-options-group', 'updraft_include_others', 'absint' ); |
| 1318 | register_setting( 'updraft-options-group', 'updraft_include_others_exclude', 'wp_filter_nohtml_kses' ); |
| 1319 | |
| 1320 | if (current_user_can('manage_options') && get_option('updraft_service') == "googledrive" && get_option('updraft_googledrive_clientid') != "" && get_option('updraft_googledrive_token','xyz') == 'xyz') { |
| 1321 | add_action('admin_notices', array($this,'show_admin_warning_googledrive') ); |
| 1322 | } |
| 1323 | } |
| 1324 | |
| 1325 | function add_admin_pages() { |
| 1326 | add_submenu_page('options-general.php', "UpdraftPlus", "UpdraftPlus", "manage_options", "updraftplus", |
| 1327 | array($this,"settings_output")); |
| 1328 | } |
| 1329 | |
| 1330 | function url_start($urls,$url) { |
| 1331 | return ($urls) ? '<a href="http://'.$url.'">' : ""; |
| 1332 | } |
| 1333 | |
| 1334 | function url_end($urls,$url) { |
| 1335 | return ($urls) ? '</a>' : " (http://$url)"; |
| 1336 | } |
| 1337 | |
| 1338 | function wordshell_random_advert($urls) { |
| 1339 | $rad = rand(0,5); |
| 1340 | switch ($rad) { |
| 1341 | case 0: |
| 1342 | return "Like automating WordPress operations? Use the CLI? ".$this->url_start($urls,'wordshell.net')."You will love WordShell".$this->url_end($urls,'www.wordshell.net')." - saves time and money fast."; |
| 1343 | break; |
| 1344 | case 1: |
| 1345 | return "Find UpdraftPlus useful? ".$this->url_start($urls,'david.dw-perspective.org.uk/donate')."Please make a donation.".$this->url_end($urls,'david.dw-perspective.org.uk/donate'); |
| 1346 | case 2: |
| 1347 | return $this->url_start($urls,'wordshell.net')."Check out WordShell".$this->url_end($urls,'www.wordshell.net')." - manage WordPress from the command line - huge time-saver"; |
| 1348 | break; |
| 1349 | case 3: |
| 1350 | return "Want some more useful plugins? ".$this->url_start($urls,'profiles.wordpress.org/DavidAnderson/')."See my WordPress profile page for others.".$this->url_end($urls,'profiles.wordpress.org/DavidAnderson/'); |
| 1351 | break; |
| 1352 | case 4: |
| 1353 | return $this->url_start($urls,'www.simbahosting.co.uk')."Need high-quality WordPress hosting from WordPress specialists? (Including automatic backups and 1-click installer). Get it from the creators of UpdraftPlus.".$this->url_end($urls,'www.simbahosting.co.uk'); |
| 1354 | break; |
| 1355 | case 5: |
| 1356 | return "Need custom WordPress services from experts (including bespoke development)?".$this->url_start($urls,'www.simbahosting.co.uk/s3/products-and-services/wordpress-experts/')." Get them from the creators of UpdraftPlus.".$this->url_end($urls,'www.simbahosting.co.uk/s3/products-and-services/wordpress-experts/'); |
| 1357 | break; |
| 1358 | } |
| 1359 | } |
| 1360 | |
| 1361 | function settings_output() { |
| 1362 | |
| 1363 | /* |
| 1364 | we use request here because the initial restore is triggered by a POSTed form. we then may need to obtain credentials |
| 1365 | for the WP_Filesystem. to do this WP outputs a form that we can't insert variables into (apparently). So the values are |
| 1366 | passed back in as GET parameters. REQUEST covers both GET and POST so this weird logic works. |
| 1367 | */ |
| 1368 | if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'updraft_restore' && isset($_REQUEST['backup_timestamp'])) { |
| 1369 | $backup_success = $this->restore_backup($_REQUEST['backup_timestamp']); |
| 1370 | if(empty($this->errors) && $backup_success == true) { |
| 1371 | echo '<p>Restore successful!</p><br/>'; |
| 1372 | echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus&updraft_restore_success=true">Return to Updraft Configuration</a>.'; |
| 1373 | return; |
| 1374 | } else { |
| 1375 | echo '<p>Restore failed...</p><ul>'; |
| 1376 | foreach ($this->errors as $err) { |
| 1377 | echo "<li>"; |
| 1378 | if (is_string($err)) { echo htmlspecialchars($err); } else { |
| 1379 | print_r($err); |
| 1380 | } |
| 1381 | echo "</li>"; |
| 1382 | } |
| 1383 | echo '</ul><b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.'; |
| 1384 | return; |
| 1385 | } |
| 1386 | //uncomment the below once i figure out how i want the flow of a restoration to work. |
| 1387 | //echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.'; |
| 1388 | } |
| 1389 | $deleted_old_dirs = false; |
| 1390 | if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'updraft_delete_old_dirs') { |
| 1391 | if($this->delete_old_dirs()) { |
| 1392 | $deleted_old_dirs = true; |
| 1393 | } else { |
| 1394 | echo '<p>Old directory removal failed for some reason. You may want to do this manually.</p><br/>'; |
| 1395 | } |
| 1396 | echo '<p>Old directories successfully removed.</p><br/>'; |
| 1397 | echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.'; |
| 1398 | return; |
| 1399 | } |
| 1400 | |
| 1401 | if(isset($_GET['error'])) { |
| 1402 | echo "<p><strong>ERROR:</strong> ".htmlspecialchars($_GET['error'])."</p>"; |
| 1403 | } |
| 1404 | if(isset($_GET['message'])) { |
| 1405 | echo "<p><strong>Note:</strong> ".htmlspecialchars($_GET['message'])."</p>"; |
| 1406 | } |
| 1407 | |
| 1408 | if(isset($_GET['action']) && $_GET['action'] == 'updraft_create_backup_dir') { |
| 1409 | if(!$this->create_backup_dir()) { |
| 1410 | echo '<p>Backup directory could not be created...</p><br/>'; |
| 1411 | } |
| 1412 | echo '<p>Backup directory successfully created.</p><br/>'; |
| 1413 | echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.'; |
| 1414 | return; |
| 1415 | } |
| 1416 | |
| 1417 | if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') { |
| 1418 | echo '<div class="updated fade" style="max-width: 800px; font-size:140%; line-height: 140%; padding:14px; clear:left;"><strong>Schedule backup:</strong> '; |
| 1419 | if (wp_schedule_single_event(time()+5, 'updraft_backup_all') === false) { |
| 1420 | echo "Failed."; |
| 1421 | } else { |
| 1422 | echo "OK. Now load a page from your site to make sure the schedule can trigger."; |
| 1423 | } |
| 1424 | echo '</div>'; |
| 1425 | } |
| 1426 | |
| 1427 | if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') $this->backup(true,true); |
| 1428 | |
| 1429 | if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_db') $this->backup_db(); |
| 1430 | |
| 1431 | ?> |
| 1432 | <div class="wrap"> |
| 1433 | <h1>UpdraftPlus - Backup/Restore</h1> |
| 1434 | |
| 1435 | Maintained by <b>David Anderson</b> (<a href="http://david.dw-perspective.org.uk">Homepage</a> | <a href="http://wordshell.net">WordShell - WordPress command line</a> | <a href="http://david.dw-perspective.org.uk/donate">Donate</a> | <a href="http://wordpress.org/extend/plugins/updraftplus/faq/">FAQs</a> | <a href="http://profiles.wordpress.org/davidanderson/">My other WordPress plugins</a>). Version: <?php echo $this->version; ?> |
| 1436 | <br> |
| 1437 | <?php |
| 1438 | if(isset($_GET['updraft_restore_success'])) { |
| 1439 | echo "<div style=\"color:blue\">Your backup has been restored. Your old themes, uploads, and plugins directories have been retained with \"-old\" appended to their name. Remove them when you are satisfied that the backup worked properly. At this time Updraft does not automatically restore your DB. You will need to use an external tool like phpMyAdmin to perform that task.</div>"; |
| 1440 | } |
| 1441 | |
| 1442 | $ws_advert = $this->wordshell_random_advert(1); |
| 1443 | echo <<<ENDHERE |
| 1444 | <div class="updated fade" style="max-width: 800px; font-size:140%; line-height: 140%; padding:14px; clear:left;">${ws_advert}</div> |
| 1445 | ENDHERE; |
| 1446 | |
| 1447 | if($deleted_old_dirs) { |
| 1448 | echo '<div style="color:blue">Old directories successfully deleted.</div>'; |
| 1449 | } |
| 1450 | if(!$this->memory_check(96)) {?> |
| 1451 | <div style="color:orange">Your PHP memory limit is too low. UpdraftPlus attempted to raise it but was unsuccessful. This plugin may not work properly with a memory limit of less than 96 Mb (though on the other hand, it has been used successfully with a 32Mb limit - your mileage may vary, but don't blame us!). Current limit is: <?php echo $this->memory_check_current(); ?> Mb</div> |
| 1452 | <?php |
| 1453 | } |
| 1454 | if(!$this->execution_time_check(300)) {?> |
| 1455 | <div style="color:orange">Your PHP max_execution_time is less than 300 seconds. This probably means you're running in safe_mode. Either disable safe_mode or modify your php.ini to set max_execution_time to a higher number. If you do not, there is a chance Updraft will be unable to complete a backup. Present limit is: <?php echo ini_get('max_execution_time'); ?> seconds.</div> |
| 1456 | <?php |
| 1457 | } |
| 1458 | |
| 1459 | if($this->scan_old_dirs()) {?> |
| 1460 | <div style="color:orange">You have old directories from a previous backup. Click to delete them after you have verified that the restoration worked.</div> |
| 1461 | <form method="post" action="<?php echo remove_query_arg(array('updraft_restore_success','action')) ?>"> |
| 1462 | <input type="hidden" name="action" value="updraft_delete_old_dirs" /> |
| 1463 | <input type="submit" class="button-primary" value="Delete Old Dirs" onclick="return(confirm('Are you sure you want to delete the old directories? This cannot be undone.'))" /> |
| 1464 | </form> |
| 1465 | <?php |
| 1466 | } |
| 1467 | if(!empty($this->errors)) { |
| 1468 | foreach($this->errors as $error) { |
| 1469 | // ignoring severity |
| 1470 | echo '<div style="color:red">'.$error['error'].'</div>'; |
| 1471 | } |
| 1472 | } |
| 1473 | ?> |
| 1474 | |
| 1475 | <h2 style="clear:left;">Existing Schedule And Backups</h2> |
| 1476 | <table class="form-table" style="float:left; clear: both; width:475px"> |
| 1477 | <tr> |
| 1478 | <?php |
| 1479 | $updraft_dir = $this->backups_dir_location(); |
| 1480 | $next_scheduled_backup = wp_next_scheduled('updraft_backup'); |
| 1481 | $next_scheduled_backup = ($next_scheduled_backup) ? date('D, F j, Y H:i T',$next_scheduled_backup) : 'No backups are scheduled at this time.'; |
| 1482 | $next_scheduled_backup_database = wp_next_scheduled('updraft_backup_database'); |
| 1483 | if (get_option('updraft_interval_database',get_option('updraft_interval')) == get_option('updraft_interval')) { |
| 1484 | $next_scheduled_backup_database = "Will take place at the same time as the files backup."; |
| 1485 | } else { |
| 1486 | $next_scheduled_backup_database = ($next_scheduled_backup_database) ? date('D, F j, Y H:i T',$next_scheduled_backup_database) : 'No backups are scheduled at this time.'; |
| 1487 | } |
| 1488 | $current_time = date('D, F j, Y H:i T',time()); |
| 1489 | $updraft_last_backup = get_option('updraft_last_backup'); |
| 1490 | if($updraft_last_backup) { |
| 1491 | $last_backup = ($updraft_last_backup['success']) ? date('D, F j, Y H:i T',$updraft_last_backup['backup_time']) : print_r($updraft_last_backup['errors'],true); |
| 1492 | $last_backup_color = ($updraft_last_backup['success']) ? 'green' : 'red'; |
| 1493 | if (!empty($updraft_last_backup['backup_nonce'])) { |
| 1494 | $potential_log_file = $updraft_dir."/log.".$updraft_last_backup['backup_nonce'].".txt"; |
| 1495 | if (is_readable($potential_log_file)) $last_backup .= "<br><a href=\"?page=updraftplus&action=downloadlog&updraftplus_backup_nonce=".$updraft_last_backup['backup_nonce']."\">Download log file</a>"; |
| 1496 | } |
| 1497 | } else { |
| 1498 | $last_backup = 'No backup has been completed.'; |
| 1499 | $last_backup_color = 'blue'; |
| 1500 | } |
| 1501 | |
| 1502 | if(is_writable($updraft_dir)) { |
| 1503 | $dir_info = '<span style="color:green">Backup directory specified is writable, which is good.</span>'; |
| 1504 | $backup_disabled = ""; |
| 1505 | } else { |
| 1506 | $backup_disabled = 'disabled="disabled"'; |
| 1507 | $dir_info = '<span style="color:red">Backup directory specified is <b>not</b> writable, or does not exist. <span style="font-size:110%;font-weight:bold"><a href="options-general.php?page=updraftplus&action=updraft_create_backup_dir">Click here</a></span> to attempt to create the directory and set the permissions. If that is unsuccessful check the permissions on your server or change it to another directory that is writable by your web server process.</span>'; |
| 1508 | } |
| 1509 | ?> |
| 1510 | |
| 1511 | <th>The Time Now:</th> |
| 1512 | <td style="color:blue"><?php echo $current_time?></td> |
| 1513 | </tr> |
| 1514 | <tr> |
| 1515 | <th>Next Scheduled Files Backup:</th> |
| 1516 | <td style="color:blue"><?php echo $next_scheduled_backup?></td> |
| 1517 | </tr> |
| 1518 | <tr> |
| 1519 | <th>Next Scheduled DB Backup:</th> |
| 1520 | <td style="color:blue"><?php echo $next_scheduled_backup_database?></td> |
| 1521 | </tr> |
| 1522 | <tr> |
| 1523 | <th>Last Backup:</th> |
| 1524 | <td style="color:<?php echo $last_backup_color ?>"><?php echo $last_backup?></td> |
| 1525 | </tr> |
| 1526 | </table> |
| 1527 | <div style="float:left; width:200px; padding-top: 100px;"> |
| 1528 | <form method="post" action=""> |
| 1529 | <input type="hidden" name="action" value="updraft_backup" /> |
| 1530 | <p><input type="submit" <?php echo $backup_disabled ?> class="button-primary" value="Backup Now!" style="padding-top:7px;padding-bottom:7px;font-size:24px !important" onclick="return(confirm('This will schedule a one-time backup. To trigger the backup you should go ahead, then wait 10 seconds, then load a page on your site.'))" /></p> |
| 1531 | </form> |
| 1532 | <div style="position:relative"> |
| 1533 | <div style="position:absolute;top:0;left:0"> |
| 1534 | <?php |
| 1535 | $backup_history = get_option('updraft_backup_history'); |
| 1536 | $backup_history = (is_array($backup_history))?$backup_history:array(); |
| 1537 | $restore_disabled = (count($backup_history) == 0) ? 'disabled="disabled"' : ""; |
| 1538 | ?> |
| 1539 | <input type="button" class="button-primary" <?php echo $restore_disabled ?> value="Restore" style="padding-top:7px;padding-bottom:7px;font-size:24px !important" onclick="jQuery('#backup-restore').fadeIn('slow');jQuery(this).parent().fadeOut('slow')" /> |
| 1540 | </div> |
| 1541 | <div style="display:none;position:absolute;top:0;left:0" id="backup-restore"> |
| 1542 | <form method="post" action=""> |
| 1543 | <b>Choose: </b> |
| 1544 | <select name="backup_timestamp" style="display:inline"> |
| 1545 | <?php |
| 1546 | foreach($backup_history as $key=>$value) { |
| 1547 | echo "<option value='$key'>".date('Y-m-d G:i',$key)."</option>\n"; |
| 1548 | } |
| 1549 | ?> |
| 1550 | </select> |
| 1551 | |
| 1552 | <input type="hidden" name="action" value="updraft_restore" /> |
| 1553 | <input type="submit" <?php echo $restore_disabled ?> class="button-primary" value="Restore Now!" style="padding-top:7px;margin-top:5px;padding-bottom:7px;font-size:24px !important" onclick="return(confirm('Restoring from backup will replace this site\'s themes, plugins, uploads and other content directories (according to what is contained in the backup set which you select). Database restoration cannot be done through this process - you must download the database and import yourself (e.g. through PHPMyAdmin). Do you wish to continue with the restoration process?'))" /> |
| 1554 | </form> |
| 1555 | </div> |
| 1556 | </div> |
| 1557 | </div> |
| 1558 | <br style="clear:both" /> |
| 1559 | <table class="form-table"> |
| 1560 | <tr> |
| 1561 | <th>Download Backups</th> |
| 1562 | <td><a href="#" title="Click to see available backups" onclick="jQuery('.download-backups').toggle();return false;"><?php echo count($backup_history)?> available</a></td> |
| 1563 | </tr> |
| 1564 | <tr> |
| 1565 | <td></td><td class="download-backups" style="display:none"> |
| 1566 | <em>Click on a button to download the corresponding file to your computer. If you are using the <a href="http://opera.com">Opera web browser</a> then you should turn Turbo mode off.</em> |
| 1567 | <table> |
| 1568 | <?php |
| 1569 | foreach($backup_history as $key=>$value) { |
| 1570 | ?> |
| 1571 | <tr> |
| 1572 | <td><b><?php echo date('Y-m-d G:i',$key)?></b></td> |
| 1573 | <td> |
| 1574 | <?php if (isset($value['db'])) { ?> |
| 1575 | <form action="admin-ajax.php" method="post"> |
| 1576 | <input type="hidden" name="action" value="updraft_download_backup" /> |
| 1577 | <input type="hidden" name="type" value="db" /> |
| 1578 | <input type="hidden" name="timestamp" value="<?php echo $key?>" /> |
| 1579 | <input type="submit" value="Database" /> |
| 1580 | </form> |
| 1581 | <?php } else { echo "(No database)"; } ?> |
| 1582 | </td> |
| 1583 | <td> |
| 1584 | <?php if (isset($value['plugins'])) { ?> |
| 1585 | <form action="admin-ajax.php" method="post"> |
| 1586 | <input type="hidden" name="action" value="updraft_download_backup" /> |
| 1587 | <input type="hidden" name="type" value="plugins" /> |
| 1588 | <input type="hidden" name="timestamp" value="<?php echo $key?>" /> |
| 1589 | <input type="submit" value="Plugins" /> |
| 1590 | </form> |
| 1591 | <?php } else { echo "(No plugins)"; } ?> |
| 1592 | </td> |
| 1593 | <td> |
| 1594 | <?php if (isset($value['themes'])) { ?> |
| 1595 | <form action="admin-ajax.php" method="post"> |
| 1596 | <input type="hidden" name="action" value="updraft_download_backup" /> |
| 1597 | <input type="hidden" name="type" value="themes" /> |
| 1598 | <input type="hidden" name="timestamp" value="<?php echo $key?>" /> |
| 1599 | <input type="submit" value="Themes" /> |
| 1600 | </form> |
| 1601 | <?php } else { echo "(No themes)"; } ?> |
| 1602 | </td> |
| 1603 | <td> |
| 1604 | <?php if (isset($value['uploads'])) { ?> |
| 1605 | <form action="admin-ajax.php" method="post"> |
| 1606 | <input type="hidden" name="action" value="updraft_download_backup" /> |
| 1607 | <input type="hidden" name="type" value="uploads" /> |
| 1608 | <input type="hidden" name="timestamp" value="<?php echo $key?>" /> |
| 1609 | <input type="submit" value="Uploads" /> |
| 1610 | </form> |
| 1611 | <?php } else { echo "(No uploads)"; } ?> |
| 1612 | </td> |
| 1613 | <td> |
| 1614 | <?php if (isset($value['others'])) { ?> |
| 1615 | <form action="admin-ajax.php" method="post"> |
| 1616 | <input type="hidden" name="action" value="updraft_download_backup" /> |
| 1617 | <input type="hidden" name="type" value="others" /> |
| 1618 | <input type="hidden" name="timestamp" value="<?php echo $key?>" /> |
| 1619 | <input type="submit" value="Others" /> |
| 1620 | </form> |
| 1621 | <?php } else { echo "(No others)"; } ?> |
| 1622 | </td> |
| 1623 | <td> |
| 1624 | <?php if (isset($value['nonce']) && preg_match("/^[0-9a-f]{12}$/",$value['nonce']) && is_readable($updraft_dir.'/log.'.$value['nonce'].'.txt')) { ?> |
| 1625 | <form action="options-general.php" method="get"> |
| 1626 | <input type="hidden" name="action" value="downloadlog" /> |
| 1627 | <input type="hidden" name="page" value="updraftplus" /> |
| 1628 | <input type="hidden" name="updraftplus_backup_nonce" value="<?php echo $value['nonce']; ?>" /> |
| 1629 | <input type="submit" value="Backup Log" /> |
| 1630 | </form> |
| 1631 | <?php } else { echo "(No backup log)"; } ?> |
| 1632 | </td> |
| 1633 | </tr> |
| 1634 | <?php }?> |
| 1635 | </table> |
| 1636 | </td> |
| 1637 | </tr> |
| 1638 | </table> |
| 1639 | <form method="post" action="options.php"> |
| 1640 | <?php settings_fields('updraft-options-group'); ?> |
| 1641 | <h2>Configure Backup Contents And Schedule</h2> |
| 1642 | <table class="form-table" style="width:850px;"> |
| 1643 | <tr> |
| 1644 | <th>File Backup Intervals:</th> |
| 1645 | <td><select name="updraft_interval"> |
| 1646 | <?php |
| 1647 | $intervals = array ("manual" => "Manual", 'every4hours' => "Every 4 hours", 'every8hours' => "Every 8 hours", 'twicedaily' => "Every 12 hours", 'daily' => "Daily", 'weekly' => "Weekly", 'fortnightly' => "Fortnightly", 'monthly' => "Monthly"); |
| 1648 | foreach ($intervals as $cronsched => $descrip) { |
| 1649 | echo "<option value=\"$cronsched\" "; |
| 1650 | if ($cronsched == get_option('updraft_interval','manual')) echo 'selected="selected"'; |
| 1651 | echo ">$descrip</option>\n"; |
| 1652 | } |
| 1653 | ?> |
| 1654 | </select></td> |
| 1655 | </tr> |
| 1656 | <tr> |
| 1657 | <th>Database Backup Intervals:</th> |
| 1658 | <td><select name="updraft_interval_database"> |
| 1659 | <?php |
| 1660 | foreach ($intervals as $cronsched => $descrip) { |
| 1661 | echo "<option value=\"$cronsched\" "; |
| 1662 | if ($cronsched == get_option('updraft_interval_database',get_option('updraft_interval'))) echo 'selected="selected"'; |
| 1663 | echo ">$descrip</option>\n"; |
| 1664 | } |
| 1665 | ?> |
| 1666 | </select></td> |
| 1667 | </tr> |
| 1668 | <tr class="backup-interval-description"> |
| 1669 | <td></td><td>If you would like to automatically schedule backups, choose schedules from the dropdown above. Backups will occur at the interval specified starting just after the current time. If you choose manual you must click the "Backup Now!" button whenever you wish a backup to occur. If the two schedules are the same, then the two backups will take place together.</td> |
| 1670 | </tr> |
| 1671 | <?php |
| 1672 | # The true (default value if non-existent) here has the effect of forcing a default of on. |
| 1673 | $include_themes = (get_option('updraft_include_themes',true)) ? 'checked="checked"' : ""; |
| 1674 | $include_plugins = (get_option('updraft_include_plugins',true)) ? 'checked="checked"' : ""; |
| 1675 | $include_uploads = (get_option('updraft_include_uploads',true)) ? 'checked="checked"' : ""; |
| 1676 | $include_others = (get_option('updraft_include_others',true)) ? 'checked="checked"' : ""; |
| 1677 | $include_others_exclude = get_option('updraft_include_others_exclude',UPDRAFT_DEFAULT_OTHERS_EXCLUDE); |
| 1678 | ?> |
| 1679 | <tr> |
| 1680 | <th>Include in Files Backup:</th> |
| 1681 | <td> |
| 1682 | <input type="checkbox" name="updraft_include_plugins" value="1" <?php echo $include_plugins; ?> /> Plugins<br> |
| 1683 | <input type="checkbox" name="updraft_include_themes" value="1" <?php echo $include_themes; ?> /> Themes<br> |
| 1684 | <input type="checkbox" name="updraft_include_uploads" value="1" <?php echo $include_uploads; ?> /> Uploads<br> |
| 1685 | <input type="checkbox" name="updraft_include_others" value="1" <?php echo $include_others; ?> /> Any other directories found inside wp-content - but exclude these directories: <input type="text" name="updraft_include_others_exclude" size="32" value="<?php echo htmlspecialchars($include_others_exclude); ?>"/><br> |
| 1686 | Include all of these, unless you are backing them up separately. Note that presently UpdraftPlus backs up these directories only - which is usually everything (except for WordPress core itself which you can download afresh from WordPress.org). But if you have made customised modifications outside of these directories, you need to back them up another way.<br>(<a href="http://wordshell.net">Use WordShell</a> for automatic backup, version control and patching).<br></td> |
| 1687 | </td> |
| 1688 | </tr> |
| 1689 | <tr> |
| 1690 | <th>Retain Backups:</th> |
| 1691 | <?php |
| 1692 | $updraft_retain = get_option('updraft_retain'); |
| 1693 | $retain = ((int)$updraft_retain > 0)?get_option('updraft_retain'):1; |
| 1694 | ?> |
| 1695 | <td><input type="text" name="updraft_retain" value="<?php echo $retain ?>" style="width:50px" /></td> |
| 1696 | </tr> |
| 1697 | <tr class="backup-retain-description"> |
| 1698 | <td></td><td>By default only the most recent backup is retained. If you'd like to preserve more, specify the number here. (This many of <strong>both</strong> files and database backups will be retained.)</td> |
| 1699 | </tr> |
| 1700 | <tr> |
| 1701 | <th>Email:</th> |
| 1702 | <td><input type="text" style="width:260px" name="updraft_email" value="<?php echo get_option('updraft_email'); ?>" /> <br>Enter an address here to have a report sent (and the whole backup, if you choose) to it.</td> |
| 1703 | </tr> |
| 1704 | <tr class="deletelocal"> |
| 1705 | <th>Delete local backup:</th> |
| 1706 | <td><input type="checkbox" name="updraft_delete_local" value="1" <?php $delete_local = (get_option('updraft_delete_local')) ? 'checked="checked"' : ""; |
| 1707 | echo $delete_local; ?> /> <br>Check this to delete the local backup file (only sensible if you have enabled a remote backup (below), otherwise you will have no backup remaining).</td> |
| 1708 | </tr> |
| 1709 | |
| 1710 | <tr> |
| 1711 | <th>Database encryption phrase:</th> |
| 1712 | <?php |
| 1713 | $updraft_encryptionphrase = get_option('updraft_encryptionphrase'); |
| 1714 | ?> |
| 1715 | <td><input type="text" name="updraft_encryptionphrase" value="<?php echo $updraft_encryptionphrase ?>" style="width:132px" /></td> |
| 1716 | </tr> |
| 1717 | <tr class="backup-crypt-description"> |
| 1718 | <td></td><td>If you enter a string here, it is used to encrypt backups (Rijndael). Do not lose it, or all your backups will be useless. Presently, only the database file is encrypted. This is also the key used to decrypt backups from this admin interface (so if you change it, then automatic decryption will not work until you change it back). You can also use the file example-decrypt.php from inside the UpdraftPlus plugin directory to decrypt manually.</td> |
| 1719 | </tr> |
| 1720 | </table> |
| 1721 | |
| 1722 | <h2>Copying Your Backup To Remote Storage</h2> |
| 1723 | |
| 1724 | <table class="form-table" style="width:850px;"> |
| 1725 | <tr> |
| 1726 | <th>Remote backup:</th> |
| 1727 | <td><select name="updraft_service" id="updraft-service"> |
| 1728 | <?php |
| 1729 | $debug_mode = (get_option('updraft_debug_mode')) ? 'checked="checked"' : ""; |
| 1730 | |
| 1731 | $set = 'selected="selected"'; |
| 1732 | |
| 1733 | // Should be one of s3, ftp, googledrive, email, or whatever else is added |
| 1734 | $active_service = get_option('updraft_service'); |
| 1735 | |
| 1736 | ?> |
| 1737 | <option value="none" <?php |
| 1738 | if ($active_service == "none") echo $set; ?>>None</option> |
| 1739 | <?php |
| 1740 | foreach ($this->backup_methods as $method => $description) { |
| 1741 | echo "<option value=\"$method\""; |
| 1742 | if ($active_service == $method) echo ' '.$set; |
| 1743 | echo '>'.$description; |
| 1744 | echo "</option>\n"; |
| 1745 | } |
| 1746 | ?> |
| 1747 | </select></td> |
| 1748 | </tr> |
| 1749 | <tr class="backup-service-description"> |
| 1750 | <td></td><td>Choose your backup method. If choosing "E-Mail", then be aware that mail servers tend to have size limits; typically around 10-20Mb; backups larger than any limits will not arrive.</td> |
| 1751 | |
| 1752 | </tr> |
| 1753 | <?php |
| 1754 | foreach ($this->backup_methods as $method => $description) { |
| 1755 | require_once(UPDRAFTPLUS_DIR.'/methods/'.$method.'.php'); |
| 1756 | $call_method = "UpdraftPlus_BackupModule_$method"; |
| 1757 | call_user_func(array($call_method, 'config_print')); |
| 1758 | } |
| 1759 | ?> |
| 1760 | </table> |
| 1761 | <script type="text/javascript"> |
| 1762 | /* <![CDATA[ */ |
| 1763 | jQuery(document).ready(function() { |
| 1764 | jQuery('.updraftplusmethod').hide(); |
| 1765 | <?php |
| 1766 | if ($active_service) echo "jQuery('.${active_service}').show();"; |
| 1767 | ?> |
| 1768 | }); |
| 1769 | /* ]]> */ |
| 1770 | </script> |
| 1771 | <table class="form-table" style="width:850px;"> |
| 1772 | <tr> |
| 1773 | <td colspan="2"><h2>Advanced / Debugging Settings</h2></td> |
| 1774 | </tr> |
| 1775 | <tr> |
| 1776 | <th>Backup Directory:</th> |
| 1777 | <td><input type="text" name="updraft_dir" style="width:525px" value="<?php echo htmlspecialchars($updraft_dir); ?>" /></td> |
| 1778 | </tr> |
| 1779 | <tr> |
| 1780 | <td></td><td><?php echo $dir_info ?> This is where Updraft Backup/Restore will write the zip files it creates initially. This directory must be writable by your web server. Typically you'll want to have it inside your wp-content folder (this is the default). <b>Do not</b> place it inside your uploads dir, as that will cause recursion issues (backups of backups of backups of...).</td> |
| 1781 | </tr> |
| 1782 | <tr> |
| 1783 | <th>Debug mode:</th> |
| 1784 | <td><input type="checkbox" name="updraft_debug_mode" value="1" <?php echo $debug_mode; ?> /> <br>Check this to receive more information on the backup process - useful if something is going wrong. You <strong>must</strong> send me this log if you are filing a bug report.</td> |
| 1785 | </tr> |
| 1786 | <tr> |
| 1787 | <td></td> |
| 1788 | <td> |
| 1789 | <p style="margin: 10px 0; padding: 10px; font-size: 140%; background-color: lightYellow; border-color: #E6DB55; border: 1px solid; border-radius: 4px;"> |
| 1790 | <?php |
| 1791 | echo $this->wordshell_random_advert(1); |
| 1792 | ?> |
| 1793 | </p> |
| 1794 | </td> |
| 1795 | </tr> |
| 1796 | <tr> |
| 1797 | <td></td> |
| 1798 | <td> |
| 1799 | <input type="hidden" name="action" value="update" /> |
| 1800 | <input type="submit" class="button-primary" value="Save Changes" /> |
| 1801 | </td> |
| 1802 | </tr> |
| 1803 | </table> |
| 1804 | </form> |
| 1805 | <?php |
| 1806 | if(get_option('updraft_debug_mode')) { |
| 1807 | ?> |
| 1808 | <div style="padding-top: 40px;"> |
| 1809 | <hr> |
| 1810 | <h3>Debug Information</h3> |
| 1811 | <?php |
| 1812 | $peak_memory_usage = memory_get_peak_usage(true)/1024/1024; |
| 1813 | $memory_usage = memory_get_usage(true)/1024/1024; |
| 1814 | echo 'Peak memory usage: '.$peak_memory_usage.' MB<br/>'; |
| 1815 | echo 'Current memory usage: '.$memory_usage.' MB<br/>'; |
| 1816 | echo 'PHP memory limit: '.ini_get('memory_limit').' <br/>'; |
| 1817 | ?> |
| 1818 | <form method="post" action=""> |
| 1819 | <input type="hidden" name="action" value="updraft_backup_debug_all" /> |
| 1820 | <p><input type="submit" class="button-primary" <?php echo $backup_disabled ?> value="Debug Backup" onclick="return(confirm('This will cause an immediate backup. The page will stall loading until it finishes (ie, unscheduled). Use this if you\'re trying to see peak memory usage.'))" /></p> |
| 1821 | </form> |
| 1822 | <form method="post" action=""> |
| 1823 | <input type="hidden" name="action" value="updraft_backup_debug_db" /> |
| 1824 | <p><input type="submit" class="button-primary" <?php echo $backup_disabled ?> value="Debug DB Backup" onclick="return(confirm('This will cause an immediate DB backup. The page will stall loading until it finishes (ie, unscheduled). The backup will remain locally despite your prefs and will not go into the backup history or up into the cloud.'))" /></p> |
| 1825 | </form> |
| 1826 | </div> |
| 1827 | <?php } ?> |
| 1828 | |
| 1829 | <p><em>UpdraftPlus is based on the original Updraft by <b>Paul Kehrer</b> (<a href="http://langui.sh" target="_blank">Blog</a> | <a href="http://twitter.com/reaperhulk" target="_blank">Twitter</a> )</em></p> |
| 1830 | |
| 1831 | |
| 1832 | <script type="text/javascript"> |
| 1833 | /* <![CDATA[ */ |
| 1834 | jQuery(document).ready(function() { |
| 1835 | jQuery('#updraft-service').change(function() { |
| 1836 | jQuery('.updraftplusmethod').hide(); |
| 1837 | var active_class = jQuery(this).val(); |
| 1838 | jQuery('.'+active_class).show(); |
| 1839 | }) |
| 1840 | }) |
| 1841 | jQuery(window).load(function() { |
| 1842 | //this is for hiding the restore progress at the top after it is done |
| 1843 | setTimeout('jQuery("#updraft-restore-progress").toggle(1000)',3000) |
| 1844 | jQuery('#updraft-restore-progress-toggle').click(function() { |
| 1845 | jQuery('#updraft-restore-progress').toggle(500) |
| 1846 | }) |
| 1847 | }) |
| 1848 | /* ]]> */ |
| 1849 | </script> |
| 1850 | <?php |
| 1851 | } |
| 1852 | |
| 1853 | function show_admin_warning($message) { |
| 1854 | echo '<div id="updraftmessage" class="updated fade">'."<p>$message</p></div>"; |
| 1855 | } |
| 1856 | |
| 1857 | function show_admin_warning_unreadablelog() { |
| 1858 | $this->show_admin_warning('<strong>UpdraftPlus notice:</strong> The log file could not be read.</a>'); |
| 1859 | } |
| 1860 | |
| 1861 | function show_admin_warning_googledrive() { |
| 1862 | $this->show_admin_warning('<strong>UpdraftPlus notice:</strong> <a href="?page=updraftplus&action=updraftmethod-googledrive-auth&updraftplus_googleauth=doit">Click here to authenticate your Google Drive account (you will not be able to back up to Google Drive without it).</a>'); |
| 1863 | } |
| 1864 | |
| 1865 | |
| 1866 | } |
| 1867 | |
| 1868 | |
| 1869 | ?> |
| 1870 |