| 1 |
<?php |
| 2 |
/* |
| 3 |
Plugin Name: UpdraftPlus - Backup/Restore |
| 4 |
Plugin URI: http://wordpress.org/extend/plugins/updraftplus |
| 5 |
Description: UpdraftPlus - Backup/Restore is a plugin designed to back up your WordPress site. Uploads, themes, plugins, and your DB can be backed up to Amazon S3, sent to an FTP server, or even emailed to you on a scheduled basis. |
| 6 |
Author: David Anderson. |
| 7 |
Version: 0.7.7 |
| 8 |
Author URI: http://wordshell.net |
| 9 |
*/ |
| 10 |
|
| 11 |
//TODO: |
| 12 |
//Put DB and file backups onto separate schedules. If the option is set identically then do in one run, otherwise do separately. |
| 13 |
//Add DropBox support |
| 14 |
//Add more logging |
| 15 |
//Struggles with large uploads - runs out of time before finishing. Break into chunks? Resume download on later run? (Add a new scheduled event to check on progress? Separate the upload from the creation?). Add in some logging (in a .php file that exists first). |
| 16 |
//More logging |
| 17 |
//improve error reporting. s3 and dir backup have decent reporting now, but not sure i know what to do from here |
| 18 |
//better implementation of retain. one that isn't dependent on being inside the cloud_backup method |
| 19 |
//list backups that aren't tracked (helps with double backup problem) |
| 20 |
//refactor db backup methods a bit. give full credit to wp-db-backup |
| 21 |
//investigate $php_errormsg further |
| 22 |
//pretty up return messages in admin area |
| 23 |
//check s3/ftp download |
| 24 |
//allow upload of backup files too. (specify 1-4 files to restore) |
| 25 |
//Add back donate link in readme.txt header. Donate link: URL |
| 26 |
//user permissions for WP users if ( function_exists('is_site_admin') && ! is_site_admin() ) around backups? |
| 27 |
|
| 28 |
/* More TODO: |
| 29 |
Are all directories in wp-content covered? No; only plugins, themes, content. We should check for others and allow the user the chance to choose which ones he wants |
| 30 |
Add turn-off-foreign-key-checks stuff into mysql dump (does WP even use these?) |
| 31 |
Use only one entry in WP options database |
| 32 |
Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed |
| 33 |
More verbose debug reports, send debug report in the email |
| 34 |
*/ |
| 35 |
|
| 36 |
/* Portions copyright 2010 Paul Kehrer |
| 37 |
Portions copyright 2011-12 David Anderson |
| 38 |
|
| 39 |
This program is free software; you can redistribute it and/or modify |
| 40 |
it under the terms of the GNU General Public License as published by |
| 41 |
the Free Software Foundation; either version 2 of the License, or |
| 42 |
(at your option) any later version. |
| 43 |
|
| 44 |
This program is distributed in the hope that it will be useful, |
| 45 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 46 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 47 |
GNU General Public License for more details. |
| 48 |
|
| 49 |
You should have received a copy of the GNU General Public License |
| 50 |
along with this program; if not, write to the Free Software |
| 51 |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 52 |
*/ |
| 53 |
// TODO: Note this might *lower* the limit - should check first. |
| 54 |
|
| 55 |
@set_time_limit(900); //15 minutes max. i'm not sure how long a really big site could take to back up? |
| 56 |
|
| 57 |
$updraft = new UpdraftPlus(); |
| 58 |
|
| 59 |
if(!$updraft->memory_check(192)) { |
| 60 |
# TODO: Better solution is to split the backup set into manageable chunks based on this limit |
| 61 |
@ini_set('memory_limit', '192M'); //up the memory limit for large backup files... should split the backup set into manageable chunks based on the limit |
| 62 |
} |
| 63 |
|
| 64 |
class UpdraftPlus { |
| 65 |
|
| 66 |
var $version = '0.7.7'; |
| 67 |
|
| 68 |
var $dbhandle; |
| 69 |
var $errors = array(); |
| 70 |
var $nonce; |
| 71 |
var $logfile_name = ""; |
| 72 |
var $logfile_handle = false; |
| 73 |
var $backup_time; |
| 74 |
|
| 75 |
function __construct() { |
| 76 |
// Initialisation actions |
| 77 |
# Create admin page |
| 78 |
add_action('admin_menu', array($this,'add_admin_pages')); |
| 79 |
add_action('admin_init', array($this,'admin_init')); |
| 80 |
add_action('updraft_backup', array($this,'backup')); |
| 81 |
add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup')); |
| 82 |
add_filter('cron_schedules', array($this,'modify_cron_schedules')); |
| 83 |
add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2); |
| 84 |
} |
| 85 |
|
| 86 |
# Adds the settings link under the plugin on the plugin screen. |
| 87 |
function plugin_action_links($links, $file) { |
| 88 |
if ($file == plugin_basename(__FILE__)){ |
| 89 |
$settings_link = '<a href="'.site_url().'/wp-admin/options-general.php?page=updraft-backuprestore.php">'.__("Settings", "wp-updates-notifier").'</a>'; |
| 90 |
array_unshift($links, $settings_link); |
| 91 |
} |
| 92 |
return $links; |
| 93 |
} |
| 94 |
|
| 95 |
function backup_time_nonce() { |
| 96 |
$this->backup_time = time(); |
| 97 |
$this->nonce = substr(md5(time().rand()),20); |
| 98 |
} |
| 99 |
|
| 100 |
# Logs the given line, adding date stamp and newline |
| 101 |
function log($line) { |
| 102 |
if ($this->logfile_handle) { |
| 103 |
fwrite($this->logfile_handle,date('r')." ".$line."\n"); |
| 104 |
} |
| 105 |
} |
| 106 |
|
| 107 |
//scheduled wp-cron events can have a race condition here if page loads are coming fast enough, but there's nothing we can do about it. |
| 108 |
function backup() { |
| 109 |
//generate backup information |
| 110 |
$this->backup_time_nonce(); |
| 111 |
|
| 112 |
//set log file name |
| 113 |
$updraft_dir = $this->backups_dir_location(); |
| 114 |
$this->logfile_name = $updraft_dir. "/log." . $this->nonce . ".txt"; |
| 115 |
|
| 116 |
# Use append mode in case it already exists |
| 117 |
$this->logfile_handle = fopen($this->logfile_name, 'a'); |
| 118 |
// Some information that may be helpful |
| 119 |
global $wp_version; |
| 120 |
$this->log("PHP version: ".phpversion()." WordPress version: ".$wp_version); |
| 121 |
//backup directories and return a numerically indexed array of file paths to the backup files |
| 122 |
$this->log("Beginning backup of directories"); |
| 123 |
$backup_array = $this->backup_dirs(); |
| 124 |
//backup DB and return string of file path |
| 125 |
$this->log("Beginning backup of database"); |
| 126 |
$db_backup = $this->backup_db(); |
| 127 |
//add db path to rest of files |
| 128 |
if(is_array($backup_array)) { $backup_array['db'] = $db_backup; } |
| 129 |
//save this to our history so we can track backups for the retain feature |
| 130 |
$this->log("Saving backup history"); |
| 131 |
$this->save_backup_history($backup_array); |
| 132 |
|
| 133 |
//cloud operations (S3,FTP,email,nothing) |
| 134 |
//this also calls the retain feature at the end (done in this method to reuse existing cloud connections) |
| 135 |
if(is_array($backup_array) && count($backup_array) >0) { |
| 136 |
$this->log("Beginning dispatch of backup to remote"); |
| 137 |
$this->cloud_backup($backup_array); |
| 138 |
} |
| 139 |
//delete local files if the pref is set |
| 140 |
foreach($backup_array as $file) { |
| 141 |
$this->log("Deleting local file: $file"); |
| 142 |
$this->delete_local($file); |
| 143 |
} |
| 144 |
|
| 145 |
//save the last backup info, including errors, if any |
| 146 |
$this->log("Saving last backup information into WordPress db"); |
| 147 |
$this->save_last_backup($backup_array); |
| 148 |
|
| 149 |
if(get_option('updraft_email') != "" && get_option('updraft_service') != 'email') { |
| 150 |
$sendmail_to = get_option('updraft_email'); |
| 151 |
$this->log("Sending email report to: ".$sendmail_to); |
| 152 |
$append_log = ""; |
| 153 |
if(get_option('updraft_debug_mode') && $this->logfile_name != "") { |
| 154 |
$append_log .= "\r\nLog contents:\r\n".file_get_contents($this->logfile_name); |
| 155 |
} |
| 156 |
wp_mail($sendmail_to,'Backed up: '.get_bloginfo('name').' (UpdraftPlus) '.date('Y-m-d H:i',time()),'Site: '.site_url()."\r\nUpdraftPlus WordPress backup is complete.\r\n\r\n".$this->wordshell_random_advert(0)."\r\n".$append_log); |
| 157 |
} |
| 158 |
|
| 159 |
// Close log file |
| 160 |
close($this->logfile_handle); |
| 161 |
if (!get_option('updraft_debug_mode')) { @unlink($this->logfile_name); } |
| 162 |
} |
| 163 |
|
| 164 |
function save_last_backup($backup_array) { |
| 165 |
$success = (empty($this->errors))?1:0; |
| 166 |
$last_backup = array('backup_time'=>$this->backup_time,'backup_array'=>$backup_array,'success'=>$success,'errors'=>$this->errors); |
| 167 |
update_option('updraft_last_backup',$last_backup); |
| 168 |
} |
| 169 |
|
| 170 |
function cloud_backup($backup_array) { |
| 171 |
switch(get_option('updraft_service')) { |
| 172 |
case 's3': |
| 173 |
@set_time_limit(900); |
| 174 |
$this->log("Cloud backup: S3"); |
| 175 |
if (count($backup_array) >0) { $this->s3_backup($backup_array); } |
| 176 |
break; |
| 177 |
case 'ftp': |
| 178 |
@set_time_limit(900); |
| 179 |
$this->log("Cloud backup: FTP"); |
| 180 |
if (count($backup_array) >0) { $this->ftp_backup($backup_array); } |
| 181 |
break; |
| 182 |
case 'email': |
| 183 |
@set_time_limit(900); |
| 184 |
$this->log("Cloud backup: Email"); |
| 185 |
//files can easily get way too big for this... |
| 186 |
foreach($backup_array as $type=>$file) { |
| 187 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 188 |
wp_mail(get_option('updraft_email'),"WordPress Backup ".date('Y-m-d H:i',$this->backup_time),"Backup is of the $type. Be wary; email backups may fail because of file size limitations on mail servers.",null,array($fullpath)); |
| 189 |
} |
| 190 |
//we don't break here so it goes and executes all the default behavior below as well. this gives us retain behavior for email |
| 191 |
default: |
| 192 |
/*retain behavior*/ |
| 193 |
$updraft_retain = get_option('updraft_retain'); |
| 194 |
$retain = (isset($updraft_retain))?get_option('updraft_retain'):1; |
| 195 |
$backup_history = $this->get_backup_history(); |
| 196 |
while (count($backup_history) > $retain) { |
| 197 |
$backup_to_delete = array_pop($backup_history); |
| 198 |
foreach($backup_to_delete as $file) { |
| 199 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 200 |
@unlink($fullpath); //delete it if it's locally available |
| 201 |
} |
| 202 |
} |
| 203 |
update_option('updraft_backup_history',$backup_history); |
| 204 |
/*retain behavior*/ |
| 205 |
break; |
| 206 |
} |
| 207 |
} |
| 208 |
|
| 209 |
function s3_backup($backup_array) { |
| 210 |
if(!class_exists('S3')) { |
| 211 |
require_once(dirname(__FILE__).'/includes/S3.php'); |
| 212 |
} |
| 213 |
$s3 = new S3(get_option('updraft_s3_login'), get_option('updraft_s3_pass')); |
| 214 |
$bucket_name = untrailingslashit(get_option('updraft_s3_remote_path')); |
| 215 |
if (@$s3->putBucket($bucket_name, S3::ACL_PRIVATE)) { |
| 216 |
foreach($backup_array as $file) { |
| 217 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 218 |
if (!$s3->putObjectFile($fullpath, $bucket_name, $file)) { |
| 219 |
$this->error("S3 Error: Failed to upload $fullpath. Error was ".$php_errormsg); |
| 220 |
} |
| 221 |
} |
| 222 |
} else { |
| 223 |
$this->error("S3 Error: Failed to create bucket $bucket_name. Error was ".$php_errormsg); |
| 224 |
} |
| 225 |
/*retain behavior*/ |
| 226 |
$updraft_retain = get_option('updraft_retain'); |
| 227 |
$retain = (isset($updraft_retain))?get_option('updraft_retain'):1; |
| 228 |
$backup_history = $this->get_backup_history(); |
| 229 |
while (count($backup_history) > $retain) { |
| 230 |
$backup_to_delete = array_pop($backup_history); |
| 231 |
foreach($backup_to_delete as $file) { |
| 232 |
//if for some reason one of the backup files is an empty string let's skip it. |
| 233 |
if($file == '') { |
| 234 |
continue; |
| 235 |
} |
| 236 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 237 |
@unlink($fullpath); //delete it if it's locally available |
| 238 |
if (!$s3->deleteObject($bucket_name, $file)) { |
| 239 |
$this->error("S3 Error: Failed to delete object $file. Error was ".$php_errormsg); |
| 240 |
} |
| 241 |
} |
| 242 |
} |
| 243 |
update_option('updraft_backup_history',$backup_history); |
| 244 |
/*retain behavior*/ |
| 245 |
} |
| 246 |
|
| 247 |
function ftp_backup($backup_array) { |
| 248 |
if( !class_exists('ftp_wrapper')) { |
| 249 |
require_once(dirname(__FILE__).'/includes/ftp.class.php'); |
| 250 |
} |
| 251 |
//handle SSL and errors at some point TODO |
| 252 |
$ftp = new ftp_wrapper(get_option('updraft_server_address'),get_option('updraft_ftp_login'),get_option('updraft_ftp_pass')); |
| 253 |
$ftp->passive = true; |
| 254 |
$ftp->connect(); |
| 255 |
//$ftp->make_dir(); we may need to recursively create dirs? TODO |
| 256 |
|
| 257 |
$ftp_remote_path = trailingslashit(get_option('updraft_ftp_remote_path')); |
| 258 |
foreach($backup_array as $file) { |
| 259 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 260 |
$ftp->put($fullpath,$ftp_remote_path.$file,FTP_BINARY); |
| 261 |
} |
| 262 |
|
| 263 |
/*retain behavior*/ |
| 264 |
$updraft_retain = get_option('updraft_retain'); |
| 265 |
$retain = (isset($updraft_retain))?get_option('updraft_retain'):1; |
| 266 |
$backup_history = $this->get_backup_history(); |
| 267 |
while (count($backup_history) > $retain) { |
| 268 |
$backup_to_delete = array_pop($backup_history); |
| 269 |
foreach($backup_to_delete as $file) { |
| 270 |
//if for some reason one of the backup files is an empty string let's skip it. |
| 271 |
if($file == '') { |
| 272 |
continue; |
| 273 |
} |
| 274 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 275 |
@unlink($fullpath); //delete it if it's locally available |
| 276 |
@$ftp->delete($ftp_remote_path.$file); |
| 277 |
} |
| 278 |
} |
| 279 |
update_option('updraft_backup_history',$backup_history); |
| 280 |
/*retain behavior*/ |
| 281 |
} |
| 282 |
|
| 283 |
function delete_local($file) { |
| 284 |
if(get_option('updraft_delete_local')) { |
| 285 |
//need error checking so we don't delete what isn't successfully uploaded? |
| 286 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 287 |
return unlink($fullpath); |
| 288 |
} |
| 289 |
return true; |
| 290 |
} |
| 291 |
|
| 292 |
function backup_dirs() { |
| 293 |
if(!$this->backup_time) { |
| 294 |
$this->backup_time_nonce(); |
| 295 |
} |
| 296 |
$wp_themes_dir = WP_CONTENT_DIR.'/themes'; |
| 297 |
$wp_upload_dir = wp_upload_dir(); |
| 298 |
$wp_upload_dir = $wp_upload_dir['basedir']; |
| 299 |
$wp_plugins_dir = WP_PLUGIN_DIR; |
| 300 |
if(!class_exists('PclZip')) { |
| 301 |
if (file_exists(ABSPATH.'/wp-admin/includes/class-pclzip.php')) { |
| 302 |
require_once(ABSPATH.'/wp-admin/includes/class-pclzip.php'); |
| 303 |
} |
| 304 |
} |
| 305 |
$updraft_dir = $this->backups_dir_location(); |
| 306 |
if(!is_writable($updraft_dir)) { |
| 307 |
$this->error('Backup directory is not writable.','fatal'); |
| 308 |
} |
| 309 |
//get the blog name and rip out all non-alphanumeric chars other than _ |
| 310 |
$blog_name = str_replace(' ','_',get_bloginfo()); |
| 311 |
$blog_name = preg_replace('/[^A-Za-z0-9_]/','', $blog_name); |
| 312 |
if(!$blog_name) { |
| 313 |
$blog_name = 'non_alpha_name'; |
| 314 |
} |
| 315 |
|
| 316 |
$backup_file_base = $updraft_dir.'/backup_'.date('Y-m-d-Hi',$this->backup_time).'_'.$blog_name.'_'.$this->nonce; |
| 317 |
|
| 318 |
$backup_array = array(); |
| 319 |
|
| 320 |
# Plugins |
| 321 |
@set_time_limit(900); |
| 322 |
if (get_option('updraft_include_plugins', true)) { |
| 323 |
$plugins = new PclZip($backup_file_base.'-plugins.zip'); |
| 324 |
if (!$plugins->create($wp_plugins_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) { |
| 325 |
$this->error('Could not create plugins zip. Error was '.$php_errmsg,'fatal'); |
| 326 |
} |
| 327 |
$backup_array['plugins'] = basename($backup_file_base.'-plugins.zip'); |
| 328 |
} |
| 329 |
|
| 330 |
# Themes |
| 331 |
@set_time_limit(900); |
| 332 |
if (get_option('updraft_include_themes', true)) { |
| 333 |
$themes = new PclZip($backup_file_base.'-themes.zip'); |
| 334 |
if (!$themes->create($wp_themes_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) { |
| 335 |
$this->error('Could not create themes zip. Error was '.$php_errmsg,'fatal'); |
| 336 |
} |
| 337 |
$backup_array['themes'] = basename($backup_file_base.'-themes.zip'); |
| 338 |
} |
| 339 |
|
| 340 |
# Uploads |
| 341 |
@set_time_limit(900); |
| 342 |
if (get_option('updraft_include_uploads', true)) { |
| 343 |
$uploads = new PclZip($backup_file_base.'-uploads.zip'); |
| 344 |
if (!$uploads->create($wp_upload_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) { |
| 345 |
$this->error('Could not create uploads zip. Error was '.$php_errmsg,'fatal'); |
| 346 |
} |
| 347 |
$backup_array['uploads'] = basename($backup_file_base.'-uploads.zip'); |
| 348 |
} |
| 349 |
|
| 350 |
return $backup_array; |
| 351 |
} |
| 352 |
|
| 353 |
function save_backup_history($backup_array) { |
| 354 |
//this stores full paths right now. should probably concatenate with ABSPATH to make it easier to move sites |
| 355 |
$backup_history = get_option('updraft_backup_history'); |
| 356 |
$backup_history = (!is_array($backup_history))?array():$backup_history; |
| 357 |
if(is_array($backup_array)) { |
| 358 |
$backup_history[$this->backup_time] = $backup_array; |
| 359 |
update_option('updraft_backup_history',$backup_history); |
| 360 |
} else { |
| 361 |
$this->error('Could not save backup history because we have no backup array. Backup probably failed.'); |
| 362 |
} |
| 363 |
} |
| 364 |
|
| 365 |
function get_backup_history() { |
| 366 |
//$backup_history = get_option('updraft_backup_history'); |
| 367 |
//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 |
| 368 |
global $wpdb; |
| 369 |
$backup_history = @unserialize($wpdb->get_var($wpdb->prepare("SELECT option_value from $wpdb->options WHERE option_name='updraft_backup_history'"))); |
| 370 |
if(is_array($backup_history)) { |
| 371 |
krsort($backup_history); //reverse sort so earliest backup is last on the array. this way we can array_pop |
| 372 |
} else { |
| 373 |
$backup_history = array(); |
| 374 |
} |
| 375 |
return $backup_history; |
| 376 |
} |
| 377 |
|
| 378 |
|
| 379 |
/*START OF WB-DB-BACKUP BLOCK*/ |
| 380 |
|
| 381 |
function backup_db() { |
| 382 |
global $table_prefix, $wpdb; |
| 383 |
if(!$this->backup_time) { |
| 384 |
$this->backup_time_nonce(); |
| 385 |
} |
| 386 |
|
| 387 |
$all_tables = $wpdb->get_results("SHOW TABLES", ARRAY_N); |
| 388 |
$all_tables = array_map(create_function('$a', 'return $a[0];'), $all_tables); |
| 389 |
|
| 390 |
$updraft_dir = $this->backups_dir_location(); |
| 391 |
//get the blog name and rip out all non-alphanumeric chars other than _ |
| 392 |
$blog_name = str_replace(' ','_',get_bloginfo()); |
| 393 |
$blog_name = preg_replace('/[^A-Za-z0-9_]/','', $blog_name); |
| 394 |
if(!$blog_name) { |
| 395 |
$blog_name = 'non_alpha_name'; |
| 396 |
} |
| 397 |
|
| 398 |
$backup_file_base = $updraft_dir.'/backup_'.date('Y-m-d-Hi',$this->backup_time).'_'.$blog_name.'_'.$this->nonce; |
| 399 |
if (is_writable($updraft_dir)) { |
| 400 |
if (function_exists('gzopen')) { |
| 401 |
$this->dbhandle = @gzopen($backup_file_base.'-db.gz','w'); |
| 402 |
} else { |
| 403 |
$this->dbhandle = @fopen($backup_file_base.'-db.gz', 'w'); |
| 404 |
} |
| 405 |
if(!$this->dbhandle) { |
| 406 |
//$this->error(__('Could not open the backup file for writing!','wp-db-backup')); |
| 407 |
} |
| 408 |
} else { |
| 409 |
//$this->error(__('The backup directory is not writable!','wp-db-backup')); |
| 410 |
} |
| 411 |
|
| 412 |
|
| 413 |
//Begin new backup of MySql |
| 414 |
$this->stow("# " . __('WordPress MySQL database backup','wp-db-backup') . "\n"); |
| 415 |
$this->stow("#\n"); |
| 416 |
$this->stow("# " . sprintf(__('Generated: %s','wp-db-backup'),date("l j. F Y H:i T")) . "\n"); |
| 417 |
$this->stow("# " . sprintf(__('Hostname: %s','wp-db-backup'),DB_HOST) . "\n"); |
| 418 |
$this->stow("# " . sprintf(__('Database: %s','wp-db-backup'),$this->backquote(DB_NAME)) . "\n"); |
| 419 |
$this->stow("# --------------------------------------------------------\n"); |
| 420 |
|
| 421 |
|
| 422 |
if (defined("DB_CHARSET")) { |
| 423 |
$this->stow("/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n"); |
| 424 |
$this->stow("/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n"); |
| 425 |
$this->stow("/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n"); |
| 426 |
$this->stow("/*!40101 SET NAMES " . DB_CHARSET . " */;\n"); |
| 427 |
} |
| 428 |
|
| 429 |
foreach ($all_tables as $table) { |
| 430 |
// Increase script execution time-limit to 15 min for every table. |
| 431 |
if ( !ini_get('safe_mode')) @set_time_limit(15*60); |
| 432 |
if ( strpos($table, $table_prefix) == 0 ) { |
| 433 |
// Create the SQL statements |
| 434 |
$this->stow("# --------------------------------------------------------\n"); |
| 435 |
$this->stow("# " . sprintf(__('Table: %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 436 |
$this->stow("# --------------------------------------------------------\n"); |
| 437 |
$this->backup_table($table); |
| 438 |
} else { |
| 439 |
$this->stow("# --------------------------------------------------------\n"); |
| 440 |
$this->stow("# " . sprintf(__('Skipping non-WP table: %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 441 |
$this->stow("# --------------------------------------------------------\n"); |
| 442 |
} |
| 443 |
} |
| 444 |
|
| 445 |
if (defined("DB_CHARSET")) { |
| 446 |
$this->stow("/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n"); |
| 447 |
$this->stow("/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n"); |
| 448 |
$this->stow("/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n"); |
| 449 |
} |
| 450 |
|
| 451 |
$this->close($this->dbhandle); |
| 452 |
|
| 453 |
if (count($this->errors)) { |
| 454 |
return false; |
| 455 |
} else { |
| 456 |
# Encrypt, if requested |
| 457 |
$encryption = get_option('updraft_encryptionphrase'); |
| 458 |
if (strlen($encryption) > 0) { |
| 459 |
$encryption_error = 0; |
| 460 |
require_once(dirname(__FILE__).'/includes/Rijndael.php'); |
| 461 |
$rijndael = new Crypt_Rijndael(); |
| 462 |
$rijndael->setKey($encryption); |
| 463 |
$in_handle = @fopen($backup_file_base.'-db.gz','r'); |
| 464 |
$buffer = ""; |
| 465 |
while (!feof ($in_handle)) { |
| 466 |
$buffer .= fread($in_handle, 16384); |
| 467 |
} |
| 468 |
fclose ($in_handle); |
| 469 |
$out_handle = @fopen($backup_file_base.'-db.gz.crypt','w'); |
| 470 |
if (!fwrite($out_handle, $rijndael->encrypt($buffer))) {$encryption_error = 1;} |
| 471 |
fclose ($out_handle); |
| 472 |
if (0 == $encryption_error) { |
| 473 |
# Delete unencrypted file |
| 474 |
@unlink($backup_file_base.'-db.gz'); |
| 475 |
return basename($backup_file_base.'-db.gz.crypt'); |
| 476 |
} else { |
| 477 |
$this->error("Encryption error occurred when encrypting database. Aborted."); |
| 478 |
} |
| 479 |
} else { |
| 480 |
return basename($backup_file_base.'-db.gz'); |
| 481 |
} |
| 482 |
} |
| 483 |
|
| 484 |
} //wp_db_backup |
| 485 |
|
| 486 |
/** |
| 487 |
* Taken partially from phpMyAdmin and partially from |
| 488 |
* Alain Wolf, Zurich - Switzerland |
| 489 |
* Website: http://restkultur.ch/personal/wolf/scripts/db_backup/ |
| 490 |
* Modified by Scott Merrill (http://www.skippy.net/) |
| 491 |
* to use the WordPress $wpdb object |
| 492 |
* @param string $table |
| 493 |
* @param string $segment |
| 494 |
* @return void |
| 495 |
*/ |
| 496 |
function backup_table($table, $segment = 'none') { |
| 497 |
global $wpdb; |
| 498 |
|
| 499 |
$table_structure = $wpdb->get_results("DESCRIBE $table"); |
| 500 |
if (! $table_structure) { |
| 501 |
//$this->error(__('Error getting table details','wp-db-backup') . ": $table"); |
| 502 |
return false; |
| 503 |
} |
| 504 |
|
| 505 |
if(($segment == 'none') || ($segment == 0)) { |
| 506 |
// Add SQL statement to drop existing table |
| 507 |
$this->stow("\n\n"); |
| 508 |
$this->stow("#\n"); |
| 509 |
$this->stow("# " . sprintf(__('Delete any existing table %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 510 |
$this->stow("#\n"); |
| 511 |
$this->stow("\n"); |
| 512 |
$this->stow("DROP TABLE IF EXISTS " . $this->backquote($table) . ";\n"); |
| 513 |
|
| 514 |
// Table structure |
| 515 |
// Comment in SQL-file |
| 516 |
$this->stow("\n\n"); |
| 517 |
$this->stow("#\n"); |
| 518 |
$this->stow("# " . sprintf(__('Table structure of table %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 519 |
$this->stow("#\n"); |
| 520 |
$this->stow("\n"); |
| 521 |
|
| 522 |
$create_table = $wpdb->get_results("SHOW CREATE TABLE $table", ARRAY_N); |
| 523 |
if (false === $create_table) { |
| 524 |
$err_msg = sprintf(__('Error with SHOW CREATE TABLE for %s.','wp-db-backup'), $table); |
| 525 |
//$this->error($err_msg); |
| 526 |
$this->stow("#\n# $err_msg\n#\n"); |
| 527 |
} |
| 528 |
$this->stow($create_table[0][1] . ' ;'); |
| 529 |
|
| 530 |
if (false === $table_structure) { |
| 531 |
$err_msg = sprintf(__('Error getting table structure of %s','wp-db-backup'), $table); |
| 532 |
//$this->error($err_msg); |
| 533 |
$this->stow("#\n# $err_msg\n#\n"); |
| 534 |
} |
| 535 |
|
| 536 |
// Comment in SQL-file |
| 537 |
$this->stow("\n\n"); |
| 538 |
$this->stow("#\n"); |
| 539 |
$this->stow('# ' . sprintf(__('Data contents of table %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 540 |
$this->stow("#\n"); |
| 541 |
} |
| 542 |
|
| 543 |
if(($segment == 'none') || ($segment >= 0)) { |
| 544 |
$defs = array(); |
| 545 |
$ints = array(); |
| 546 |
foreach ($table_structure as $struct) { |
| 547 |
if ( (0 === strpos($struct->Type, 'tinyint')) || |
| 548 |
(0 === strpos(strtolower($struct->Type), 'smallint')) || |
| 549 |
(0 === strpos(strtolower($struct->Type), 'mediumint')) || |
| 550 |
(0 === strpos(strtolower($struct->Type), 'int')) || |
| 551 |
(0 === strpos(strtolower($struct->Type), 'bigint')) ) { |
| 552 |
$defs[strtolower($struct->Field)] = ( null === $struct->Default ) ? 'NULL' : $struct->Default; |
| 553 |
$ints[strtolower($struct->Field)] = "1"; |
| 554 |
} |
| 555 |
} |
| 556 |
|
| 557 |
|
| 558 |
// Batch by $row_inc |
| 559 |
if ( ! defined('ROWS_PER_SEGMENT') ) { |
| 560 |
define('ROWS_PER_SEGMENT', 100); |
| 561 |
} |
| 562 |
|
| 563 |
if($segment == 'none') { |
| 564 |
$row_start = 0; |
| 565 |
$row_inc = ROWS_PER_SEGMENT; |
| 566 |
} else { |
| 567 |
$row_start = $segment * ROWS_PER_SEGMENT; |
| 568 |
$row_inc = ROWS_PER_SEGMENT; |
| 569 |
} |
| 570 |
do { |
| 571 |
// don't include extra stuff, if so requested |
| 572 |
$excs = array('revisions' => 0, 'spam' => 1); //TODO, FIX THIS |
| 573 |
$where = ''; |
| 574 |
if ( is_array($excs['spam'] ) && in_array($table, $excs['spam']) ) { |
| 575 |
$where = ' WHERE comment_approved != "spam"'; |
| 576 |
} elseif ( is_array($excs['revisions'] ) && in_array($table, $excs['revisions']) ) { |
| 577 |
$where = ' WHERE post_type != "revision"'; |
| 578 |
} |
| 579 |
|
| 580 |
if ( !ini_get('safe_mode')) @set_time_limit(15*60); |
| 581 |
$table_data = $wpdb->get_results("SELECT * FROM $table $where LIMIT {$row_start}, {$row_inc}", ARRAY_A); |
| 582 |
$entries = 'INSERT INTO ' . $this->backquote($table) . ' VALUES ('; |
| 583 |
// \x08\\x09, not required |
| 584 |
$search = array("\x00", "\x0a", "\x0d", "\x1a"); |
| 585 |
$replace = array('\0', '\n', '\r', '\Z'); |
| 586 |
if($table_data) { |
| 587 |
foreach ($table_data as $row) { |
| 588 |
$values = array(); |
| 589 |
foreach ($row as $key => $value) { |
| 590 |
if ($ints[strtolower($key)]) { |
| 591 |
// make sure there are no blank spots in the insert syntax, |
| 592 |
// yet try to avoid quotation marks around integers |
| 593 |
$value = ( null === $value || '' === $value) ? $defs[strtolower($key)] : $value; |
| 594 |
$values[] = ( '' === $value ) ? "''" : $value; |
| 595 |
} else { |
| 596 |
$values[] = "'" . str_replace($search, $replace, $this->sql_addslashes($value)) . "'"; |
| 597 |
} |
| 598 |
} |
| 599 |
$this->stow(" \n" . $entries . implode(', ', $values) . ');'); |
| 600 |
} |
| 601 |
$row_start += $row_inc; |
| 602 |
} |
| 603 |
} while((count($table_data) > 0) and ($segment=='none')); |
| 604 |
} |
| 605 |
|
| 606 |
if(($segment == 'none') || ($segment < 0)) { |
| 607 |
// Create footer/closing comment in SQL-file |
| 608 |
$this->stow("\n"); |
| 609 |
$this->stow("#\n"); |
| 610 |
$this->stow("# " . sprintf(__('End of data contents of table %s','wp-db-backup'),$this->backquote($table)) . "\n"); |
| 611 |
$this->stow("# --------------------------------------------------------\n"); |
| 612 |
$this->stow("\n"); |
| 613 |
} |
| 614 |
} // end backup_table() |
| 615 |
|
| 616 |
|
| 617 |
function stow($query_line) { |
| 618 |
if (function_exists('gzopen')) { |
| 619 |
if(! @gzwrite($this->dbhandle, $query_line)) { |
| 620 |
//$this->error(__('There was an error writing a line to the backup script:','wp-db-backup') . ' ' . $query_line . ' ' . $php_errormsg); |
| 621 |
} |
| 622 |
} else { |
| 623 |
if(false === @fwrite($this->dbhandle, $query_line)) { |
| 624 |
//$this->error(__('There was an error writing a line to the backup script:','wp-db-backup') . ' ' . $query_line . ' ' . $php_errormsg); |
| 625 |
} |
| 626 |
} |
| 627 |
} |
| 628 |
|
| 629 |
|
| 630 |
function close($handle) { |
| 631 |
if (function_exists('gzopen')) { |
| 632 |
gzclose($handle); |
| 633 |
} else { |
| 634 |
fclose($handle); |
| 635 |
} |
| 636 |
} |
| 637 |
|
| 638 |
/** |
| 639 |
* Logs any error messages |
| 640 |
* @param array $args |
| 641 |
* @return bool |
| 642 |
*/ |
| 643 |
function error($error,$severity='') { |
| 644 |
$this->errors[] = array('error'=>$error,'severity'=>$severity); |
| 645 |
if ($severity == 'fatal') { |
| 646 |
//do something... |
| 647 |
} |
| 648 |
return true; |
| 649 |
} |
| 650 |
|
| 651 |
|
| 652 |
|
| 653 |
/** |
| 654 |
* Add backquotes to tables and db-names in |
| 655 |
* SQL queries. Taken from phpMyAdmin. |
| 656 |
*/ |
| 657 |
function backquote($a_name) { |
| 658 |
if (!empty($a_name) && $a_name != '*') { |
| 659 |
if (is_array($a_name)) { |
| 660 |
$result = array(); |
| 661 |
reset($a_name); |
| 662 |
while(list($key, $val) = each($a_name)) |
| 663 |
$result[$key] = '`' . $val . '`'; |
| 664 |
return $result; |
| 665 |
} else { |
| 666 |
return '`' . $a_name . '`'; |
| 667 |
} |
| 668 |
} else { |
| 669 |
return $a_name; |
| 670 |
} |
| 671 |
} |
| 672 |
|
| 673 |
/** |
| 674 |
* Better addslashes for SQL queries. |
| 675 |
* Taken from phpMyAdmin. |
| 676 |
*/ |
| 677 |
function sql_addslashes($a_string = '', $is_like = false) { |
| 678 |
if ($is_like) $a_string = str_replace('\\', '\\\\\\\\', $a_string); |
| 679 |
else $a_string = str_replace('\\', '\\\\', $a_string); |
| 680 |
return str_replace('\'', '\\\'', $a_string); |
| 681 |
} |
| 682 |
|
| 683 |
/*END OF WP-DB-BACKUP BLOCK */ |
| 684 |
/*END OF WP-DB-BACKUP BLOCK */ |
| 685 |
/*END OF WP-DB-BACKUP BLOCK */ |
| 686 |
/*END OF WP-DB-BACKUP BLOCK */ |
| 687 |
/*END OF WP-DB-BACKUP BLOCK */ |
| 688 |
|
| 689 |
/* |
| 690 |
this function is both the backup scheduler and ostensibly a filter callback for saving the option. |
| 691 |
it is called in the register_setting for the updraft_interval, which means when the admin settings |
| 692 |
are saved it is called. it returns the actual result from wp_filter_nohtml_kses (a sanitization filter) |
| 693 |
so the option can be properly saved. |
| 694 |
*/ |
| 695 |
function schedule_backup($interval) { |
| 696 |
//clear schedule and add new so we don't stack up scheduled backups |
| 697 |
wp_clear_scheduled_hook('updraft_backup'); |
| 698 |
switch($interval) { |
| 699 |
case 'daily': |
| 700 |
case 'weekly': |
| 701 |
case 'monthly': |
| 702 |
wp_schedule_event(time()+30, $interval, 'updraft_backup'); |
| 703 |
break; |
| 704 |
} |
| 705 |
return wp_filter_nohtml_kses($interval); |
| 706 |
} |
| 707 |
|
| 708 |
//wp-cron only has hourly, daily and twicedaily, so we need to add weekly and monthly. |
| 709 |
function modify_cron_schedules($schedules) { |
| 710 |
$schedules['weekly'] = array( |
| 711 |
'interval' => 604800, |
| 712 |
'display' => 'Once Weekly' |
| 713 |
); |
| 714 |
$schedules['monthly'] = array( |
| 715 |
'interval' => 2592000, |
| 716 |
'display' => 'Once Monthly' |
| 717 |
); |
| 718 |
return $schedules; |
| 719 |
} |
| 720 |
|
| 721 |
function backups_dir_location() { |
| 722 |
$updraft_dir = untrailingslashit(get_option('updraft_dir')); |
| 723 |
$default_backup_dir = WP_CONTENT_DIR.'/updraft'; |
| 724 |
//if the option isn't set, default it to /backups inside the upload dir |
| 725 |
$updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir; |
| 726 |
//check for the existence of the dir and an enumeration preventer. |
| 727 |
if(!is_dir($updraft_dir) || !is_file($updraft_dir.'/index.html') || !is_file($updraft_dir.'/.htaccess')) { |
| 728 |
@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 |
| 729 |
@file_put_contents($updraft_dir.'/index.html','Nothing to see here.'); |
| 730 |
@file_put_contents($updraft_dir.'/.htaccess','deny from all'); |
| 731 |
} |
| 732 |
return $updraft_dir; |
| 733 |
} |
| 734 |
|
| 735 |
function updraft_download_backup() { |
| 736 |
$type = $_POST['type']; |
| 737 |
$timestamp = (int)$_POST['timestamp']; |
| 738 |
$backup_history = $this->get_backup_history(); |
| 739 |
$file = $backup_history[$timestamp][$type]; |
| 740 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 741 |
if(!is_readable($fullpath)) { |
| 742 |
//if the file doesn't exist and they're using one of the cloud options, fetch it down from the cloud. |
| 743 |
$this->download_backup($file); |
| 744 |
} |
| 745 |
if(@is_readable($fullpath) && is_file($fullpath)) { |
| 746 |
$len = filesize($fullpath); |
| 747 |
|
| 748 |
$filearr = explode('.',$file); |
| 749 |
//we've only got zip and gz...for now |
| 750 |
$file_ext = array_pop($filearr); |
| 751 |
if($file_ext == 'zip') { |
| 752 |
header('Content-type: application/zip'); |
| 753 |
} else { |
| 754 |
// This catches both when what was popped was 'crypt' (*-db.gz.crypt) and when it was 'gz' (unencrypted) |
| 755 |
header('Content-type: application/x-gzip'); |
| 756 |
} |
| 757 |
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 |
| 758 |
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past |
| 759 |
header("Content-Length: $len;"); |
| 760 |
if ($file_ext == 'crypt') { |
| 761 |
header("Content-Disposition: attachment; filename=\"".substr($file,0,-6)."\";"); |
| 762 |
} else { |
| 763 |
header("Content-Disposition: attachment; filename=\"$file\";"); |
| 764 |
} |
| 765 |
ob_end_flush(); |
| 766 |
if ($file_ext == 'crypt') { |
| 767 |
$encryption = get_option('updraft_encryptionphrase'); |
| 768 |
if ($encryption == "") { |
| 769 |
$this->error('Decryption of database failed: the database file is encrypted, but you have no encryption key entered.'); |
| 770 |
} else { |
| 771 |
require_once(dirname(__FILE__).'/includes/Rijndael.php'); |
| 772 |
$rijndael = new Crypt_Rijndael(); |
| 773 |
$rijndael->setKey($encryption); |
| 774 |
$in_handle = fopen($fullpath,'r'); |
| 775 |
$ciphertext = ""; |
| 776 |
while (!feof ($in_handle)) { |
| 777 |
$ciphertext .= fread($in_handle, 16384); |
| 778 |
} |
| 779 |
fclose ($in_handle); |
| 780 |
print $rijndael->decrypt($ciphertext); |
| 781 |
} |
| 782 |
} else { |
| 783 |
readfile($fullpath); |
| 784 |
} |
| 785 |
$this->delete_local($file); |
| 786 |
exit; //we exit immediately because otherwise admin-ajax appends an additional zero to the end for some reason I don't understand. seriously, why die('0')? |
| 787 |
} else { |
| 788 |
echo 'Download failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then S3 or FTP retrieval may have failed.'; |
| 789 |
} |
| 790 |
} |
| 791 |
|
| 792 |
function download_backup($file) { |
| 793 |
switch(get_option('updraft_service')) { |
| 794 |
case 's3': |
| 795 |
$this->download_s3_backup($file); |
| 796 |
break; |
| 797 |
case 'ftp': |
| 798 |
$this->download_ftp_backup($file); |
| 799 |
break; |
| 800 |
default: |
| 801 |
$this->error('Automatic backup restoration is only available via S3, FTP, and local. Email and downloaded backup restoration must be performed manually.'); |
| 802 |
} |
| 803 |
} |
| 804 |
|
| 805 |
function download_s3_backup($file) { |
| 806 |
if(!class_exists('S3')) { |
| 807 |
require_once(dirname(__FILE__).'/includes/S3.php'); |
| 808 |
} |
| 809 |
$s3 = new S3(get_option('updraft_s3_login'), get_option('updraft_s3_pass')); |
| 810 |
$bucket_name = untrailingslashit(get_option('updraft_s3_remote_path')); |
| 811 |
if (@$s3->putBucket($bucket_name, S3::ACL_PRIVATE)) { |
| 812 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 813 |
if (!$s3->getObject($bucket_name, $file, $fullpath)) { |
| 814 |
$this->error("S3 Error: Failed to download $fullpath. Error was ".$php_errormsg); |
| 815 |
} |
| 816 |
} else { |
| 817 |
$this->error("S3 Error: Failed to create bucket $bucket_name. Error was ".$php_errormsg); |
| 818 |
} |
| 819 |
} |
| 820 |
|
| 821 |
function download_ftp_backup($file) { |
| 822 |
if( !class_exists('ftp_wrapper')) { |
| 823 |
require_once(dirname(__FILE__).'/includes/ftp.class.php'); |
| 824 |
} |
| 825 |
//handle SSL and errors at some point TODO |
| 826 |
$ftp = new ftp_wrapper(get_option('updraft_server_address'),get_option('updraft_ftp_login'),get_option('updraft_ftp_pass')); |
| 827 |
$ftp->passive = true; |
| 828 |
$ftp->connect(); |
| 829 |
//$ftp->make_dir(); we may need to recursively create dirs? TODO |
| 830 |
|
| 831 |
$ftp_remote_path = trailingslashit(get_option('updraft_ftp_remote_path')); |
| 832 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file; |
| 833 |
$ftp->get($fullpath,$ftp_remote_path.$file,FTP_BINARY); |
| 834 |
} |
| 835 |
|
| 836 |
function restore_backup($timestamp) { |
| 837 |
global $wp_filesystem; |
| 838 |
$backup_history = get_option('updraft_backup_history'); |
| 839 |
if(!is_array($backup_history[$timestamp])) { |
| 840 |
echo '<p>This backup does not exist in the backup history -- restoration aborted! timestamp: '.$timestamp.'</p><br/>'; |
| 841 |
return false; |
| 842 |
} |
| 843 |
|
| 844 |
$credentials = request_filesystem_credentials("options-general.php?page=updraft-backuprestore.php&action=updraft_restore&backup_timestamp=$timestamp"); |
| 845 |
WP_Filesystem($credentials); |
| 846 |
if ( $wp_filesystem->errors->get_error_code() ) { |
| 847 |
foreach ( $wp_filesystem->errors->get_error_messages() as $message ) |
| 848 |
show_message($message); |
| 849 |
exit; |
| 850 |
} |
| 851 |
|
| 852 |
//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?) |
| 853 |
echo '<span style="font-weight:bold">Restoration Progress </span><div id="updraft-restore-progress">'; |
| 854 |
|
| 855 |
$updraft_dir = trailingslashit(get_option('updraft_dir')); |
| 856 |
foreach($backup_history[$timestamp] as $type=>$file) { |
| 857 |
$fullpath = $updraft_dir.$file; |
| 858 |
if(!is_readable($fullpath) && $type != 'db') { |
| 859 |
$this->download_backup($file); |
| 860 |
} |
| 861 |
if(is_readable($fullpath) && $type != 'db') { |
| 862 |
if(!class_exists('WP_Upgrader')) { |
| 863 |
require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' ); |
| 864 |
} |
| 865 |
require_once('includes/updraft-restorer.php'); |
| 866 |
$restorer = new Updraft_Restorer(); |
| 867 |
$val = $restorer->restore_backup($fullpath,$type); |
| 868 |
if(is_wp_error($val)) { |
| 869 |
print_r($val); |
| 870 |
echo '</div>'; //close the updraft_restore_progress div even if we error |
| 871 |
return false; |
| 872 |
} |
| 873 |
} |
| 874 |
} |
| 875 |
echo '</div>'; //close the updraft_restore_progress div |
| 876 |
if(ini_get('safe_mode')) { |
| 877 |
echo "<p>DB could not be restored because safe_mode is active on your server. You will need to manually restore the file via phpMyAdmin or another method.</p><br/>"; |
| 878 |
return false; |
| 879 |
} |
| 880 |
return true; |
| 881 |
} |
| 882 |
|
| 883 |
|
| 884 |
//deletes the -old directories that are created when a backup is restored. |
| 885 |
function delete_old_dirs() { |
| 886 |
global $wp_filesystem; |
| 887 |
$credentials = request_filesystem_credentials("options-general.php?page=updraft-backuprestore.php&action=updraft_delete_old_dirs"); |
| 888 |
WP_Filesystem($credentials); |
| 889 |
if ( $wp_filesystem->errors->get_error_code() ) { |
| 890 |
foreach ( $wp_filesystem->errors->get_error_messages() as $message ) |
| 891 |
show_message($message); |
| 892 |
exit; |
| 893 |
} |
| 894 |
|
| 895 |
$to_delete = array('themes-old','plugins-old','uploads-old'); |
| 896 |
|
| 897 |
foreach($to_delete as $name) { |
| 898 |
//recursively delete |
| 899 |
if(!$wp_filesystem->delete(WP_CONTENT_DIR.'/'.$name, true)) { |
| 900 |
return false; |
| 901 |
} |
| 902 |
} |
| 903 |
return true; |
| 904 |
} |
| 905 |
|
| 906 |
//scans the content dir to see if any -old dirs are present |
| 907 |
function scan_old_dirs() { |
| 908 |
$dirArr = scandir(WP_CONTENT_DIR); |
| 909 |
foreach($dirArr as $dir) { |
| 910 |
if(strpos($dir,'-old') !== false) { |
| 911 |
return true; |
| 912 |
} |
| 913 |
} |
| 914 |
return false; |
| 915 |
} |
| 916 |
|
| 917 |
|
| 918 |
function retain_range($input) { |
| 919 |
$input = (int)$input; |
| 920 |
if($input > 0 && $input < 3650) { |
| 921 |
return $input; |
| 922 |
} else { |
| 923 |
return 1; |
| 924 |
} |
| 925 |
} |
| 926 |
|
| 927 |
function create_backup_dir() { |
| 928 |
global $wp_filesystem; |
| 929 |
$credentials = request_filesystem_credentials("options-general.php?page=updraft-backuprestore.php&action=updraft_create_backup_dir"); |
| 930 |
WP_Filesystem($credentials); |
| 931 |
if ( $wp_filesystem->errors->get_error_code() ) { |
| 932 |
foreach ( $wp_filesystem->errors->get_error_messages() as $message ) |
| 933 |
show_message($message); |
| 934 |
exit; |
| 935 |
} |
| 936 |
|
| 937 |
$updraft_dir = untrailingslashit(get_option('updraft_dir')); |
| 938 |
$default_backup_dir = WP_CONTENT_DIR.'/updraft'; |
| 939 |
$updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir; |
| 940 |
|
| 941 |
//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...) |
| 942 |
if(!$wp_filesystem->mkdir($updraft_dir, 0777)) { |
| 943 |
return false; |
| 944 |
} |
| 945 |
return true; |
| 946 |
} |
| 947 |
|
| 948 |
|
| 949 |
function memory_check_current() { |
| 950 |
# Returns in megabytes |
| 951 |
$memory_limit = ini_get('memory_limit'); |
| 952 |
$memory_unit = $memory_limit[strlen($memory_limit)-1]; |
| 953 |
$memory_limit = substr($memory_limit,0,strlen($memory_limit)-1); |
| 954 |
switch($memory_unit) { |
| 955 |
case 'K': |
| 956 |
$memory_limit = $memory_limit/1024; |
| 957 |
break; |
| 958 |
case 'G': |
| 959 |
$memory_limit = $memory_limit*1024; |
| 960 |
break; |
| 961 |
case 'M': |
| 962 |
//assumed size, no change needed |
| 963 |
break; |
| 964 |
} |
| 965 |
return $memory_limit; |
| 966 |
} |
| 967 |
|
| 968 |
function memory_check($memory) { |
| 969 |
$memory_limit = $this->memory_check_current(); |
| 970 |
return ($memory_limit >= $memory)?true:false; |
| 971 |
} |
| 972 |
|
| 973 |
function execution_time_check($time) { |
| 974 |
return (ini_get('max_execution_time') >= $time)?true:false; |
| 975 |
} |
| 976 |
|
| 977 |
function admin_init() { |
| 978 |
if(get_option('updraft_debug_mode')) { |
| 979 |
ini_set('display_errors',1); |
| 980 |
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED); |
| 981 |
ini_set('track_errors',1); |
| 982 |
} |
| 983 |
wp_enqueue_script('jquery'); |
| 984 |
register_setting( 'updraft-options-group', 'updraft_interval', array($this,'schedule_backup') ); |
| 985 |
register_setting( 'updraft-options-group', 'updraft_retain', array($this,'retain_range') ); |
| 986 |
register_setting( 'updraft-options-group', 'updraft_encryptionphrase', 'wp_filter_nohtml_kses' ); |
| 987 |
register_setting( 'updraft-options-group', 'updraft_service', 'wp_filter_nohtml_kses' ); |
| 988 |
register_setting( 'updraft-options-group', 'updraft_s3_login', 'wp_filter_nohtml_kses' ); |
| 989 |
register_setting( 'updraft-options-group', 'updraft_s3_pass', 'wp_filter_nohtml_kses' ); |
| 990 |
register_setting( 'updraft-options-group', 'updraft_ftp_login', 'wp_filter_nohtml_kses' ); |
| 991 |
register_setting( 'updraft-options-group', 'updraft_ftp_pass', 'wp_filter_nohtml_kses' ); |
| 992 |
register_setting( 'updraft-options-group', 'updraft_dir', 'wp_filter_nohtml_kses' ); |
| 993 |
register_setting( 'updraft-options-group', 'updraft_email', 'wp_filter_nohtml_kses' ); |
| 994 |
register_setting( 'updraft-options-group', 'updraft_s3_remote_path', 'wp_filter_nohtml_kses' ); |
| 995 |
register_setting( 'updraft-options-group', 'updraft_ftp_remote_path', 'wp_filter_nohtml_kses' ); |
| 996 |
register_setting( 'updraft-options-group', 'updraft_server_address', 'wp_filter_nohtml_kses' ); |
| 997 |
register_setting( 'updraft-options-group', 'updraft_delete_local', 'absint' ); |
| 998 |
register_setting( 'updraft-options-group', 'updraft_debug_mode', 'absint' ); |
| 999 |
register_setting( 'updraft-options-group', 'updraft_include_plugins', 'absint' ); |
| 1000 |
register_setting( 'updraft-options-group', 'updraft_include_themes', 'absint' ); |
| 1001 |
register_setting( 'updraft-options-group', 'updraft_include_uploads', 'absint' ); |
| 1002 |
|
| 1003 |
if (current_user_can('manage_options')) { |
| 1004 |
$updraft_dir = $this->backups_dir_location(); |
| 1005 |
if(strpos($updraft_dir,WP_CONTENT_DIR) !== false) { |
| 1006 |
$relative_dir = str_replace(WP_CONTENT_DIR,'',$updraft_dir); |
| 1007 |
$possible_updraft_url = WP_CONTENT_URL.$relative_dir; |
| 1008 |
$resp = wp_remote_request($possible_updraft_url, array('timeout' => 15)); |
| 1009 |
if ( is_wp_error($resp) ) { |
| 1010 |
add_action('admin_notices', array($this,'show_admin_warning_accessible_unknownresult') ); |
| 1011 |
} else { |
| 1012 |
if(strpos($resp['response']['code'],'403') === false) { |
| 1013 |
add_action('admin_notices', array($this,'show_admin_warning_accessible') ); |
| 1014 |
} |
| 1015 |
} |
| 1016 |
if (isset($dir_protection_info)) { |
| 1017 |
} |
| 1018 |
} |
| 1019 |
} |
| 1020 |
} |
| 1021 |
|
| 1022 |
|
| 1023 |
function add_admin_pages() { |
| 1024 |
add_submenu_page('options-general.php', "UpdraftPlus", "UpdraftPlus", "manage_options", "updraft-backuprestore.php", |
| 1025 |
array($this,"settings_output")); |
| 1026 |
} |
| 1027 |
|
| 1028 |
function wordshell_random_advert($urls) { |
| 1029 |
$url_start = ($urls) ? '<a href="http://wordshell.net">' : ""; |
| 1030 |
$url_end = ($urls) ? '</a>' : " (www.wordshell.net)"; |
| 1031 |
if (rand(0,1) == 0) { |
| 1032 |
return "Like automating WordPress operations? Use the CLI? ${url_start}You will love WordShell${url_end} - saves time and money fast."; |
| 1033 |
} else { |
| 1034 |
return "${url_start}Check out WordShell${url_end} - manage WordPress from the command line - huge time-saver"; |
| 1035 |
} |
| 1036 |
} |
| 1037 |
|
| 1038 |
function settings_output() { |
| 1039 |
|
| 1040 |
$ws_advert = $this->wordshell_random_advert(1); |
| 1041 |
echo <<<ENDHERE |
| 1042 |
<div class="updated fade" style="font-size:140%; padding:14px;">${ws_advert}</div> |
| 1043 |
ENDHERE; |
| 1044 |
|
| 1045 |
/* |
| 1046 |
we use request here because the initial restore is triggered by a POSTed form. we then may need to obtain credentials |
| 1047 |
for the WP_Filesystem. to do this WP outputs a form that we can't insert variables into (apparently). So the values are |
| 1048 |
passed back in as GET parameters. REQUEST covers both GET and POST so this weird logic works. |
| 1049 |
*/ |
| 1050 |
if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'updraft_restore' && isset($_REQUEST['backup_timestamp'])) { |
| 1051 |
$backup_success = $this->restore_backup($_REQUEST['backup_timestamp']); |
| 1052 |
if(empty($this->errors) && $backup_success == true) { |
| 1053 |
echo '<p>Restore successful!</p><br/>'; |
| 1054 |
echo '<b>Actions:</b> <a href="options-general.php?page=updraft-backuprestore.php&updraft_restore_success=true">Return to Updraft Configuration</a>.'; |
| 1055 |
return; |
| 1056 |
} else { |
| 1057 |
echo '<p>Restore failed...</p><br/>'; |
| 1058 |
echo '<b>Actions:</b> <a href="options-general.php?page=updraft-backuprestore.php">Return to Updraft Configuration</a>.'; |
| 1059 |
return; |
| 1060 |
} |
| 1061 |
//uncomment the below once i figure out how i want the flow of a restoration to work. |
| 1062 |
//echo '<b>Actions:</b> <a href="options-general.php?page=updraft-backuprestore.php">Return to Updraft Configuration</a>.'; |
| 1063 |
} |
| 1064 |
$deleted_old_dirs = false; |
| 1065 |
if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'updraft_delete_old_dirs') { |
| 1066 |
if($this->delete_old_dirs()) { |
| 1067 |
$deleted_old_dirs = true; |
| 1068 |
} else { |
| 1069 |
echo '<p>Old directory removal failed for some reason. You may want to do this manually.</p><br/>'; |
| 1070 |
} |
| 1071 |
echo '<p>Old directories successfully removed.</p><br/>'; |
| 1072 |
echo '<b>Actions:</b> <a href="options-general.php?page=updraft-backuprestore.php">Return to Updraft Configuration</a>.'; |
| 1073 |
return; |
| 1074 |
} |
| 1075 |
|
| 1076 |
if(isset($_GET['action']) && $_GET['action'] == 'updraft_create_backup_dir') { |
| 1077 |
if(!$this->create_backup_dir()) { |
| 1078 |
echo '<p>Backup directory could not be created...</p><br/>'; |
| 1079 |
} |
| 1080 |
echo '<p>Backup directory successfully created.</p><br/>'; |
| 1081 |
echo '<b>Actions:</b> <a href="options-general.php?page=updraft-backuprestore.php">Return to Updraft Configuration</a>.'; |
| 1082 |
return; |
| 1083 |
} |
| 1084 |
|
| 1085 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') { |
| 1086 |
wp_schedule_single_event(time()+3, 'updraft_backup'); |
| 1087 |
} |
| 1088 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') { |
| 1089 |
$this->backup(); |
| 1090 |
} |
| 1091 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_db') { |
| 1092 |
$this->backup_db(); |
| 1093 |
} |
| 1094 |
|
| 1095 |
?> |
| 1096 |
<div class="wrap"> |
| 1097 |
<h2>UpdraftPlus - Backup/Restore</h2> |
| 1098 |
|
| 1099 |
Version: <b><?php echo $this->version; ?></b><br /> |
| 1100 |
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> ) |
| 1101 |
<br /> |
| 1102 |
Based on 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> ) |
| 1103 |
<br /> |
| 1104 |
<?php |
| 1105 |
if(isset($_GET['updraft_restore_success'])) { |
| 1106 |
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>"; |
| 1107 |
} |
| 1108 |
if($deleted_old_dirs) { |
| 1109 |
echo "<div style=\"color:blue\">Old directories successfully deleted.</div>"; |
| 1110 |
} |
| 1111 |
if(!$this->memory_check(96)) {?> |
| 1112 |
<div style="color:orange">Your PHP memory limit is too low. Updraft attempted to raise it but was unsuccessful. This plugin may not work properly with a memory limit of less than 96 Mb. Current limit is: <?php echo $this->memory_check_current(); ?> Mb</div> |
| 1113 |
<?php |
| 1114 |
} |
| 1115 |
if(!$this->execution_time_check(300)) {?> |
| 1116 |
<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> |
| 1117 |
<?php |
| 1118 |
} |
| 1119 |
|
| 1120 |
if($this->scan_old_dirs()) {?> |
| 1121 |
<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> |
| 1122 |
<form method="post" action="<?php echo remove_query_arg(array('updraft_restore_success','action')) ?>"> |
| 1123 |
<input type="hidden" name="action" value="updraft_delete_old_dirs" /> |
| 1124 |
<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.'))" /> |
| 1125 |
</form> |
| 1126 |
<?php |
| 1127 |
} |
| 1128 |
if(!empty($this->errors)) { |
| 1129 |
foreach($this->errors as $error) { |
| 1130 |
//ignoring severity here right now |
| 1131 |
echo '<div style="color:red">'.$error['error'].'</div>'; |
| 1132 |
} |
| 1133 |
} |
| 1134 |
?> |
| 1135 |
<table class="form-table" style="float:left;width:475px"> |
| 1136 |
<tr> |
| 1137 |
<?php |
| 1138 |
$next_scheduled_backup = wp_next_scheduled('updraft_backup'); |
| 1139 |
if($next_scheduled_backup) { |
| 1140 |
$next_scheduled_backup = date('D, F j, Y H:i T',$next_scheduled_backup); |
| 1141 |
} else { |
| 1142 |
$next_scheduled_backup = 'No backups are scheduled at this time.'; |
| 1143 |
} |
| 1144 |
$current_time = date('D, F j, Y H:i T',time()); |
| 1145 |
$updraft_last_backup = get_option('updraft_last_backup'); |
| 1146 |
if($updraft_last_backup) { |
| 1147 |
if($updraft_last_backup['success']) { |
| 1148 |
$last_backup = date('D, F j, Y H:i T',$updraft_last_backup['backup_time']); |
| 1149 |
$last_backup_color = 'green'; |
| 1150 |
} else { |
| 1151 |
$last_backup = print_r($updraft_last_backup['errors'],true); |
| 1152 |
$last_backup_color = 'red'; |
| 1153 |
} |
| 1154 |
} else { |
| 1155 |
$last_backup = 'No backup has been completed.'; |
| 1156 |
$last_backup_color = 'blue'; |
| 1157 |
} |
| 1158 |
|
| 1159 |
$updraft_dir = $this->backups_dir_location(); |
| 1160 |
if(is_writable($updraft_dir)) { |
| 1161 |
$dir_info = '<span style="color:green">Backup directory specified is writable, which is good.</span>'; |
| 1162 |
$backup_disabled = ""; |
| 1163 |
} else { |
| 1164 |
$backup_disabled = 'disabled="disabled"'; |
| 1165 |
$dir_info = '<span style="color:red">Backup directory specified is <b>not</b> writable. <span style="font-size:110%;font-weight:bold"><a href="options-general.php?page=updraft-backuprestore.php&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>'; |
| 1166 |
} |
| 1167 |
?> |
| 1168 |
<th>Current Time:</th> |
| 1169 |
<td style="color:blue"><?php echo $current_time?></td> |
| 1170 |
</tr> |
| 1171 |
<tr> |
| 1172 |
<th>Next Scheduled Backup:</th> |
| 1173 |
<td style="color:blue"><?php echo $next_scheduled_backup?></td> |
| 1174 |
</tr> |
| 1175 |
<tr> |
| 1176 |
<th>Last Backup:</th> |
| 1177 |
<td style="color:<?php echo $last_backup_color ?>"><?php echo $last_backup?></td> |
| 1178 |
</tr> |
| 1179 |
</table> |
| 1180 |
<div style="float:left;width:200px"> |
| 1181 |
<form method="post" action=""> |
| 1182 |
<input type="hidden" name="action" value="updraft_backup" /> |
| 1183 |
<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 immediately you may need to load a page on your site.'))" /></p> |
| 1184 |
</form> |
| 1185 |
<div style="position:relative"> |
| 1186 |
<div style="position:absolute;top:0;left:0"> |
| 1187 |
<?php |
| 1188 |
$backup_history = get_option('updraft_backup_history'); |
| 1189 |
$backup_history = (is_array($backup_history))?$backup_history:array(); |
| 1190 |
$restore_disabled = (count($backup_history) == 0) ? 'disabled="disabled"' : ""; |
| 1191 |
?> |
| 1192 |
<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')" /> |
| 1193 |
</div> |
| 1194 |
<div style="display:none;position:absolute;top:0;left:0" id="backup-restore"> |
| 1195 |
<form method="post" action=""> |
| 1196 |
<b>Choose: </b> |
| 1197 |
<select name="backup_timestamp" style="display:inline"> |
| 1198 |
<?php |
| 1199 |
foreach($backup_history as $key=>$value) { |
| 1200 |
echo "<option value='$key'>".date('Y-m-d G:i',$key)."</option>\n"; |
| 1201 |
} |
| 1202 |
?> |
| 1203 |
</select> |
| 1204 |
|
| 1205 |
<input type="hidden" name="action" value="updraft_restore" /> |
| 1206 |
<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, and uploads directories. DB restoration must be done separately at this time. Continue with the restoration process?'))" /> |
| 1207 |
</form> |
| 1208 |
</div> |
| 1209 |
</div> |
| 1210 |
</div> |
| 1211 |
<br style="clear:both" /> |
| 1212 |
<table class="form-table"> |
| 1213 |
<tr> |
| 1214 |
<th>Download Backups</th> |
| 1215 |
<td><a href="#" title="Click to see available backups" onclick="jQuery('.download-backups').toggle();return false;"><?php echo count($backup_history)?> available</a></td> |
| 1216 |
</tr> |
| 1217 |
<tr> |
| 1218 |
<td></td><td class="download-backups" style="display:none"> |
| 1219 |
<em>Click on a button to download the corresponding file to your computer. If you are using Opera, you should turn Turbo mode off.</em> |
| 1220 |
<table> |
| 1221 |
<?php |
| 1222 |
foreach($backup_history as $key=>$value) { |
| 1223 |
?> |
| 1224 |
<tr> |
| 1225 |
<td><b><?php echo date('Y-m-d G:i',$key)?></b></td> |
| 1226 |
<td> |
| 1227 |
<?php if (isset($value['db'])) { ?> |
| 1228 |
<form action="admin-ajax.php" method="post"> |
| 1229 |
<input type="hidden" name="action" value="updraft_download_backup" /> |
| 1230 |
<input type="hidden" name="type" value="db" /> |
| 1231 |
<input type="hidden" name="timestamp" value="<?php echo $key?>" /> |
| 1232 |
<input type="submit" value="Database" /> |
| 1233 |
</form> |
| 1234 |
<?php } else { echo "(No database in backup)"; } ?> |
| 1235 |
</td> |
| 1236 |
<td> |
| 1237 |
<?php if (isset($value['plugins'])) { ?> |
| 1238 |
<form action="admin-ajax.php" method="post"> |
| 1239 |
<input type="hidden" name="action" value="updraft_download_backup" /> |
| 1240 |
<input type="hidden" name="type" value="plugins" /> |
| 1241 |
<input type="hidden" name="timestamp" value="<?php echo $key?>" /> |
| 1242 |
<input type="submit" value="Plugins" /> |
| 1243 |
</form> |
| 1244 |
<?php } else { echo "(No plugins in backup)"; } ?> |
| 1245 |
</td> |
| 1246 |
<td> |
| 1247 |
<?php if (isset($value['themes'])) { ?> |
| 1248 |
<form action="admin-ajax.php" method="post"> |
| 1249 |
<input type="hidden" name="action" value="updraft_download_backup" /> |
| 1250 |
<input type="hidden" name="type" value="themes" /> |
| 1251 |
<input type="hidden" name="timestamp" value="<?php echo $key?>" /> |
| 1252 |
<input type="submit" value="Themes" /> |
| 1253 |
</form> |
| 1254 |
<?php } else { echo "(No themes in backup)"; } ?> |
| 1255 |
</td> |
| 1256 |
<td> |
| 1257 |
<?php if (isset($value['uploads'])) { ?> |
| 1258 |
<form action="admin-ajax.php" method="post"> |
| 1259 |
<input type="hidden" name="action" value="updraft_download_backup" /> |
| 1260 |
<input type="hidden" name="type" value="uploads" /> |
| 1261 |
<input type="hidden" name="timestamp" value="<?php echo $key?>" /> |
| 1262 |
<input type="submit" value="Uploads" /> |
| 1263 |
</form> |
| 1264 |
<?php } else { echo "(No uploads in backup)"; } ?> |
| 1265 |
</td> |
| 1266 |
</tr> |
| 1267 |
<?php }?> |
| 1268 |
</table> |
| 1269 |
</td> |
| 1270 |
</tr> |
| 1271 |
</table> |
| 1272 |
<form method="post" action="options.php"> |
| 1273 |
<?php settings_fields('updraft-options-group'); ?> |
| 1274 |
<table class="form-table"> |
| 1275 |
<tr> |
| 1276 |
<th>Backup Directory:</th> |
| 1277 |
<td><input type="text" name="updraft_dir" style="width:525px" value="<?php echo $updraft_dir ?>" /></td> |
| 1278 |
</tr> |
| 1279 |
<tr> |
| 1280 |
<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> |
| 1281 |
</tr> |
| 1282 |
<tr> |
| 1283 |
<th>Backup Intervals:</th> |
| 1284 |
<td><select name="updraft_interval"> |
| 1285 |
<?php |
| 1286 |
$intervals = array ("manual", "daily", "weekly", "monthly"); |
| 1287 |
foreach ($intervals as $ival) { |
| 1288 |
echo "<option value=\"$ival\" "; |
| 1289 |
if ($ival == get_option('updraft_interval')) { echo 'selected="selected"';} |
| 1290 |
echo ">".ucfirst($ival)."</option>\n"; |
| 1291 |
} |
| 1292 |
?> |
| 1293 |
</select></td> |
| 1294 |
</tr> |
| 1295 |
<tr class="backup-interval-description"> |
| 1296 |
<td></td><td>If you would like to automatically schedule backups, choose a schedule from the dropdown above. Backups will occur at the interval specified starting five minutes after the current time. If you choose manual you must click the "Backup Now!" button to cause a backup to occur.</td> |
| 1297 |
</tr> |
| 1298 |
<?php |
| 1299 |
# The true (default value if non-existent) here has the effect of forcing a default of on. |
| 1300 |
$include_themes = (get_option('updraft_include_themes',true)) ? 'checked="checked"' : ""; |
| 1301 |
$include_plugins = (get_option('updraft_include_plugins',true)) ? 'checked="checked"' : ""; |
| 1302 |
$include_uploads = (get_option('updraft_include_uploads',true)) ? 'checked="checked"' : ""; |
| 1303 |
?> |
| 1304 |
<tr> |
| 1305 |
<th>Include in Backup:</th> |
| 1306 |
<td> |
| 1307 |
<input type="checkbox" name="updraft_include_plugins" value="1" <?php echo $include_plugins; ?> /> Plugins<br /> |
| 1308 |
<input type="checkbox" name="updraft_include_themes" value="1" <?php echo $include_themes; ?> /> Themes<br /> |
| 1309 |
<input type="checkbox" name="updraft_include_uploads" value="1" <?php echo $include_uploads; ?> /> Uploads<br /> |
| 1310 |
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. The database is always included.<br />(<a href="http://wordshell.net">Use WordShell</a> for automatic backup, version control and patching).<br /></td> |
| 1311 |
</td> |
| 1312 |
</tr> |
| 1313 |
<tr> |
| 1314 |
<th>Retain Backups:</th> |
| 1315 |
<?php |
| 1316 |
$updraft_retain = get_option('updraft_retain'); |
| 1317 |
$retain = ((int)$updraft_retain > 0)?get_option('updraft_retain'):1; |
| 1318 |
?> |
| 1319 |
<td><input type="text" name="updraft_retain" value="<?php echo $retain ?>" style="width:50px" /></td> |
| 1320 |
</tr> |
| 1321 |
<tr class="backup-retain-description"> |
| 1322 |
<td></td><td>By default only the most recent backup is retained. If you'd like to preserve more, specify the number here.</td> |
| 1323 |
</tr> |
| 1324 |
<tr> |
| 1325 |
<th>Encryption phrase:</th> |
| 1326 |
<?php |
| 1327 |
$updraft_encryptionphrase = get_option('updraft_encryptionphrase'); |
| 1328 |
?> |
| 1329 |
<td><input type="text" name="updraft_encryptionphrase" value="<?php echo $updraft_encryptionphrase ?>" style="width:132px" /></td> |
| 1330 |
</tr> |
| 1331 |
<tr class="backup-crypt-description"> |
| 1332 |
<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> |
| 1333 |
</tr> |
| 1334 |
|
| 1335 |
<tr> |
| 1336 |
<th>Remote backup:</th> |
| 1337 |
<td><select name="updraft_service" id="updraft-service"> |
| 1338 |
<?php |
| 1339 |
$delete_local = (get_option('updraft_delete_local')) ? 'checked="checked"' : ""; |
| 1340 |
$debug_mode = (get_option('updraft_debug_mode')) ? 'checked="checked"' : ""; |
| 1341 |
|
| 1342 |
$display_none = 'style="display:none"'; |
| 1343 |
$s3 = ""; $ftp = ""; $email = ""; |
| 1344 |
$email_display=""; |
| 1345 |
$display_email_complete = ""; |
| 1346 |
$set = 'selected="selected"'; |
| 1347 |
switch(get_option('updraft_service')) { |
| 1348 |
case 's3': |
| 1349 |
$s3 = $set; |
| 1350 |
$ftp_display = $display_none; |
| 1351 |
break; |
| 1352 |
case 'ftp': |
| 1353 |
$ftp = $set; |
| 1354 |
$s3_display = $display_none; |
| 1355 |
break; |
| 1356 |
case 'email': |
| 1357 |
$email = $set; |
| 1358 |
$ftp_display = $display_none; |
| 1359 |
$s3_display = $display_none; |
| 1360 |
$display_email_complete = $display_none; |
| 1361 |
break; |
| 1362 |
default: |
| 1363 |
$none = $set; |
| 1364 |
$ftp_display = $display_none; |
| 1365 |
$s3_display = $display_none; |
| 1366 |
$display_delete_local = $display_none; |
| 1367 |
break; |
| 1368 |
} |
| 1369 |
?> |
| 1370 |
<option value="none" <?php echo $none?>>None</option> |
| 1371 |
<option value="s3" <?php echo $s3?>>Amazon S3</option> |
| 1372 |
<option value="ftp" <?php echo $ftp?>>FTP</option> |
| 1373 |
<option value="email" <?php echo $email?>>E-mail</option> |
| 1374 |
</select></td> |
| 1375 |
</tr> |
| 1376 |
<tr class="backup-service-description"> |
| 1377 |
<td></td><td>Choose which backup method you would like to employ. Be aware that email servers tend to have strict file size limitations and it is possible you will not receive your backup emails (>10MB is a typical threshold). Select none if you do not wish to send your backups anywhere. <b>Not recommended.</b></td> |
| 1378 |
|
| 1379 |
</tr> |
| 1380 |
<tr class="s3" <?php echo $s3_display?>> |
| 1381 |
<th>S3 access key:</th> |
| 1382 |
<td><input type="text" autocomplete="off" style="width:292px" name="updraft_s3_login" value="<?php echo get_option('updraft_s3_login') ?>" /></td> |
| 1383 |
</tr> |
| 1384 |
<tr class="s3" <?php echo $s3_display?>> |
| 1385 |
<th>S3 secret key:</th> |
| 1386 |
<td><input type="password" autocomplete="off" style="width:292px" name="updraft_s3_pass" value="<?php echo get_option('updraft_s3_pass'); ?>" /></td> |
| 1387 |
</tr> |
| 1388 |
<tr class="s3" <?php echo $s3_display?>> |
| 1389 |
<th>S3 bucket:</th> |
| 1390 |
<td><input type="text" style="width:292px" name="updraft_s3_remote_path" value="<?php echo get_option('updraft_s3_remote_path'); ?>" /></td> |
| 1391 |
</tr> |
| 1392 |
<tr class="s3" <?php echo $s3_display?>> |
| 1393 |
<th></th> |
| 1394 |
<td><p>Get your access key and secret key from your AWS page, then pick a (globally unique) bucket name (letters and numbers) to use for storage. (Do not enter the s3:// prefix).</p></td> |
| 1395 |
</tr> |
| 1396 |
<tr class="ftp" <?php echo $ftp_display?>> |
| 1397 |
<th><a href="#" title="Click for help!" onclick="jQuery('.ftp-description').toggle();return false;">FTP Server:</a></th> |
| 1398 |
<td><input type="text" style="width:260px" name="updraft_server_address" value="<?php echo get_option('updraft_server_address'); ?>" /></td> |
| 1399 |
</tr> |
| 1400 |
<tr class="ftp" <?php echo $ftp_display?>> |
| 1401 |
<th><a href="#" title="Click for help!" onclick="jQuery('.ftp-description').toggle();return false;">FTP Login:</a></th> |
| 1402 |
<td><input type="text" autocomplete="off" name="updraft_ftp_login" value="<?php echo get_option('updraft_ftp_login') ?>" /></td> |
| 1403 |
</tr> |
| 1404 |
<tr class="ftp" <?php echo $ftp_display?>> |
| 1405 |
<th><a href="#" title="Click for help!" onclick="jQuery('.ftp-description').toggle();return false;">FTP Password:</a></th> |
| 1406 |
<td><input type="password" autocomplete="off" style="width:260px" name="updraft_ftp_pass" value="<?php echo get_option('updraft_ftp_pass'); ?>" /></td> |
| 1407 |
</tr> |
| 1408 |
<tr class="ftp" <?php echo $ftp_display?>> |
| 1409 |
<th><a href="#" title="Click for help!" onclick="jQuery('.ftp-description').toggle();return false;">Remote Path:</a></th> |
| 1410 |
<td><input type="text" style="width:260px" name="updraft_ftp_remote_path" value="<?php echo get_option('updraft_ftp_remote_path'); ?>" /></td> |
| 1411 |
</tr> |
| 1412 |
<tr class="ftp-description" style="display:none"> |
| 1413 |
<td colspan="2">An FTP remote path will look like '/home/backup/some/folder'</td> |
| 1414 |
</tr> |
| 1415 |
<tr class="email" <?php echo $email_display?>> |
| 1416 |
<th>Email:</th> |
| 1417 |
<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> |
| 1418 |
</tr> |
| 1419 |
<tr class="deletelocal s3 ftp email" <?php echo $display_delete_local?>> |
| 1420 |
<th>Delete local backup:</th> |
| 1421 |
<td><input type="checkbox" name="updraft_delete_local" value="1" <?php echo $delete_local; ?> /> <br />Check this to delete the local backup file after it has been sent off the server.</td> |
| 1422 |
</tr> |
| 1423 |
<tr> |
| 1424 |
<th>Debug mode:</th> |
| 1425 |
<td><input type="checkbox" name="updraft_debug_mode" value="1" <?php echo $debug_mode; ?> /> <br />Check this for more information, if something is going wrong. Will also drop a log file in your backup directory which you can examine.</td> |
| 1426 |
</tr> |
| 1427 |
<tr> |
| 1428 |
<td> |
| 1429 |
<input type="hidden" name="action" value="update" /> |
| 1430 |
<input type="submit" class="button-primary" value="Save Changes" /> |
| 1431 |
</td> |
| 1432 |
</tr> |
| 1433 |
</table> |
| 1434 |
</form> |
| 1435 |
<?php |
| 1436 |
if(get_option('updraft_debug_mode')) { |
| 1437 |
?> |
| 1438 |
<div> |
| 1439 |
<h3>Debug Information</h3> |
| 1440 |
<?php |
| 1441 |
$peak_memory_usage = memory_get_peak_usage(true)/1024/1024; |
| 1442 |
$memory_usage = memory_get_usage(true)/1024/1024; |
| 1443 |
echo 'Peak memory usage: '.$peak_memory_usage.' MB<br/>'; |
| 1444 |
echo 'Current memory usage: '.$memory_usage.' MB<br/>'; |
| 1445 |
echo 'PHP memory limit: '.ini_get('memory_limit').' <br/>'; |
| 1446 |
?> |
| 1447 |
<form method="post" action=""> |
| 1448 |
<input type="hidden" name="action" value="updraft_backup_debug_all" /> |
| 1449 |
<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> |
| 1450 |
</form> |
| 1451 |
<form method="post" action=""> |
| 1452 |
<input type="hidden" name="action" value="updraft_backup_debug_db" /> |
| 1453 |
<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> |
| 1454 |
</form> |
| 1455 |
</div> |
| 1456 |
<?php } ?> |
| 1457 |
<script type="text/javascript"> |
| 1458 |
jQuery(document).ready(function() { |
| 1459 |
jQuery('#updraft-service').change(function() { |
| 1460 |
switch(jQuery(this).val()) { |
| 1461 |
case 'none': |
| 1462 |
jQuery('.deletelocal,.s3,.ftp,.s3-description,.ftp-description').hide() |
| 1463 |
jQuery('.email,.email-complete').show() |
| 1464 |
break; |
| 1465 |
case 's3': |
| 1466 |
jQuery('.ftp,.ftp-description').hide() |
| 1467 |
jQuery('.s3,.deletelocal,.email,.email-complete').show() |
| 1468 |
break; |
| 1469 |
case 'ftp': |
| 1470 |
jQuery('.s3,.s3-description').hide() |
| 1471 |
jQuery('.ftp,.deletelocal,.email,.email-complete').show() |
| 1472 |
break; |
| 1473 |
case 'email': |
| 1474 |
jQuery('.s3,.ftp,.s3-description,.ftp-description,.email-complete').hide() |
| 1475 |
jQuery('.email,.deletelocal').show() |
| 1476 |
break; |
| 1477 |
} |
| 1478 |
}) |
| 1479 |
}) |
| 1480 |
jQuery(window).load(function() { |
| 1481 |
//this is for hiding the restore progress at the top after it is done |
| 1482 |
setTimeout('jQuery("#updraft-restore-progress").toggle(1000)',3000) |
| 1483 |
jQuery('#updraft-restore-progress-toggle').click(function() { |
| 1484 |
jQuery('#updraft-restore-progress').toggle(500) |
| 1485 |
}) |
| 1486 |
}) |
| 1487 |
</script> |
| 1488 |
<?php |
| 1489 |
} |
| 1490 |
|
| 1491 |
/*array2json provided by bin-co.com under BSD license*/ |
| 1492 |
function array2json($arr) { |
| 1493 |
if(function_exists('json_encode')) return stripslashes(json_encode($arr)); //Latest versions of PHP already have this functionality. |
| 1494 |
$parts = array(); |
| 1495 |
$is_list = false; |
| 1496 |
|
| 1497 |
//Find out if the given array is a numerical array |
| 1498 |
$keys = array_keys($arr); |
| 1499 |
$max_length = count($arr)-1; |
| 1500 |
if(($keys[0] == 0) and ($keys[$max_length] == $max_length)) {//See if the first key is 0 and last key is length - 1 |
| 1501 |
$is_list = true; |
| 1502 |
for($i=0; $i<count($keys); $i++) { //See if each key correspondes to its position |
| 1503 |
if($i != $keys[$i]) { //A key fails at position check. |
| 1504 |
$is_list = false; //It is an associative array. |
| 1505 |
break; |
| 1506 |
} |
| 1507 |
} |
| 1508 |
} |
| 1509 |
|
| 1510 |
foreach($arr as $key=>$value) { |
| 1511 |
if(is_array($value)) { //Custom handling for arrays |
| 1512 |
if($is_list) $parts[] = $this->array2json($value); /* :RECURSION: */ |
| 1513 |
else $parts[] = '"' . $key . '":' . $this->array2json($value); /* :RECURSION: */ |
| 1514 |
} else { |
| 1515 |
$str = ''; |
| 1516 |
if(!$is_list) $str = '"' . $key . '":'; |
| 1517 |
|
| 1518 |
//Custom handling for multiple data types |
| 1519 |
if(is_numeric($value)) $str .= $value; //Numbers |
| 1520 |
elseif($value === false) $str .= 'false'; //The booleans |
| 1521 |
elseif($value === true) $str .= 'true'; |
| 1522 |
else $str .= '"' . addslashes($value) . '"'; //All other things |
| 1523 |
// :TODO: Is there any more datatype we should be in the lookout for? (Object?) |
| 1524 |
|
| 1525 |
$parts[] = $str; |
| 1526 |
} |
| 1527 |
} |
| 1528 |
$json = implode(',',$parts); |
| 1529 |
|
| 1530 |
if($is_list) return '[' . $json . ']';//Return numerical JSON |
| 1531 |
return '{' . $json . '}';//Return associative JSON |
| 1532 |
} |
| 1533 |
|
| 1534 |
function show_admin_warning($message) { |
| 1535 |
echo '<div id="updraftmessage" class="updated fade">'; |
| 1536 |
echo "<p>$message</p></div>"; |
| 1537 |
} |
| 1538 |
function show_admin_warning_accessible() { |
| 1539 |
$this->show_admin_warning("UpdraftPlus backup directory specified is accessible via the web. This is a potential security problem (people may be able to download your backups - which is undesirable if your database is not encrypted and if you have non-public assets amongst the files). If using Apache, enable .htaccess support to allow web access to be denied; otherwise, you should deny access manually."); |
| 1540 |
} |
| 1541 |
function show_admin_warning_accessible_unknownresult() { |
| 1542 |
$this->show_admin_warning("UpdraftPlus tried to check if the backup directory is accessible via web, but the result was unknown."); |
| 1543 |
} |
| 1544 |
|
| 1545 |
|
| 1546 |
} |
| 1547 |
|
| 1548 |
?> |
| 1549 |
|