UpdraftPlus WordPress Backup Plugin - Version 0.7.7

Version Description

  • 05/29/2012 =
  • Implementation of a logging mechanism to allow easier debugging and development
Download this release

Release Info

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

Code changes from version 0.7.4 to 0.7.7

Files changed (2) hide show
  1. readme.txt +6 -3
  2. updraftplus.php +63 -16
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: David Anderson
3
  Tags: backup, restore, database, cloud, amazon, s3, ftp, cloud
4
  Requires at least: 3.2
5
  Tested up to: 3.3.2
6
- Stable tag: 0.7.4
7
  License: GPLv2 or later
8
 
9
  == Description ==
@@ -39,13 +39,16 @@ Nothing, probably. That's the point of an encryption key - people who don't have
39
 
40
  = I found a bug. What do I do? =
41
 
42
- Contact me! This is a complex plugin and the only way I can ensure it's robust is to get bug reports and fix the problems that crop up. Please include as much information as you can when reporting (PHP version, your blog's site, the error you saw and how you got to the page that caused it, etcetera). If you can send a patch, that's even better.
43
 
44
  == Upgrade Notice ==
45
- New fork of Updraft, fixing some serious bugs and adding new features
46
 
47
  == Changelog ==
48
 
 
 
 
49
  = 0.7.4 - 05/21/2012 =
50
  * Removed CloudFront method; I have no way of testing this
51
  * Backup all tables found in the database that have this site's table prefix
3
  Tags: backup, restore, database, cloud, amazon, s3, ftp, cloud
4
  Requires at least: 3.2
5
  Tested up to: 3.3.2
6
+ Stable tag: 0.7.7
7
  License: GPLv2 or later
8
 
9
  == Description ==
39
 
40
  = I found a bug. What do I do? =
41
 
42
+ Contact me! This is a complex plugin and the only way I can ensure it's robust is to get bug reports and fix the problems that crop up. Please turn on debugging mode and send me the log if you can find it. Include as much information as you can when reporting (PHP version, your blog's site, the error you saw and how you got to the page that caused it, etcetera). If you can send a patch, that's even better.
43
 
44
  == Upgrade Notice ==
45
+ Added a logging mechanism for easier development
46
 
47
  == Changelog ==
48
 
49
+ = 0.7.7 - 05/29/2012 =
50
+ * Implementation of a logging mechanism to allow easier debugging and development
51
+
52
  = 0.7.4 - 05/21/2012 =
53
  * Removed CloudFront method; I have no way of testing this
54
  * Backup all tables found in the database that have this site's table prefix
updraftplus.php CHANGED
@@ -2,13 +2,16 @@
2
  /*
3
  Plugin Name: UpdraftPlus - Backup/Restore
4
  Plugin URI: http://wordpress.org/extend/plugins/updraftplus
5
- Description: UpdraftPlus - Backup/Restore is a plugin designed to back up your WordPress blog. Uploads, themes, plugins, and your DB can be backed up to Amazon S3, sent to an FTP server, or even emailed to you on a scheduled basis.
6
  Author: David Anderson.
7
- Version: 0.7.4
8
  Author URI: http://wordshell.net
9
  */
10
 
11
  //TODO:
 
 
 
12
  //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).
13
  //More logging
14
  //improve error reporting. s3 and dir backup have decent reporting now, but not sure i know what to do from here
@@ -25,7 +28,6 @@ Author URI: http://wordshell.net
25
  /* More TODO:
26
  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
27
  Add turn-off-foreign-key-checks stuff into mysql dump (does WP even use these?)
28
- Put DB and file backups onto separate schedules
29
  Use only one entry in WP options database
30
  Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
31
  More verbose debug reports, send debug report in the email
@@ -50,7 +52,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
50
  */
51
  // TODO: Note this might *lower* the limit - should check first.
52
 
53
- @set_time_limit(900); //15 minutes max. i'm not sure how long a really big blog could take to back up?
54
 
55
  $updraft = new UpdraftPlus();
56
 
@@ -61,11 +63,13 @@ if(!$updraft->memory_check(192)) {
61
 
62
  class UpdraftPlus {
63
 
64
- var $version = '0.7.4';
65
 
66
  var $dbhandle;
67
  var $errors = array();
68
  var $nonce;
 
 
69
  var $backup_time;
70
 
71
  function __construct() {
@@ -92,37 +96,69 @@ class UpdraftPlus {
92
  $this->backup_time = time();
93
  $this->nonce = substr(md5(time().rand()),20);
94
  }
 
 
 
 
 
 
 
95
 
96
  //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.
97
  function backup() {
98
  //generate backup information
99
  $this->backup_time_nonce();
100
 
 
 
 
 
 
 
 
 
 
101
  //backup directories and return a numerically indexed array of file paths to the backup files
 
102
  $backup_array = $this->backup_dirs();
103
  //backup DB and return string of file path
 
104
  $db_backup = $this->backup_db();
105
  //add db path to rest of files
106
  if(is_array($backup_array)) { $backup_array['db'] = $db_backup; }
107
  //save this to our history so we can track backups for the retain feature
 
108
  $this->save_backup_history($backup_array);
109
 
110
  //cloud operations (S3,FTP,email,nothing)
111
  //this also calls the retain feature at the end (done in this method to reuse existing cloud connections)
112
  if(is_array($backup_array) && count($backup_array) >0) {
 
113
  $this->cloud_backup($backup_array);
114
  }
115
  //delete local files if the pref is set
116
  foreach($backup_array as $file) {
 
117
  $this->delete_local($file);
118
  }
119
 
120
  //save the last backup info, including errors, if any
 
121
  $this->save_last_backup($backup_array);
122
 
123
  if(get_option('updraft_email') != "" && get_option('updraft_service') != 'email') {
124
- wp_mail(get_option('updraft_email'),'Backed up: '.get_bloginfo('name').' (UpdraftPlus) '.date('Y-m-d H:i',time()),'Site: '.site_url()."\r\nUpdraftPlus WordPress backup is complete.");
 
 
 
 
 
 
125
  }
 
 
 
 
126
  }
127
 
128
  function save_last_backup($backup_array) {
@@ -134,12 +170,18 @@ class UpdraftPlus {
134
  function cloud_backup($backup_array) {
135
  switch(get_option('updraft_service')) {
136
  case 's3':
 
 
137
  if (count($backup_array) >0) { $this->s3_backup($backup_array); }
138
  break;
139
  case 'ftp':
 
 
140
  if (count($backup_array) >0) { $this->ftp_backup($backup_array); }
141
  break;
142
  case 'email':
 
 
143
  //files can easily get way too big for this...
144
  foreach($backup_array as $type=>$file) {
145
  $fullpath = trailingslashit(get_option('updraft_dir')).$file;
@@ -276,6 +318,7 @@ class UpdraftPlus {
276
  $backup_array = array();
277
 
278
  # Plugins
 
279
  if (get_option('updraft_include_plugins', true)) {
280
  $plugins = new PclZip($backup_file_base.'-plugins.zip');
281
  if (!$plugins->create($wp_plugins_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
@@ -285,6 +328,7 @@ class UpdraftPlus {
285
  }
286
 
287
  # Themes
 
288
  if (get_option('updraft_include_themes', true)) {
289
  $themes = new PclZip($backup_file_base.'-themes.zip');
290
  if (!$themes->create($wp_themes_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
@@ -294,6 +338,7 @@ class UpdraftPlus {
294
  }
295
 
296
  # Uploads
 
297
  if (get_option('updraft_include_uploads', true)) {
298
  $uploads = new PclZip($backup_file_base.'-uploads.zip');
299
  if (!$uploads->create($wp_upload_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
@@ -306,7 +351,7 @@ class UpdraftPlus {
306
  }
307
 
308
  function save_backup_history($backup_array) {
309
- //this stores full paths right now. should probably concatenate with ABSPATH to make it easier to move blogs
310
  $backup_history = get_option('updraft_backup_history');
311
  $backup_history = (!is_array($backup_history))?array():$backup_history;
312
  if(is_array($backup_array)) {
@@ -645,7 +690,7 @@ class UpdraftPlus {
645
  this function is both the backup scheduler and ostensibly a filter callback for saving the option.
646
  it is called in the register_setting for the updraft_interval, which means when the admin settings
647
  are saved it is called. it returns the actual result from wp_filter_nohtml_kses (a sanitization filter)
648
- so the option can be properly saved. this is an UGLY HACK and there must be a better way.
649
  */
650
  function schedule_backup($interval) {
651
  //clear schedule and add new so we don't stack up scheduled backups
@@ -654,7 +699,7 @@ class UpdraftPlus {
654
  case 'daily':
655
  case 'weekly':
656
  case 'monthly':
657
- wp_schedule_event(time()+300, $interval, 'updraft_backup');
658
  break;
659
  }
660
  return wp_filter_nohtml_kses($interval);
@@ -980,17 +1025,19 @@ class UpdraftPlus {
980
  array($this,"settings_output"));
981
  }
982
 
983
- function wordshell_random_advert() {
 
 
984
  if (rand(0,1) == 0) {
985
- return 'Like automating WordPress operations? Use the CLI? <a href="http://wordshell.net">You will love WordShell</a> - saves time and money fast.';
986
  } else {
987
- return '<a href="http://wordshell.net">Check out WordShell</a> - manage WordPress from the command line - huge time-saver';
988
  }
989
  }
990
 
991
  function settings_output() {
992
 
993
- $ws_advert = $this->wordshell_random_advert();
994
  echo <<<ENDHERE
995
  <div class="updated fade" style="font-size:140%; padding:14px;">${ws_advert}</div>
996
  ENDHERE;
@@ -1133,7 +1180,7 @@ ENDHERE;
1133
  <div style="float:left;width:200px">
1134
  <form method="post" action="">
1135
  <input type="hidden" name="action" value="updraft_backup" />
1136
- <p><input type="submit" <?php echo $backup_disabled ?> class="button-primary" value="Backup Now!" style="padding-top:7px;padding-bottom:7px;font-size:24px !important" onclick="return(confirm('This will schedule a one time backup. To trigger the backup immediately you may need to load a page on your blog.'))" /></p>
1137
  </form>
1138
  <div style="position:relative">
1139
  <div style="position:absolute;top:0;left:0">
@@ -1156,7 +1203,7 @@ ENDHERE;
1156
  </select>
1157
 
1158
  <input type="hidden" name="action" value="updraft_restore" />
1159
- <input type="submit" <?php echo $restore_disabled ?> class="button-primary" value="Restore Now!" style="padding-top:7px;margin-top:5px;padding-bottom:7px;font-size:24px !important" onclick="return(confirm('Restoring from backup will replace this blog\'s themes, plugins, and uploads directories. DB restoration must be done separately at this time. Continue with the restoration process?'))" />
1160
  </form>
1161
  </div>
1162
  </div>
@@ -1375,7 +1422,7 @@ ENDHERE;
1375
  </tr>
1376
  <tr>
1377
  <th>Debug mode:</th>
1378
- <td><input type="checkbox" name="updraft_debug_mode" value="1" <?php echo $debug_mode; ?> /> <br />Check this for more information, if something is going wrong.</td>
1379
  </tr>
1380
  <tr>
1381
  <td>
2
  /*
3
  Plugin Name: UpdraftPlus - Backup/Restore
4
  Plugin URI: http://wordpress.org/extend/plugins/updraftplus
5
+ Description: UpdraftPlus - Backup/Restore is a plugin designed to back up your WordPress site. Uploads, themes, plugins, and your DB can be backed up to Amazon S3, sent to an FTP server, or even emailed to you on a scheduled basis.
6
  Author: David Anderson.
7
+ Version: 0.7.7
8
  Author URI: http://wordshell.net
9
  */
10
 
11
  //TODO:
12
+ //Put DB and file backups onto separate schedules. If the option is set identically then do in one run, otherwise do separately.
13
+ //Add DropBox support
14
+ //Add more logging
15
  //Struggles with large uploads - runs out of time before finishing. Break into chunks? Resume download on later run? (Add a new scheduled event to check on progress? Separate the upload from the creation?). Add in some logging (in a .php file that exists first).
16
  //More logging
17
  //improve error reporting. s3 and dir backup have decent reporting now, but not sure i know what to do from here
28
  /* More TODO:
29
  Are all directories in wp-content covered? No; only plugins, themes, content. We should check for others and allow the user the chance to choose which ones he wants
30
  Add turn-off-foreign-key-checks stuff into mysql dump (does WP even use these?)
 
31
  Use only one entry in WP options database
32
  Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
33
  More verbose debug reports, send debug report in the email
52
  */
53
  // TODO: Note this might *lower* the limit - should check first.
54
 
55
+ @set_time_limit(900); //15 minutes max. i'm not sure how long a really big site could take to back up?
56
 
57
  $updraft = new UpdraftPlus();
58
 
63
 
64
  class UpdraftPlus {
65
 
66
+ var $version = '0.7.7';
67
 
68
  var $dbhandle;
69
  var $errors = array();
70
  var $nonce;
71
+ var $logfile_name = "";
72
+ var $logfile_handle = false;
73
  var $backup_time;
74
 
75
  function __construct() {
96
  $this->backup_time = time();
97
  $this->nonce = substr(md5(time().rand()),20);
98
  }
99
+
100
+ # Logs the given line, adding date stamp and newline
101
+ function log($line) {
102
+ if ($this->logfile_handle) {
103
+ fwrite($this->logfile_handle,date('r')." ".$line."\n");
104
+ }
105
+ }
106
 
107
  //scheduled wp-cron events can have a race condition here if page loads are coming fast enough, but there's nothing we can do about it.
108
  function backup() {
109
  //generate backup information
110
  $this->backup_time_nonce();
111
 
112
+ //set log file name
113
+ $updraft_dir = $this->backups_dir_location();
114
+ $this->logfile_name = $updraft_dir. "/log." . $this->nonce . ".txt";
115
+
116
+ # Use append mode in case it already exists
117
+ $this->logfile_handle = fopen($this->logfile_name, 'a');
118
+ // Some information that may be helpful
119
+ global $wp_version;
120
+ $this->log("PHP version: ".phpversion()." WordPress version: ".$wp_version);
121
  //backup directories and return a numerically indexed array of file paths to the backup files
122
+ $this->log("Beginning backup of directories");
123
  $backup_array = $this->backup_dirs();
124
  //backup DB and return string of file path
125
+ $this->log("Beginning backup of database");
126
  $db_backup = $this->backup_db();
127
  //add db path to rest of files
128
  if(is_array($backup_array)) { $backup_array['db'] = $db_backup; }
129
  //save this to our history so we can track backups for the retain feature
130
+ $this->log("Saving backup history");
131
  $this->save_backup_history($backup_array);
132
 
133
  //cloud operations (S3,FTP,email,nothing)
134
  //this also calls the retain feature at the end (done in this method to reuse existing cloud connections)
135
  if(is_array($backup_array) && count($backup_array) >0) {
136
+ $this->log("Beginning dispatch of backup to remote");
137
  $this->cloud_backup($backup_array);
138
  }
139
  //delete local files if the pref is set
140
  foreach($backup_array as $file) {
141
+ $this->log("Deleting local file: $file");
142
  $this->delete_local($file);
143
  }
144
 
145
  //save the last backup info, including errors, if any
146
+ $this->log("Saving last backup information into WordPress db");
147
  $this->save_last_backup($backup_array);
148
 
149
  if(get_option('updraft_email') != "" && get_option('updraft_service') != 'email') {
150
+ $sendmail_to = get_option('updraft_email');
151
+ $this->log("Sending email report to: ".$sendmail_to);
152
+ $append_log = "";
153
+ if(get_option('updraft_debug_mode') && $this->logfile_name != "") {
154
+ $append_log .= "\r\nLog contents:\r\n".file_get_contents($this->logfile_name);
155
+ }
156
+ wp_mail($sendmail_to,'Backed up: '.get_bloginfo('name').' (UpdraftPlus) '.date('Y-m-d H:i',time()),'Site: '.site_url()."\r\nUpdraftPlus WordPress backup is complete.\r\n\r\n".$this->wordshell_random_advert(0)."\r\n".$append_log);
157
  }
158
+
159
+ // Close log file
160
+ close($this->logfile_handle);
161
+ if (!get_option('updraft_debug_mode')) { @unlink($this->logfile_name); }
162
  }
163
 
164
  function save_last_backup($backup_array) {
170
  function cloud_backup($backup_array) {
171
  switch(get_option('updraft_service')) {
172
  case 's3':
173
+ @set_time_limit(900);
174
+ $this->log("Cloud backup: S3");
175
  if (count($backup_array) >0) { $this->s3_backup($backup_array); }
176
  break;
177
  case 'ftp':
178
+ @set_time_limit(900);
179
+ $this->log("Cloud backup: FTP");
180
  if (count($backup_array) >0) { $this->ftp_backup($backup_array); }
181
  break;
182
  case 'email':
183
+ @set_time_limit(900);
184
+ $this->log("Cloud backup: Email");
185
  //files can easily get way too big for this...
186
  foreach($backup_array as $type=>$file) {
187
  $fullpath = trailingslashit(get_option('updraft_dir')).$file;
318
  $backup_array = array();
319
 
320
  # Plugins
321
+ @set_time_limit(900);
322
  if (get_option('updraft_include_plugins', true)) {
323
  $plugins = new PclZip($backup_file_base.'-plugins.zip');
324
  if (!$plugins->create($wp_plugins_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
328
  }
329
 
330
  # Themes
331
+ @set_time_limit(900);
332
  if (get_option('updraft_include_themes', true)) {
333
  $themes = new PclZip($backup_file_base.'-themes.zip');
334
  if (!$themes->create($wp_themes_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
338
  }
339
 
340
  # Uploads
341
+ @set_time_limit(900);
342
  if (get_option('updraft_include_uploads', true)) {
343
  $uploads = new PclZip($backup_file_base.'-uploads.zip');
344
  if (!$uploads->create($wp_upload_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
351
  }
352
 
353
  function save_backup_history($backup_array) {
354
+ //this stores full paths right now. should probably concatenate with ABSPATH to make it easier to move sites
355
  $backup_history = get_option('updraft_backup_history');
356
  $backup_history = (!is_array($backup_history))?array():$backup_history;
357
  if(is_array($backup_array)) {
690
  this function is both the backup scheduler and ostensibly a filter callback for saving the option.
691
  it is called in the register_setting for the updraft_interval, which means when the admin settings
692
  are saved it is called. it returns the actual result from wp_filter_nohtml_kses (a sanitization filter)
693
+ so the option can be properly saved.
694
  */
695
  function schedule_backup($interval) {
696
  //clear schedule and add new so we don't stack up scheduled backups
699
  case 'daily':
700
  case 'weekly':
701
  case 'monthly':
702
+ wp_schedule_event(time()+30, $interval, 'updraft_backup');
703
  break;
704
  }
705
  return wp_filter_nohtml_kses($interval);
1025
  array($this,"settings_output"));
1026
  }
1027
 
1028
+ function wordshell_random_advert($urls) {
1029
+ $url_start = ($urls) ? '<a href="http://wordshell.net">' : "";
1030
+ $url_end = ($urls) ? '</a>' : " (www.wordshell.net)";
1031
  if (rand(0,1) == 0) {
1032
+ return "Like automating WordPress operations? Use the CLI? ${url_start}You will love WordShell${url_end} - saves time and money fast.";
1033
  } else {
1034
+ return "${url_start}Check out WordShell${url_end} - manage WordPress from the command line - huge time-saver";
1035
  }
1036
  }
1037
 
1038
  function settings_output() {
1039
 
1040
+ $ws_advert = $this->wordshell_random_advert(1);
1041
  echo <<<ENDHERE
1042
  <div class="updated fade" style="font-size:140%; padding:14px;">${ws_advert}</div>
1043
  ENDHERE;
1180
  <div style="float:left;width:200px">
1181
  <form method="post" action="">
1182
  <input type="hidden" name="action" value="updraft_backup" />
1183
+ <p><input type="submit" <?php echo $backup_disabled ?> class="button-primary" value="Backup Now!" style="padding-top:7px;padding-bottom:7px;font-size:24px !important" onclick="return(confirm('This will schedule a one time backup. To trigger the backup immediately you may need to load a page on your site.'))" /></p>
1184
  </form>
1185
  <div style="position:relative">
1186
  <div style="position:absolute;top:0;left:0">
1203
  </select>
1204
 
1205
  <input type="hidden" name="action" value="updraft_restore" />
1206
+ <input type="submit" <?php echo $restore_disabled ?> class="button-primary" value="Restore Now!" style="padding-top:7px;margin-top:5px;padding-bottom:7px;font-size:24px !important" onclick="return(confirm('Restoring from backup will replace this site\'s themes, plugins, and uploads directories. DB restoration must be done separately at this time. Continue with the restoration process?'))" />
1207
  </form>
1208
  </div>
1209
  </div>
1422
  </tr>
1423
  <tr>
1424
  <th>Debug mode:</th>
1425
+ <td><input type="checkbox" name="updraft_debug_mode" value="1" <?php echo $debug_mode; ?> /> <br />Check this for more information, if something is going wrong. Will also drop a log file in your backup directory which you can examine.</td>
1426
  </tr>
1427
  <tr>
1428
  <td>