PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.2.39
UpdraftPlus: WP Backup & Migration Plugin v1.2.39
1.26.7 1.26.6 1.26.5 1.26.4 1.26.3 1.9.19 1.9.25 1.9.26 1.9.30 1.9.31 1.9.32 1.9.4 1.9.40 1.9.41 1.9.42 1.9.43 1.9.44 1.9.45 1.9.46 1.9.5 1.9.50 1.9.51 1.9.60 1.9.62 1.9.63 All 371 releases
updraftplus / updraftplus.php

updraftplus.php in UpdraftPlus: WP Backup & Migration Plugin 1.2.39, at updraftplus.php

2,092 lines 94.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: UpdraftPlus - Backup/Restore
4 Plugin URI: http://wordpress.org/extend/plugins/updraftplus
5 Description: Backup and restore: your content and database can be automatically backed up to Amazon S3, Dropbox, Google Drive, FTP or email, on separate schedules.
6 Author: David Anderson.
7 Version: 1.2.39
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 //Add SFTP, Box.Net, SugarSync and Microsoft Skydrive support??
16 //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?)
17 //?? On 'backup now', open up a Lightbox, count down 5 seconds, then start examining the log file (if it can be found)
18 //Should make clear in dashboard what is a non-fatal error (i.e. can be retried) - leads to unnecessary bug reports
19 //Eventually, when everything can be resumed, we will no longer need the backup() routine; it can be replaced with the resume() routine
20 // Should we resume if the only errors were upon deletion (i.e. the backup itself was fine?) Presently we do, but it displays errors for the user to confuse them.
21 // Make jobs *individually* resumable (i.e. all the state info must be keyed on the nonce; then call the resume event *specifying the nonce*)
22 // Separate 'retain' settings for db + files (since they are on separate schedules)
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
27 Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
28 // Does not delete old custom directories upon a restore?
29 // Re-do making of zip files to allow resumption (every x files, store the state in a transient)
30 */
31
32 /* Portions copyright 2010 Paul Kehrer
33 Portions copyright 2011-12 David Anderson
34 Other portions copyright as indicated authors in the relevant files
35 Particular thanks to Sorin Iclanzan, author of the "Backup" plugin, from which much Google Drive code was taken under the GPLv3+
36
37 This program is free software; you can redistribute it and/or modify
38 it under the terms of the GNU General Public License as published by
39 the Free Software Foundation; either version 3 of the License, or
40 (at your option) any later version.
41
42 This program is distributed in the hope that it will be useful,
43 but WITHOUT ANY WARRANTY; without even the implied warranty of
44 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45 GNU General Public License for more details.
46
47 You should have received a copy of the GNU General Public License
48 along with this program; if not, write to the Free Software
49 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
50 */
51
52 // 15 minutes
53 @set_time_limit(900);
54
55 if (!isset($updraftplus)) $updraftplus = new UpdraftPlus();
56
57 if (!$updraftplus->memory_check(192)) {
58 # TODO: Better solution is to split the backup set into manageable chunks based on this limit
59 @ini_set('memory_limit', '192M'); //up the memory limit for large backup files
60 }
61
62 define('UPDRAFTPLUS_DIR', dirname(__FILE__));
63 define('UPDRAFTPLUS_URL', plugins_url('', __FILE__));
64 define('UPDRAFT_DEFAULT_OTHERS_EXCLUDE','upgrade,cache,updraft,index.php');
65
66 class UpdraftPlus {
67
68 var $version = '1.2.39';
69
70 // Choices will be shown in the admin menu in the order used here
71 var $backup_methods = array (
72 "s3" => "Amazon S3",
73 "dropbox" => "Dropbox",
74 "googledrive" => "Google Drive",
75 "ftp" => "FTP",
76 "email" => "Email"
77 );
78
79 var $dbhandle;
80 var $dbhandle_isgz;
81 var $errors = array();
82 var $nonce;
83 var $cronrun_type = "none";
84 var $logfile_name = "";
85 var $logfile_handle = false;
86 var $backup_time;
87
88 var $opened_log_time;
89
90 function __construct() {
91 // Initialisation actions - takes place on plugin load
92 # Create admin page
93 add_action('admin_menu', array($this,'add_admin_pages'));
94 add_action('admin_init', array($this,'admin_init'));
95 add_action('updraft_backup', array($this,'backup_files'));
96 add_action('updraft_backup_database', array($this,'backup_database'));
97 # backup_all is used by the manual "Backup Now" button
98 add_action('updraft_backup_all', array($this,'backup_all'));
99 # this is our runs-after-backup event, whose purpose is to see if it succeeded or failed, and resume/mom-up etc.
100 add_action('updraft_backup_resume', array($this,'backup_resume'));
101 add_action('wp_enqueue_scripts', array($this, 'ajax_enqueue') );
102 add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup'));
103 add_action('wp_ajax_updraft_ajax', array($this, 'updraft_ajax_handler'));
104 # http://codex.wordpress.org/Plugin_API/Filter_Reference/cron_schedules
105 add_filter('cron_schedules', array($this,'modify_cron_schedules'));
106 add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2);
107 add_action('init', array($this, 'handle_url_actions'));
108 }
109
110 // Handle actions passed on to method plugins; e.g. Google OAuth 2.0 - ?page=updraftplus&action=updraftmethod-googledrive-auth
111 // Also handle action=downloadlog
112 function handle_url_actions() {
113 // First, basic security check: must be an admin page, with ability to manage options, with the right parameters
114 if ( is_admin() && current_user_can('manage_options') && isset( $_GET['page'] ) && $_GET['page'] == 'updraftplus' && isset($_GET['action']) ) {
115 if (preg_match("/^updraftmethod-([a-z]+)-([a-z]+)$/", $_GET['action'], $matches) && file_exists(UPDRAFTPLUS_DIR.'/methods/'.$matches[1].'.php')) {
116 $method = $matches[1];
117 require_once(UPDRAFTPLUS_DIR.'/methods/'.$method.'.php');
118 $call_class = "UpdraftPlus_BackupModule_".$method;
119 $call_method = "action_".$matches[2];
120 if (method_exists($call_class, $call_method)) call_user_func(array($call_class,$call_method));
121 } elseif ($_GET['action'] == 'downloadlog' && isset($_GET['updraftplus_backup_nonce']) && preg_match("/^[0-9a-f]{12}$/",$_GET['updraftplus_backup_nonce'])) {
122 $updraft_dir = $this->backups_dir_location();
123 $log_file = $updraft_dir.'/log.'.$_GET['updraftplus_backup_nonce'].'.txt';
124 if (is_readable($log_file)) {
125 header('Content-type: text/plain');
126 readfile($log_file);
127 exit;
128 } else {
129 add_action('admin_notices', array($this,'show_admin_warning_unreadablelog') );
130 }
131 }
132 }
133 }
134
135 # Adds the settings link under the plugin on the plugin screen.
136 function plugin_action_links($links, $file) {
137 if ($file == plugin_basename(__FILE__)){
138 $settings_link = '<a href="'.site_url().'/wp-admin/options-general.php?page=updraftplus">'.__("Settings", "UpdraftPlus").'</a>';
139 array_unshift($links, $settings_link);
140 $settings_link = '<a href="http://david.dw-perspective.org.uk/donate">'.__("Donate","UpdraftPlus").'</a>';
141 array_unshift($links, $settings_link);
142 }
143 return $links;
144 }
145
146 function backup_time_nonce() {
147 $this->backup_time = time();
148 $nonce = substr(md5(time().rand()), 20);
149 $this->nonce = $nonce;
150 // Short-lived, as we only use this for detecting a race condition
151 set_transient("updraftplus_runtype_$nonce", $this->cronrun_type, 300);
152 }
153
154 function logfile_open($nonce) {
155 //set log file name and open log file
156 $updraft_dir = $this->backups_dir_location();
157 $this->logfile_name = $updraft_dir. "/log.$nonce.txt";
158 // Use append mode in case it already exists
159 $this->logfile_handle = fopen($this->logfile_name, 'a');
160 $this->opened_log_time = microtime(true);
161 $this->log("Opened log file at time: ".date('r'));
162 }
163
164 # Logs the given line, adding (relative) time stamp and newline
165 function log($line) {
166 if ($this->logfile_handle) fwrite($this->logfile_handle, sprintf("%08.03f", round(microtime(true)-$this->opened_log_time, 3))." ".$line."\n");
167 update_option("updraft_lastmessage", $line." (".date('M d H:i:s').")");
168 }
169
170 function backup_resume($resumption_no) {
171 @ignore_user_abort(true);
172 // This is scheduled for 5 minutes after a backup job starts
173 $bnonce = get_transient('updraftplus_backup_job_nonce');
174 if (!$bnonce) return;
175 $this->nonce = $bnonce;
176 $this->logfile_open($bnonce);
177 $this->log("Resume backup ($resumption_no): begin run (will check for any remaining jobs)");
178 $btime = get_transient('updraftplus_backup_job_time');
179 if (!$btime) {
180 $this->log("Did not find stored time setting - aborting");
181 return;
182 }
183 $this->log("Resuming backup: resumption=$resumption_no, nonce=$bnonce, begun at=$btime");
184 // Schedule again, to run in 5 minutes again, in case we again fail
185 $resume_delay = 300;
186 // A different argument than before is needed otherwise the event is ignored
187 $next_resumption = $resumption_no+1;
188 if ($next_resumption < 10) {
189 wp_schedule_single_event(time()+$resume_delay, 'updraft_backup_resume' ,array($next_resumption));
190 } else {
191 $this->log("The current run is our tenth attempt - will not try again");
192 }
193 $this->backup_time = $btime;
194
195 $backup_array = $this->resumable_backup_of_files(false);
196 // This save, if there was something, is then immediately picked up again
197 if (is_array($backup_array)) $this->save_backup_history($backup_array);
198
199 // Returns an array, most recent first, of backup sets
200 $backup_history = $this->get_backup_history();
201 if (!isset($backup_history[$btime])) $this->log("Could not find a record in the database of a backup with this timestamp");
202
203 $our_files=$backup_history[$btime];
204 if (!is_array($our_files)) $our_files = array();
205
206 $undone_files = array();
207
208 $backup_database = get_transient("updraft_backdb_".$bnonce);
209
210 // The transient is read and written below (instead of using the existing variable) so that we can copy-and-paste this part as needed.
211 if ($backup_database == "begun" || $backup_database == "finished") {
212 if ($backup_database == "begun") {
213 $this->log("Resuming creation of database dump");
214 } else {
215 $this->log("Database dump: Creation was completed already");
216 }
217 $db_backup = $this->backup_db($backup_database);
218 if(is_array($our_files) && is_string($db_backup)) $our_files['db'] = $db_backup;
219 $backup_contains = get_transient("updraft_backupcontains_".$this->nonce);
220 $backup_contains = (substr($backup_contains,0,10) == "Files only") ? "Files and database" : "Database only (no files)";
221 set_transient("updraft_backupcontains_".$this->nonce, $backup_contains, 3600*3);
222 } else {
223 $this->log("Unrecognised data when trying to ascertain if the database was backed up ($backup_database)");
224 }
225
226 // Save this to our history so we can track backups for the retain feature
227 $this->log("Saving backup history");
228 // This is done before cloud despatch, because we want a record of what *should* be in the backup. Whether it actually makes it there or not is not yet known.
229 $this->save_backup_history($our_files);
230
231 // Potentially encrypt the database if it is not already
232 if (isset($our_files['db']) && !preg_match("/\.crypt$/", $our_files['db'])) {
233 $our_files['db'] = $this->encrypt_file($our_files['db']);
234 $this->save_backup_history($our_files);
235 }
236
237 foreach ($our_files as $key => $file) {
238
239 if ($key == 'nonce') continue;
240
241 $hash = md5($file);
242 $fullpath = trailingslashit(get_option('updraft_dir')).$file;
243 if (get_transient('updraft_'.$hash) === "yes") {
244 $this->log("$file: $key: This file has been successfully uploaded in the last 3 hours");
245 } elseif (is_file($fullpath)) {
246 $this->log("$file: $key: This file has NOT been successfully uploaded in the last 3 hours: will retry");
247 $undone_files[$key] = $file;
248 } else {
249 $this->log("$file: Note: This file was not marked as successfully uploaded, but does not exist on the local filesystem");
250 $this->uploaded_file($file);
251 }
252 }
253
254 if (count($undone_files) == 0) {
255 $this->log("There were no more files that needed uploading; backup job is complete");
256 return;
257 }
258
259 $this->log("Requesting backup of the files that were not successfully uploaded");
260 $this->cloud_backup($undone_files);
261
262 $this->log("Resume backup ($resumption_no): finish run");
263
264 $this->backup_finish($next_resumption, true, true, $resumption_no);
265
266 }
267
268 function backup_all() {
269 $this->backup(true,true);
270 }
271
272 function backup_files() {
273 # Note that the "false" for database gets over-ridden automatically if they turn out to have the same schedules
274 $this->cronrun_type = "files";
275 $this->backup(true,false);
276 }
277
278 function backup_database() {
279 # Note that nothing will happen if the file backup had the same schedule
280 $this->cronrun_type = "database";
281 $this->backup(false,true);
282 }
283
284 function check_backup_race( $to_delete = false ) {
285 // Avoid caching
286 global $wpdb;
287 $row = $wpdb->get_row($wpdb->prepare("SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", "_transient_updraftplus_backup_job_nonce"));
288 $cur_trans = ( is_object( $row ) ) ? $row->option_value : "";
289 // Check if another backup job ID is stored in the transient
290 if ($cur_trans != "" && $cur_trans != $this->nonce) {
291 // Also check if that job is of the same type as ours, as two cron jobs could legitimately fire at the same time
292 $otherjob_crontype = get_transient("updraftplus_runtype_".$cur_trans);
293 // $this->cronrun_type should be "files", "database" or blank (if we were not run via a cron job)
294 if ($otherjob_crontype == $this->cronrun_type) {
295 $this->log("Another backup job ($cur_trans) of the same type ($otherjob_crontype) appears to now be running - terminating our run (apparent race condition)");
296 $bdir = $this->backups_dir_location();
297 if (is_array($to_delete)) {
298 foreach ($to_delete as $key => $file) {
299 if (is_file($bdir.'/'.$file)) {
300 $this->log("Deleting the file we created: ".$file);
301 @unlink($bdir.'/'.$file);
302 }
303 }
304 }
305 exit;
306 }
307 }
308 }
309
310 // This uses a transient; its only purpose is to indicate *total* completion; there is no actual danger, just wasted time, in resuming when it was not needed. So the transient just helps save resources.
311 function resumable_backup_of_files($resumptionrun) {
312 //backup directories and return a numerically indexed array of file paths to the backup files
313 $transient_status = get_transient("updraft_backf_".$this->nonce);
314 if ($transient_status == "finished") {
315 $this->log("Creation of backups of directories: already finished");
316 } elseif ($transient_status == "begun") {
317 if ($resumptionrun) {
318 $this->log("Creation of backups of directories: had begun; will resume");
319 } else {
320 $this->log("Creation of backups of directories: beginning");
321 }
322 } else {
323 # This is not necessarily a backup run which is meant to contain files at all
324 $this->log("This backup run is not intended for files - skipping");
325 return array();
326 }
327 // We want this array, even if already finished
328 $backup_array = $this->backup_dirs($transient_status);
329 $backup_contains = "Files only (no database)";
330 // This can get over-written later
331 set_transient("updraft_backupcontains_".$this->nonce, $backup_contains, 3600*3);
332 set_transient("updraft_backf_".$this->nonce, "finished", 3600*3);
333 return $backup_array;
334 }
335
336 function backup($backup_files, $backup_database) {
337
338 @ignore_user_abort(true);
339 //generate backup information
340 $this->backup_time_nonce();
341
342 $this->logfile_open($this->nonce);
343
344 // Log some information that may be helpful
345 global $wp_version;
346 $this->log("UpdraftPlus: ".$this->version." WordPress: ".$wp_version." PHP: ".phpversion()." (".@php_uname().") PHP Max Execution Time: ".@ini_get("max_execution_time"));
347 $this->log("Tasks: Backup files: $backup_files (schedule: ".get_option('updraft_interval','unset').") Backup DB: $backup_database (schedule: ".get_option('updraft_interval_database','unset').")");
348
349 # If the files and database schedules are the same, and if this the file one, then we rope in database too.
350 # On the other hand, if the schedules were the same and this was the database run, then there is nothing to do.
351 if (get_option('updraft_interval') == get_option('updraft_interval_database') || get_option('updraft_interval_database','xyz') == 'xyz' ) {
352 $backup_database = ($backup_files == true) ? true : false;
353 }
354
355 $this->log("Processed schedules. Tasks now: Backup files: $backup_files Backup DB: $backup_database");
356
357 $clear_nonce_transient = false;
358
359 # Possibly now nothing is to be done, except to close the log file
360 if ($backup_files || $backup_database) {
361
362 $clear_nonce_transient = true;
363
364 // Do not set the transient or schedule the resume event until now, when we know there is something to do - otherwise 'vacatated' runs (when the database is on the same schedule as the files, and they get combined, leading to an empty run) can over-write the resume event and prevent resumption (because it is 'successful' - there was nothing to do).
365 // If we don't finish in 3 hours, then we won't finish
366 // This transient indicates the identity of the current backup job (which can be used to find the files and logfile)
367 set_transient("updraftplus_backup_job_nonce", $this->nonce, 3600*3);
368 set_transient("updraftplus_backup_job_time", $this->backup_time, 3600*3);
369
370 // Schedule the event to run later, which checks on success and can resume the backup
371 // We save the time to a variable because it is needed for un-scheduling
372 $resume_delay = 300;
373 wp_schedule_single_event(time()+$resume_delay, 'updraft_backup_resume', array(1));
374 $this->log("In case we run out of time, scheduled a resumption at: $resume_delay seconds from now");
375
376 $backup_contains = "";
377 set_transient("updraft_backupcontains_".$this->nonce, "");
378
379 $backup_array = array();
380
381 $this->check_backup_race();
382
383 // Save what *should* be done, to make it resumable from this point on
384 set_transient("updraft_backdb_".$this->nonce, "begun", 3600*3);
385
386 // The function itself will set to 'finished' if relevant
387 // The presence of the transient indicates that files are supposed to be in this set
388 if ($backup_files) {
389 set_transient("updraft_backf_".$this->nonce, "begun", 3600*3);
390 $backup_array = $this->resumable_backup_of_files(false);
391 }
392
393 // Save this to our history so we can track backups for the retain feature
394 $this->log("Saving backup history");
395 $this->save_backup_history($backup_array);
396
397 $this->check_backup_race($backup_array);
398
399 // The transient is read and written below (instead of using the existing variable) so that we can copy-and-paste this part as needed.
400 if ($backup_database) {
401 $this->log("Beginning backup of database");
402 $db_backup = $this->backup_db();
403 if ($db_backup) $backup_array['db'] = $db_backup;
404 $backup_contains = get_transient("updraft_backupcontains_".$this->nonce);
405 $backup_contains = (substr($backup_contains,0,10) == "Files only") ? "Files and database" : "Database only (no files)";
406 set_transient("updraft_backupcontains_".$this->nonce, $backup_contains, 3600*3);
407 set_transient("updraft_backdb_".$this->nonce, "finished", 3600*3);
408 }
409
410 $this->check_backup_race($backup_array);
411
412 // Save this to our history so we can track backups for the retain feature
413 $this->log("Saving backup history");
414 // This is done before cloud despatch, because we want a record of what *should* be in the backup. Whether it actually makes it there or not is not yet known.
415 $this->save_backup_history($backup_array);
416
417 // Now encrypt the database, and re-save
418 if ($backup_database && isset($backup_array['db'])) {
419 $backup_array['db'] = $this->encrypt_file($backup_array['db']);
420 // Re-save with the possibly-altered database filename
421 $this->save_backup_history($backup_array);
422 }
423
424 //cloud operations (S3,Google Drive,FTP,email,nothing)
425 //this also calls the retain (prune) feature at the end (done in this method to reuse existing cloud connections)
426 if(is_array($backup_array) && count($backup_array) >0) {
427 $this->log("Beginning dispatch of backup to remote");
428 $this->cloud_backup($backup_array);
429 }
430
431 //save the last backup info, including errors, if any
432 $this->log("Saving last backup information into WordPress db");
433 $this->save_last_backup($backup_array);
434
435 }
436
437 // Close log file; delete and also delete transients if not in debug mode
438 $this->backup_finish(1, $clear_nonce_transient, $clear_nonce_transient, 0);
439
440 }
441
442 // Encrypts the file if the option is set; returns the basename of the file (according to whether it was encrypted or nto)
443 function encrypt_file($file) {
444 $encryption = get_option('updraft_encryptionphrase');
445 if (strlen($encryption) > 0) {
446 $this->log("$file: applying encryption");
447 $encryption_error = 0;
448 require_once(UPDRAFTPLUS_DIR.'/includes/Rijndael.php');
449 $rijndael = new Crypt_Rijndael();
450 $rijndael->setKey($encryption);
451 $updraft_dir = $this->backups_dir_location();
452 $in_handle = @fopen($updraft_dir.'/'.$file,'r');
453 $buffer = "";
454 while (!feof ($in_handle)) {
455 $buffer .= fread($in_handle, 16384);
456 }
457 fclose ($in_handle);
458 $out_handle = @fopen($updraft_dir.'/'.$file.'.crypt','w');
459 if (!fwrite($out_handle, $rijndael->encrypt($buffer))) {$encryption_error = 1;}
460 fclose ($out_handle);
461 if (0 == $encryption_error) {
462 $this->log("$file: encryption successful");
463 # Delete unencrypted file
464 @unlink($updraft_dir.'/'.$file);
465 return basename($file.'.crypt');
466 } else {
467 $this->log("Encryption error occurred when encrypting database. Encryption aborted.");
468 $this->error("Encryption error occurred when encrypting database. Encryption aborted.");
469 return basename($file);
470 }
471 } else {
472 return basename($file);
473 }
474 }
475
476 function backup_finish($cancel_event, $clear_nonce_transient, $allow_email, $resumption_no) {
477
478 // In fact, leaving the hook to run (if debug is set) is harmless, as the resume job should only do tasks that were left unfinished, which at this stage is none.
479 if (empty($this->errors)) {
480 if ($clear_nonce_transient) {
481 $this->log("There were no errors in the uploads, so the 'resume' event is being unscheduled");
482 wp_clear_scheduled_hook('updraft_backup_resume', array($cancel_event));
483 delete_transient("updraftplus_backup_job_nonce");
484 delete_transient("updraftplus_backup_job_time");
485 }
486 } else {
487 $this->log("There were errors in the uploads, so the 'resume' event is remaining scheduled");
488 }
489
490 // Send the results email if appropriate, which means:
491 // - The caller allowed it (which is not the case in an 'empty' run)
492 // - And: An email address was set (which must be so in email mode)
493 // And one of:
494 // - Debug mode
495 // - There were no errors (which means we completed and so this is the final run - time for the final report)
496 // - It was the tenth resumption; everything failed
497
498 $send_an_email = false;
499
500 // Make sure that the final status is shown
501 if (empty($this->errors)) {
502 $send_an_email = true;
503 $final_message = "The backup apparently succeeded and is now complete";
504 } elseif ($resumption_no >=9) {
505 $send_an_email = true;
506 $final_message = "The backup attempt has finished, apparently unsuccesfully";
507 } else {
508 // There are errors, but a resumption will be attempted
509 $final_message = "The backup has not finished; a resumption is scheduled within 5 minutes";
510 }
511
512 // Now over-ride the decision to send an email, if needed
513 if (get_option('updraft_debug_mode')) {
514 $send_an_email = true;
515 $this->log("An email has been scheduled for this job, because we are in debug mode");
516 }
517 // If there's no email address, or the set was empty, that is the final over-ride: don't send
518 if (!$allow_email) {
519 $send_an_email = false;
520 $this->log("No email will be sent - this backup set was empty.");
521 } elseif (get_option('updraft_email') == '') {
522 $send_an_email = false;
523 $this->log("No email will/can be sent - the user has not configured an email address.");
524 }
525
526 if ($send_an_email) $this->send_results_email();
527
528 $this->log($final_message);
529
530 @fclose($this->logfile_handle);
531
532 // Don't delete the log file now; delete it upon rotation
533 //if (!get_option('updraft_debug_mode')) @unlink($this->logfile_name);
534
535 }
536
537 function send_results_email() {
538
539 $debug_mode = get_option('updraft_debug_mode');
540
541 $sendmail_to = get_option('updraft_email');
542
543 $this->log("Sending email report to: ".substr($sendmail_to, 0, 5)."...");
544
545 $append_log = ($debug_mode && $this->logfile_name != "") ? "\r\nLog contents:\r\n".file_get_contents($this->logfile_name) : "" ;
546
547 wp_mail($sendmail_to,'Backed up: '.get_bloginfo('name').' (UpdraftPlus '.$this->version.') '.date('Y-m-d H:i',time()),'Site: '.site_url()."\r\nUpdraftPlus WordPress backup is complete.\r\nBackup contains: ".get_transient("updraft_backupcontains_".$this->nonce)."\r\n\r\n".$this->wordshell_random_advert(0)."\r\n".$append_log);
548
549 }
550
551 function save_last_backup($backup_array) {
552 $success = (empty($this->errors)) ? 1 : 0;
553
554 $last_backup = array('backup_time'=>$this->backup_time, 'backup_array'=>$backup_array, 'success'=>$success, 'errors'=>$this->errors, 'backup_nonce' => $this->nonce);
555
556 update_option('updraft_last_backup', $last_backup);
557 }
558
559 // This should be called whenever a file is successfully uploaded
560 function uploaded_file($file, $id = false) {
561 # We take an MD5 hash because set_transient wants a name of 45 characters or less
562 $hash = md5($file);
563 $this->log("Recording as successfully uploaded: $file ($hash)");
564 set_transient("updraft_".$hash, "yes", 3600*4);
565 if ($id) {
566 $ids = get_option('updraft_file_ids', array() );
567 $ids[$file] = $id;
568 update_option('updraft_file_ids',$ids);
569 $this->log("Stored file<->id correlation in database ($file <-> $id)");
570 }
571 // Delete local files if the option is set
572 $this->delete_local($file);
573
574 }
575
576 // Dispatch to the relevant function
577 function cloud_backup($backup_array) {
578 $service = get_option('updraft_service');
579 $this->log("Cloud backup selection: ".$service);
580 @set_time_limit(900);
581
582 $method_include = UPDRAFTPLUS_DIR.'/methods/'.$service.'.php';
583 if (file_exists($method_include)) require_once($method_include);
584
585 $objname = "UpdraftPlus_BackupModule_${service}";
586 if (method_exists($objname, "backup")) {
587 // New style - external, allowing more plugability
588 $remote_obj = new $objname;
589 $remote_obj->backup($backup_array);
590 } else {
591 $this->prune_retained_backups("local", null, null);
592 }
593 }
594
595 function prune_file($updraft_service, $dofile, $method_object = null, $object_passback = null ) {
596 $this->log("Delete this file: $dofile, service=$updraft_service");
597 $fullpath = trailingslashit(get_option('updraft_dir')).$dofile;
598 // delete it if it's locally available
599 if (file_exists($fullpath)) {
600 $this->log("Deleting local copy ($fullpath)");
601 @unlink($fullpath);
602 }
603
604 // Despatch to the particular method's deletion routine
605 if (!is_null($method_object)) $method_object->delete($dofile, $object_passback);
606 }
607
608 // Carries out retain behaviour. Pass in a valid S3 or FTP object and path if relevant.
609 function prune_retained_backups($updraft_service, $backup_method_object = null, $backup_passback = null) {
610
611 $this->log("Retain: beginning examination of existing backup sets");
612
613 // Number of backups to retain
614 $updraft_retain = get_option('updraft_retain', 1);
615 $retain = (is_numeric($updraft_retain)) ? $updraft_retain : 1;
616 $this->log("Retain: user setting: number to retain = $retain");
617
618 // Returns an array, most recent first, of backup sets
619 $backup_history = $this->get_backup_history();
620 $db_backups_found = 0;
621 $file_backups_found = 0;
622 $this->log("Number of backup sets in history: ".count($backup_history));
623
624 foreach ($backup_history as $backup_datestamp => $backup_to_examine) {
625 // $backup_to_examine is an array of file names, keyed on db/plugins/themes/uploads
626 // The new backup_history array is saved afterwards, so remember to unset the ones that are to be deleted
627 $this->log("Examining backup set with datestamp: $backup_datestamp");
628
629 if (isset($backup_to_examine['db'])) {
630 $db_backups_found++;
631 $this->log("$backup_datestamp: this set includes a database (".$backup_to_examine['db']."); db count is now $db_backups_found");
632 if ($db_backups_found > $retain) {
633 $this->log("$backup_datestamp: over retain limit; will delete this database");
634 $dofile = $backup_to_examine['db'];
635 if (!empty($dofile)) $this->prune_file($updraft_service, $dofile, $backup_method_object, $backup_passback);
636 unset($backup_to_examine['db']);
637 }
638 }
639 if (isset($backup_to_examine['plugins']) || isset($backup_to_examine['themes']) || isset($backup_to_examine['uploads']) || isset($backup_to_examine['others'])) {
640 $file_backups_found++;
641 $this->log("$backup_datestamp: this set includes files; fileset count is now $file_backups_found");
642 if ($file_backups_found > $retain) {
643 $this->log("$backup_datestamp: over retain limit; will delete this file set");
644 $file = isset($backup_to_examine['plugins']) ? $backup_to_examine['plugins'] : "";
645 $file2 = isset($backup_to_examine['themes']) ? $backup_to_examine['themes'] : "";
646 $file3 = isset($backup_to_examine['uploads']) ? $backup_to_examine['uploads'] : "";
647 $file4 = isset($backup_to_examine['others']) ? $backup_to_examine['others'] : "";
648 foreach (array($file, $file2, $file3, $file4) as $dofile) {
649 if (!empty($dofile)) $this->prune_file($updraft_service, $dofile, $backup_method_object, $backup_passback);
650 }
651 unset($backup_to_examine['plugins']);
652 unset($backup_to_examine['themes']);
653 unset($backup_to_examine['uploads']);
654 unset($backup_to_examine['others']);
655 }
656 }
657 // Delete backup set completely if empty, o/w just remove DB
658 if (count($backup_to_examine) == 0 || (count($backup_to_examine) == 1 && isset($backup_to_examine['nonce']))) {
659 $this->log("$backup_datestamp: this backup set is now empty; will remove from history");
660 unset($backup_history[$backup_datestamp]);
661 if (isset($backup_to_examine['nonce'])) {
662 $fullpath = trailingslashit(get_option('updraft_dir')).'log.'.$backup_to_examine['nonce'].'.txt';
663 if (is_file($fullpath)) {
664 $this->log("$backup_datestamp: deleting log file (log.".$backup_to_examine['nonce'].".txt)");
665 @unlink($fullpath);
666 } else {
667 $this->log("$backup_datestamp: corresponding log file not found - must have already been deleted");
668 }
669 } else {
670 $this->log("$backup_datestamp: no nonce record found in the backup set, so cannot delete any remaining log file");
671 }
672 } else {
673 $this->log("$backup_datestamp: this backup set remains non-empty; will retain in history");
674 $backup_history[$backup_datestamp] = $backup_to_examine;
675 }
676 }
677 $this->log("Retain: saving new backup history (sets now: ".count($backup_history).") and finishing retain operation");
678 update_option('updraft_backup_history',$backup_history);
679 }
680
681 function delete_local($file) {
682 if(get_option('updraft_delete_local')) {
683 $this->log("Deleting local file: $file");
684 //need error checking so we don't delete what isn't successfully uploaded?
685 $fullpath = trailingslashit(get_option('updraft_dir')).$file;
686 return unlink($fullpath);
687 }
688 return true;
689 }
690
691 function create_zip($create_from_dir, $whichone, $create_in_dir, $backup_file_basename) {
692 // Note: $create_from_dir can be an array or a string
693 @set_time_limit(900);
694
695 if ($whichone != "others") $this->log("Beginning creation of dump of $whichone");
696
697 $full_path = $create_in_dir.'/'.$backup_file_basename.'-'.$whichone.'.zip';
698
699 if (file_exists($full_path)) {
700 $this->log("$backup_file_basename-$whichone.zip: this file has already been created");
701 return basename($full_path);
702 }
703
704 // Temporary file, to be able to detect actual completion (upon which, it is renamed)
705 $zip_object = new PclZip($full_path.'.tmp');
706
707 $microtime_start = microtime(true);
708 # The paths in the zip should then begin with '$whichone', having removed WP_CONTENT_DIR from the front
709 if (!$zip_object->create($create_from_dir, PCLZIP_OPT_REMOVE_PATH, WP_CONTENT_DIR)) {
710 $this->log("ERROR: PclZip failure: Could not create $whichone zip");
711 $this->error("Could not create $whichone zip. Consult the log file for more information.");
712 return false;
713 } else {
714 rename($full_path.'.tmp', $full_path);
715 $timetaken = max(microtime(true)-$microtime_start, 0.000001);
716 $kbsize = filesize($full_path)/1024;
717 $rate = round($kbsize/$timetaken, 1);
718 $this->log("Created $whichone zip - file size is ".round($kbsize,1)." Kb in ".round($timetaken,1)." s ($rate Kb/s)");
719 }
720 $this->check_backup_race($backup_array);
721 return basename($full_path);
722 }
723
724 // This function is resumable
725 function backup_dirs($transient_status) {
726
727 if(!$this->backup_time) $this->backup_time_nonce();
728
729 if(!class_exists('PclZip')) require_once(ABSPATH.'/wp-admin/includes/class-pclzip.php');
730
731 $updraft_dir = $this->backups_dir_location();
732 if(!is_writable($updraft_dir)) {
733 $this->log('Backup directory is not writable, or does not exist');
734 $this->error('Backup directory is not writable, or does not exist.');
735 return array();
736 }
737
738 //get the blog name and rip out all non-alphanumeric chars other than _
739 $blog_name = str_replace(' ','_',get_bloginfo());
740 $blog_name = preg_replace('/[^A-Za-z0-9_]/','', $blog_name);
741 if(!$blog_name) $blog_name = 'non_alpha_name';
742
743 $backup_file_basename = 'backup_'.date('Y-m-d-Hi', $this->backup_time).'_'.$blog_name.'_'.$this->nonce;
744
745 $backup_array = array();
746
747 $wp_themes_dir = WP_CONTENT_DIR.'/themes';
748 $wp_upload_dir = wp_upload_dir();
749 $wp_upload_dir = $wp_upload_dir['basedir'];
750 $wp_plugins_dir = WP_PLUGIN_DIR;
751
752 $possible_backups = array ('plugins' => $wp_plugins_dir, 'themes' => $wp_themes_dir, 'uploads' => $wp_upload_dir);
753
754 # Plugins, themes, uploads
755 foreach ($possible_backups as $youwhat => $whichdir) {
756 if (get_option("updraft_include_$youwhat", true)) {
757 if ($transient_status == 'finished') {
758 $backup_array[$youwhat] = $backup_file_basename.'-'.$youwhat.'.zip';
759 } else {
760 $created = $this->create_zip($whichdir, $youwhat, $updraft_dir, $backup_file_basename);
761 if ($created) $backup_array[$youwhat] = $created;
762 }
763 } else {
764 $this->log("No backup of $youwhat: excluded by user's options");
765 }
766 }
767
768 # Others
769 if (get_option('updraft_include_others', true)) {
770 $this->log("Beginning backup of other directories found in the content directory");
771
772 if ($transient_status == 'finished') {
773 $backup_array['others'] = $backup_file_basename.'-others.zip';
774 } else {
775
776 // http://www.phpconcept.net/pclzip/user-guide/53
777 /* First parameter to create is:
778 An array of filenames or dirnames,
779 or
780 A string containing the filename or a dirname,
781 or
782 A string containing a list of filename or dirname separated by a comma.
783 */
784
785 # Initialise
786 $other_dirlist = array();
787
788 $others_skip = preg_split("/,/",get_option('updraft_include_others_exclude', UPDRAFT_DEFAULT_OTHERS_EXCLUDE));
789 # Make the values into the keys
790 $others_skip = array_flip($others_skip);
791
792 $this->log('Looking for candidates to back up in: '.WP_CONTENT_DIR);
793 if ($handle = opendir(WP_CONTENT_DIR)) {
794 while (false !== ($entry = readdir($handle))) {
795 $candidate = WP_CONTENT_DIR.'/'.$entry;
796 if ($entry == "." || $entry == "..") { ; }
797 elseif ($candidate == $updraft_dir) { $this->log("$entry: skipping: this is the updraft directory"); }
798 elseif ($candidate == $wp_themes_dir) { $this->log("$entry: skipping: this is the themes directory"); }
799 elseif ($candidate == $wp_upload_dir) { $this->log("$entry: skipping: this is the uploads directory"); }
800 elseif ($candidate == $wp_plugins_dir) { $this->log("$entry: skipping: this is the plugins directory"); }
801 elseif (isset($others_skip[$entry])) { $this->log("$entry: skipping: excluded by options"); }
802 else { $this->log("$entry: adding to list"); array_push($other_dirlist, $candidate); }
803 }
804 } else {
805 $this->log('ERROR: Could not read the content directory: '.WP_CONTENT_DIR);
806 $this->error('Could not read the content directory: '.WP_CONTENT_DIR);
807 }
808
809 if (count($other_dirlist)>0) {
810 $created = $this->create_zip($other_dirlist, 'others', $updraft_dir, $backup_file_basename);
811 if ($created) $backup_array['others'] = $created;
812 } else {
813 $this->log("No backup of other directories: there was nothing found to back up");
814 }
815 # If we are not already finished
816 }
817 } else {
818 $this->log("No backup of other directories: excluded by user's options");
819 }
820 return $backup_array;
821 }
822
823 function save_backup_history($backup_array) {
824 if(is_array($backup_array)) {
825 $backup_history = get_option('updraft_backup_history');
826 $backup_history = (is_array($backup_history)) ? $backup_history : array();
827 $backup_array['nonce'] = $this->nonce;
828 $backup_history[$this->backup_time] = $backup_array;
829 update_option('updraft_backup_history',$backup_history);
830 } else {
831 $this->log('Could not save backup history because we have no backup array. Backup probably failed.');
832 $this->error('Could not save backup history because we have no backup array. Backup probably failed.');
833 }
834 }
835
836 function get_backup_history() {
837 //$backup_history = get_option('updraft_backup_history');
838 //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
839 global $wpdb;
840 $backup_history = @unserialize($wpdb->get_var($wpdb->prepare("SELECT option_value from $wpdb->options WHERE option_name='updraft_backup_history'")));
841 if(is_array($backup_history)) {
842 krsort($backup_history); //reverse sort so earliest backup is last on the array. this way we can array_pop
843 } else {
844 $backup_history = array();
845 }
846 return $backup_history;
847 }
848
849 // Open a file, store its filehandle
850 function backup_db_open($file, $allow_gz = true) {
851 if (function_exists('gzopen') && $allow_gz == true) {
852 $this->dbhandle = @gzopen($file, 'w');
853 $this->dbhandle_isgz = true;
854 } else {
855 $this->dbhandle = @fopen($file, 'w');
856 $this->dbhandle_isgz = false;
857 }
858 if(!$this->dbhandle) {
859 //$this->error(__('Could not open the backup file for writing!','wp-db-backup'));
860 }
861 }
862
863 function backup_db_header() {
864
865 //Begin new backup of MySql
866 $this->stow("# " . 'WordPress MySQL database backup' . "\n");
867 $this->stow("#\n");
868 $this->stow("# " . sprintf(__('Generated: %s','wp-db-backup'),date("l j. F Y H:i T")) . "\n");
869 $this->stow("# " . sprintf(__('Hostname: %s','wp-db-backup'),DB_HOST) . "\n");
870 $this->stow("# " . sprintf(__('Database: %s','wp-db-backup'),$this->backquote(DB_NAME)) . "\n");
871 $this->stow("# --------------------------------------------------------\n");
872
873 if (defined("DB_CHARSET")) {
874 $this->stow("/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n");
875 $this->stow("/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n");
876 $this->stow("/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n");
877 $this->stow("/*!40101 SET NAMES " . DB_CHARSET . " */;\n");
878 }
879 $this->stow("/*!40101 SET foreign_key_checks = 0 */;\n");
880 }
881
882 /* This function is resumable, using the following method:
883 - Each table is written out to ($final_filename).table.tmp
884 - When the writing finishes, it is renamed to ($final_filename).table
885 - When all tables are finished, they are concatenated into the final file
886 */
887 function backup_db($already_done = "begun") {
888
889 // Get the file prefix
890 $updraft_dir = $this->backups_dir_location();
891
892 // Get the blog name and rip out all non-alphanumeric chars other than _
893 $blog_name = preg_replace('/[^A-Za-z0-9_]/','', str_replace(' ','_', get_bloginfo()));
894 if (!$blog_name) $blog_name = 'non_alpha_name';
895 $file_base = 'backup_'.date('Y-m-d-Hi',$this->backup_time).'_'.$blog_name.'_'.$this->nonce;
896 $backup_file_base = $updraft_dir.'/'.$file_base;
897
898 if ("finished" == $already_done) return basename($backup_file_base.'-db.gz');
899
900 $total_tables = 0;
901
902 global $table_prefix, $wpdb;
903 if(!$this->backup_time) $this->backup_time_nonce();
904
905 $all_tables = $wpdb->get_results("SHOW TABLES", ARRAY_N);
906 $all_tables = array_map(create_function('$a', 'return $a[0];'), $all_tables);
907
908
909
910 if (!is_writable($updraft_dir)) {
911 $this->log('The backup directory is not writable.');
912 $this->error('The backup directory is not writable.');
913 return false;
914 }
915
916 $stitch_files = array();
917
918 foreach ($all_tables as $table) {
919 $total_tables++;
920 // Increase script execution time-limit to 15 min for every table.
921 if ( !ini_get('safe_mode') || strtolower(ini_get('safe_mode')) == "off") @set_time_limit(15*60);
922 // The table file may already exist if we have produced it on a previous run
923 $table_file_prefix = $file_base.'-db-table-'.$table.'.table';
924 if (file_exists($updraft_dir.'/'.$table_file_prefix.'.gz')) {
925 $this->log("Table $table: corresponding file already exists; moving on");
926 } else {
927 // Open file, store the handle
928 $this->backup_db_open($updraft_dir.'/'.$table_file_prefix.'.tmp.gz', true);
929 # === is needed, otherwise 'false' matches (i.e. prefix does not match)
930 if ( strpos($table, $table_prefix) === 0 ) {
931 // Create the SQL statements
932 $this->stow("# --------------------------------------------------------\n");
933 $this->stow("# " . sprintf(__('Table: %s','wp-db-backup'),$this->backquote($table)) . "\n");
934 $this->stow("# --------------------------------------------------------\n");
935 $this->backup_table($table);
936 } else {
937 $this->stow("# --------------------------------------------------------\n");
938 $this->stow("# " . sprintf(__('Skipping non-WP table: %s','wp-db-backup'),$this->backquote($table)) . "\n");
939 $this->stow("# --------------------------------------------------------\n");
940 }
941 // Close file
942 $this->close($this->dbhandle);
943 $this->log("Table $table: finishing file (${table_file_prefix}.gz)");
944 rename($updraft_dir.'/'.$table_file_prefix.'.tmp.gz', $updraft_dir.'/'.$table_file_prefix.'.gz');
945 }
946 $stitch_files[] = $table_file_prefix;
947 }
948
949 // Finally, stitch the files together
950 $this->backup_db_open($backup_file_base.'-db.gz', true);
951 $this->backup_db_header();
952
953 foreach ($stitch_files as $table_file) {
954 $this->log("{$table_file}.gz: adding to final database dump");
955 $handle = gzopen($updraft_dir.'/'.$table_file.'.gz', "r");
956 while (!feof($handle)) { $this->stow(gzread($handle, 65536)); }
957 gzclose($handle);
958 @unlink($updraft_dir.'/'.$table_file.'.gz');
959 }
960
961 if (defined("DB_CHARSET")) {
962 $this->stow("/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n");
963 $this->stow("/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n");
964 $this->stow("/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n");
965 }
966
967 $this->log($file_base.'-db.gz: finished writing out complete database file');
968 $this->close($this->dbhandle);
969
970 if (count($this->errors)) {
971 return false;
972 } else {
973 # We no longer encrypt here - because the operation can take long, we made it resumable and moved it to the upload loop
974 $this->log("Total database tables backed up: $total_tables");
975 return basename($backup_file_base.'-db.gz');
976 }
977
978 } //wp_db_backup
979
980 /**
981 * Taken partially from phpMyAdmin and partially from
982 * Alain Wolf, Zurich - Switzerland
983 * Website: http://restkultur.ch/personal/wolf/scripts/db_backup/
984 * Modified by Scott Merrill (http://www.skippy.net/)
985 * to use the WordPress $wpdb object
986 * @param string $table
987 * @param string $segment
988 * @return void
989 */
990 function backup_table($table, $segment = 'none') {
991 global $wpdb;
992
993 $microtime = microtime(true);
994
995 $total_rows = 0;
996
997 $table_structure = $wpdb->get_results("DESCRIBE $table");
998 if (! $table_structure) {
999 //$this->error(__('Error getting table details','wp-db-backup') . ": $table");
1000 return false;
1001 }
1002
1003 if(($segment == 'none') || ($segment == 0)) {
1004 // Add SQL statement to drop existing table
1005 $this->stow("\n\n");
1006 $this->stow("#\n");
1007 $this->stow("# " . sprintf(__('Delete any existing table %s','wp-db-backup'),$this->backquote($table)) . "\n");
1008 $this->stow("#\n");
1009 $this->stow("\n");
1010 $this->stow("DROP TABLE IF EXISTS " . $this->backquote($table) . ";\n");
1011
1012 // Table structure
1013 // Comment in SQL-file
1014 $this->stow("\n\n");
1015 $this->stow("#\n");
1016 $this->stow("# " . sprintf(__('Table structure of table %s','wp-db-backup'),$this->backquote($table)) . "\n");
1017 $this->stow("#\n");
1018 $this->stow("\n");
1019
1020 $create_table = $wpdb->get_results("SHOW CREATE TABLE $table", ARRAY_N);
1021 if (false === $create_table) {
1022 $err_msg = sprintf(__('Error with SHOW CREATE TABLE for %s.','wp-db-backup'), $table);
1023 //$this->error($err_msg);
1024 $this->stow("#\n# $err_msg\n#\n");
1025 }
1026 $this->stow($create_table[0][1] . ' ;');
1027
1028 if (false === $table_structure) {
1029 $err_msg = sprintf(__('Error getting table structure of %s','wp-db-backup'), $table);
1030 //$this->error($err_msg);
1031 $this->stow("#\n# $err_msg\n#\n");
1032 }
1033
1034 // Comment in SQL-file
1035 $this->stow("\n\n");
1036 $this->stow("#\n");
1037 $this->stow('# ' . sprintf(__('Data contents of table %s','wp-db-backup'),$this->backquote($table)) . "\n");
1038 $this->stow("#\n");
1039 }
1040
1041 // In UpdraftPlus, segment is always 'none'
1042 if(($segment == 'none') || ($segment >= 0)) {
1043 $defs = array();
1044 $integer_fields = array();
1045 // $table_structure was from "DESCRIBE $table"
1046 foreach ($table_structure as $struct) {
1047 if ( (0 === strpos($struct->Type, 'tinyint')) || (0 === strpos(strtolower($struct->Type), 'smallint')) ||
1048 (0 === strpos(strtolower($struct->Type), 'mediumint')) || (0 === strpos(strtolower($struct->Type), 'int')) || (0 === strpos(strtolower($struct->Type), 'bigint')) ) {
1049 $defs[strtolower($struct->Field)] = ( null === $struct->Default ) ? 'NULL' : $struct->Default;
1050 $integer_fields[strtolower($struct->Field)] = "1";
1051 }
1052 }
1053
1054 // Batch by $row_inc
1055 if ( ! defined('ROWS_PER_SEGMENT') ) define('ROWS_PER_SEGMENT', 100);
1056
1057 if($segment == 'none') {
1058 $row_start = 0;
1059 $row_inc = ROWS_PER_SEGMENT;
1060 } else {
1061 $row_start = $segment * ROWS_PER_SEGMENT;
1062 $row_inc = ROWS_PER_SEGMENT;
1063 }
1064 do {
1065
1066 if ( !ini_get('safe_mode') || strtolower(ini_get('safe_mode')) == "off") @set_time_limit(15*60);
1067 $table_data = $wpdb->get_results("SELECT * FROM $table LIMIT {$row_start}, {$row_inc}", ARRAY_A);
1068 $entries = 'INSERT INTO ' . $this->backquote($table) . ' VALUES (';
1069 // \x08\\x09, not required
1070 $search = array("\x00", "\x0a", "\x0d", "\x1a");
1071 $replace = array('\0', '\n', '\r', '\Z');
1072 if($table_data) {
1073 foreach ($table_data as $row) {
1074 $total_rows++;
1075 $values = array();
1076 foreach ($row as $key => $value) {
1077 if (isset($integer_fields[strtolower($key)])) {
1078 // make sure there are no blank spots in the insert syntax,
1079 // yet try to avoid quotation marks around integers
1080 $value = ( null === $value || '' === $value) ? $defs[strtolower($key)] : $value;
1081 $values[] = ( '' === $value ) ? "''" : $value;
1082 } else {
1083 $values[] = "'" . str_replace($search, $replace, str_replace('\'', '\\\'', str_replace('\\', '\\\\', $value))) . "'";
1084 }
1085 }
1086 $this->stow(" \n" . $entries . implode(', ', $values) . ');');
1087 }
1088 $row_start += $row_inc;
1089 }
1090 } while((count($table_data) > 0) and ($segment=='none'));
1091 }
1092
1093 if(($segment == 'none') || ($segment < 0)) {
1094 // Create footer/closing comment in SQL-file
1095 $this->stow("\n");
1096 $this->stow("#\n");
1097 $this->stow("# " . sprintf(__('End of data contents of table %s','wp-db-backup'),$this->backquote($table)) . "\n");
1098 $this->stow("# --------------------------------------------------------\n");
1099 $this->stow("\n");
1100 }
1101 $this->log("Table $table: Total rows added: $total_rows in ".sprintf("%.02f",microtime(true)-$microtime)." seconds");
1102
1103 } // end backup_table()
1104
1105 function stow($query_line) {
1106 if ($this->dbhandle_isgz) {
1107 if(! @gzwrite($this->dbhandle, $query_line)) {
1108 //$this->error(__('There was an error writing a line to the backup script:','wp-db-backup') . ' ' . $query_line . ' ' . $php_errormsg);
1109 }
1110 } else {
1111 if(false === @fwrite($this->dbhandle, $query_line)) {
1112 //$this->error(__('There was an error writing a line to the backup script:','wp-db-backup') . ' ' . $query_line . ' ' . $php_errormsg);
1113 }
1114 }
1115 }
1116
1117 function close($handle) {
1118 if ($this->dbhandle_isgz) {
1119 gzclose($handle);
1120 } else {
1121 fclose($handle);
1122 }
1123 }
1124
1125 function error($error) {
1126 if (count($this->errors) == 0) $this->log("An error condition has occurred for the first time on this run");
1127 $this->errors[] = $error;
1128 return true;
1129 }
1130
1131 /**
1132 * Add backquotes to tables and db-names in
1133 * SQL queries. Taken from phpMyAdmin.
1134 */
1135 function backquote($a_name) {
1136 if (!empty($a_name) && $a_name != '*') {
1137 if (is_array($a_name)) {
1138 $result = array();
1139 reset($a_name);
1140 while(list($key, $val) = each($a_name))
1141 $result[$key] = '`' . $val . '`';
1142 return $result;
1143 } else {
1144 return '`' . $a_name . '`';
1145 }
1146 } else {
1147 return $a_name;
1148 }
1149 }
1150
1151 /*END OF WP-DB-BACKUP BLOCK */
1152
1153 /*
1154 this function is both the backup scheduler and ostensibly a filter callback for saving the option.
1155 it is called in the register_setting for the updraft_interval, which means when the admin settings
1156 are saved it is called. it returns the actual result from wp_filter_nohtml_kses (a sanitization filter)
1157 so the option can be properly saved.
1158 */
1159 function schedule_backup($interval) {
1160 //clear schedule and add new so we don't stack up scheduled backups
1161 wp_clear_scheduled_hook('updraft_backup');
1162 switch($interval) {
1163 case 'every4hours':
1164 case 'every8hours':
1165 case 'twicedaily':
1166 case 'daily':
1167 case 'weekly':
1168 case 'fortnightly':
1169 case 'monthly':
1170 wp_schedule_event(time()+30, $interval, 'updraft_backup');
1171 break;
1172 }
1173 return wp_filter_nohtml_kses($interval);
1174 }
1175
1176 function schedule_backup_database($interval) {
1177 //clear schedule and add new so we don't stack up scheduled backups
1178 wp_clear_scheduled_hook('updraft_backup_database');
1179 switch($interval) {
1180 case 'every4hours':
1181 case 'every8hours':
1182 case 'twicedaily':
1183 case 'daily':
1184 case 'weekly':
1185 case 'fortnightly':
1186 case 'monthly':
1187 wp_schedule_event(time()+30, $interval, 'updraft_backup_database');
1188 break;
1189 }
1190 return wp_filter_nohtml_kses($interval);
1191 }
1192
1193 //wp-cron only has hourly, daily and twicedaily, so we need to add some of our own
1194 function modify_cron_schedules($schedules) {
1195 $schedules['weekly'] = array( 'interval' => 604800, 'display' => 'Once Weekly' );
1196 $schedules['fortnightly'] = array( 'interval' => 1209600, 'display' => 'Once Each Fortnight' );
1197 $schedules['monthly'] = array( 'interval' => 2592000, 'display' => 'Once Monthly' );
1198 $schedules['every4hours'] = array( 'interval' => 14400, 'display' => 'Every 4 hours' );
1199 $schedules['every8hours'] = array( 'interval' => 28800, 'display' => 'Every 8 hours' );
1200 return $schedules;
1201 }
1202
1203 function backups_dir_location() {
1204 $updraft_dir = untrailingslashit(get_option('updraft_dir'));
1205 $default_backup_dir = WP_CONTENT_DIR.'/updraft';
1206 //if the option isn't set, default it to /backups inside the upload dir
1207 $updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir;
1208 //check for the existence of the dir and an enumeration preventer.
1209 if(!is_dir($updraft_dir) || !is_file($updraft_dir.'/index.html') || !is_file($updraft_dir.'/.htaccess')) {
1210 @mkdir($updraft_dir,0777,true); //recursively create the dir with 0777 permissions. 0777 is default for php creation. not ideal, but I'll get back to this
1211 @file_put_contents($updraft_dir.'/index.html','Nothing to see here.');
1212 @file_put_contents($updraft_dir.'/.htaccess','deny from all');
1213 }
1214 return $updraft_dir;
1215 }
1216
1217 // Called via AJAX
1218 function updraft_ajax_handler() {
1219 // Test the nonce (probably not needed, since we're presumably admin-authed, but there's no harm)
1220 $nonce = (empty($_REQUEST['nonce'])) ? "" : $_REQUEST['nonce'];
1221 if (! wp_verify_nonce($nonce, 'updraftplus-credentialtest-nonce') || empty($_REQUEST['subaction'])) die('Security check');
1222
1223 if ('lastlog' == $_GET['subaction']) {
1224 echo htmlspecialchars(get_option('updraft_lastmessage', '(Nothing yet logged)'));
1225 } elseif ($_POST['subaction'] == 'credentials_test') {
1226 $method = (preg_match("/^[a-z0-9]+$/", $_POST['method'])) ? $_POST['method'] : "";
1227
1228 // Test the credentials, return a code
1229 require_once(UPDRAFTPLUS_DIR."/methods/$method.php");
1230
1231 $objname = "UpdraftPlus_BackupModule_${method}";
1232 if (method_exists($objname, "credentials_test")) call_user_func(array('UpdraftPlus_BackupModule_'.$method, 'credentials_test'));
1233 }
1234
1235 die;
1236
1237 }
1238
1239 function updraft_download_backup() {
1240 $type = $_POST['type'];
1241 $timestamp = (int)$_POST['timestamp'];
1242 $backup_history = $this->get_backup_history();
1243 $file = $backup_history[$timestamp][$type];
1244 $fullpath = trailingslashit(get_option('updraft_dir')).$file;
1245 if(!is_readable($fullpath)) {
1246 //if the file doesn't exist and they're using one of the cloud options, fetch it down from the cloud.
1247 $this->download_backup($file);
1248 }
1249 if(@is_readable($fullpath) && is_file($fullpath)) {
1250 $len = filesize($fullpath);
1251
1252 $filearr = explode('.',$file);
1253 // //we've only got zip and gz...for now
1254 $file_ext = array_pop($filearr);
1255 if($file_ext == 'zip') {
1256 header('Content-type: application/zip');
1257 } else {
1258 // This catches both when what was popped was 'crypt' (*-db.gz.crypt) and when it was 'gz' (unencrypted)
1259 header('Content-type: application/x-gzip');
1260 }
1261 header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
1262 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
1263 header("Content-Length: $len;");
1264 if ($file_ext == 'crypt') {
1265 header("Content-Disposition: attachment; filename=\"".substr($file,0,-6)."\";");
1266 } else {
1267 header("Content-Disposition: attachment; filename=\"$file\";");
1268 }
1269 ob_end_flush();
1270 if ($file_ext == 'crypt') {
1271 $encryption = get_option('updraft_encryptionphrase');
1272 if ($encryption == "") {
1273 $this->error('Decryption of database failed: the database file is encrypted, but you have no encryption key entered.');
1274 } else {
1275 require_once(dirname(__FILE__).'/includes/Rijndael.php');
1276 $rijndael = new Crypt_Rijndael();
1277 $rijndael->setKey($encryption);
1278 $in_handle = fopen($fullpath,'r');
1279 $ciphertext = "";
1280 while (!feof ($in_handle)) {
1281 $ciphertext .= fread($in_handle, 16384);
1282 }
1283 fclose ($in_handle);
1284 print $rijndael->decrypt($ciphertext);
1285 }
1286 } else {
1287 readfile($fullpath);
1288 }
1289 $this->delete_local($file);
1290 exit; //we exit immediately because otherwise admin-ajax appends an additional zero to the end
1291 } else {
1292 echo 'Download failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then remote retrieval may have failed.';
1293 }
1294 }
1295
1296 function download_backup($file) {
1297 $service = get_option('updraft_service');
1298
1299 $method_include = UPDRAFTPLUS_DIR.'/methods/'.$service.'.php';
1300 if (file_exists($method_include)) require_once($method_include);
1301
1302 $objname = "UpdraftPlus_BackupModule_${service}";
1303 if (method_exists($objname, "download")) {
1304 $remote_obj = new $objname;
1305 $remote_obj->download($file);
1306 } else {
1307 $this->error('Automatic backup restoration is not available with the method: $service.');
1308 }
1309
1310 }
1311
1312 function restore_backup($timestamp) {
1313 global $wp_filesystem;
1314 $backup_history = get_option('updraft_backup_history');
1315 if(!is_array($backup_history[$timestamp])) {
1316 echo '<p>This backup does not exist in the backup history - restoration aborted. Timestamp: '.$timestamp.'</p><br/>';
1317 return false;
1318 }
1319
1320 $credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_restore&backup_timestamp=$timestamp");
1321 WP_Filesystem($credentials);
1322 if ( $wp_filesystem->errors->get_error_code() ) {
1323 foreach ( $wp_filesystem->errors->get_error_messages() as $message )
1324 show_message($message);
1325 exit;
1326 }
1327
1328 //if we make it this far then WP_Filesystem has been instantiated and is functional (tested with ftpext, what about suPHP and other situations where direct may work?)
1329 echo '<span style="font-weight:bold">Restoration Progress </span><div id="updraft-restore-progress">';
1330
1331 $updraft_dir = trailingslashit(get_option('updraft_dir'));
1332 foreach($backup_history[$timestamp] as $type=>$file) {
1333 $fullpath = $updraft_dir.$file;
1334 if(!is_readable($fullpath) && $type != 'db') {
1335 $this->download_backup($file);
1336 }
1337 # Types: uploads, themes, plugins, others, db
1338 if(is_readable($fullpath) && $type != 'db') {
1339 if(!class_exists('WP_Upgrader')) {
1340 require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
1341 }
1342 require_once(UPDRAFTPLUS_DIR.'/includes/updraft-restorer.php');
1343 $restorer = new Updraft_Restorer();
1344 $val = $restorer->restore_backup($fullpath,$type);
1345 if(is_wp_error($val)) {
1346 print_r($val);
1347 echo '</div>'; //close the updraft_restore_progress div even if we error
1348 return false;
1349 }
1350 }
1351 }
1352 echo '</div>'; //close the updraft_restore_progress div
1353 # The 'off' check is for badly configured setups - http://wordpress.org/support/topic/plugin-wp-super-cache-warning-php-safe-mode-enabled-but-safe-mode-is-off
1354 if(ini_get('safe_mode') && strtolower(ini_get('safe_mode')) != "off") {
1355 echo "<p>DB could not be restored because PHP safe_mode is active on your server. You will need to manually restore the file via phpMyAdmin or another method.</p><br/>";
1356 return false;
1357 }
1358 return true;
1359 }
1360
1361 //deletes the -old directories that are created when a backup is restored.
1362 function delete_old_dirs() {
1363 global $wp_filesystem;
1364 $credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_delete_old_dirs");
1365 WP_Filesystem($credentials);
1366 if ( $wp_filesystem->errors->get_error_code() ) {
1367 foreach ( $wp_filesystem->errors->get_error_messages() as $message )
1368 show_message($message);
1369 exit;
1370 }
1371
1372 $to_delete = array('themes-old','plugins-old','uploads-old','others-old');
1373
1374 foreach($to_delete as $name) {
1375 //recursively delete
1376 if(!$wp_filesystem->delete(WP_CONTENT_DIR.'/'.$name, true)) {
1377 return false;
1378 }
1379 }
1380 return true;
1381 }
1382
1383 //scans the content dir to see if any -old dirs are present
1384 function scan_old_dirs() {
1385 $dirArr = scandir(WP_CONTENT_DIR);
1386 foreach($dirArr as $dir) {
1387 if(strpos($dir,'-old') !== false) {
1388 return true;
1389 }
1390 }
1391 return false;
1392 }
1393
1394
1395 function retain_range($input) {
1396 $input = (int)$input;
1397 if($input > 0 && $input < 3650) {
1398 return $input;
1399 } else {
1400 return 1;
1401 }
1402 }
1403
1404 function create_backup_dir() {
1405 global $wp_filesystem;
1406 $credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_create_backup_dir");
1407 WP_Filesystem($credentials);
1408 if ( $wp_filesystem->errors->get_error_code() ) {
1409 foreach ( $wp_filesystem->errors->get_error_messages() as $message ) show_message($message);
1410 exit;
1411 }
1412
1413 $updraft_dir = untrailingslashit(get_option('updraft_dir'));
1414 $default_backup_dir = WP_CONTENT_DIR.'/updraft';
1415 $updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir;
1416
1417 //chmod the backup dir to 0777. ideally we'd rather chgrp it but i'm not sure if it's possible to detect the group apache is running under (or what if it's not apache...)
1418 if(!$wp_filesystem->mkdir($updraft_dir, 0777)) return false;
1419
1420 return true;
1421 }
1422
1423 function memory_check_current() {
1424 # Returns in megabytes
1425 $memory_limit = ini_get('memory_limit');
1426 $memory_unit = $memory_limit[strlen($memory_limit)-1];
1427 $memory_limit = substr($memory_limit,0,strlen($memory_limit)-1);
1428 switch($memory_unit) {
1429 case 'K':
1430 $memory_limit = $memory_limit/1024;
1431 break;
1432 case 'G':
1433 $memory_limit = $memory_limit*1024;
1434 break;
1435 case 'M':
1436 //assumed size, no change needed
1437 break;
1438 }
1439 return $memory_limit;
1440 }
1441
1442 function memory_check($memory) {
1443 $memory_limit = $this->memory_check_current();
1444 return ($memory_limit >= $memory)?true:false;
1445 }
1446
1447 function execution_time_check($time) {
1448 return (ini_get('max_execution_time') >= $time)?true:false;
1449 }
1450
1451 function register_settings() {
1452 register_setting( 'updraft-options-group', 'updraft_interval', array($this,'schedule_backup') );
1453 register_setting( 'updraft-options-group', 'updraft_interval_database', array($this,'schedule_backup_database') );
1454 register_setting( 'updraft-options-group', 'updraft_retain', array($this,'retain_range') );
1455 register_setting( 'updraft-options-group', 'updraft_encryptionphrase', 'wp_filter_nohtml_kses' );
1456 register_setting( 'updraft-options-group', 'updraft_service', 'wp_filter_nohtml_kses' );
1457
1458 register_setting( 'updraft-options-group', 'updraft_s3_login' );
1459 register_setting( 'updraft-options-group', 'updraft_s3_pass' );
1460 register_setting( 'updraft-options-group', 'updraft_s3_remote_path', 'wp_filter_nohtml_kses' );
1461
1462 register_setting( 'updraft-options-group', 'updraft_dropbox_appkey' );
1463 register_setting( 'updraft-options-group', 'updraft_dropbox_secret' );
1464 register_setting( 'updraft-options-group', 'updraft_dropbox_folder' );
1465
1466 register_setting( 'updraft-options-group', 'updraft_googledrive_clientid', 'wp_filter_nohtml_kses' );
1467 register_setting( 'updraft-options-group', 'updraft_googledrive_secret' );
1468 register_setting( 'updraft-options-group', 'updraft_googledrive_remotepath', 'wp_filter_nohtml_kses' );
1469 register_setting( 'updraft-options-group', 'updraft_ftp_login' );
1470 register_setting( 'updraft-options-group', 'updraft_ftp_pass' );
1471 register_setting( 'updraft-options-group', 'updraft_ftp_remote_path' );
1472 register_setting( 'updraft-options-group', 'updraft_server_address', 'wp_filter_nohtml_kses' );
1473 register_setting( 'updraft-options-group', 'updraft_dir' );
1474 register_setting( 'updraft-options-group', 'updraft_email', 'wp_filter_nohtml_kses' );
1475 register_setting( 'updraft-options-group', 'updraft_delete_local', 'absint' );
1476 register_setting( 'updraft-options-group', 'updraft_debug_mode', 'absint' );
1477 register_setting( 'updraft-options-group', 'updraft_include_plugins', 'absint' );
1478 register_setting( 'updraft-options-group', 'updraft_include_themes', 'absint' );
1479 register_setting( 'updraft-options-group', 'updraft_include_uploads', 'absint' );
1480 register_setting( 'updraft-options-group', 'updraft_include_others', 'absint' );
1481 register_setting( 'updraft-options-group', 'updraft_include_others_exclude', 'wp_filter_nohtml_kses' );
1482 }
1483
1484 function admin_init() {
1485 if(get_option('updraft_debug_mode')) {
1486 ini_set('display_errors',1);
1487 error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
1488 ini_set('track_errors',1);
1489 }
1490 wp_enqueue_script('jquery');
1491
1492 $this->register_settings();
1493
1494 if (current_user_can('manage_options') && get_option('updraft_service') == "googledrive" && get_option('updraft_googledrive_clientid') != "" && get_option('updraft_googledrive_token','xyz') == 'xyz') {
1495 add_action('admin_notices', array($this,'show_admin_warning_googledrive') );
1496 }
1497
1498 if (current_user_can('manage_options') && get_option('updraft_service') == "dropbox" && get_option('updraft_dropboxtk_request_token','xyz') == 'xyz') {
1499 add_action('admin_notices', array($this,'show_admin_warning_dropbox') );
1500 }
1501 }
1502
1503 function ajax_enqueue() {
1504 // wp_enqueue_script('updraftplus-ajax', plugins_url('/includes/ajax.js', __FILE__) );
1505 // wp_localize_script('updraftplus-ajax', 'updraft_credentials_test', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
1506 }
1507
1508 function add_admin_pages() {
1509 add_submenu_page('options-general.php', "UpdraftPlus", "UpdraftPlus", "manage_options", "updraftplus",
1510 array($this,"settings_output"));
1511 }
1512
1513 function url_start($urls,$url) {
1514 return ($urls) ? '<a href="http://'.$url.'">' : "";
1515 }
1516
1517 function url_end($urls,$url) {
1518 return ($urls) ? '</a>' : " (http://$url)";
1519 }
1520
1521 function wordshell_random_advert($urls) {
1522 $rad = rand(0,5);
1523 switch ($rad) {
1524 case 0:
1525 return "Like automating WordPress operations? Use the CLI? ".$this->url_start($urls,'wordshell.net')."You will love WordShell".$this->url_end($urls,'www.wordshell.net')." - saves time and money fast.";
1526 break;
1527 case 1:
1528 return "Find UpdraftPlus useful? ".$this->url_start($urls,'david.dw-perspective.org.uk/donate')."Please make a donation.".$this->url_end($urls,'david.dw-perspective.org.uk/donate');
1529 case 2:
1530 return $this->url_start($urls,'wordshell.net')."Check out WordShell".$this->url_end($urls,'www.wordshell.net')." - manage WordPress from the command line - huge time-saver";
1531 break;
1532 case 3:
1533 return "Want some more useful plugins? ".$this->url_start($urls,'profiles.wordpress.org/DavidAnderson/')."See my WordPress profile page for others.".$this->url_end($urls,'profiles.wordpress.org/DavidAnderson/');
1534 break;
1535 case 4:
1536 return $this->url_start($urls,'www.simbahosting.co.uk')."Need high-quality WordPress hosting from WordPress specialists? (Including automatic backups and 1-click installer). Get it from the creators of UpdraftPlus.".$this->url_end($urls,'www.simbahosting.co.uk');
1537 break;
1538 case 5:
1539 return "Need custom WordPress services from experts (including bespoke development)?".$this->url_start($urls,'www.simbahosting.co.uk/s3/products-and-services/wordpress-experts/')." Get them from the creators of UpdraftPlus.".$this->url_end($urls,'www.simbahosting.co.uk/s3/products-and-services/wordpress-experts/');
1540 break;
1541 }
1542 }
1543
1544 function settings_output() {
1545
1546 /*
1547 we use request here because the initial restore is triggered by a POSTed form. we then may need to obtain credentials
1548 for the WP_Filesystem. to do this WP outputs a form that we can't insert variables into (apparently). So the values are
1549 passed back in as GET parameters. REQUEST covers both GET and POST so this weird logic works.
1550 */
1551 if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'updraft_restore' && isset($_REQUEST['backup_timestamp'])) {
1552 $backup_success = $this->restore_backup($_REQUEST['backup_timestamp']);
1553 if(empty($this->errors) && $backup_success == true) {
1554 echo '<p>Restore successful!</p><br/>';
1555 echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus&updraft_restore_success=true">Return to Updraft Configuration</a>.';
1556 return;
1557 } else {
1558 echo '<p>Restore failed...</p><ul>';
1559 foreach ($this->errors as $err) {
1560 echo "<li>";
1561 if (is_string($err)) { echo htmlspecialchars($err); } else {
1562 print_r($err);
1563 }
1564 echo "</li>";
1565 }
1566 echo '</ul><b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.';
1567 return;
1568 }
1569 //uncomment the below once i figure out how i want the flow of a restoration to work.
1570 //echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.';
1571 }
1572 $deleted_old_dirs = false;
1573 if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'updraft_delete_old_dirs') {
1574 if($this->delete_old_dirs()) {
1575 $deleted_old_dirs = true;
1576 } else {
1577 echo '<p>Old directory removal failed for some reason. You may want to do this manually.</p><br/>';
1578 }
1579 echo '<p>Old directories successfully removed.</p><br/>';
1580 echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.';
1581 return;
1582 }
1583
1584 if(isset($_GET['error'])) {
1585 echo "<p><strong>ERROR:</strong> ".htmlspecialchars($_GET['error'])."</p>";
1586 }
1587 if(isset($_GET['message'])) {
1588 echo "<p><strong>Note:</strong> ".htmlspecialchars($_GET['message'])."</p>";
1589 }
1590
1591 if(isset($_GET['action']) && $_GET['action'] == 'updraft_create_backup_dir') {
1592 if(!$this->create_backup_dir()) {
1593 echo '<p>Backup directory could not be created...</p><br/>';
1594 }
1595 echo '<p>Backup directory successfully created.</p><br/>';
1596 echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.';
1597 return;
1598 }
1599
1600 if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') {
1601 echo '<div class="updated fade" style="max-width: 800px; font-size:140%; line-height: 140%; padding:14px; clear:left;"><strong>Schedule backup:</strong> ';
1602 if (wp_schedule_single_event(time()+5, 'updraft_backup_all') === false) {
1603 $this->log("A backup run failed to schedule");
1604 echo "Failed.";
1605 } else {
1606 echo "OK. Now load any page from your site to make sure the schedule can trigger.";
1607 $this->log("A backup run has been scheduled");
1608 }
1609 echo '</div>';
1610 }
1611
1612 if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') { $this->backup(true,true); }
1613 elseif (isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_db') { $this->backup_db(); }
1614 elseif (isset($_POST['action']) && $_POST['action'] == 'updraft_wipesettings') {
1615 $settings = array('updraft_interval', 'updraft_interval_database', 'updraft_retain', 'updraft_encryptionphrase', 'updraft_service', 'updraft_s3_login', 'updraft_s3_pass', 'updraft_s3_remote_path', 'updraft_dropbox_appkey', 'updraft_dropbox_secret', 'updraft_dropbox_folder', 'updraft_googledrive_clientid', 'updraft_googledrive_secret', 'updraft_googledrive_remotepath', 'updraft_ftp_login', 'updraft_ftp_pass', 'updraft_ftp_remote_path', 'updraft_server_address', 'updraft_dir', 'updraft_email', 'updraft_delete_local', 'updraft_debug_mode', 'updraft_include_plugins', 'updraft_include_themes', 'updraft_include_uploads', 'updraft_include_others', 'updraft_include_others_exclude', 'updraft_lastmessage');
1616 foreach ($settings as $s) {
1617 delete_option($s);
1618 }
1619 $this->show_admin_warning("Your settings have been wiped.");
1620 }
1621
1622 ?>
1623 <div class="wrap">
1624 <h1>UpdraftPlus - Backup/Restore</h1>
1625
1626 Maintained by <b>David Anderson</b> (<a href="http://david.dw-perspective.org.uk">Homepage</a> | <a href="http://wordshell.net">WordShell - WordPress command line</a> | <a href="http://david.dw-perspective.org.uk/donate">Donate</a> | <a href="http://wordpress.org/extend/plugins/updraftplus/faq/">FAQs</a> | <a href="http://profiles.wordpress.org/davidanderson/">My other WordPress plugins</a>). Version: <?php echo $this->version; ?>
1627 <br>
1628 <?php
1629 if(isset($_GET['updraft_restore_success'])) {
1630 echo "<div style=\"color:blue\">Your backup has been restored. Your old themes, uploads, and plugins directories have been retained with \"-old\" appended to their name. Remove them when you are satisfied that the backup worked properly. At this time Updraft does not automatically restore your DB. You will need to use an external tool like phpMyAdmin to perform that task.</div>";
1631 }
1632
1633 $ws_advert = $this->wordshell_random_advert(1);
1634 echo <<<ENDHERE
1635 <div class="updated fade" style="max-width: 800px; font-size:140%; line-height: 140%; padding:14px; clear:left;">${ws_advert}</div>
1636 ENDHERE;
1637
1638 if($deleted_old_dirs) {
1639 echo '<div style="color:blue">Old directories successfully deleted.</div>';
1640 }
1641 if(!$this->memory_check(96)) {?>
1642 <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>
1643 <?php
1644 }
1645 if(!$this->execution_time_check(300)) {?>
1646 <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>
1647 <?php
1648 }
1649
1650 if($this->scan_old_dirs()) {?>
1651 <div style="color:orange">You have old directories from a previous backup. Click to delete them after you have verified that the restoration worked.</div>
1652 <form method="post" action="<?php echo remove_query_arg(array('updraft_restore_success','action')) ?>">
1653 <input type="hidden" name="action" value="updraft_delete_old_dirs" />
1654 <input type="submit" class="button-primary" value="Delete Old Dirs" onclick="return(confirm('Are you sure you want to delete the old directories? This cannot be undone.'))" />
1655 </form>
1656 <?php
1657 }
1658 if(!empty($this->errors)) {
1659 foreach($this->errors as $error) {
1660 // ignoring severity
1661 echo '<div style="color:red">'.$error['error'].'</div>';
1662 }
1663 }
1664 ?>
1665
1666 <h2 style="clear:left;">Existing Schedule And Backups</h2>
1667 <table class="form-table" style="float:left; clear: both; width:545px;">
1668 <tr>
1669 <?php
1670 $updraft_dir = $this->backups_dir_location();
1671 $next_scheduled_backup = wp_next_scheduled('updraft_backup');
1672 $next_scheduled_backup = ($next_scheduled_backup) ? date('D, F j, Y H:i T',$next_scheduled_backup) : 'No backups are scheduled at this time.';
1673 $next_scheduled_backup_database = wp_next_scheduled('updraft_backup_database');
1674 if (get_option('updraft_interval_database',get_option('updraft_interval')) == get_option('updraft_interval')) {
1675 $next_scheduled_backup_database = "Will take place at the same time as the files backup.";
1676 } else {
1677 $next_scheduled_backup_database = ($next_scheduled_backup_database) ? date('D, F j, Y H:i T',$next_scheduled_backup_database) : 'No backups are scheduled at this time.';
1678 }
1679 $current_time = date('D, F j, Y H:i T',time());
1680 $updraft_last_backup = get_option('updraft_last_backup');
1681 if($updraft_last_backup) {
1682 $last_backup = ($updraft_last_backup['success']) ? date('D, F j, Y H:i T',$updraft_last_backup['backup_time']) : implode("<br>",$updraft_last_backup['errors']);
1683 $last_backup_color = ($updraft_last_backup['success']) ? 'green' : 'red';
1684 if (!empty($updraft_last_backup['backup_nonce'])) {
1685 $potential_log_file = $updraft_dir."/log.".$updraft_last_backup['backup_nonce'].".txt";
1686 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>";
1687 }
1688 } else {
1689 $last_backup = 'No backup has been completed.';
1690 $last_backup_color = 'blue';
1691 }
1692
1693 if(is_writable($updraft_dir)) {
1694 $dir_info = '<span style="color:green">Backup directory specified is writable, which is good.</span>';
1695 $backup_disabled = "";
1696 } else {
1697 $backup_disabled = 'disabled="disabled"';
1698 $dir_info = '<span style="color:red">Backup directory specified is <b>not</b> writable, or does not exist. <span style="font-size:110%;font-weight:bold"><a href="options-general.php?page=updraftplus&action=updraft_create_backup_dir">Click here</a></span> to attempt to create the directory and set the permissions. If that is unsuccessful check the permissions on your server or change it to another directory that is writable by your web server process.</span>';
1699 }
1700 ?>
1701
1702 <th>Time now:</th>
1703 <td style="color:blue"><?php echo $current_time?></td>
1704 </tr>
1705 <tr>
1706 <th>Next scheduled files backup:</th>
1707 <td style="color:blue"><?php echo $next_scheduled_backup?></td>
1708 </tr>
1709 <tr>
1710 <th>Next scheduled DB backup:</th>
1711 <td style="color:blue"><?php echo $next_scheduled_backup_database?></td>
1712 </tr>
1713 <tr>
1714 <th>Last backup:</th>
1715 <td style="color:<?php echo $last_backup_color ?>"><?php echo $last_backup?></td>
1716 </tr>
1717 </table>
1718 <div style="float:left; width:200px; padding-top: 40px;">
1719 <form method="post" action="">
1720 <input type="hidden" name="action" value="updraft_backup" />
1721 <p><input type="submit" <?php echo $backup_disabled ?> class="button-primary" value="Backup Now!" style="padding-top:3px;padding-bottom:3px;font-size:24px !important" onclick="return(confirm('This will schedule a one-time backup. To trigger the backup you should go ahead, then wait 10 seconds, then visit any page on your site. WordPress should then start the backup running in the background.'))" /></p>
1722 </form>
1723 <div style="position:relative">
1724 <div style="position:absolute;top:0;left:0">
1725 <?php
1726 $backup_history = get_option('updraft_backup_history');
1727 $backup_history = (is_array($backup_history))?$backup_history:array();
1728 $restore_disabled = (count($backup_history) == 0) ? 'disabled="disabled"' : "";
1729 ?>
1730 <input type="button" class="button-primary" <?php echo $restore_disabled ?> value="Restore" style="padding-top:3px;padding-bottom:3px;font-size:24px !important" onclick="jQuery('#backup-restore').fadeIn('slow');jQuery(this).parent().fadeOut('slow')" />
1731 </div>
1732 <div style="display:none;position:absolute;top:0;left:0" id="backup-restore">
1733 <form method="post" action="">
1734 <b>Choose: </b>
1735 <select name="backup_timestamp" style="display:inline">
1736 <?php
1737 foreach($backup_history as $key=>$value) {
1738 echo "<option value='$key'>".date('Y-m-d G:i',$key)."</option>\n";
1739 }
1740 ?>
1741 </select>
1742
1743 <input type="hidden" name="action" value="updraft_restore" />
1744 <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, uploads and other content directories (according to what is contained in the backup set which you select). Database restoration cannot be done through this process - you must download the database and import yourself (e.g. through PHPMyAdmin). Do you wish to continue with the restoration process?'))" />
1745 </form>
1746 </div>
1747 </div>
1748 </div>
1749 <br style="clear:both" />
1750 <table class="form-table">
1751 <tr>
1752 <th>Last backup log message:</th>
1753 <td id="updraft_lastlogcontainer"><?php echo htmlspecialchars(get_option('updraft_lastmessage', '(Nothing yet logged)')); ?></td>
1754 </tr>
1755 <tr>
1756 <th>Download backups</th>
1757 <td><a href="#" title="Click to see available backups" onclick="jQuery('.download-backups').toggle();return false;"><?php echo count($backup_history)?> available</a></td>
1758 </tr>
1759 <tr>
1760 <td></td><td class="download-backups" style="display:none">
1761 <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.</em>
1762 <table>
1763 <?php
1764 foreach($backup_history as $key=>$value) {
1765 ?>
1766 <tr>
1767 <td><b><?php echo date('Y-m-d G:i',$key)?></b></td>
1768 <td>
1769 <?php if (isset($value['db'])) { ?>
1770 <form action="admin-ajax.php" method="post">
1771 <input type="hidden" name="action" value="updraft_download_backup" />
1772 <input type="hidden" name="type" value="db" />
1773 <input type="hidden" name="timestamp" value="<?php echo $key?>" />
1774 <input type="submit" value="Database" />
1775 </form>
1776 <?php } else { echo "(No database)"; } ?>
1777 </td>
1778 <td>
1779 <?php if (isset($value['plugins'])) { ?>
1780 <form action="admin-ajax.php" method="post">
1781 <input type="hidden" name="action" value="updraft_download_backup" />
1782 <input type="hidden" name="type" value="plugins" />
1783 <input type="hidden" name="timestamp" value="<?php echo $key?>" />
1784 <input type="submit" value="Plugins" />
1785 </form>
1786 <?php } else { echo "(No plugins)"; } ?>
1787 </td>
1788 <td>
1789 <?php if (isset($value['themes'])) { ?>
1790 <form action="admin-ajax.php" method="post">
1791 <input type="hidden" name="action" value="updraft_download_backup" />
1792 <input type="hidden" name="type" value="themes" />
1793 <input type="hidden" name="timestamp" value="<?php echo $key?>" />
1794 <input type="submit" value="Themes" />
1795 </form>
1796 <?php } else { echo "(No themes)"; } ?>
1797 </td>
1798 <td>
1799 <?php if (isset($value['uploads'])) { ?>
1800 <form action="admin-ajax.php" method="post">
1801 <input type="hidden" name="action" value="updraft_download_backup" />
1802 <input type="hidden" name="type" value="uploads" />
1803 <input type="hidden" name="timestamp" value="<?php echo $key?>" />
1804 <input type="submit" value="Uploads" />
1805 </form>
1806 <?php } else { echo "(No uploads)"; } ?>
1807 </td>
1808 <td>
1809 <?php if (isset($value['others'])) { ?>
1810 <form action="admin-ajax.php" method="post">
1811 <input type="hidden" name="action" value="updraft_download_backup" />
1812 <input type="hidden" name="type" value="others" />
1813 <input type="hidden" name="timestamp" value="<?php echo $key?>" />
1814 <input type="submit" value="Others" />
1815 </form>
1816 <?php } else { echo "(No others)"; } ?>
1817 </td>
1818 <td>
1819 <?php if (isset($value['nonce']) && preg_match("/^[0-9a-f]{12}$/",$value['nonce']) && is_readable($updraft_dir.'/log.'.$value['nonce'].'.txt')) { ?>
1820 <form action="options-general.php" method="get">
1821 <input type="hidden" name="action" value="downloadlog" />
1822 <input type="hidden" name="page" value="updraftplus" />
1823 <input type="hidden" name="updraftplus_backup_nonce" value="<?php echo $value['nonce']; ?>" />
1824 <input type="submit" value="Backup Log" />
1825 </form>
1826 <?php } else { echo "(No backup log)"; } ?>
1827 </td>
1828 </tr>
1829 <?php }?>
1830 </table>
1831 </td>
1832 </tr>
1833 </table>
1834 <form method="post" action="options.php">
1835 <?php settings_fields('updraft-options-group'); ?>
1836 <h2>Configure Backup Contents And Schedule</h2>
1837 <table class="form-table" style="width:850px;">
1838 <tr>
1839 <th>File backup intervals:</th>
1840 <td><select name="updraft_interval">
1841 <?php
1842 $intervals = array ("manual" => "Manual", 'every4hours' => "Every 4 hours", 'every8hours' => "Every 8 hours", 'twicedaily' => "Every 12 hours", 'daily' => "Daily", 'weekly' => "Weekly", 'fortnightly' => "Fortnightly", 'monthly' => "Monthly");
1843 foreach ($intervals as $cronsched => $descrip) {
1844 echo "<option value=\"$cronsched\" ";
1845 if ($cronsched == get_option('updraft_interval','manual')) echo 'selected="selected"';
1846 echo ">$descrip</option>\n";
1847 }
1848 ?>
1849 </select></td>
1850 </tr>
1851 <tr>
1852 <th>Database backup intervals:</th>
1853 <td><select name="updraft_interval_database">
1854 <?php
1855 foreach ($intervals as $cronsched => $descrip) {
1856 echo "<option value=\"$cronsched\" ";
1857 if ($cronsched == get_option('updraft_interval_database',get_option('updraft_interval'))) echo 'selected="selected"';
1858 echo ">$descrip</option>\n";
1859 }
1860 ?>
1861 </select></td>
1862 </tr>
1863 <tr class="backup-interval-description">
1864 <td></td><td>If you would like to automatically schedule backups, choose schedules from the dropdowns above. Backups will occur at the intervals specified starting just after the current time. If you choose manual you must click the &quot;Backup Now!&quot; button whenever you wish a backup to occur. If the two schedules are the same, then the two backups will take place together.</td>
1865 </tr>
1866 <?php
1867 # The true (default value if non-existent) here has the effect of forcing a default of on.
1868 $include_themes = (get_option('updraft_include_themes',true)) ? 'checked="checked"' : "";
1869 $include_plugins = (get_option('updraft_include_plugins',true)) ? 'checked="checked"' : "";
1870 $include_uploads = (get_option('updraft_include_uploads',true)) ? 'checked="checked"' : "";
1871 $include_others = (get_option('updraft_include_others',true)) ? 'checked="checked"' : "";
1872 $include_others_exclude = get_option('updraft_include_others_exclude',UPDRAFT_DEFAULT_OTHERS_EXCLUDE);
1873 ?>
1874 <tr>
1875 <th>Include in Files Backup:</th>
1876 <td>
1877 <input type="checkbox" name="updraft_include_plugins" value="1" <?php echo $include_plugins; ?> /> Plugins<br>
1878 <input type="checkbox" name="updraft_include_themes" value="1" <?php echo $include_themes; ?> /> Themes<br>
1879 <input type="checkbox" name="updraft_include_uploads" value="1" <?php echo $include_uploads; ?> /> Uploads<br>
1880 <input type="checkbox" name="updraft_include_others" value="1" <?php echo $include_others; ?> /> Any other directories found inside wp-content - but exclude these directories: <input type="text" name="updraft_include_others_exclude" size="32" value="<?php echo htmlspecialchars($include_others_exclude); ?>"/><br>
1881 Include all of these, unless you are backing them up separately. Note that presently UpdraftPlus backs up these directories only - which is usually everything (except for WordPress core itself which you can download afresh from WordPress.org). But if you have made customised modifications outside of these directories, you need to back them up another way.<br>(<a href="http://wordshell.net">Use WordShell</a> for automatic backup, version control and patching).<br></td>
1882 </td>
1883 </tr>
1884 <tr>
1885 <th>Retain backups:</th>
1886 <?php
1887 $updraft_retain = get_option('updraft_retain');
1888 $retain = ((int)$updraft_retain > 0)?get_option('updraft_retain'):1;
1889 ?>
1890 <td><input type="text" name="updraft_retain" value="<?php echo $retain ?>" style="width:50px" /></td>
1891 </tr>
1892 <tr class="backup-retain-description">
1893 <td></td><td>By default only the most recent backup is retained. If you'd like to preserve more, specify the number here. (This many of <strong>both</strong> files and database backups will be retained.)</td>
1894 </tr>
1895 <tr>
1896 <th>Email:</th>
1897 <td><input type="text" style="width:260px" name="updraft_email" value="<?php echo get_option('updraft_email'); ?>" /> <br>Enter an address here to have a report sent (and the whole backup, if you choose) to it.</td>
1898 </tr>
1899 <tr class="deletelocal">
1900 <th>Delete local backup:</th>
1901 <td><input type="checkbox" name="updraft_delete_local" value="1" <?php $delete_local = (get_option('updraft_delete_local')) ? 'checked="checked"' : "";
1902 echo $delete_local; ?> /> <br>Check this to delete the local backup file (only sensible if you have enabled a remote backup (below), otherwise you will have no backup remaining).</td>
1903 </tr>
1904
1905 <tr>
1906 <th>Database encryption phrase:</th>
1907 <?php
1908 $updraft_encryptionphrase = get_option('updraft_encryptionphrase');
1909 ?>
1910 <td><input type="text" name="updraft_encryptionphrase" value="<?php echo $updraft_encryptionphrase ?>" style="width:132px" /></td>
1911 </tr>
1912 <tr class="backup-crypt-description">
1913 <td></td><td>If you enter text here, it is used to encrypt backups (Rijndael). Do not lose it, or all your backups will be useless. Presently, only the database file is encrypted. This is also the key used to decrypt backups from this admin interface (so if you change it, then automatic decryption will not work until you change it back). You can also use the file example-decrypt.php from inside the UpdraftPlus plugin directory to decrypt manually.</td>
1914 </tr>
1915 </table>
1916
1917 <h2>Copying Your Backup To Remote Storage</h2>
1918
1919 <table class="form-table" style="width:850px;">
1920 <tr>
1921 <th>Choose your remote storage:</th>
1922 <td><select name="updraft_service" id="updraft-service">
1923 <?php
1924 $debug_mode = (get_option('updraft_debug_mode')) ? 'checked="checked"' : "";
1925
1926 $set = 'selected="selected"';
1927
1928 // Should be one of s3, dropbox, ftp, googledrive, email, or whatever else is added
1929 $active_service = get_option('updraft_service');
1930
1931 ?>
1932 <option value="none" <?php
1933 if ($active_service == "none") echo $set; ?>>None</option>
1934 <?php
1935 foreach ($this->backup_methods as $method => $description) {
1936 echo "<option value=\"$method\"";
1937 if ($active_service == $method) echo ' '.$set;
1938 echo '>'.$description;
1939 echo "</option>\n";
1940 }
1941 ?>
1942 </select></td>
1943 </tr>
1944 <?php
1945 foreach ($this->backup_methods as $method => $description) {
1946 require_once(UPDRAFTPLUS_DIR.'/methods/'.$method.'.php');
1947 $call_method = "UpdraftPlus_BackupModule_$method";
1948 call_user_func(array($call_method, 'config_print'));
1949 }
1950 ?>
1951 </table>
1952 <script type="text/javascript">
1953 /* <![CDATA[ */
1954 var lastlog_lastmessage = "";
1955 var lastlog_sdata = {
1956 action: 'updraft_ajax',
1957 subaction: 'lastlog',
1958 nonce: '<?php echo wp_create_nonce('updraftplus-credentialtest-nonce'); ?>'
1959 };
1960 function updraft_showlastlog(){
1961 jQuery.get(ajaxurl, lastlog_sdata, function(response) {
1962 nexttimer = 1500;
1963 if (lastlog_lastmessage == response) { nexttimer = 4500; }
1964 window.setTimeout(function(){updraft_showlastlog()}, nexttimer);
1965 jQuery('#updraft_lastlogcontainer').html(response);
1966 lastlog_lastmessage = response;
1967 });
1968 }
1969 jQuery(document).ready(function() {
1970 window.setTimeout(function(){updraft_showlastlog()}, 1200);
1971 jQuery('.updraftplusmethod').hide();
1972 <?php
1973 if ($active_service) echo "jQuery('.${active_service}').show();";
1974 foreach ($this->backup_methods as $method => $description) {
1975 // already done: require_once(UPDRAFTPLUS_DIR.'/methods/'.$method.'.php');
1976 $call_method = "UpdraftPlus_BackupModule_$method";
1977 if (method_exists($call_method, 'config_print_javascript_onready')) call_user_func(array($call_method, 'config_print_javascript_onready'));
1978 }
1979 ?>
1980 });
1981 /* ]]> */
1982 </script>
1983 <table class="form-table" style="width:850px;">
1984 <tr>
1985 <td colspan="2"><h2>Advanced / Debugging Settings</h2></td>
1986 </tr>
1987 <tr>
1988 <th>Backup directory:</th>
1989 <td><input type="text" name="updraft_dir" style="width:525px" value="<?php echo htmlspecialchars($updraft_dir); ?>" /></td>
1990 </tr>
1991 <tr>
1992 <td></td><td><?php echo $dir_info ?> This is where Updraft Backup/Restore will write the zip files it creates initially. This directory must be writable by your web server. Typically you'll want to have it inside your wp-content folder (this is the default). <b>Do not</b> place it inside your uploads dir, as that will cause recursion issues (backups of backups of backups of...).</td>
1993 </tr>
1994 <tr>
1995 <th>Debug / Expert mode:</th>
1996 <td><input type="checkbox" name="updraft_debug_mode" value="1" <?php echo $debug_mode; ?> /> <br>Check this to enable some more options below (that will appear after you save), and potentially receive more information and emails on the backup process - useful if something is going wrong. You <strong>must</strong> send me this log if you are filing a bug report.</td>
1997 </tr>
1998 <tr>
1999 <td></td>
2000 <td>
2001 <p style="margin: 10px 0; padding: 10px; font-size: 140%; background-color: lightYellow; border-color: #E6DB55; border: 1px solid; border-radius: 4px;">
2002 <?php
2003 echo $this->wordshell_random_advert(1);
2004 ?>
2005 </p>
2006 </td>
2007 </tr>
2008 <tr>
2009 <td></td>
2010 <td>
2011 <input type="hidden" name="action" value="update" />
2012 <input type="submit" class="button-primary" value="Save Changes" />
2013 </td>
2014 </tr>
2015 </table>
2016 </form>
2017 <?php
2018 if(get_option('updraft_debug_mode')) {
2019 ?>
2020 <div style="padding-top: 40px;">
2021 <hr>
2022 <h3>Debug Information And Expert Options</h3>
2023 <p>
2024 <?php
2025 $peak_memory_usage = memory_get_peak_usage(true)/1024/1024;
2026 $memory_usage = memory_get_usage(true)/1024/1024;
2027 echo 'Peak memory usage: '.$peak_memory_usage.' MB<br/>';
2028 echo 'Current memory usage: '.$memory_usage.' MB<br/>';
2029 echo 'PHP memory limit: '.ini_get('memory_limit').' <br/>';
2030 ?>
2031 </p>
2032 <p style="max-width: 600px;">The buttons below will immediately execute a backup run, independently of WordPress's scheduler. If these work whilst your scheduled backups and the &quot;Backup Now&quot; button do absolutely nothing (i.e. not even produce a log file), then it means that your scheduler is broken. You should then disable all your other plugins, and try the &quot; Backup Now&quot; button. If that fails, then contact your web hosting company and ask them if they have disabled wp-cron. If it succeeds, then re-activate your other plugins one-by-one, and find the one that is the problem and report a bug to them.</p>
2033
2034 <form method="post">
2035 <input type="hidden" name="action" value="updraft_backup_debug_all" />
2036 <p><input type="submit" class="button-primary" <?php echo $backup_disabled ?> value="Debug Full Backup" onclick="return(confirm('This will cause an immediate backup. The page will stall loading until it finishes (ie, unscheduled).'))" /></p>
2037 </form>
2038 <form method="post">
2039 <input type="hidden" name="action" value="updraft_backup_debug_db" />
2040 <p><input type="submit" class="button-primary" <?php echo $backup_disabled ?> value="Debug DB Backup" onclick="return(confirm('This will cause an immediate DB backup. The page will stall loading until it finishes (ie, unscheduled). The backup may well run out of time; really this button is only helpful for checking that the backup is able to get through the initial stages, or for small WordPress sites.'))" /></p>
2041 </form>
2042 <h3>Wipe Settings</h3>
2043 <p style="max-width: 600px;">This button will delete all UpdraftPlus settings (but not any of your existing backups from your cloud storage). You will then need to enter all your settings again. You can also do this before deactivating/deinstalling UpdraftPlus if you wish.</p>
2044 <form method="post">
2045 <input type="hidden" name="action" value="updraft_wipesettings" />
2046 <p><input type="submit" class="button-primary" value="Wipe All Settings" onclick="return(confirm('This will delete all your UpdraftPlus settings - are you sure you want to do this?'))" /></p>
2047 </form>
2048 </div>
2049 <?php } ?>
2050
2051 <script type="text/javascript">
2052 /* <![CDATA[ */
2053 jQuery(document).ready(function() {
2054 jQuery('#updraft-service').change(function() {
2055 jQuery('.updraftplusmethod').hide();
2056 var active_class = jQuery(this).val();
2057 jQuery('.'+active_class).show();
2058 })
2059 })
2060 jQuery(window).load(function() {
2061 //this is for hiding the restore progress at the top after it is done
2062 setTimeout('jQuery("#updraft-restore-progress").toggle(1000)',3000)
2063 jQuery('#updraft-restore-progress-toggle').click(function() {
2064 jQuery('#updraft-restore-progress').toggle(500)
2065 })
2066 })
2067 /* ]]> */
2068 </script>
2069 <?php
2070 }
2071
2072 function show_admin_warning($message) {
2073 echo '<div id="updraftmessage" class="updated fade">'."<p>$message</p></div>";
2074 }
2075
2076 function show_admin_warning_unreadablelog() {
2077 $this->show_admin_warning('<strong>UpdraftPlus notice:</strong> The log file could not be read.</a>');
2078 }
2079
2080 function show_admin_warning_dropbox() {
2081 $this->show_admin_warning('<strong>UpdraftPlus notice:</strong> <a href="?page=updraftplus&action=updraftmethod-dropbox-auth&updraftplus_dropboxauth=doit">Click here to authenticate your Dropbox account (you will not be able to back up to Dropbox without it).</a>');
2082 }
2083
2084 function show_admin_warning_googledrive() {
2085 $this->show_admin_warning('<strong>UpdraftPlus notice:</strong> <a href="?page=updraftplus&action=updraftmethod-googledrive-auth&updraftplus_googleauth=doit">Click here to authenticate your Google Drive account (you will not be able to back up to Google Drive without it).</a>');
2086 }
2087
2088 }
2089
2090
2091 ?>
2092