PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.3.8
UpdraftPlus: WP Backup & Migration Plugin v1.3.8
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.3.8, at updraftplus.php

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