UpdraftPlus WordPress Backup Plugin - Version 1.3.4

Version Description

  • 01/24/2013 =
  • Fixed faulty assumptions in 'resume' code, now leading to more reliable resuming
  • Removed some duplicate code; first attempt and resumptions now uses same code
Download this release

Release Info

Developer DavidAnderson
Plugin Icon 128x128 UpdraftPlus WordPress Backup Plugin
Version 1.3.4
Comparing to
See all releases

Code changes from version 1.3.3 to 1.3.4

Files changed (3) hide show
  1. includes/updraft-restorer.php +3 -3
  2. readme.txt +3 -2
  3. updraftplus.php +129 -210
includes/updraft-restorer.php CHANGED
@@ -20,7 +20,7 @@ class Updraft_Restorer extends WP_Upgrader {
20
  $this->init();
21
  $this->backup_strings();
22
 
23
- $res = $this->fs_connect( array(ABSPATH, WP_CONTENT_DIR) );
24
  if(!$res) exit;
25
 
26
  $wp_dir = trailingslashit($wp_filesystem->abspath());
@@ -80,10 +80,10 @@ class Updraft_Restorer extends WP_Upgrader {
80
 
81
  switch($type) {
82
  case 'uploads':
83
- $wp_filesystem->chmod($wp_dir . "wp-content/$type", 0777, true);
84
  break;
85
  default:
86
- $wp_filesystem->chmod($wp_dir . "wp-content/$type", FS_CHMOD_DIR);
87
  }
88
  }
89
 
20
  $this->init();
21
  $this->backup_strings();
22
 
23
+ $res = $this->fs_connect(array(ABSPATH, WP_CONTENT_DIR) );
24
  if(!$res) exit;
25
 
26
  $wp_dir = trailingslashit($wp_filesystem->abspath());
80
 
81
  switch($type) {
82
  case 'uploads':
83
+ @$wp_filesystem->chmod($wp_dir . "wp-content/$type", 0777, true);
84
  break;
85
  default:
86
+ @$wp_filesystem->chmod($wp_dir . "wp-content/$type", FS_CHMOD_DIR);
87
  }
88
  }
89
 
readme.txt CHANGED
@@ -113,7 +113,7 @@ You can check the changelog for changes; but the original Updraft, before I fork
113
 
114
  = Any known bugs ? =
115
 
116
- Not a bug as such, but one issue to be aware of is that backups of very large sites (lots of uploaded media) can fail, or require a longer period of time to succeed, due to timing out. This depends on how many seconds your web host allows a PHP process to run and how many resources they give you. With such sites, you need to use Amazon S3, which UpdraftPlus supports (since 0.9.20) or Google Drive (since 0.9.21) or Dropbox (since 1.2.19) with chunked, resumable uploads. Other backup methods have code (since 0.9.0) to retry failed uploads of an archive, but the upload cannot be chunked, so if an archive is enormous (i.e. cannot be completely uploaded in the time that PHP is allowed for running on your web host) it cannot work.
117
 
118
  = I encrypted my database - how do I decrypt it? =
119
 
@@ -141,8 +141,9 @@ Thanks for asking - yes, I have. Check out my profile page - http://profiles.wor
141
 
142
  == Changelog ==
143
 
144
- = 1.3.3 - 01/24/2013 =
145
  * Fixed faulty assumptions in 'resume' code, now leading to more reliable resuming
 
146
 
147
  = 1.3.2 - 01/23/2013 =
148
  * Internal reorganisation, enabling UpdraftPlus Premium
113
 
114
  = Any known bugs ? =
115
 
116
+ Not a bug as such, but one issue to be aware of is that backups of very large sites (lots of uploaded media) are quite complex matters, given the limits of running inside WordPress on a huge variety of different web hosting setups. With large sites, you need to use Amazon S3, which UpdraftPlus supports (since 0.9.20) or Google Drive (since 0.9.21) or Dropbox (since 1.2.19), because these support chunked, resumable uploads. Other backup methods have code (since 0.9.0) to retry failed uploads of an archive, but the upload cannot be chunked, so if an archive is enormous (i.e. cannot be completely uploaded in the time that PHP is allowed for running on your web host) it cannot work.
117
 
118
  = I encrypted my database - how do I decrypt it? =
119
 
141
 
142
  == Changelog ==
143
 
144
+ = 1.3.4 - 01/24/2013 =
145
  * Fixed faulty assumptions in 'resume' code, now leading to more reliable resuming
146
+ * Removed some duplicate code; first attempt and resumptions now uses same code
147
 
148
  = 1.3.2 - 01/23/2013 =
149
  * Internal reorganisation, enabling UpdraftPlus Premium
updraftplus.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: UpdraftPlus - Backup/Restore
4
  Plugin URI: http://wordpress.org/extend/plugins/updraftplus
5
  Description: Backup and restore: your content and database can be automatically backed up to Amazon S3, Dropbox, Google Drive, FTP or email, on separate schedules.
6
  Author: David Anderson.
7
- Version: 1.3.3
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  License: GPLv3 or later
10
  Author URI: http://wordshell.net
@@ -14,17 +14,20 @@ Author URI: http://wordshell.net
14
  TODO
15
  //Add SFTP, Box.Net, SugarSync and Microsoft Skydrive support??
16
  //The restorer has a hard-coded wp-content - fix
 
 
 
17
  //improve error reporting / pretty up return messages in admin area. One thing: have a "backup is now finished" flag. Otherwise with the resuming things get ambiguous/confusing. See http://wordpress.org/support/topic/backup-status - user was not aware that backup completely failed. Maybe a "backup status" field for each nonce that gets updated? (Even via AJAX?)
18
  //?? On 'backup now', open up a Lightbox, count down 5 seconds, then start examining the log file (if it can be found)
19
  //Should make clear in dashboard what is a non-fatal error (i.e. can be retried) - leads to unnecessary bug reports
20
- //Eventually, when everything can be resumed, we will no longer need the backup() routine; it can be replaced with the resume() routine
21
  // Should we resume if the only errors were upon deletion (i.e. the backup itself was fine?) Presently we do, but it displays errors for the user to confuse them. Perhaps better to make pruning a separate scheuled task??
22
  // Make jobs *individually* resumable (i.e. all the state info must be keyed on the nonce; then call the resume event *specifying the nonce*)
23
  // Warn the user if their zip-file creation is slooowww...
24
  // Create a "Want Support?" button/console, that leads them through what is needed, and performs some basic tests...
25
  // Resuming partial FTP uploads
26
  // Turn expert options into a jQuery toggle
27
- // Provide backup/restoration for UpdraftPlus's settings, to allow 'bootstrap' on a fresh WP install
28
  // Multiple jobs
29
  // Create single zip, containing even WordPress itself
30
 
@@ -73,7 +76,7 @@ if (!class_exists('UpdraftPlus_Options')) require_once(UPDRAFTPLUS_DIR.'/options
73
 
74
  class UpdraftPlus {
75
 
76
- var $version = '1.3.3';
77
  var $plugin_title = 'UpdraftPlus Backup/Restore';
78
 
79
  // Choices will be shown in the admin menu in the order used here
@@ -89,7 +92,6 @@ class UpdraftPlus {
89
  var $dbhandle_isgz;
90
  var $errors = array();
91
  var $nonce;
92
- var $cronrun_type = "none";
93
  var $logfile_name = "";
94
  var $logfile_handle = false;
95
  var $backup_time;
@@ -97,6 +99,8 @@ class UpdraftPlus {
97
  var $opened_log_time;
98
  var $backup_dir;
99
 
 
 
100
  function __construct() {
101
  // Initialisation actions - takes place on plugin load
102
  # Create admin page
@@ -106,7 +110,7 @@ class UpdraftPlus {
106
  # backup_all is used by the manual "Backup Now" button
107
  add_action('updraft_backup_all', array($this,'backup_all'));
108
  # this is our runs-after-backup event, whose purpose is to see if it succeeded or failed, and resume/mom-up etc.
109
- add_action('updraft_backup_resume', array($this,'backup_resume'));
110
  add_action('wp_enqueue_scripts', array($this, 'ajax_enqueue') );
111
  add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup'));
112
  add_action('wp_ajax_updraft_ajax', array($this, 'updraft_ajax_handler'));
@@ -156,8 +160,6 @@ class UpdraftPlus {
156
  $this->backup_time = time();
157
  $nonce = substr(md5(time().rand()), 20);
158
  $this->nonce = $nonce;
159
- // Short-lived, as we only use this for detecting a race condition
160
- set_transient("updraftplus_runtype_$nonce", $this->cronrun_type, 300);
161
  }
162
 
163
  function logfile_open($nonce) {
@@ -177,50 +179,57 @@ class UpdraftPlus {
177
  UpdraftPlus_Options::update_updraft_option("updraft_lastmessage", $line." (".date('M d H:i:s').")");
178
  }
179
 
180
- function backup_resume($resumption_array) {
181
- $resumption_no = $resumption_array[0];
182
- $bnonce = $resumption_array[1];
183
- $btime = $resumption_array[2];
184
 
185
  @ignore_user_abort(true);
186
  // This is scheduled for 5 minutes after a backup job starts
187
- if (!$bnonce || !$btime) return;
188
  // Restore state
189
- $this->nonce = $bnonce;
190
- $this->backup_time = $btime;
191
- $this->logfile_open($bnonce);
 
 
 
 
192
 
193
- $this->log("Resuming backup: resumption=$resumption_no, nonce=$bnonce, begun at=$btime");
194
  // Schedule again, to run in 5 minutes again, in case we again fail
195
  $resume_delay = 300;
196
  // A different argument than before is needed otherwise the event is ignored
197
  $next_resumption = $resumption_no+1;
198
  if ($next_resumption < 10) {
199
- $this->log("Scheduling next resumption ($next_resumption) in case this run gets aborted");
200
- wp_schedule_single_event(time()+$resume_delay, 'updraft_backup_resume' ,array($next_resumption, $bnonce, $btime));
201
  } else {
202
  $this->log("The current run is our tenth attempt - will not schedule a further attempt");
203
  }
204
 
205
- $backup_array = $this->resumable_backup_of_files(false);
 
206
  // This save, if there was something, is then immediately picked up again
207
  if (is_array($backup_array)) $this->save_backup_history($backup_array);
208
 
209
  // Returns an array, most recent first, of backup sets
210
  $backup_history = $this->get_backup_history();
211
- if (!isset($backup_history[$btime])) $this->log("Could not find a record in the database of a backup with this timestamp");
 
 
212
 
213
  $our_files=$backup_history[$btime];
214
  if (!is_array($our_files)) $our_files = array();
215
 
216
  $undone_files = array();
217
 
218
- $backup_database = get_transient("updraft_backdb_".$bnonce);
219
 
220
  // The transient is read and written below (instead of using the existing variable) so that we can copy-and-paste this part as needed.
221
  if ($backup_database == "begun" || $backup_database == "finished" || $backup_database == "encrypted") {
222
  if ($backup_database == "begun") {
223
- $this->log("Resuming creation of database dump");
 
 
 
 
224
  } elseif ($backup_database == 'encrypted') {
225
  $this->log("Database dump: Creation and encryption were completed already");
226
  } else {
@@ -228,10 +237,7 @@ class UpdraftPlus {
228
  }
229
  $db_backup = $this->backup_db($backup_database);
230
  if(is_array($our_files) && is_string($db_backup)) $our_files['db'] = $db_backup;
231
- $backup_contains = get_transient("updraft_backupcontains_".$this->nonce);
232
- $backup_contains = (substr($backup_contains,0,10) == "Files only") ? "Files and database" : "Database only (no files)";
233
- set_transient("updraft_backupcontains_".$this->nonce, $backup_contains, 3600*3);
234
- set_transient("updraft_backdb_".$this->nonce, "finished", 3600*3);
235
  } else {
236
  $this->log("Unrecognised data when trying to ascertain if the database was backed up ($backup_database)");
237
  }
@@ -245,8 +251,7 @@ class UpdraftPlus {
245
  if (isset($our_files['db']) && !preg_match("/\.crypt$/", $our_files['db'])) {
246
  $our_files['db'] = $this->encrypt_file($our_files['db']);
247
  $this->save_backup_history($our_files);
248
- // TODO: This transient is redundant; the presence of the .crypt in the db key already indicates what this transient indicates
249
- set_transient("updraft_backdb_".$this->nonce, "encrypted", 3600*3);
250
  }
251
 
252
  foreach ($our_files as $key => $file) {
@@ -256,10 +261,10 @@ class UpdraftPlus {
256
 
257
  $hash = md5($file);
258
  $fullpath = $this->backups_dir_location().'/'.$file;
259
- if (get_transient('updraft_'.$hash) === "yes") {
260
- $this->log("$file: $key: This file has been successfully uploaded in the last 3 hours");
261
  } elseif (is_file($fullpath)) {
262
- $this->log("$file: $key: This file has NOT been successfully uploaded in the last 3 hours: will retry");
263
  $undone_files[$key] = $file;
264
  } else {
265
  $this->log("$file: Note: This file was not marked as successfully uploaded, but does not exist on the local filesystem");
@@ -270,7 +275,7 @@ class UpdraftPlus {
270
  if (count($undone_files) == 0) {
271
  $this->log("There were no more files that needed uploading; backup job is complete");
272
  // No email, as the user probably already got one if something else completed the run
273
- backup_finish($next_resumption, true, false, $resumption_no);
274
  return;
275
  }
276
 
@@ -278,66 +283,49 @@ class UpdraftPlus {
278
  $this->cloud_backup($undone_files);
279
 
280
  $this->log("Resume backup ($bnonce, $resumption_no): finish run");
281
-
282
  $this->backup_finish($next_resumption, true, true, $resumption_no);
283
 
284
  }
285
 
286
  function backup_all() {
287
- $this->backup(true,true);
288
  }
289
 
290
  function backup_files() {
291
  # Note that the "false" for database gets over-ridden automatically if they turn out to have the same schedules
292
- $this->cronrun_type = "files";
293
- $this->backup(true,false);
294
  }
295
 
296
  function backup_database() {
297
  # Note that nothing will happen if the file backup had the same schedule
298
- $this->cronrun_type = "database";
299
- $this->backup(false,true);
300
  }
301
 
302
- function check_backup_race( $to_delete = false ) {
303
- // Avoid caching
304
- // Short-circuit - we believe WordPress has taken steps itself to prevent this race
305
- // Furthermore, we now pass the nonce to the resumption job, so races should not be possible on resumptions
306
- return true;
 
 
 
307
 
308
- // We no longer even user the nonce
309
- global $wpdb;
310
- $row = $wpdb->get_row($wpdb->prepare("SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", "_transient_updraftplus_backup_job_nonce"));
311
- $cur_trans = ( is_object( $row ) ) ? $row->option_value : "";
312
- // Check if another backup job ID is stored in the transient
313
- if ($cur_trans != "" && $cur_trans != $this->nonce) {
314
- // Also check if that job is of the same type as ours, as two cron jobs could legitimately fire at the same time
315
- $otherjob_crontype = get_transient("updraftplus_runtype_".$cur_trans);
316
- // $this->cronrun_type should be "files", "database" or blank (if we were not run via a cron job)
317
- if ($otherjob_crontype == $this->cronrun_type) {
318
- $this->log("Another backup job ($cur_trans) of the same type ($otherjob_crontype) appears to now be running - terminating our run (apparent race condition)");
319
- $bdir = $this->backups_dir_location();
320
- if (is_array($to_delete)) {
321
- foreach ($to_delete as $key => $file) {
322
- if (is_file($bdir.'/'.$file)) {
323
- $this->log("Deleting the file we created: ".$file);
324
- @unlink($bdir.'/'.$file);
325
- }
326
- }
327
- }
328
- exit;
329
- }
330
  }
 
331
  }
332
 
333
  // This uses a transient; its only purpose is to indicate *total* completion; there is no actual danger, just wasted time, in resuming when it was not needed. So the transient just helps save resources.
334
- function resumable_backup_of_files($resumptionrun) {
335
  //backup directories and return a numerically indexed array of file paths to the backup files
336
- $transient_status = get_transient("updraft_backf_".$this->nonce);
337
  if ($transient_status == "finished") {
338
  $this->log("Creation of backups of directories: already finished");
339
  } elseif ($transient_status == "begun") {
340
- if ($resumptionrun) {
341
  $this->log("Creation of backups of directories: had begun; will resume");
342
  } else {
343
  $this->log("Creation of backups of directories: beginning");
@@ -349,24 +337,23 @@ class UpdraftPlus {
349
  }
350
  // We want this array, even if already finished
351
  $backup_array = $this->backup_dirs($transient_status);
352
- $backup_contains = "Files only (no database)";
353
  // This can get over-written later
354
- set_transient("updraft_backupcontains_".$this->nonce, $backup_contains, 3600*3);
355
- set_transient("updraft_backf_".$this->nonce, "finished", 3600*3);
356
  return $backup_array;
357
  }
358
 
359
- function backup($backup_files, $backup_database) {
 
360
 
361
  @ignore_user_abort(true);
 
362
  //generate backup information
363
  $this->backup_time_nonce();
364
-
365
  $this->logfile_open($this->nonce);
366
 
367
  // Log some information that may be helpful
368
  global $wp_version;
369
- $this->log("Tasks: Backup files: $backup_files (schedule: ".UpdraftPlus_Options::get_updraft_option('updraft_interval','unset').") Backup DB: $backup_database (schedule: ".UpdraftPlus_Options::get_updraft_option('updraft_interval_database','unset').")");
370
 
371
  # If the files and database schedules are the same, and if this the file one, then we rope in database too.
372
  # On the other hand, if the schedules were the same and this was the database run, then there is nothing to do.
@@ -376,85 +363,18 @@ class UpdraftPlus {
376
 
377
  $this->log("Processed schedules. Tasks now: Backup files: $backup_files Backup DB: $backup_database");
378
 
379
- $clear_nonce_transient = false;
380
-
381
- # Possibly now nothing is to be done, except to close the log file
382
- if ($backup_files || $backup_database) {
383
-
384
- $clear_nonce_transient = true;
385
-
386
- // Do not 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).
387
- // If we don't finish in 3 hours, then we won't finish
388
-
389
- // Schedule the event to run later, which checks on success and can resume the backup
390
- // We save the time to a variable because it is needed for un-scheduling
391
- $resume_delay = 300;
392
- wp_schedule_single_event(time()+$resume_delay, 'updraft_backup_resume', array(1, $this->nonce, $this->backup_time));
393
- $this->log("In case we run out of time, scheduled a resumption at: $resume_delay seconds from now");
394
-
395
- $backup_contains = "";
396
- set_transient("updraft_backupcontains_".$this->nonce, "");
397
-
398
- $backup_array = array();
399
-
400
- $this->check_backup_race();
401
-
402
- // Save what *should* be done, to make it resumable from this point on
403
- set_transient("updraft_backdb_".$this->nonce, "begun", 3600*3);
404
-
405
- // The function itself will set to 'finished' if relevant
406
- // The presence of the transient indicates that files are supposed to be in this set
407
- if ($backup_files) {
408
- set_transient("updraft_backf_".$this->nonce, "begun", 3600*3);
409
- $backup_array = $this->resumable_backup_of_files(false);
410
- }
411
-
412
- // Save this to our history so we can track backups for the retain feature
413
- $this->log("Saving backup history");
414
- $this->save_backup_history($backup_array);
415
-
416
- $this->check_backup_race($backup_array);
417
-
418
- // The transient is read and written below (instead of using the existing variable) so that we can copy-and-paste this part as needed.
419
- if ($backup_database) {
420
- $this->log("Beginning backup of database");
421
- $db_backup = $this->backup_db();
422
- if ($db_backup) $backup_array['db'] = $db_backup;
423
- $backup_contains = get_transient("updraft_backupcontains_".$this->nonce);
424
- $backup_contains = (substr($backup_contains,0,10) == "Files only") ? "Files and database" : "Database only (no files)";
425
- set_transient("updraft_backupcontains_".$this->nonce, $backup_contains, 3600*3);
426
- set_transient("updraft_backdb_".$this->nonce, "finished", 3600*3);
427
- }
428
-
429
- $this->check_backup_race($backup_array);
430
-
431
- // Save this to our history so we can track backups for the retain feature
432
- $this->log("Saving backup history");
433
- // 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.
434
- $this->save_backup_history($backup_array);
435
-
436
- // Now encrypt the database, and re-save
437
- if ($backup_database && isset($backup_array['db'])) {
438
- $backup_array['db'] = $this->encrypt_file($backup_array['db']);
439
- set_transient("updraft_backdb_".$this->nonce, "encrypted", 3600*3);
440
- // Re-save with the possibly-altered database filename
441
- $this->save_backup_history($backup_array);
442
- }
443
-
444
- //cloud operations (S3,Google Drive,FTP,email,nothing)
445
- //this also calls the retain (prune) feature at the end (done in this method to reuse existing cloud connections)
446
- if(is_array($backup_array) && count($backup_array) >0) {
447
- $this->cloud_backup($backup_array);
448
- }
449
-
450
- //save the last backup info, including errors, if any
451
- $this->log("Saving last backup information into WordPress db");
452
- $this->save_last_backup($backup_array);
453
-
454
  }
455
 
456
- // Close log file; delete and also delete transients if not in debug mode
457
- $this->backup_finish(1, $clear_nonce_transient, $clear_nonce_transient, 0);
 
 
 
 
458
 
459
  }
460
 
@@ -494,6 +414,7 @@ class UpdraftPlus {
494
  if ($clear_nonce_transient) {
495
  $this->log("There were no errors in the uploads, so the 'resume' event is being unscheduled");
496
  wp_clear_scheduled_hook('updraft_backup_resume', array($cancel_event, $this->nonce, $this->backup_time));
 
497
  }
498
  } else {
499
  $this->log("There were errors in the uploads, so the 'resume' event is remaining scheduled");
@@ -556,7 +477,12 @@ class UpdraftPlus {
556
 
557
  $append_log = ($debug_mode && $this->logfile_name != "") ? "\r\nLog contents:\r\n".file_get_contents($this->logfile_name) : "" ;
558
 
559
- 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("updraft_backupcontains_".$this->nonce)."\r\n\r\n".$this->wordshell_random_advert(0)."\r\n".$append_log);
 
 
 
 
 
560
 
561
  }
562
 
@@ -570,10 +496,9 @@ class UpdraftPlus {
570
 
571
  // This should be called whenever a file is successfully uploaded
572
  function uploaded_file($file, $id = false) {
573
- # We take an MD5 hash because set_transient wants a name of 45 characters or less
574
  $hash = md5($file);
575
  $this->log("Recording as successfully uploaded: $file ($hash)");
576
- set_transient("updraft_".$hash, "yes", 3600*4);
577
  if ($id) {
578
  $ids = UpdraftPlus_Options::get_updraft_option('updraft_file_ids', array() );
579
  $ids[$file] = $id;
@@ -754,7 +679,7 @@ class UpdraftPlus {
754
  $rate = round($kbsize/$timetaken, 1);
755
  $this->log("Created $whichone zip - file size is ".round($kbsize,1)." Kb in ".round($timetaken,1)." s ($rate Kb/s)");
756
  }
757
- $this->check_backup_race($backup_array);
758
  return basename($full_path);
759
  }
760
 
@@ -804,51 +729,51 @@ class UpdraftPlus {
804
 
805
  # Others
806
  if (UpdraftPlus_Options::get_updraft_option('updraft_include_others', true)) {
807
- $this->log("Beginning backup of other directories found in the content directory");
808
 
809
  if ($transient_status == 'finished') {
810
  $backup_array['others'] = $backup_file_basename.'-others.zip';
811
  } else {
812
-
813
- // http://www.phpconcept.net/pclzip/user-guide/53
814
- /* First parameter to create is:
815
- An array of filenames or dirnames,
816
- or
817
- A string containing the filename or a dirname,
818
- or
819
- A string containing a list of filename or dirname separated by a comma.
820
- */
821
-
822
- # Initialise
823
- $other_dirlist = array();
824
-
825
- $others_skip = preg_split("/,/",UpdraftPlus_Options::get_updraft_option('updraft_include_others_exclude', UPDRAFT_DEFAULT_OTHERS_EXCLUDE));
826
- # Make the values into the keys
827
- $others_skip = array_flip($others_skip);
828
-
829
- $this->log('Looking for candidates to back up in: '.WP_CONTENT_DIR);
830
- if ($handle = opendir(WP_CONTENT_DIR)) {
831
- while (false !== ($entry = readdir($handle))) {
832
- $candidate = WP_CONTENT_DIR.'/'.$entry;
833
- if ($entry == "." || $entry == "..") { ; }
834
- elseif ($candidate == $updraft_dir) { $this->log("others: $entry: skipping: this is the updraft directory"); }
835
- elseif ($candidate == $wp_themes_dir) { $this->log("others: $entry: skipping: this is the themes directory"); }
836
- elseif ($candidate == $wp_upload_dir) { $this->log("others: $entry: skipping: this is the uploads directory"); }
837
- elseif ($candidate == $wp_plugins_dir) { $this->log("others: $entry: skipping: this is the plugins directory"); }
838
- elseif (isset($others_skip[$entry])) { $this->log("others: $entry: skipping: excluded by options"); }
839
- else { $this->log("others: $entry: adding to list"); array_push($other_dirlist, $candidate); }
 
 
 
 
 
840
  }
841
- } else {
842
- $this->log('ERROR: Could not read the content directory: '.WP_CONTENT_DIR);
843
- $this->error('Could not read the content directory: '.WP_CONTENT_DIR);
844
- }
845
 
846
- if (count($other_dirlist)>0) {
847
- $created = $this->create_zip($other_dirlist, 'others', $updraft_dir, $backup_file_basename);
848
- if ($created) $backup_array['others'] = $created;
849
- } else {
850
- $this->log("No backup of other directories: there was nothing found to back up");
851
- }
852
  # If we are not already finished
853
  }
854
  } else {
@@ -876,7 +801,7 @@ class UpdraftPlus {
876
  global $wpdb;
877
  $backup_history = @unserialize($wpdb->get_var($wpdb->prepare("SELECT option_value from $wpdb->options WHERE option_name='updraft_backup_history'")));
878
  if(is_array($backup_history)) {
879
- krsort($backup_history); //reverse sort so earliest backup is last on the array. this way we can array_pop
880
  } else {
881
  $backup_history = array();
882
  }
@@ -1075,10 +1000,7 @@ class UpdraftPlus {
1075
  }
1076
 
1077
  // Comment in SQL-file
1078
- $this->stow("\n\n");
1079
- $this->stow("#\n");
1080
- $this->stow('# ' . sprintf(__('Data contents of table %s','wp-db-backup'),$this->backquote($table)) . "\n");
1081
- $this->stow("#\n");
1082
  }
1083
 
1084
  // In UpdraftPlus, segment is always 'none'
@@ -1094,19 +1016,16 @@ class UpdraftPlus {
1094
  }
1095
  }
1096
 
1097
- // Batch by $row_inc
1098
- if ( ! defined('ROWS_PER_SEGMENT') ) define('ROWS_PER_SEGMENT', 100);
1099
-
1100
  if($segment == 'none') {
1101
  $row_start = 0;
1102
- $row_inc = ROWS_PER_SEGMENT;
1103
  } else {
1104
- $row_start = $segment * ROWS_PER_SEGMENT;
1105
- $row_inc = ROWS_PER_SEGMENT;
1106
  }
1107
- do {
1108
 
1109
- if ( !ini_get('safe_mode') || strtolower(ini_get('safe_mode')) == "off") @set_time_limit(15*60);
 
1110
  $table_data = $wpdb->get_results("SELECT * FROM $table LIMIT {$row_start}, {$row_inc}", ARRAY_A);
1111
  $entries = 'INSERT INTO ' . $this->backquote($table) . ' VALUES (';
1112
  // \x08\\x09, not required
@@ -1731,7 +1650,7 @@ class UpdraftPlus {
1731
  $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>';
1732
  }
1733
 
1734
- 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>
1735
  </tr>
1736
  <tr>
1737
  <td></td>
@@ -1827,7 +1746,7 @@ class UpdraftPlus {
1827
  echo '</div>';
1828
  }
1829
 
1830
- if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') { $this->backup(true,true); }
1831
  elseif (isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_db') { $this->backup_db(); }
1832
  elseif (isset($_POST['action']) && $_POST['action'] == 'updraft_wipesettings') {
1833
  $settings = array('updraft_interval', 'updraft_interval_database', 'updraft_retain', 'updraft_retain_db', 'updraft_encryptionphrase', 'updraft_service', 'updraft_s3_login', 'updraft_s3_pass', 'updraft_s3_remote_path', 'updraft_dropbox_appkey', 'updraft_dropbox_secret', 'updraft_dropbox_folder', 'updraft_googledrive_clientid', 'updraft_googledrive_secret', 'updraft_googledrive_remotepath', 'updraft_ftp_login', 'updraft_ftp_pass', 'updraft_ftp_remote_path', 'updraft_server_address', 'updraft_dir', 'updraft_email', 'updraft_delete_local', 'updraft_debug_mode', 'updraft_include_plugins', 'updraft_include_themes', 'updraft_include_uploads', 'updraft_include_others', 'updraft_include_others_exclude', 'updraft_lastmessage');
4
  Plugin URI: http://wordpress.org/extend/plugins/updraftplus
5
  Description: Backup and restore: your content and database can be automatically backed up to Amazon S3, Dropbox, Google Drive, FTP or email, on separate schedules.
6
  Author: David Anderson.
7
+ Version: 1.3.4
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  License: GPLv3 or later
10
  Author URI: http://wordshell.net
14
  TODO
15
  //Add SFTP, Box.Net, SugarSync and Microsoft Skydrive support??
16
  //The restorer has a hard-coded wp-content - fix
17
+ //Read safe-mode only once, remembering it will be totally removed from PHP
18
+ //Button for wiping files. Also auto-wipe on de-activate/de-install.
19
+ //Change DB encryption to not require whole gzip in memory (twice)
20
  //improve error reporting / pretty up return messages in admin area. One thing: have a "backup is now finished" flag. Otherwise with the resuming things get ambiguous/confusing. See http://wordpress.org/support/topic/backup-status - user was not aware that backup completely failed. Maybe a "backup status" field for each nonce that gets updated? (Even via AJAX?)
21
  //?? On 'backup now', open up a Lightbox, count down 5 seconds, then start examining the log file (if it can be found)
22
  //Should make clear in dashboard what is a non-fatal error (i.e. can be retried) - leads to unnecessary bug reports
23
+ // Move the inclusion, cloud and retention data into the backup job (i.e. don't read current config, make it an attribute of each job). In fact, everything should be. So audit all code for where get_option is called inside a backup run: it shouldn't happen.
24
  // Should we resume if the only errors were upon deletion (i.e. the backup itself was fine?) Presently we do, but it displays errors for the user to confuse them. Perhaps better to make pruning a separate scheuled task??
25
  // Make jobs *individually* resumable (i.e. all the state info must be keyed on the nonce; then call the resume event *specifying the nonce*)
26
  // Warn the user if their zip-file creation is slooowww...
27
  // Create a "Want Support?" button/console, that leads them through what is needed, and performs some basic tests...
28
  // Resuming partial FTP uploads
29
  // Turn expert options into a jQuery toggle
30
+ // Provide backup/restoration for UpdraftPlus's settings, to allow 'bootstrap' on a fresh WP install - some kind of single-use code which a remote UpdraftPlus can use to authenticate
31
  // Multiple jobs
32
  // Create single zip, containing even WordPress itself
33
 
76
 
77
  class UpdraftPlus {
78
 
79
+ var $version = '1.3.4';
80
  var $plugin_title = 'UpdraftPlus Backup/Restore';
81
 
82
  // Choices will be shown in the admin menu in the order used here
92
  var $dbhandle_isgz;
93
  var $errors = array();
94
  var $nonce;
 
95
  var $logfile_name = "";
96
  var $logfile_handle = false;
97
  var $backup_time;
99
  var $opened_log_time;
100
  var $backup_dir;
101
 
102
+ var $jobdata;
103
+
104
  function __construct() {
105
  // Initialisation actions - takes place on plugin load
106
  # Create admin page
110
  # backup_all is used by the manual "Backup Now" button
111
  add_action('updraft_backup_all', array($this,'backup_all'));
112
  # this is our runs-after-backup event, whose purpose is to see if it succeeded or failed, and resume/mom-up etc.
113
+ add_action('updraft_backup_resume', array($this,'backup_resume'), 10, 3);
114
  add_action('wp_enqueue_scripts', array($this, 'ajax_enqueue') );
115
  add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup'));
116
  add_action('wp_ajax_updraft_ajax', array($this, 'updraft_ajax_handler'));
160
  $this->backup_time = time();
161
  $nonce = substr(md5(time().rand()), 20);
162
  $this->nonce = $nonce;
 
 
163
  }
164
 
165
  function logfile_open($nonce) {
179
  UpdraftPlus_Options::update_updraft_option("updraft_lastmessage", $line." (".date('M d H:i:s').")");
180
  }
181
 
182
+ function backup_resume($resumption_no, $bnonce, $btime) {
 
 
 
183
 
184
  @ignore_user_abort(true);
185
  // This is scheduled for 5 minutes after a backup job starts
186
+
187
  // Restore state
188
+ if ($resumption_no > 0) {
189
+ $this->nonce = $bnonce;
190
+ $this->backup_time = $btime;
191
+ $this->logfile_open($bnonce);
192
+ }
193
+
194
+ $this->log("Backup run: resumption=$resumption_no, nonce=$bnonce, begun at=$btime");
195
 
 
196
  // Schedule again, to run in 5 minutes again, in case we again fail
197
  $resume_delay = 300;
198
  // A different argument than before is needed otherwise the event is ignored
199
  $next_resumption = $resumption_no+1;
200
  if ($next_resumption < 10) {
201
+ $this->log("Scheduling a resumption ($next_resumption) in case this run gets aborted");
202
+ wp_schedule_single_event(time()+$resume_delay, 'updraft_backup_resume', array($next_resumption, $bnonce, $btime));
203
  } else {
204
  $this->log("The current run is our tenth attempt - will not schedule a further attempt");
205
  }
206
 
207
+ // This should be always called; if there were no files in this run, it returns us an empty array
208
+ $backup_array = $this->resumable_backup_of_files($resumption_no);
209
  // This save, if there was something, is then immediately picked up again
210
  if (is_array($backup_array)) $this->save_backup_history($backup_array);
211
 
212
  // Returns an array, most recent first, of backup sets
213
  $backup_history = $this->get_backup_history();
214
+ if (!isset($backup_history[$btime])) {
215
+ $this->log("Could not find a record in the database of a backup with this timestamp");
216
+ }
217
 
218
  $our_files=$backup_history[$btime];
219
  if (!is_array($our_files)) $our_files = array();
220
 
221
  $undone_files = array();
222
 
223
+ $backup_database = $this->jobdata_get('backup_database');
224
 
225
  // The transient is read and written below (instead of using the existing variable) so that we can copy-and-paste this part as needed.
226
  if ($backup_database == "begun" || $backup_database == "finished" || $backup_database == "encrypted") {
227
  if ($backup_database == "begun") {
228
+ if ($resumption_no > 0) {
229
+ $this->log("Resuming creation of database dump");
230
+ } else {
231
+ $this->log("Beginning creation of database dump");
232
+ }
233
  } elseif ($backup_database == 'encrypted') {
234
  $this->log("Database dump: Creation and encryption were completed already");
235
  } else {
237
  }
238
  $db_backup = $this->backup_db($backup_database);
239
  if(is_array($our_files) && is_string($db_backup)) $our_files['db'] = $db_backup;
240
+ if ($backup_database != 'encrypted') $this->jobdata_set("backup_database", 'finished');
 
 
 
241
  } else {
242
  $this->log("Unrecognised data when trying to ascertain if the database was backed up ($backup_database)");
243
  }
251
  if (isset($our_files['db']) && !preg_match("/\.crypt$/", $our_files['db'])) {
252
  $our_files['db'] = $this->encrypt_file($our_files['db']);
253
  $this->save_backup_history($our_files);
254
+ $this->jobdata_set("backup_database", "encrypted");
 
255
  }
256
 
257
  foreach ($our_files as $key => $file) {
261
 
262
  $hash = md5($file);
263
  $fullpath = $this->backups_dir_location().'/'.$file;
264
+ if ($this->jobdata_get("uploaded_$hash") === "yes") {
265
+ $this->log("$file: $key: This file has already been successfully uploaded");
266
  } elseif (is_file($fullpath)) {
267
+ $this->log("$file: $key: This file has not yet been successfully uploaded: will queue");
268
  $undone_files[$key] = $file;
269
  } else {
270
  $this->log("$file: Note: This file was not marked as successfully uploaded, but does not exist on the local filesystem");
275
  if (count($undone_files) == 0) {
276
  $this->log("There were no more files that needed uploading; backup job is complete");
277
  // No email, as the user probably already got one if something else completed the run
278
+ $this->backup_finish($next_resumption, true, false, $resumption_no);
279
  return;
280
  }
281
 
283
  $this->cloud_backup($undone_files);
284
 
285
  $this->log("Resume backup ($bnonce, $resumption_no): finish run");
 
286
  $this->backup_finish($next_resumption, true, true, $resumption_no);
287
 
288
  }
289
 
290
  function backup_all() {
291
+ $this->boot_backup(true,true);
292
  }
293
 
294
  function backup_files() {
295
  # Note that the "false" for database gets over-ridden automatically if they turn out to have the same schedules
296
+ $this->boot_backup(true,false);
 
297
  }
298
 
299
  function backup_database() {
300
  # Note that nothing will happen if the file backup had the same schedule
301
+ $this->boot_backup(false,true);
 
302
  }
303
 
304
+ function jobdata_set($key, $value) {
305
+ if (is_array($this->jobdata)) {
306
+ $this->jobdata[$key] = $value;
307
+ } else {
308
+ $this->jobdata = array($key => $value);
309
+ }
310
+ set_transient("updraft_jobdata_".$this->nonce, $this->jobdata, 14400);
311
+ }
312
 
313
+ function jobdata_get($key) {
314
+ if (!is_array($this->jobdata)) {
315
+ $this->jobdata = get_transient("updraft_jobdata_".$this->nonce);
316
+ if (!is_array($this->jobdata)) return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  }
318
+ return (isset($this->jobdata[$key])) ? $this->jobdata[$key] : false;
319
  }
320
 
321
  // This uses a transient; its only purpose is to indicate *total* completion; there is no actual danger, just wasted time, in resuming when it was not needed. So the transient just helps save resources.
322
+ function resumable_backup_of_files($resumption_no) {
323
  //backup directories and return a numerically indexed array of file paths to the backup files
324
+ $transient_status = $this->jobdata_get("backup_files");
325
  if ($transient_status == "finished") {
326
  $this->log("Creation of backups of directories: already finished");
327
  } elseif ($transient_status == "begun") {
328
+ if ($resumption_no>0) {
329
  $this->log("Creation of backups of directories: had begun; will resume");
330
  } else {
331
  $this->log("Creation of backups of directories: beginning");
337
  }
338
  // We want this array, even if already finished
339
  $backup_array = $this->backup_dirs($transient_status);
 
340
  // This can get over-written later
341
+ $this->jobdata_set('backup_files', 'finished');
 
342
  return $backup_array;
343
  }
344
 
345
+ // This procedure initiates a backup run
346
+ function boot_backup($backup_files, $backup_database) {
347
 
348
  @ignore_user_abort(true);
349
+
350
  //generate backup information
351
  $this->backup_time_nonce();
 
352
  $this->logfile_open($this->nonce);
353
 
354
  // Log some information that may be helpful
355
  global $wp_version;
356
+ $this->log("Tasks: Backup files: $backup_files (schedule: ".UpdraftPlus_Options::get_updraft_option('updraft_interval', 'unset').") Backup DB: $backup_database (schedule: ".UpdraftPlus_Options::get_updraft_option('updraft_interval_database', 'unset').")");
357
 
358
  # If the files and database schedules are the same, and if this the file one, then we rope in database too.
359
  # On the other hand, if the schedules were the same and this was the database run, then there is nothing to do.
363
 
364
  $this->log("Processed schedules. Tasks now: Backup files: $backup_files Backup DB: $backup_database");
365
 
366
+ # If nothing to be done, then just finish
367
+ if (!$backup_files && !$backup_database) {
368
+ $this->backup_finish(1, false, false, 0);
369
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
370
  }
371
 
372
+ // Save what *should* be done, to make it resumable from this point on
373
+ if ($backup_database) $this->jobdata_set("backup_database", "begun");
374
+ if ($backup_files) $this->jobdata_set("backup_files", "begun");
375
+
376
+ // Everthing is now set up; now go
377
+ $this->backup_resume(0, $this->nonce, $this->backup_time);
378
 
379
  }
380
 
414
  if ($clear_nonce_transient) {
415
  $this->log("There were no errors in the uploads, so the 'resume' event is being unscheduled");
416
  wp_clear_scheduled_hook('updraft_backup_resume', array($cancel_event, $this->nonce, $this->backup_time));
417
+ // TODO: Delete the job transient (is presently useful for debugging, and only lasts 4 hours)
418
  }
419
  } else {
420
  $this->log("There were errors in the uploads, so the 'resume' event is remaining scheduled");
477
 
478
  $append_log = ($debug_mode && $this->logfile_name != "") ? "\r\nLog contents:\r\n".file_get_contents($this->logfile_name) : "" ;
479
 
480
+ $backup_files = $this->jobdata_get("backup_files");
481
+ $backup_db = $this->jobdata_get("backup_database");
482
+
483
+ $backup_contains = (substr($backup_contains,0,10) == "Files only") ? "Files and database" : "Database only (no files)";
484
+
485
+ 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: ".$backup_contains."\r\n\r\n".$this->wordshell_random_advert(0)."\r\n".$append_log);
486
 
487
  }
488
 
496
 
497
  // This should be called whenever a file is successfully uploaded
498
  function uploaded_file($file, $id = false) {
 
499
  $hash = md5($file);
500
  $this->log("Recording as successfully uploaded: $file ($hash)");
501
+ $this->jobdata_set("uploaded_$hash", "yes");
502
  if ($id) {
503
  $ids = UpdraftPlus_Options::get_updraft_option('updraft_file_ids', array() );
504
  $ids[$file] = $id;
679
  $rate = round($kbsize/$timetaken, 1);
680
  $this->log("Created $whichone zip - file size is ".round($kbsize,1)." Kb in ".round($timetaken,1)." s ($rate Kb/s)");
681
  }
682
+
683
  return basename($full_path);
684
  }
685
 
729
 
730
  # Others
731
  if (UpdraftPlus_Options::get_updraft_option('updraft_include_others', true)) {
 
732
 
733
  if ($transient_status == 'finished') {
734
  $backup_array['others'] = $backup_file_basename.'-others.zip';
735
  } else {
736
+ $this->log("Beginning backup of other directories found in the content directory");
737
+
738
+ // http://www.phpconcept.net/pclzip/user-guide/53
739
+ /* First parameter to create is:
740
+ An array of filenames or dirnames,
741
+ or
742
+ A string containing the filename or a dirname,
743
+ or
744
+ A string containing a list of filename or dirname separated by a comma.
745
+ */
746
+
747
+ # Initialise
748
+ $other_dirlist = array();
749
+
750
+ $others_skip = preg_split("/,/",UpdraftPlus_Options::get_updraft_option('updraft_include_others_exclude', UPDRAFT_DEFAULT_OTHERS_EXCLUDE));
751
+ # Make the values into the keys
752
+ $others_skip = array_flip($others_skip);
753
+
754
+ $this->log('Looking for candidates to back up in: '.WP_CONTENT_DIR);
755
+ if ($handle = opendir(WP_CONTENT_DIR)) {
756
+ while (false !== ($entry = readdir($handle))) {
757
+ $candidate = WP_CONTENT_DIR.'/'.$entry;
758
+ if ($entry == "." || $entry == "..") { ; }
759
+ elseif ($candidate == $updraft_dir) { $this->log("others: $entry: skipping: this is the updraft directory"); }
760
+ elseif ($candidate == $wp_themes_dir) { $this->log("others: $entry: skipping: this is the themes directory"); }
761
+ elseif ($candidate == $wp_upload_dir) { $this->log("others: $entry: skipping: this is the uploads directory"); }
762
+ elseif ($candidate == $wp_plugins_dir) { $this->log("others: $entry: skipping: this is the plugins directory"); }
763
+ elseif (isset($others_skip[$entry])) { $this->log("others: $entry: skipping: excluded by options"); }
764
+ else { $this->log("others: $entry: adding to list"); array_push($other_dirlist, $candidate); }
765
+ }
766
+ } else {
767
+ $this->log('ERROR: Could not read the content directory: '.WP_CONTENT_DIR);
768
+ $this->error('Could not read the content directory: '.WP_CONTENT_DIR);
769
  }
 
 
 
 
770
 
771
+ if (count($other_dirlist)>0) {
772
+ $created = $this->create_zip($other_dirlist, 'others', $updraft_dir, $backup_file_basename);
773
+ if ($created) $backup_array['others'] = $created;
774
+ } else {
775
+ $this->log("No backup of other directories: there was nothing found to back up");
776
+ }
777
  # If we are not already finished
778
  }
779
  } else {
801
  global $wpdb;
802
  $backup_history = @unserialize($wpdb->get_var($wpdb->prepare("SELECT option_value from $wpdb->options WHERE option_name='updraft_backup_history'")));
803
  if(is_array($backup_history)) {
804
+ krsort($backup_history); //reverse sort so earliest backup is last on the array. Then we can array_pop.
805
  } else {
806
  $backup_history = array();
807
  }
1000
  }
1001
 
1002
  // Comment in SQL-file
1003
+ $this->stow("\n\n#\n# " . sprintf(__('Data contents of table %s','wp-db-backup'),$this->backquote($table)) . "\n#\n");
 
 
 
1004
  }
1005
 
1006
  // In UpdraftPlus, segment is always 'none'
1016
  }
1017
  }
1018
 
 
 
 
1019
  if($segment == 'none') {
1020
  $row_start = 0;
1021
+ $row_inc = 100;
1022
  } else {
1023
+ $row_start = $segment * 100;
1024
+ $row_inc = 100;
1025
  }
 
1026
 
1027
+ do {
1028
+ if ( !@ini_get('safe_mode') || strtolower(@ini_get('safe_mode')) == "off") @set_time_limit(15*60);
1029
  $table_data = $wpdb->get_results("SELECT * FROM $table LIMIT {$row_start}, {$row_inc}", ARRAY_A);
1030
  $entries = 'INSERT INTO ' . $this->backquote($table) . ' VALUES (';
1031
  // \x08\\x09, not required
1650
  $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>';
1651
  }
1652
 
1653
+ echo $dir_info ?> This is where UpdraftPlus 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>
1654
  </tr>
1655
  <tr>
1656
  <td></td>
1746
  echo '</div>';
1747
  }
1748
 
1749
+ if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') { $this->boot_backup(true,true); }
1750
  elseif (isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_db') { $this->backup_db(); }
1751
  elseif (isset($_POST['action']) && $_POST['action'] == 'updraft_wipesettings') {
1752
  $settings = array('updraft_interval', 'updraft_interval_database', 'updraft_retain', 'updraft_retain_db', 'updraft_encryptionphrase', 'updraft_service', 'updraft_s3_login', 'updraft_s3_pass', 'updraft_s3_remote_path', 'updraft_dropbox_appkey', 'updraft_dropbox_secret', 'updraft_dropbox_folder', 'updraft_googledrive_clientid', 'updraft_googledrive_secret', 'updraft_googledrive_remotepath', 'updraft_ftp_login', 'updraft_ftp_pass', 'updraft_ftp_remote_path', 'updraft_server_address', 'updraft_dir', 'updraft_email', 'updraft_delete_local', 'updraft_debug_mode', 'updraft_include_plugins', 'updraft_include_themes', 'updraft_include_uploads', 'updraft_include_others', 'updraft_include_others_exclude', 'updraft_lastmessage');