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

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