Version Description
- 11/22/2012 =
- Completed basic Google Drive support (thanks to Sorin Iclanzan, code taken from "Backup" plugin under GPLv3+); now supporting uploading, purging and restoring - i.e. full UpdraftPlus functionality
- Licence change to GPLv3+ (from GPLv2+) to allow incorporating Sorin's code
- Tidied/organised the settings screen further
Download this release
Release Info
Developer | DavidAnderson |
Plugin | UpdraftPlus WordPress Backup Plugin |
Version | 0.9.10 |
Comparing to | |
See all releases |
Code changes from version 0.9.2 to 0.9.10
- includes/class-gdocs.php +627 -0
- readme.txt +10 -5
- updraftplus.php +304 -345
includes/class-gdocs.php
ADDED
@@ -0,0 +1,627 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// Originally contained: GDocs class
|
4 |
+
// Contains: UpdraftPlus_GDocs class (new methods added - could not extend, as too much was private)
|
5 |
+
|
6 |
+
// The following copyright notice is reproduced exactly as found in the "Backup" plugin (http://wordpress.org/extend/plugins/backup)
|
7 |
+
// It applies to the code apart from the methods we added (get_content_link, download_data)
|
8 |
+
|
9 |
+
/*
|
10 |
+
Copyright 2012 Sorin Iclanzan (email : sorin@hel.io)
|
11 |
+
|
12 |
+
This file is part of Backup.
|
13 |
+
|
14 |
+
Backup is free software: you can redistribute it and/or modify
|
15 |
+
it under the terms of the GNU General Public License as published by
|
16 |
+
the Free Software Foundation, either version 3 of the License, or
|
17 |
+
(at your option) any later version.
|
18 |
+
|
19 |
+
Backup is distributed in the hope that it will be useful,
|
20 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
21 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
22 |
+
GNU General Public License for more details.
|
23 |
+
|
24 |
+
You should have received a copy of the GNU General Public License
|
25 |
+
along with Backup. If not, see http://www.gnu.org/licenses/gpl.html.
|
26 |
+
*/
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Google Docs class
|
30 |
+
*
|
31 |
+
* Implements communication with Google Docs via the Google Documents List v3 API.
|
32 |
+
*
|
33 |
+
* Currently uploading, resuming and deleting resources is implemented as well as retrieving quotas.
|
34 |
+
*
|
35 |
+
* @uses WP_Error for storing error messages.
|
36 |
+
*/
|
37 |
+
class UpdraftPlus_GDocs {
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Stores the API version.
|
41 |
+
*
|
42 |
+
* @var string
|
43 |
+
* @access private
|
44 |
+
*/
|
45 |
+
private $gdata_version;
|
46 |
+
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Stores the base URL for the API requests.
|
50 |
+
*
|
51 |
+
* @var string
|
52 |
+
* @access private
|
53 |
+
*/
|
54 |
+
private $base_url;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Stores the URL to the metadata feed.
|
58 |
+
* @var string
|
59 |
+
*/
|
60 |
+
private $metadata_url;
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Stores the token needed to access the API.
|
64 |
+
* @var string
|
65 |
+
* @access private
|
66 |
+
*/
|
67 |
+
private $token;
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Stores feeds to avoid requesting them again for successive use.
|
71 |
+
*
|
72 |
+
* @var array
|
73 |
+
* @access private
|
74 |
+
*/
|
75 |
+
private $cache = array();
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Files are uploadded in chunks of this size in bytes.
|
79 |
+
*
|
80 |
+
* @var integer
|
81 |
+
* @access private
|
82 |
+
*/
|
83 |
+
private $chunk_size;
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Stores whether or not to verify host SSL certificate.
|
87 |
+
*
|
88 |
+
* @var boolean
|
89 |
+
* @access private
|
90 |
+
*/
|
91 |
+
private $ssl_verify;
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Stores the number of seconds to wait for a response before timing out.
|
95 |
+
*
|
96 |
+
* @var integer
|
97 |
+
* @access private
|
98 |
+
*/
|
99 |
+
private $request_timeout;
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Stores the MIME type of the file that is uploading
|
103 |
+
*
|
104 |
+
* @var string
|
105 |
+
* @access private
|
106 |
+
*/
|
107 |
+
private $upload_file_type;
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Stores info about the file being uploaded.
|
111 |
+
*
|
112 |
+
* @var array
|
113 |
+
* @access private
|
114 |
+
*/
|
115 |
+
private $file;
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Stores the number of seconds the upload process is allowed to run
|
119 |
+
*
|
120 |
+
* @var integer
|
121 |
+
* @access private
|
122 |
+
*/
|
123 |
+
private $time_limit;
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Stores a timer for upload processes
|
127 |
+
*
|
128 |
+
* @var array
|
129 |
+
*/
|
130 |
+
private $timer;
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Constructor - Sets the access token.
|
134 |
+
*
|
135 |
+
* @param string $token Access token
|
136 |
+
*/
|
137 |
+
function __construct( $token ) {
|
138 |
+
$this->token = $token;
|
139 |
+
$this->gdata_version = '3.0';
|
140 |
+
$this->base_url = 'https://docs.google.com/feeds/default/private/full/';
|
141 |
+
$this->metadata_url = 'https://docs.google.com/feeds/metadata/default';
|
142 |
+
$this->chunk_size = 524288; // 512 KiB
|
143 |
+
$this->max_resume_attempts = 5;
|
144 |
+
$this->request_timeout = 5;
|
145 |
+
$this->ssl_verify = true;
|
146 |
+
$this->timer = array(
|
147 |
+
'start' => 0,
|
148 |
+
'stop' => 0,
|
149 |
+
'delta' => 0,
|
150 |
+
'cycle' => 0
|
151 |
+
);
|
152 |
+
$this->time_limit = @ini_get( 'max_execution_time' );
|
153 |
+
if ( ! $this->time_limit && '0' !== $this->time_limit )
|
154 |
+
$this->time_limit = 30; // default php max exec time
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Sets an option.
|
159 |
+
*
|
160 |
+
* @access public
|
161 |
+
* @param string $option The option to set.
|
162 |
+
* @param mixed $value The value to set the option to.
|
163 |
+
*/
|
164 |
+
public function set_option( $option, $value ) {
|
165 |
+
switch ( $option ) {
|
166 |
+
case 'chunk_size':
|
167 |
+
if ( floatval($value) >= 0.5 ) {
|
168 |
+
$this->chunk_size = floatval($value) * 1024 * 1024; // Transform from MiB to bytes
|
169 |
+
return true;
|
170 |
+
}
|
171 |
+
break;
|
172 |
+
case 'ssl_verify':
|
173 |
+
$this->ssl_verify = ( bool ) $value;
|
174 |
+
return true;
|
175 |
+
case 'request_timeout':
|
176 |
+
if ( intval( $value ) > 0 ) {
|
177 |
+
$this->request_timeout = intval( $value );
|
178 |
+
return true;
|
179 |
+
}
|
180 |
+
break;
|
181 |
+
case 'max_resume_attempts':
|
182 |
+
$this->max_resume_attempts = intval($value);
|
183 |
+
return true;
|
184 |
+
}
|
185 |
+
return false;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Gets an option.
|
190 |
+
*
|
191 |
+
* @access public
|
192 |
+
* @param string $option The option to get.
|
193 |
+
*/
|
194 |
+
public function get_option( $option ) {
|
195 |
+
switch ( $option ) {
|
196 |
+
case 'chunk_size':
|
197 |
+
return $this->chunk_size;
|
198 |
+
case 'ssl_verify':
|
199 |
+
return $this->ssl_verify;
|
200 |
+
case 'request_timeout':
|
201 |
+
return $this->request_timeout;
|
202 |
+
case 'max_resume_attempts':
|
203 |
+
return $this->max_resume_attempts;
|
204 |
+
}
|
205 |
+
return false;
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* This function makes all the requests to the API.
|
210 |
+
*
|
211 |
+
* @uses wp_remote_request
|
212 |
+
* @access private
|
213 |
+
* @param string $url The URL where the request is sent.
|
214 |
+
* @param string $method The HTTP request method, defaults to 'GET'.
|
215 |
+
* @param array $headers Headers to be sent.
|
216 |
+
* @param string $body The body of the request.
|
217 |
+
* @return mixed Returns an array containing the response on success or an instance of WP_Error on failure.
|
218 |
+
*/
|
219 |
+
private function request( $url, $method = 'GET', $headers = array(), $body = NULL ) {
|
220 |
+
$args = array(
|
221 |
+
'method' => $method,
|
222 |
+
'timeout' => $this->request_timeout,
|
223 |
+
'httpversion' => '1.1',
|
224 |
+
'redirection' => 0,
|
225 |
+
'sslverify' => $this->ssl_verify,
|
226 |
+
'headers' => array(
|
227 |
+
'Authorization' => 'Bearer ' . $this->token,
|
228 |
+
'GData-Version' => $this->gdata_version
|
229 |
+
)
|
230 |
+
);
|
231 |
+
if ( ! empty( $headers ) )
|
232 |
+
$args['headers'] = array_merge( $args['headers'], $headers );
|
233 |
+
if ( ! empty( $body ) )
|
234 |
+
$args['body'] = $body;
|
235 |
+
|
236 |
+
return wp_remote_request( $url, $args );
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Returns the feed from a URL.
|
241 |
+
*
|
242 |
+
* @access public
|
243 |
+
* @param string $url The feed URL.
|
244 |
+
* @return mixed Returns the feed as an instance of SimpleXMLElement on success or an instance of WP_Error on failure.
|
245 |
+
*/
|
246 |
+
public function get_feed( $url ) {
|
247 |
+
if ( ! isset( $this->cache[$url] ) ) {
|
248 |
+
$result = $this->cache_feed( $url );
|
249 |
+
if ( is_wp_error( $result ) )
|
250 |
+
return $result;
|
251 |
+
}
|
252 |
+
|
253 |
+
return $this->cache[$url];
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Requests a feed and adds it to cache.
|
258 |
+
*
|
259 |
+
* @access private
|
260 |
+
* @param string $url The feed URL.
|
261 |
+
* @return mixed Returns TRUE on success or an instance of WP_Error on failure.
|
262 |
+
*/
|
263 |
+
private function cache_feed( $url ) {
|
264 |
+
$result = $this->request( $url );
|
265 |
+
|
266 |
+
if ( is_wp_error( $result ) )
|
267 |
+
return $result;
|
268 |
+
|
269 |
+
if ( $result['response']['code'] == '200' ) {
|
270 |
+
$feed = @simplexml_load_string( $result['body'] );
|
271 |
+
if ( $feed === false )
|
272 |
+
return new WP_Error( 'invalid_data', "Could not create SimpleXMLElement from '" . $result['body'] . "'." );
|
273 |
+
|
274 |
+
$this->cache[$url] = $feed;
|
275 |
+
return true;
|
276 |
+
}
|
277 |
+
return new WP_Error( 'bad_response', "Received response code '" . $result['response']['code'] . " " . $result['response']['message'] . "' while trying to get '" . $url . "'. Response body: " . $result['body'] );
|
278 |
+
|
279 |
+
}
|
280 |
+
|
281 |
+
/**
|
282 |
+
* Deletes a resource from Google Docs.
|
283 |
+
*
|
284 |
+
* @access public
|
285 |
+
* @param string $id Gdata Id of the resource to be deleted.
|
286 |
+
* @return mixed Returns TRUE on success, an instance of WP_Error on failure.
|
287 |
+
*/
|
288 |
+
public function delete_resource( $id ) {
|
289 |
+
$headers = array( 'If-Match' => '*' );
|
290 |
+
|
291 |
+
$result = $this->request( $this->base_url . $id . '?delete=true', 'DELETE', $headers );
|
292 |
+
if ( is_wp_error( $result ) )
|
293 |
+
return $result;
|
294 |
+
|
295 |
+
if ( $result['response']['code'] == '200' )
|
296 |
+
return true;
|
297 |
+
return new WP_Error( 'bad_response', "Received response code '" . $result['response']['code'] . " " . $result['response']['message'] . "' while trying to delete resource '" . $id . "'. The resource might not have been deleted." );
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* Get the resumable-create-media link needed to upload files.
|
302 |
+
*
|
303 |
+
* @access private
|
304 |
+
* @param string $parent The Id of the folder where the upload is to be made. Default is empty string.
|
305 |
+
* @return mixed Returns a link on success, instance of WP_Error on failure.
|
306 |
+
*/
|
307 |
+
private function get_resumable_create_media_link( $parent = '' ) {
|
308 |
+
$url = $this->base_url;
|
309 |
+
if ( $parent )
|
310 |
+
$url .= $parent;
|
311 |
+
|
312 |
+
$feed = $this->get_feed( $url );
|
313 |
+
|
314 |
+
if ( is_wp_error( $feed ) )
|
315 |
+
return $feed;
|
316 |
+
|
317 |
+
foreach ( $feed->link as $link )
|
318 |
+
if ( $link['rel'] == 'http://schemas.google.com/g/2005#resumable-create-media' )
|
319 |
+
return ( string ) $link['href'];
|
320 |
+
return new WP_Error( 'not_found', "The 'resumable_create_media_link' was not found in feed." );
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* Get used quota in bytes.
|
325 |
+
*
|
326 |
+
* @access public
|
327 |
+
* @return mixed Returns the number of bytes used in Google Docs on success or an instance of WP_Error on failure.
|
328 |
+
*/
|
329 |
+
public function get_quota_used() {
|
330 |
+
$feed = $this->get_feed( $this->metadata_url );
|
331 |
+
if ( is_wp_error( $feed ) )
|
332 |
+
return $feed;
|
333 |
+
return ( string ) $feed->children( "http://schemas.google.com/g/2005" )->quotaBytesUsed;
|
334 |
+
}
|
335 |
+
|
336 |
+
/**
|
337 |
+
* Get total quota in bytes.
|
338 |
+
*
|
339 |
+
* @access public
|
340 |
+
* @return string|WP_Error Returns the total quota in bytes in Google Docs on success or an instance of WP_Error on failure.
|
341 |
+
*/
|
342 |
+
public function get_quota_total() {
|
343 |
+
$feed = $this->get_feed( $this->metadata_url );
|
344 |
+
if ( is_wp_error( $feed ) )
|
345 |
+
return $feed;
|
346 |
+
return ( string ) $feed->children( "http://schemas.google.com/g/2005" )->quotaBytesTotal;
|
347 |
+
}
|
348 |
+
|
349 |
+
/**
|
350 |
+
* Function to prepare a file to be uploaded to Google Docs.
|
351 |
+
*
|
352 |
+
* The function requests a URI for uploading and prepends a new element in the resume_list array.
|
353 |
+
*
|
354 |
+
* @uses wp_check_filetype
|
355 |
+
* @access public
|
356 |
+
*
|
357 |
+
* @param string $file Path to the file that is to be uploaded.
|
358 |
+
* @param string $title Title to be given to the file.
|
359 |
+
* @param string $parent ID of the folder in which to upload the file.
|
360 |
+
* @param string $type MIME type of the file to be uploaded. The function tries to identify the type if it is omitted.
|
361 |
+
* @return mixed Returns the URI where to upload on success, an instance of WP_Error on failure.
|
362 |
+
*/
|
363 |
+
public function prepare_upload( $file, $title, $parent = '', $type = '' ) {
|
364 |
+
if ( ! @is_readable( $file ) )
|
365 |
+
return new WP_Error( 'not_file', "The path '" . $file . "' does not point to a readable file." );
|
366 |
+
|
367 |
+
// If a mime type wasn't passed try to guess it from the extension based on the WordPress allowed mime types
|
368 |
+
if ( empty( $type ) ) {
|
369 |
+
$check = wp_check_filetype( $file );
|
370 |
+
$this->upload_file_type = $type = $check['type'];
|
371 |
+
}
|
372 |
+
|
373 |
+
$size = filesize( $file );
|
374 |
+
|
375 |
+
$body = '<?xml version=\'1.0\' encoding=\'UTF-8\'?><entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007"><category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/docs/2007#file"/><title>' . $title . '</title></entry>';
|
376 |
+
|
377 |
+
$headers = array(
|
378 |
+
'Content-Type' => 'application/atom+xml',
|
379 |
+
'X-Upload-Content-Type' => $type,
|
380 |
+
'X-Upload-Content-Length' => (string) $size
|
381 |
+
);
|
382 |
+
|
383 |
+
$url = $this->get_resumable_create_media_link( $parent );
|
384 |
+
|
385 |
+
if ( is_wp_error( $url ) )
|
386 |
+
return $url;
|
387 |
+
|
388 |
+
$url .= '?convert=false'; // needed to upload a file
|
389 |
+
|
390 |
+
$result = $this->request( $url, 'POST', $headers, $body );
|
391 |
+
|
392 |
+
if ( is_wp_error( $result ) )
|
393 |
+
return $result;
|
394 |
+
|
395 |
+
if ( $result['response']['code'] != '200' )
|
396 |
+
return new WP_Error( 'bad_response', "Received response code '" . $result['response']['code'] . " " . $result['response']['message'] . "' while trying to get '" . $url . "'." );
|
397 |
+
|
398 |
+
$this->file = array(
|
399 |
+
'path' => $file,
|
400 |
+
'size' => $size,
|
401 |
+
'location' => $result['headers']['location'],
|
402 |
+
'pointer' => 0
|
403 |
+
);
|
404 |
+
|
405 |
+
// Open file for reading.
|
406 |
+
if ( !$this->file['handle'] = fopen( $file, "rb" ) )
|
407 |
+
return new WP_Error( 'open_error', "Could not open file '" . $file . "' for reading." );
|
408 |
+
|
409 |
+
// Start timer
|
410 |
+
$this->timer['start'] = microtime( true );
|
411 |
+
|
412 |
+
return $result['headers']['location'];
|
413 |
+
}
|
414 |
+
|
415 |
+
|
416 |
+
/**
|
417 |
+
* Resume an upload.
|
418 |
+
*
|
419 |
+
* @access public
|
420 |
+
* @param string $file Path to the file which needs to be uploaded
|
421 |
+
* @param string $location URI where to upload the file
|
422 |
+
* @return mixed Returns the next location URI on success, an instance of WP_Error on failure.
|
423 |
+
*/
|
424 |
+
public function resume_upload( $file, $location ) {
|
425 |
+
|
426 |
+
if ( ! @is_readable( $file ) )
|
427 |
+
return new WP_Error( 'not_file', "The path '" . $this->resume_list[$id]['path'] . "' does not point to a readable file. Upload has been canceled." );
|
428 |
+
|
429 |
+
$size = filesize( $file );
|
430 |
+
|
431 |
+
$headers = array( 'Content-Range' => 'bytes */' . $size );
|
432 |
+
$result = $this->request( $location, 'PUT', $headers );
|
433 |
+
if( is_wp_error( $result ) )
|
434 |
+
return $result;
|
435 |
+
|
436 |
+
if ( '308' != $result['response']['code'] ) {
|
437 |
+
if ( '201' == $result['response']['code'] ) {
|
438 |
+
$feed = @simplexml_load_string( $result['body'] );
|
439 |
+
if ( $feed === false )
|
440 |
+
return new WP_Error( 'invalid_data', "Could not create SimpleXMLElement from '" . $result['body'] . "'." );
|
441 |
+
$this->file['id'] = substr( ( string ) $feed->children( "http://schemas.google.com/g/2005" )->resourceId, 5 );
|
442 |
+
return true;
|
443 |
+
}
|
444 |
+
return new WP_Error( 'bad_response', "Received response code '" . $result['response']['code'] . " " . $result['response']['message'] . "' while trying to resume the upload of file '" . $file . "'." );
|
445 |
+
}
|
446 |
+
if( isset( $result['headers']['location'] ) )
|
447 |
+
$location = $result['headers']['location'];
|
448 |
+
$pointer = $this->pointer( $result['headers']['range'] );
|
449 |
+
|
450 |
+
$this->file = array(
|
451 |
+
'path' => $file,
|
452 |
+
'size' => $size,
|
453 |
+
'location' => $location,
|
454 |
+
'pointer' => $pointer
|
455 |
+
);
|
456 |
+
|
457 |
+
// Open file for reading.
|
458 |
+
if ( !$this->file['handle'] = fopen( $file, "rb" ) )
|
459 |
+
return new WP_Error( 'open_error', "Could not open file '" . $file . "' for reading." );
|
460 |
+
|
461 |
+
// Start timer
|
462 |
+
$this->timer['start'] = microtime( true );
|
463 |
+
|
464 |
+
return $location;
|
465 |
+
}
|
466 |
+
|
467 |
+
/**
|
468 |
+
* Work out where the file pointer should be from the range header.
|
469 |
+
*
|
470 |
+
* @access private
|
471 |
+
* @param string $range The range HTTP response header.
|
472 |
+
* @return integer Returns the number of bytes that have been uploaded.
|
473 |
+
*/
|
474 |
+
private function pointer( $range ) {
|
475 |
+
return intval(substr( $range, strpos( $range, '-' ) + 1 )) + 1;
|
476 |
+
}
|
477 |
+
|
478 |
+
/**
|
479 |
+
* Uploads a chunk of the file being uploaded.
|
480 |
+
*
|
481 |
+
* @access public
|
482 |
+
* @return mixed Returns TRUE if the chunk was uploaded successfully;
|
483 |
+
* returns Google Docs resource ID if the file upload finished;
|
484 |
+
* returns an instance of WP_Error on failure.
|
485 |
+
*/
|
486 |
+
public function upload_chunk() {
|
487 |
+
if ( !isset( $this->file['handle'] ) )
|
488 |
+
return new WP_Error( "no_upload", "There is no file being uploaded." );
|
489 |
+
|
490 |
+
$cycle_start = microtime( true );
|
491 |
+
fseek( $this->file['handle'], $this->file['pointer'] );
|
492 |
+
$chunk = @fread( $this->file['handle'], $this->chunk_size );
|
493 |
+
if ( false === $chunk )
|
494 |
+
return new WP_Error( 'read_error', "Failed to read from file '" . $this->resume_list[$id]['path'] . "'." );
|
495 |
+
|
496 |
+
$chunk_size = strlen( $chunk );
|
497 |
+
$bytes = 'bytes ' . (string)$this->file['pointer'] . '-' . (string)($this->file['pointer'] + $chunk_size - 1) . '/' . (string)$this->file['size'];
|
498 |
+
|
499 |
+
$headers = array( 'Content-Range' => $bytes );
|
500 |
+
|
501 |
+
$result = $this->request( $this->file['location'], 'PUT', $headers, $chunk );
|
502 |
+
|
503 |
+
if ( !is_wp_error( $result ) )
|
504 |
+
if ( '308' == $result['response']['code'] ) {
|
505 |
+
if ( isset( $result['headers']['range'] ) )
|
506 |
+
$this->file['pointer'] = $this->pointer( $result['headers']['range'] );
|
507 |
+
else
|
508 |
+
$this->file['pointer'] += $chunk_size;
|
509 |
+
|
510 |
+
if ( isset( $result['headers']['location'] ) )
|
511 |
+
$this->file['location'] = $result['headers']['location'];
|
512 |
+
|
513 |
+
if ( $this->timer['cycle'] )
|
514 |
+
$this->timer['cycle'] = ( microtime( true ) - $cycle_start + $this->timer['cycle'] ) / 2;
|
515 |
+
else
|
516 |
+
$this->timer['cycle'] = microtime(true) - $cycle_start;
|
517 |
+
|
518 |
+
return $this->file['location'];
|
519 |
+
}
|
520 |
+
elseif ( '201' == $result['response']['code'] ) {
|
521 |
+
fclose( $this->file['handle'] );
|
522 |
+
|
523 |
+
// Stop timer
|
524 |
+
$this->timer['stop'] = microtime(true);
|
525 |
+
$this->timer['delta'] = $this->timer['stop'] - $this->timer['start'];
|
526 |
+
|
527 |
+
if ( $this->timer['cycle'] )
|
528 |
+
$this->timer['cycle'] = ( microtime( true ) - $cycle_start + $this->timer['cycle'] ) / 2;
|
529 |
+
else
|
530 |
+
$this->timer['cycle'] = microtime(true) - $cycle_start;
|
531 |
+
|
532 |
+
$this->file['pointer'] = $this->file['size'];
|
533 |
+
|
534 |
+
$feed = @simplexml_load_string( $result['body'] );
|
535 |
+
if ( $feed === false )
|
536 |
+
return new WP_Error( 'invalid_data', "Could not create SimpleXMLElement from '" . $result['body'] . "'." );
|
537 |
+
$this->file['id'] = substr( ( string ) $feed->children( "http://schemas.google.com/g/2005" )->resourceId, 5 );
|
538 |
+
return true;
|
539 |
+
}
|
540 |
+
|
541 |
+
// If we got to this point it means the upload wasn't successful.
|
542 |
+
fclose( $this->file['handle'] );
|
543 |
+
if ( is_wp_error( $result ) )
|
544 |
+
return $result;
|
545 |
+
return new WP_Error( 'bad_response', "Received response code '" . $result['response']['code'] . " " . $result['response']['message'] . "' while trying to upload a file chunk." );
|
546 |
+
}
|
547 |
+
|
548 |
+
/**
|
549 |
+
* Get the resource ID of the most recent uploaded file.
|
550 |
+
*
|
551 |
+
* @access public
|
552 |
+
* @return string The ID of the uploaded file or an empty string.
|
553 |
+
*/
|
554 |
+
public function get_file_id() {
|
555 |
+
if ( isset( $this->file['id'] ) )
|
556 |
+
return $this->file['id'];
|
557 |
+
return '';
|
558 |
+
}
|
559 |
+
|
560 |
+
/**
|
561 |
+
* Get the upload speed recorded on the last upload performed.
|
562 |
+
*
|
563 |
+
* @access public
|
564 |
+
* @return integer Returns the upload speed in bytes/second or 0.
|
565 |
+
*/
|
566 |
+
public function get_upload_speed() {
|
567 |
+
if ( $this->timer['cycle'] > 0 )
|
568 |
+
if ( $this->file['size'] < $this->chunk_size )
|
569 |
+
return $this->file['size'] / $this->timer['cycle'];
|
570 |
+
else
|
571 |
+
return $this->chunk_size / $this->timer['cycle'];
|
572 |
+
return 0;
|
573 |
+
}
|
574 |
+
|
575 |
+
/**
|
576 |
+
* Get the percentage of the file uploaded.
|
577 |
+
*
|
578 |
+
* @return float Returns a percentage on success, 0 on failure.
|
579 |
+
*/
|
580 |
+
public function get_upload_percentage() {
|
581 |
+
if ( isset( $this->file['path'] ) )
|
582 |
+
return $this->file['pointer'] * 100 / $this->file['size'];
|
583 |
+
return 0;
|
584 |
+
}
|
585 |
+
|
586 |
+
/**
|
587 |
+
* Returns the time taken for an upload to complete.
|
588 |
+
*
|
589 |
+
* @access public
|
590 |
+
* @return float Returns the number of seconds the last upload took to complete, 0 if there has been no completed upload.
|
591 |
+
*/
|
592 |
+
public function time_taken() {
|
593 |
+
return $this->timer['delta'];
|
594 |
+
}
|
595 |
+
|
596 |
+
public function get_content_link( $id, $title ) {
|
597 |
+
|
598 |
+
$feed = $this->get_feed($this->base_url . $id);
|
599 |
+
|
600 |
+
if ( is_wp_error( $feed ) )
|
601 |
+
return $feed;
|
602 |
+
|
603 |
+
if ( $feed->title != $title )
|
604 |
+
return new WP_Error( 'bad_response', "Unexpected response");
|
605 |
+
|
606 |
+
$att = $feed->content->attributes();
|
607 |
+
return $att['src'];
|
608 |
+
|
609 |
+
}
|
610 |
+
|
611 |
+
public function download_data( $link, $saveas ) {
|
612 |
+
|
613 |
+
$result = $this->request( $link );
|
614 |
+
|
615 |
+
if ( is_wp_error( $result ) )
|
616 |
+
return $result;
|
617 |
+
|
618 |
+
if ( $result['response']['code'] != '200' )
|
619 |
+
return new WP_Error( 'bad_response', "Received response code '" . $result['response']['code'] . " " . $result['response']['message'] . "' while trying to get '" . $url . "'." );
|
620 |
+
|
621 |
+
file_put_contents($saveas, $result['body']);
|
622 |
+
|
623 |
+
}
|
624 |
+
|
625 |
+
|
626 |
+
|
627 |
+
}
|
readme.txt
CHANGED
@@ -3,12 +3,12 @@ Contributors: David Anderson
|
|
3 |
Tags: backup, restore, database, cloud, amazon, s3, Amazon S3, google drive, google, gdrive, ftp, cloud, updraft, back up
|
4 |
Requires at least: 3.2
|
5 |
Tested up to: 3.5
|
6 |
-
Stable tag: 0.9.
|
7 |
Donate link: http://david.dw-perspective.org.uk/donate
|
8 |
License: GPLv3 or later
|
9 |
|
10 |
== Upgrade Notice ==
|
11 |
-
|
12 |
|
13 |
== Description ==
|
14 |
|
@@ -70,19 +70,24 @@ Contact me! This is a complex plugin and the only way I can ensure it's robust i
|
|
70 |
|
71 |
== Changelog ==
|
72 |
|
|
|
|
|
|
|
|
|
|
|
73 |
= 0.9.2 - 11/21/2012 =
|
74 |
-
* Failed uploads can now be
|
75 |
|
76 |
= 0.8.51 - 11/19/2012 =
|
77 |
* Moved screenshot into assets, reducing plugin download size
|
78 |
|
79 |
= 0.8.50 - 10/13/2012 =
|
80 |
-
* Important new feature: back up other directories found in the WP content directory (not just plugins/themes/uploads, as in original Updraft)
|
81 |
|
82 |
= 0.8.37 - 10/12/2012 =
|
83 |
* Don't whinge about Google Drive authentication if that method is not current
|
84 |
|
85 |
-
= 0.8.36 - 03/
|
86 |
* Support using sub-directories in Amazon S3
|
87 |
* Some more debug logging for Amazon S3
|
88 |
|
3 |
Tags: backup, restore, database, cloud, amazon, s3, Amazon S3, google drive, google, gdrive, ftp, cloud, updraft, back up
|
4 |
Requires at least: 3.2
|
5 |
Tested up to: 3.5
|
6 |
+
Stable tag: 0.9.10
|
7 |
Donate link: http://david.dw-perspective.org.uk/donate
|
8 |
License: GPLv3 or later
|
9 |
|
10 |
== Upgrade Notice ==
|
11 |
+
Full Google Drive support
|
12 |
|
13 |
== Description ==
|
14 |
|
70 |
|
71 |
== Changelog ==
|
72 |
|
73 |
+
= 0.9.10 - 11/22/2012 =
|
74 |
+
* Completed basic Google Drive support (thanks to Sorin Iclanzan, code taken from "Backup" plugin under GPLv3+); now supporting uploading, purging and restoring - i.e. full UpdraftPlus functionality
|
75 |
+
* Licence change to GPLv3+ (from GPLv2+) to allow incorporating Sorin's code
|
76 |
+
* Tidied/organised the settings screen further
|
77 |
+
|
78 |
= 0.9.2 - 11/21/2012 =
|
79 |
+
* Failed uploads can now be re-tried, giving really big blogs a better opportunity to eventually succeed uploading
|
80 |
|
81 |
= 0.8.51 - 11/19/2012 =
|
82 |
* Moved screenshot into assets, reducing plugin download size
|
83 |
|
84 |
= 0.8.50 - 10/13/2012 =
|
85 |
+
* Important new feature: back up other directories found in the WP content (wp-content) directory (not just plugins/themes/uploads, as in original Updraft)
|
86 |
|
87 |
= 0.8.37 - 10/12/2012 =
|
88 |
* Don't whinge about Google Drive authentication if that method is not current
|
89 |
|
90 |
+
= 0.8.36 - 10/03/2012 =
|
91 |
* Support using sub-directories in Amazon S3
|
92 |
* Some more debug logging for Amazon S3
|
93 |
|
updraftplus.php
CHANGED
@@ -4,12 +4,14 @@ 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, on separate schedules.
|
6 |
Author: David Anderson.
|
7 |
-
Version: 0.9.
|
8 |
Donate link: http://david.dw-perspective.org.uk/donate
|
|
|
9 |
Author URI: http://wordshell.net
|
10 |
*/
|
11 |
|
12 |
//TODO (some of these items mine, some from original Updraft awaiting review):
|
|
|
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?).
|
15 |
//improve error reporting. s3 and dir backup have decent reporting now, but not sure i know what to do from here
|
@@ -21,7 +23,6 @@ Author URI: http://wordshell.net
|
|
21 |
//Rip out the "last backup" bit, and/or put in a display of the last log
|
22 |
|
23 |
/* More TODO:
|
24 |
-
DONE, TESTING: 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
|
25 |
Use only one entry in WP options database
|
26 |
Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
|
27 |
// Does not delete old custom directories upon a restore?
|
@@ -29,10 +30,12 @@ Encrypt filesystem, if memory allows (and have option for abort if not); split u
|
|
29 |
|
30 |
/* Portions copyright 2010 Paul Kehrer
|
31 |
Portions copyright 2011-12 David Anderson
|
|
|
|
|
32 |
|
33 |
This program is free software; you can redistribute it and/or modify
|
34 |
it under the terms of the GNU General Public License as published by
|
35 |
-
the Free Software Foundation; either version
|
36 |
(at your option) any later version.
|
37 |
|
38 |
This program is distributed in the hope that it will be useful,
|
@@ -59,7 +62,7 @@ define('UPDRAFT_DEFAULT_OTHERS_EXCLUDE','upgrade,cache,updraft,index.php');
|
|
59 |
|
60 |
class UpdraftPlus {
|
61 |
|
62 |
-
var $version = '0.9.
|
63 |
|
64 |
var $dbhandle;
|
65 |
var $errors = array();
|
@@ -67,7 +70,10 @@ class UpdraftPlus {
|
|
67 |
var $logfile_name = "";
|
68 |
var $logfile_handle = false;
|
69 |
var $backup_time;
|
70 |
-
|
|
|
|
|
|
|
71 |
function __construct() {
|
72 |
// Initialisation actions
|
73 |
# Create admin page
|
@@ -91,11 +97,11 @@ class UpdraftPlus {
|
|
91 |
if ( is_admin() && isset( $_GET['page'] ) && $_GET['page'] == 'updraftplus' && isset( $_GET['action'] ) && $_GET['action'] == 'auth' ) {
|
92 |
if ( isset( $_GET['state'] ) ) {
|
93 |
if ( $_GET['state'] == 'token' )
|
94 |
-
$this->
|
95 |
elseif ( $_GET['state'] == 'revoke' )
|
96 |
-
$this->
|
97 |
} elseif (isset($_GET['updraftplus_googleauth'])) {
|
98 |
-
$this->
|
99 |
}
|
100 |
}
|
101 |
}
|
@@ -103,7 +109,7 @@ class UpdraftPlus {
|
|
103 |
/**
|
104 |
* Acquire single-use authorization code from Google OAuth 2.0
|
105 |
*/
|
106 |
-
function
|
107 |
$params = array(
|
108 |
'response_type' => 'code',
|
109 |
'client_id' => get_option('updraft_googledrive_clientid'),
|
@@ -149,254 +155,30 @@ class UpdraftPlus {
|
|
149 |
}
|
150 |
}
|
151 |
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
* @param string $parent ID of the folder in which to upload the file
|
158 |
-
* @param string $token Access token from Google Account
|
159 |
-
* @return boolean Returns TRUE on success, FALSE on failure
|
160 |
-
*/
|
161 |
-
function googledrive_upload_file( $file, $title, $parent = '', $token) {
|
162 |
-
|
163 |
-
$size = filesize( $file );
|
164 |
-
|
165 |
-
$content = '<?xml version=\'1.0\' encoding=\'UTF-8\'?>
|
166 |
-
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007">
|
167 |
-
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/docs/2007#file"/>
|
168 |
-
<title>' . $title . '</title>
|
169 |
-
</entry>';
|
170 |
-
|
171 |
-
$header = array(
|
172 |
-
'Authorization: Bearer ' . $token,
|
173 |
-
'Content-Length: ' . strlen( $content ),
|
174 |
-
'Content-Type: application/atom+xml',
|
175 |
-
'X-Upload-Content-Type: application/octet-stream',
|
176 |
-
'X-Upload-Content-Length: ' . $size,
|
177 |
-
'GData-Version: 3.0'
|
178 |
-
);
|
179 |
-
|
180 |
-
$context = array(
|
181 |
-
'http' => array(
|
182 |
-
'ignore_errors' => true,
|
183 |
-
'follow_location' => false,
|
184 |
-
'method' => 'POST',
|
185 |
-
'header' => join( "\r\n", $header ),
|
186 |
-
'content' => $content
|
187 |
-
)
|
188 |
-
);
|
189 |
-
|
190 |
-
$url = $this->get_resumable_create_media_link( $token, $parent );
|
191 |
-
if ( $url ) {
|
192 |
-
$url .= '?convert=false'; // needed to upload a file
|
193 |
-
$this->log("Google Drive: resumable create media link: ".$url);
|
194 |
} else {
|
195 |
-
$this->
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
$result = @file_get_contents( $url, false, stream_context_create( $context ) );
|
200 |
-
|
201 |
-
if ( $result !== FALSE ) {
|
202 |
-
if ( strpos( $response = array_shift( $http_response_header ), '200' ) ) {
|
203 |
-
$response_header = array();
|
204 |
-
foreach ( $http_response_header as $header_line ) {
|
205 |
-
list( $key, $value ) = explode( ':', $header_line, 2 );
|
206 |
-
$response_header[trim( $key )] = trim( $value );
|
207 |
-
#$this->log("Google Drive: header: ".trim($key).": ".trim($value));
|
208 |
-
}
|
209 |
-
if ( isset( $response_header['Location'] ) ) {
|
210 |
-
$next_location = $response_header['Location'];
|
211 |
-
$pointer = 0;
|
212 |
-
# 1Mb
|
213 |
-
$max_chunk_size = 524288*2;
|
214 |
-
while ( $pointer < $size - 1 ) {
|
215 |
-
$this->log(basename($file).": Google Drive upload: pointer=$pointer (size=$size)");
|
216 |
-
$chunk = file_get_contents( $file, false, NULL, $pointer, $max_chunk_size );
|
217 |
-
$next_location = $this->upload_chunk( $next_location, $chunk, $pointer, $size, $token );
|
218 |
-
if( $next_location === false ) {
|
219 |
-
$this->log("Google Drive Upload: next_location is false (pointer: $pointer; chunk length: ".strlen($chunk).")");
|
220 |
-
return false;
|
221 |
-
}
|
222 |
-
$pointer += strlen( $chunk );
|
223 |
-
// if object it means we have our simpleXMLElement response
|
224 |
-
if ( is_object( $next_location ) ) {
|
225 |
-
// return resource Id
|
226 |
-
$this->log("Google Drive Upload: Success");
|
227 |
-
# Google Drive returns 501 not implemented for me for some reason instead of expected result...
|
228 |
-
#return substr( $next_location->children( "http://schemas.google.com/g/2005" )->resourceId, 5 );
|
229 |
-
return true;
|
230 |
-
}
|
231 |
-
|
232 |
-
}
|
233 |
-
}
|
234 |
-
}
|
235 |
-
else {
|
236 |
-
$this->log( 'Bad response: ' . $response . ' Response header: ' . var_export( $response_header, true ) . ' Response body: ' . $result . ' Request URL: ' . $url, __FILE__, __LINE__ );
|
237 |
-
return false;
|
238 |
-
}
|
239 |
-
}
|
240 |
-
else {
|
241 |
-
$this->log( 'Unable to request file from ' . $url, __FILE__, __LINE__ );
|
242 |
-
}
|
243 |
-
|
244 |
-
return true;
|
245 |
-
|
246 |
-
}
|
247 |
-
|
248 |
-
/**
|
249 |
-
* Get the resumable-create-media link needed to upload files
|
250 |
-
*
|
251 |
-
* @param string $token The Google Account access token
|
252 |
-
* @param string $parent The Id of the folder where the upload is to be made. Default is empty string.
|
253 |
-
* @return string|boolean Returns a link on success, FALSE on failure.
|
254 |
-
*/
|
255 |
-
function get_resumable_create_media_link( $token, $parent = '' ) {
|
256 |
-
$header = array(
|
257 |
-
'Authorization: Bearer ' . $token,
|
258 |
-
'GData-Version: 3.0'
|
259 |
-
);
|
260 |
-
$context = array(
|
261 |
-
'http' => array(
|
262 |
-
'ignore_errors' => true,
|
263 |
-
'method' => 'GET',
|
264 |
-
'header' => join( "\r\n", $header )
|
265 |
-
)
|
266 |
-
);
|
267 |
-
$url = 'https://docs.google.com/feeds/default/private/full';
|
268 |
-
|
269 |
-
if ( $parent ) {
|
270 |
-
$url .= '/' . $parent;
|
271 |
-
}
|
272 |
-
|
273 |
-
$result = @file_get_contents( $url, false, stream_context_create( $context ) );
|
274 |
-
|
275 |
-
if ( $result !== false ) {
|
276 |
-
$xml = simplexml_load_string( $result );
|
277 |
-
if ( $xml === false ) {
|
278 |
-
$this->log( 'Could not create SimpleXMLElement from ' . $result, __FILE__, __LINE__ );
|
279 |
-
return false;
|
280 |
-
}
|
281 |
-
else {
|
282 |
-
foreach ( $xml->link as $link ) {
|
283 |
-
if ( $link['rel'] == 'http://schemas.google.com/g/2005#resumable-create-media' ) { return $link['href']; }
|
284 |
-
}
|
285 |
-
}
|
286 |
-
}
|
287 |
-
return false;
|
288 |
-
}
|
289 |
-
|
290 |
-
|
291 |
-
/**
|
292 |
-
* Handles the upload to Google Drive of a single chunk of a file
|
293 |
-
*
|
294 |
-
* @param string $location URL where the chunk needs to be uploaded
|
295 |
-
* @param string $chunk Part of the file to upload
|
296 |
-
* @param integer $pointer The byte number marking the beginning of the chunk in file
|
297 |
-
* @param integer $size The size of the file the chunk is part of, in bytes
|
298 |
-
* @param string $token Google Account access token
|
299 |
-
* @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
|
300 |
-
*/
|
301 |
-
function upload_chunk( $location, $chunk, $pointer, $size, $token ) {
|
302 |
-
$chunk_size = strlen( $chunk );
|
303 |
-
$bytes = (string)$pointer . '-' . (string)($pointer + $chunk_size - 1) . '/' . (string)$size;
|
304 |
-
$this->log("Google Drive chunk: location=$location, length=$chunk_size, range=$bytes");
|
305 |
-
$header = array(
|
306 |
-
'Authorization: Bearer ' . $token,
|
307 |
-
'Content-Length: ' . $chunk_size,
|
308 |
-
'Content-Type: application/octet-stream',
|
309 |
-
'Content-Range: bytes ' . $bytes,
|
310 |
-
'GData-Version: 3.0'
|
311 |
-
);
|
312 |
-
$context = array(
|
313 |
-
'http' => array(
|
314 |
-
'ignore_errors' => true,
|
315 |
-
'follow_location' => false,
|
316 |
-
'method' => 'PUT',
|
317 |
-
'header' => join( "\r\n", $header ),
|
318 |
-
'content' => $chunk
|
319 |
-
)
|
320 |
-
);
|
321 |
-
|
322 |
-
$result = @file_get_contents( $location, false, stream_context_create( $context ) );
|
323 |
-
|
324 |
-
if ( isset( $http_response_header ) ) {
|
325 |
-
$response = array_shift( $http_response_header );
|
326 |
-
$headers = array();
|
327 |
-
foreach ( $http_response_header as $header_line ) {
|
328 |
-
list( $key, $value ) = explode( ':', $header_line, 2 );
|
329 |
-
$headers[trim( $key )] = trim( $value );
|
330 |
-
}
|
331 |
-
|
332 |
-
if ( strpos( $response, '308' ) ) {
|
333 |
-
if ( isset( $headers['Location'] ) ) {
|
334 |
-
$this->log('Google Drive: 308 response: '.$headers['Location']);
|
335 |
-
return $headers['Location'];
|
336 |
}
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
}
|
342 |
-
elseif ( strpos( $response, '201' ) ) {
|
343 |
-
#$this->log("Google Drive response: ".$result);
|
344 |
-
$xml = simplexml_load_string( $result );
|
345 |
-
if ( $xml === false ) {
|
346 |
-
$this->log('ERROR: Could not create SimpleXMLElement from ' . $result, __FILE__, __LINE__ );
|
347 |
-
return false;
|
348 |
-
}
|
349 |
-
else {
|
350 |
-
return $xml;
|
351 |
-
}
|
352 |
-
}
|
353 |
-
else {
|
354 |
-
$this->log('ERROR: Bad response: ' . $response, __FILE__, __LINE__ );
|
355 |
-
return false;
|
356 |
}
|
357 |
}
|
358 |
-
else {
|
359 |
-
$this->log('ERROR: Received no response from ' . $location . ' while trying to upload bytes ' . $bytes );
|
360 |
-
return false;
|
361 |
-
}
|
362 |
-
}
|
363 |
-
|
364 |
-
function googledrive_delete_file( $file, $token) {
|
365 |
-
$this->log("Delete from Google Drive: $file: not yet implemented");
|
366 |
-
# TODO - somehow, turn this into a Gdata resource ID, then despatch it to googledrive_delete_file_byid
|
367 |
return;
|
368 |
}
|
369 |
|
370 |
/**
|
371 |
-
*
|
372 |
-
*
|
373 |
-
* @param string $id Gdata resource Id of the file to be deleted
|
374 |
-
* @param string $token Google Account access token
|
375 |
-
* @return boolean Returns TRUE on success, FALSE on failure
|
376 |
-
*/
|
377 |
-
function googledrive_delete_file_byid( $id, $token ) {
|
378 |
-
$header = array(
|
379 |
-
'If-Match: *',
|
380 |
-
'Authorization: Bearer ' . $token,
|
381 |
-
'GData-Version: 3.0'
|
382 |
-
);
|
383 |
-
$context = array(
|
384 |
-
'http' => array(
|
385 |
-
'method' => 'DELETE',
|
386 |
-
'header' => join( "\r\n", $header )
|
387 |
-
)
|
388 |
-
);
|
389 |
-
stream_context_set_default( $context );
|
390 |
-
$headers = get_headers( 'https://docs.google.com/feeds/default/private/full/' . $id . '?delete=true',1 );
|
391 |
-
|
392 |
-
if ( strpos( $headers[0], '200' ) ) { return true; }
|
393 |
-
return false;
|
394 |
-
}
|
395 |
-
|
396 |
-
/**
|
397 |
-
* Get a Google account refresh token using the code received from auth_request
|
398 |
*/
|
399 |
-
function
|
400 |
if( isset( $_GET['code'] ) ) {
|
401 |
$context = array(
|
402 |
'http' => array(
|
@@ -439,7 +221,7 @@ class UpdraftPlus {
|
|
439 |
/**
|
440 |
* Revoke a Google account refresh token
|
441 |
*/
|
442 |
-
function
|
443 |
@file_get_contents( 'https://accounts.google.com/o/oauth2/revoke?token=' . get_option('updraft_googledrive_token') );
|
444 |
update_option('updraft_googledrive_token','');
|
445 |
header( 'Location: '.admin_url( 'options-general.php?page=updraftplus&message=' . __( 'Authorization revoked.', 'backup' ) ) );
|
@@ -467,6 +249,7 @@ class UpdraftPlus {
|
|
467 |
}
|
468 |
|
469 |
function backup_resume($resumption_no) {
|
|
|
470 |
// This is scheduled for 5 minutes after a backup job starts
|
471 |
$bnonce = get_transient('updraftplus_backup_job_nonce');
|
472 |
if (!$bnonce) return;
|
@@ -550,6 +333,7 @@ class UpdraftPlus {
|
|
550 |
//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. TODO: I reckon there is. Store a transient based on the backup schedule. Then as the backup proceeds, check for its existence; if it has changed, then another task has begun, so abort.
|
551 |
function backup($backup_files, $backup_database) {
|
552 |
|
|
|
553 |
//generate backup information
|
554 |
$this->backup_time_nonce();
|
555 |
// If we don't finish in 3 hours, then we won't finish
|
@@ -678,10 +462,16 @@ class UpdraftPlus {
|
|
678 |
}
|
679 |
|
680 |
// This should be called whenever a file is successfully uploaded
|
681 |
-
function uploaded_file($file) {
|
682 |
# We take an MD5 hash because set_transient wants a name of 45 characters or less
|
683 |
$hash = md5($file);
|
684 |
set_transient("updraft_".$hash, "yes", 3600*3);
|
|
|
|
|
|
|
|
|
|
|
|
|
685 |
}
|
686 |
|
687 |
function cloud_backup($backup_array) {
|
@@ -766,8 +556,8 @@ class UpdraftPlus {
|
|
766 |
$this->log("$backup_datestamp: Delete remote ftp: $remote_path/$file");
|
767 |
@$remote_object->delete($remote_path.$file);
|
768 |
} elseif ($updraft_service == "googledrive") {
|
769 |
-
$this->log("$backup_datestamp: Delete remote file from Google Drive: $
|
770 |
-
$this->googledrive_delete_file($
|
771 |
}
|
772 |
}
|
773 |
unset($backup_to_examine['db']);
|
@@ -810,8 +600,8 @@ class UpdraftPlus {
|
|
810 |
$this->log("$backup_datestamp: Delete remote ftp: $remote_path/$dofile");
|
811 |
@$remote_object->delete($remote_path.$dofile);
|
812 |
} elseif ($updraft_service == "googledrive") {
|
813 |
-
$this->log("$backup_datestamp: Delete remote file from Google Drive: $
|
814 |
-
$this->googledrive_delete_file($
|
815 |
}
|
816 |
}
|
817 |
}
|
@@ -862,28 +652,138 @@ class UpdraftPlus {
|
|
862 |
$this->error("S3 Error: Failed to create bucket $bucket_name. Error was ".$php_errormsg);
|
863 |
}
|
864 |
}
|
865 |
-
|
866 |
-
function
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
|
|
879 |
}
|
880 |
-
|
881 |
-
$this->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
882 |
} else {
|
883 |
-
$this->log('
|
|
|
|
|
|
|
|
|
884 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
885 |
}
|
886 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
887 |
function ftp_backup($backup_array) {
|
888 |
if( !class_exists('ftp_wrapper')) {
|
889 |
require_once(dirname(__FILE__).'/includes/ftp.class.php');
|
@@ -1278,7 +1178,7 @@ class UpdraftPlus {
|
|
1278 |
$row_start = $segment * ROWS_PER_SEGMENT;
|
1279 |
$row_inc = ROWS_PER_SEGMENT;
|
1280 |
}
|
1281 |
-
do {
|
1282 |
// don't include extra stuff, if so requested
|
1283 |
$excs = array('revisions' => 0, 'spam' => 1); //TODO, FIX THIS
|
1284 |
$where = '';
|
@@ -1349,16 +1249,8 @@ class UpdraftPlus {
|
|
1349 |
}
|
1350 |
}
|
1351 |
|
1352 |
-
/**
|
1353 |
-
* Logs any error messages
|
1354 |
-
* @param array $args
|
1355 |
-
* @return bool
|
1356 |
-
*/
|
1357 |
function error($error,$severity='') {
|
1358 |
-
$this->errors[] =
|
1359 |
-
if ($severity == 'fatal') {
|
1360 |
-
//do something...
|
1361 |
-
}
|
1362 |
return true;
|
1363 |
}
|
1364 |
|
@@ -1467,7 +1359,7 @@ class UpdraftPlus {
|
|
1467 |
$len = filesize($fullpath);
|
1468 |
|
1469 |
$filearr = explode('.',$file);
|
1470 |
-
|
1471 |
$file_ext = array_pop($filearr);
|
1472 |
if($file_ext == 'zip') {
|
1473 |
header('Content-type: application/zip');
|
@@ -1527,7 +1419,48 @@ class UpdraftPlus {
|
|
1527 |
}
|
1528 |
|
1529 |
function download_googledrive_backup($file) {
|
1530 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1531 |
}
|
1532 |
|
1533 |
function download_s3_backup($file) {
|
@@ -1552,9 +1485,8 @@ class UpdraftPlus {
|
|
1552 |
}
|
1553 |
|
1554 |
function download_ftp_backup($file) {
|
1555 |
-
if( !class_exists('ftp_wrapper'))
|
1556 |
-
|
1557 |
-
}
|
1558 |
//handle SSL and errors at some point TODO
|
1559 |
$ftp = new ftp_wrapper(get_option('updraft_server_address'),get_option('updraft_ftp_login'),get_option('updraft_ftp_pass'));
|
1560 |
$ftp->passive = true;
|
@@ -1779,11 +1711,6 @@ class UpdraftPlus {
|
|
1779 |
|
1780 |
function settings_output() {
|
1781 |
|
1782 |
-
$ws_advert = $this->wordshell_random_advert(1);
|
1783 |
-
echo <<<ENDHERE
|
1784 |
-
<div class="updated fade" style="font-size:140%; padding:14px;">${ws_advert}</div>
|
1785 |
-
ENDHERE;
|
1786 |
-
|
1787 |
/*
|
1788 |
we use request here because the initial restore is triggered by a POSTed form. we then may need to obtain credentials
|
1789 |
for the WP_Filesystem. to do this WP outputs a form that we can't insert variables into (apparently). So the values are
|
@@ -1796,8 +1723,15 @@ ENDHERE;
|
|
1796 |
echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus&updraft_restore_success=true">Return to Updraft Configuration</a>.';
|
1797 |
return;
|
1798 |
} else {
|
1799 |
-
echo '<p>Restore failed...</p><
|
1800 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1801 |
return;
|
1802 |
}
|
1803 |
//uncomment the below once i figure out how i want the flow of a restoration to work.
|
@@ -1832,11 +1766,13 @@ ENDHERE;
|
|
1832 |
}
|
1833 |
|
1834 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') {
|
|
|
1835 |
if (wp_schedule_single_event(time()+5, 'updraft_backup_all') === false) {
|
1836 |
-
echo "
|
1837 |
} else {
|
1838 |
-
echo "
|
1839 |
}
|
|
|
1840 |
}
|
1841 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') {
|
1842 |
$this->backup(true,true);
|
@@ -1847,19 +1783,24 @@ ENDHERE;
|
|
1847 |
|
1848 |
?>
|
1849 |
<div class="wrap">
|
1850 |
-
<
|
1851 |
|
1852 |
-
Version: <b><?php echo $this->version; ?></b><br
|
1853 |
-
Maintained by <b>David Anderson</b> (<a href="http://david.dw-perspective.org.uk">Homepage</a> | <a href="http://wordshell.net">WordShell - WordPress command line</a> | <a href="http://david.dw-perspective.org.uk/donate">Donate</a> | <a href="http://wordpress.org/extend/plugins/updraftplus/faq/">FAQs</a> | <a href="http://profiles.wordpress.org/davidanderson/">My other WordPress plugins</a>)
|
1854 |
-
<br
|
1855 |
-
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> )
|
1856 |
-
<br />
|
1857 |
<?php
|
1858 |
if(isset($_GET['updraft_restore_success'])) {
|
1859 |
echo "<div style=\"color:blue\">Your backup has been restored. Your old themes, uploads, and plugins directories have been retained with \"-old\" appended to their name. Remove them when you are satisfied that the backup worked properly. At this time Updraft does not automatically restore your DB. You will need to use an external tool like phpMyAdmin to perform that task.</div>";
|
1860 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1861 |
if($deleted_old_dirs) {
|
1862 |
-
echo
|
1863 |
}
|
1864 |
if(!$this->memory_check(96)) {?>
|
1865 |
<div style="color:orange">Your PHP memory limit is too low. Updraft attempted to raise it but was unsuccessful. This plugin may not work properly with a memory limit of less than 96 Mb (though on the other hand, it has been used successfully with a 32Mb limit - your mileage may vary, but don't blame us!). Current limit is: <?php echo $this->memory_check_current(); ?> Mb</div>
|
@@ -1885,7 +1826,9 @@ ENDHERE;
|
|
1885 |
}
|
1886 |
}
|
1887 |
?>
|
1888 |
-
|
|
|
|
|
1889 |
<tr>
|
1890 |
<?php
|
1891 |
$next_scheduled_backup = wp_next_scheduled('updraft_backup');
|
@@ -1920,7 +1863,8 @@ ENDHERE;
|
|
1920 |
$dir_info = '<span style="color:red">Backup directory specified is <b>not</b> writable, or does not exist. <span style="font-size:110%;font-weight:bold"><a href="options-general.php?page=updraftplus&action=updraft_create_backup_dir">Click here</a></span> to attempt to create the directory and set the permissions. If that is unsuccessful check the permissions on your server or change it to another directory that is writable by your web server process.</span>';
|
1921 |
}
|
1922 |
?>
|
1923 |
-
|
|
|
1924 |
<td style="color:blue"><?php echo $current_time?></td>
|
1925 |
</tr>
|
1926 |
<tr>
|
@@ -1936,10 +1880,10 @@ ENDHERE;
|
|
1936 |
<td style="color:<?php echo $last_backup_color ?>"><?php echo $last_backup?></td>
|
1937 |
</tr>
|
1938 |
</table>
|
1939 |
-
<div style="float:left;width:200px">
|
1940 |
<form method="post" action="">
|
1941 |
<input type="hidden" name="action" value="updraft_backup" />
|
1942 |
-
<p><input type="submit" <?php echo $backup_disabled ?> class="button-primary" value="Backup Now!" style="padding-top:7px;padding-bottom:7px;font-size:24px !important" onclick="return(confirm('This will schedule a one
|
1943 |
</form>
|
1944 |
<div style="position:relative">
|
1945 |
<div style="position:absolute;top:0;left:0">
|
@@ -2040,14 +1984,8 @@ ENDHERE;
|
|
2040 |
</table>
|
2041 |
<form method="post" action="options.php">
|
2042 |
<?php settings_fields('updraft-options-group'); ?>
|
2043 |
-
<
|
2044 |
-
<
|
2045 |
-
<th>Backup Directory:</th>
|
2046 |
-
<td><input type="text" name="updraft_dir" style="width:525px" value="<?php echo $updraft_dir ?>" /></td>
|
2047 |
-
</tr>
|
2048 |
-
<tr>
|
2049 |
-
<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>
|
2050 |
-
</tr>
|
2051 |
<tr>
|
2052 |
<th>File Backup Intervals:</th>
|
2053 |
<td><select name="updraft_interval">
|
@@ -2088,11 +2026,11 @@ ENDHERE;
|
|
2088 |
<tr>
|
2089 |
<th>Include in Files Backup:</th>
|
2090 |
<td>
|
2091 |
-
<input type="checkbox" name="updraft_include_plugins" value="1" <?php echo $include_plugins; ?> /> Plugins<br
|
2092 |
-
<input type="checkbox" name="updraft_include_themes" value="1" <?php echo $include_themes; ?> /> Themes<br
|
2093 |
-
<input type="checkbox" name="updraft_include_uploads" value="1" <?php echo $include_uploads; ?> /> Uploads<br
|
2094 |
-
<input type="checkbox" name="updraft_include_others" value="1" <?php echo $include_others; ?> /> Any other directories found inside wp-content - but exclude these directories: <input type="text" name="updraft_include_others_exclude" size="32" value="<?php echo htmlspecialchars($include_others_exclude); ?>"/><br
|
2095 |
-
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
|
2096 |
</td>
|
2097 |
</tr>
|
2098 |
<tr>
|
@@ -2103,6 +2041,15 @@ ENDHERE;
|
|
2103 |
?>
|
2104 |
<td><input type="text" name="updraft_retain" value="<?php echo $retain ?>" style="width:50px" /></td>
|
2105 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2106 |
<tr class="backup-retain-description">
|
2107 |
<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>
|
2108 |
</tr>
|
@@ -2116,7 +2063,11 @@ ENDHERE;
|
|
2116 |
<tr class="backup-crypt-description">
|
2117 |
<td></td><td>If you enter a string here, it is used to encrypt backups (Rijndael). Do not lose it, or all your backups will be useless. Presently, only the database file is encrypted. This is also the key used to decrypt backups from this admin interface (so if you change it, then automatic decryption will not work until you change it back). You can also use the file example-decrypt.php from inside the UpdraftPlus plugin directory to decrypt manually.</td>
|
2118 |
</tr>
|
|
|
|
|
|
|
2119 |
|
|
|
2120 |
<tr>
|
2121 |
<th>Remote backup:</th>
|
2122 |
<td><select name="updraft_service" id="updraft-service">
|
@@ -2163,13 +2114,13 @@ ENDHERE;
|
|
2163 |
?>
|
2164 |
<option value="none" <?php echo $none?>>None</option>
|
2165 |
<option value="s3" <?php echo $s3?>>Amazon S3</option>
|
2166 |
-
<option value="googledrive" <?php echo $googledrive?>>Google Drive
|
2167 |
<option value="ftp" <?php echo $ftp?>>FTP</option>
|
2168 |
<option value="email" <?php echo $email?>>E-mail</option>
|
2169 |
</select></td>
|
2170 |
</tr>
|
2171 |
<tr class="backup-service-description">
|
2172 |
-
<td></td><td>Choose your backup method.
|
2173 |
|
2174 |
</tr>
|
2175 |
|
@@ -2202,7 +2153,7 @@ ENDHERE;
|
|
2202 |
</tr>
|
2203 |
<tr class="googledrive" <?php echo $googledrive_display?>>
|
2204 |
<th>Google Drive Folder ID:</th>
|
2205 |
-
<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>
|
2206 |
</tr>
|
2207 |
<tr class="googledrive" <?php echo $googledrive_display?>>
|
2208 |
<th>Authenticate with Google:</th>
|
@@ -2214,16 +2165,17 @@ ENDHERE;
|
|
2214 |
}
|
2215 |
?>
|
2216 |
</p>
|
2217 |
-
<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>
|
2218 |
-
<p><strong>N.B. : If you choose Google Drive, then no backups will be deleted - all will be retained. Patches welcome!</strong></p>
|
2219 |
</td>
|
2220 |
</tr>
|
2221 |
<tr class="googledrive" <?php echo $googledrive_display?>>
|
2222 |
<th></th>
|
2223 |
-
<td
|
|
|
|
|
2224 |
<?php
|
2225 |
-
if (!class_exists('SimpleXMLElement')) { echo "<
|
2226 |
?>
|
|
|
2227 |
</td>
|
2228 |
</tr>
|
2229 |
|
@@ -2246,17 +2198,19 @@ ENDHERE;
|
|
2246 |
<tr class="ftp-description" style="display:none">
|
2247 |
<td colspan="2">An FTP remote path will look like '/home/backup/some/folder'</td>
|
2248 |
</tr>
|
2249 |
-
|
2250 |
-
|
2251 |
-
|
|
|
|
|
|
|
2252 |
</tr>
|
2253 |
-
<tr
|
2254 |
-
<
|
2255 |
-
<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>
|
2256 |
</tr>
|
2257 |
<tr>
|
2258 |
<th>Debug mode:</th>
|
2259 |
-
<td><input type="checkbox" name="updraft_debug_mode" value="1" <?php echo $debug_mode; ?> /> <br
|
2260 |
</tr>
|
2261 |
<tr>
|
2262 |
<td>
|
@@ -2269,7 +2223,8 @@ ENDHERE;
|
|
2269 |
<?php
|
2270 |
if(get_option('updraft_debug_mode')) {
|
2271 |
?>
|
2272 |
-
<div>
|
|
|
2273 |
<h3>Debug Information</h3>
|
2274 |
<?php
|
2275 |
$peak_memory_usage = memory_get_peak_usage(true)/1024/1024;
|
@@ -2288,29 +2243,33 @@ ENDHERE;
|
|
2288 |
</form>
|
2289 |
</div>
|
2290 |
<?php } ?>
|
|
|
|
|
|
|
|
|
2291 |
<script type="text/javascript">
|
2292 |
jQuery(document).ready(function() {
|
2293 |
jQuery('#updraft-service').change(function() {
|
2294 |
switch(jQuery(this).val()) {
|
2295 |
case 'none':
|
2296 |
-
jQuery('.deletelocal,.s3,.ftp,.googledrive,.s3-description,.ftp-description').
|
2297 |
-
jQuery('.email,.email-complete').
|
2298 |
break;
|
2299 |
case 's3':
|
2300 |
-
jQuery('.ftp,.ftp-description,.googledrive').
|
2301 |
-
jQuery('.s3,.deletelocal,.email,.email-complete').
|
2302 |
break;
|
2303 |
case 'googledrive':
|
2304 |
-
jQuery('.ftp,.ftp-description,.s3').
|
2305 |
-
jQuery('.googledrive,.deletelocal,.googledrive,.email,.email-complete').
|
2306 |
break;
|
2307 |
case 'ftp':
|
2308 |
-
jQuery('.googledrive,.s3,.s3-description').
|
2309 |
-
jQuery('.ftp,.deletelocal,.email,.email-complete').
|
2310 |
break;
|
2311 |
case 'email':
|
2312 |
-
jQuery('.s3,.ftp,.s3-description,.googledrive,.ftp-description,.email-complete').
|
2313 |
-
jQuery('.email,.deletelocal').
|
2314 |
break;
|
2315 |
}
|
2316 |
})
|
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, on separate schedules.
|
6 |
Author: David Anderson.
|
7 |
+
Version: 0.9.10
|
8 |
Donate link: http://david.dw-perspective.org.uk/donate
|
9 |
+
License: GPL3
|
10 |
Author URI: http://wordshell.net
|
11 |
*/
|
12 |
|
13 |
//TODO (some of these items mine, some from original Updraft awaiting review):
|
14 |
+
//GoogleDrive resume partial upload support (store the current status in a transient after each chunk; use that on resumption)
|
15 |
//Add DropBox support
|
16 |
//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?).
|
17 |
//improve error reporting. s3 and dir backup have decent reporting now, but not sure i know what to do from here
|
23 |
//Rip out the "last backup" bit, and/or put in a display of the last log
|
24 |
|
25 |
/* More TODO:
|
|
|
26 |
Use only one entry in WP options database
|
27 |
Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
|
28 |
// Does not delete old custom directories upon a restore?
|
30 |
|
31 |
/* Portions copyright 2010 Paul Kehrer
|
32 |
Portions copyright 2011-12 David Anderson
|
33 |
+
Other portions copyright as indicated authors in the relevant files
|
34 |
+
Particular thanks to Sorin Iclanzan, author of the "Backup" plugin, from which much Google Drive code was taken under the GPLv3+
|
35 |
|
36 |
This program is free software; you can redistribute it and/or modify
|
37 |
it under the terms of the GNU General Public License as published by
|
38 |
+
the Free Software Foundation; either version 3 of the License, or
|
39 |
(at your option) any later version.
|
40 |
|
41 |
This program is distributed in the hope that it will be useful,
|
62 |
|
63 |
class UpdraftPlus {
|
64 |
|
65 |
+
var $version = '0.9.10';
|
66 |
|
67 |
var $dbhandle;
|
68 |
var $errors = array();
|
70 |
var $logfile_name = "";
|
71 |
var $logfile_handle = false;
|
72 |
var $backup_time;
|
73 |
+
var $gdocs;
|
74 |
+
var $gdocs_access_token;
|
75 |
+
var $gdocs_location;
|
76 |
+
|
77 |
function __construct() {
|
78 |
// Initialisation actions
|
79 |
# Create admin page
|
97 |
if ( is_admin() && isset( $_GET['page'] ) && $_GET['page'] == 'updraftplus' && isset( $_GET['action'] ) && $_GET['action'] == 'auth' ) {
|
98 |
if ( isset( $_GET['state'] ) ) {
|
99 |
if ( $_GET['state'] == 'token' )
|
100 |
+
$this->gdrive_auth_token();
|
101 |
elseif ( $_GET['state'] == 'revoke' )
|
102 |
+
$this->gdrive_auth_revoke();
|
103 |
} elseif (isset($_GET['updraftplus_googleauth'])) {
|
104 |
+
$this->gdrive_auth_request();
|
105 |
}
|
106 |
}
|
107 |
}
|
109 |
/**
|
110 |
* Acquire single-use authorization code from Google OAuth 2.0
|
111 |
*/
|
112 |
+
function gdrive_auth_request() {
|
113 |
$params = array(
|
114 |
'response_type' => 'code',
|
115 |
'client_id' => get_option('updraft_googledrive_clientid'),
|
155 |
}
|
156 |
}
|
157 |
|
158 |
+
function googledrive_delete_file( $file, $token) {
|
159 |
+
$ids = get_option('updraft_file_ids', array());
|
160 |
+
if (!isset($ids[$file])) {
|
161 |
+
$this->log("Could not delete: could not find a record of the Google Drive file ID for this file");
|
162 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
163 |
} else {
|
164 |
+
$del == $this->gdocs->delete_resource($ids[$file]);
|
165 |
+
if (is_wp_error($del)) {
|
166 |
+
foreach ($del->get_error_messages() as $msg) {
|
167 |
+
$this->log("Deletion failed: $msg");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
}
|
169 |
+
} else {
|
170 |
+
$this->log("Deletion successful");
|
171 |
+
unset($ids[$file]);
|
172 |
+
update_option('updraft_file_ids', $ids);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
173 |
}
|
174 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
175 |
return;
|
176 |
}
|
177 |
|
178 |
/**
|
179 |
+
* Get a Google account refresh token using the code received from gdrive_auth_request
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
*/
|
181 |
+
function gdrive_auth_token() {
|
182 |
if( isset( $_GET['code'] ) ) {
|
183 |
$context = array(
|
184 |
'http' => array(
|
221 |
/**
|
222 |
* Revoke a Google account refresh token
|
223 |
*/
|
224 |
+
function gdrive_auth_revoke() {
|
225 |
@file_get_contents( 'https://accounts.google.com/o/oauth2/revoke?token=' . get_option('updraft_googledrive_token') );
|
226 |
update_option('updraft_googledrive_token','');
|
227 |
header( 'Location: '.admin_url( 'options-general.php?page=updraftplus&message=' . __( 'Authorization revoked.', 'backup' ) ) );
|
249 |
}
|
250 |
|
251 |
function backup_resume($resumption_no) {
|
252 |
+
@ignore_user_abort(true);
|
253 |
// This is scheduled for 5 minutes after a backup job starts
|
254 |
$bnonce = get_transient('updraftplus_backup_job_nonce');
|
255 |
if (!$bnonce) return;
|
333 |
//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. TODO: I reckon there is. Store a transient based on the backup schedule. Then as the backup proceeds, check for its existence; if it has changed, then another task has begun, so abort.
|
334 |
function backup($backup_files, $backup_database) {
|
335 |
|
336 |
+
@ignore_user_abort(true);
|
337 |
//generate backup information
|
338 |
$this->backup_time_nonce();
|
339 |
// If we don't finish in 3 hours, then we won't finish
|
462 |
}
|
463 |
|
464 |
// This should be called whenever a file is successfully uploaded
|
465 |
+
function uploaded_file($file, $id = false) {
|
466 |
# We take an MD5 hash because set_transient wants a name of 45 characters or less
|
467 |
$hash = md5($file);
|
468 |
set_transient("updraft_".$hash, "yes", 3600*3);
|
469 |
+
if ($id) {
|
470 |
+
$ids = get_option('updraft_file_ids', array() );
|
471 |
+
$ids[$file] = $id;
|
472 |
+
update_option('updraft_file_ids',$ids);
|
473 |
+
$this->log("Stored file<->id correlation in database ($file <-> $id)");
|
474 |
+
}
|
475 |
}
|
476 |
|
477 |
function cloud_backup($backup_array) {
|
556 |
$this->log("$backup_datestamp: Delete remote ftp: $remote_path/$file");
|
557 |
@$remote_object->delete($remote_path.$file);
|
558 |
} elseif ($updraft_service == "googledrive") {
|
559 |
+
$this->log("$backup_datestamp: Delete remote file from Google Drive: $file");
|
560 |
+
$this->googledrive_delete_file($file,$remote_object);
|
561 |
}
|
562 |
}
|
563 |
unset($backup_to_examine['db']);
|
600 |
$this->log("$backup_datestamp: Delete remote ftp: $remote_path/$dofile");
|
601 |
@$remote_object->delete($remote_path.$dofile);
|
602 |
} elseif ($updraft_service == "googledrive") {
|
603 |
+
$this->log("$backup_datestamp: Delete remote file from Google Drive: $dofile");
|
604 |
+
$this->googledrive_delete_file($dofile,$remote_object);
|
605 |
}
|
606 |
}
|
607 |
}
|
652 |
$this->error("S3 Error: Failed to create bucket $bucket_name. Error was ".$php_errormsg);
|
653 |
}
|
654 |
}
|
655 |
+
|
656 |
+
// This function taken from wordpress.org/extend/plugins/backup, by Sorin Iclanzan, under the GPLv3 or later at your choice
|
657 |
+
function is_gdocs( $thing ) {
|
658 |
+
if ( is_object( $thing ) && is_a( $thing, 'UpdraftPlus_GDocs' ) )
|
659 |
+
return true;
|
660 |
+
return false;
|
661 |
+
}
|
662 |
+
|
663 |
+
// This function modified from wordpress.org/extend/plugins/backup, by Sorin Iclanzan, under the GPLv3 or later at your choice
|
664 |
+
function need_gdocs() {
|
665 |
+
|
666 |
+
if ( ! $this->is_gdocs( $this->gdocs ) ) {
|
667 |
+
if ( get_option('updraft_googledrive_token') == "" || get_option('updraft_googledrive_clientid') == "" || get_option('updraft_googledrive_secret') == "" ) {
|
668 |
+
$this->log("GoogleDrive: this account is not authorised");
|
669 |
+
return new WP_Error( "not_authorized", "Account is not authorized." );
|
670 |
}
|
671 |
+
|
672 |
+
if ( is_wp_error( $this->gdocs_access_token ) ) return $access_token;
|
673 |
+
|
674 |
+
$this->gdocs = new UpdraftPlus_GDocs( $this->gdocs_access_token );
|
675 |
+
$this->gdocs->set_option( 'chunk_size', $this->options['chunk_size'] );
|
676 |
+
$this->gdocs->set_option( 'time_limit', $this->options['time_limit'] );
|
677 |
+
$this->gdocs->set_option( 'request_timeout', $this->options['request_timeout'] );
|
678 |
+
$this->gdocs->set_option( 'max_resume_attempts', $this->options['backup_attempts'] );
|
679 |
+
}
|
680 |
+
return true;
|
681 |
+
}
|
682 |
+
|
683 |
+
function googledrive_upload_file( $file, $title, $parent = '') {
|
684 |
+
|
685 |
+
// Make sure $this->gdocs is a UpdraftPlus_GDocs object, or give an error
|
686 |
+
if ( is_wp_error( $e = $this->need_gdocs() ) ) return false;
|
687 |
+
|
688 |
+
if ( empty( $this->gdocs_location ) ) {
|
689 |
+
$this->log("$file: Attempting to upload file to Google Drive.");
|
690 |
+
$location = $this->gdocs->prepare_upload(
|
691 |
+
$file,
|
692 |
+
$title,
|
693 |
+
$parent
|
694 |
+
);
|
695 |
} else {
|
696 |
+
$this->log('$file: Attempting to resume upload.');
|
697 |
+
$location = $this->gdocs->resume_upload(
|
698 |
+
$file,
|
699 |
+
$this->gdocs_location
|
700 |
+
);
|
701 |
}
|
702 |
+
|
703 |
+
if ( is_wp_error( $location ) ) {
|
704 |
+
$this->log("GoogleDrive upload: an error occurred");
|
705 |
+
foreach ($location->get_error_messages() as $msg) {
|
706 |
+
$this->log("Error details: ".$msg);
|
707 |
+
}
|
708 |
+
// TODO
|
709 |
+
//$this->reschedule_backup( $id );
|
710 |
+
return false;
|
711 |
+
}
|
712 |
+
|
713 |
+
if (!is_string($location) && true == $location) {
|
714 |
+
$this->log("$file: this file is already uploaded");
|
715 |
+
return true;
|
716 |
+
}
|
717 |
+
|
718 |
+
if ( is_string( $location ) ) {
|
719 |
+
$res = $location;
|
720 |
+
$this->log("Uploading file with title ".$title);
|
721 |
+
$d = 0;
|
722 |
+
// echo '<div id="progress">';
|
723 |
+
do {
|
724 |
+
$this->gdocs_location = $res;
|
725 |
+
$res = $this->gdocs->upload_chunk();
|
726 |
+
$p = $this->gdocs->get_upload_percentage();
|
727 |
+
if ( $p - $d >= 1 ) {
|
728 |
+
$b = intval( $p - $d );
|
729 |
+
// echo '<span style="width:' . $b . '%"></span>';
|
730 |
+
$d += $b;
|
731 |
+
}
|
732 |
+
// $this->options['backup_list'][$id]['percentage'] = $p;
|
733 |
+
// $this->options['backup_list'][$id]['speed'] = $this->gdocs->get_upload_speed();
|
734 |
+
} while ( is_string( $res ) );
|
735 |
+
// echo '</div>';
|
736 |
+
|
737 |
+
if ( is_wp_error( $res ) ) {
|
738 |
+
$this->log( "An error occurred during GoogleDrive upload (2)" );
|
739 |
+
# TODO
|
740 |
+
// $this->reschedule_backup( $id );
|
741 |
+
return false;
|
742 |
+
}
|
743 |
+
|
744 |
+
$this->log("The file was successfully uploaded to Google Drive in ".number_format_i18n( $this->gdocs->time_taken(), 3)." seconds at an upload speed of ".size_format( $this->gdocs->get_upload_speed() )."/s.");
|
745 |
+
|
746 |
+
$this->gdocs_location = null;
|
747 |
+
// unset( $this->options['backup_list'][$id]['location'], $this->options['backup_list'][$id]['attempt'] );
|
748 |
+
}
|
749 |
+
|
750 |
+
return $this->gdocs->get_file_id();
|
751 |
+
// unset( $this->options['backup_list'][$id]['percentage'], $this->options['backup_list'][$id]['speed'] );
|
752 |
+
// $this->update_quota();
|
753 |
+
// Google's "user info" service
|
754 |
+
// if ( empty( $this->options['user_info'] ) ) $this->set_user_info();
|
755 |
+
|
756 |
}
|
757 |
+
|
758 |
+
// This function just does the formalities, and off-loads the main work to googledrive_upload_file
|
759 |
+
function googledrive_backup($backup_array) {
|
760 |
+
|
761 |
+
require_once(dirname(__FILE__).'/includes/class-gdocs.php');
|
762 |
+
|
763 |
+
// Do we have an access token?
|
764 |
+
if ( !$access_token = $this->access_token( get_option('updraft_googledrive_token'), get_option('updraft_googledrive_clientid'), get_option('updraft_googledrive_secret') )) {
|
765 |
+
$this->log('ERROR: Have not yet obtained an access token from Google (has the user authorised?)');
|
766 |
+
return new WP_Error( "no_access_token", "Have not yet obtained an access token from Google (has the user authorised?");
|
767 |
+
}
|
768 |
+
|
769 |
+
$this->gdocs_access_token = $access_token;
|
770 |
+
|
771 |
+
foreach ($backup_array as $file) {
|
772 |
+
$file_path = trailingslashit(get_option('updraft_dir')).$file;
|
773 |
+
$file_name = basename($file_path);
|
774 |
+
$this->log("$file_name: Attempting to upload to Google Drive");
|
775 |
+
$timer_start = microtime( true );
|
776 |
+
if ( $id = $this->googledrive_upload_file( $file_path, $file_name, get_option('updraft_googledrive_remotepath')) ) {
|
777 |
+
$this->log('OK: Archive ' . $file_name . ' uploaded to Google Drive in ' . ( round(microtime( true ) - $timer_start,2) ) . ' seconds (id: '.$id.')' );
|
778 |
+
$this->uploaded_file($file, $id);
|
779 |
+
} else {
|
780 |
+
$this->error("$file_name: Failed to upload to Google Drive" );
|
781 |
+
$this->log("ERROR: $file_name: Failed to upload to Google Drive" );
|
782 |
+
}
|
783 |
+
}
|
784 |
+
$this->prune_retained_backups("googledrive",$access_token,get_option('updraft_googledrive_remotepath'));
|
785 |
+
}
|
786 |
+
|
787 |
function ftp_backup($backup_array) {
|
788 |
if( !class_exists('ftp_wrapper')) {
|
789 |
require_once(dirname(__FILE__).'/includes/ftp.class.php');
|
1178 |
$row_start = $segment * ROWS_PER_SEGMENT;
|
1179 |
$row_inc = ROWS_PER_SEGMENT;
|
1180 |
}
|
1181 |
+
do {
|
1182 |
// don't include extra stuff, if so requested
|
1183 |
$excs = array('revisions' => 0, 'spam' => 1); //TODO, FIX THIS
|
1184 |
$where = '';
|
1249 |
}
|
1250 |
}
|
1251 |
|
|
|
|
|
|
|
|
|
|
|
1252 |
function error($error,$severity='') {
|
1253 |
+
$this->errors[] = $error;
|
|
|
|
|
|
|
1254 |
return true;
|
1255 |
}
|
1256 |
|
1359 |
$len = filesize($fullpath);
|
1360 |
|
1361 |
$filearr = explode('.',$file);
|
1362 |
+
// //we've only got zip and gz...for now
|
1363 |
$file_ext = array_pop($filearr);
|
1364 |
if($file_ext == 'zip') {
|
1365 |
header('Content-type: application/zip');
|
1419 |
}
|
1420 |
|
1421 |
function download_googledrive_backup($file) {
|
1422 |
+
|
1423 |
+
require_once(dirname(__FILE__).'/includes/class-gdocs.php');
|
1424 |
+
|
1425 |
+
// Do we have an access token?
|
1426 |
+
if ( !$access_token = $this->access_token( get_option('updraft_googledrive_token'), get_option('updraft_googledrive_clientid'), get_option('updraft_googledrive_secret') )) {
|
1427 |
+
$this->error('ERROR: Have not yet obtained an access token from Google (has the user authorised?)');
|
1428 |
+
return false;
|
1429 |
+
}
|
1430 |
+
|
1431 |
+
$this->gdocs_access_token = $access_token;
|
1432 |
+
|
1433 |
+
// Make sure $this->gdocs is a UpdraftPlus_GDocs object, or give an error
|
1434 |
+
if ( is_wp_error( $e = $this->need_gdocs() ) ) return false;
|
1435 |
+
|
1436 |
+
$ids = get_option('updraft_file_ids', array());
|
1437 |
+
if (!isset($ids[$file])) {
|
1438 |
+
$this->error("Google Drive error: $file: could not download: could not find a record of the Google Drive file ID for this file");
|
1439 |
+
return;
|
1440 |
+
} else {
|
1441 |
+
$content_link = $this->gdocs->get_content_link( $ids[$file], $file );
|
1442 |
+
if (is_wp_error($content_link)) {
|
1443 |
+
$this->error("Could not find $file in order to download it (id: ".$ids[$file].")");
|
1444 |
+
foreach ($content_link->get_error_messages() as $msg) {
|
1445 |
+
$this->error($msg);
|
1446 |
+
}
|
1447 |
+
return false;
|
1448 |
+
}
|
1449 |
+
// Actually download the thing
|
1450 |
+
$download_to = trailingslashit(get_option('updraft_dir')).$file;
|
1451 |
+
$this->gdocs->download_data($content_link, $download_to);
|
1452 |
+
|
1453 |
+
if (filesize($download_to) >0) {
|
1454 |
+
return true;
|
1455 |
+
} else {
|
1456 |
+
$this->error("Google Drive error: zero-size file was downloaded");
|
1457 |
+
return false;
|
1458 |
+
}
|
1459 |
+
|
1460 |
+
}
|
1461 |
+
|
1462 |
+
return;
|
1463 |
+
|
1464 |
}
|
1465 |
|
1466 |
function download_s3_backup($file) {
|
1485 |
}
|
1486 |
|
1487 |
function download_ftp_backup($file) {
|
1488 |
+
if( !class_exists('ftp_wrapper')) require_once(dirname(__FILE__).'/includes/ftp.class.php');
|
1489 |
+
|
|
|
1490 |
//handle SSL and errors at some point TODO
|
1491 |
$ftp = new ftp_wrapper(get_option('updraft_server_address'),get_option('updraft_ftp_login'),get_option('updraft_ftp_pass'));
|
1492 |
$ftp->passive = true;
|
1711 |
|
1712 |
function settings_output() {
|
1713 |
|
|
|
|
|
|
|
|
|
|
|
1714 |
/*
|
1715 |
we use request here because the initial restore is triggered by a POSTed form. we then may need to obtain credentials
|
1716 |
for the WP_Filesystem. to do this WP outputs a form that we can't insert variables into (apparently). So the values are
|
1723 |
echo '<b>Actions:</b> <a href="options-general.php?page=updraftplus&updraft_restore_success=true">Return to Updraft Configuration</a>.';
|
1724 |
return;
|
1725 |
} else {
|
1726 |
+
echo '<p>Restore failed...</p><ul>';
|
1727 |
+
foreach ($this->errors as $err) {
|
1728 |
+
echo "<li>";
|
1729 |
+
if (is_string($err)) { echo htmlspecialchars($err); } else {
|
1730 |
+
print_r($err);
|
1731 |
+
}
|
1732 |
+
echo "</li>";
|
1733 |
+
}
|
1734 |
+
echo '</ul><b>Actions:</b> <a href="options-general.php?page=updraftplus">Return to Updraft Configuration</a>.';
|
1735 |
return;
|
1736 |
}
|
1737 |
//uncomment the below once i figure out how i want the flow of a restoration to work.
|
1766 |
}
|
1767 |
|
1768 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') {
|
1769 |
+
echo '<div class="updated fade" style="max-width: 800px; font-size:140%; padding:14px; clear:left;"><strong>Schedule backup:</strong> ';
|
1770 |
if (wp_schedule_single_event(time()+5, 'updraft_backup_all') === false) {
|
1771 |
+
echo "Failed.";
|
1772 |
} else {
|
1773 |
+
echo "OK. Now load a page from your site to make sure the schedule can trigger.";
|
1774 |
}
|
1775 |
+
echo '</div>';
|
1776 |
}
|
1777 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') {
|
1778 |
$this->backup(true,true);
|
1783 |
|
1784 |
?>
|
1785 |
<div class="wrap">
|
1786 |
+
<h1>UpdraftPlus - Backup/Restore</h1>
|
1787 |
|
1788 |
+
<!-- Version: <b><?php echo $this->version; ?></b><br>-->
|
1789 |
+
Maintained by <b>David Anderson</b> (<a href="http://david.dw-perspective.org.uk">Homepage</a> | <a href="http://wordshell.net">WordShell - WordPress command line</a> | <a href="http://david.dw-perspective.org.uk/donate">Donate</a> | <a href="http://wordpress.org/extend/plugins/updraftplus/faq/">FAQs</a> | <a href="http://profiles.wordpress.org/davidanderson/">My other WordPress plugins</a>). Version: <?php echo $this->version; ?>
|
1790 |
+
<br>
|
|
|
|
|
1791 |
<?php
|
1792 |
if(isset($_GET['updraft_restore_success'])) {
|
1793 |
echo "<div style=\"color:blue\">Your backup has been restored. Your old themes, uploads, and plugins directories have been retained with \"-old\" appended to their name. Remove them when you are satisfied that the backup worked properly. At this time Updraft does not automatically restore your DB. You will need to use an external tool like phpMyAdmin to perform that task.</div>";
|
1794 |
}
|
1795 |
+
|
1796 |
+
$ws_advert = $this->wordshell_random_advert(1);
|
1797 |
+
echo <<<ENDHERE
|
1798 |
+
<div class="updated fade" style="max-width: 800px; font-size:140%; padding:14px; clear:left;">${ws_advert}</div>
|
1799 |
+
ENDHERE;
|
1800 |
+
|
1801 |
+
|
1802 |
if($deleted_old_dirs) {
|
1803 |
+
echo '<div style="color:blue">Old directories successfully deleted.</div>';
|
1804 |
}
|
1805 |
if(!$this->memory_check(96)) {?>
|
1806 |
<div style="color:orange">Your PHP memory limit is too low. Updraft attempted to raise it but was unsuccessful. This plugin may not work properly with a memory limit of less than 96 Mb (though on the other hand, it has been used successfully with a 32Mb limit - your mileage may vary, but don't blame us!). Current limit is: <?php echo $this->memory_check_current(); ?> Mb</div>
|
1826 |
}
|
1827 |
}
|
1828 |
?>
|
1829 |
+
|
1830 |
+
<h2 style="clear:left;">Existing Schedule And Backups</h2>
|
1831 |
+
<table class="form-table" style="float:left; clear: both; width:475px">
|
1832 |
<tr>
|
1833 |
<?php
|
1834 |
$next_scheduled_backup = wp_next_scheduled('updraft_backup');
|
1863 |
$dir_info = '<span style="color:red">Backup directory specified is <b>not</b> writable, or does not exist. <span style="font-size:110%;font-weight:bold"><a href="options-general.php?page=updraftplus&action=updraft_create_backup_dir">Click here</a></span> to attempt to create the directory and set the permissions. If that is unsuccessful check the permissions on your server or change it to another directory that is writable by your web server process.</span>';
|
1864 |
}
|
1865 |
?>
|
1866 |
+
|
1867 |
+
<th>The Time Now:</th>
|
1868 |
<td style="color:blue"><?php echo $current_time?></td>
|
1869 |
</tr>
|
1870 |
<tr>
|
1880 |
<td style="color:<?php echo $last_backup_color ?>"><?php echo $last_backup?></td>
|
1881 |
</tr>
|
1882 |
</table>
|
1883 |
+
<div style="float:left; width:200px; padding-top: 100px;">
|
1884 |
<form method="post" action="">
|
1885 |
<input type="hidden" name="action" value="updraft_backup" />
|
1886 |
+
<p><input type="submit" <?php echo $backup_disabled ?> class="button-primary" value="Backup Now!" style="padding-top:7px;padding-bottom:7px;font-size:24px !important" onclick="return(confirm('This will schedule a one-time backup. To trigger the backup immediately you may need to load a page on your site.'))" /></p>
|
1887 |
</form>
|
1888 |
<div style="position:relative">
|
1889 |
<div style="position:absolute;top:0;left:0">
|
1984 |
</table>
|
1985 |
<form method="post" action="options.php">
|
1986 |
<?php settings_fields('updraft-options-group'); ?>
|
1987 |
+
<h2>Configure Backup Contents And Schedule</h2>
|
1988 |
+
<table class="form-table" style="width:850px;">
|
|
|
|
|
|
|
|
|
|
|
|
|
1989 |
<tr>
|
1990 |
<th>File Backup Intervals:</th>
|
1991 |
<td><select name="updraft_interval">
|
2026 |
<tr>
|
2027 |
<th>Include in Files Backup:</th>
|
2028 |
<td>
|
2029 |
+
<input type="checkbox" name="updraft_include_plugins" value="1" <?php echo $include_plugins; ?> /> Plugins<br>
|
2030 |
+
<input type="checkbox" name="updraft_include_themes" value="1" <?php echo $include_themes; ?> /> Themes<br>
|
2031 |
+
<input type="checkbox" name="updraft_include_uploads" value="1" <?php echo $include_uploads; ?> /> Uploads<br>
|
2032 |
+
<input type="checkbox" name="updraft_include_others" value="1" <?php echo $include_others; ?> /> Any other directories found inside wp-content - but exclude these directories: <input type="text" name="updraft_include_others_exclude" size="32" value="<?php echo htmlspecialchars($include_others_exclude); ?>"/><br>
|
2033 |
+
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>
|
2034 |
</td>
|
2035 |
</tr>
|
2036 |
<tr>
|
2041 |
?>
|
2042 |
<td><input type="text" name="updraft_retain" value="<?php echo $retain ?>" style="width:50px" /></td>
|
2043 |
</tr>
|
2044 |
+
<tr class="email" <?php echo $email_display?>>
|
2045 |
+
<th>Email:</th>
|
2046 |
+
<td><input type="text" style="width:260px" name="updraft_email" value="<?php echo get_option('updraft_email'); ?>" /> <br>Enter an address here to have a report sent (and the whole backup, if you choose) to it.</td>
|
2047 |
+
</tr>
|
2048 |
+
<tr class="deletelocal s3 ftp email" <?php echo $display_delete_local?>>
|
2049 |
+
<th>Delete local backup:</th>
|
2050 |
+
<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 (below), otherwise you will have no backup remaining).</td>
|
2051 |
+
</tr>
|
2052 |
+
|
2053 |
<tr class="backup-retain-description">
|
2054 |
<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>
|
2055 |
</tr>
|
2063 |
<tr class="backup-crypt-description">
|
2064 |
<td></td><td>If you enter a string here, it is used to encrypt backups (Rijndael). Do not lose it, or all your backups will be useless. Presently, only the database file is encrypted. This is also the key used to decrypt backups from this admin interface (so if you change it, then automatic decryption will not work until you change it back). You can also use the file example-decrypt.php from inside the UpdraftPlus plugin directory to decrypt manually.</td>
|
2065 |
</tr>
|
2066 |
+
</table>
|
2067 |
+
|
2068 |
+
<h2>Copying Your Backup To Remote Storage</h2>
|
2069 |
|
2070 |
+
<table class="form-table" style="width:850px;">
|
2071 |
<tr>
|
2072 |
<th>Remote backup:</th>
|
2073 |
<td><select name="updraft_service" id="updraft-service">
|
2114 |
?>
|
2115 |
<option value="none" <?php echo $none?>>None</option>
|
2116 |
<option value="s3" <?php echo $s3?>>Amazon S3</option>
|
2117 |
+
<option value="googledrive" <?php echo $googledrive?>>Google Drive</option>
|
2118 |
<option value="ftp" <?php echo $ftp?>>FTP</option>
|
2119 |
<option value="email" <?php echo $email?>>E-mail</option>
|
2120 |
</select></td>
|
2121 |
</tr>
|
2122 |
<tr class="backup-service-description">
|
2123 |
+
<td></td><td>Choose your backup method. If choosing "E-Mail", then be aware that mail servers tend to have size limits; typically around 10-20Mb; backups larger than any limits will not arrive.</td>
|
2124 |
|
2125 |
</tr>
|
2126 |
|
2153 |
</tr>
|
2154 |
<tr class="googledrive" <?php echo $googledrive_display?>>
|
2155 |
<th>Google Drive Folder ID:</th>
|
2156 |
+
<td><input type="text" style="width:332px" name="updraft_googledrive_remotepath" value="<?php echo get_option('updraft_googledrive_remotepath'); ?>" /> <em>(To get a folder's ID navigate to that folder in Google Drive in your web browser and copy the ID from your browser's address bar. It is the part that comes after <kbd>#folders/.</kbd> Leave empty to use your root folder)</em></td>
|
2157 |
</tr>
|
2158 |
<tr class="googledrive" <?php echo $googledrive_display?>>
|
2159 |
<th>Authenticate with Google:</th>
|
2165 |
}
|
2166 |
?>
|
2167 |
</p>
|
|
|
|
|
2168 |
</td>
|
2169 |
</tr>
|
2170 |
<tr class="googledrive" <?php echo $googledrive_display?>>
|
2171 |
<th></th>
|
2172 |
+
<td>
|
2173 |
+
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.
|
2174 |
+
|
2175 |
<?php
|
2176 |
+
if (!class_exists('SimpleXMLElement')) { echo " <b>WARNING:</b> You do not have the SimpleXMLElement installed. Google Drive backups will <b>not</b> work until you do."; }
|
2177 |
?>
|
2178 |
+
|
2179 |
</td>
|
2180 |
</tr>
|
2181 |
|
2198 |
<tr class="ftp-description" style="display:none">
|
2199 |
<td colspan="2">An FTP remote path will look like '/home/backup/some/folder'</td>
|
2200 |
</tr>
|
2201 |
+
</table>
|
2202 |
+
<table class="form-table" style="width:850px;">
|
2203 |
+
<tr><td colspan="2"><h2>Advanced / Debugging Settings</h2></td></tr>
|
2204 |
+
<tr>
|
2205 |
+
<th>Backup Directory:</th>
|
2206 |
+
<td><input type="text" name="updraft_dir" style="width:525px" value="<?php echo $updraft_dir ?>" /></td>
|
2207 |
</tr>
|
2208 |
+
<tr>
|
2209 |
+
<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>
|
|
|
2210 |
</tr>
|
2211 |
<tr>
|
2212 |
<th>Debug mode:</th>
|
2213 |
+
<td><input type="checkbox" name="updraft_debug_mode" value="1" <?php echo $debug_mode; ?> /> <br>Check this for more information, if something is going wrong. Will also drop a log file in your backup directory which you can examine.</td>
|
2214 |
</tr>
|
2215 |
<tr>
|
2216 |
<td>
|
2223 |
<?php
|
2224 |
if(get_option('updraft_debug_mode')) {
|
2225 |
?>
|
2226 |
+
<div style="padding-top: 40px;">
|
2227 |
+
<hr>
|
2228 |
<h3>Debug Information</h3>
|
2229 |
<?php
|
2230 |
$peak_memory_usage = memory_get_peak_usage(true)/1024/1024;
|
2243 |
</form>
|
2244 |
</div>
|
2245 |
<?php } ?>
|
2246 |
+
|
2247 |
+
<p><em>UpdraftPlus is based on the original 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> )</em></p>
|
2248 |
+
|
2249 |
+
|
2250 |
<script type="text/javascript">
|
2251 |
jQuery(document).ready(function() {
|
2252 |
jQuery('#updraft-service').change(function() {
|
2253 |
switch(jQuery(this).val()) {
|
2254 |
case 'none':
|
2255 |
+
jQuery('.deletelocal,.s3,.ftp,.googledrive,.s3-description,.ftp-description').fadeOut()
|
2256 |
+
jQuery('.email,.email-complete').fadeIn()
|
2257 |
break;
|
2258 |
case 's3':
|
2259 |
+
jQuery('.ftp,.ftp-description,.googledrive').fadeOut()
|
2260 |
+
jQuery('.s3,.deletelocal,.email,.email-complete').fadeIn()
|
2261 |
break;
|
2262 |
case 'googledrive':
|
2263 |
+
jQuery('.ftp,.ftp-description,.s3').fadeOut()
|
2264 |
+
jQuery('.googledrive,.deletelocal,.googledrive,.email,.email-complete').fadeIn()
|
2265 |
break;
|
2266 |
case 'ftp':
|
2267 |
+
jQuery('.googledrive,.s3,.s3-description').fadeOut()
|
2268 |
+
jQuery('.ftp,.deletelocal,.email,.email-complete').fadeIn()
|
2269 |
break;
|
2270 |
case 'email':
|
2271 |
+
jQuery('.s3,.ftp,.s3-description,.googledrive,.ftp-description,.email-complete').fadeOut()
|
2272 |
+
jQuery('.email,.deletelocal').fadeIn()
|
2273 |
break;
|
2274 |
}
|
2275 |
})
|