Version Description
- 06/29/2012 =
- Marking as tested up to WordPress 3.4.1
Download this release
Release Info
Developer | DavidAnderson |
Plugin | UpdraftPlus WordPress Backup Plugin |
Version | 0.8.29 |
Comparing to | |
See all releases |
Code changes from version 0.7.7 to 0.8.29
- readme.txt +17 -8
- updraftplus.php +764 -152
readme.txt
CHANGED
@@ -1,14 +1,18 @@
|
|
1 |
=== UpdraftPlus ===
|
2 |
Contributors: David Anderson
|
3 |
-
Tags: backup, restore, database, cloud, amazon, s3, ftp, cloud
|
4 |
Requires at least: 3.2
|
5 |
-
Tested up to: 3.
|
6 |
-
Stable tag: 0.
|
|
|
7 |
License: GPLv2 or later
|
8 |
|
9 |
== Description ==
|
10 |
|
11 |
-
UpdraftPlus simplifies backups (and restoration) for your blog. Backup into the cloud (S3, FTP, and email) and restore with a single click.
|
|
|
|
|
|
|
12 |
|
13 |
== Installation ==
|
14 |
|
@@ -41,11 +45,16 @@ Nothing, probably. That's the point of an encryption key - people who don't have
|
|
41 |
|
42 |
Contact me! This is a complex plugin and the only way I can ensure it's robust is to get bug reports and fix the problems that crop up. Please turn on debugging mode and send me the log if you can find it. Include as much information as you can when reporting (PHP version, your blog's site, the error you saw and how you got to the page that caused it, etcetera). If you can send a patch, that's even better.
|
43 |
|
44 |
-
== Upgrade Notice ==
|
45 |
-
Added a logging mechanism for easier development
|
46 |
-
|
47 |
== Changelog ==
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
= 0.7.7 - 05/29/2012 =
|
50 |
* Implementation of a logging mechanism to allow easier debugging and development
|
51 |
|
@@ -56,7 +65,7 @@ Added a logging mechanism for easier development
|
|
56 |
* Added ability to decrypt encrypted database backups
|
57 |
* Added ability to opt out of backing up each file group
|
58 |
* Now adds database character set, the lack of which before made database backups unusable without modifications
|
59 |
-
* Version number bump to make
|
60 |
|
61 |
= 0.1.3 - 01/16/2012 =
|
62 |
* Force backup of all tables found in database (vanilla Updraft only backed up WP core tables)
|
1 |
=== UpdraftPlus ===
|
2 |
Contributors: David Anderson
|
3 |
+
Tags: backup, restore, database, cloud, amazon, s3, google drive, google, gdrive, ftp, cloud, updraft, back up
|
4 |
Requires at least: 3.2
|
5 |
+
Tested up to: 3.4.1
|
6 |
+
Stable tag: 0.8.29
|
7 |
+
Donate link: http://david.dw-perspective.org.uk/donate
|
8 |
License: GPLv2 or later
|
9 |
|
10 |
== Description ==
|
11 |
|
12 |
+
UpdraftPlus simplifies backups (and restoration) for your blog. Backup into the cloud (S3, Google Drive, FTP, and email) and restore with a single click. Backups of files and database can be upon separate schedules.
|
13 |
+
|
14 |
+
== Upgrade Notice ==
|
15 |
+
Tested up to WordPress 3.4.1
|
16 |
|
17 |
== Installation ==
|
18 |
|
45 |
|
46 |
Contact me! This is a complex plugin and the only way I can ensure it's robust is to get bug reports and fix the problems that crop up. Please turn on debugging mode and send me the log if you can find it. Include as much information as you can when reporting (PHP version, your blog's site, the error you saw and how you got to the page that caused it, etcetera). If you can send a patch, that's even better.
|
47 |
|
|
|
|
|
|
|
48 |
== Changelog ==
|
49 |
|
50 |
+
= 0.8.29 - 06/29/2012 =
|
51 |
+
* Marking as tested up to WordPress 3.4.1
|
52 |
+
|
53 |
+
= 0.8.28 - 06/06/2012 =
|
54 |
+
* Now experimentally supports Google Drive (thanks to Sorin Iclanzan, code re-used from his Google Drive-only 'backup' plugin)
|
55 |
+
* New feature: backup files and database on separate schedules
|
56 |
+
* Tidied and improved retain behaviour
|
57 |
+
|
58 |
= 0.7.7 - 05/29/2012 =
|
59 |
* Implementation of a logging mechanism to allow easier debugging and development
|
60 |
|
65 |
* Added ability to decrypt encrypted database backups
|
66 |
* Added ability to opt out of backing up each file group
|
67 |
* Now adds database character set, the lack of which before made database backups unusable without modifications
|
68 |
+
* Version number bump to make clear that this is an improvement on the original Updraft, and is now tried and tested
|
69 |
|
70 |
= 0.1.3 - 01/16/2012 =
|
71 |
* Force backup of all tables found in database (vanilla Updraft only backed up WP core tables)
|
updraftplus.php
CHANGED
@@ -2,35 +2,26 @@
|
|
2 |
/*
|
3 |
Plugin Name: UpdraftPlus - Backup/Restore
|
4 |
Plugin URI: http://wordpress.org/extend/plugins/updraftplus
|
5 |
-
Description:
|
6 |
Author: David Anderson.
|
7 |
-
Version: 0.
|
|
|
8 |
Author URI: http://wordshell.net
|
9 |
*/
|
10 |
|
11 |
//TODO:
|
12 |
-
//Put DB and file backups onto separate schedules. If the option is set identically then do in one run, otherwise do separately.
|
13 |
//Add DropBox support
|
14 |
-
//Add more logging
|
15 |
//Struggles with large uploads - runs out of time before finishing. Break into chunks? Resume download on later run? (Add a new scheduled event to check on progress? Separate the upload from the creation?). Add in some logging (in a .php file that exists first).
|
16 |
-
//More logging
|
17 |
//improve error reporting. s3 and dir backup have decent reporting now, but not sure i know what to do from here
|
18 |
-
//better implementation of retain. one that isn't dependent on being inside the cloud_backup method
|
19 |
//list backups that aren't tracked (helps with double backup problem)
|
20 |
-
//refactor db backup methods a bit. give full credit to wp-db-backup
|
21 |
//investigate $php_errormsg further
|
22 |
//pretty up return messages in admin area
|
23 |
//check s3/ftp download
|
24 |
-
//allow upload of backup files too. (specify 1-4 files to restore)
|
25 |
-
//Add back donate link in readme.txt header. Donate link: URL
|
26 |
-
//user permissions for WP users if ( function_exists('is_site_admin') && ! is_site_admin() ) around backups?
|
27 |
|
28 |
/* More TODO:
|
29 |
Are all directories in wp-content covered? No; only plugins, themes, content. We should check for others and allow the user the chance to choose which ones he wants
|
30 |
-
Add turn-off-foreign-key-checks stuff into mysql dump (does WP even use these?)
|
31 |
Use only one entry in WP options database
|
32 |
Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
|
33 |
-
More verbose debug reports, send debug report in the email
|
34 |
*/
|
35 |
|
36 |
/* Portions copyright 2010 Paul Kehrer
|
@@ -63,7 +54,7 @@ if(!$updraft->memory_check(192)) {
|
|
63 |
|
64 |
class UpdraftPlus {
|
65 |
|
66 |
-
var $version = '0.
|
67 |
|
68 |
var $dbhandle;
|
69 |
var $errors = array();
|
@@ -77,16 +68,381 @@ class UpdraftPlus {
|
|
77 |
# Create admin page
|
78 |
add_action('admin_menu', array($this,'add_admin_pages'));
|
79 |
add_action('admin_init', array($this,'admin_init'));
|
80 |
-
add_action('updraft_backup', array($this,'
|
|
|
|
|
|
|
81 |
add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup'));
|
82 |
add_filter('cron_schedules', array($this,'modify_cron_schedules'));
|
83 |
add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
}
|
85 |
|
86 |
# Adds the settings link under the plugin on the plugin screen.
|
87 |
function plugin_action_links($links, $file) {
|
88 |
if ($file == plugin_basename(__FILE__)){
|
89 |
-
$settings_link = '<a href="'.site_url().'/wp-admin/options-general.php?page=
|
|
|
|
|
90 |
array_unshift($links, $settings_link);
|
91 |
}
|
92 |
return $links;
|
@@ -104,56 +460,98 @@ class UpdraftPlus {
|
|
104 |
}
|
105 |
}
|
106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
//scheduled wp-cron events can have a race condition here if page loads are coming fast enough, but there's nothing we can do about it.
|
108 |
-
function backup() {
|
|
|
109 |
//generate backup information
|
110 |
$this->backup_time_nonce();
|
111 |
|
112 |
-
//set log file name
|
113 |
$updraft_dir = $this->backups_dir_location();
|
114 |
$this->logfile_name = $updraft_dir. "/log." . $this->nonce . ".txt";
|
115 |
-
|
116 |
-
# Use append mode in case it already exists
|
117 |
$this->logfile_handle = fopen($this->logfile_name, 'a');
|
118 |
-
|
|
|
119 |
global $wp_version;
|
120 |
-
$this->log("PHP version: ".phpversion()." WordPress version: ".$wp_version);
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
//save this to our history so we can track backups for the retain feature
|
130 |
-
$this->log("Saving backup history");
|
131 |
-
$this->save_backup_history($backup_array);
|
132 |
-
|
133 |
-
//cloud operations (S3,FTP,email,nothing)
|
134 |
-
//this also calls the retain feature at the end (done in this method to reuse existing cloud connections)
|
135 |
-
if(is_array($backup_array) && count($backup_array) >0) {
|
136 |
-
$this->log("Beginning dispatch of backup to remote");
|
137 |
-
$this->cloud_backup($backup_array);
|
138 |
-
}
|
139 |
-
//delete local files if the pref is set
|
140 |
-
foreach($backup_array as $file) {
|
141 |
-
$this->log("Deleting local file: $file");
|
142 |
-
$this->delete_local($file);
|
143 |
}
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
$
|
151 |
-
|
152 |
-
$
|
153 |
-
|
154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
}
|
156 |
-
wp_mail($sendmail_to,'Backed up: '.get_bloginfo('name').' (UpdraftPlus) '.date('Y-m-d H:i',time()),'Site: '.site_url()."\r\nUpdraftPlus WordPress backup is complete.\r\n\r\n".$this->wordshell_random_advert(0)."\r\n".$append_log);
|
157 |
}
|
158 |
|
159 |
// Close log file
|
@@ -174,6 +572,11 @@ class UpdraftPlus {
|
|
174 |
$this->log("Cloud backup: S3");
|
175 |
if (count($backup_array) >0) { $this->s3_backup($backup_array); }
|
176 |
break;
|
|
|
|
|
|
|
|
|
|
|
177 |
case 'ftp':
|
178 |
@set_time_limit(900);
|
179 |
$this->log("Cloud backup: FTP");
|
@@ -189,23 +592,130 @@ class UpdraftPlus {
|
|
189 |
}
|
190 |
//we don't break here so it goes and executes all the default behavior below as well. this gives us retain behavior for email
|
191 |
default:
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
200 |
@unlink($fullpath); //delete it if it's locally available
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
}
|
|
|
202 |
}
|
203 |
-
|
204 |
-
|
205 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
206 |
}
|
|
|
|
|
207 |
}
|
208 |
-
|
209 |
function s3_backup($backup_array) {
|
210 |
if(!class_exists('S3')) {
|
211 |
require_once(dirname(__FILE__).'/includes/S3.php');
|
@@ -219,29 +729,29 @@ class UpdraftPlus {
|
|
219 |
$this->error("S3 Error: Failed to upload $fullpath. Error was ".$php_errormsg);
|
220 |
}
|
221 |
}
|
|
|
222 |
} else {
|
223 |
$this->error("S3 Error: Failed to create bucket $bucket_name. Error was ".$php_errormsg);
|
224 |
}
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
$
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
if (!$s3->deleteObject($bucket_name, $file)) {
|
239 |
-
$this->error("S3 Error: Failed to delete object $file. Error was ".$php_errormsg);
|
240 |
}
|
241 |
}
|
|
|
|
|
|
|
242 |
}
|
243 |
-
update_option('updraft_backup_history',$backup_history);
|
244 |
-
/*retain behavior*/
|
245 |
}
|
246 |
|
247 |
function ftp_backup($backup_array) {
|
@@ -259,30 +769,13 @@ class UpdraftPlus {
|
|
259 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
260 |
$ftp->put($fullpath,$ftp_remote_path.$file,FTP_BINARY);
|
261 |
}
|
262 |
-
|
263 |
-
/*retain behavior*/
|
264 |
-
$updraft_retain = get_option('updraft_retain');
|
265 |
-
$retain = (isset($updraft_retain))?get_option('updraft_retain'):1;
|
266 |
-
$backup_history = $this->get_backup_history();
|
267 |
-
while (count($backup_history) > $retain) {
|
268 |
-
$backup_to_delete = array_pop($backup_history);
|
269 |
-
foreach($backup_to_delete as $file) {
|
270 |
-
//if for some reason one of the backup files is an empty string let's skip it.
|
271 |
-
if($file == '') {
|
272 |
-
continue;
|
273 |
-
}
|
274 |
-
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
275 |
-
@unlink($fullpath); //delete it if it's locally available
|
276 |
-
@$ftp->delete($ftp_remote_path.$file);
|
277 |
-
}
|
278 |
-
}
|
279 |
-
update_option('updraft_backup_history',$backup_history);
|
280 |
-
/*retain behavior*/
|
281 |
}
|
282 |
|
283 |
function delete_local($file) {
|
284 |
if(get_option('updraft_delete_local')) {
|
285 |
-
|
|
|
286 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
287 |
return unlink($fullpath);
|
288 |
}
|
@@ -320,41 +813,49 @@ class UpdraftPlus {
|
|
320 |
# Plugins
|
321 |
@set_time_limit(900);
|
322 |
if (get_option('updraft_include_plugins', true)) {
|
|
|
323 |
$plugins = new PclZip($backup_file_base.'-plugins.zip');
|
324 |
if (!$plugins->create($wp_plugins_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
|
325 |
$this->error('Could not create plugins zip. Error was '.$php_errmsg,'fatal');
|
326 |
}
|
327 |
$backup_array['plugins'] = basename($backup_file_base.'-plugins.zip');
|
|
|
|
|
328 |
}
|
329 |
-
|
330 |
# Themes
|
331 |
@set_time_limit(900);
|
332 |
if (get_option('updraft_include_themes', true)) {
|
|
|
333 |
$themes = new PclZip($backup_file_base.'-themes.zip');
|
334 |
if (!$themes->create($wp_themes_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
|
335 |
$this->error('Could not create themes zip. Error was '.$php_errmsg,'fatal');
|
336 |
}
|
337 |
$backup_array['themes'] = basename($backup_file_base.'-themes.zip');
|
|
|
|
|
338 |
}
|
339 |
|
340 |
# Uploads
|
341 |
@set_time_limit(900);
|
342 |
if (get_option('updraft_include_uploads', true)) {
|
|
|
343 |
$uploads = new PclZip($backup_file_base.'-uploads.zip');
|
344 |
if (!$uploads->create($wp_upload_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
|
345 |
$this->error('Could not create uploads zip. Error was '.$php_errmsg,'fatal');
|
346 |
}
|
347 |
$backup_array['uploads'] = basename($backup_file_base.'-uploads.zip');
|
|
|
|
|
348 |
}
|
349 |
-
|
350 |
return $backup_array;
|
351 |
}
|
352 |
|
353 |
function save_backup_history($backup_array) {
|
354 |
//this stores full paths right now. should probably concatenate with ABSPATH to make it easier to move sites
|
355 |
-
$backup_history = get_option('updraft_backup_history');
|
356 |
-
$backup_history = (!is_array($backup_history))?array():$backup_history;
|
357 |
if(is_array($backup_array)) {
|
|
|
|
|
358 |
$backup_history[$this->backup_time] = $backup_array;
|
359 |
update_option('updraft_backup_history',$backup_history);
|
360 |
} else {
|
@@ -379,6 +880,9 @@ class UpdraftPlus {
|
|
379 |
/*START OF WB-DB-BACKUP BLOCK*/
|
380 |
|
381 |
function backup_db() {
|
|
|
|
|
|
|
382 |
global $table_prefix, $wpdb;
|
383 |
if(!$this->backup_time) {
|
384 |
$this->backup_time_nonce();
|
@@ -409,7 +913,6 @@ class UpdraftPlus {
|
|
409 |
//$this->error(__('The backup directory is not writable!','wp-db-backup'));
|
410 |
}
|
411 |
|
412 |
-
|
413 |
//Begin new backup of MySql
|
414 |
$this->stow("# " . __('WordPress MySQL database backup','wp-db-backup') . "\n");
|
415 |
$this->stow("#\n");
|
@@ -427,6 +930,7 @@ class UpdraftPlus {
|
|
427 |
}
|
428 |
|
429 |
foreach ($all_tables as $table) {
|
|
|
430 |
// Increase script execution time-limit to 15 min for every table.
|
431 |
if ( !ini_get('safe_mode')) @set_time_limit(15*60);
|
432 |
if ( strpos($table, $table_prefix) == 0 ) {
|
@@ -456,6 +960,7 @@ class UpdraftPlus {
|
|
456 |
# Encrypt, if requested
|
457 |
$encryption = get_option('updraft_encryptionphrase');
|
458 |
if (strlen($encryption) > 0) {
|
|
|
459 |
$encryption_error = 0;
|
460 |
require_once(dirname(__FILE__).'/includes/Rijndael.php');
|
461 |
$rijndael = new Crypt_Rijndael();
|
@@ -480,6 +985,7 @@ class UpdraftPlus {
|
|
480 |
return basename($backup_file_base.'-db.gz');
|
481 |
}
|
482 |
}
|
|
|
483 |
|
484 |
} //wp_db_backup
|
485 |
|
@@ -496,6 +1002,8 @@ class UpdraftPlus {
|
|
496 |
function backup_table($table, $segment = 'none') {
|
497 |
global $wpdb;
|
498 |
|
|
|
|
|
499 |
$table_structure = $wpdb->get_results("DESCRIBE $table");
|
500 |
if (! $table_structure) {
|
501 |
//$this->error(__('Error getting table details','wp-db-backup') . ": $table");
|
@@ -585,6 +1093,7 @@ class UpdraftPlus {
|
|
585 |
$replace = array('\0', '\n', '\r', '\Z');
|
586 |
if($table_data) {
|
587 |
foreach ($table_data as $row) {
|
|
|
588 |
$values = array();
|
589 |
foreach ($row as $key => $value) {
|
590 |
if ($ints[strtolower($key)]) {
|
@@ -611,6 +1120,8 @@ class UpdraftPlus {
|
|
611 |
$this->stow("# --------------------------------------------------------\n");
|
612 |
$this->stow("\n");
|
613 |
}
|
|
|
|
|
614 |
} // end backup_table()
|
615 |
|
616 |
|
@@ -648,8 +1159,6 @@ class UpdraftPlus {
|
|
648 |
return true;
|
649 |
}
|
650 |
|
651 |
-
|
652 |
-
|
653 |
/**
|
654 |
* Add backquotes to tables and db-names in
|
655 |
* SQL queries. Taken from phpMyAdmin.
|
@@ -680,10 +1189,6 @@ class UpdraftPlus {
|
|
680 |
return str_replace('\'', '\\\'', $a_string);
|
681 |
}
|
682 |
|
683 |
-
/*END OF WP-DB-BACKUP BLOCK */
|
684 |
-
/*END OF WP-DB-BACKUP BLOCK */
|
685 |
-
/*END OF WP-DB-BACKUP BLOCK */
|
686 |
-
/*END OF WP-DB-BACKUP BLOCK */
|
687 |
/*END OF WP-DB-BACKUP BLOCK */
|
688 |
|
689 |
/*
|
@@ -705,6 +1210,19 @@ class UpdraftPlus {
|
|
705 |
return wp_filter_nohtml_kses($interval);
|
706 |
}
|
707 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
708 |
//wp-cron only has hourly, daily and twicedaily, so we need to add weekly and monthly.
|
709 |
function modify_cron_schedules($schedules) {
|
710 |
$schedules['weekly'] = array(
|
@@ -785,12 +1303,15 @@ class UpdraftPlus {
|
|
785 |
$this->delete_local($file);
|
786 |
exit; //we exit immediately because otherwise admin-ajax appends an additional zero to the end for some reason I don't understand. seriously, why die('0')?
|
787 |
} else {
|
788 |
-
echo 'Download failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then S3 or FTP retrieval may have failed.';
|
789 |
}
|
790 |
}
|
791 |
|
792 |
function download_backup($file) {
|
793 |
switch(get_option('updraft_service')) {
|
|
|
|
|
|
|
794 |
case 's3':
|
795 |
$this->download_s3_backup($file);
|
796 |
break;
|
@@ -802,6 +1323,10 @@ class UpdraftPlus {
|
|
802 |
}
|
803 |
}
|
804 |
|
|
|
|
|
|
|
|
|
805 |
function download_s3_backup($file) {
|
806 |
if(!class_exists('S3')) {
|
807 |
require_once(dirname(__FILE__).'/includes/S3.php');
|
@@ -841,7 +1366,7 @@ class UpdraftPlus {
|
|
841 |
return false;
|
842 |
}
|
843 |
|
844 |
-
$credentials = request_filesystem_credentials("options-general.php?page=
|
845 |
WP_Filesystem($credentials);
|
846 |
if ( $wp_filesystem->errors->get_error_code() ) {
|
847 |
foreach ( $wp_filesystem->errors->get_error_messages() as $message )
|
@@ -880,11 +1405,10 @@ class UpdraftPlus {
|
|
880 |
return true;
|
881 |
}
|
882 |
|
883 |
-
|
884 |
//deletes the -old directories that are created when a backup is restored.
|
885 |
function delete_old_dirs() {
|
886 |
global $wp_filesystem;
|
887 |
-
$credentials = request_filesystem_credentials("options-general.php?page=
|
888 |
WP_Filesystem($credentials);
|
889 |
if ( $wp_filesystem->errors->get_error_code() ) {
|
890 |
foreach ( $wp_filesystem->errors->get_error_messages() as $message )
|
@@ -926,7 +1450,7 @@ class UpdraftPlus {
|
|
926 |
|
927 |
function create_backup_dir() {
|
928 |
global $wp_filesystem;
|
929 |
-
$credentials = request_filesystem_credentials("options-general.php?page=
|
930 |
WP_Filesystem($credentials);
|
931 |
if ( $wp_filesystem->errors->get_error_code() ) {
|
932 |
foreach ( $wp_filesystem->errors->get_error_messages() as $message )
|
@@ -982,16 +1506,20 @@ class UpdraftPlus {
|
|
982 |
}
|
983 |
wp_enqueue_script('jquery');
|
984 |
register_setting( 'updraft-options-group', 'updraft_interval', array($this,'schedule_backup') );
|
|
|
985 |
register_setting( 'updraft-options-group', 'updraft_retain', array($this,'retain_range') );
|
986 |
register_setting( 'updraft-options-group', 'updraft_encryptionphrase', 'wp_filter_nohtml_kses' );
|
987 |
register_setting( 'updraft-options-group', 'updraft_service', 'wp_filter_nohtml_kses' );
|
988 |
register_setting( 'updraft-options-group', 'updraft_s3_login', 'wp_filter_nohtml_kses' );
|
989 |
register_setting( 'updraft-options-group', 'updraft_s3_pass', 'wp_filter_nohtml_kses' );
|
|
|
|
|
|
|
|
|
990 |
register_setting( 'updraft-options-group', 'updraft_ftp_login', 'wp_filter_nohtml_kses' );
|
991 |
register_setting( 'updraft-options-group', 'updraft_ftp_pass', 'wp_filter_nohtml_kses' );
|
992 |
register_setting( 'updraft-options-group', 'updraft_dir', 'wp_filter_nohtml_kses' );
|
993 |
register_setting( 'updraft-options-group', 'updraft_email', 'wp_filter_nohtml_kses' );
|
994 |
-
register_setting( 'updraft-options-group', 'updraft_s3_remote_path', 'wp_filter_nohtml_kses' );
|
995 |
register_setting( 'updraft-options-group', 'updraft_ftp_remote_path', 'wp_filter_nohtml_kses' );
|
996 |
register_setting( 'updraft-options-group', 'updraft_server_address', 'wp_filter_nohtml_kses' );
|
997 |
register_setting( 'updraft-options-group', 'updraft_delete_local', 'absint' );
|
@@ -1000,6 +1528,7 @@ class UpdraftPlus {
|
|
1000 |
register_setting( 'updraft-options-group', 'updraft_include_themes', 'absint' );
|
1001 |
register_setting( 'updraft-options-group', 'updraft_include_uploads', 'absint' );
|
1002 |
|
|
|
1003 |
if (current_user_can('manage_options')) {
|
1004 |
$updraft_dir = $this->backups_dir_location();
|
1005 |
if(strpos($updraft_dir,WP_CONTENT_DIR) !== false) {
|
@@ -1013,15 +1542,16 @@ class UpdraftPlus {
|
|
1013 |
add_action('admin_notices', array($this,'show_admin_warning_accessible') );
|
1014 |
}
|
1015 |
}
|
1016 |
-
if (isset($dir_protection_info)) {
|
1017 |
-
}
|
1018 |
}
|
1019 |
}
|
|
|
|
|
|
|
|
|
1020 |
}
|
1021 |
-
|
1022 |
|
1023 |
function add_admin_pages() {
|
1024 |
-
add_submenu_page('options-general.php', "UpdraftPlus", "UpdraftPlus", "manage_options", "
|
1025 |
array($this,"settings_output"));
|
1026 |
}
|
1027 |
|
@@ -1051,15 +1581,15 @@ ENDHERE;
|
|
1051 |
$backup_success = $this->restore_backup($_REQUEST['backup_timestamp']);
|
1052 |
if(empty($this->errors) && $backup_success == true) {
|
1053 |
echo '<p>Restore successful!</p><br/>';
|
1054 |
-
echo '<b>Actions:</b> <a href="options-general.php?page=
|
1055 |
return;
|
1056 |
} else {
|
1057 |
echo '<p>Restore failed...</p><br/>';
|
1058 |
-
echo '<b>Actions:</b> <a href="options-general.php?page=
|
1059 |
return;
|
1060 |
}
|
1061 |
//uncomment the below once i figure out how i want the flow of a restoration to work.
|
1062 |
-
//echo '<b>Actions:</b> <a href="options-general.php?page=
|
1063 |
}
|
1064 |
$deleted_old_dirs = false;
|
1065 |
if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'updraft_delete_old_dirs') {
|
@@ -1069,24 +1599,31 @@ ENDHERE;
|
|
1069 |
echo '<p>Old directory removal failed for some reason. You may want to do this manually.</p><br/>';
|
1070 |
}
|
1071 |
echo '<p>Old directories successfully removed.</p><br/>';
|
1072 |
-
echo '<b>Actions:</b> <a href="options-general.php?page=
|
1073 |
return;
|
1074 |
}
|
1075 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1076 |
if(isset($_GET['action']) && $_GET['action'] == 'updraft_create_backup_dir') {
|
1077 |
if(!$this->create_backup_dir()) {
|
1078 |
echo '<p>Backup directory could not be created...</p><br/>';
|
1079 |
}
|
1080 |
echo '<p>Backup directory successfully created.</p><br/>';
|
1081 |
-
echo '<b>Actions:</b> <a href="options-general.php?page=
|
1082 |
return;
|
1083 |
}
|
1084 |
|
1085 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') {
|
1086 |
-
wp_schedule_single_event(time()+
|
1087 |
}
|
1088 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') {
|
1089 |
-
$this->backup();
|
1090 |
}
|
1091 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_db') {
|
1092 |
$this->backup_db();
|
@@ -1097,7 +1634,7 @@ ENDHERE;
|
|
1097 |
<h2>UpdraftPlus - Backup/Restore</h2>
|
1098 |
|
1099 |
Version: <b><?php echo $this->version; ?></b><br />
|
1100 |
-
Maintained by <b>David Anderson</b> (<a href="http://david.dw-perspective.org.uk">Homepage</a> | <a href="http://wordshell.net">WordShell - WordPress command line</a> )
|
1101 |
<br />
|
1102 |
Based on Updraft by <b>Paul Kehrer</b> (<a href="http://langui.sh" target="_blank">Blog</a> | <a href="http://twitter.com/reaperhulk" target="_blank">Twitter</a> )
|
1103 |
<br />
|
@@ -1136,10 +1673,12 @@ ENDHERE;
|
|
1136 |
<tr>
|
1137 |
<?php
|
1138 |
$next_scheduled_backup = wp_next_scheduled('updraft_backup');
|
1139 |
-
|
1140 |
-
|
|
|
|
|
1141 |
} else {
|
1142 |
-
$
|
1143 |
}
|
1144 |
$current_time = date('D, F j, Y H:i T',time());
|
1145 |
$updraft_last_backup = get_option('updraft_last_backup');
|
@@ -1162,16 +1701,20 @@ ENDHERE;
|
|
1162 |
$backup_disabled = "";
|
1163 |
} else {
|
1164 |
$backup_disabled = 'disabled="disabled"';
|
1165 |
-
$dir_info = '<span style="color:red">Backup directory specified is <b>not</b> writable. <span style="font-size:110%;font-weight:bold"><a href="options-general.php?page=
|
1166 |
}
|
1167 |
?>
|
1168 |
-
<th>
|
1169 |
<td style="color:blue"><?php echo $current_time?></td>
|
1170 |
</tr>
|
1171 |
<tr>
|
1172 |
-
<th>Next Scheduled Backup:</th>
|
1173 |
<td style="color:blue"><?php echo $next_scheduled_backup?></td>
|
1174 |
</tr>
|
|
|
|
|
|
|
|
|
1175 |
<tr>
|
1176 |
<th>Last Backup:</th>
|
1177 |
<td style="color:<?php echo $last_backup_color ?>"><?php echo $last_backup?></td>
|
@@ -1280,20 +1823,33 @@ ENDHERE;
|
|
1280 |
<td></td><td><?php echo $dir_info ?> This is where Updraft Backup/Restore will write the zip files it creates initially. This directory must be writable by your web server. Typically you'll want to have it inside your wp-content folder (this is the default). <b>Do not</b> place it inside your uploads dir, as that will cause recursion issues (backups of backups of backups of...).</td>
|
1281 |
</tr>
|
1282 |
<tr>
|
1283 |
-
<th>Backup Intervals:</th>
|
1284 |
<td><select name="updraft_interval">
|
1285 |
<?php
|
1286 |
$intervals = array ("manual", "daily", "weekly", "monthly");
|
1287 |
foreach ($intervals as $ival) {
|
1288 |
echo "<option value=\"$ival\" ";
|
1289 |
-
if ($ival == get_option('updraft_interval')) { echo 'selected="selected"';}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1290 |
echo ">".ucfirst($ival)."</option>\n";
|
1291 |
}
|
1292 |
?>
|
1293 |
</select></td>
|
1294 |
</tr>
|
1295 |
<tr class="backup-interval-description">
|
1296 |
-
<td></td><td>If you would like to automatically schedule backups, choose
|
1297 |
</tr>
|
1298 |
<?php
|
1299 |
# The true (default value if non-existent) here has the effect of forcing a default of on.
|
@@ -1302,12 +1858,12 @@ ENDHERE;
|
|
1302 |
$include_uploads = (get_option('updraft_include_uploads',true)) ? 'checked="checked"' : "";
|
1303 |
?>
|
1304 |
<tr>
|
1305 |
-
<th>Include in Backup:</th>
|
1306 |
<td>
|
1307 |
<input type="checkbox" name="updraft_include_plugins" value="1" <?php echo $include_plugins; ?> /> Plugins<br />
|
1308 |
<input type="checkbox" name="updraft_include_themes" value="1" <?php echo $include_themes; ?> /> Themes<br />
|
1309 |
<input type="checkbox" name="updraft_include_uploads" value="1" <?php echo $include_uploads; ?> /> Uploads<br />
|
1310 |
-
Include all of these, unless you are backing them up separately. Note that presently UpdraftPlus backs up these directories only - which is usually everything (except for WordPress core itself which you can download afresh from WordPress.org). But if you have made customised modifications outside of these directories, you need to back them up another way
|
1311 |
</td>
|
1312 |
</tr>
|
1313 |
<tr>
|
@@ -1319,10 +1875,10 @@ ENDHERE;
|
|
1319 |
<td><input type="text" name="updraft_retain" value="<?php echo $retain ?>" style="width:50px" /></td>
|
1320 |
</tr>
|
1321 |
<tr class="backup-retain-description">
|
1322 |
-
<td></td><td>By default only the most recent backup is retained. If you'd like to preserve more, specify the number here
|
1323 |
</tr>
|
1324 |
<tr>
|
1325 |
-
<th>
|
1326 |
<?php
|
1327 |
$updraft_encryptionphrase = get_option('updraft_encryptionphrase');
|
1328 |
?>
|
@@ -1340,28 +1896,37 @@ ENDHERE;
|
|
1340 |
$debug_mode = (get_option('updraft_debug_mode')) ? 'checked="checked"' : "";
|
1341 |
|
1342 |
$display_none = 'style="display:none"';
|
1343 |
-
$s3 = ""; $ftp = ""; $email = "";
|
1344 |
$email_display="";
|
1345 |
$display_email_complete = "";
|
1346 |
$set = 'selected="selected"';
|
1347 |
switch(get_option('updraft_service')) {
|
1348 |
case 's3':
|
1349 |
$s3 = $set;
|
|
|
|
|
|
|
|
|
|
|
|
|
1350 |
$ftp_display = $display_none;
|
1351 |
break;
|
1352 |
case 'ftp':
|
1353 |
$ftp = $set;
|
|
|
1354 |
$s3_display = $display_none;
|
1355 |
break;
|
1356 |
case 'email':
|
1357 |
$email = $set;
|
1358 |
$ftp_display = $display_none;
|
1359 |
$s3_display = $display_none;
|
|
|
1360 |
$display_email_complete = $display_none;
|
1361 |
break;
|
1362 |
default:
|
1363 |
$none = $set;
|
1364 |
$ftp_display = $display_none;
|
|
|
1365 |
$s3_display = $display_none;
|
1366 |
$display_delete_local = $display_none;
|
1367 |
break;
|
@@ -1369,14 +1934,17 @@ ENDHERE;
|
|
1369 |
?>
|
1370 |
<option value="none" <?php echo $none?>>None</option>
|
1371 |
<option value="s3" <?php echo $s3?>>Amazon S3</option>
|
|
|
1372 |
<option value="ftp" <?php echo $ftp?>>FTP</option>
|
1373 |
<option value="email" <?php echo $email?>>E-mail</option>
|
1374 |
</select></td>
|
1375 |
</tr>
|
1376 |
<tr class="backup-service-description">
|
1377 |
-
<td></td><td>Choose
|
1378 |
|
1379 |
</tr>
|
|
|
|
|
1380 |
<tr class="s3" <?php echo $s3_display?>>
|
1381 |
<th>S3 access key:</th>
|
1382 |
<td><input type="text" autocomplete="off" style="width:292px" name="updraft_s3_login" value="<?php echo get_option('updraft_s3_login') ?>" /></td>
|
@@ -1393,6 +1961,43 @@ ENDHERE;
|
|
1393 |
<th></th>
|
1394 |
<td><p>Get your access key and secret key from your AWS page, then pick a (globally unique) bucket name (letters and numbers) to use for storage. (Do not enter the s3:// prefix).</p></td>
|
1395 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1396 |
<tr class="ftp" <?php echo $ftp_display?>>
|
1397 |
<th><a href="#" title="Click for help!" onclick="jQuery('.ftp-description').toggle();return false;">FTP Server:</a></th>
|
1398 |
<td><input type="text" style="width:260px" name="updraft_server_address" value="<?php echo get_option('updraft_server_address'); ?>" /></td>
|
@@ -1418,7 +2023,7 @@ ENDHERE;
|
|
1418 |
</tr>
|
1419 |
<tr class="deletelocal s3 ftp email" <?php echo $display_delete_local?>>
|
1420 |
<th>Delete local backup:</th>
|
1421 |
-
<td><input type="checkbox" name="updraft_delete_local" value="1" <?php echo $delete_local; ?> /> <br />Check this to delete the local backup file
|
1422 |
</tr>
|
1423 |
<tr>
|
1424 |
<th>Debug mode:</th>
|
@@ -1459,19 +2064,23 @@ ENDHERE;
|
|
1459 |
jQuery('#updraft-service').change(function() {
|
1460 |
switch(jQuery(this).val()) {
|
1461 |
case 'none':
|
1462 |
-
jQuery('.deletelocal,.s3,.ftp,.s3-description,.ftp-description').hide()
|
1463 |
jQuery('.email,.email-complete').show()
|
1464 |
break;
|
1465 |
case 's3':
|
1466 |
-
jQuery('.ftp,.ftp-description').hide()
|
1467 |
jQuery('.s3,.deletelocal,.email,.email-complete').show()
|
1468 |
break;
|
|
|
|
|
|
|
|
|
1469 |
case 'ftp':
|
1470 |
-
jQuery('.s3,.s3-description').hide()
|
1471 |
jQuery('.ftp,.deletelocal,.email,.email-complete').show()
|
1472 |
break;
|
1473 |
case 'email':
|
1474 |
-
jQuery('.s3,.ftp,.s3-description,.ftp-description,.email-complete').hide()
|
1475 |
jQuery('.email,.deletelocal').show()
|
1476 |
break;
|
1477 |
}
|
@@ -1538,6 +2147,9 @@ ENDHERE;
|
|
1538 |
function show_admin_warning_accessible() {
|
1539 |
$this->show_admin_warning("UpdraftPlus backup directory specified is accessible via the web. This is a potential security problem (people may be able to download your backups - which is undesirable if your database is not encrypted and if you have non-public assets amongst the files). If using Apache, enable .htaccess support to allow web access to be denied; otherwise, you should deny access manually.");
|
1540 |
}
|
|
|
|
|
|
|
1541 |
function show_admin_warning_accessible_unknownresult() {
|
1542 |
$this->show_admin_warning("UpdraftPlus tried to check if the backup directory is accessible via web, but the result was unknown.");
|
1543 |
}
|
2 |
/*
|
3 |
Plugin Name: UpdraftPlus - Backup/Restore
|
4 |
Plugin URI: http://wordpress.org/extend/plugins/updraftplus
|
5 |
+
Description: Uploads, themes, plugins, and your DB can be automatically backed up to Amazon S3, Google Drive, FTP, or emailed. Files and DB can be on separate schedules.
|
6 |
Author: David Anderson.
|
7 |
+
Version: 0.8.29
|
8 |
+
Donate link: http://david.dw-perspective.org.uk/donate
|
9 |
Author URI: http://wordshell.net
|
10 |
*/
|
11 |
|
12 |
//TODO:
|
|
|
13 |
//Add DropBox support
|
|
|
14 |
//Struggles with large uploads - runs out of time before finishing. Break into chunks? Resume download on later run? (Add a new scheduled event to check on progress? Separate the upload from the creation?). Add in some logging (in a .php file that exists first).
|
|
|
15 |
//improve error reporting. s3 and dir backup have decent reporting now, but not sure i know what to do from here
|
|
|
16 |
//list backups that aren't tracked (helps with double backup problem)
|
|
|
17 |
//investigate $php_errormsg further
|
18 |
//pretty up return messages in admin area
|
19 |
//check s3/ftp download
|
|
|
|
|
|
|
20 |
|
21 |
/* More TODO:
|
22 |
Are all directories in wp-content covered? No; only plugins, themes, content. We should check for others and allow the user the chance to choose which ones he wants
|
|
|
23 |
Use only one entry in WP options database
|
24 |
Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
|
|
|
25 |
*/
|
26 |
|
27 |
/* Portions copyright 2010 Paul Kehrer
|
54 |
|
55 |
class UpdraftPlus {
|
56 |
|
57 |
+
var $version = '0.8.29';
|
58 |
|
59 |
var $dbhandle;
|
60 |
var $errors = array();
|
68 |
# Create admin page
|
69 |
add_action('admin_menu', array($this,'add_admin_pages'));
|
70 |
add_action('admin_init', array($this,'admin_init'));
|
71 |
+
add_action('updraft_backup', array($this,'backup_files'));
|
72 |
+
add_action('updraft_backup_database', array($this,'backup_database'));
|
73 |
+
# backup_all is used by the manual "Backup Now" button
|
74 |
+
add_action('updraft_backup_all', array($this,'backup_all'));
|
75 |
add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup'));
|
76 |
add_filter('cron_schedules', array($this,'modify_cron_schedules'));
|
77 |
add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2);
|
78 |
+
add_action('init', array($this, 'googledrive_backup_auth'));
|
79 |
+
}
|
80 |
+
|
81 |
+
// Handle Google OAuth 2.0
|
82 |
+
function googledrive_backup_auth() {
|
83 |
+
if ( is_admin() && isset( $_GET['page'] ) && $_GET['page'] == 'updraftplus' && isset( $_GET['action'] ) && $_GET['action'] == 'auth' ) {
|
84 |
+
if ( isset( $_GET['state'] ) ) {
|
85 |
+
if ( $_GET['state'] == 'token' )
|
86 |
+
$this->auth_token();
|
87 |
+
elseif ( $_GET['state'] == 'revoke' )
|
88 |
+
$this->auth_revoke();
|
89 |
+
} elseif (isset($_GET['updraftplus_googleauth'])) {
|
90 |
+
$this->auth_request();
|
91 |
+
}
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Acquire single-use authorization code from Google OAuth 2.0
|
97 |
+
*/
|
98 |
+
function auth_request() {
|
99 |
+
$params = array(
|
100 |
+
'response_type' => 'code',
|
101 |
+
'client_id' => get_option('updraft_googledrive_clientid'),
|
102 |
+
'redirect_uri' => admin_url('options-general.php?page=updraftplus&action=auth'),
|
103 |
+
'scope' => 'https://www.googleapis.com/auth/drive.file https://docs.google.com/feeds/ https://docs.googleusercontent.com/ https://spreadsheets.google.com/feeds/',
|
104 |
+
'state' => 'token',
|
105 |
+
'access_type' => 'offline',
|
106 |
+
'approval_prompt' => 'auto'
|
107 |
+
);
|
108 |
+
header('Location: https://accounts.google.com/o/oauth2/auth?'.http_build_query($params));
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Get a Google account access token using the refresh token
|
113 |
+
*/
|
114 |
+
function access_token( $token, $client_id, $client_secret ) {
|
115 |
+
$context = array(
|
116 |
+
'http' => array(
|
117 |
+
'method' => 'POST',
|
118 |
+
'header' => 'Content-type: application/x-www-form-urlencoded',
|
119 |
+
'content' => http_build_query( array(
|
120 |
+
'refresh_token' => $token,
|
121 |
+
'client_id' => $client_id,
|
122 |
+
'client_secret' => $client_secret,
|
123 |
+
'grant_type' => 'refresh_token'
|
124 |
+
) )
|
125 |
+
)
|
126 |
+
);
|
127 |
+
$this->log("Google Drive: requesting access token: client_id=$client_id");
|
128 |
+
$result = @file_get_contents('https://accounts.google.com/o/oauth2/token', false, stream_context_create($context));
|
129 |
+
if($result) {
|
130 |
+
$result = json_decode( $result, true );
|
131 |
+
if ( isset( $result['access_token'] ) ) {
|
132 |
+
$this->log("Google Drive: successfully obtained access token");
|
133 |
+
return $result['access_token'];
|
134 |
+
} else {
|
135 |
+
$this->log("Google Drive error when requesting access token: response does not contain access_token");
|
136 |
+
return false;
|
137 |
+
}
|
138 |
+
} else {
|
139 |
+
$this->log("Google Drive error when requesting access token: no response");
|
140 |
+
return false;
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Function to upload a file to Google Drive
|
146 |
+
*
|
147 |
+
* @param string $file Path to the file that is to be uploaded
|
148 |
+
* @param string $title Title to be given to the file
|
149 |
+
* @param string $parent ID of the folder in which to upload the file
|
150 |
+
* @param string $token Access token from Google Account
|
151 |
+
* @return boolean Returns TRUE on success, FALSE on failure
|
152 |
+
*/
|
153 |
+
function googledrive_upload_file( $file, $title, $parent = '', $token) {
|
154 |
+
|
155 |
+
$size = filesize( $file );
|
156 |
+
|
157 |
+
$content = '<?xml version=\'1.0\' encoding=\'UTF-8\'?>
|
158 |
+
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007">
|
159 |
+
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/docs/2007#file"/>
|
160 |
+
<title>' . $title . '</title>
|
161 |
+
</entry>';
|
162 |
+
|
163 |
+
$header = array(
|
164 |
+
'Authorization: Bearer ' . $token,
|
165 |
+
'Content-Length: ' . strlen( $content ),
|
166 |
+
'Content-Type: application/atom+xml',
|
167 |
+
'X-Upload-Content-Type: application/octet-stream',
|
168 |
+
'X-Upload-Content-Length: ' . $size,
|
169 |
+
'GData-Version: 3.0'
|
170 |
+
);
|
171 |
+
|
172 |
+
$context = array(
|
173 |
+
'http' => array(
|
174 |
+
'ignore_errors' => true,
|
175 |
+
'follow_location' => false,
|
176 |
+
'method' => 'POST',
|
177 |
+
'header' => join( "\r\n", $header ),
|
178 |
+
'content' => $content
|
179 |
+
)
|
180 |
+
);
|
181 |
+
|
182 |
+
$url = $this->get_resumable_create_media_link( $token, $parent );
|
183 |
+
if ( $url ) {
|
184 |
+
$url .= '?convert=false'; // needed to upload a file
|
185 |
+
$this->log("Google Drive: resumable create media link: ".$url);
|
186 |
+
} else {
|
187 |
+
$this->log('Could not retrieve resumable create media link.', __FILE__, __LINE__ );
|
188 |
+
return false;
|
189 |
+
}
|
190 |
+
|
191 |
+
$result = @file_get_contents( $url, false, stream_context_create( $context ) );
|
192 |
+
|
193 |
+
if ( $result !== FALSE ) {
|
194 |
+
if ( strpos( $response = array_shift( $http_response_header ), '200' ) ) {
|
195 |
+
$response_header = array();
|
196 |
+
foreach ( $http_response_header as $header_line ) {
|
197 |
+
list( $key, $value ) = explode( ':', $header_line, 2 );
|
198 |
+
$response_header[trim( $key )] = trim( $value );
|
199 |
+
#$this->log("Google Drive: header: ".trim($key).": ".trim($value));
|
200 |
+
}
|
201 |
+
if ( isset( $response_header['Location'] ) ) {
|
202 |
+
$next_location = $response_header['Location'];
|
203 |
+
$pointer = 0;
|
204 |
+
# 1Mb
|
205 |
+
$max_chunk_size = 524288*2;
|
206 |
+
while ( $pointer < $size - 1 ) {
|
207 |
+
$this->log(basename($file).": Google Drive upload: pointer=$pointer (size=$size)");
|
208 |
+
$chunk = file_get_contents( $file, false, NULL, $pointer, $max_chunk_size );
|
209 |
+
$next_location = $this->upload_chunk( $next_location, $chunk, $pointer, $size, $token );
|
210 |
+
if( $next_location === false ) {
|
211 |
+
$this->log("Google Drive Upload: next_location is false (pointer: $pointer; chunk length: ".strlen($chunk).")");
|
212 |
+
return false;
|
213 |
+
}
|
214 |
+
$pointer += strlen( $chunk );
|
215 |
+
// if object it means we have our simpleXMLElement response
|
216 |
+
if ( is_object( $next_location ) ) {
|
217 |
+
// return resource Id
|
218 |
+
$this->log("Google Drive Upload: Success");
|
219 |
+
# Google Drive returns 501 not implemented for me for some reason instead of expected result...
|
220 |
+
#return substr( $next_location->children( "http://schemas.google.com/g/2005" )->resourceId, 5 );
|
221 |
+
return true;
|
222 |
+
}
|
223 |
+
|
224 |
+
}
|
225 |
+
}
|
226 |
+
}
|
227 |
+
else {
|
228 |
+
$this->log( 'Bad response: ' . $response . ' Response header: ' . var_export( $response_header, true ) . ' Response body: ' . $result . ' Request URL: ' . $url, __FILE__, __LINE__ );
|
229 |
+
return false;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
else {
|
233 |
+
$this->log( 'Unable to request file from ' . $url, __FILE__, __LINE__ );
|
234 |
+
}
|
235 |
+
|
236 |
+
return true;
|
237 |
+
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Get the resumable-create-media link needed to upload files
|
242 |
+
*
|
243 |
+
* @param string $token The Google Account access token
|
244 |
+
* @param string $parent The Id of the folder where the upload is to be made. Default is empty string.
|
245 |
+
* @return string|boolean Returns a link on success, FALSE on failure.
|
246 |
+
*/
|
247 |
+
function get_resumable_create_media_link( $token, $parent = '' ) {
|
248 |
+
$header = array(
|
249 |
+
'Authorization: Bearer ' . $token,
|
250 |
+
'GData-Version: 3.0'
|
251 |
+
);
|
252 |
+
$context = array(
|
253 |
+
'http' => array(
|
254 |
+
'ignore_errors' => true,
|
255 |
+
'method' => 'GET',
|
256 |
+
'header' => join( "\r\n", $header )
|
257 |
+
)
|
258 |
+
);
|
259 |
+
$url = 'https://docs.google.com/feeds/default/private/full';
|
260 |
+
|
261 |
+
if ( $parent ) {
|
262 |
+
$url .= '/' . $parent;
|
263 |
+
}
|
264 |
+
|
265 |
+
$result = @file_get_contents( $url, false, stream_context_create( $context ) );
|
266 |
+
|
267 |
+
if ( $result !== false ) {
|
268 |
+
$xml = simplexml_load_string( $result );
|
269 |
+
if ( $xml === false ) {
|
270 |
+
$this->log( 'Could not create SimpleXMLElement from ' . $result, __FILE__, __LINE__ );
|
271 |
+
return false;
|
272 |
+
}
|
273 |
+
else {
|
274 |
+
foreach ( $xml->link as $link ) {
|
275 |
+
if ( $link['rel'] == 'http://schemas.google.com/g/2005#resumable-create-media' ) { return $link['href']; }
|
276 |
+
}
|
277 |
+
}
|
278 |
+
}
|
279 |
+
return false;
|
280 |
+
}
|
281 |
+
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Handles the upload to Google Drive of a single chunk of a file
|
285 |
+
*
|
286 |
+
* @param string $location URL where the chunk needs to be uploaded
|
287 |
+
* @param string $chunk Part of the file to upload
|
288 |
+
* @param integer $pointer The byte number marking the beginning of the chunk in file
|
289 |
+
* @param integer $size The size of the file the chunk is part of, in bytes
|
290 |
+
* @param string $token Google Account access token
|
291 |
+
* @return string|boolean The funcion returns the location where the next chunk needs to be uploaded, TRUE if the last chunk was uploaded or FALSE on failure
|
292 |
+
*/
|
293 |
+
function upload_chunk( $location, $chunk, $pointer, $size, $token ) {
|
294 |
+
$chunk_size = strlen( $chunk );
|
295 |
+
$bytes = (string)$pointer . '-' . (string)($pointer + $chunk_size - 1) . '/' . (string)$size;
|
296 |
+
$this->log("Google Drive chunk: location=$location, length=$chunk_size, range=$bytes");
|
297 |
+
$header = array(
|
298 |
+
'Authorization: Bearer ' . $token,
|
299 |
+
'Content-Length: ' . $chunk_size,
|
300 |
+
'Content-Type: application/octet-stream',
|
301 |
+
'Content-Range: bytes ' . $bytes,
|
302 |
+
'GData-Version: 3.0'
|
303 |
+
);
|
304 |
+
$context = array(
|
305 |
+
'http' => array(
|
306 |
+
'ignore_errors' => true,
|
307 |
+
'follow_location' => false,
|
308 |
+
'method' => 'PUT',
|
309 |
+
'header' => join( "\r\n", $header ),
|
310 |
+
'content' => $chunk
|
311 |
+
)
|
312 |
+
);
|
313 |
+
|
314 |
+
$result = @file_get_contents( $location, false, stream_context_create( $context ) );
|
315 |
+
|
316 |
+
if ( isset( $http_response_header ) ) {
|
317 |
+
$response = array_shift( $http_response_header );
|
318 |
+
$headers = array();
|
319 |
+
foreach ( $http_response_header as $header_line ) {
|
320 |
+
list( $key, $value ) = explode( ':', $header_line, 2 );
|
321 |
+
$headers[trim( $key )] = trim( $value );
|
322 |
+
}
|
323 |
+
|
324 |
+
if ( strpos( $response, '308' ) ) {
|
325 |
+
if ( isset( $headers['Location'] ) ) {
|
326 |
+
$this->log('Google Drive: 308 response: '.$headers['Location']);
|
327 |
+
return $headers['Location'];
|
328 |
+
}
|
329 |
+
else {
|
330 |
+
$this->log('Google Drive 308 response: no location header: '.$location);
|
331 |
+
return $location;
|
332 |
+
}
|
333 |
+
}
|
334 |
+
elseif ( strpos( $response, '201' ) ) {
|
335 |
+
#$this->log("Google Drive response: ".$result);
|
336 |
+
$xml = simplexml_load_string( $result );
|
337 |
+
if ( $xml === false ) {
|
338 |
+
$this->log('ERROR: Could not create SimpleXMLElement from ' . $result, __FILE__, __LINE__ );
|
339 |
+
return false;
|
340 |
+
}
|
341 |
+
else {
|
342 |
+
return $xml;
|
343 |
+
}
|
344 |
+
}
|
345 |
+
else {
|
346 |
+
$this->log('ERROR: Bad response: ' . $response, __FILE__, __LINE__ );
|
347 |
+
return false;
|
348 |
+
}
|
349 |
+
}
|
350 |
+
else {
|
351 |
+
$this->log('ERROR: Received no response from ' . $location . ' while trying to upload bytes ' . $bytes );
|
352 |
+
return false;
|
353 |
+
}
|
354 |
+
}
|
355 |
+
|
356 |
+
function googledrive_delete_file( $file, $token) {
|
357 |
+
$this->log("Delete from Google Drive: $file: not yet implemented");
|
358 |
+
# TODO - somehow, turn this into a Gdata resource ID, then despatch it to googledrive_delete_file_byid
|
359 |
+
return;
|
360 |
+
}
|
361 |
+
|
362 |
+
/**
|
363 |
+
* Deletes a file from Google Drive
|
364 |
+
*
|
365 |
+
* @param string $id Gdata resource Id of the file to be deleted
|
366 |
+
* @param string $token Google Account access token
|
367 |
+
* @return boolean Returns TRUE on success, FALSE on failure
|
368 |
+
*/
|
369 |
+
function googledrive_delete_file_byid( $id, $token ) {
|
370 |
+
$header = array(
|
371 |
+
'If-Match: *',
|
372 |
+
'Authorization: Bearer ' . $token,
|
373 |
+
'GData-Version: 3.0'
|
374 |
+
);
|
375 |
+
$context = array(
|
376 |
+
'http' => array(
|
377 |
+
'method' => 'DELETE',
|
378 |
+
'header' => join( "\r\n", $header )
|
379 |
+
)
|
380 |
+
);
|
381 |
+
stream_context_set_default( $context );
|
382 |
+
$headers = get_headers( 'https://docs.google.com/feeds/default/private/full/' . $id . '?delete=true',1 );
|
383 |
+
|
384 |
+
if ( strpos( $headers[0], '200' ) ) { return true; }
|
385 |
+
return false;
|
386 |
+
}
|
387 |
+
|
388 |
+
/**
|
389 |
+
* Get a Google account refresh token using the code received from auth_request
|
390 |
+
*/
|
391 |
+
function auth_token() {
|
392 |
+
if( isset( $_GET['code'] ) ) {
|
393 |
+
$context = array(
|
394 |
+
'http' => array(
|
395 |
+
'timeout' => 30,
|
396 |
+
'method' => 'POST',
|
397 |
+
'header' => 'Content-type: application/x-www-form-urlencoded',
|
398 |
+
'content' => http_build_query( array(
|
399 |
+
'code' => $_GET['code'],
|
400 |
+
'client_id' => get_option('updraft_googledrive_clientid'),
|
401 |
+
'client_secret' => get_option('updraft_googledrive_secret'),
|
402 |
+
'redirect_uri' => admin_url('options-general.php?page=updraftplus&action=auth'),
|
403 |
+
'grant_type' => 'authorization_code'
|
404 |
+
) )
|
405 |
+
)
|
406 |
+
);
|
407 |
+
$result = @file_get_contents('https://accounts.google.com/o/oauth2/token', false, stream_context_create($context));
|
408 |
+
# Oddly, sometimes fails and then trying again works...
|
409 |
+
/*
|
410 |
+
if (!$result) { sleep(1); $result = @file_get_contents('https://accounts.google.com/o/oauth2/token', false, stream_context_create($context));}
|
411 |
+
if (!$result) { sleep(1); $result = @file_get_contents('https://accounts.google.com/o/oauth2/token', false, stream_context_create($context));}
|
412 |
+
*/
|
413 |
+
if($result) {
|
414 |
+
$result = json_decode( $result, true );
|
415 |
+
if ( isset( $result['refresh_token'] ) ) {
|
416 |
+
update_option('updraft_googledrive_token',$result['refresh_token']); // Save token
|
417 |
+
header('Location: '.admin_url('options-general.php?page=updraftplus&message=' . __( 'Authorization was successful.', 'updraftplus' ) ) );
|
418 |
+
}
|
419 |
+
else {
|
420 |
+
header('Location: '.admin_url('options-general.php?page=updraftplus&error=' . __( 'No refresh token was received!', 'updraftplus' ) ) );
|
421 |
+
}
|
422 |
+
} else {
|
423 |
+
header('Location: '.admin_url('options-general.php?page=updraftplus&error=' . __( 'Bad response!', 'backup' ) ) );
|
424 |
+
}
|
425 |
+
}
|
426 |
+
else {
|
427 |
+
header('Location: '.admin_url('options-general.php?page=updraftplus&error=' . __( 'Authorisation failed!', 'backup' ) ) );
|
428 |
+
}
|
429 |
+
}
|
430 |
+
|
431 |
+
/**
|
432 |
+
* Revoke a Google account refresh token
|
433 |
+
*/
|
434 |
+
function auth_revoke() {
|
435 |
+
@file_get_contents( 'https://accounts.google.com/o/oauth2/revoke?token=' . get_option('updraft_googledrive_token') );
|
436 |
+
update_option('updraft_googledrive_token','');
|
437 |
+
header( 'Location: '.admin_url( 'options-general.php?page=updraftplus&message=' . __( 'Authorization revoked.', 'backup' ) ) );
|
438 |
}
|
439 |
|
440 |
# Adds the settings link under the plugin on the plugin screen.
|
441 |
function plugin_action_links($links, $file) {
|
442 |
if ($file == plugin_basename(__FILE__)){
|
443 |
+
$settings_link = '<a href="'.site_url().'/wp-admin/options-general.php?page=updraftplus">'.__("Settings", "UpdraftPlus").'</a>';
|
444 |
+
array_unshift($links, $settings_link);
|
445 |
+
$settings_link = '<a href="http://david.dw-perspective.org.uk/donate">'.__("Donate","UpdraftPlus").'</a>';
|
446 |
array_unshift($links, $settings_link);
|
447 |
}
|
448 |
return $links;
|
460 |
}
|
461 |
}
|
462 |
|
463 |
+
function backup_all() {
|
464 |
+
$this->backup(true,true);
|
465 |
+
}
|
466 |
+
|
467 |
+
function backup_files() {
|
468 |
+
# Note that the "false" for database gets over-ridden automatically if they turn out to have the same schedules
|
469 |
+
$this->backup(true,false);
|
470 |
+
}
|
471 |
+
|
472 |
+
function backup_database() {
|
473 |
+
# Note that nothing will happen if the file backup had the same schedule
|
474 |
+
$this->backup(false,true);
|
475 |
+
}
|
476 |
+
|
477 |
//scheduled wp-cron events can have a race condition here if page loads are coming fast enough, but there's nothing we can do about it.
|
478 |
+
function backup($backup_files, $backup_database) {
|
479 |
+
|
480 |
//generate backup information
|
481 |
$this->backup_time_nonce();
|
482 |
|
483 |
+
//set log file name and open log file
|
484 |
$updraft_dir = $this->backups_dir_location();
|
485 |
$this->logfile_name = $updraft_dir. "/log." . $this->nonce . ".txt";
|
486 |
+
// Use append mode in case it already exists
|
|
|
487 |
$this->logfile_handle = fopen($this->logfile_name, 'a');
|
488 |
+
|
489 |
+
// Log some information that may be helpful
|
490 |
global $wp_version;
|
491 |
+
$this->log("PHP version: ".phpversion()." WordPress version: ".$wp_version." Backup files: $backup_files (schedule: ".get_option('updraft_interval','unset').") Backup DB: $backup_database (schedule: ".get_option('updraft_interval_database','unset').")");
|
492 |
+
|
493 |
+
# If the files and database schedules are the same, and if this the file one, then we rope in database too.
|
494 |
+
# On the other hand, if the schedules were the same and this was the database run, then there is nothing to do.
|
495 |
+
if (get_option('updraft_interval') == get_option('updraft_interval_database') || get_option('updraft_interval_database','xyz') == 'xyz' ) {
|
496 |
+
if ($backup_files == true)
|
497 |
+
{ $backup_database = true; }
|
498 |
+
else
|
499 |
+
{ $backup_database = false; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
500 |
}
|
501 |
+
|
502 |
+
$this->log("Processed schedules. Tasks now: Backup files: $backup_files Backup DB: $backup_database");
|
503 |
+
|
504 |
+
# Possibly now nothing is to be done, except to close the log file
|
505 |
+
if ($backup_files || $backup_database) {
|
506 |
+
|
507 |
+
$backup_contains = "";
|
508 |
+
|
509 |
+
$backup_array = array();
|
510 |
+
|
511 |
+
//backup directories and return a numerically indexed array of file paths to the backup files
|
512 |
+
if ($backup_files) {
|
513 |
+
$this->log("Beginning backup of directories");
|
514 |
+
$backup_array = $this->backup_dirs();
|
515 |
+
$backup_contains = "Files only (no database)";
|
516 |
+
}
|
517 |
+
|
518 |
+
//backup DB and return string of file path
|
519 |
+
if ($backup_database) {
|
520 |
+
$this->log("Beginning backup of database");
|
521 |
+
$db_backup = $this->backup_db();
|
522 |
+
//add db path to rest of files
|
523 |
+
if(is_array($backup_array)) { $backup_array['db'] = $db_backup; }
|
524 |
+
$backup_contains = ($backup_files) ? "Files and database" : "Database only (no files)";
|
525 |
+
}
|
526 |
+
|
527 |
+
//save this to our history so we can track backups for the retain feature
|
528 |
+
$this->log("Saving backup history");
|
529 |
+
$this->save_backup_history($backup_array);
|
530 |
+
|
531 |
+
//cloud operations (S3,Google Drive,FTP,email,nothing)
|
532 |
+
//this also calls the retain feature at the end (done in this method to reuse existing cloud connections)
|
533 |
+
if(is_array($backup_array) && count($backup_array) >0) {
|
534 |
+
$this->log("Beginning dispatch of backup to remote");
|
535 |
+
$this->cloud_backup($backup_array);
|
536 |
+
}
|
537 |
+
//delete local files if the pref is set
|
538 |
+
foreach($backup_array as $file) {
|
539 |
+
$this->delete_local($file);
|
540 |
+
}
|
541 |
+
|
542 |
+
//save the last backup info, including errors, if any
|
543 |
+
$this->log("Saving last backup information into WordPress db");
|
544 |
+
$this->save_last_backup($backup_array);
|
545 |
+
|
546 |
+
if(get_option('updraft_email') != "" && get_option('updraft_service') != 'email') {
|
547 |
+
$sendmail_to = get_option('updraft_email');
|
548 |
+
$this->log("Sending email report to: ".$sendmail_to);
|
549 |
+
$append_log = "";
|
550 |
+
if(get_option('updraft_debug_mode') && $this->logfile_name != "") {
|
551 |
+
$append_log .= "\r\nLog contents:\r\n".file_get_contents($this->logfile_name);
|
552 |
+
}
|
553 |
+
wp_mail($sendmail_to,'Backed up: '.get_bloginfo('name').' (UpdraftPlus) '.date('Y-m-d H:i',time()),'Site: '.site_url()."\r\nUpdraftPlus WordPress backup is complete.\r\nBackup contains: $backup_contains\r\n\r\n".$this->wordshell_random_advert(0)."\r\n".$append_log);
|
554 |
}
|
|
|
555 |
}
|
556 |
|
557 |
// Close log file
|
572 |
$this->log("Cloud backup: S3");
|
573 |
if (count($backup_array) >0) { $this->s3_backup($backup_array); }
|
574 |
break;
|
575 |
+
case 'googledrive':
|
576 |
+
@set_time_limit(900);
|
577 |
+
$this->log("Cloud backup: Google Drive");
|
578 |
+
if (count($backup_array) >0) { $this->googledrive_backup($backup_array); }
|
579 |
+
break;
|
580 |
case 'ftp':
|
581 |
@set_time_limit(900);
|
582 |
$this->log("Cloud backup: FTP");
|
592 |
}
|
593 |
//we don't break here so it goes and executes all the default behavior below as well. this gives us retain behavior for email
|
594 |
default:
|
595 |
+
$this->prune_retained_backups("local");
|
596 |
+
break;
|
597 |
+
}
|
598 |
+
}
|
599 |
+
|
600 |
+
// Carries out retain behaviour. Pass in a valid S3 or FTP object and path if relevant.
|
601 |
+
function prune_retained_backups($updraft_service,$remote_object,$remote_path) {
|
602 |
+
$this->log("Retain: beginning examination of existing backup sets");
|
603 |
+
$updraft_retain = get_option('updraft_retain');
|
604 |
+
// Number of backups to retain
|
605 |
+
$retain = (isset($updraft_retain))?get_option('updraft_retain'):1;
|
606 |
+
$this->log("Retain: user setting: number to retain = $retain");
|
607 |
+
// Returns an array, most recent first, of backup sets
|
608 |
+
$backup_history = $this->get_backup_history();
|
609 |
+
$db_backups_found = 0; $file_backups_found = 0;
|
610 |
+
$this->log("Number of backup sets in history: ".count($backup_history));
|
611 |
+
foreach ($backup_history as $backup_datestamp => $backup_to_examine) {
|
612 |
+
// $backup_to_examine is an array of file names, keyed on db/plugins/themes/uploads
|
613 |
+
// The new backup_history array is saved afterwards, so remember to unset the ones that are to be deleted
|
614 |
+
$this->log("Examining backup set with datestamp: $backup_datestamp");
|
615 |
+
if (isset($backup_to_examine['db'])) {
|
616 |
+
$db_backups_found++;
|
617 |
+
$this->log("$backup_datestamp: this set includes a database (".$backup_to_examine['db']."); db count is now $db_backups_found");
|
618 |
+
if ($db_backups_found > $retain) {
|
619 |
+
$this->log("$backup_datestamp: over retain limit; will delete this database");
|
620 |
+
$file = $backup_to_examine['db'];
|
621 |
+
$this->log("$backup_datestamp: Delete this file: $file");
|
622 |
+
if ($file != '') {
|
623 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
624 |
@unlink($fullpath); //delete it if it's locally available
|
625 |
+
if ($updraft_service == "s3") {
|
626 |
+
$this->log("$backup_datestamp: Delete remote: s3://$remote_path/$file");
|
627 |
+
if (!$remote_object->deleteObject($remote_path, $file)) {
|
628 |
+
$this->error("S3 Error: Failed to delete object $file. Error was ".$php_errormsg);
|
629 |
+
}
|
630 |
+
} elseif ($updraft_service == "ftp") {
|
631 |
+
$this->log("$backup_datestamp: Delete remote ftp: $remote_path/$file");
|
632 |
+
@$remote_object->delete($remote_path.$file);
|
633 |
+
} elseif ($updraft_service == "googledrive") {
|
634 |
+
$this->log("$backup_datestamp: Delete remote file from Google Drive: $remote_path/$file");
|
635 |
+
$this->googledrive_delete_file($remote_path.'/'.$file,$remote_object);
|
636 |
+
}
|
637 |
}
|
638 |
+
unset($backup_to_examine['db']);
|
639 |
}
|
640 |
+
}
|
641 |
+
if (isset($backup_to_examine['plugins']) || isset($backup_to_examine['themes']) || isset($backup_to_examine['uploads'])) {
|
642 |
+
$file_backups_found++;
|
643 |
+
$this->log("$backup_datestamp: this set includes files; fileset count is now $file_backups_found");
|
644 |
+
if ($file_backups_found > $retain) {
|
645 |
+
$this->log("$backup_datestamp: over retain limit; will delete this file set");
|
646 |
+
$file = isset($backup_to_examine['plugins']) ? $backup_to_examine['plugins'] : "";
|
647 |
+
$file2 = isset($backup_to_examine['themes']) ? $backup_to_examine['themes'] : "";
|
648 |
+
$file3 = isset($backup_to_examine['uploads']) ? $backup_to_examine['uploads'] : "";
|
649 |
+
if ($file) {
|
650 |
+
$this->log("$backup_datestamp: Delete this file: $file");
|
651 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
652 |
+
@unlink($fullpath); //delete it if it's locally available
|
653 |
+
if ($updraft_service == "s3") {
|
654 |
+
$this->log("$backup_datestamp: Delete remote: s3://$remote_path/$file");
|
655 |
+
if (!$remote_object->deleteObject($bucket_name, $file)) {
|
656 |
+
$this->error("S3 Error: Failed to delete object $file. Error was ".$php_errormsg);
|
657 |
+
}
|
658 |
+
} elseif ($updraft_service == "ftp") {
|
659 |
+
$this->log("$backup_datestamp: Delete remote ftp: $remote_path/$file");
|
660 |
+
@$remote_object->delete($remote_path.$file);
|
661 |
+
} elseif ($updraft_service == "googledrive") {
|
662 |
+
$this->log("$backup_datestamp: Delete remote file from Google Drive: $remote_path/$file");
|
663 |
+
$this->googledrive_delete_file($remote_path.'/'.$file,$remote_object);
|
664 |
+
}
|
665 |
+
}
|
666 |
+
if ($file2) {
|
667 |
+
$this->log("$backup_datestamp: Delete this file: $file2");
|
668 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file2;
|
669 |
+
@unlink($fullpath); //delete it if it's locally available
|
670 |
+
if ($updraft_service == "s3") {
|
671 |
+
$this->log("$backup_datestamp: Delete remote: s3://$remote_path/$file2");
|
672 |
+
if (!$remote_object->deleteObject($bucket_name, $file2)) {
|
673 |
+
$this->error("S3 Error: Failed to delete object $file2. Error was ".$php_errormsg);
|
674 |
+
}
|
675 |
+
} elseif ($updraft_service == "ftp") {
|
676 |
+
$this->log("$backup_datestamp: Delete remote ftp: $remote_path/$file2");
|
677 |
+
@$remote_object->delete($remote_path.$file2);
|
678 |
+
} elseif ($updraft_service == "googledrive") {
|
679 |
+
$this->log("$backup_datestamp: Delete remote file from Google Drive: $remote_path/$file");
|
680 |
+
$this->googledrive_delete_file($remote_path.'/'.$file,$remote_object);
|
681 |
+
}
|
682 |
+
|
683 |
+
}
|
684 |
+
if ($file3) {
|
685 |
+
$this->log("$backup_datestamp: Delete this file: $file3");
|
686 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file3;
|
687 |
+
@unlink($fullpath); //delete it if it's locally available
|
688 |
+
if ($updraft_service == "s3") {
|
689 |
+
$this->log("$backup_datestamp: Delete remote: s3://$remote_path/$file3");
|
690 |
+
if (!$remote_object->deleteObject($bucket_name, $file3)) {
|
691 |
+
$this->error("S3 Error: Failed to delete object $file3. Error was ".$php_errormsg);
|
692 |
+
}
|
693 |
+
} elseif ($updraft_service == "ftp") {
|
694 |
+
$this->log("$backup_datestamp: Delete remote ftp: $remote_path/$file3");
|
695 |
+
@$remote_object->delete($remote_path.$file3);
|
696 |
+
} elseif ($updraft_service == "googledrive") {
|
697 |
+
$this->log("$backup_datestamp: Delete remote file from Google Drive: $remote_path/$file");
|
698 |
+
$this->googledrive_delete_file($remote_path.'/'.$file,$remote_object);
|
699 |
+
}
|
700 |
+
}
|
701 |
+
unset($backup_to_examine['plugins']);
|
702 |
+
unset($backup_to_examine['themes']);
|
703 |
+
unset($backup_to_examine['uploads']);
|
704 |
+
}
|
705 |
+
}
|
706 |
+
// Delete backup set completely if empty, o/w just remove DB
|
707 |
+
if (count($backup_to_examine)==0) {
|
708 |
+
$this->log("$backup_datestamp: this backup set is now empty; will remove from history");
|
709 |
+
unset($backup_history[$backup_datestamp]);
|
710 |
+
} else {
|
711 |
+
$this->log("$backup_datestamp: this backup set remains non-empty; will retain in history");
|
712 |
+
$backup_history[$backup_datestamp] = $backup_to_examine;
|
713 |
+
}
|
714 |
}
|
715 |
+
$this->log("Retain: saving new backup history (sets now: ".count($backup_history).") and finishing retain operation");
|
716 |
+
update_option('updraft_backup_history',$backup_history);
|
717 |
}
|
718 |
+
|
719 |
function s3_backup($backup_array) {
|
720 |
if(!class_exists('S3')) {
|
721 |
require_once(dirname(__FILE__).'/includes/S3.php');
|
729 |
$this->error("S3 Error: Failed to upload $fullpath. Error was ".$php_errormsg);
|
730 |
}
|
731 |
}
|
732 |
+
$this->prune_retained_backups('s3',$s3,$bucket_name);
|
733 |
} else {
|
734 |
$this->error("S3 Error: Failed to create bucket $bucket_name. Error was ".$php_errormsg);
|
735 |
}
|
736 |
+
}
|
737 |
+
|
738 |
+
function googledrive_backup($backup_array) {
|
739 |
+
if ( $access = $this->access_token( get_option('updraft_googledrive_token'), get_option('updraft_googledrive_clientid'), get_option('updraft_googledrive_secret') ) ) {
|
740 |
+
foreach ($backup_array as $file) {
|
741 |
+
$file_path = trailingslashit(get_option('updraft_dir')).$file;
|
742 |
+
$file_name = basename($file_path);
|
743 |
+
$this->log("$file_name: Attempting to upload to Google Drive");
|
744 |
+
$timer_start = microtime( true );
|
745 |
+
if ( $id = $this->googledrive_upload_file( $file_path, $file_name, get_option('updraft_googledrive_remotepath'), $access ) ) {
|
746 |
+
$this->log('OK: Archive ' . $file_name . ' uploaded to Google Drive in ' . ( round(microtime( true ) - $timer_start,2) ) . ' seconds' );
|
747 |
+
} else {
|
748 |
+
$this->log("ERROR: $file_name: Failed to upload to Google Drive" );
|
|
|
|
|
749 |
}
|
750 |
}
|
751 |
+
$this->prune_retained_backups("googledrive",$access,get_option('updraft_googledrive_remotepath'));
|
752 |
+
} else {
|
753 |
+
$this->log('ERROR: Did not receive an access token from Google', __FILE__, __LINE__ );
|
754 |
}
|
|
|
|
|
755 |
}
|
756 |
|
757 |
function ftp_backup($backup_array) {
|
769 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
770 |
$ftp->put($fullpath,$ftp_remote_path.$file,FTP_BINARY);
|
771 |
}
|
772 |
+
$this->prune_retained_backups("ftp",$ftp,$ftp_remote_path);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
773 |
}
|
774 |
|
775 |
function delete_local($file) {
|
776 |
if(get_option('updraft_delete_local')) {
|
777 |
+
$this->log("Deleting local file: $file");
|
778 |
+
//need error checking so we don't delete what isn't successfully uploaded?
|
779 |
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
780 |
return unlink($fullpath);
|
781 |
}
|
813 |
# Plugins
|
814 |
@set_time_limit(900);
|
815 |
if (get_option('updraft_include_plugins', true)) {
|
816 |
+
$this->log("Beginning backup of plugins");
|
817 |
$plugins = new PclZip($backup_file_base.'-plugins.zip');
|
818 |
if (!$plugins->create($wp_plugins_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
|
819 |
$this->error('Could not create plugins zip. Error was '.$php_errmsg,'fatal');
|
820 |
}
|
821 |
$backup_array['plugins'] = basename($backup_file_base.'-plugins.zip');
|
822 |
+
} else {
|
823 |
+
$this->log("No backup of plugins: excluded by user's options");
|
824 |
}
|
825 |
+
|
826 |
# Themes
|
827 |
@set_time_limit(900);
|
828 |
if (get_option('updraft_include_themes', true)) {
|
829 |
+
$this->log("Beginning backup of themes");
|
830 |
$themes = new PclZip($backup_file_base.'-themes.zip');
|
831 |
if (!$themes->create($wp_themes_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
|
832 |
$this->error('Could not create themes zip. Error was '.$php_errmsg,'fatal');
|
833 |
}
|
834 |
$backup_array['themes'] = basename($backup_file_base.'-themes.zip');
|
835 |
+
} else {
|
836 |
+
$this->log("No backup of themes: excluded by user's options");
|
837 |
}
|
838 |
|
839 |
# Uploads
|
840 |
@set_time_limit(900);
|
841 |
if (get_option('updraft_include_uploads', true)) {
|
842 |
+
$this->log("Beginning backup of uploads");
|
843 |
$uploads = new PclZip($backup_file_base.'-uploads.zip');
|
844 |
if (!$uploads->create($wp_upload_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
|
845 |
$this->error('Could not create uploads zip. Error was '.$php_errmsg,'fatal');
|
846 |
}
|
847 |
$backup_array['uploads'] = basename($backup_file_base.'-uploads.zip');
|
848 |
+
} else {
|
849 |
+
$this->log("No backup of uploads: excluded by user's options");
|
850 |
}
|
|
|
851 |
return $backup_array;
|
852 |
}
|
853 |
|
854 |
function save_backup_history($backup_array) {
|
855 |
//this stores full paths right now. should probably concatenate with ABSPATH to make it easier to move sites
|
|
|
|
|
856 |
if(is_array($backup_array)) {
|
857 |
+
$backup_history = get_option('updraft_backup_history');
|
858 |
+
$backup_history = (is_array($backup_history)) ? $backup_history : array();
|
859 |
$backup_history[$this->backup_time] = $backup_array;
|
860 |
update_option('updraft_backup_history',$backup_history);
|
861 |
} else {
|
880 |
/*START OF WB-DB-BACKUP BLOCK*/
|
881 |
|
882 |
function backup_db() {
|
883 |
+
|
884 |
+
$total_tables = 0;
|
885 |
+
|
886 |
global $table_prefix, $wpdb;
|
887 |
if(!$this->backup_time) {
|
888 |
$this->backup_time_nonce();
|
913 |
//$this->error(__('The backup directory is not writable!','wp-db-backup'));
|
914 |
}
|
915 |
|
|
|
916 |
//Begin new backup of MySql
|
917 |
$this->stow("# " . __('WordPress MySQL database backup','wp-db-backup') . "\n");
|
918 |
$this->stow("#\n");
|
930 |
}
|
931 |
|
932 |
foreach ($all_tables as $table) {
|
933 |
+
$total_tables++;
|
934 |
// Increase script execution time-limit to 15 min for every table.
|
935 |
if ( !ini_get('safe_mode')) @set_time_limit(15*60);
|
936 |
if ( strpos($table, $table_prefix) == 0 ) {
|
960 |
# Encrypt, if requested
|
961 |
$encryption = get_option('updraft_encryptionphrase');
|
962 |
if (strlen($encryption) > 0) {
|
963 |
+
$this->log("Database: applying encryption");
|
964 |
$encryption_error = 0;
|
965 |
require_once(dirname(__FILE__).'/includes/Rijndael.php');
|
966 |
$rijndael = new Crypt_Rijndael();
|
985 |
return basename($backup_file_base.'-db.gz');
|
986 |
}
|
987 |
}
|
988 |
+
$this->log("Total database tables backed up: $total_tables");
|
989 |
|
990 |
} //wp_db_backup
|
991 |
|
1002 |
function backup_table($table, $segment = 'none') {
|
1003 |
global $wpdb;
|
1004 |
|
1005 |
+
$total_rows = 0;
|
1006 |
+
|
1007 |
$table_structure = $wpdb->get_results("DESCRIBE $table");
|
1008 |
if (! $table_structure) {
|
1009 |
//$this->error(__('Error getting table details','wp-db-backup') . ": $table");
|
1093 |
$replace = array('\0', '\n', '\r', '\Z');
|
1094 |
if($table_data) {
|
1095 |
foreach ($table_data as $row) {
|
1096 |
+
$total_rows++;
|
1097 |
$values = array();
|
1098 |
foreach ($row as $key => $value) {
|
1099 |
if ($ints[strtolower($key)]) {
|
1120 |
$this->stow("# --------------------------------------------------------\n");
|
1121 |
$this->stow("\n");
|
1122 |
}
|
1123 |
+
$this->log("Table $table: Total rows added: $total_rows");
|
1124 |
+
|
1125 |
} // end backup_table()
|
1126 |
|
1127 |
|
1159 |
return true;
|
1160 |
}
|
1161 |
|
|
|
|
|
1162 |
/**
|
1163 |
* Add backquotes to tables and db-names in
|
1164 |
* SQL queries. Taken from phpMyAdmin.
|
1189 |
return str_replace('\'', '\\\'', $a_string);
|
1190 |
}
|
1191 |
|
|
|
|
|
|
|
|
|
1192 |
/*END OF WP-DB-BACKUP BLOCK */
|
1193 |
|
1194 |
/*
|
1210 |
return wp_filter_nohtml_kses($interval);
|
1211 |
}
|
1212 |
|
1213 |
+
function schedule_backup_database($interval) {
|
1214 |
+
//clear schedule and add new so we don't stack up scheduled backups
|
1215 |
+
wp_clear_scheduled_hook('updraft_backup_database');
|
1216 |
+
switch($interval) {
|
1217 |
+
case 'daily':
|
1218 |
+
case 'weekly':
|
1219 |
+
case 'monthly':
|
1220 |
+
wp_schedule_event(time()+30, $interval, 'updraft_backup_database');
|
1221 |
+
break;
|
1222 |
+
}
|
1223 |
+
return wp_filter_nohtml_kses($interval);
|
1224 |
+
}
|
1225 |
+
|
1226 |
//wp-cron only has hourly, daily and twicedaily, so we need to add weekly and monthly.
|
1227 |
function modify_cron_schedules($schedules) {
|
1228 |
$schedules['weekly'] = array(
|
1303 |
$this->delete_local($file);
|
1304 |
exit; //we exit immediately because otherwise admin-ajax appends an additional zero to the end for some reason I don't understand. seriously, why die('0')?
|
1305 |
} else {
|
1306 |
+
echo 'Download failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then S3 or Google Drive or FTP retrieval may have failed. (Note that Google Drive downloading is not yet supported - you need to download manually if you use Google Drive).';
|
1307 |
}
|
1308 |
}
|
1309 |
|
1310 |
function download_backup($file) {
|
1311 |
switch(get_option('updraft_service')) {
|
1312 |
+
case 'googledrive':
|
1313 |
+
$this->download_googledrive_backup($file);
|
1314 |
+
break;
|
1315 |
case 's3':
|
1316 |
$this->download_s3_backup($file);
|
1317 |
break;
|
1323 |
}
|
1324 |
}
|
1325 |
|
1326 |
+
function download_googledrive_backup($file) {
|
1327 |
+
$this->error("Google Drive error: we do not yet support downloading existing backups from Google Drive - you need to restore the backup manually");
|
1328 |
+
}
|
1329 |
+
|
1330 |
function download_s3_backup($file) {
|
1331 |
if(!class_exists('S3')) {
|
1332 |
require_once(dirname(__FILE__).'/includes/S3.php');
|
1366 |
return false;
|
1367 |
}
|
1368 |
|
1369 |
+
$credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_restore&backup_timestamp=$timestamp");
|
1370 |
WP_Filesystem($credentials);
|
1371 |
if ( $wp_filesystem->errors->get_error_code() ) {
|
1372 |
foreach ( $wp_filesystem->errors->get_error_messages() as $message )
|
1405 |
return true;
|
1406 |
}
|
1407 |
|
|
|
1408 |
//deletes the -old directories that are created when a backup is restored.
|
1409 |
function delete_old_dirs() {
|
1410 |
global $wp_filesystem;
|
1411 |
+
$credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_delete_old_dirs");
|
1412 |
WP_Filesystem($credentials);
|
1413 |
if ( $wp_filesystem->errors->get_error_code() ) {
|
1414 |
foreach ( $wp_filesystem->errors->get_error_messages() as $message )
|
1450 |
|
1451 |
function create_backup_dir() {
|
1452 |
global $wp_filesystem;
|
1453 |
+
$credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_create_backup_dir");
|
1454 |
WP_Filesystem($credentials);
|
1455 |
if ( $wp_filesystem->errors->get_error_code() ) {
|
1456 |
foreach ( $wp_filesystem->errors->get_error_messages() as $message )
|
1506 |
}
|
1507 |
wp_enqueue_script('jquery');
|
1508 |
register_setting( 'updraft-options-group', 'updraft_interval', array($this,'schedule_backup') );
|
1509 |
+
register_setting( 'updraft-options-group', 'updraft_interval_database', array($this,'schedule_backup_database') );
|
1510 |
register_setting( 'updraft-options-group', 'updraft_retain', array($this,'retain_range') );
|
1511 |
register_setting( 'updraft-options-group', 'updraft_encryptionphrase', 'wp_filter_nohtml_kses' );
|
1512 |
register_setting( 'updraft-options-group', 'updraft_service', 'wp_filter_nohtml_kses' );
|
1513 |
register_setting( 'updraft-options-group', 'updraft_s3_login', 'wp_filter_nohtml_kses' );
|
1514 |
register_setting( 'updraft-options-group', 'updraft_s3_pass', 'wp_filter_nohtml_kses' );
|
1515 |
+
register_setting( 'updraft-options-group', 'updraft_s3_remote_path', 'wp_filter_nohtml_kses' );
|
1516 |
+
register_setting( 'updraft-options-group', 'updraft_googledrive_clientid', 'wp_filter_nohtml_kses' );
|
1517 |
+
register_setting( 'updraft-options-group', 'updraft_googledrive_secret', 'wp_filter_nohtml_kses' );
|
1518 |
+
register_setting( 'updraft-options-group', 'updraft_googledrive_remotepath', 'wp_filter_nohtml_kses' );
|
1519 |
register_setting( 'updraft-options-group', 'updraft_ftp_login', 'wp_filter_nohtml_kses' );
|
1520 |
register_setting( 'updraft-options-group', 'updraft_ftp_pass', 'wp_filter_nohtml_kses' );
|
1521 |
register_setting( 'updraft-options-group', 'updraft_dir', 'wp_filter_nohtml_kses' );
|
1522 |
register_setting( 'updraft-options-group', 'updraft_email', 'wp_filter_nohtml_kses' );
|
|
|
1523 |
register_setting( 'updraft-options-group', 'updraft_ftp_remote_path', 'wp_filter_nohtml_kses' );
|
1524 |
register_setting( 'updraft-options-group', 'updraft_server_address', 'wp_filter_nohtml_kses' );
|
1525 |
register_setting( 'updraft-options-group', 'updraft_delete_local', 'absint' );
|
1528 |
register_setting( 'updraft-options-group', 'updraft_include_themes', 'absint' );
|
1529 |
register_setting( 'updraft-options-group', 'updraft_include_uploads', 'absint' );
|
1530 |
|
1531 |
+
/* I see no need for this check; people can only download backups/logs if they can guess a nonce formed from a random number and if .htaccess files have no effect. The database will be encrypted. Very unlikely.
|
1532 |
if (current_user_can('manage_options')) {
|
1533 |
$updraft_dir = $this->backups_dir_location();
|
1534 |
if(strpos($updraft_dir,WP_CONTENT_DIR) !== false) {
|
1542 |
add_action('admin_notices', array($this,'show_admin_warning_accessible') );
|
1543 |
}
|
1544 |
}
|
|
|
|
|
1545 |
}
|
1546 |
}
|
1547 |
+
*/
|
1548 |
+
if (current_user_can('manage_options') && get_option('updraft_googledrive_clientid') != "" && get_option('updraft_googledrive_token','xyz') == 'xyz') {
|
1549 |
+
add_action('admin_notices', array($this,'show_admin_warning_googledrive') );
|
1550 |
+
}
|
1551 |
}
|
|
|
1552 |
|
1553 |
function add_admin_pages() {
|
1554 |
+
add_submenu_page('options-general.php', "UpdraftPlus", "UpdraftPlus", "manage_options", "updraftplus",
|
1555 |
array($this,"settings_output"));
|
1556 |
}
|
1557 |
|
1581 |
$backup_success = $this->restore_backup($_REQUEST['backup_timestamp']);
|
1582 |
if(empty($this->errors) && $backup_success == true) {
|
1583 |
echo '<p>Restore successful!</p><br/>';
|
1584 |
+
echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus&updraft_restore_success=true">Return to Updraft Configuration</a>.';
|
1585 |
return;
|
1586 |
} else {
|
1587 |
echo '<p>Restore failed...</p><br/>';
|
1588 |
+
echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.';
|
1589 |
return;
|
1590 |
}
|
1591 |
//uncomment the below once i figure out how i want the flow of a restoration to work.
|
1592 |
+
//echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.';
|
1593 |
}
|
1594 |
$deleted_old_dirs = false;
|
1595 |
if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'updraft_delete_old_dirs') {
|
1599 |
echo '<p>Old directory removal failed for some reason. You may want to do this manually.</p><br/>';
|
1600 |
}
|
1601 |
echo '<p>Old directories successfully removed.</p><br/>';
|
1602 |
+
echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.';
|
1603 |
return;
|
1604 |
}
|
1605 |
|
1606 |
+
if(isset($_GET['error'])) {
|
1607 |
+
echo "<p><strong>ERROR:</strong> ".htmlspecialchars($_GET['error'])."</p>";
|
1608 |
+
}
|
1609 |
+
if(isset($_GET['message'])) {
|
1610 |
+
echo "<p><strong>Note:</strong> ".htmlspecialchars($_GET['message'])."</p>";
|
1611 |
+
}
|
1612 |
+
|
1613 |
if(isset($_GET['action']) && $_GET['action'] == 'updraft_create_backup_dir') {
|
1614 |
if(!$this->create_backup_dir()) {
|
1615 |
echo '<p>Backup directory could not be created...</p><br/>';
|
1616 |
}
|
1617 |
echo '<p>Backup directory successfully created.</p><br/>';
|
1618 |
+
echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.';
|
1619 |
return;
|
1620 |
}
|
1621 |
|
1622 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') {
|
1623 |
+
wp_schedule_single_event(time()+5, 'updraft_backup_all');
|
1624 |
}
|
1625 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') {
|
1626 |
+
$this->backup(true,true);
|
1627 |
}
|
1628 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_db') {
|
1629 |
$this->backup_db();
|
1634 |
<h2>UpdraftPlus - Backup/Restore</h2>
|
1635 |
|
1636 |
Version: <b><?php echo $this->version; ?></b><br />
|
1637 |
+
Maintained by <b>David Anderson</b> (<a href="http://david.dw-perspective.org.uk">Homepage</a> | <a href="http://wordshell.net">WordShell - WordPress command line</a> | <a href="http://david.dw-perspective.org.uk/donate">Donate</a>)
|
1638 |
<br />
|
1639 |
Based on Updraft by <b>Paul Kehrer</b> (<a href="http://langui.sh" target="_blank">Blog</a> | <a href="http://twitter.com/reaperhulk" target="_blank">Twitter</a> )
|
1640 |
<br />
|
1673 |
<tr>
|
1674 |
<?php
|
1675 |
$next_scheduled_backup = wp_next_scheduled('updraft_backup');
|
1676 |
+
$next_scheduled_backup = ($next_scheduled_backup) ? date('D, F j, Y H:i T',$next_scheduled_backup) : 'No backups are scheduled at this time.';
|
1677 |
+
$next_scheduled_backup_database = wp_next_scheduled('updraft_backup_database');
|
1678 |
+
if (get_option('updraft_interval_database',get_option('updraft_interval')) == get_option('updraft_interval')) {
|
1679 |
+
$next_scheduled_backup_database = "Will take place at the same time as the files backup.";
|
1680 |
} else {
|
1681 |
+
$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.';
|
1682 |
}
|
1683 |
$current_time = date('D, F j, Y H:i T',time());
|
1684 |
$updraft_last_backup = get_option('updraft_last_backup');
|
1701 |
$backup_disabled = "";
|
1702 |
} else {
|
1703 |
$backup_disabled = 'disabled="disabled"';
|
1704 |
+
$dir_info = '<span style="color:red">Backup directory specified is <b>not</b> writable. <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>';
|
1705 |
}
|
1706 |
?>
|
1707 |
+
<th>Now:</th>
|
1708 |
<td style="color:blue"><?php echo $current_time?></td>
|
1709 |
</tr>
|
1710 |
<tr>
|
1711 |
+
<th>Next Scheduled Files Backup:</th>
|
1712 |
<td style="color:blue"><?php echo $next_scheduled_backup?></td>
|
1713 |
</tr>
|
1714 |
+
<tr>
|
1715 |
+
<th>Next Scheduled DB Backup:</th>
|
1716 |
+
<td style="color:blue"><?php echo $next_scheduled_backup_database?></td>
|
1717 |
+
</tr>
|
1718 |
<tr>
|
1719 |
<th>Last Backup:</th>
|
1720 |
<td style="color:<?php echo $last_backup_color ?>"><?php echo $last_backup?></td>
|
1823 |
<td></td><td><?php echo $dir_info ?> This is where Updraft Backup/Restore will write the zip files it creates initially. This directory must be writable by your web server. Typically you'll want to have it inside your wp-content folder (this is the default). <b>Do not</b> place it inside your uploads dir, as that will cause recursion issues (backups of backups of backups of...).</td>
|
1824 |
</tr>
|
1825 |
<tr>
|
1826 |
+
<th>File Backup Intervals:</th>
|
1827 |
<td><select name="updraft_interval">
|
1828 |
<?php
|
1829 |
$intervals = array ("manual", "daily", "weekly", "monthly");
|
1830 |
foreach ($intervals as $ival) {
|
1831 |
echo "<option value=\"$ival\" ";
|
1832 |
+
if ($ival == get_option('updraft_interval','manual')) { echo 'selected="selected"';}
|
1833 |
+
echo ">".ucfirst($ival)."</option>\n";
|
1834 |
+
}
|
1835 |
+
?>
|
1836 |
+
</select></td>
|
1837 |
+
</tr>
|
1838 |
+
<tr>
|
1839 |
+
<th>Database Backup Intervals:</th>
|
1840 |
+
<td><select name="updraft_interval_database">
|
1841 |
+
<?php
|
1842 |
+
$intervals = array ("manual", "daily", "weekly", "monthly");
|
1843 |
+
foreach ($intervals as $ival) {
|
1844 |
+
echo "<option value=\"$ival\" ";
|
1845 |
+
if ($ival == get_option('updraft_interval_database',get_option('updraft_interval'))) { echo 'selected="selected"';}
|
1846 |
echo ">".ucfirst($ival)."</option>\n";
|
1847 |
}
|
1848 |
?>
|
1849 |
</select></td>
|
1850 |
</tr>
|
1851 |
<tr class="backup-interval-description">
|
1852 |
+
<td></td><td>If you would like to automatically schedule backups, choose schedules from the dropdown above. Backups will occur at the interval specified starting just after the current time. If you choose manual you must click the "Backup Now!" button whenever you wish a backup to occur. If the two schedules are the same, then the two backups will take place together.</td>
|
1853 |
</tr>
|
1854 |
<?php
|
1855 |
# The true (default value if non-existent) here has the effect of forcing a default of on.
|
1858 |
$include_uploads = (get_option('updraft_include_uploads',true)) ? 'checked="checked"' : "";
|
1859 |
?>
|
1860 |
<tr>
|
1861 |
+
<th>Include in Files Backup:</th>
|
1862 |
<td>
|
1863 |
<input type="checkbox" name="updraft_include_plugins" value="1" <?php echo $include_plugins; ?> /> Plugins<br />
|
1864 |
<input type="checkbox" name="updraft_include_themes" value="1" <?php echo $include_themes; ?> /> Themes<br />
|
1865 |
<input type="checkbox" name="updraft_include_uploads" value="1" <?php echo $include_uploads; ?> /> Uploads<br />
|
1866 |
+
Include all of these, unless you are backing them up separately. Note that presently UpdraftPlus backs up these directories only - which is usually everything (except for WordPress core itself which you can download afresh from WordPress.org). But if you have made customised modifications outside of these directories, you need to back them up another way.<br />(<a href="http://wordshell.net">Use WordShell</a> for automatic backup, version control and patching).<br /></td>
|
1867 |
</td>
|
1868 |
</tr>
|
1869 |
<tr>
|
1875 |
<td><input type="text" name="updraft_retain" value="<?php echo $retain ?>" style="width:50px" /></td>
|
1876 |
</tr>
|
1877 |
<tr class="backup-retain-description">
|
1878 |
+
<td></td><td>By default only the most recent backup is retained. If you'd like to preserve more, specify the number here. (This many of <strong>both</strong> files and database backups will be retained.)</td>
|
1879 |
</tr>
|
1880 |
<tr>
|
1881 |
+
<th>Database encryption phrase:</th>
|
1882 |
<?php
|
1883 |
$updraft_encryptionphrase = get_option('updraft_encryptionphrase');
|
1884 |
?>
|
1896 |
$debug_mode = (get_option('updraft_debug_mode')) ? 'checked="checked"' : "";
|
1897 |
|
1898 |
$display_none = 'style="display:none"';
|
1899 |
+
$s3 = ""; $ftp = ""; $email = ""; $googledrive="";
|
1900 |
$email_display="";
|
1901 |
$display_email_complete = "";
|
1902 |
$set = 'selected="selected"';
|
1903 |
switch(get_option('updraft_service')) {
|
1904 |
case 's3':
|
1905 |
$s3 = $set;
|
1906 |
+
$googledrive_display = $display_none;
|
1907 |
+
$ftp_display = $display_none;
|
1908 |
+
break;
|
1909 |
+
case 'googledrive':
|
1910 |
+
$googledrive = $set;
|
1911 |
+
$s3_display = $display_none;
|
1912 |
$ftp_display = $display_none;
|
1913 |
break;
|
1914 |
case 'ftp':
|
1915 |
$ftp = $set;
|
1916 |
+
$googledrive_display = $display_none;
|
1917 |
$s3_display = $display_none;
|
1918 |
break;
|
1919 |
case 'email':
|
1920 |
$email = $set;
|
1921 |
$ftp_display = $display_none;
|
1922 |
$s3_display = $display_none;
|
1923 |
+
$googledrive_display = $display_none;
|
1924 |
$display_email_complete = $display_none;
|
1925 |
break;
|
1926 |
default:
|
1927 |
$none = $set;
|
1928 |
$ftp_display = $display_none;
|
1929 |
+
$googledrive_display = $display_none;
|
1930 |
$s3_display = $display_none;
|
1931 |
$display_delete_local = $display_none;
|
1932 |
break;
|
1934 |
?>
|
1935 |
<option value="none" <?php echo $none?>>None</option>
|
1936 |
<option value="s3" <?php echo $s3?>>Amazon S3</option>
|
1937 |
+
<option value="googledrive" <?php echo $googledrive?>>Google Drive (experimental, may work for you, may not)</option>
|
1938 |
<option value="ftp" <?php echo $ftp?>>FTP</option>
|
1939 |
<option value="email" <?php echo $email?>>E-mail</option>
|
1940 |
</select></td>
|
1941 |
</tr>
|
1942 |
<tr class="backup-service-description">
|
1943 |
+
<td></td><td>Choose your backup method. Be aware that mail servers tend to have strict file size limitations; typically around 10-20Mb; backups larger than this may not arrive. Select none if you do not wish to send your backups anywhere <b>(not recommended)</b>.</td>
|
1944 |
|
1945 |
</tr>
|
1946 |
+
|
1947 |
+
<!-- Amazon S3 -->
|
1948 |
<tr class="s3" <?php echo $s3_display?>>
|
1949 |
<th>S3 access key:</th>
|
1950 |
<td><input type="text" autocomplete="off" style="width:292px" name="updraft_s3_login" value="<?php echo get_option('updraft_s3_login') ?>" /></td>
|
1961 |
<th></th>
|
1962 |
<td><p>Get your access key and secret key from your AWS page, then pick a (globally unique) bucket name (letters and numbers) to use for storage. (Do not enter the s3:// prefix).</p></td>
|
1963 |
</tr>
|
1964 |
+
|
1965 |
+
<!-- Google Drive -->
|
1966 |
+
<tr class="googledrive" <?php echo $googledrive_display?>>
|
1967 |
+
<th>Google Drive Client ID:</th>
|
1968 |
+
<td><input type="text" autocomplete="off" style="width:332px" name="updraft_googledrive_clientid" value="<?php echo get_option('updraft_googledrive_clientid') ?>" /></td>
|
1969 |
+
</tr>
|
1970 |
+
<tr class="googledrive" <?php echo $googledrive_display?>>
|
1971 |
+
<th>Google Drive Client Secret:</th>
|
1972 |
+
<td><input type="password" autocomplete="off" style="width:332px" name="updraft_googledrive_secret" value="<?php echo get_option('updraft_googledrive_secret'); ?>" /></td>
|
1973 |
+
</tr>
|
1974 |
+
<tr class="googledrive" <?php echo $googledrive_display?>>
|
1975 |
+
<th>Google Drive Folder ID:</th>
|
1976 |
+
<td><input type="text" style="width:332px" name="updraft_googledrive_remotepath" value="<?php echo get_option('updraft_googledrive_remotepath'); ?>" /> <em>(Leave empty to use your root folder)</em></td>
|
1977 |
+
</tr>
|
1978 |
+
<tr class="googledrive" <?php echo $googledrive_display?>>
|
1979 |
+
<th>Authenticate with Google:</th>
|
1980 |
+
<td><p><a href="?page=updraftplus&action=auth&updraftplus_googleauth=doit"><strong>After</strong> you have saved your settings (by clicking "Save Changes" below), then come back here once and click this link to complete authentication with Google.</a>
|
1981 |
+
|
1982 |
+
<?php
|
1983 |
+
if (get_option('updraft_googledrive_token','xyz') != 'xyz') {
|
1984 |
+
echo " (You appear to be already authenticated)";
|
1985 |
+
}
|
1986 |
+
?>
|
1987 |
+
</p>
|
1988 |
+
<p>To get a folder's ID navigate to that folder in Google Drive and copy the ID from your browser's address bar. It is the part that comes after <kbd>#folders/.</kbd></p>
|
1989 |
+
<p><strong>N.B. : If you choose Google Drive, then no backups will be deleted - all will be retained. Patches welcome!</strong></p>
|
1990 |
+
</td>
|
1991 |
+
</tr>
|
1992 |
+
<tr class="googledrive" <?php echo $googledrive_display?>>
|
1993 |
+
<th></th>
|
1994 |
+
<td><p>Create a Client ID in the API Access section of your <a href="https://code.google.com/apis/console/">Google API Console</a>. Select 'Web Application' as the application type.</p><p>You must add <kbd><?php echo admin_url('options-general.php?page=updraftplus&action=auth'); ?></kbd> as the authorised redirect URI when asked.</p>
|
1995 |
+
<?php
|
1996 |
+
if (!class_exists('SimpleXMLElement')) { echo "<p><b>WARNING:</b> You do not have SimpleXMLElement installed. Google Drive backups will <b>not</b> work until you do.</p>"; }
|
1997 |
+
?>
|
1998 |
+
</td>
|
1999 |
+
</tr>
|
2000 |
+
|
2001 |
<tr class="ftp" <?php echo $ftp_display?>>
|
2002 |
<th><a href="#" title="Click for help!" onclick="jQuery('.ftp-description').toggle();return false;">FTP Server:</a></th>
|
2003 |
<td><input type="text" style="width:260px" name="updraft_server_address" value="<?php echo get_option('updraft_server_address'); ?>" /></td>
|
2023 |
</tr>
|
2024 |
<tr class="deletelocal s3 ftp email" <?php echo $display_delete_local?>>
|
2025 |
<th>Delete local backup:</th>
|
2026 |
+
<td><input type="checkbox" name="updraft_delete_local" value="1" <?php echo $delete_local; ?> /> <br />Check this to delete the local backup file (only sensible if you have enabled a remote backup, otherwise you will have no backup remaining).</td>
|
2027 |
</tr>
|
2028 |
<tr>
|
2029 |
<th>Debug mode:</th>
|
2064 |
jQuery('#updraft-service').change(function() {
|
2065 |
switch(jQuery(this).val()) {
|
2066 |
case 'none':
|
2067 |
+
jQuery('.deletelocal,.s3,.ftp,.googledrive,.s3-description,.ftp-description').hide()
|
2068 |
jQuery('.email,.email-complete').show()
|
2069 |
break;
|
2070 |
case 's3':
|
2071 |
+
jQuery('.ftp,.ftp-description,.googledrive').hide()
|
2072 |
jQuery('.s3,.deletelocal,.email,.email-complete').show()
|
2073 |
break;
|
2074 |
+
case 'googledrive':
|
2075 |
+
jQuery('.ftp,.ftp-description,.s3').hide()
|
2076 |
+
jQuery('.googledrive,.deletelocal,.googledrive,.email,.email-complete').show()
|
2077 |
+
break;
|
2078 |
case 'ftp':
|
2079 |
+
jQuery('.googledrive,.s3,.s3-description').hide()
|
2080 |
jQuery('.ftp,.deletelocal,.email,.email-complete').show()
|
2081 |
break;
|
2082 |
case 'email':
|
2083 |
+
jQuery('.s3,.ftp,.s3-description,.googledrive,.ftp-description,.email-complete').hide()
|
2084 |
jQuery('.email,.deletelocal').show()
|
2085 |
break;
|
2086 |
}
|
2147 |
function show_admin_warning_accessible() {
|
2148 |
$this->show_admin_warning("UpdraftPlus backup directory specified is accessible via the web. This is a potential security problem (people may be able to download your backups - which is undesirable if your database is not encrypted and if you have non-public assets amongst the files). If using Apache, enable .htaccess support to allow web access to be denied; otherwise, you should deny access manually.");
|
2149 |
}
|
2150 |
+
function show_admin_warning_googledrive() {
|
2151 |
+
$this->show_admin_warning('UpdraftPlus notice: <a href="?page=updraftplus&action=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>');
|
2152 |
+
}
|
2153 |
function show_admin_warning_accessible_unknownresult() {
|
2154 |
$this->show_admin_warning("UpdraftPlus tried to check if the backup directory is accessible via web, but the result was unknown.");
|
2155 |
}
|