UpdraftPlus WordPress Backup Plugin - Version 0.9.1

Version Description

  • 11/19/2012 =
  • Failed uploads can now be resumed, giving really big blogs a better opportunity to eventually succeed uploading
Download this release

Release Info

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

Code changes from version 0.8.50 to 0.9.1

Files changed (3) hide show
  1. readme.txt +20 -10
  2. screenshot-1.png +0 -0
  3. updraftplus.php +182 -51
readme.txt CHANGED
@@ -2,17 +2,17 @@
2
  Contributors: David Anderson
3
  Tags: backup, restore, database, cloud, amazon, s3, Amazon S3, google drive, google, gdrive, ftp, cloud, updraft, back up
4
  Requires at least: 3.2
5
- Tested up to: 3.4.2
6
- Stable tag: 0.8.37
7
  Donate link: http://david.dw-perspective.org.uk/donate
8
  License: GPLv2 or later
9
 
10
- == Description ==
 
11
 
12
- UpdraftPlus simplifies backups (and restoration). Backup into the cloud (S3, Google Drive, FTP, and email) and restore with a single click. Backups of files and database can have separate schedules.
13
 
14
- == Upgrade Notice ==
15
- Now backs up all directories found in the WP content directory (not just plugins/themes/uploads)
16
 
17
  == Installation ==
18
 
@@ -33,7 +33,11 @@ You can check the changelog for changes; but the original Updraft, before I fork
33
 
34
  That's very good of you, thank you. You are looking for WordShell, <a href="http://wordshell.net">http://wordshell.net</a>.
35
 
36
- = I want to restore, but cannot do so from the WP Admin console =
 
 
 
 
37
 
38
  That's no problem. If you have your backed files, then you simply need to unzip them into the right places. UpdraftPlus does not back up the WordPress core - you can just get a fresh copy of that from www.wordpress.org. After installing that, then unzip the zip files for your uploads, themes and plugins back into the wp-content directory. Then re-install the database (e.g. by running it through PHPMyAdmin). Please don't ask me how to carry out these steps - they are basic operations which you can hire any of hundreds of thousands of people to show you how to do.
39
 
@@ -50,7 +54,7 @@ Unless you disable any of these, it will back up your database (all tables which
50
  It does not back up WordPress core (since you can always get another copy of this from wordpress.org), and does not back up any extra files which you have added outside of the WordPress content directory (files which, by their nature, are unknown to WordPress). By default the WordPress content directory is "wp-content" in your WordPress root. It will not back up database tables which do not have the WordPress prefix (i.e. database tables from other applications but sharing a database with WordPress).
51
 
52
  = Any known bugs ? =
53
- The major one is that backups of very large sites (lots of uploaded media) can fail due to timing out. If your site is very large, then be doubly-sure to test when setting up that your backups are not empty.
54
 
55
  = I encrypted my database - how do I decrypt it? =
56
 
@@ -66,10 +70,16 @@ Contact me! This is a complex plugin and the only way I can ensure it's robust i
66
 
67
  == Changelog ==
68
 
69
- = 0.8.50 - 13/10/2012 =
 
 
 
 
 
 
70
  * Important new feature: back up other directories found in the WP content directory (not just plugins/themes/uploads, as in original Updraft)
71
 
72
- = 0.8.37 - 12/10/2012 =
73
  * Don't whinge about Google Drive authentication if that method is not current
74
 
75
  = 0.8.36 - 03/10/2012 =
2
  Contributors: David Anderson
3
  Tags: backup, restore, database, cloud, amazon, s3, Amazon S3, google drive, google, gdrive, ftp, cloud, updraft, back up
4
  Requires at least: 3.2
5
+ Tested up to: 3.5
6
+ Stable tag: 0.8.51
7
  Donate link: http://david.dw-perspective.org.uk/donate
8
  License: GPLv2 or later
9
 
10
+ == Upgrade Notice ==
11
+ Screenshots now moved into assets directory. Also, try the trunk for 0.9 with new code for re-trying of failed uploads.
12
 
13
+ == Description ==
14
 
15
+ UpdraftPlus simplifies backups and restoration. Schedule backups into the cloud (S3, Google Drive, FTP, and email) and restore with a single click.
 
16
 
17
  == Installation ==
18
 
33
 
34
  That's very good of you, thank you. You are looking for WordShell, <a href="http://wordshell.net">http://wordshell.net</a>.
35
 
36
+ = Some of my files have uploaded into my cloud storage, but not others. =
37
+
38
+ From version 0.9.0, UpdraftPlus features a resumption feature - if you wait 5 minutes and visit a page on your site, then it should re-try not-yet-uploaded files. If that fails, then turn on debugging and paste the debug log (log in via FTP, and look in wp-content/updraft) into the support forum.
39
+
40
+ = I want to restore, but have failed to do so from the WP Admin console =
41
 
42
  That's no problem. If you have your backed files, then you simply need to unzip them into the right places. UpdraftPlus does not back up the WordPress core - you can just get a fresh copy of that from www.wordpress.org. After installing that, then unzip the zip files for your uploads, themes and plugins back into the wp-content directory. Then re-install the database (e.g. by running it through PHPMyAdmin). Please don't ask me how to carry out these steps - they are basic operations which you can hire any of hundreds of thousands of people to show you how to do.
43
 
54
  It does not back up WordPress core (since you can always get another copy of this from wordpress.org), and does not back up any extra files which you have added outside of the WordPress content directory (files which, by their nature, are unknown to WordPress). By default the WordPress content directory is "wp-content" in your WordPress root. It will not back up database tables which do not have the WordPress prefix (i.e. database tables from other applications but sharing a database with WordPress).
55
 
56
  = Any known bugs ? =
57
+ The major one is that backups of very large sites (lots of uploaded media) can fail due to timing out. If your site is very large, then be doubly-sure to test when setting up that your backups are not empty. Since 0.9.0 there is a feature to re-try failed uploads on a separate scheduled run, which means UpdraftPlus should succeed for more sites than before (since we now only need enough time on each run to upload a single file, not all of them).
58
 
59
  = I encrypted my database - how do I decrypt it? =
60
 
70
 
71
  == Changelog ==
72
 
73
+ = 0.9.1 - 11/19/2012 =
74
+ * Failed uploads can now be resumed, giving really big blogs a better opportunity to eventually succeed uploading
75
+
76
+ = 0.8.51 - 11/19/2012 =
77
+ * Moved screenshot into assets, reducing plugin download size
78
+
79
+ = 0.8.50 - 10/13/2012 =
80
  * Important new feature: back up other directories found in the WP content directory (not just plugins/themes/uploads, as in original Updraft)
81
 
82
+ = 0.8.37 - 10/12/2012 =
83
  * Don't whinge about Google Drive authentication if that method is not current
84
 
85
  = 0.8.36 - 03/10/2012 =
screenshot-1.png DELETED
Binary file
updraftplus.php CHANGED
@@ -4,24 +4,27 @@ Plugin Name: UpdraftPlus - Backup/Restore
4
  Plugin URI: http://wordpress.org/extend/plugins/updraftplus
5
  Description: Uploads, themes, plugins, and your DB can be automatically backed up to Amazon S3, Google Drive, FTP, or emailed, on separate schedules.
6
  Author: David Anderson.
7
- Version: 0.8.50
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  Author URI: http://wordshell.net
10
  */
11
 
12
  //TODO (some of these items mine, some from original Updraft awaiting review):
13
  //Add DropBox support
14
- //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).
15
  //improve error reporting. s3 and dir backup have decent reporting now, but not sure i know what to do from here
16
  //list backups that aren't tracked (helps with double backup problem)
17
  //investigate $php_errormsg further
18
  //pretty up return messages in admin area
19
  //check s3/ftp download
20
 
 
 
21
  /* More TODO:
22
  DONE, TESTING: 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
23
  Use only one entry in WP options database
24
  Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
 
25
  */
26
 
27
  /* Portions copyright 2010 Paul Kehrer
@@ -56,7 +59,7 @@ define('UPDRAFT_DEFAULT_OTHERS_EXCLUDE','upgrade,cache,updraft,index.php');
56
 
57
  class UpdraftPlus {
58
 
59
- var $version = '0.8.50';
60
 
61
  var $dbhandle;
62
  var $errors = array();
@@ -74,7 +77,10 @@ class UpdraftPlus {
74
  add_action('updraft_backup_database', array($this,'backup_database'));
75
  # backup_all is used by the manual "Backup Now" button
76
  add_action('updraft_backup_all', array($this,'backup_all'));
 
 
77
  add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup'));
 
78
  add_filter('cron_schedules', array($this,'modify_cron_schedules'));
79
  add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2);
80
  add_action('init', array($this, 'googledrive_backup_auth'));
@@ -457,11 +463,68 @@ class UpdraftPlus {
457
 
458
  # Logs the given line, adding date stamp and newline
459
  function log($line) {
460
- if ($this->logfile_handle) {
461
- fwrite($this->logfile_handle,date('r')." ".$line."\n");
462
- }
463
  }
464
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
  function backup_all() {
466
  $this->backup(true,true);
467
  }
@@ -475,18 +538,32 @@ class UpdraftPlus {
475
  # Note that nothing will happen if the file backup had the same schedule
476
  $this->backup(false,true);
477
  }
478
-
479
- //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.
480
- function backup($backup_files, $backup_database) {
481
 
482
- //generate backup information
483
- $this->backup_time_nonce();
484
-
485
  //set log file name and open log file
486
  $updraft_dir = $this->backups_dir_location();
487
- $this->logfile_name = $updraft_dir. "/log." . $this->nonce . ".txt";
488
  // Use append mode in case it already exists
489
  $this->logfile_handle = fopen($this->logfile_name, 'a');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
 
491
  // Log some information that may be helpful
492
  global $wp_version;
@@ -495,10 +572,7 @@ class UpdraftPlus {
495
  # If the files and database schedules are the same, and if this the file one, then we rope in database too.
496
  # On the other hand, if the schedules were the same and this was the database run, then there is nothing to do.
497
  if (get_option('updraft_interval') == get_option('updraft_interval_database') || get_option('updraft_interval_database','xyz') == 'xyz' ) {
498
- if ($backup_files == true)
499
- { $backup_database = true; }
500
- else
501
- { $backup_database = false; }
502
  }
503
 
504
  $this->log("Processed schedules. Tasks now: Backup files: $backup_files Backup DB: $backup_database");
@@ -526,45 +600,88 @@ class UpdraftPlus {
526
  $backup_contains = ($backup_files) ? "Files and database" : "Database only (no files)";
527
  }
528
 
 
 
529
  //save this to our history so we can track backups for the retain feature
530
  $this->log("Saving backup history");
 
531
  $this->save_backup_history($backup_array);
532
 
533
  //cloud operations (S3,Google Drive,FTP,email,nothing)
534
- //this also calls the retain feature at the end (done in this method to reuse existing cloud connections)
535
  if(is_array($backup_array) && count($backup_array) >0) {
536
  $this->log("Beginning dispatch of backup to remote");
537
  $this->cloud_backup($backup_array);
538
  }
539
- //delete local files if the pref is set
540
- foreach($backup_array as $file) {
541
- $this->delete_local($file);
542
- }
543
-
544
  //save the last backup info, including errors, if any
545
  $this->log("Saving last backup information into WordPress db");
546
  $this->save_last_backup($backup_array);
547
-
548
- if(get_option('updraft_email') != "" && get_option('updraft_service') != 'email') {
549
- $sendmail_to = get_option('updraft_email');
550
- $this->log("Sending email report to: ".$sendmail_to);
551
- $append_log = "";
552
- if(get_option('updraft_debug_mode') && $this->logfile_name != "") {
553
- $append_log .= "\r\nLog contents:\r\n".file_get_contents($this->logfile_name);
554
- }
555
- 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\nBackup contains: $backup_contains\r\n\r\n".$this->wordshell_random_advert(0)."\r\n".$append_log);
556
- }
557
  }
558
-
559
- // Close log file
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
560
  @fclose($this->logfile_handle);
561
- if (!get_option('updraft_debug_mode')) { @unlink($this->logfile_name); }
 
 
562
  }
563
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564
  function save_last_backup($backup_array) {
565
  $success = (empty($this->errors))?1:0;
566
- $last_backup = array('backup_time'=>$this->backup_time,'backup_array'=>$backup_array,'success'=>$success,'errors'=>$this->errors);
567
- update_option('updraft_last_backup',$last_backup);
 
 
 
 
 
 
 
 
 
568
  }
569
 
570
  function cloud_backup($backup_array) {
@@ -572,17 +689,17 @@ class UpdraftPlus {
572
  case 's3':
573
  @set_time_limit(900);
574
  $this->log("Cloud backup: S3");
575
- if (count($backup_array) >0) { $this->s3_backup($backup_array); }
576
  break;
577
  case 'googledrive':
578
  @set_time_limit(900);
579
  $this->log("Cloud backup: Google Drive");
580
- if (count($backup_array) >0) { $this->googledrive_backup($backup_array); }
581
  break;
582
  case 'ftp':
583
  @set_time_limit(900);
584
  $this->log("Cloud backup: FTP");
585
- if (count($backup_array) >0) { $this->ftp_backup($backup_array); }
586
  break;
587
  case 'email':
588
  @set_time_limit(900);
@@ -591,6 +708,7 @@ class UpdraftPlus {
591
  foreach($backup_array as $type=>$file) {
592
  $fullpath = trailingslashit(get_option('updraft_dir')).$file;
593
  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));
 
594
  }
595
  //we don't break here so it goes and executes all the default behavior below as well. this gives us retain behavior for email
596
  default:
@@ -717,9 +835,7 @@ class UpdraftPlus {
717
  }
718
 
719
  function s3_backup($backup_array) {
720
- if(!class_exists('S3')) {
721
- require_once(dirname(__FILE__).'/includes/S3.php');
722
- }
723
  $s3 = new S3(get_option('updraft_s3_login'), get_option('updraft_s3_pass'));
724
  $bucket_name = untrailingslashit(get_option('updraft_s3_remote_path'));
725
  $bucket_path = "";
@@ -735,6 +851,9 @@ class UpdraftPlus {
735
  if (!$s3->putObjectFile($fullpath, $bucket_name, $bucket_path.$file)) {
736
  $this->log("S3 upload: failed");
737
  $this->error("S3 Error: Failed to upload $fullpath. Error was ".$php_errormsg);
 
 
 
738
  }
739
  }
740
  $this->prune_retained_backups('s3',$s3,$orig_bucket_name);
@@ -753,7 +872,9 @@ class UpdraftPlus {
753
  $timer_start = microtime( true );
754
  if ( $id = $this->googledrive_upload_file( $file_path, $file_name, get_option('updraft_googledrive_remotepath'), $access ) ) {
755
  $this->log('OK: Archive ' . $file_name . ' uploaded to Google Drive in ' . ( round(microtime( true ) - $timer_start,2) ) . ' seconds' );
 
756
  } else {
 
757
  $this->log("ERROR: $file_name: Failed to upload to Google Drive" );
758
  }
759
  }
@@ -776,7 +897,13 @@ class UpdraftPlus {
776
  $ftp_remote_path = trailingslashit(get_option('updraft_ftp_remote_path'));
777
  foreach($backup_array as $file) {
778
  $fullpath = trailingslashit(get_option('updraft_dir')).$file;
779
- $ftp->put($fullpath,$ftp_remote_path.$file,FTP_BINARY);
 
 
 
 
 
 
780
  }
781
  $this->prune_retained_backups("ftp",$ftp,$ftp_remote_path);
782
  }
@@ -804,7 +931,7 @@ class UpdraftPlus {
804
 
805
  $updraft_dir = $this->backups_dir_location();
806
  if(!is_writable($updraft_dir)) {
807
- $this->error('Backup directory is not writable.','fatal');
808
  }
809
  //get the blog name and rip out all non-alphanumeric chars other than _
810
  $blog_name = str_replace(' ','_',get_bloginfo());
@@ -919,7 +1046,7 @@ class UpdraftPlus {
919
  }
920
 
921
  function save_backup_history($backup_array) {
922
- //this stores full paths right now. should probably concatenate with ABSPATH to make it easier to move sites
923
  if(is_array($backup_array)) {
924
  $backup_history = get_option('updraft_backup_history');
925
  $backup_history = (is_array($backup_history)) ? $backup_history : array();
@@ -1698,7 +1825,11 @@ ENDHERE;
1698
  }
1699
 
1700
  if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') {
1701
- wp_schedule_single_event(time()+5, 'updraft_backup_all');
 
 
 
 
1702
  }
1703
  if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') {
1704
  $this->backup(true,true);
@@ -1724,7 +1855,7 @@ ENDHERE;
1724
  echo "<div style=\"color:blue\">Old directories successfully deleted.</div>";
1725
  }
1726
  if(!$this->memory_check(96)) {?>
1727
- <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>
1728
  <?php
1729
  }
1730
  if(!$this->execution_time_check(300)) {?>
@@ -1779,7 +1910,7 @@ ENDHERE;
1779
  $backup_disabled = "";
1780
  } else {
1781
  $backup_disabled = 'disabled="disabled"';
1782
- $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=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>';
1783
  }
1784
  ?>
1785
  <th>Now:</th>
4
  Plugin URI: http://wordpress.org/extend/plugins/updraftplus
5
  Description: Uploads, themes, plugins, and your DB can be automatically backed up to Amazon S3, Google Drive, FTP, or emailed, on separate schedules.
6
  Author: David Anderson.
7
+ Version: 0.9.1
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  Author URI: http://wordshell.net
10
  */
11
 
12
  //TODO (some of these items mine, some from original Updraft awaiting review):
13
  //Add DropBox support
14
+ //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?).
15
  //improve error reporting. s3 and dir backup have decent reporting now, but not sure i know what to do from here
16
  //list backups that aren't tracked (helps with double backup problem)
17
  //investigate $php_errormsg further
18
  //pretty up return messages in admin area
19
  //check s3/ftp download
20
 
21
+ //Rip out the "last backup" bit, and/or put in a display of the last log
22
+
23
  /* More TODO:
24
  DONE, TESTING: 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
25
  Use only one entry in WP options database
26
  Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
27
+ // Does not delete old custom directories upon a restore?
28
  */
29
 
30
  /* Portions copyright 2010 Paul Kehrer
59
 
60
  class UpdraftPlus {
61
 
62
+ var $version = '0.9.1';
63
 
64
  var $dbhandle;
65
  var $errors = array();
77
  add_action('updraft_backup_database', array($this,'backup_database'));
78
  # backup_all is used by the manual "Backup Now" button
79
  add_action('updraft_backup_all', array($this,'backup_all'));
80
+ # this is our runs-after-backup event, whose purpose is to see if it succeeded or failed, and resume/mom-up etc.
81
+ add_action('updraft_backup_resume', array($this,'backup_resume'));
82
  add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup'));
83
+ # http://codex.wordpress.org/Plugin_API/Filter_Reference/cron_schedules
84
  add_filter('cron_schedules', array($this,'modify_cron_schedules'));
85
  add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2);
86
  add_action('init', array($this, 'googledrive_backup_auth'));
463
 
464
  # Logs the given line, adding date stamp and newline
465
  function log($line) {
466
+ if ($this->logfile_handle) fwrite($this->logfile_handle,date('r')." ".$line."\n");
 
 
467
  }
468
 
469
+ function backup_resume($resumption_no) {
470
+ // This is scheduled for 5 minutes after a backup job starts
471
+ $bnonce = get_transient('updraftplus_backup_job_nonce');
472
+ if (!$bnonce) return;
473
+ $this->nonce = $bnonce;
474
+ $this->logfile_open($bnonce);
475
+ $this->log("Resume backup ($resumption_no): begin run (will check for any remaining jobs)");
476
+ $btime = get_transient('updraftplus_backup_job_time');
477
+ if (!$btime) {
478
+ $this->log("Did not find stored time setting - aborting");
479
+ return;
480
+ }
481
+ $this->log("Resuming backup: resumption=$resumption_no, nonce=$bnonce, begun at=$btime");
482
+ // Schedule again, to run in 5 minutes again, in case we again fail
483
+ $resume_delay = 300;
484
+ // A different argument than before is needed otherwise the event is ignored
485
+ $next_resumption = $resumption_no+1;
486
+ if ($next_resumption < 10) {
487
+ wp_schedule_single_event(time()+$resume_delay, 'updraft_backup_resume' ,array($next_resumption));
488
+ } else {
489
+ $this->log("This is our tenth attempt - will not try again");
490
+ }
491
+ $this->backup_time = $btime;
492
+
493
+ // Returns an array, most recent first, of backup sets
494
+ $backup_history = $this->get_backup_history();
495
+ if (!isset($backup_history[$btime])) $this->log("Error: Could not find a record in the database of a backup with this timestamp");
496
+
497
+ $our_files=$backup_history[$btime];
498
+ $undone_files = array();
499
+ foreach ($our_files as $key => $file) {
500
+ $hash=md5($file);
501
+ $fullpath = trailingslashit(get_option('updraft_dir')).$file;
502
+ if (get_transient('updraft_'.$hash) === "yes") {
503
+ $this->log("$file: $key: This file has been successfully uploaded in the last 3 hours");
504
+ } elseif (is_file($fullpath)) {
505
+ $this->log("$file: $key: This file has NOT been successfully uploaded in the last 3 hours: will retry");
506
+ $undone_files[$key] = $file;
507
+ } else {
508
+ $this-log("$file: Note: This file was not marked as successfully uploaded, but does not exist on the local filesystem");
509
+ $this->uploaded_file($file);
510
+ }
511
+ }
512
+
513
+ if (count($undone_files) == 0) {
514
+ $this->log("There were no files that needed uploading; backup job is finished");
515
+ return;
516
+ }
517
+
518
+ $this->log("Requesting backup of the files that were not successfully uploaded");
519
+ $this->cloud_backup($undone_files);
520
+ $this->cloud_backup_finish($undone_files);
521
+
522
+ $this->log("Resume backup ($resumption_no): finish run");
523
+
524
+ $this->backup_finish($next_resumption);
525
+
526
+ }
527
+
528
  function backup_all() {
529
  $this->backup(true,true);
530
  }
538
  # Note that nothing will happen if the file backup had the same schedule
539
  $this->backup(false,true);
540
  }
 
 
 
541
 
542
+ function logfile_open($nonce) {
 
 
543
  //set log file name and open log file
544
  $updraft_dir = $this->backups_dir_location();
545
+ $this->logfile_name = $updraft_dir. "/log.$nonce.txt";
546
  // Use append mode in case it already exists
547
  $this->logfile_handle = fopen($this->logfile_name, 'a');
548
+ }
549
+
550
+ //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. TODO: I reckon there is. Store a transient based on the backup schedule. Then as the backup proceeds, check for its existence; if it has changed, then another task has begun, so abort.
551
+ function backup($backup_files, $backup_database) {
552
+
553
+ //generate backup information
554
+ $this->backup_time_nonce();
555
+ // If we don't finish in 3 hours, then we won't finish
556
+ // This transient indicates the identity of the current backup job (which can be used to find the files and logfile)
557
+ set_transient("updraftplus_backup_job_nonce",$this->nonce,3600*3);
558
+ set_transient("updraftplus_backup_job_time",$this->backup_time,3600*3);
559
+ $this->logfile_open($this->nonce);
560
+
561
+ // Schedule the even to run later, which checks on success and can resume the backup
562
+ // We save the time to a variable because it is needed for un-scheduling
563
+ // $resume_delay = (get_option('updraft_debug_mode')) ? 60 : 300;
564
+ $resume_delay = 300;
565
+ wp_schedule_single_event(time()+$resume_delay, 'updraft_backup_resume', array(1));
566
+ $this->log("In case we run out of time, scheduled a resumption at: $resume_delay seconds from now");
567
 
568
  // Log some information that may be helpful
569
  global $wp_version;
572
  # If the files and database schedules are the same, and if this the file one, then we rope in database too.
573
  # On the other hand, if the schedules were the same and this was the database run, then there is nothing to do.
574
  if (get_option('updraft_interval') == get_option('updraft_interval_database') || get_option('updraft_interval_database','xyz') == 'xyz' ) {
575
+ $backup_database = ($backup_files == true) ? true : false;
 
 
 
576
  }
577
 
578
  $this->log("Processed schedules. Tasks now: Backup files: $backup_files Backup DB: $backup_database");
600
  $backup_contains = ($backup_files) ? "Files and database" : "Database only (no files)";
601
  }
602
 
603
+ set_transient("updraftplus_backupcontains", $backup_contains, 3600*3);
604
+
605
  //save this to our history so we can track backups for the retain feature
606
  $this->log("Saving backup history");
607
+ // 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.
608
  $this->save_backup_history($backup_array);
609
 
610
  //cloud operations (S3,Google Drive,FTP,email,nothing)
611
+ //this also calls the retain (prune) feature at the end (done in this method to reuse existing cloud connections)
612
  if(is_array($backup_array) && count($backup_array) >0) {
613
  $this->log("Beginning dispatch of backup to remote");
614
  $this->cloud_backup($backup_array);
615
  }
616
+
 
 
 
 
617
  //save the last backup info, including errors, if any
618
  $this->log("Saving last backup information into WordPress db");
619
  $this->save_last_backup($backup_array);
620
+
621
+ // Delete local files, send the email
622
+ $this->cloud_backup_finish($backup_array);
623
+
 
 
 
 
 
 
624
  }
625
+
626
+ // Close log file; delete and also delete transients if not in debug mode
627
+ $this->backup_finish(1);
628
+
629
+ }
630
+
631
+ function backup_finish($cancel_event) {
632
+
633
+ // In fact, leaving the hook to run (if debug is set) is harmless, as the resume job should only do tasks that were left unfinished, which at this stage is none.
634
+ if (empty($this->errors)) {
635
+ $this->log("There were no errors in the uploads, so the 'resume' event is being unscheduled");
636
+ wp_clear_scheduled_hook('updraft_backup_resume', array($cancel_event));
637
+ delete_transient("updraftplus_backup_job_nonce");
638
+ delete_transient("updraftplus_backup_job_time");
639
+ } else {
640
+ $this->log("There were errors in the uploads, so the 'resume' event is remaining unscheduled");
641
+ }
642
+
643
  @fclose($this->logfile_handle);
644
+
645
+ if (!get_option('updraft_debug_mode')) @unlink($this->logfile_name);
646
+
647
  }
648
+
649
+ function cloud_backup_finish($backup_array) {
650
+
651
+ //delete local files if the pref is set
652
+ foreach($backup_array as $file) { $this->delete_local($file); }
653
+
654
+ // Send the results email if requested
655
+ if(get_option('updraft_email') != "" && get_option('updraft_service') != 'email') $this->send_results_email();
656
+
657
+ }
658
+
659
+
660
+ function send_results_email() {
661
+
662
+ $sendmail_to = get_option('updraft_email');
663
+
664
+ $this->log("Sending email report to: ".$sendmail_to);
665
+
666
+ $append_log = (get_option('updraft_debug_mode') && $this->logfile_name != "") ? "\r\nLog contents:\r\n".file_get_contents($this->logfile_name) : "" ;
667
+
668
+ 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\nBackup contains: ".get_transient("updraftplus_backupcontains")."\r\n\r\n".$this->wordshell_random_advert(0)."\r\n".$append_log);
669
+
670
+ }
671
+
672
  function save_last_backup($backup_array) {
673
  $success = (empty($this->errors))?1:0;
674
+
675
+ $last_backup = array('backup_time'=>$this->backup_time, 'backup_array'=>$backup_array, 'success'=>$success, 'errors'=>$this->errors);
676
+
677
+ update_option('updraft_last_backup', $last_backup);
678
+ }
679
+
680
+ // This should be called whenever a file is successfully uploaded
681
+ function uploaded_file($file) {
682
+ # We take an MD5 hash because set_transient wants a name of 45 characters or less
683
+ $hash = md5($file);
684
+ set_transient("updraft_".$hash, "yes", 3600*3);
685
  }
686
 
687
  function cloud_backup($backup_array) {
689
  case 's3':
690
  @set_time_limit(900);
691
  $this->log("Cloud backup: S3");
692
+ if (count($backup_array) >0) $this->s3_backup($backup_array);
693
  break;
694
  case 'googledrive':
695
  @set_time_limit(900);
696
  $this->log("Cloud backup: Google Drive");
697
+ if (count($backup_array) >0) $this->googledrive_backup($backup_array);
698
  break;
699
  case 'ftp':
700
  @set_time_limit(900);
701
  $this->log("Cloud backup: FTP");
702
+ if (count($backup_array) >0) $this->ftp_backup($backup_array);
703
  break;
704
  case 'email':
705
  @set_time_limit(900);
708
  foreach($backup_array as $type=>$file) {
709
  $fullpath = trailingslashit(get_option('updraft_dir')).$file;
710
  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));
711
+ $this->uploaded_file($file);
712
  }
713
  //we don't break here so it goes and executes all the default behavior below as well. this gives us retain behavior for email
714
  default:
835
  }
836
 
837
  function s3_backup($backup_array) {
838
+ if(!class_exists('S3')) require_once(dirname(__FILE__).'/includes/S3.php');
 
 
839
  $s3 = new S3(get_option('updraft_s3_login'), get_option('updraft_s3_pass'));
840
  $bucket_name = untrailingslashit(get_option('updraft_s3_remote_path'));
841
  $bucket_path = "";
851
  if (!$s3->putObjectFile($fullpath, $bucket_name, $bucket_path.$file)) {
852
  $this->log("S3 upload: failed");
853
  $this->error("S3 Error: Failed to upload $fullpath. Error was ".$php_errormsg);
854
+ } else {
855
+ $this->log("S3 upload: success");
856
+ $this->uploaded_file($file);
857
  }
858
  }
859
  $this->prune_retained_backups('s3',$s3,$orig_bucket_name);
872
  $timer_start = microtime( true );
873
  if ( $id = $this->googledrive_upload_file( $file_path, $file_name, get_option('updraft_googledrive_remotepath'), $access ) ) {
874
  $this->log('OK: Archive ' . $file_name . ' uploaded to Google Drive in ' . ( round(microtime( true ) - $timer_start,2) ) . ' seconds' );
875
+ $this->uploaded_file($file);
876
  } else {
877
+ $this->error("$file_name: Failed to upload to Google Drive" );
878
  $this->log("ERROR: $file_name: Failed to upload to Google Drive" );
879
  }
880
  }
897
  $ftp_remote_path = trailingslashit(get_option('updraft_ftp_remote_path'));
898
  foreach($backup_array as $file) {
899
  $fullpath = trailingslashit(get_option('updraft_dir')).$file;
900
+ if ($ftp->put($fullpath,$ftp_remote_path.$file,FTP_BINARY)) {
901
+ $this->log("ERROR: $file_name: Successfully uploaded via FTP");
902
+ $this->uploaded_file($file);
903
+ } else {
904
+ $this->error("$file_name: Failed to upload to FTP" );
905
+ $this->log("ERROR: $file_name: Failed to upload to FTP" );
906
+ }
907
  }
908
  $this->prune_retained_backups("ftp",$ftp,$ftp_remote_path);
909
  }
931
 
932
  $updraft_dir = $this->backups_dir_location();
933
  if(!is_writable($updraft_dir)) {
934
+ $this->error('Backup directory is not writable, or does not exist.','fatal');
935
  }
936
  //get the blog name and rip out all non-alphanumeric chars other than _
937
  $blog_name = str_replace(' ','_',get_bloginfo());
1046
  }
1047
 
1048
  function save_backup_history($backup_array) {
1049
+ //TODO: this stores full paths right now. should probably concatenate with ABSPATH to make it easier to move sites
1050
  if(is_array($backup_array)) {
1051
  $backup_history = get_option('updraft_backup_history');
1052
  $backup_history = (is_array($backup_history)) ? $backup_history : array();
1825
  }
1826
 
1827
  if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') {
1828
+ if (wp_schedule_single_event(time()+5, 'updraft_backup_all') === false) {
1829
+ echo "<!-- updraft schedule: failed -->";
1830
+ } else {
1831
+ echo "<!-- updraft schedule: ok -->";
1832
+ }
1833
  }
1834
  if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') {
1835
  $this->backup(true,true);
1855
  echo "<div style=\"color:blue\">Old directories successfully deleted.</div>";
1856
  }
1857
  if(!$this->memory_check(96)) {?>
1858
+ <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 (though on the other hand, it has been used successfully with a 32Mb limit - your mileage may vary, but don't blame us!). Current limit is: <?php echo $this->memory_check_current(); ?> Mb</div>
1859
  <?php
1860
  }
1861
  if(!$this->execution_time_check(300)) {?>
1910
  $backup_disabled = "";
1911
  } else {
1912
  $backup_disabled = 'disabled="disabled"';
1913
+ $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>';
1914
  }
1915
  ?>
1916
  <th>Now:</th>