UpdraftPlus WordPress Backup Plugin - Version 1.4.27

Version Description

  • 02/23/2013 =
  • Now remembers what cloud service you used for historical backups, if you later switch
  • Now performs user downloads from the settings page asynchronously, meaning that enormous backups can be fetched this way
  • Fixed bug which forced GoogleDrive users to re-authenticate unnecessarily
  • Fixed apparent race condition that broke some backups
  • Include disk free space warning
  • More intelligent scheduling of resumptions, leading to faster completion on hosts with low max_execution_time values
  • Polls and updates in-page backup history status (no refresh required)
Download this release

Release Info

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

Code changes from version 1.4.14 to 1.4.27

includes/Dropbox/API.php CHANGED
@@ -202,19 +202,30 @@ class Dropbox_API
202
  * @param string $file Path to file, relative to root, including path
203
  * @param string $outFile Filename to write the downloaded file to
204
  * @param string $revision The revision of the file to retrieve
 
205
  * @return array
206
  */
207
- public function getFile($file, $outFile = false, $revision = null)
208
  {
209
  // Only allow php response format for this call
210
  if ($this->responseFormat !== 'php') {
211
  throw new Exception('This method only supports the `php` response format');
212
  }
 
 
213
 
214
  $handle = null;
215
  if ($outFile !== false) {
216
  // Create a file handle if $outFile is specified
217
- if (!$handle = fopen($outFile, 'w')) {
 
 
 
 
 
 
 
 
218
  throw new Exception("Unable to open file handle for $outFile");
219
  } else {
220
  $this->OAuth->setOutFile($handle);
@@ -223,7 +234,6 @@ class Dropbox_API
223
 
224
  $file = $this->encodePath($file);
225
  $call = 'files/' . $this->root . '/' . $file;
226
- $params = array('rev' => $revision);
227
  $response = $this->fetch('GET', self::CONTENT_URL, $call, $params);
228
 
229
  // Close the file handle if one was opened
202
  * @param string $file Path to file, relative to root, including path
203
  * @param string $outFile Filename to write the downloaded file to
204
  * @param string $revision The revision of the file to retrieve
205
+ * @param boolean $allow_resume - append to the file if it already exists
206
  * @return array
207
  */
208
+ public function getFile($file, $outFile = false, $revision = null, $allow_resume = false)
209
  {
210
  // Only allow php response format for this call
211
  if ($this->responseFormat !== 'php') {
212
  throw new Exception('This method only supports the `php` response format');
213
  }
214
+
215
+ $params = array('rev' => $revision);
216
 
217
  $handle = null;
218
  if ($outFile !== false) {
219
  // Create a file handle if $outFile is specified
220
+ if ($allow_resume && file_exists($outFile)) {
221
+ if (!$handle = fopen($outFile, 'a')) {
222
+ throw new Exception("Unable to open file handle for $outFile");
223
+ } else {
224
+ $this->OAuth->setOutFile($handle);
225
+ $params['headers'] = array('Range: bytes='.filesize($outFile).'-');
226
+ }
227
+ }
228
+ elseif (!$handle = fopen($outFile, 'w')) {
229
  throw new Exception("Unable to open file handle for $outFile");
230
  } else {
231
  $this->OAuth->setOutFile($handle);
234
 
235
  $file = $this->encodePath($file);
236
  $call = 'files/' . $this->root . '/' . $file;
 
237
  $response = $this->fetch('GET', self::CONTENT_URL, $call, $params);
238
 
239
  // Close the file handle if one was opened
includes/Dropbox/OAuth/Consumer/Curl.php CHANGED
@@ -70,6 +70,7 @@ class Dropbox_Curl extends Dropbox_ConsumerAbstract
70
  $options[CURLOPT_HEADER] = false;
71
  $options[CURLOPT_FILE] = $this->outFile;
72
  $options[CURLOPT_BINARYTRANSFER] = true;
 
73
  $this->outFile = null;
74
  } elseif ($method == 'POST') { // POST
75
  $options[CURLOPT_POST] = true;
70
  $options[CURLOPT_HEADER] = false;
71
  $options[CURLOPT_FILE] = $this->outFile;
72
  $options[CURLOPT_BINARYTRANSFER] = true;
73
+ if (isset($additional['headers'])) $options[CURLOPT_HTTPHEADER] = $additional['headers'];
74
  $this->outFile = null;
75
  } elseif ($method == 'POST') { // POST
76
  $options[CURLOPT_POST] = true;
includes/S3.php CHANGED
@@ -753,9 +753,10 @@ class S3
753
  * @param string $bucket Bucket name
754
  * @param string $uri Object URI
755
  * @param mixed $saveTo Filename or resource to write to
 
756
  * @return mixed
757
  */
758
- public static function getObject($bucket, $uri, $saveTo = false)
759
  {
760
  $rest = new S3Request('GET', $bucket, $uri, self::$endpoint);
761
  if ($saveTo !== false)
@@ -763,14 +764,23 @@ class S3
763
  if (is_resource($saveTo))
764
  $rest->fp =& $saveTo;
765
  else
766
- if (($rest->fp = @fopen($saveTo, 'wb')) !== false)
767
- $rest->file = realpath($saveTo);
768
- else
769
- $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo);
 
 
 
 
 
 
 
 
 
770
  }
771
  if ($rest->response->error === false) $rest->getResponse();
772
 
773
- if ($rest->response->error === false && $rest->response->code !== 200)
774
  $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
775
  if ($rest->response->error !== false)
776
  {
753
  * @param string $bucket Bucket name
754
  * @param string $uri Object URI
755
  * @param mixed $saveTo Filename or resource to write to
756
+ * @param boolean resume, if possible
757
  * @return mixed
758
  */
759
+ public static function getObject($bucket, $uri, $saveTo = false, $resume = false)
760
  {
761
  $rest = new S3Request('GET', $bucket, $uri, self::$endpoint);
762
  if ($saveTo !== false)
764
  if (is_resource($saveTo))
765
  $rest->fp =& $saveTo;
766
  else
767
+ if ($resume && file_exists($saveTo)) {
768
+ if (($rest->fp = @fopen($saveTo, 'ab')) !== false) {
769
+ $rest->setHeader('Range', "bytes=".filesize($saveTo).'-');
770
+ $rest->file = realpath($saveTo);
771
+ } else {
772
+ $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo);
773
+ }
774
+ } else {
775
+ if (($rest->fp = @fopen($saveTo, 'wb')) !== false)
776
+ $rest->file = realpath($saveTo);
777
+ else
778
+ $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo);
779
+ }
780
  }
781
  if ($rest->response->error === false) $rest->getResponse();
782
 
783
+ if ($rest->response->error === false && ( !$resume && $rest->response->code != 200) || ( $resume && $rest->response->code != 206 && $rest->response->code != 200))
784
  $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
785
  if ($rest->response->error !== false)
786
  {
includes/class-gdocs.php CHANGED
@@ -611,17 +611,28 @@ class UpdraftPlus_GDocs {
611
 
612
  }
613
 
614
- public function download_data( $link, $saveas ) {
 
 
 
 
 
 
 
 
615
 
616
- $result = $this->request( $link );
617
 
618
  if ( is_wp_error( $result ) )
619
  return $result;
620
 
621
- if ( $result['response']['code'] != '200' )
622
  return new WP_Error( 'bad_response', "Received response code '" . $result['response']['code'] . " " . $result['response']['message'] . "' while trying to get '" . $url . "'." );
623
 
624
- file_put_contents($saveas, $result['body']);
 
 
 
625
 
626
  }
627
 
611
 
612
  }
613
 
614
+ public function download_data( $link, $saveas, $allow_resume = false ) {
615
+
616
+ if ($allow_resume && is_file($saveas)) {
617
+ $headers = array('Range' => 'bytes='.filesize($saveas).'-');
618
+ $put_flag = FILE_APPEND;
619
+ } else {
620
+ $headers = array();
621
+ $put_flag = NULL;
622
+ }
623
 
624
+ $result = $this->request($link, 'GET', $headers);
625
 
626
  if ( is_wp_error( $result ) )
627
  return $result;
628
 
629
+ if ( $result['response']['code'] != '200' && (!$allow_resume || 206 !== $result['response']['code']))
630
  return new WP_Error( 'bad_response', "Received response code '" . $result['response']['code'] . " " . $result['response']['message'] . "' while trying to get '" . $url . "'." );
631
 
632
+ global $updraftplus;
633
+ $updraftplus->log("Google Drive downloaded bytes: ".strlen($result['body']));
634
+
635
+ file_put_contents($saveas, $result['body'], $put_flag);
636
 
637
  }
638
 
includes/updraft-restorer.php CHANGED
@@ -14,6 +14,7 @@ class Updraft_Restorer extends WP_Upgrader {
14
 
15
  function restore_backup($backup_file, $type) {
16
 
 
17
  if ($type != 'plugins' && $type != 'themes' && $type != 'others' && $type != 'uploads') continue;
18
 
19
  global $wp_filesystem;
@@ -34,7 +35,7 @@ class Updraft_Restorer extends WP_Upgrader {
34
  $working_dir = $this->unpack_package($download , $delete);
35
  if (is_wp_error($working_dir)) return $working_dir;
36
 
37
- if ($type == "others" ) {
38
 
39
  // In this special case, the backup contents are not in a folder, so it is not simply a case of moving the folder around, but rather looping over all that we find
40
 
14
 
15
  function restore_backup($backup_file, $type) {
16
 
17
+ // Various keys can get stored in the data - but only some represent actual data entities
18
  if ($type != 'plugins' && $type != 'themes' && $type != 'others' && $type != 'uploads') continue;
19
 
20
  global $wp_filesystem;
35
  $working_dir = $this->unpack_package($download , $delete);
36
  if (is_wp_error($working_dir)) return $working_dir;
37
 
38
+ if ($type == 'others' ) {
39
 
40
  // In this special case, the backup contents are not in a folder, so it is not simply a case of moving the folder around, but rather looping over all that we find
41
 
methods/dropbox.php CHANGED
@@ -185,7 +185,7 @@ class UpdraftPlus_BackupModule_dropbox {
185
 
186
  $try_the_other_one = false;
187
  try {
188
- $get = $dropbox->getFile($file, $updraft_dir.'/'.$file);
189
  } catch (Exception $e) {
190
  // TODO: Remove this October 2013 (we stored in the wrong place for a while...)
191
  $try_the_other_one = true;
@@ -197,7 +197,10 @@ class UpdraftPlus_BackupModule_dropbox {
197
  $dropbox_folder = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dropbox_folder'));
198
  $updraftplus->error('Dropbox error: '.$e);
199
  try {
200
- $get = $dropbox->getFile($file, $updraft_dir.'/'.$file);
 
 
 
201
  } catch (Exception $e) {
202
  $updraftplus->error($possible_error);
203
  $updraftplus->error($e->getMessage());
185
 
186
  $try_the_other_one = false;
187
  try {
188
+ $get = $dropbox->getFile($file, $updraft_dir.'/'.$file, null, true);
189
  } catch (Exception $e) {
190
  // TODO: Remove this October 2013 (we stored in the wrong place for a while...)
191
  $try_the_other_one = true;
197
  $dropbox_folder = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dropbox_folder'));
198
  $updraftplus->error('Dropbox error: '.$e);
199
  try {
200
+ $get = $dropbox->getFile($file, $updraft_dir.'/'.$file, null, true);
201
+ if (isset($get['response']['body'])) {
202
+ $updraftplus->log("Dropbox: downloaded ".round(strlen($get['response']['body'])/1024,1).' Kb');
203
+ }
204
  } catch (Exception $e) {
205
  $updraftplus->error($possible_error);
206
  $updraftplus->error($e->getMessage());
methods/ftp.php CHANGED
@@ -72,6 +72,7 @@ class UpdraftPlus_BackupModule_ftp {
72
 
73
  $ftp_remote_path = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_ftp_remote_path'));
74
  $fullpath = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir')).$file;
 
75
  $ftp->get($fullpath, $ftp_remote_path.$file, FTP_BINARY);
76
  }
77
 
72
 
73
  $ftp_remote_path = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_ftp_remote_path'));
74
  $fullpath = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir')).$file;
75
+
76
  $ftp->get($fullpath, $ftp_remote_path.$file, FTP_BINARY);
77
  }
78
 
methods/googledrive.php CHANGED
@@ -270,10 +270,11 @@ class UpdraftPlus_BackupModule_googledrive {
270
  return false;
271
  }
272
  // Actually download the thing
 
273
  $download_to = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir')).$file;
274
- $gdocs_object->download_data($content_link, $download_to);
275
 
276
- if (filesize($download_to) >0) {
277
  return true;
278
  } else {
279
  $updraftplus->error("Google Drive error: zero-size file was downloaded");
270
  return false;
271
  }
272
  // Actually download the thing
273
+
274
  $download_to = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir')).$file;
275
+ $gdocs_object->download_data($content_link, $download_to, true);
276
 
277
+ if (filesize($download_to) > 0) {
278
  return true;
279
  } else {
280
  $updraftplus->error("Google Drive error: zero-size file was downloaded");
methods/s3.php CHANGED
@@ -85,9 +85,8 @@ class UpdraftPlus_BackupModule_s3 {
85
  $s3->setExceptions(true);
86
  try {
87
  $uploadId = $s3->initiateMultipartUpload($bucket_name, $filepath);
88
- } catch (Exception $e) {
89
  $updraftplus->log('S3 error whilst trying initiateMultipartUpload: '.$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
90
- $s3->setExceptions(false);
91
  $uploadId = false;
92
  }
93
  $s3->setExceptions(false);
@@ -207,10 +206,12 @@ class UpdraftPlus_BackupModule_s3 {
207
  if (!empty($region)) {
208
  $this->set_endpoint($s3, $region);
209
  $fullpath = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir')).$file;
210
- if (!$s3->getObject($bucket_name, $bucket_path.$file, $fullpath)) {
 
211
  $updraftplus->error("S3 Error: Failed to download $file. Check your permissions and credentials.");
212
  }
213
  } else {
 
214
  $updraftplus->error("S3 Error: Failed to access bucket $bucket_name. Check your permissions and credentials.");
215
  }
216
 
85
  $s3->setExceptions(true);
86
  try {
87
  $uploadId = $s3->initiateMultipartUpload($bucket_name, $filepath);
88
+ } catch (Exception $e) {
89
  $updraftplus->log('S3 error whilst trying initiateMultipartUpload: '.$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
 
90
  $uploadId = false;
91
  }
92
  $s3->setExceptions(false);
206
  if (!empty($region)) {
207
  $this->set_endpoint($s3, $region);
208
  $fullpath = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir')).$file;
209
+ if (!$s3->getObject($bucket_name, $bucket_path.$file, $fullpath, true)) {
210
+ $updraftplus->log("S3 Error: Failed to download $file. Check your permissions and credentials.");
211
  $updraftplus->error("S3 Error: Failed to download $file. Check your permissions and credentials.");
212
  }
213
  } else {
214
+ $updraftplus->log("S3 Error: Failed to access bucket $bucket_name. Check your permissions and credentials.");
215
  $updraftplus->error("S3 Error: Failed to access bucket $bucket_name. Check your permissions and credentials.");
216
  }
217
 
options.php CHANGED
@@ -52,6 +52,7 @@ class UpdraftPlus_Options {
52
  register_setting('updraft-options-group', 'updraft_googledrive_clientid', array($updraftplus, 'googledrive_clientid_checkchange') );
53
  register_setting('updraft-options-group', 'updraft_googledrive_secret' );
54
  register_setting('updraft-options-group', 'updraft_googledrive_remotepath' );
 
55
  register_setting('updraft-options-group', 'updraft_ftp_login' );
56
  register_setting('updraft-options-group', 'updraft_ftp_pass' );
57
  register_setting('updraft-options-group', 'updraft_ftp_remote_path' );
52
  register_setting('updraft-options-group', 'updraft_googledrive_clientid', array($updraftplus, 'googledrive_clientid_checkchange') );
53
  register_setting('updraft-options-group', 'updraft_googledrive_secret' );
54
  register_setting('updraft-options-group', 'updraft_googledrive_remotepath' );
55
+
56
  register_setting('updraft-options-group', 'updraft_ftp_login' );
57
  register_setting('updraft-options-group', 'updraft_ftp_pass' );
58
  register_setting('updraft-options-group', 'updraft_ftp_remote_path' );
readme.txt CHANGED
@@ -3,7 +3,7 @@ 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.4.14
7
  Author URI: http://updraftplus.com
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  License: GPLv3 or later
@@ -21,6 +21,7 @@ Clean-up old rubbish, and display more status in final email
21
  * Files and databases can have separate schedules
22
  * Failed uploads are automatically resumed/retried
23
  * Select which files to backup (plugins, themes, content, other)
 
24
  * Database backups can be encrypted for security
25
  * Debug mode that gives full logging of the backup
26
  * Thousands of users: widely tested and reliable
@@ -100,6 +101,15 @@ Thanks for asking - yes, I have. Check out my profile page - http://profiles.wor
100
 
101
  == Changelog ==
102
 
 
 
 
 
 
 
 
 
 
103
  = 1.4.14 - 02/19/2013 =
104
  * Display final status message in email
105
  * Clean-up any old temporary files detected
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.4.27
7
  Author URI: http://updraftplus.com
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  License: GPLv3 or later
21
  * Files and databases can have separate schedules
22
  * Failed uploads are automatically resumed/retried
23
  * Select which files to backup (plugins, themes, content, other)
24
+ * Download backup archives direct from your WordPress dashboard
25
  * Database backups can be encrypted for security
26
  * Debug mode that gives full logging of the backup
27
  * Thousands of users: widely tested and reliable
101
 
102
  == Changelog ==
103
 
104
+ = 1.4.27 - 02/23/2013 =
105
+ * Now remembers what cloud service you used for historical backups, if you later switch
106
+ * Now performs user downloads from the settings page asynchronously, meaning that enormous backups can be fetched this way
107
+ * Fixed bug which forced GoogleDrive users to re-authenticate unnecessarily
108
+ * Fixed apparent race condition that broke some backups
109
+ * Include disk free space warning
110
+ * More intelligent scheduling of resumptions, leading to faster completion on hosts with low max_execution_time values
111
+ * Polls and updates in-page backup history status (no refresh required)
112
+
113
  = 1.4.14 - 02/19/2013 =
114
  * Display final status message in email
115
  * Clean-up any old temporary files detected
updraftplus.php CHANGED
@@ -4,19 +4,18 @@ Plugin Name: UpdraftPlus - Backup/Restore
4
  Plugin URI: http://updraftplus.com
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.4.14
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  License: GPLv3 or later
10
  Author URI: http://wordshell.net
11
- */
12
 
13
  /*
14
- TODO
15
- //Put in old-WP-version warning, and point them to where they can get help
 
16
  //Add SFTP, Box.Net, SugarSync and Microsoft Skydrive support??
17
  //The restorer has a hard-coded wp-content - fix
18
- //Change DB encryption to not require whole gzip in memory (twice)
19
- //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?)
20
  //?? On 'backup now', open up a Lightbox, count down 5 seconds, then start examining the log file (if it can be found)
21
  //Should make clear in dashboard what is a non-fatal error (i.e. can be retried) - leads to unnecessary bug reports
22
  // 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,17 +23,19 @@ TODO
24
  // Warn the user if their zip-file creation is slooowww...
25
  // Create a "Want Support?" button/console, that leads them through what is needed, and performs some basic tests...
26
  // Resuming partial FTP uploads
 
 
27
  // 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
28
  // Multiple jobs
 
 
29
  // Change FTP to use SSL by default
30
- // Chunked downloading of material for resumption + do resume it
31
- // Disk free-space display
32
  // Multisite add-on should allow restoring of each blog individually
33
  // When looking for files to delete, is the current encryption setting used? Should not be.
34
  // Create single zip, containing even WordPress itself
35
  // When a new backup starts, AJAX-update the 'Last backup' display in the admin page.
36
  // Remove the recurrence of admin notices when settings are saved due to _wp_referer
37
- // Auto-detect what the real execution time is (max_execution_time is just one of the upper limits, there can be others, some insivible directly), and tweak our resumption time accordingly
38
 
39
  Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
40
  // Does not delete old custom directories upon a restore?
@@ -236,12 +237,20 @@ class UpdraftPlus {
236
  $logline .= (method_exists('ZipArchive', 'addFile')) ? "Y" : "N";
237
  }
238
  $this->log($logline);
 
 
239
  }
240
 
241
  # Logs the given line, adding (relative) time stamp and newline
242
  function log($line) {
243
  if ($this->logfile_handle) fwrite($this->logfile_handle, sprintf("%08.03f", round(microtime(true)-$this->opened_log_time, 3))." ".$line."\n");
244
- UpdraftPlus_Options::update_updraft_option("updraft_lastmessage", $line." (".date('M d H:i:s').")");
 
 
 
 
 
 
245
  }
246
 
247
  // 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
@@ -258,7 +267,7 @@ class UpdraftPlus {
258
 
259
  if ($this->current_resumption >= 9 && $this->newresumption_scheduled == false && $percent > ( $this->current_resumption - 9)) {
260
  $resume_interval = $this->jobdata_get('resume_interval');
261
- if (!is_numeric($resume_interval) || $resume_interval<200) { $resume_interval = 200; }
262
  $schedule_for = time()+$resume_interval;
263
  $this->newresumption_scheduled = $schedule_for;
264
  $this->log("This is resumption ".$this->current_resumption.", but meaningful uploading is still taking place; so a new one will be scheduled");
@@ -266,6 +275,12 @@ class UpdraftPlus {
266
  }
267
  }
268
 
 
 
 
 
 
 
269
  function backup_resume($resumption_no, $bnonce) {
270
 
271
  @ignore_user_abort(true);
@@ -280,18 +295,22 @@ class UpdraftPlus {
280
 
281
  $btime = $this->backup_time;
282
 
283
- $this->log("Backup run: resumption=$resumption_no, nonce=$bnonce, begun at=$btime");
 
 
 
 
284
  $this->current_resumption = $resumption_no;
285
 
286
  // Schedule again, to run in 5 minutes again, in case we again fail
287
  // The actual interval can be increased (for future resumptions) by other code, if it detects apparent overlapping
288
  $resume_interval = $this->jobdata_get('resume_interval');
289
- if (!is_numeric($resume_interval) || $resume_interval<200) $resume_interval = 200;
290
 
291
  // A different argument than before is needed otherwise the event is ignored
292
  $next_resumption = $resumption_no+1;
293
  if ($next_resumption < 10) {
294
- $this->log("Scheduling a resumption ($next_resumption) in case this run gets aborted");
295
  $schedule_for = time()+$resume_interval;
296
  wp_schedule_single_event($schedule_for, 'updraft_backup_resume', array($next_resumption, $bnonce));
297
  $this->newresumption_scheduled = $schedule_for;
@@ -301,16 +320,15 @@ class UpdraftPlus {
301
 
302
  // This should be always called; if there were no files in this run, it returns us an empty array
303
  $backup_array = $this->resumable_backup_of_files($resumption_no);
304
- // This save, if there was something, is then immediately picked up again
305
- if (is_array($backup_array)) $this->save_backup_history($backup_array);
306
 
307
- // Returns an array, most recent first, of backup sets
308
- $backup_history = $this->get_backup_history();
309
- if (!isset($backup_history[$btime])) {
310
- $this->log("Could not find a record in the database of a backup with this timestamp");
311
  }
312
 
313
- $our_files=$backup_history[$btime];
 
314
  if (!is_array($our_files)) $our_files = array();
315
 
316
  $undone_files = array();
@@ -330,8 +348,13 @@ class UpdraftPlus {
330
  } else {
331
  $this->log("Database dump: Creation was completed already");
332
  }
 
333
  $db_backup = $this->backup_db($backup_database);
334
- if(is_array($our_files) && is_string($db_backup)) $our_files['db'] = $db_backup;
 
 
 
 
335
  if ($backup_database != 'encrypted') $this->jobdata_set("backup_database", 'finished');
336
  } else {
337
  $this->log("Unrecognised data when trying to ascertain if the database was backed up ($backup_database)");
@@ -345,10 +368,15 @@ class UpdraftPlus {
345
  // Potentially encrypt the database if it is not already
346
  if (isset($our_files['db']) && !preg_match("/\.crypt$/", $our_files['db'])) {
347
  $our_files['db'] = $this->encrypt_file($our_files['db']);
348
- $this->save_backup_history($our_files);
349
  if (preg_match("/\.crypt$/", $our_files['db'])) $this->jobdata_set("backup_database", 'encrypted');
350
  }
351
 
 
 
 
 
 
352
  foreach ($our_files as $key => $file) {
353
 
354
  // Only continue if the stored info was about a dump
@@ -397,15 +425,30 @@ class UpdraftPlus {
397
  $this->boot_backup(false,true);
398
  }
399
 
400
- function jobdata_set($key, $value) {
401
- if (is_array($this->jobdata)) {
 
 
 
 
 
 
 
402
  $this->jobdata[$key] = $value;
403
- } else {
404
- $this->jobdata = array($key => $value);
405
  }
406
- set_transient("updraft_jobdata_".$this->nonce, $this->jobdata, 14400);
 
 
 
 
 
 
 
 
 
407
  }
408
 
 
409
  function jobdata_get($key) {
410
  if (!is_array($this->jobdata)) {
411
  $this->jobdata = get_transient("updraft_jobdata_".$this->nonce);
@@ -455,7 +498,7 @@ class UpdraftPlus {
455
 
456
  # If the files and database schedules are the same, and if this the file one, then we rope in database too.
457
  # On the other hand, if the schedules were the same and this was the database run, then there is nothing to do.
458
- if (UpdraftPlus_Options::get_updraft_option('updraft_interval') == UpdraftPlus_Options::get_updraft_option('updraft_interval_database') || UpdraftPlus_Options::get_updraft_option('updraft_interval_database','xyz') == 'xyz' ) {
459
  $backup_database = ($backup_files == true) ? true : false;
460
  }
461
 
@@ -467,15 +510,23 @@ class UpdraftPlus {
467
  return;
468
  }
469
 
470
- // Save what *should* be done, to make it resumable from this point on
471
- if ($backup_database) $this->jobdata_set("backup_database", 'begun');
472
- if ($backup_files) $this->jobdata_set('backup_files', 'begun');
473
- $this->jobdata_set('service', UpdraftPlus_Options::get_updraft_option('updraft_service'));
 
 
 
 
 
 
474
 
475
- // This can be adapted if we see a need
476
- $this->jobdata_set('resume_interval', 300);
 
477
 
478
- $this->jobdata_set('backup_time', $this->backup_time);
 
479
 
480
  // Everthing is now set up; now go
481
  $this->backup_resume(0, $this->nonce);
@@ -703,6 +754,7 @@ class UpdraftPlus {
703
  unset($backup_to_examine['db']);
704
  }
705
  }
 
706
  if (isset($backup_to_examine['plugins']) || isset($backup_to_examine['themes']) || isset($backup_to_examine['uploads']) || isset($backup_to_examine['others'])) {
707
  $file_backups_found++;
708
  $this->log("$backup_datestamp: this set includes files; fileset count is now $file_backups_found");
@@ -721,8 +773,10 @@ class UpdraftPlus {
721
  unset($backup_to_examine['others']);
722
  }
723
  }
 
724
  // Delete backup set completely if empty, o/w just remove DB
725
- if (count($backup_to_examine) == 0 || (count($backup_to_examine) == 1 && isset($backup_to_examine['nonce']))) {
 
726
  $this->log("$backup_datestamp: this backup set is now empty; will remove from history");
727
  unset($backup_history[$backup_datestamp]);
728
  if (isset($backup_to_examine['nonce'])) {
@@ -759,7 +813,7 @@ class UpdraftPlus {
759
  // Reschedule - remove presently scheduled event
760
  wp_clear_scheduled_hook('updraft_backup_resume', array($this->current_resumption + 1, $this->nonce));
761
  // Add new event
762
- if ($how_far_ahead < 200) $how_far_ahead=200;
763
  $schedule_for = time() + $how_far_ahead;
764
  wp_schedule_single_event($schedule_for, 'updraft_backup_resume', array($this->current_resumption + 1, $this->nonce));
765
  $this->newresumption_scheduled = $schedule_for;
@@ -767,7 +821,7 @@ class UpdraftPlus {
767
 
768
  function increase_resume_and_reschedule($howmuch = 120) {
769
  $resume_interval = $this->jobdata_get('resume_interval');
770
- if (!is_numeric($resume_interval) || $resume_interval<200) { $resume_interval = 200; }
771
  if ($this->newresumption_scheduled != false) $this->reschedule($resume_interval+$howmuch);
772
  $this->jobdata_set('resume_interval', $resume_interval+$howmuch);
773
  $this->log("To decrease the likelihood of overlaps, increasing resumption interval to: ".($resume_interval+$howmuch));
@@ -852,9 +906,13 @@ class UpdraftPlus {
852
  if (UpdraftPlus_Options::get_updraft_option("updraft_include_$youwhat", true)) {
853
  if ($transient_status == 'finished') {
854
  $backup_array[$youwhat] = $backup_file_basename.'-'.$youwhat.'.zip';
 
855
  } else {
856
  $created = $this->create_zip($whichdir, $youwhat, $updraft_dir, $backup_file_basename);
857
- if ($created) $backup_array[$youwhat] = $created;
 
 
 
858
  }
859
  } else {
860
  $this->log("No backup of $youwhat: excluded by user's options");
@@ -866,6 +924,7 @@ class UpdraftPlus {
866
 
867
  if ($transient_status == 'finished') {
868
  $backup_array['others'] = $backup_file_basename.'-others.zip';
 
869
  } else {
870
  $this->log("Beginning backup of other directories found in the content directory");
871
 
@@ -905,7 +964,10 @@ class UpdraftPlus {
905
 
906
  if (count($other_dirlist)>0) {
907
  $created = $this->create_zip($other_dirlist, 'others', $updraft_dir, $backup_file_basename);
908
- if ($created) $backup_array['others'] = $created;
 
 
 
909
  } else {
910
  $this->log("No backup of other directories: there was nothing found to back up");
911
  }
@@ -922,8 +984,9 @@ class UpdraftPlus {
922
  $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
923
  $backup_history = (is_array($backup_history)) ? $backup_history : array();
924
  $backup_array['nonce'] = $this->nonce;
 
925
  $backup_history[$this->backup_time] = $backup_array;
926
- UpdraftPlus_Options::update_updraft_option('updraft_backup_history',$backup_history);
927
  } else {
928
  $this->log('Could not save backup history because we have no backup array. Backup probably failed.');
929
  $this->error('Could not save backup history because we have no backup array. Backup probably failed.');
@@ -931,10 +994,11 @@ class UpdraftPlus {
931
  }
932
 
933
  function get_backup_history() {
934
- //$backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
 
935
  //by doing a raw DB query to get the most up-to-date data from this option we slightly narrow the window for the multiple-cron race condition
936
- global $wpdb;
937
- $backup_history = @unserialize($wpdb->get_var($wpdb->prepare("SELECT option_value from $wpdb->options WHERE option_name='updraft_backup_history'")));
938
  if(is_array($backup_history)) {
939
  krsort($backup_history); //reverse sort so earliest backup is last on the array. Then we can array_pop.
940
  } else {
@@ -1299,7 +1363,7 @@ class UpdraftPlus {
1299
 
1300
  // Acts as a WordPress options filter
1301
  function googledrive_clientid_checkchange($client_id) {
1302
- if (UpdraftPlus_Options::get_updraft_option('updraft_googledrive_token') != '' && UpdraftPlus_Options::get_updraft_option('updraft_googledrive_token') != $client_id) {
1303
  require_once(UPDRAFTPLUS_DIR.'/methods/googledrive.php');
1304
  UpdraftPlus_BackupModule_googledrive::gdrive_auth_revoke(true);
1305
  }
@@ -1358,6 +1422,28 @@ class UpdraftPlus {
1358
 
1359
  if ('lastlog' == $_GET['subaction']) {
1360
  echo htmlspecialchars(UpdraftPlus_Options::get_updraft_option('updraft_lastmessage', '(Nothing yet logged)'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1361
  } elseif ($_POST['subaction'] == 'credentials_test') {
1362
  $method = (preg_match("/^[a-z0-9]+$/", $_POST['method'])) ? $_POST['method'] : "";
1363
 
@@ -1373,20 +1459,118 @@ class UpdraftPlus {
1373
  }
1374
 
1375
  function updraft_download_backup() {
1376
- $type = $_POST['type'];
1377
- $timestamp = (int)$_POST['timestamp'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1378
  $backup_history = $this->get_backup_history();
 
1379
  $file = $backup_history[$timestamp][$type];
 
 
1380
  $fullpath = $this->backups_dir_location().'/'.$file;
1381
- if(!is_readable($fullpath)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1382
  //if the file doesn't exist and they're using one of the cloud options, fetch it down from the cloud.
1383
- $this->download_backup($file);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1384
  }
1385
- if(@is_readable($fullpath) && is_file($fullpath)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1386
  $len = filesize($fullpath);
1387
 
1388
  $filearr = explode('.',$file);
1389
- // //we've only got zip and gz...for now
1390
  $file_ext = array_pop($filearr);
1391
  if($file_ext == 'zip') {
1392
  header('Content-type: application/zip');
@@ -1422,15 +1606,17 @@ class UpdraftPlus {
1422
  } else {
1423
  readfile($fullpath);
1424
  }
1425
- $this->delete_local($file);
1426
- exit; //we exit immediately because otherwise admin-ajax appends an additional zero to the end
1427
  } else {
1428
- echo 'Download failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then remote retrieval may have failed.';
1429
  }
1430
  }
1431
-
1432
- function download_backup($file) {
1433
- $service = UpdraftPlus_Options::get_updraft_option('updraft_service');
 
 
 
1434
 
1435
  $method_include = UPDRAFTPLUS_DIR.'/methods/'.$service.'.php';
1436
  if (file_exists($method_include)) require_once($method_include);
@@ -1440,6 +1626,7 @@ class UpdraftPlus {
1440
  $remote_obj = new $objname;
1441
  $remote_obj->download($file);
1442
  } else {
 
1443
  $this->error("Automatic backup restoration is not available with the method: $service.");
1444
  }
1445
 
@@ -1465,11 +1652,15 @@ class UpdraftPlus {
1465
  echo '<span style="font-weight:bold">Restoration Progress</span><div id="updraft-restore-progress">';
1466
 
1467
  $updraft_dir = $this->backups_dir_location().'/';
 
 
 
1468
  foreach($backup_history[$timestamp] as $type => $file) {
1469
- if ($type == 'nonce') continue;
 
1470
  $fullpath = $updraft_dir.$file;
1471
  if(!is_readable($fullpath) && $type != 'db') {
1472
- $this->download_backup($file);
1473
  }
1474
  # Types: uploads, themes, plugins, others, db
1475
  if(is_readable($fullpath) && $type != 'db') {
@@ -1574,6 +1765,13 @@ class UpdraftPlus {
1574
  return $memory_limit;
1575
  }
1576
 
 
 
 
 
 
 
 
1577
  function memory_check($memory) {
1578
  $memory_limit = $this->memory_check_current();
1579
  return ($memory_limit >= $memory)?true:false;
@@ -1581,7 +1779,7 @@ class UpdraftPlus {
1581
 
1582
  function execution_time_check($time) {
1583
  $setting = ini_get('max_execution_time');
1584
- return ( $setting==0 || $setting >= $time) ? true : false;
1585
  }
1586
 
1587
  function admin_init() {
@@ -1599,6 +1797,12 @@ class UpdraftPlus {
1599
  if (UpdraftPlus_Options::user_can_manage() && UpdraftPlus_Options::get_updraft_option('updraft_service') == "dropbox" && UpdraftPlus_Options::get_updraft_option('updraft_dropboxtk_request_token','') == '') {
1600
  add_action('admin_notices', array($this,'show_admin_warning_dropbox') );
1601
  }
 
 
 
 
 
 
1602
  }
1603
 
1604
  function url_start($urls,$url) {
@@ -1631,7 +1835,7 @@ class UpdraftPlus {
1631
  if (!defined('UPDRAFTPLUS_PREMIUM')) {
1632
  return $this->url_start($urls,'updraftplus.com')."Need even more features and support? Check out UpdraftPlus Premium".$this->url_end($urls,'updraftplus.com');
1633
  } else {
1634
- return "Thanks for being an UpdraftPlus premium user. Keep visiting ".$this->url_start($urls,'www.updraftplus.com')."updraftplus.com".$this->url_end($urls,'www.updraftplus.com')." to see what's going on.";
1635
  }
1636
  break;
1637
  case 6:
@@ -1646,8 +1850,9 @@ class UpdraftPlus {
1646
  }
1647
  }
1648
 
1649
- function settings_formcontents() {
1650
  $updraft_dir = $this->backups_dir_location();
 
1651
  ?>
1652
  <table class="form-table" style="width:850px;">
1653
  <tr>
@@ -1772,18 +1977,50 @@ class UpdraftPlus {
1772
  jQuery.get(ajaxurl, lastlog_sdata, function(response) {
1773
  nexttimer = 1500;
1774
  if (lastlog_lastmessage == response) { nexttimer = 4500; }
1775
- window.setTimeout(function(){updraft_showlastlog()}, nexttimer);
1776
  jQuery('#updraft_lastlogcontainer').html(response);
1777
  lastlog_lastmessage = response;
1778
  });
1779
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1780
  jQuery(document).ready(function() {
1781
  jQuery('#enableexpertmode').click(function() {
1782
  jQuery('.expertmode').fadeIn();
1783
  return false;
1784
  });
1785
  <?php if (!is_writable($updraft_dir)) echo "jQuery('.backupdirrow').show();\n"; ?>
1786
- window.setTimeout(function(){updraft_showlastlog()}, 1200);
1787
  jQuery('.updraftplusmethod').hide();
1788
  <?php
1789
  if ($active_service) echo "jQuery('.${active_service}').show();";
@@ -1858,6 +2095,37 @@ class UpdraftPlus {
1858
  <?php
1859
  }
1860
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1861
  function settings_output() {
1862
 
1863
  /*
@@ -1915,15 +2183,15 @@ class UpdraftPlus {
1915
  }
1916
 
1917
  if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') {
 
1918
  echo '<div class="updated fade" style="max-width: 800px; font-size:140%; line-height: 140%; padding:14px; clear:left;"><strong>Schedule backup:</strong> ';
1919
  if (wp_schedule_single_event(time()+5, 'updraft_backup_all') === false) {
1920
  $this->log("A backup run failed to schedule");
1921
- echo "Failed.";
1922
  } else {
1923
- echo "OK. Now load any page from your site to make sure the schedule can trigger.";
1924
  $this->log("A backup run has been scheduled");
1925
  }
1926
- echo '</div>';
1927
  }
1928
 
1929
  // updraft_file_ids is not deleted
@@ -1954,11 +2222,11 @@ class UpdraftPlus {
1954
  if($deleted_old_dirs) echo '<div style="color:blue">Old directories successfully deleted.</div>';
1955
 
1956
  if(!$this->memory_check(96)) {?>
1957
- <div style="color:orange">Your PHP memory limit is too low. UpdraftPlus 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>
1958
  <?php
1959
  }
1960
- if(!$this->execution_time_check(300)) {?>
1961
- <div style="color:orange">Your PHP max_execution_time is less than 300 seconds. This probably means you're running in safe_mode. Either disable safe_mode or modify your php.ini to set max_execution_time to a higher number. If you do not, there is a chance Updraft will be unable to complete a backup. Present limit is: <?php echo ini_get('max_execution_time'); ?> seconds.</div>
1962
  <?php
1963
  }
1964
 
@@ -1980,6 +2248,12 @@ class UpdraftPlus {
1980
 
1981
  <h2 style="clear:left;">Existing Schedule And Backups</h2>
1982
  <table class="form-table" style="float:left; clear: both; width:545px;">
 
 
 
 
 
 
1983
  <tr>
1984
  <?php
1985
  $updraft_dir = $this->backups_dir_location();
@@ -2008,30 +2282,11 @@ class UpdraftPlus {
2008
  }
2009
  }
2010
  $current_time = get_date_from_gmt(gmdate('Y-m-d H:i:s'), 'D, F j, Y H:i T');
2011
- $updraft_last_backup = UpdraftPlus_Options::get_updraft_option('updraft_last_backup');
2012
- if($updraft_last_backup) {
2013
- if ($updraft_last_backup['success']) {
2014
- // Convert to GMT, then to blog time
2015
- $last_backup = get_date_from_gmt(gmdate('Y-m-d H:i:s', $updraft_last_backup['backup_time']), 'D, F j, Y H:i T');
2016
- } else {
2017
- $last_backup = implode("<br>",$updraft_last_backup['errors']);
2018
- }
2019
 
2020
- $last_backup_color = ($updraft_last_backup['success']) ? 'green' : 'red';
2021
- if (!empty($updraft_last_backup['backup_nonce'])) {
2022
- $potential_log_file = $updraft_dir."/log.".$updraft_last_backup['backup_nonce'].".txt";
2023
- if (is_readable($potential_log_file)) $last_backup .= "<br><a href=\"?page=updraftplus&action=downloadlog&updraftplus_backup_nonce=".$updraft_last_backup['backup_nonce']."\">Download log file</a>";
2024
- }
2025
- } else {
2026
- $last_backup = 'No backup has been completed.';
2027
- $last_backup_color = 'blue';
2028
- }
2029
 
2030
- if(is_writable($updraft_dir)) {
2031
- $backup_disabled = "";
2032
- } else {
2033
- $backup_disabled = 'disabled="disabled"';
2034
- }
2035
  ?>
2036
 
2037
  <th>Time now:</th>
@@ -2046,8 +2301,8 @@ class UpdraftPlus {
2046
  <td style="color:blue"><?php echo $next_scheduled_backup_database?></td>
2047
  </tr>
2048
  <tr>
2049
- <th>Last backup:</th>
2050
- <td style="color:<?php echo $last_backup_color ?>"><?php echo $last_backup?></td>
2051
  </tr>
2052
  </table>
2053
  <div style="float:left; width:200px; padding-top: 40px;">
@@ -2084,103 +2339,96 @@ class UpdraftPlus {
2084
  <br style="clear:both" />
2085
  <table class="form-table">
2086
  <tr>
2087
- <th>Last backup log message:</th>
2088
  <td id="updraft_lastlogcontainer"><?php echo htmlspecialchars(UpdraftPlus_Options::get_updraft_option('updraft_lastmessage', '(Nothing yet logged)')); ?></td>
2089
  </tr>
2090
  <tr>
2091
  <th>Download backups and logs:</th>
2092
- <td><a href="#" title="Click to see available backups" onclick="jQuery('.download-backups').toggle();return false;"><?php echo count($backup_history)?> available</a></td>
2093
  </tr>
2094
  <tr>
2095
  <td></td><td class="download-backups" style="display:none">
2096
- <em>Click on a button to download the corresponding file to your computer. If you are using the <a href="http://opera.com">Opera web browser</a> then you should turn Turbo mode off. <strong>Note</strong> - if you use remote storage (e.g. Amazon, Dropbox, FTP, Google Drive), then pressing a button will make UpdraftPlus try to bring a backup file back from the remote storage to your webserver, and from there to your computer. If the backup file is very big, then likely you will run out of time using this method. In that case you should get the file directly (i.e. visit Amazon S3's or Dropbox's website, etc.).</em>
2097
- <table>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2098
  <?php
2099
- foreach($backup_history as $key=>$value) {
2100
  ?>
2101
- <tr>
2102
- <td><b><?php echo date('Y-m-d G:i',$key)?></b></td>
2103
- <td>
2104
- <?php if (isset($value['db'])) { ?>
2105
- <form action="admin-ajax.php" method="post">
2106
- <input type="hidden" name="action" value="updraft_download_backup" />
2107
- <input type="hidden" name="type" value="db" />
2108
- <input type="hidden" name="timestamp" value="<?php echo $key?>" />
2109
- <input type="submit" value="Database" />
2110
- </form>
2111
- <?php } else { echo "(No database)"; } ?>
2112
- </td>
2113
- <td>
2114
- <?php if (isset($value['plugins'])) { ?>
2115
- <form action="admin-ajax.php" method="post">
2116
- <input type="hidden" name="action" value="updraft_download_backup" />
2117
- <input type="hidden" name="type" value="plugins" />
2118
- <input type="hidden" name="timestamp" value="<?php echo $key?>" />
2119
- <input type="submit" value="Plugins" />
2120
- </form>
2121
- <?php } else { echo "(No plugins)"; } ?>
2122
- </td>
2123
- <td>
2124
- <?php if (isset($value['themes'])) { ?>
2125
- <form action="admin-ajax.php" method="post">
2126
- <input type="hidden" name="action" value="updraft_download_backup" />
2127
- <input type="hidden" name="type" value="themes" />
2128
- <input type="hidden" name="timestamp" value="<?php echo $key?>" />
2129
- <input type="submit" value="Themes" />
2130
- </form>
2131
- <?php } else { echo "(No themes)"; } ?>
2132
- </td>
2133
- <td>
2134
- <?php if (isset($value['uploads'])) { ?>
2135
- <form action="admin-ajax.php" method="post">
2136
- <input type="hidden" name="action" value="updraft_download_backup" />
2137
- <input type="hidden" name="type" value="uploads" />
2138
- <input type="hidden" name="timestamp" value="<?php echo $key?>" />
2139
- <input type="submit" value="Uploads" />
2140
- </form>
2141
- <?php } else { echo "(No uploads)"; } ?>
2142
- </td>
2143
- <td>
2144
- <?php if (isset($value['others'])) { ?>
2145
- <form action="admin-ajax.php" method="post">
2146
- <input type="hidden" name="action" value="updraft_download_backup" />
2147
- <input type="hidden" name="type" value="others" />
2148
- <input type="hidden" name="timestamp" value="<?php echo $key?>" />
2149
- <input type="submit" value="Others" />
2150
- </form>
2151
- <?php } else { echo "(No others)"; } ?>
2152
- </td>
2153
- <td>
2154
- <?php if (isset($value['nonce']) && preg_match("/^[0-9a-f]{12}$/",$value['nonce']) && is_readable($updraft_dir.'/log.'.$value['nonce'].'.txt')) { ?>
2155
- <form action="options-general.php" method="get">
2156
- <input type="hidden" name="action" value="downloadlog" />
2157
- <input type="hidden" name="page" value="updraftplus" />
2158
- <input type="hidden" name="updraftplus_backup_nonce" value="<?php echo $value['nonce']; ?>" />
2159
- <input type="submit" value="Backup Log" />
2160
- </form>
2161
- <?php } else { echo "(No backup log)"; } ?>
2162
- </td>
2163
- </tr>
2164
- <?php }?>
2165
- </table>
2166
  </td>
2167
  </tr>
2168
  </table>
2169
  <?php
2170
- if (!defined('UPDRAFTPLUS_PREMIUM') && is_multisite()) {
2171
  ?>
2172
- <h2>UpdraftPlus Premium</h2>
2173
  <table>
2174
  <tr>
2175
  <td>
2176
- <p style="max-width:800px;">Do you need WordPress Multisite support? Please check out <a href="http://updraftplus.com">UpdraftPlus Premium</a> - and in coming weeks, it will add even more premium features. Why not support UpdraftPlus development?</p>
2177
  </td>
2178
  </tr>
2179
  </table>
2180
  <?php } ?>
2181
  <h2>Configure Backup Contents And Schedule</h2>
2182
  <?php UpdraftPlus_Options::options_form_begin(); ?>
2183
- <?php $this->settings_formcontents(); ?>
2184
  </form>
2185
  <div style="padding-top: 40px; display:none;" class="expertmode">
2186
  <hr>
@@ -2219,7 +2467,7 @@ class UpdraftPlus {
2219
  jQuery('.updraftplusmethod').hide();
2220
  var active_class = jQuery(this).val();
2221
  jQuery('.'+active_class).show();
2222
- })
2223
  })
2224
  jQuery(window).load(function() {
2225
  //this is for hiding the restore progress at the top after it is done
@@ -2233,10 +2481,101 @@ class UpdraftPlus {
2233
  <?php
2234
  }
2235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2236
  function show_admin_warning($message, $class = "updated") {
2237
  echo '<div id="updraftmessage" class="'.$class.' fade">'."<p>$message</p></div>";
2238
  }
2239
 
 
 
 
 
 
 
 
 
2240
  function show_admin_warning_unreadablelog() {
2241
  $this->show_admin_warning('<strong>UpdraftPlus notice:</strong> The log file could not be read.');
2242
  }
4
  Plugin URI: http://updraftplus.com
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.4.27
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  License: GPLv3 or later
10
  Author URI: http://wordshell.net
11
+ */
12
 
13
  /*
14
+ TODO - some are out of date/done, needs pruning
15
+ //When a manual backup is run, use a timer to update the 'Download backups and logs' section, just like 'Last finished backup run'. Beware of over-writing anything that's in there from a resumable downloader.
16
+ //Change DB encryption to not require whole gzip in memory (twice)
17
  //Add SFTP, Box.Net, SugarSync and Microsoft Skydrive support??
18
  //The restorer has a hard-coded wp-content - fix
 
 
19
  //?? On 'backup now', open up a Lightbox, count down 5 seconds, then start examining the log file (if it can be found)
20
  //Should make clear in dashboard what is a non-fatal error (i.e. can be retried) - leads to unnecessary bug reports
21
  // 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.
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
+ // Make disk space check more intelligent (currently hard-coded at 35Mb)
27
+ // Specific folders on DropBox
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
+ // Multisite - a separate 'blogs' zip
31
+ // Allow connecting to remote storage, scanning + populating backup history from it
32
  // Change FTP to use SSL by default
33
+ // GoogleDrive in-dashboard download resumption loads the whole archive into memory - should instead either chunk or directly stream fo the file handle
 
34
  // Multisite add-on should allow restoring of each blog individually
35
  // When looking for files to delete, is the current encryption setting used? Should not be.
36
  // Create single zip, containing even WordPress itself
37
  // When a new backup starts, AJAX-update the 'Last backup' display in the admin page.
38
  // Remove the recurrence of admin notices when settings are saved due to _wp_referer
 
39
 
40
  Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
41
  // Does not delete old custom directories upon a restore?
237
  $logline .= (method_exists('ZipArchive', 'addFile')) ? "Y" : "N";
238
  }
239
  $this->log($logline);
240
+ $disk_free_space = @disk_free_space($updraft_dir);
241
+ $this->log("Free space on disk containing Updraft's temporary directory: ".round($disk_free_space/1048576,1)." Mb");
242
  }
243
 
244
  # Logs the given line, adding (relative) time stamp and newline
245
  function log($line) {
246
  if ($this->logfile_handle) fwrite($this->logfile_handle, sprintf("%08.03f", round(microtime(true)-$this->opened_log_time, 3))." ".$line."\n");
247
+ if ('download' == $this->jobdata_get('job_type')) {
248
+ // Download messages are keyed on the job (since they could be running several), and transient
249
+ // The values of the POST array were checked before
250
+ set_transient('ud_dlmess_'.$_POST['timestamp'].'_'.$_POST['type'], $line." (".date('M d H:i:s').")", 3600);
251
+ } else {
252
+ UpdraftPlus_Options::update_updraft_option("updraft_lastmessage", $line." (".date('M d H:i:s').")");
253
+ }
254
  }
255
 
256
  // 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
267
 
268
  if ($this->current_resumption >= 9 && $this->newresumption_scheduled == false && $percent > ( $this->current_resumption - 9)) {
269
  $resume_interval = $this->jobdata_get('resume_interval');
270
+ if (!is_numeric($resume_interval) || $resume_interval<$this->minimum_resume_interval()) { $resume_interval = $this->minimum_resume_interval(); }
271
  $schedule_for = time()+$resume_interval;
272
  $this->newresumption_scheduled = $schedule_for;
273
  $this->log("This is resumption ".$this->current_resumption.", but meaningful uploading is still taking place; so a new one will be scheduled");
275
  }
276
  }
277
 
278
+ function minimum_resume_interval() {
279
+ $inter = ini_get('max_execution_time');
280
+ if (!$inter || $inter>300) $inter = 300;
281
+ return $inter;
282
+ }
283
+
284
  function backup_resume($resumption_no, $bnonce) {
285
 
286
  @ignore_user_abort(true);
295
 
296
  $btime = $this->backup_time;
297
 
298
+ $job_type = $this->jobdata_get('job_type');
299
+
300
+ $updraft_dir = $this->backups_dir_location();
301
+
302
+ $this->log("Backup run: resumption=$resumption_no, nonce=$bnonce, begun at=$btime, job type: $job_type");
303
  $this->current_resumption = $resumption_no;
304
 
305
  // Schedule again, to run in 5 minutes again, in case we again fail
306
  // The actual interval can be increased (for future resumptions) by other code, if it detects apparent overlapping
307
  $resume_interval = $this->jobdata_get('resume_interval');
308
+ if (!is_numeric($resume_interval) || $resume_interval<$this->minimum_resume_interval()) $resume_interval = $this->minimum_resume_interval();
309
 
310
  // A different argument than before is needed otherwise the event is ignored
311
  $next_resumption = $resumption_no+1;
312
  if ($next_resumption < 10) {
313
+ $this->log("Scheduling a resumption ($next_resumption) after $resume_interval seconds in case this run gets aborted");
314
  $schedule_for = time()+$resume_interval;
315
  wp_schedule_single_event($schedule_for, 'updraft_backup_resume', array($next_resumption, $bnonce));
316
  $this->newresumption_scheduled = $schedule_for;
320
 
321
  // This should be always called; if there were no files in this run, it returns us an empty array
322
  $backup_array = $this->resumable_backup_of_files($resumption_no);
 
 
323
 
324
+ // This save, if there was something, is then immediately picked up again
325
+ if (is_array($backup_array)) {
326
+ $this->log("Saving backup status to database (elements: ".count($backup_array).")");
327
+ $this->save_backup_history($backup_array);
328
  }
329
 
330
+ // Switch of variable name is purely vestigial
331
+ $our_files = $backup_array;
332
  if (!is_array($our_files)) $our_files = array();
333
 
334
  $undone_files = array();
348
  } else {
349
  $this->log("Database dump: Creation was completed already");
350
  }
351
+
352
  $db_backup = $this->backup_db($backup_database);
353
+
354
+ if(is_array($our_files) && is_string($db_backup)) {
355
+ $our_files['db'] = $db_backup;
356
+ }
357
+
358
  if ($backup_database != 'encrypted') $this->jobdata_set("backup_database", 'finished');
359
  } else {
360
  $this->log("Unrecognised data when trying to ascertain if the database was backed up ($backup_database)");
368
  // Potentially encrypt the database if it is not already
369
  if (isset($our_files['db']) && !preg_match("/\.crypt$/", $our_files['db'])) {
370
  $our_files['db'] = $this->encrypt_file($our_files['db']);
371
+ // No need to save backup history now, as it will happen in a few lines time
372
  if (preg_match("/\.crypt$/", $our_files['db'])) $this->jobdata_set("backup_database", 'encrypted');
373
  }
374
 
375
+ if (isset($our_files['db']) && file_exists($updraft_dir.'/'.$our_files['db'])) {
376
+ $our_files['db-size'] = filesize($updraft_dir.'/'.$our_files['db']);
377
+ $this->save_backup_history($our_files);
378
+ }
379
+
380
  foreach ($our_files as $key => $file) {
381
 
382
  // Only continue if the stored info was about a dump
425
  $this->boot_backup(false,true);
426
  }
427
 
428
+ // This works with any amount of settings, but we provide also a jobdata_set for efficiency as normally there's only one setting
429
+ function jobdata_set_multi() {
430
+ if (!is_array($this->jobdata)) $this->jobdata = array();
431
+
432
+ $args = func_num_args();
433
+
434
+ for ($i=1; $i<=$args/2; $i++) {
435
+ $key = func_get_arg($i*2-2);
436
+ $value = func_get_arg($i*2-1);
437
  $this->jobdata[$key] = $value;
 
 
438
  }
439
+ if ($this->nonce) set_transient("updraft_jobdata_".$this->nonce, $this->jobdata, UPDRAFT_TRANSTIME);
440
+ }
441
+
442
+ function jobdata_set($key, $value) {
443
+ if (is_array($this->jobdata)) {
444
+ $this->jobdata[$key] = $value;
445
+ } else {
446
+ $this->jobdata = array($key => $value);
447
+ }
448
+ set_transient("updraft_jobdata_".$this->nonce, $this->jobdata, 14400);
449
  }
450
 
451
+
452
  function jobdata_get($key) {
453
  if (!is_array($this->jobdata)) {
454
  $this->jobdata = get_transient("updraft_jobdata_".$this->nonce);
498
 
499
  # If the files and database schedules are the same, and if this the file one, then we rope in database too.
500
  # On the other hand, if the schedules were the same and this was the database run, then there is nothing to do.
501
+ if (UpdraftPlus_Options::get_updraft_option('updraft_interval') == UpdraftPlus_Options::get_updraft_option('updraft_interval_database') || UpdraftPlus_Options::get_updraft_option('updraft_interval_database', 'xyz') == 'xyz' ) {
502
  $backup_database = ($backup_files == true) ? true : false;
503
  }
504
 
510
  return;
511
  }
512
 
513
+ $resume_interval = $this->minimum_resume_interval();
514
+ $max_execution_time = ini_get('max_execution_time');
515
+ if ($max_execution_time >0 && $max_execution_time<300 && $resume_interval< $max_execution_time + 30) $resume_interval = $max_execution_time + 30;
516
+
517
+ $initial_jobdata = array(
518
+ 'resume_interval', $resume_interval,
519
+ 'job_type', 'backup',
520
+ 'backup_time', $this->backup_time,
521
+ 'service', UpdraftPlus_Options::get_updraft_option('updraft_service')
522
+ );
523
 
524
+ // Save what *should* be done, to make it resumable from this point on
525
+ if ($backup_database) array_push($initial_jobdata, 'backup_database', 'begun');
526
+ if ($backup_files) array_push($initial_jobdata, 'backup_files', 'begun');
527
 
528
+ // Use of jobdata_set_multi saves around 200ms
529
+ call_user_func_array(array($this, 'jobdata_set_multi'), $initial_jobdata);
530
 
531
  // Everthing is now set up; now go
532
  $this->backup_resume(0, $this->nonce);
754
  unset($backup_to_examine['db']);
755
  }
756
  }
757
+
758
  if (isset($backup_to_examine['plugins']) || isset($backup_to_examine['themes']) || isset($backup_to_examine['uploads']) || isset($backup_to_examine['others'])) {
759
  $file_backups_found++;
760
  $this->log("$backup_datestamp: this set includes files; fileset count is now $file_backups_found");
773
  unset($backup_to_examine['others']);
774
  }
775
  }
776
+
777
  // Delete backup set completely if empty, o/w just remove DB
778
+ // We search on the four keys which represent data, allowing other keys to be used to track other things
779
+ if (!isset($backup_to_examine['plugins']) && !isset($backup_to_examine['themes']) && !isset($backup_to_examine['others']) && !isset($backup_to_examine['uploads']) && !isset($backup_to_examine['db']) ) {
780
  $this->log("$backup_datestamp: this backup set is now empty; will remove from history");
781
  unset($backup_history[$backup_datestamp]);
782
  if (isset($backup_to_examine['nonce'])) {
813
  // Reschedule - remove presently scheduled event
814
  wp_clear_scheduled_hook('updraft_backup_resume', array($this->current_resumption + 1, $this->nonce));
815
  // Add new event
816
+ if ($how_far_ahead < $this->minimum_resume_interval()) $how_far_ahead=$this->minimum_resume_interval();
817
  $schedule_for = time() + $how_far_ahead;
818
  wp_schedule_single_event($schedule_for, 'updraft_backup_resume', array($this->current_resumption + 1, $this->nonce));
819
  $this->newresumption_scheduled = $schedule_for;
821
 
822
  function increase_resume_and_reschedule($howmuch = 120) {
823
  $resume_interval = $this->jobdata_get('resume_interval');
824
+ if (!is_numeric($resume_interval) || $resume_interval<$this->minimum_resume_interval()) { $resume_interval = $this->minimum_resume_interval(); }
825
  if ($this->newresumption_scheduled != false) $this->reschedule($resume_interval+$howmuch);
826
  $this->jobdata_set('resume_interval', $resume_interval+$howmuch);
827
  $this->log("To decrease the likelihood of overlaps, increasing resumption interval to: ".($resume_interval+$howmuch));
906
  if (UpdraftPlus_Options::get_updraft_option("updraft_include_$youwhat", true)) {
907
  if ($transient_status == 'finished') {
908
  $backup_array[$youwhat] = $backup_file_basename.'-'.$youwhat.'.zip';
909
+ if (file_exists($updraft_dir.'/'.$backup_file_basename.'-'.$youwhat.'.zip')) $backup_array[$youwhat.'-size'] = filesize($updraft_dir.'/'.$backup_file_basename.'-'.$youwhat.'.zip');
910
  } else {
911
  $created = $this->create_zip($whichdir, $youwhat, $updraft_dir, $backup_file_basename);
912
+ if ($created) {
913
+ $backup_array[$youwhat] = $created;
914
+ $backup_array[$youwhat.'-size'] = filesize($updraft_dir.'/'.$created);
915
+ }
916
  }
917
  } else {
918
  $this->log("No backup of $youwhat: excluded by user's options");
924
 
925
  if ($transient_status == 'finished') {
926
  $backup_array['others'] = $backup_file_basename.'-others.zip';
927
+ if (file_exists($updraft_dir.'/'.$backup_file_basename.'-others.zip')) $backup_array['others-size'] = filesize($updraft_dir.'/'.$backup_file_basename.'-others.zip');
928
  } else {
929
  $this->log("Beginning backup of other directories found in the content directory");
930
 
964
 
965
  if (count($other_dirlist)>0) {
966
  $created = $this->create_zip($other_dirlist, 'others', $updraft_dir, $backup_file_basename);
967
+ if ($created) {
968
+ $backup_array['others'] = $created;
969
+ $backup_array['others-size'] = filesize($updraft_dir.'/'.$created);
970
+ }
971
  } else {
972
  $this->log("No backup of other directories: there was nothing found to back up");
973
  }
984
  $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
985
  $backup_history = (is_array($backup_history)) ? $backup_history : array();
986
  $backup_array['nonce'] = $this->nonce;
987
+ $backup_array['service'] = $this->jobdata_get('service');
988
  $backup_history[$this->backup_time] = $backup_array;
989
+ UpdraftPlus_Options::update_updraft_option('updraft_backup_history', $backup_history);
990
  } else {
991
  $this->log('Could not save backup history because we have no backup array. Backup probably failed.');
992
  $this->error('Could not save backup history because we have no backup array. Backup probably failed.');
994
  }
995
 
996
  function get_backup_history() {
997
+ $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
998
+ // In fact, it looks like the line below actually *introduces* a race condition
999
  //by doing a raw DB query to get the most up-to-date data from this option we slightly narrow the window for the multiple-cron race condition
1000
+ // global $wpdb;
1001
+ // $backup_history = @unserialize($wpdb->get_var($wpdb->prepare("SELECT option_value from $wpdb->options WHERE option_name='updraft_backup_history'")));
1002
  if(is_array($backup_history)) {
1003
  krsort($backup_history); //reverse sort so earliest backup is last on the array. Then we can array_pop.
1004
  } else {
1363
 
1364
  // Acts as a WordPress options filter
1365
  function googledrive_clientid_checkchange($client_id) {
1366
+ if (UpdraftPlus_Options::get_updraft_option('updraft_googledrive_token') != '' && UpdraftPlus_Options::get_updraft_option('updraft_googledrive_clientid') != $client_id) {
1367
  require_once(UPDRAFTPLUS_DIR.'/methods/googledrive.php');
1368
  UpdraftPlus_BackupModule_googledrive::gdrive_auth_revoke(true);
1369
  }
1422
 
1423
  if ('lastlog' == $_GET['subaction']) {
1424
  echo htmlspecialchars(UpdraftPlus_Options::get_updraft_option('updraft_lastmessage', '(Nothing yet logged)'));
1425
+ } elseif ('lastbackup' == $_GET['subaction']) {
1426
+ echo $this->last_backup_html();
1427
+ } elseif ('historystatus' == $_GET['subaction']) {
1428
+ echo $this->existing_backup_table();
1429
+ } elseif ('downloadstatus' == $_GET['subaction'] && isset($_GET['timestamp']) && isset($_GET['type'])) {
1430
+
1431
+ echo get_transient('ud_dlmess_'.$_GET['timestamp'].'_'.$_GET['type']).'<br>';
1432
+
1433
+ if ($file = get_transient('ud_dlfile_'.$_GET['timestamp'].'_'.$_GET['type'])) {
1434
+ if ('failed' == $file) {
1435
+ echo "Download failed";
1436
+ } elseif (preg_match('/^downloaded:(.*)$/', $file, $matches) && file_exists($matches[1])) {
1437
+ $size = round(filesize($matches[1])/1024, 1);
1438
+ echo "File ready: $size Kb: You should: <button type=\"button\" onclick=\"updraftplus_downloadstage2('".$_GET['timestamp']."', '".$_GET['type']."')\">Download to your computer</button> and then, if you wish, <button id=\"uddownloaddelete_".$_GET['timestamp']."_".$_GET['type']."\" type=\"button\" onclick=\"updraftplus_deletefromserver('".$_GET['timestamp']."', '".$_GET['type']."')\">Delete from your web server</button>";
1439
+ } elseif (preg_match('/^downloading:(.*)$/', $file, $matches) && file_exists($matches[1])) {
1440
+ $size = round(filesize($matches[1])/1024, 1);
1441
+ echo "File downloading: ".basename($matches[1]).": $size Kb";
1442
+ } else {
1443
+ echo "No local copy present.";
1444
+ }
1445
+ }
1446
+
1447
  } elseif ($_POST['subaction'] == 'credentials_test') {
1448
  $method = (preg_match("/^[a-z0-9]+$/", $_POST['method'])) ? $_POST['method'] : "";
1449
 
1459
  }
1460
 
1461
  function updraft_download_backup() {
1462
+
1463
+ if (!isset($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'], 'updraftplus_download')) die;
1464
+
1465
+ if (!isset($_REQUEST['timestamp']) || !is_numeric($_REQUEST['timestamp']) || !isset($_REQUEST['type']) || ('plugins' != $_REQUEST['type'] && 'themes' != $_REQUEST['type'] && 'uploads' != $_REQUEST['type'] && 'others' != $_REQUEST['type'] && 'db' != $_REQUEST['type'])) exit;
1466
+
1467
+ // Get the information on what is wanted
1468
+ $type = $_REQUEST['type'];
1469
+ $timestamp = $_REQUEST['timestamp'];
1470
+
1471
+ // You need a nonce before you can set job data. And we certainly don't yet have one.
1472
+ $this->backup_time_nonce();
1473
+
1474
+ $debug_mode = UpdraftPlus_Options::get_updraft_option('updraft_debug_mode');
1475
+
1476
+ // Set the job type before logging, as there can be different logging destinations
1477
+ $this->jobdata_set('job_type', 'download');
1478
+
1479
+ // Retrieve the information from our backup history
1480
  $backup_history = $this->get_backup_history();
1481
+ // Base name
1482
  $file = $backup_history[$timestamp][$type];
1483
+
1484
+ // Where it should end up being downloaded to
1485
  $fullpath = $this->backups_dir_location().'/'.$file;
1486
+
1487
+ if (isset($_GET['stage']) && '2' == $_GET['stage']) {
1488
+ $this->spool_file($timestamp, $type, $fullpath);
1489
+ die;
1490
+ }
1491
+
1492
+ if (isset($_POST['stage']) && 'delete' == $_POST['stage']) {
1493
+ @unlink($fullpath);
1494
+ echo 'deleted';
1495
+ $this->log('The file has been deleted');
1496
+ die;
1497
+ }
1498
+
1499
+ // TODO: FIXME: Failed downloads may leave log files forever (though they are small)
1500
+ // Not that log() assumes that the data is in _POST, not _GET
1501
+ if ($debug_mode) $this->logfile_open($this->nonce);
1502
+
1503
+ $this->log("Requested to obtain file: timestamp=$timestamp, type=$type");
1504
+
1505
+ // The AJAX responder that updates on progress wants to see this
1506
+ set_transient('ud_dlfile_'.$timestamp.'_'.$type, 'downloading:'.$fullpath, 3600);
1507
+
1508
+ $service = (isset($backup_history[$timestamp]['service'])) ? $backup_history[$timestamp]['service'] : false;
1509
+ $this->jobdata_set('service', $service);
1510
+
1511
+ // Fetch it from the cloud, if we have not already got it
1512
+
1513
+ $needs_downloading = false;
1514
+ $known_size = isset($backup_history[$timestamp][$type.'-size']) ? $backup_history[$timestamp][$type.'-size'] : false;
1515
+
1516
+ if(!file_exists($fullpath)) {
1517
  //if the file doesn't exist and they're using one of the cloud options, fetch it down from the cloud.
1518
+ $needs_downloading = true;
1519
+ $this->log('File does not yet exist locally - needs downloading');
1520
+ } elseif ($known_size>0 && filesize($fullpath) < $known_size) {
1521
+ $this->log('The file was found locally but did not match the size in the backup history - will resume downloading');
1522
+ $needs_downloading = true;
1523
+ } elseif ($known_size>0) {
1524
+ $this->log('The file was found locally and matched the recorded size from the backup history ('.round($known_size/1024,1).' Kb)');
1525
+ } else {
1526
+ $this->log('No file size was found recorded in the backup history. We will assume the local one is complete.');
1527
+ }
1528
+
1529
+ if ($needs_downloading) {
1530
+ // Close browser connection so that it can resume AJAX polling
1531
+ header('Connection: close');
1532
+ header('Content-Length: 0');
1533
+ header('Content-Encoding: none');
1534
+ session_write_close();
1535
+ echo "\r\n\r\n";
1536
+ $this->download_file($file, $service, true);
1537
+ if (is_readable($fullpath)) {
1538
+ $this->log('Remote fetch was successful (file size: '.round(filesize($fullpath)/1024,1).' Kb)');
1539
+ } else {
1540
+ $this->log('Remote fetch failed');
1541
+ }
1542
  }
1543
+
1544
+ // Now, spool the thing to the browser
1545
+ if(is_file($fullpath) && is_readable($fullpath)) {
1546
+
1547
+ // That message is then picked up by the AJAX listener
1548
+ set_transient('ud_dlfile_'.$timestamp.'_'.$type, 'downloaded:'.$fullpath, 3600);
1549
+
1550
+ } else {
1551
+
1552
+ set_transient('ud_dlfile_'.$timestamp.'_'.$type, 'failed', 3600);
1553
+
1554
+ echo 'Remote fetch failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then remote retrieval may have failed.';
1555
+ }
1556
+
1557
+ @fclose($this->logfile_handle);
1558
+ if (!$debug_mode) @unlink($this->logfile_name);
1559
+
1560
+ exit;
1561
+
1562
+ }
1563
+
1564
+ function spool_file($timestamp, $type, $fullpath) {
1565
+
1566
+ if (file_exists($fullpath)) {
1567
+
1568
+ $file = basename($fullpath);
1569
+
1570
  $len = filesize($fullpath);
1571
 
1572
  $filearr = explode('.',$file);
1573
+ // //we've only got zip and gz...for now
1574
  $file_ext = array_pop($filearr);
1575
  if($file_ext == 'zip') {
1576
  header('Content-type: application/zip');
1606
  } else {
1607
  readfile($fullpath);
1608
  }
1609
+ // $this->delete_local($file);
 
1610
  } else {
1611
+ echo "File not found";
1612
  }
1613
  }
1614
+
1615
+ function download_file($file, $service=false, $detach_from_browser) {
1616
+
1617
+ if (!$service) $service = UpdraftPlus_Options::get_updraft_option('updraft_service');
1618
+
1619
+ $this->log("Requested file from remote service: service=$service, file=$file");
1620
 
1621
  $method_include = UPDRAFTPLUS_DIR.'/methods/'.$service.'.php';
1622
  if (file_exists($method_include)) require_once($method_include);
1626
  $remote_obj = new $objname;
1627
  $remote_obj->download($file);
1628
  } else {
1629
+ $this->log("Automatic backup restoration is not available with the method: $service.");
1630
  $this->error("Automatic backup restoration is not available with the method: $service.");
1631
  }
1632
 
1652
  echo '<span style="font-weight:bold">Restoration Progress</span><div id="updraft-restore-progress">';
1653
 
1654
  $updraft_dir = $this->backups_dir_location().'/';
1655
+
1656
+ $service = (isset($backup_history[$timestamp]['service'])) ? $backup_history[$timestamp]['service'] : false;
1657
+
1658
  foreach($backup_history[$timestamp] as $type => $file) {
1659
+ // All restorable entities must be given explicitly, as we can store other arbitrary data in the history array
1660
+ if ('themes' != $type && 'plugins' != $type && 'uploads' != $type && 'others' != $type && 'db' != $type) continue;
1661
  $fullpath = $updraft_dir.$file;
1662
  if(!is_readable($fullpath) && $type != 'db') {
1663
+ $this->download_file($file, $service);
1664
  }
1665
  # Types: uploads, themes, plugins, others, db
1666
  if(is_readable($fullpath) && $type != 'db') {
1765
  return $memory_limit;
1766
  }
1767
 
1768
+ function disk_space_check($space) {
1769
+ $updraft_dir = $this->backups_dir_location();
1770
+ $disk_free_space = @disk_free_space($updraft_dir);
1771
+ if ($disk_free_space == false) return -1;
1772
+ return ($disk_free_space > $space) ? true : false;
1773
+ }
1774
+
1775
  function memory_check($memory) {
1776
  $memory_limit = $this->memory_check_current();
1777
  return ($memory_limit >= $memory)?true:false;
1779
 
1780
  function execution_time_check($time) {
1781
  $setting = ini_get('max_execution_time');
1782
+ return ( $setting==0 || $setting >= $time) ? true : false;
1783
  }
1784
 
1785
  function admin_init() {
1797
  if (UpdraftPlus_Options::user_can_manage() && UpdraftPlus_Options::get_updraft_option('updraft_service') == "dropbox" && UpdraftPlus_Options::get_updraft_option('updraft_dropboxtk_request_token','') == '') {
1798
  add_action('admin_notices', array($this,'show_admin_warning_dropbox') );
1799
  }
1800
+
1801
+ if (UpdraftPlus_Options::user_can_manage() && $this->disk_space_check(1024*1024*35) === false) add_action('admin_notices', array($this, 'show_admin_warning_diskspace'));
1802
+
1803
+ global $wp_version, $pagenow;
1804
+ if ($pagenow == 'options-general.php' && version_compare($wp_version, '3.2', '<')) add_action('admin_notices', array($this, 'show_admin_warning_wordpressversion'));
1805
+
1806
  }
1807
 
1808
  function url_start($urls,$url) {
1835
  if (!defined('UPDRAFTPLUS_PREMIUM')) {
1836
  return $this->url_start($urls,'updraftplus.com')."Need even more features and support? Check out UpdraftPlus Premium".$this->url_end($urls,'updraftplus.com');
1837
  } else {
1838
+ return "Thanks for being an UpdraftPlus premium user. Keep visiting ".$this->url_start($urls,'updraftplus.com')."updraftplus.com".$this->url_end($urls,'updraftplus.com')." to see what's going on.";
1839
  }
1840
  break;
1841
  case 6:
1850
  }
1851
  }
1852
 
1853
+ function settings_formcontents($last_backup_html) {
1854
  $updraft_dir = $this->backups_dir_location();
1855
+
1856
  ?>
1857
  <table class="form-table" style="width:850px;">
1858
  <tr>
1977
  jQuery.get(ajaxurl, lastlog_sdata, function(response) {
1978
  nexttimer = 1500;
1979
  if (lastlog_lastmessage == response) { nexttimer = 4500; }
1980
+ setTimeout(function(){updraft_showlastlog()}, nexttimer);
1981
  jQuery('#updraft_lastlogcontainer').html(response);
1982
  lastlog_lastmessage = response;
1983
  });
1984
  }
1985
+ var lastbackup_sdata = {
1986
+ action: 'updraft_ajax',
1987
+ subaction: 'lastbackup',
1988
+ nonce: '<?php echo wp_create_nonce('updraftplus-credentialtest-nonce'); ?>'
1989
+ };
1990
+ var lastbackup_laststatus = '<?php echo $last_backup_html?>'
1991
+ function updraft_showlastbackup(){
1992
+ jQuery.get(ajaxurl, lastbackup_sdata, function(response) {
1993
+ if (lastbackup_laststatus == response) {
1994
+ setTimeout(function(){updraft_showlastbackup()}, 7000);
1995
+ } else {
1996
+ jQuery('#updraft_last_backup').html(response);
1997
+ }
1998
+ lastbackup_laststatus = response;
1999
+ });
2000
+ }
2001
+ var updraft_historytimer = 0;
2002
+ function updraft_historytimertoggle() {
2003
+ if (updraft_historytimer) {
2004
+ clearTimeout(updraft_historytimer);
2005
+ updraft_historytimer = 0;
2006
+ } else {
2007
+ updraft_updatehistory();
2008
+ updraft_historytimer = setInterval(function(){updraft_updatehistory()}, 30000);
2009
+ }
2010
+ }
2011
+ function updraft_updatehistory() {
2012
+ jQuery.get(ajaxurl, { action: 'updraft_ajax', subaction: 'historystatus', nonce: '<?php echo wp_create_nonce('updraftplus-credentialtest-nonce'); ?>' }, function(response) {
2013
+ jQuery('#updraft_existing_backups').html(response);
2014
+ });
2015
+ }
2016
+
2017
  jQuery(document).ready(function() {
2018
  jQuery('#enableexpertmode').click(function() {
2019
  jQuery('.expertmode').fadeIn();
2020
  return false;
2021
  });
2022
  <?php if (!is_writable($updraft_dir)) echo "jQuery('.backupdirrow').show();\n"; ?>
2023
+ setTimeout(function(){updraft_showlastlog();}, 1200);
2024
  jQuery('.updraftplusmethod').hide();
2025
  <?php
2026
  if ($active_service) echo "jQuery('.${active_service}').show();";
2095
  <?php
2096
  }
2097
 
2098
+ function last_backup_html() {
2099
+
2100
+ $updraft_last_backup = UpdraftPlus_Options::get_updraft_option('updraft_last_backup');
2101
+
2102
+ $updraft_dir = $this->backups_dir_location();
2103
+
2104
+ if($updraft_last_backup) {
2105
+
2106
+ if ($updraft_last_backup['success']) {
2107
+ // Convert to GMT, then to blog time
2108
+ $last_backup_text = get_date_from_gmt(gmdate('Y-m-d H:i:s', $updraft_last_backup['backup_time']), 'D, F j, Y H:i T');
2109
+ } else {
2110
+ $last_backup_text = implode("<br>",$updraft_last_backup['errors']);
2111
+ }
2112
+
2113
+ if (!empty($updraft_last_backup['backup_nonce'])) {
2114
+ $potential_log_file = $updraft_dir."/log.".$updraft_last_backup['backup_nonce'].".txt";
2115
+ if (is_readable($potential_log_file)) $last_backup_text .= "<br><a href=\"?page=updraftplus&action=downloadlog&updraftplus_backup_nonce=".$updraft_last_backup['backup_nonce']."\">Download log file</a>";
2116
+ }
2117
+
2118
+ $last_backup_color = ($updraft_last_backup['success']) ? 'green' : 'red';
2119
+
2120
+ } else {
2121
+ $last_backup_text = 'No backup has been completed.';
2122
+ $last_backup_color = 'blue';
2123
+ }
2124
+
2125
+ return "<span style=\"color:${last_backup_color}\">${last_backup_text}</span>";
2126
+
2127
+ }
2128
+
2129
  function settings_output() {
2130
 
2131
  /*
2183
  }
2184
 
2185
  if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') {
2186
+ // For unknown reasons, the <script> runs twice if put inside the <div>
2187
  echo '<div class="updated fade" style="max-width: 800px; font-size:140%; line-height: 140%; padding:14px; clear:left;"><strong>Schedule backup:</strong> ';
2188
  if (wp_schedule_single_event(time()+5, 'updraft_backup_all') === false) {
2189
  $this->log("A backup run failed to schedule");
2190
+ echo "Failed.</div>";
2191
  } else {
2192
+ echo "OK. Now load any page from your site to make sure the schedule can trigger.</div><script>setTimeout(function(){updraft_showlastbackup();}, 7000);</script>";
2193
  $this->log("A backup run has been scheduled");
2194
  }
 
2195
  }
2196
 
2197
  // updraft_file_ids is not deleted
2222
  if($deleted_old_dirs) echo '<div style="color:blue">Old directories successfully deleted.</div>';
2223
 
2224
  if(!$this->memory_check(96)) {?>
2225
+ <div style="color:orange">Your PHP memory limit is quite low. UpdraftPlus 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>
2226
  <?php
2227
  }
2228
+ if(!$this->execution_time_check(60)) {?>
2229
+ <div style="color:orange">Your PHP max_execution_time is less than 60 seconds. This possibly means you're running in safe_mode. Either disable safe_mode or modify your php.ini to set max_execution_time to a higher number. If you do not, then longer will be needed to complete a backup. Present limit is: <?php echo ini_get('max_execution_time'); ?> seconds.</div>
2230
  <?php
2231
  }
2232
 
2248
 
2249
  <h2 style="clear:left;">Existing Schedule And Backups</h2>
2250
  <table class="form-table" style="float:left; clear: both; width:545px;">
2251
+ <noscript>
2252
+ <tr>
2253
+ <th>JavaScript warning:</th>
2254
+ <td style="color:red">This admin interface uses JavaScript heavily. You either need to activate it within your browser, or to use a JavaScript-capable browser.</td>
2255
+ </tr>
2256
+ </noscript>
2257
  <tr>
2258
  <?php
2259
  $updraft_dir = $this->backups_dir_location();
2282
  }
2283
  }
2284
  $current_time = get_date_from_gmt(gmdate('Y-m-d H:i:s'), 'D, F j, Y H:i T');
 
 
 
 
 
 
 
 
2285
 
2286
+ $backup_disabled = (is_writable($updraft_dir)) ? '' : 'disabled="disabled"';
2287
+
2288
+ $last_backup_html = $this->last_backup_html();
 
 
 
 
 
 
2289
 
 
 
 
 
 
2290
  ?>
2291
 
2292
  <th>Time now:</th>
2301
  <td style="color:blue"><?php echo $next_scheduled_backup_database?></td>
2302
  </tr>
2303
  <tr>
2304
+ <th>Last finished backup run:</th>
2305
+ <td id="updraft_last_backup"><?php echo $last_backup_html ?></td>
2306
  </tr>
2307
  </table>
2308
  <div style="float:left; width:200px; padding-top: 40px;">
2339
  <br style="clear:both" />
2340
  <table class="form-table">
2341
  <tr>
2342
+ <th>Last log message:</th>
2343
  <td id="updraft_lastlogcontainer"><?php echo htmlspecialchars(UpdraftPlus_Options::get_updraft_option('updraft_lastmessage', '(Nothing yet logged)')); ?></td>
2344
  </tr>
2345
  <tr>
2346
  <th>Download backups and logs:</th>
2347
+ <td><a href="#" title="Click to see available backups" onclick="jQuery('.download-backups').toggle(); updraft_historytimertoggle();"><?php echo count($backup_history)?> available</a></td>
2348
  </tr>
2349
  <tr>
2350
  <td></td><td class="download-backups" style="display:none">
2351
+ <p><em><strong>Note</strong> - Pressing a button will make UpdraftPlus try to bring a backup file back from the remote storage (if any - e.g. Amazon S3, Dropbox, Google Drive, FTP) to your webserver, before then allowing you to download it to your computer. If the fetch from the remote storage stops progressing (wait 30 seconds to make sure), then click again to resume from where it left off. Remember that you can always visit the cloud storage website vendor's website directly.</em></p>
2352
+ <div id="ud_downloadstatus"></div>
2353
+ <script>
2354
+ var lastlog_lastmessage = "";
2355
+ function updraftplus_deletefromserver(timestamp, type) {
2356
+ var pdata = {
2357
+ action: 'updraft_download_backup',
2358
+ stage: 'delete',
2359
+ timestamp: timestamp,
2360
+ type: type,
2361
+ _wpnonce: '<?php echo wp_create_nonce("updraftplus_download"); ?>'
2362
+ };
2363
+ jQuery.post(ajaxurl, pdata, function(response) {
2364
+ if (response == 'deleted') {
2365
+
2366
+ } else {
2367
+ alert('We requested to delete the file, but could not understand the server\'s response '+response);
2368
+ }
2369
+ });
2370
+ }
2371
+ function updraftplus_downloadstage2(timestamp, type) {
2372
+ location.href=ajaxurl+'?_wpnonce=<?php echo wp_create_nonce("updraftplus_download"); ?>&timestamp='+timestamp+'&type='+type+'&stage=2&action=updraft_download_backup';
2373
+ }
2374
+ function updraft_downloader(nonce, what) {
2375
+ // Create somewhere for the status to be found
2376
+ var stid = 'uddlstatus_'+nonce+'_'+what;
2377
+ if (!jQuery('#'+stid).length) {
2378
+ jQuery('#ud_downloadstatus').append('<div style="clear:left; border: 1px dashed; padding: 8px; margin-top: 4px; max-width:840px;" id="'+stid+'"><button onclick="jQuery(\'#'+stid+'\').fadeOut().remove();" type="button" style="float:right;">X</button><strong>Download '+what+' ('+nonce+')</strong>: <span id="'+stid+'_st">Begun looking for this entity</span></div>');
2379
+ setTimeout(function(){updraft_downloader_status(nonce, what)}, 200);
2380
+ }
2381
+ // Reset, in case this is a re-try
2382
+ jQuery('#'+stid+'_st').html('Begun looking for this entity');
2383
+ // Now send the actual request to kick it all off
2384
+ jQuery.post(ajaxurl, jQuery('#uddownloadform_'+what+'_'+nonce).serialize());
2385
+ // We don't want the form to submit as that replaces the document
2386
+ return false;
2387
+ }
2388
+ var dlstatus_sdata = {
2389
+ action: 'updraft_ajax',
2390
+ subaction: 'downloadstatus',
2391
+ nonce: '<?php echo wp_create_nonce('updraftplus-credentialtest-nonce'); ?>'
2392
+ };
2393
+ dlstatus_lastlog = '';
2394
+ function updraft_downloader_status(nonce, what) {
2395
+ var stid = 'uddlstatus_'+nonce+'_'+what;
2396
+ if (jQuery('#'+stid).length) {
2397
+ dlstatus_sdata.timestamp = nonce;
2398
+ dlstatus_sdata.type = what;
2399
+ jQuery.get(ajaxurl, dlstatus_sdata, function(response) {
2400
+ nexttimer = 1250;
2401
+ if (dlstatus_lastlog == response) { nexttimer = 3000; }
2402
+ setTimeout(function(){updraft_downloader_status(nonce, what)}, nexttimer);
2403
+ jQuery('#'+stid+'_st').html(response);
2404
+ dlstatus_lastlog = response;
2405
+ });
2406
+ }
2407
+ }
2408
+ </script>
2409
+ <div id="updraft_existing_backups">
2410
  <?php
2411
+ print $this->existing_backup_table($backup_history);
2412
  ?>
2413
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2414
  </td>
2415
  </tr>
2416
  </table>
2417
  <?php
2418
+ if (is_multisite() && !file_exists(UPDRAFTPLUS_DIR.'/addons/multisite.php')) {
2419
  ?>
2420
+ <h2>UpdraftPlus Multisite</h2>
2421
  <table>
2422
  <tr>
2423
  <td>
2424
+ <p style="max-width:800px;">Do you need WordPress Multisite support? Please check out <a href="http://updraftplus.com">UpdraftPlus Premium</a>.</p>
2425
  </td>
2426
  </tr>
2427
  </table>
2428
  <?php } ?>
2429
  <h2>Configure Backup Contents And Schedule</h2>
2430
  <?php UpdraftPlus_Options::options_form_begin(); ?>
2431
+ <?php $this->settings_formcontents($last_backup_html); ?>
2432
  </form>
2433
  <div style="padding-top: 40px; display:none;" class="expertmode">
2434
  <hr>
2467
  jQuery('.updraftplusmethod').hide();
2468
  var active_class = jQuery(this).val();
2469
  jQuery('.'+active_class).show();
2470
+ });
2471
  })
2472
  jQuery(window).load(function() {
2473
  //this is for hiding the restore progress at the top after it is done
2481
  <?php
2482
  }
2483
 
2484
+ function existing_backup_table($backup_history = false) {
2485
+
2486
+ // Fetch it if it was not passed
2487
+ if ($backup_history === false) $backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
2488
+ if (!is_array($backup_history)) $backup_history=array();
2489
+
2490
+ $updraft_dir = $this->backups_dir_location();
2491
+
2492
+ echo '<table>';
2493
+ foreach($backup_history as $key=>$value) {
2494
+ ?>
2495
+ <tr>
2496
+ <td><b><?php echo date('Y-m-d G:i',$key)?></b></td>
2497
+ <td>
2498
+ <?php if (isset($value['db'])) { ?>
2499
+ <form id="uddownloadform_db_<?php echo $key;?>" action="admin-ajax.php" onsubmit="return updraft_downloader(<?php echo $key;?>, 'db')" method="post">
2500
+ <?php wp_nonce_field('updraftplus_download'); ?>
2501
+ <input type="hidden" name="action" value="updraft_download_backup" />
2502
+ <input type="hidden" name="type" value="db" />
2503
+ <input type="hidden" name="timestamp" value="<?php echo $key?>" />
2504
+ <input type="submit" value="Database" />
2505
+ </form>
2506
+ <?php } else { echo "(No database)"; } ?>
2507
+ </td>
2508
+ <td>
2509
+ <?php if (isset($value['plugins'])) { ?>
2510
+ <form id="uddownloadform_plugins_<?php echo $key;?>" action="admin-ajax.php" onsubmit="return updraft_downloader(<?php echo $key;?>, 'plugins')" method="post">
2511
+ <?php wp_nonce_field('updraftplus_download'); ?>
2512
+ <input type="hidden" name="action" value="updraft_download_backup" />
2513
+ <input type="hidden" name="type" value="plugins" />
2514
+ <input type="hidden" name="timestamp" value="<?php echo $key?>" />
2515
+ <input type="submit" value="Plugins" />
2516
+ </form>
2517
+ <?php } else { echo "(No plugins)"; } ?>
2518
+ </td>
2519
+ <td>
2520
+ <?php if (isset($value['themes'])) { ?>
2521
+ <form id="uddownloadform_themes_<?php echo $key;?>" action="admin-ajax.php" onsubmit="return updraft_downloader(<?php echo $key;?>, 'themes')" method="post">
2522
+ <?php wp_nonce_field('updraftplus_download'); ?>
2523
+ <input type="hidden" name="action" value="updraft_download_backup" />
2524
+ <input type="hidden" name="type" value="themes" />
2525
+ <input type="hidden" name="timestamp" value="<?php echo $key?>" />
2526
+ <input type="submit" value="Themes" />
2527
+ </form>
2528
+ <?php } else { echo "(No themes)"; } ?>
2529
+ </td>
2530
+ <td>
2531
+ <?php if (isset($value['uploads'])) { ?>
2532
+ <form id="uddownloadform_uploads_<?php echo $key;?>" action="admin-ajax.php" onsubmit="return updraft_downloader(<?php echo $key;?>, 'uploads')" method="post">
2533
+ <?php wp_nonce_field('updraftplus_download'); ?>
2534
+ <input type="hidden" name="action" value="updraft_download_backup" />
2535
+ <input type="hidden" name="type" value="uploads" />
2536
+ <input type="hidden" name="timestamp" value="<?php echo $key?>" />
2537
+ <input type="submit" value="Uploads" />
2538
+ </form>
2539
+ <?php } else { echo "(No uploads)"; } ?>
2540
+ </td>
2541
+ <td>
2542
+ <?php if (isset($value['others'])) { ?>
2543
+ <form id="uddownloadform_others_<?php echo $key;?>" action="admin-ajax.php" onsubmit="return updraft_downloader(<?php echo $key;?>, 'others')" method="post">
2544
+ <?php wp_nonce_field('updraftplus_download'); ?>
2545
+ <input type="hidden" name="action" value="updraft_download_backup" />
2546
+ <input type="hidden" name="type" value="others" />
2547
+ <input type="hidden" name="timestamp" value="<?php echo $key?>" />
2548
+ <input type="submit" value="Others" />
2549
+ </form>
2550
+ <?php } else { echo "(No others)"; } ?>
2551
+ </td>
2552
+ <td>
2553
+ <?php if (isset($value['nonce']) && preg_match("/^[0-9a-f]{12}$/",$value['nonce']) && is_readable($updraft_dir.'/log.'.$value['nonce'].'.txt')) { ?>
2554
+ <form action="options-general.php" method="get">
2555
+ <input type="hidden" name="action" value="downloadlog" />
2556
+ <input type="hidden" name="page" value="updraftplus" />
2557
+ <input type="hidden" name="updraftplus_backup_nonce" value="<?php echo $value['nonce']; ?>" />
2558
+ <input type="submit" value="Backup Log" />
2559
+ </form>
2560
+ <?php } else { echo "(No backup log)"; } ?>
2561
+ </td>
2562
+ </tr>
2563
+ <?php }
2564
+ echo '</table>';
2565
+ }
2566
+
2567
  function show_admin_warning($message, $class = "updated") {
2568
  echo '<div id="updraftmessage" class="'.$class.' fade">'."<p>$message</p></div>";
2569
  }
2570
 
2571
+ function show_admin_warning_diskspace() {
2572
+ $this->show_admin_warning('<strong>Warning:</strong> You have less than 35Mb of free disk space on the disk which UpdraftPlus is configured to use to create backups. UpdraftPlus could well run out of space. Contact your the operator of your server (e.g. your web hosting company) to resolve this issue.');
2573
+ }
2574
+
2575
+ function show_admin_warning_wordpressversion() {
2576
+ $this->show_admin_warning('<strong>Warning:</strong> UpdraftPlus does not officially support versions of WordPress before 3.2. It may work for you, but if it does not, then please be aware that no support is available until you upgrade WordPress.');
2577
+ }
2578
+
2579
  function show_admin_warning_unreadablelog() {
2580
  $this->show_admin_warning('<strong>UpdraftPlus notice:</strong> The log file could not be read.');
2581
  }