UpdraftPlus WordPress Backup Plugin - Version 1.3.22

Version Description

  • 01/31/2013 =
  • More help for really large uploads; dynamically alter the maximum number of resumption attempts if something useful is still happening
Download this release

Release Info

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

Code changes from version 1.3.20 to 1.3.22

methods/dropbox.php CHANGED
@@ -11,12 +11,12 @@ class UpdraftPlus_BackupModule_dropbox {
11
  global $updraftplus;
12
 
13
  // Update upload ID
14
- set_transient('updraf_dbid_'.$this->current_file_hash, $uploadid, 3600*3);
15
- set_transient('updraf_dbof_'.$this->current_file_hash, $offset, 3600*3);
16
 
17
  if ($this->current_file_size > 0) {
18
  $percent = round(100*($offset/$this->current_file_size),1);
19
- $updraftplus->log("Dropbox: Chunked Upload: ${percent}% ($uploadid, $offset)");
20
  } else {
21
  $updraftplus->log("Dropbox: Chunked Upload: $offset bytes uploaded");
22
  }
11
  global $updraftplus;
12
 
13
  // Update upload ID
14
+ set_transient('updraf_dbid_'.$this->current_file_hash, $uploadid, UPDRAFT_TRANSTIME);
15
+ set_transient('updraf_dbof_'.$this->current_file_hash, $offset, UPDRAFT_TRANSTIME);
16
 
17
  if ($this->current_file_size > 0) {
18
  $percent = round(100*($offset/$this->current_file_size),1);
19
+ $updraftplus->record_uploaded_chunk($percent, "($uploadid, $offset)");
20
  } else {
21
  $updraftplus->log("Dropbox: Chunked Upload: $offset bytes uploaded");
22
  }
methods/googledrive.php CHANGED
@@ -200,12 +200,13 @@ class UpdraftPlus_BackupModule_googledrive {
200
  // This counter is only used for when deciding what to log
201
  $counter = 0;
202
  do {
203
- $log_string = ($counter == 0) ? "Upload %: $d, URL: $res" : "Upload %: $d";
 
 
204
  $counter++; if ($counter >= 20) $counter=0;
205
- $updraftplus->log($log_string);
206
 
207
  $res = $gdocs_object->upload_chunk();
208
- if (is_string($res)) set_transient($transkey, $res, 3600*3);
209
  $p = $gdocs_object->get_upload_percentage();
210
  if ( $p - $d >= 1 ) {
211
  $b = intval( $p - $d );
200
  // This counter is only used for when deciding what to log
201
  $counter = 0;
202
  do {
203
+ $log_string = ($counter == 0) ? "URL: $res" : "";
204
+ $updraftplus->record_uploaded_chunk($d, $log_string);
205
+
206
  $counter++; if ($counter >= 20) $counter=0;
 
207
 
208
  $res = $gdocs_object->upload_chunk();
209
+ if (is_string($res)) set_transient($transkey, $res, UPDRAFT_TRANSTIME);
210
  $p = $gdocs_object->get_upload_percentage();
211
  if ( $p - $d >= 1 ) {
212
  $b = intval( $p - $d );
methods/s3.php CHANGED
@@ -25,6 +25,7 @@ class UpdraftPlus_BackupModule_s3 {
25
  foreach($backup_array as $file) {
26
 
27
  // We upload in 5Mb chunks to allow more efficient resuming and hence uploading of larger files
 
28
  $fullpath = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir')).$file;
29
  $chunks = floor(filesize($fullpath) / 5242880)+1;
30
  $hash = md5($file);
@@ -53,7 +54,7 @@ class UpdraftPlus_BackupModule_s3 {
53
  continue;
54
  } else {
55
  $updraftplus->log("S3 chunked upload: got multipart ID: $uploadId");
56
- set_transient("updraft_${hash}_uid", $uploadId, 3600*3);
57
  }
58
  } else {
59
  $updraftplus->log("S3 chunked upload: retrieved previously obtained multipart ID: $uploadId");
@@ -71,9 +72,9 @@ class UpdraftPlus_BackupModule_s3 {
71
  } else {
72
  $etag = $s3->uploadPart($bucket_name, $filepath, $uploadId, $fullpath, $i);
73
  if (is_string($etag)) {
74
- $updraftplus->log("S3 chunk $i: uploaded (etag: $etag)");
75
  array_push($etags, $etag);
76
- set_transient("upd_${hash}_e$i", $etag, 3600*3);
77
  $successes++;
78
  } else {
79
  $updraftplus->log("S3 chunk $i: upload failed");
@@ -83,13 +84,22 @@ class UpdraftPlus_BackupModule_s3 {
83
  }
84
  if ($successes >= $chunks) {
85
  $updraftplus->log("S3 upload: all chunks uploaded; will now instruct S3 to re-assemble");
86
- if ($s3->completeMultipartUpload ($bucket_name, $filepath, $uploadId, $etags)) {
87
- $updraftplus->log("S3 upload: re-assembly succeeded");
88
- $updraftplus->uploaded_file($file);
89
- } else {
90
- $updraftplus->log("S3 upload: re-assembly failed");
91
- $updraftplus->error("S3 upload: re-assembly failed ($file)");
 
 
 
 
 
 
 
92
  }
 
 
93
  } else {
94
  $updraftplus->log("S3 upload: upload was not completely successful on this run");
95
  }
25
  foreach($backup_array as $file) {
26
 
27
  // We upload in 5Mb chunks to allow more efficient resuming and hence uploading of larger files
28
+ // N.B.: 5Mb is Amazon's minimum. So don't go lower or you'll break it.
29
  $fullpath = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir')).$file;
30
  $chunks = floor(filesize($fullpath) / 5242880)+1;
31
  $hash = md5($file);
54
  continue;
55
  } else {
56
  $updraftplus->log("S3 chunked upload: got multipart ID: $uploadId");
57
+ set_transient("updraft_${hash}_uid", $uploadId, UPDRAFT_TRANSTIME);
58
  }
59
  } else {
60
  $updraftplus->log("S3 chunked upload: retrieved previously obtained multipart ID: $uploadId");
72
  } else {
73
  $etag = $s3->uploadPart($bucket_name, $filepath, $uploadId, $fullpath, $i);
74
  if (is_string($etag)) {
75
+ $updraftplus->record_uploaded_chunk(round(100*$i/$chunks,1), "$i, $etag");
76
  array_push($etags, $etag);
77
+ set_transient("upd_${hash}_e$i", $etag, UPDRAFT_TRANSTIME);
78
  $successes++;
79
  } else {
80
  $updraftplus->log("S3 chunk $i: upload failed");
84
  }
85
  if ($successes >= $chunks) {
86
  $updraftplus->log("S3 upload: all chunks uploaded; will now instruct S3 to re-assemble");
87
+
88
+ $s3->setExceptions(true);
89
+ try {
90
+ if ($s3->completeMultipartUpload ($bucket_name, $filepath, $uploadId, $etags)) {
91
+ $updraftplus->log("S3 upload: re-assembly succeeded");
92
+ $updraftplus->uploaded_file($file);
93
+ } else {
94
+ $updraftplus->log("S3 upload: re-assembly failed");
95
+ $updraftplus->error("S3 upload: re-assembly failed ($file)");
96
+ }
97
+ } catch (Exception $e) {
98
+ $updraftplus->log('S3 re-assembly error: '.$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
99
+ $updraftplus->error('S3 re-assembly error: '.$e->getMessage().' (see log file for more)');
100
  }
101
+ // Remember to unset, as the deletion code later reuses the object
102
+ $s3->setExceptions(false);
103
  } else {
104
  $updraftplus->log("S3 upload: upload was not completely successful on this run");
105
  }
readme.txt CHANGED
@@ -3,12 +3,12 @@ Contributors: David Anderson
3
  Tags: backup, restore, database, cloud, amazon, s3, dropbox, google drive, ftp, cloud, back up, multisite
4
  Requires at least: 3.2
5
  Tested up to: 3.5.1
6
- Stable tag: 1.3.20
7
  Donate link: http://david.dw-perspective.org.uk/donate
8
  License: GPLv3 or later
9
 
10
  == Upgrade Notice ==
11
- Prevent potential endless logging loop in S3 method
12
 
13
  == Description ==
14
 
@@ -141,6 +141,9 @@ Thanks for asking - yes, I have. Check out my profile page - http://profiles.wor
141
 
142
  == Changelog ==
143
 
 
 
 
144
  = 1.3.20 - 01/30/2013 =
145
  * Add extra error checking in S3 method (can prevent logging loop)
146
 
3
  Tags: backup, restore, database, cloud, amazon, s3, dropbox, google drive, ftp, cloud, back up, multisite
4
  Requires at least: 3.2
5
  Tested up to: 3.5.1
6
+ Stable tag: 1.3.22
7
  Donate link: http://david.dw-perspective.org.uk/donate
8
  License: GPLv3 or later
9
 
10
  == Upgrade Notice ==
11
+ 1. More help for really large backup sets. 2. Prevent potential endless logging loop in S3 method.
12
 
13
  == Description ==
14
 
141
 
142
  == Changelog ==
143
 
144
+ = 1.3.22 - 01/31/2013 =
145
+ * More help for really large uploads; dynamically alter the maximum number of resumption attempts if something useful is still happening
146
+
147
  = 1.3.20 - 01/30/2013 =
148
  * Add extra error checking in S3 method (can prevent logging loop)
149
 
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.20
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  License: GPLv3 or later
10
  Author URI: http://wordshell.net
@@ -27,6 +27,8 @@ TODO
27
  // Resuming partial FTP uploads
28
  // 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
29
  // Multiple jobs
 
 
30
  // When looking for files to delete, is the current encryption setting used? Should not be.
31
  // Create single zip, containing even WordPress itself
32
  // When a new backup starts, AJAX-update the 'Last backup' display in the admin page.
@@ -71,6 +73,9 @@ if (!$updraftplus->memory_check(192)) {
71
  define('UPDRAFTPLUS_DIR', dirname(__FILE__));
72
  define('UPDRAFTPLUS_URL', plugins_url('', __FILE__));
73
  define('UPDRAFT_DEFAULT_OTHERS_EXCLUDE','upgrade,cache,updraft,index.php,backup');
 
 
 
74
 
75
  if (is_file(UPDRAFTPLUS_DIR.'/premium.php')) require_once(UPDRAFTPLUS_DIR.'/premium.php');
76
 
@@ -78,7 +83,7 @@ if (!class_exists('UpdraftPlus_Options')) require_once(UPDRAFTPLUS_DIR.'/options
78
 
79
  class UpdraftPlus {
80
 
81
- var $version = '1.3.20';
82
  var $plugin_title = 'UpdraftPlus Backup/Restore';
83
 
84
  // Choices will be shown in the admin menu in the order used here
@@ -103,6 +108,10 @@ class UpdraftPlus {
103
 
104
  var $jobdata;
105
 
 
 
 
 
106
  function __construct() {
107
  // Initialisation actions - takes place on plugin load
108
  # Create admin page
@@ -182,6 +191,25 @@ class UpdraftPlus {
182
  UpdraftPlus_Options::update_updraft_option("updraft_lastmessage", $line." (".date('M d H:i:s').")");
183
  }
184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  function backup_resume($resumption_no, $bnonce, $btime) {
186
 
187
  @ignore_user_abort(true);
@@ -195,6 +223,7 @@ class UpdraftPlus {
195
  }
196
 
197
  $this->log("Backup run: resumption=$resumption_no, nonce=$bnonce, begun at=$btime");
 
198
 
199
  // Schedule again, to run in 5 minutes again, in case we again fail
200
  $resume_delay = 300;
@@ -203,8 +232,9 @@ class UpdraftPlus {
203
  if ($next_resumption < 10) {
204
  $this->log("Scheduling a resumption ($next_resumption) in case this run gets aborted");
205
  wp_schedule_single_event(time()+$resume_delay, 'updraft_backup_resume', array($next_resumption, $bnonce, $btime));
 
206
  } else {
207
- $this->log("The current run is our tenth attempt - will not schedule a further attempt");
208
  }
209
 
210
  // This should be always called; if there were no files in this run, it returns us an empty array
@@ -289,9 +319,6 @@ class UpdraftPlus {
289
  if (is_array($our_files)) $this->save_last_backup($our_files);
290
  $this->backup_finish($next_resumption, true, true, $resumption_no);
291
 
292
-
293
-
294
-
295
  }
296
 
297
  function backup_all() {
@@ -378,6 +405,7 @@ class UpdraftPlus {
378
  // Save what *should* be done, to make it resumable from this point on
379
  if ($backup_database) $this->jobdata_set("backup_database", "begun");
380
  if ($backup_files) $this->jobdata_set("backup_files", "begun");
 
381
 
382
  // Everthing is now set up; now go
383
  $this->backup_resume(0, $this->nonce, $this->backup_time);
@@ -440,7 +468,7 @@ class UpdraftPlus {
440
  if (empty($this->errors)) {
441
  $send_an_email = true;
442
  $final_message = "The backup apparently succeeded and is now complete";
443
- } elseif ($resumption_no >=9) {
444
  $send_an_email = true;
445
  $final_message = "The backup attempt has finished, apparently unsuccesfully";
446
  } else {
@@ -521,12 +549,13 @@ class UpdraftPlus {
521
  }
522
  // Delete local files immediately if the option is set
523
  // Where we are only backing up locally, only the "prune" function should do deleting
524
- if (UpdraftPlus_Options::get_updraft_option('updraft_service', 'none') != 'none') $this->delete_local($file);
525
  }
526
 
527
  // Dispatch to the relevant function
528
  function cloud_backup($backup_array) {
529
- $service = UpdraftPlus_Options::get_updraft_option('updraft_service');
 
530
  $this->log("Cloud backup selection: ".$service);
531
  @set_time_limit(900);
532
 
@@ -549,8 +578,8 @@ class UpdraftPlus {
549
  }
550
  }
551
 
552
- function prune_file($updraft_service, $dofile, $method_object = null, $object_passback = null ) {
553
- $this->log("Delete this file: $dofile, service=$updraft_service");
554
  $fullpath = $this->backups_dir_location().'/'.$dofile;
555
  // delete it if it's locally available
556
  if (file_exists($fullpath)) {
@@ -563,10 +592,10 @@ class UpdraftPlus {
563
  }
564
 
565
  // Carries out retain behaviour. Pass in a valid S3 or FTP object and path if relevant.
566
- function prune_retained_backups($updraft_service, $backup_method_object = null, $backup_passback = null) {
567
 
568
  // If they turned off deletion on local backups, then there is nothing to do
569
- if (UpdraftPlus_Options::get_updraft_option('updraft_delete_local') == 0 && $updraft_service == 'none') {
570
  $this->log("Prune old backups from local store: nothing to do, since the user disabled local deletion and we are using local backups");
571
  return;
572
  }
@@ -600,7 +629,7 @@ class UpdraftPlus {
600
  if ($db_backups_found > $updraft_retain_db) {
601
  $this->log("$backup_datestamp: over retain limit ($updraft_retain_db); will delete this database");
602
  $dofile = $backup_to_examine['db'];
603
- if (!empty($dofile)) $this->prune_file($updraft_service, $dofile, $backup_method_object, $backup_passback);
604
  unset($backup_to_examine['db']);
605
  }
606
  }
@@ -614,7 +643,7 @@ class UpdraftPlus {
614
  $file3 = isset($backup_to_examine['uploads']) ? $backup_to_examine['uploads'] : "";
615
  $file4 = isset($backup_to_examine['others']) ? $backup_to_examine['others'] : "";
616
  foreach (array($file, $file2, $file3, $file4) as $dofile) {
617
- if (!empty($dofile)) $this->prune_file($updraft_service, $dofile, $backup_method_object, $backup_passback);
618
  }
619
  unset($backup_to_examine['plugins']);
620
  unset($backup_to_examine['themes']);
@@ -1882,7 +1911,7 @@ class UpdraftPlus {
1882
  <div style="float:left; width:200px; padding-top: 40px;">
1883
  <form method="post" action="">
1884
  <input type="hidden" name="action" value="updraft_backup" />
1885
- <p><input type="submit" <?php echo $backup_disabled ?> class="button-primary" value="Backup Now!" style="padding-top:3px;padding-bottom:3px;font-size:24px !important" onclick="return(confirm('This will schedule a one-time backup. To trigger the backup you should go ahead, then wait 10 seconds, then visit any page on your site. WordPress should then start the backup running in the background.'))"></p>
1886
  </form>
1887
  <div style="position:relative">
1888
  <div style="position:absolute;top:0;left:0">
@@ -1891,7 +1920,7 @@ class UpdraftPlus {
1891
  $backup_history = (is_array($backup_history))?$backup_history:array();
1892
  $restore_disabled = (count($backup_history) == 0) ? 'disabled="disabled"' : "";
1893
  ?>
1894
- <input type="button" class="button-primary" <?php echo $restore_disabled ?> value="Restore" style="padding-top:3px;padding-bottom:3px;font-size:24px !important" onclick="jQuery('#backup-restore').fadeIn('slow');jQuery(this).parent().fadeOut('slow')">
1895
  </div>
1896
  <div style="display:none;position:absolute;top:0;left:0" id="backup-restore">
1897
  <form method="post" action="">
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.22
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  License: GPLv3 or later
10
  Author URI: http://wordshell.net
27
  // Resuming partial FTP uploads
28
  // 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
29
  // Multiple jobs
30
+ // Don't stop at 10 retries if something useful is still measurably being done (in particular, chunked uploads are proceeding - set a flag to indicate "try it again")
31
+ // Change FTP to use SSL by default
32
  // When looking for files to delete, is the current encryption setting used? Should not be.
33
  // Create single zip, containing even WordPress itself
34
  // When a new backup starts, AJAX-update the 'Last backup' display in the admin page.
73
  define('UPDRAFTPLUS_DIR', dirname(__FILE__));
74
  define('UPDRAFTPLUS_URL', plugins_url('', __FILE__));
75
  define('UPDRAFT_DEFAULT_OTHERS_EXCLUDE','upgrade,cache,updraft,index.php,backup');
76
+ // This is used in various places, based on our assumption of the maximum time any job should take. May need lengthening in future if we get reports which show enormous sets hitting the limit.
77
+ // Also one section requires at least 1% progress each run, so on a 5-minute schedule, that equals just under 9 hours
78
+ define('UPDRAFT_TRANSTIME', 3600*9);
79
 
80
  if (is_file(UPDRAFTPLUS_DIR.'/premium.php')) require_once(UPDRAFTPLUS_DIR.'/premium.php');
81
 
83
 
84
  class UpdraftPlus {
85
 
86
+ var $version = '1.3.22';
87
  var $plugin_title = 'UpdraftPlus Backup/Restore';
88
 
89
  // Choices will be shown in the admin menu in the order used here
108
 
109
  var $jobdata;
110
 
111
+ // Used to schedule resumption attempts beyond the tenth, if needed
112
+ var $current_resumption;
113
+ var $newresumption_scheduled;
114
+
115
  function __construct() {
116
  // Initialisation actions - takes place on plugin load
117
  # Create admin page
191
  UpdraftPlus_Options::update_updraft_option("updraft_lastmessage", $line." (".date('M d H:i:s').")");
192
  }
193
 
194
+ // This function is used by cloud methods to provide standardised logging, but more importantly to help us detect that meaningful activity took place during a resumption run, so that we can schedule further resumptions if it is worthwhile
195
+ function record_uploaded_chunk($percent, $extra) {
196
+ // Log it
197
+ $service = $this->jobdata_get('service');
198
+ $log = ucfirst($service)." chunked upload: $percent % uploaded";
199
+ if ($extra) $log .= " ($extra)";
200
+ $this->log($log);
201
+ // If we are on an 'overtime' resumption run, and we are still meainingfully uploading, then schedule a new resumption
202
+ // Our definition of meaningful is that we must maintain an overall average of at least 1% per run, after allowing 5 runs for everything else to get going
203
+ // i.e. Max 100 runs = 500 minutes = 8 hrs 40
204
+ // If they get 2 minutes on each run, and the file is 1Gb, then that equals 10.2Mb/120s = minimum 87Kb/s upload speed required
205
+
206
+ if ($this->current_resumption >= 9 && $this->newresumption_scheduled !== true && $percent > ( $this->current_resumption - 5)) {
207
+ $this->newresumption_scheduled = true;
208
+ $this->log("This is resumption ".$this->current_resumption.", but meaningful uploading is still taking place; so a new one will be scheduled");
209
+ wp_schedule_single_event(time()+300, 'updraft_backup_resume', array($this->current_resumption + 1, $this->nonce, $this->backup_time));
210
+ }
211
+ }
212
+
213
  function backup_resume($resumption_no, $bnonce, $btime) {
214
 
215
  @ignore_user_abort(true);
223
  }
224
 
225
  $this->log("Backup run: resumption=$resumption_no, nonce=$bnonce, begun at=$btime");
226
+ $this->current_resumption = $resumption_no;
227
 
228
  // Schedule again, to run in 5 minutes again, in case we again fail
229
  $resume_delay = 300;
232
  if ($next_resumption < 10) {
233
  $this->log("Scheduling a resumption ($next_resumption) in case this run gets aborted");
234
  wp_schedule_single_event(time()+$resume_delay, 'updraft_backup_resume', array($next_resumption, $bnonce, $btime));
235
+ $this->newresumption_scheduled=true;
236
  } else {
237
+ $this->log("The current run is our tenth attempt - will not schedule a further attempt until we see something useful happening");
238
  }
239
 
240
  // This should be always called; if there were no files in this run, it returns us an empty array
319
  if (is_array($our_files)) $this->save_last_backup($our_files);
320
  $this->backup_finish($next_resumption, true, true, $resumption_no);
321
 
 
 
 
322
  }
323
 
324
  function backup_all() {
405
  // Save what *should* be done, to make it resumable from this point on
406
  if ($backup_database) $this->jobdata_set("backup_database", "begun");
407
  if ($backup_files) $this->jobdata_set("backup_files", "begun");
408
+ $this->jobdata_set('service', UpdraftPlus_Options::get_updraft_option('updraft_service'));
409
 
410
  // Everthing is now set up; now go
411
  $this->backup_resume(0, $this->nonce, $this->backup_time);
468
  if (empty($this->errors)) {
469
  $send_an_email = true;
470
  $final_message = "The backup apparently succeeded and is now complete";
471
+ } elseif ($this->newresumption_scheduled == false) {
472
  $send_an_email = true;
473
  $final_message = "The backup attempt has finished, apparently unsuccesfully";
474
  } else {
549
  }
550
  // Delete local files immediately if the option is set
551
  // Where we are only backing up locally, only the "prune" function should do deleting
552
+ if ($this->jobdata_get('service') != '' && $this->jobdata_get('service') != 'none') $this->delete_local($file);
553
  }
554
 
555
  // Dispatch to the relevant function
556
  function cloud_backup($backup_array) {
557
+
558
+ $service = $this->jobdata_get('service');
559
  $this->log("Cloud backup selection: ".$service);
560
  @set_time_limit(900);
561
 
578
  }
579
  }
580
 
581
+ function prune_file($service, $dofile, $method_object = null, $object_passback = null ) {
582
+ $this->log("Delete this file: $dofile, service=$service");
583
  $fullpath = $this->backups_dir_location().'/'.$dofile;
584
  // delete it if it's locally available
585
  if (file_exists($fullpath)) {
592
  }
593
 
594
  // Carries out retain behaviour. Pass in a valid S3 or FTP object and path if relevant.
595
+ function prune_retained_backups($service, $backup_method_object = null, $backup_passback = null) {
596
 
597
  // If they turned off deletion on local backups, then there is nothing to do
598
+ if (UpdraftPlus_Options::get_updraft_option('updraft_delete_local') == 0 && $service == 'none') {
599
  $this->log("Prune old backups from local store: nothing to do, since the user disabled local deletion and we are using local backups");
600
  return;
601
  }
629
  if ($db_backups_found > $updraft_retain_db) {
630
  $this->log("$backup_datestamp: over retain limit ($updraft_retain_db); will delete this database");
631
  $dofile = $backup_to_examine['db'];
632
+ if (!empty($dofile)) $this->prune_file($service, $dofile, $backup_method_object, $backup_passback);
633
  unset($backup_to_examine['db']);
634
  }
635
  }
643
  $file3 = isset($backup_to_examine['uploads']) ? $backup_to_examine['uploads'] : "";
644
  $file4 = isset($backup_to_examine['others']) ? $backup_to_examine['others'] : "";
645
  foreach (array($file, $file2, $file3, $file4) as $dofile) {
646
+ if (!empty($dofile)) $this->prune_file($service, $dofile, $backup_method_object, $backup_passback);
647
  }
648
  unset($backup_to_examine['plugins']);
649
  unset($backup_to_examine['themes']);
1911
  <div style="float:left; width:200px; padding-top: 40px;">
1912
  <form method="post" action="">
1913
  <input type="hidden" name="action" value="updraft_backup" />
1914
+ <p><input type="submit" <?php echo $backup_disabled ?> class="button-primary" value="Backup Now!" style="padding-top:2px;padding-bottom:2px;font-size:22px !important" onclick="return(confirm('This will schedule a one-time backup. To trigger the backup you should go ahead, then wait 10 seconds, then visit any page on your site. WordPress should then start the backup running in the background.'))"></p>
1915
  </form>
1916
  <div style="position:relative">
1917
  <div style="position:absolute;top:0;left:0">
1920
  $backup_history = (is_array($backup_history))?$backup_history:array();
1921
  $restore_disabled = (count($backup_history) == 0) ? 'disabled="disabled"' : "";
1922
  ?>
1923
+ <input type="button" class="button-primary" <?php echo $restore_disabled ?> value="Restore" style="padding-top:2px;padding-bottom:2px;font-size:22px !important" onclick="jQuery('#backup-restore').fadeIn('slow');jQuery(this).parent().fadeOut('slow')">
1924
  </div>
1925
  <div style="display:none;position:absolute;top:0;left:0" id="backup-restore">
1926
  <form method="post" action="">