Version Description
- 03/11/2013 =
- Improve batching on zip creation for sites with very large files
- Unlimited early resumption if zip file creation takes too long
- Suppress some warning notices that can break JavaScript on sites with notices sent to the browser
- Earlier warning/failure if backup directory was not writable
- Hooks for Dropbox folders add-on
- More scheduler/overlap tweaks, to assist enormous uploads
- When the temporary directory is within the site, store+display relatively (removes need to modify upon site move)
- Sort existing backups display by date
- Use WordPress time for creation of filenames
- Fix bug in 1.4.47 which caused problems on new site installs
Download this release
Release Info
Developer | DavidAnderson |
Plugin | UpdraftPlus WordPress Backup Plugin |
Version | 1.4.48 |
Comparing to | |
See all releases |
Code changes from version 1.4.30 to 1.4.48
- includes/Dropbox/API.php +1 -1
- includes/Dropbox/OAuth/Consumer/ConsumerAbstract.php +2 -1
- includes/updraft-restorer.php +12 -0
- methods/dropbox.php +11 -2
- methods/email.php +4 -2
- methods/ftp.php +4 -2
- methods/googledrive.php +5 -3
- methods/s3.php +5 -3
- options.php +1 -1
- readme.txt +23 -2
- updraftplus.php +203 -62
includes/Dropbox/API.php
CHANGED
@@ -173,7 +173,7 @@ class Dropbox_API
|
|
173 |
if (isset($response['body']->offset)) {
|
174 |
$offset = $response['body']->offset;
|
175 |
if ($callback) {
|
176 |
-
call_user_func($callback, $offset, $uploadID);
|
177 |
}
|
178 |
}
|
179 |
|
173 |
if (isset($response['body']->offset)) {
|
174 |
$offset = $response['body']->offset;
|
175 |
if ($callback) {
|
176 |
+
call_user_func($callback, $offset, $uploadID, $file);
|
177 |
}
|
178 |
}
|
179 |
|
includes/Dropbox/OAuth/Consumer/ConsumerAbstract.php
CHANGED
@@ -43,7 +43,8 @@ abstract class Dropbox_ConsumerAbstract
|
|
43 |
*/
|
44 |
protected function authenticate()
|
45 |
{
|
46 |
-
|
|
|
47 |
try {
|
48 |
$this->getAccessToken();
|
49 |
} catch(Dropbox_Exception $e) {
|
43 |
*/
|
44 |
protected function authenticate()
|
45 |
{
|
46 |
+
$access_token = $this->storage->get('access_token');
|
47 |
+
if (empty($access_token) || !isset($access_token->oauth_token)) {
|
48 |
try {
|
49 |
$this->getAccessToken();
|
50 |
} catch(Dropbox_Exception $e) {
|
includes/updraft-restorer.php
CHANGED
@@ -43,6 +43,18 @@ class Updraft_Restorer extends WP_Upgrader {
|
|
43 |
if ( !empty($upgrade_files) ) {
|
44 |
foreach ( $upgrade_files as $filestruc ) {
|
45 |
$file = $filestruc['name'];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
# Sanity check (should not be possible as these were excluded at backup time)
|
47 |
if ($file != "plugins" && $file != "themes" && $file != "uploads" && $file != "upgrade") {
|
48 |
# First, move the existing one, if necessary (may not be present)
|
43 |
if ( !empty($upgrade_files) ) {
|
44 |
foreach ( $upgrade_files as $filestruc ) {
|
45 |
$file = $filestruc['name'];
|
46 |
+
|
47 |
+
// Correctly restore files in 'others' in no directory that were wrongly backed up in versions 1.4.0 - 1.4.48
|
48 |
+
if (preg_match('/^([\-_A-Za-z0-9]+\.php)$/', $file, $matches) && $wp_filesystem->exists($working_dir . "/$file/$file")) {
|
49 |
+
echo "Found file: $file/$file: presuming this is a backup with a known fault (backup made with versions 1.4.0 - 1.4.48); will rename to simply $file<br>";
|
50 |
+
$file = $matches[1];
|
51 |
+
$tmp_file = rand(0,999999999).'.php';
|
52 |
+
// Rename directory
|
53 |
+
$wp_filesystem->move($working_dir . "/$file", $working_dir . "/".$tmp_file, true);
|
54 |
+
$wp_filesystem->move($working_dir . "/$tmp_file/$file", $working_dir ."/".$file, true);
|
55 |
+
$wp_filesystem->rmdir($working_dir . "/$tmp_file", false);
|
56 |
+
}
|
57 |
+
|
58 |
# Sanity check (should not be possible as these were excluded at backup time)
|
59 |
if ($file != "plugins" && $file != "themes" && $file != "uploads" && $file != "upgrade") {
|
60 |
# First, move the existing one, if necessary (may not be present)
|
methods/dropbox.php
CHANGED
@@ -7,7 +7,7 @@ class UpdraftPlus_BackupModule_dropbox {
|
|
7 |
private $current_file_hash;
|
8 |
private $current_file_size;
|
9 |
|
10 |
-
function chunked_callback($offset, $uploadid) {
|
11 |
global $updraftplus;
|
12 |
|
13 |
// Update upload ID
|
@@ -16,9 +16,11 @@ class UpdraftPlus_BackupModule_dropbox {
|
|
16 |
|
17 |
if ($this->current_file_size > 0) {
|
18 |
$percent = round(100*($offset/$this->current_file_size),1);
|
19 |
-
$updraftplus->record_uploaded_chunk($percent, "$uploadid, $offset");
|
20 |
} else {
|
21 |
$updraftplus->log("Dropbox: Chunked Upload: $offset bytes uploaded");
|
|
|
|
|
22 |
}
|
23 |
|
24 |
}
|
@@ -34,6 +36,8 @@ class UpdraftPlus_BackupModule_dropbox {
|
|
34 |
return false;
|
35 |
}
|
36 |
|
|
|
|
|
37 |
try {
|
38 |
$dropbox = $this->bootstrap();
|
39 |
$dropbox->setChunkSize(524288); // 512Kb
|
@@ -265,6 +269,11 @@ class UpdraftPlus_BackupModule_dropbox {
|
|
265 |
if ( isset( $_GET['oauth_token'] ) ) {
|
266 |
self::auth_token();
|
267 |
} elseif (isset($_GET['updraftplus_dropboxauth'])) {
|
|
|
|
|
|
|
|
|
|
|
268 |
self::auth_request();
|
269 |
}
|
270 |
}
|
7 |
private $current_file_hash;
|
8 |
private $current_file_size;
|
9 |
|
10 |
+
function chunked_callback($offset, $uploadid, $fullpath = false) {
|
11 |
global $updraftplus;
|
12 |
|
13 |
// Update upload ID
|
16 |
|
17 |
if ($this->current_file_size > 0) {
|
18 |
$percent = round(100*($offset/$this->current_file_size),1);
|
19 |
+
$updraftplus->record_uploaded_chunk($percent, "$uploadid, $offset", $fullpath);
|
20 |
} else {
|
21 |
$updraftplus->log("Dropbox: Chunked Upload: $offset bytes uploaded");
|
22 |
+
// This act is done by record_uploaded_chunk, and helps prevent overlapping runs
|
23 |
+
touch($fullpath);
|
24 |
}
|
25 |
|
26 |
}
|
36 |
return false;
|
37 |
}
|
38 |
|
39 |
+
$updraftplus->log("Dropbox: access gained");
|
40 |
+
|
41 |
try {
|
42 |
$dropbox = $this->bootstrap();
|
43 |
$dropbox->setChunkSize(524288); // 512Kb
|
269 |
if ( isset( $_GET['oauth_token'] ) ) {
|
270 |
self::auth_token();
|
271 |
} elseif (isset($_GET['updraftplus_dropboxauth'])) {
|
272 |
+
// Clear out the existing credentials
|
273 |
+
if ('doit' == $_GET['updraftplus_dropboxauth']) {
|
274 |
+
UpdraftPlus_Options::update_updraft_option("updraft_dropboxtk_request_token",'');
|
275 |
+
UpdraftPlus_Options::update_updraft_option("updraft_dropboxtk_access_token",'');
|
276 |
+
}
|
277 |
self::auth_request();
|
278 |
}
|
279 |
}
|
methods/email.php
CHANGED
@@ -8,8 +8,10 @@ class UpdraftPlus_BackupModule_email {
|
|
8 |
|
9 |
global $updraftplus;
|
10 |
|
|
|
|
|
11 |
foreach ($backup_array as $type => $file) {
|
12 |
-
$fullpath =
|
13 |
wp_mail(UpdraftPlus_Options::get_updraft_option('updraft_email'), "WordPress Backup ".date('Y-m-d H:i',$updraftplus->backup_time), "Backup is of the $type. Be wary; email backups may fail because of file size limitations on mail servers.", null, array($fullpath));
|
14 |
$updraftplus->uploaded_file($file);
|
15 |
}
|
@@ -20,7 +22,7 @@ class UpdraftPlus_BackupModule_email {
|
|
20 |
?>
|
21 |
<tr class="updraftplusmethod email">
|
22 |
<th>Note:</th>
|
23 |
-
<td>The email address entered above will be used. 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. If you really need a large backup via email, then you could fund a new feature (to break the backup set into configurable-size pieces) - but the demand has not yet existed for such a feature.</td>
|
24 |
</tr>
|
25 |
<?php
|
26 |
}
|
8 |
|
9 |
global $updraftplus;
|
10 |
|
11 |
+
$updraft_dir = $updraftplus->backups_dir_location().'/';
|
12 |
+
|
13 |
foreach ($backup_array as $type => $file) {
|
14 |
+
$fullpath = $updraft_dir.$file;
|
15 |
wp_mail(UpdraftPlus_Options::get_updraft_option('updraft_email'), "WordPress Backup ".date('Y-m-d H:i',$updraftplus->backup_time), "Backup is of the $type. Be wary; email backups may fail because of file size limitations on mail servers.", null, array($fullpath));
|
16 |
$updraftplus->uploaded_file($file);
|
17 |
}
|
22 |
?>
|
23 |
<tr class="updraftplusmethod email">
|
24 |
<th>Note:</th>
|
25 |
+
<td>The email address entered above will be used. If choosing "E-Mail", then <strong>be aware</strong> that mail servers tend to have size limits; typically around 10-20Mb; backups larger than any limits will not arrive. If you really need a large backup via email, then you could fund a new feature (to break the backup set into configurable-size pieces) - but the demand has not yet existed for such a feature.</td>
|
26 |
</tr>
|
27 |
<?php
|
28 |
}
|
methods/ftp.php
CHANGED
@@ -22,9 +22,11 @@ class UpdraftPlus_BackupModule_ftp {
|
|
22 |
|
23 |
//$ftp->make_dir(); we may need to recursively create dirs? TODO
|
24 |
|
|
|
|
|
25 |
$ftp_remote_path = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_ftp_remote_path'));
|
26 |
foreach($backup_array as $file) {
|
27 |
-
$fullpath =
|
28 |
$updraftplus->log("FTP upload attempt: $file -> ftp://$user@$server/${ftp_remote_path}${file}");
|
29 |
$timer_start = microtime(true);
|
30 |
$size_k = round(filesize($fullpath)/1024,1);
|
@@ -67,7 +69,7 @@ class UpdraftPlus_BackupModule_ftp {
|
|
67 |
//$ftp->make_dir(); we may need to recursively create dirs? TODO
|
68 |
|
69 |
$ftp_remote_path = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_ftp_remote_path'));
|
70 |
-
$fullpath =
|
71 |
|
72 |
$ftp->get($fullpath, $ftp_remote_path.$file, FTP_BINARY);
|
73 |
}
|
22 |
|
23 |
//$ftp->make_dir(); we may need to recursively create dirs? TODO
|
24 |
|
25 |
+
$updraft_dir = $updraftplus->backups_dir_location().'/';
|
26 |
+
|
27 |
$ftp_remote_path = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_ftp_remote_path'));
|
28 |
foreach($backup_array as $file) {
|
29 |
+
$fullpath = $updraft_dir.$file;
|
30 |
$updraftplus->log("FTP upload attempt: $file -> ftp://$user@$server/${ftp_remote_path}${file}");
|
31 |
$timer_start = microtime(true);
|
32 |
$size_k = round(filesize($fullpath)/1024,1);
|
69 |
//$ftp->make_dir(); we may need to recursively create dirs? TODO
|
70 |
|
71 |
$ftp_remote_path = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_ftp_remote_path'));
|
72 |
+
$fullpath = $updraftplus->backups_dir_location().'/'.$file;
|
73 |
|
74 |
$ftp->get($fullpath, $ftp_remote_path.$file, FTP_BINARY);
|
75 |
}
|
methods/googledrive.php
CHANGED
@@ -119,8 +119,10 @@ class UpdraftPlus_BackupModule_googledrive {
|
|
119 |
|
120 |
$this->gdocs_access_token = $access_token;
|
121 |
|
|
|
|
|
122 |
foreach ($backup_array as $file) {
|
123 |
-
$file_path =
|
124 |
$file_name = basename($file_path);
|
125 |
$updraftplus->log("$file_name: Attempting to upload to Google Drive");
|
126 |
$timer_start = microtime(true);
|
@@ -201,7 +203,7 @@ class UpdraftPlus_BackupModule_googledrive {
|
|
201 |
$counter = 0;
|
202 |
do {
|
203 |
$log_string = ($counter == 0) ? "URL: $res" : "";
|
204 |
-
$updraftplus->record_uploaded_chunk($d, $log_string);
|
205 |
|
206 |
$counter++; if ($counter >= 20) $counter=0;
|
207 |
|
@@ -271,7 +273,7 @@ class UpdraftPlus_BackupModule_googledrive {
|
|
271 |
}
|
272 |
// Actually download the thing
|
273 |
|
274 |
-
$download_to =
|
275 |
$gdocs_object->download_data($content_link, $download_to, true);
|
276 |
|
277 |
if (filesize($download_to) > 0) {
|
119 |
|
120 |
$this->gdocs_access_token = $access_token;
|
121 |
|
122 |
+
$updraft_dir = $updraftplus->backups_dir_location().'/';
|
123 |
+
|
124 |
foreach ($backup_array as $file) {
|
125 |
+
$file_path = $updraft_dir.$file;
|
126 |
$file_name = basename($file_path);
|
127 |
$updraftplus->log("$file_name: Attempting to upload to Google Drive");
|
128 |
$timer_start = microtime(true);
|
203 |
$counter = 0;
|
204 |
do {
|
205 |
$log_string = ($counter == 0) ? "URL: $res" : "";
|
206 |
+
$updraftplus->record_uploaded_chunk($d, $log_string, $file);
|
207 |
|
208 |
$counter++; if ($counter >= 20) $counter=0;
|
209 |
|
273 |
}
|
274 |
// Actually download the thing
|
275 |
|
276 |
+
$download_to = $updraftplus->backups_dir_location().'/'.$file;
|
277 |
$gdocs_object->download_data($content_link, $download_to, true);
|
278 |
|
279 |
if (filesize($download_to) > 0) {
|
methods/s3.php
CHANGED
@@ -53,11 +53,13 @@ class UpdraftPlus_BackupModule_s3 {
|
|
53 |
if (empty($region)) $region = $s3->getBucketLocation($bucket_name);
|
54 |
$this->set_endpoint($s3, $region);
|
55 |
|
|
|
|
|
56 |
foreach($backup_array as $key => $file) {
|
57 |
|
58 |
// We upload in 5Mb chunks to allow more efficient resuming and hence uploading of larger files
|
59 |
// N.B.: 5Mb is Amazon's minimum. So don't go lower or you'll break it.
|
60 |
-
$fullpath =
|
61 |
$orig_file_size = filesize($fullpath);
|
62 |
$chunks = floor($orig_file_size / 5242880);
|
63 |
// There will be a remnant unless the file size was exactly on a 5Mb boundary
|
@@ -119,7 +121,7 @@ class UpdraftPlus_BackupModule_s3 {
|
|
119 |
}
|
120 |
$etag = $s3->uploadPart($bucket_name, $filepath, $uploadId, $fullpath, $i);
|
121 |
if ($etag !== false && is_string($etag)) {
|
122 |
-
$updraftplus->record_uploaded_chunk(round(100*$i/$chunks,1), "$i, $etag");
|
123 |
array_push($etags, $etag);
|
124 |
set_transient("upd_${hash}_e$i", $etag, UPDRAFT_TRANSTIME);
|
125 |
$successes++;
|
@@ -205,7 +207,7 @@ class UpdraftPlus_BackupModule_s3 {
|
|
205 |
$region = @$s3->getBucketLocation($bucket_name);
|
206 |
if (!empty($region)) {
|
207 |
$this->set_endpoint($s3, $region);
|
208 |
-
$fullpath =
|
209 |
if (!$s3->getObject($bucket_name, $bucket_path.$file, $fullpath, true)) {
|
210 |
$updraftplus->log("S3 Error: Failed to download $file. Check your permissions and credentials.");
|
211 |
$updraftplus->error("S3 Error: Failed to download $file. Check your permissions and credentials.");
|
53 |
if (empty($region)) $region = $s3->getBucketLocation($bucket_name);
|
54 |
$this->set_endpoint($s3, $region);
|
55 |
|
56 |
+
$updraft_dir = $updraftplus->backups_dir_location().'/';
|
57 |
+
|
58 |
foreach($backup_array as $key => $file) {
|
59 |
|
60 |
// We upload in 5Mb chunks to allow more efficient resuming and hence uploading of larger files
|
61 |
// N.B.: 5Mb is Amazon's minimum. So don't go lower or you'll break it.
|
62 |
+
$fullpath = $updraft_dir.$file;
|
63 |
$orig_file_size = filesize($fullpath);
|
64 |
$chunks = floor($orig_file_size / 5242880);
|
65 |
// There will be a remnant unless the file size was exactly on a 5Mb boundary
|
121 |
}
|
122 |
$etag = $s3->uploadPart($bucket_name, $filepath, $uploadId, $fullpath, $i);
|
123 |
if ($etag !== false && is_string($etag)) {
|
124 |
+
$updraftplus->record_uploaded_chunk(round(100*$i/$chunks,1), "$i, $etag", $fullpath);
|
125 |
array_push($etags, $etag);
|
126 |
set_transient("upd_${hash}_e$i", $etag, UPDRAFT_TRANSTIME);
|
127 |
$successes++;
|
207 |
$region = @$s3->getBucketLocation($bucket_name);
|
208 |
if (!empty($region)) {
|
209 |
$this->set_endpoint($s3, $region);
|
210 |
+
$fullpath = $updraftplus->backups_dir_location().'/'.$file;
|
211 |
if (!$s3->getObject($bucket_name, $bucket_path.$file, $fullpath, true)) {
|
212 |
$updraftplus->log("S3 Error: Failed to download $file. Check your permissions and credentials.");
|
213 |
$updraftplus->error("S3 Error: Failed to download $file. Check your permissions and credentials.");
|
options.php
CHANGED
@@ -60,7 +60,7 @@ class UpdraftPlus_Options {
|
|
60 |
register_setting('updraft-options-group', 'updraft_ftp_pass' );
|
61 |
register_setting('updraft-options-group', 'updraft_ftp_remote_path' );
|
62 |
register_setting('updraft-options-group', 'updraft_server_address' );
|
63 |
-
register_setting('updraft-options-group', 'updraft_dir' );
|
64 |
register_setting('updraft-options-group', 'updraft_email');
|
65 |
register_setting('updraft-options-group', 'updraft_delete_local', 'absint' );
|
66 |
register_setting('updraft-options-group', 'updraft_debug_mode', 'absint' );
|
60 |
register_setting('updraft-options-group', 'updraft_ftp_pass' );
|
61 |
register_setting('updraft-options-group', 'updraft_ftp_remote_path' );
|
62 |
register_setting('updraft-options-group', 'updraft_server_address' );
|
63 |
+
register_setting('updraft-options-group', 'updraft_dir', array($updraftplus, 'prune_updraft_dir_prefix') );
|
64 |
register_setting('updraft-options-group', 'updraft_email');
|
65 |
register_setting('updraft-options-group', 'updraft_delete_local', 'absint' );
|
66 |
register_setting('updraft-options-group', 'updraft_debug_mode', 'absint' );
|
readme.txt
CHANGED
@@ -3,13 +3,13 @@ Contributors: David Anderson
|
|
3 |
Tags: backup, restore, database, cloud, amazon, s3, dropbox, google drive, ftp, webdav, back up, multisite
|
4 |
Requires at least: 3.2
|
5 |
Tested up to: 3.5.1
|
6 |
-
Stable tag: 1.4.
|
7 |
Author URI: http://updraftplus.com
|
8 |
Donate link: http://david.dw-perspective.org.uk/donate
|
9 |
License: GPLv3 or later
|
10 |
|
11 |
== Upgrade Notice ==
|
12 |
-
|
13 |
|
14 |
== Description ==
|
15 |
|
@@ -25,8 +25,13 @@ Various tweaks and bug-fixes; plus, asynchronous downloading
|
|
25 |
* Database backups can be encrypted for security
|
26 |
* Debug mode that gives full logging of the backup
|
27 |
* Thousands of users: widely tested and reliable
|
|
|
28 |
* Premium/multi-site version and support available - <a href="http://updraftplus.com">http://updraftplus.com</a>
|
29 |
|
|
|
|
|
|
|
|
|
30 |
= Best New WordPress Plugin =
|
31 |
|
32 |
That's according to WordPress big cheese, Vladimir Prelovac. Check out his weekly chart to see where UpdraftPlus is right now: http://www.prelovac.com/vladimir/wordpress-plugins-rising-stars
|
@@ -41,6 +46,10 @@ If you need WordPress multisite compatibility (you'll know if you do), <a href="
|
|
41 |
|
42 |
UpdraftPlus is written by professional WordPress developers. If your site needs guaranteed support, then we are available. Just <a href="http://updraftplus.com/shop/">go to our shop.</a>
|
43 |
|
|
|
|
|
|
|
|
|
44 |
= Other support =
|
45 |
|
46 |
We hang out in the support forum for this plugin - http://wordpress.org/support/plugin/updraftplus - however, to save our time so that we can spend it on development, please read the plugin's Frequently Asked Questions - <a href="http://updraftplus.com/support/frequently-asked-questions/">http://updraftplus.com/support/frequently-asked-questions/</a> - before going there, and ensure that you have updated to the latest released version of UpdraftPlus.
|
@@ -101,6 +110,18 @@ Thanks for asking - yes, I have. Check out my profile page - http://profiles.wor
|
|
101 |
|
102 |
== Changelog ==
|
103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
= 1.4.30 - 03/04/2013 =
|
105 |
* Hooks for WebDAV support via add-on
|
106 |
|
3 |
Tags: backup, restore, database, cloud, amazon, s3, dropbox, google drive, ftp, webdav, back up, multisite
|
4 |
Requires at least: 3.2
|
5 |
Tested up to: 3.5.1
|
6 |
+
Stable tag: 1.4.48
|
7 |
Author URI: http://updraftplus.com
|
8 |
Donate link: http://david.dw-perspective.org.uk/donate
|
9 |
License: GPLv3 or later
|
10 |
|
11 |
== Upgrade Notice ==
|
12 |
+
Many tweaks + small bug-fixes: recommended upgraded for all
|
13 |
|
14 |
== Description ==
|
15 |
|
25 |
* Database backups can be encrypted for security
|
26 |
* Debug mode that gives full logging of the backup
|
27 |
* Thousands of users: widely tested and reliable
|
28 |
+
* Internationalised (translations very welcome - see below)
|
29 |
* Premium/multi-site version and support available - <a href="http://updraftplus.com">http://updraftplus.com</a>
|
30 |
|
31 |
+
= Don't Risk Anything Less =
|
32 |
+
|
33 |
+
Your backups are worth the same as your entire investment in your website. The day may come when you get hacked, or your hosting company does, or they go bust - without good backups, you lose everything. Do you really want to entrust all your work to plugins with only a few thousand downloads, or that has no professional backing or support? Believe us - writing a reliable backup plugin that works consistently across the huge range of WordPress deployments is hard.
|
34 |
+
|
35 |
= Best New WordPress Plugin =
|
36 |
|
37 |
That's according to WordPress big cheese, Vladimir Prelovac. Check out his weekly chart to see where UpdraftPlus is right now: http://www.prelovac.com/vladimir/wordpress-plugins-rising-stars
|
46 |
|
47 |
UpdraftPlus is written by professional WordPress developers. If your site needs guaranteed support, then we are available. Just <a href="http://updraftplus.com/shop/">go to our shop.</a>
|
48 |
|
49 |
+
= Are you multi-lingual? Can you translate? =
|
50 |
+
|
51 |
+
Are you able to translate UpdraftPlus into another language? UpdraftPlus's code is already internationalized. Just grab the .pot file from <a href="http://plugins.svn.wordpress.org/updraftplus/trunk/languages/updraftplus.pot">http://plugins.svn.wordpress.org/updraftplus/trunk/languages/updraftplus.pot</a>, load it into any application for translating, and sent it to us (contact@updraftplus.com) when done. We will add your name to the 'Notes' page here.
|
52 |
+
|
53 |
= Other support =
|
54 |
|
55 |
We hang out in the support forum for this plugin - http://wordpress.org/support/plugin/updraftplus - however, to save our time so that we can spend it on development, please read the plugin's Frequently Asked Questions - <a href="http://updraftplus.com/support/frequently-asked-questions/">http://updraftplus.com/support/frequently-asked-questions/</a> - before going there, and ensure that you have updated to the latest released version of UpdraftPlus.
|
110 |
|
111 |
== Changelog ==
|
112 |
|
113 |
+
= 1.4.48 - 03/11/2013 =
|
114 |
+
* Improve batching on zip creation for sites with very large files
|
115 |
+
* Unlimited early resumption if zip file creation takes too long
|
116 |
+
* Suppress some warning notices that can break JavaScript on sites with notices sent to the browser
|
117 |
+
* Earlier warning/failure if backup directory was not writable
|
118 |
+
* Hooks for Dropbox folders add-on
|
119 |
+
* More scheduler/overlap tweaks, to assist enormous uploads
|
120 |
+
* When the temporary directory is within the site, store+display relatively (removes need to modify upon site move)
|
121 |
+
* Sort existing backups display by date
|
122 |
+
* Use WordPress time for creation of filenames
|
123 |
+
* Fix bug in 1.4.47 which caused problems on new site installs
|
124 |
+
|
125 |
= 1.4.30 - 03/04/2013 =
|
126 |
* Hooks for WebDAV support via add-on
|
127 |
|
updraftplus.php
CHANGED
@@ -3,15 +3,17 @@
|
|
3 |
Plugin Name: UpdraftPlus - Backup/Restore
|
4 |
Plugin URI: http://updraftplus.com
|
5 |
Description: Backup and restore: your site can be backed up locally or to Amazon S3, Dropbox, Google Drive, (S)FTP, WebDAV & email, on automatic schedules.
|
6 |
-
Author:
|
7 |
-
Version: 1.4.
|
8 |
Donate link: http://david.dw-perspective.org.uk/donate
|
9 |
License: GPLv3 or later
|
10 |
Author URI: http://wordshell.net
|
11 |
*/
|
12 |
|
13 |
/*
|
14 |
-
TODO - some are out of date/done, needs pruning
|
|
|
|
|
15 |
//When a manual backup is run, use a timer to update the 'Download backups and logs' section, just like 'Last finished backup run'. Beware of over-writing anything that's in there from a resumable downloader.
|
16 |
//Change DB encryption to not require whole gzip in memory (twice)
|
17 |
//Add Rackspace, Box.Net, SugarSync and Microsoft Skydrive support??
|
@@ -25,12 +27,10 @@ TODO - some are out of date/done, needs pruning
|
|
25 |
// Resuming partial (S)FTP uploads
|
26 |
// Translations
|
27 |
// Make disk space check more intelligent (currently hard-coded at 35Mb)
|
28 |
-
// Specific folders on DropBox
|
29 |
// Provide backup/restoration for UpdraftPlus's settings, to allow 'bootstrap' on a fresh WP install - some kind of single-use code which a remote UpdraftPlus can use to authenticate
|
30 |
// Multiple jobs
|
31 |
// Multisite - a separate 'blogs' zip
|
32 |
// Allow connecting to remote storage, scanning + populating backup history from it
|
33 |
-
// Change FTP to use SSL by default
|
34 |
// GoogleDrive in-dashboard download resumption loads the whole archive into memory - should instead either chunk or directly stream fo the file handle
|
35 |
// Multisite add-on should allow restoring of each blog individually
|
36 |
// When looking for files to delete, is the current encryption setting used? Should not be.
|
@@ -88,7 +88,7 @@ if ($dir_handle = opendir(UPDRAFTPLUS_DIR.'/addons')) {
|
|
88 |
if (!isset($updraftplus)) $updraftplus = new UpdraftPlus();
|
89 |
|
90 |
if (!$updraftplus->memory_check(192)) {
|
91 |
-
|
92 |
@ini_set('memory_limit', '192M'); //up the memory limit for large backup files
|
93 |
}
|
94 |
|
@@ -146,7 +146,7 @@ class UpdraftPlus {
|
|
146 |
if (preg_match("/Version: ([\d\.]+)(\r|\n)/", $file_data, $matches)) {
|
147 |
$this->version = $matches[1];
|
148 |
}
|
149 |
-
fclose(
|
150 |
}
|
151 |
|
152 |
# Create admin page
|
@@ -164,7 +164,7 @@ class UpdraftPlus {
|
|
164 |
add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2);
|
165 |
add_action('init', array($this, 'handle_url_actions'));
|
166 |
|
167 |
-
if (defined('UPDRAFTPLUS_PREFERPCLZIP') && UPDRAFTPLUS_PREFERPCLZIP
|
168 |
|
169 |
}
|
170 |
|
@@ -213,8 +213,8 @@ class UpdraftPlus {
|
|
213 |
if ($file == plugin_basename(__FILE__)){
|
214 |
$settings_link = '<a href="'.site_url().'/wp-admin/options-general.php?page=updraftplus">'.__("Settings", "UpdraftPlus").'</a>';
|
215 |
array_unshift($links, $settings_link);
|
216 |
-
|
217 |
-
|
218 |
$settings_link = '<a href="http://updraftplus.com">'.__("Add-Ons / Pro Support","UpdraftPlus").'</a>';
|
219 |
array_unshift($links, $settings_link);
|
220 |
}
|
@@ -228,15 +228,18 @@ class UpdraftPlus {
|
|
228 |
}
|
229 |
|
230 |
function logfile_open($nonce) {
|
|
|
231 |
//set log file name and open log file
|
232 |
$updraft_dir = $this->backups_dir_location();
|
233 |
$this->logfile_name = $updraft_dir. "/log.$nonce.txt";
|
|
|
234 |
// Use append mode in case it already exists
|
235 |
$this->logfile_handle = fopen($this->logfile_name, 'a');
|
236 |
$this->opened_log_time = microtime(true);
|
237 |
$this->log("Opened log file at time: ".date('r'));
|
238 |
global $wp_version;
|
239 |
-
$logline = "UpdraftPlus: ".$this->version."
|
|
|
240 |
// method_exists causes some faulty PHP installations to segfault, leading to support requests
|
241 |
if (version_compare(phpversion(), '5.2.0', '>=') && extension_loaded('zip')) {
|
242 |
$logline .= 'Y';
|
@@ -250,7 +253,7 @@ class UpdraftPlus {
|
|
250 |
|
251 |
# Logs the given line, adding (relative) time stamp and newline
|
252 |
function log($line) {
|
253 |
-
if ($this->logfile_handle) fwrite($this->logfile_handle, sprintf("%08.03f", round(microtime(true)-$this->opened_log_time, 3))." ".$
|
254 |
if ('download' == $this->jobdata_get('job_type')) {
|
255 |
// Download messages are keyed on the job (since they could be running several), and transient
|
256 |
// The values of the POST array were checked before
|
@@ -258,11 +261,15 @@ class UpdraftPlus {
|
|
258 |
} else {
|
259 |
UpdraftPlus_Options::update_updraft_option('updraft_lastmessage', $line." (".date('M d H:i:s').")");
|
260 |
}
|
261 |
-
if (
|
262 |
}
|
263 |
|
264 |
// This function is used by cloud methods to provide standardised logging, but more importantly to help us detect that meaningful activity took place during a resumption run, so that we can schedule further resumptions if it is worthwhile
|
265 |
-
function record_uploaded_chunk($percent, $extra) {
|
|
|
|
|
|
|
|
|
266 |
// Log it
|
267 |
$service = $this->jobdata_get('service');
|
268 |
$log = ucfirst($service)." chunked upload: $percent % uploaded";
|
@@ -274,19 +281,17 @@ class UpdraftPlus {
|
|
274 |
// If they get 2 minutes on each run, and the file is 1Gb, then that equals 10.2Mb/120s = minimum 87Kb/s upload speed required
|
275 |
|
276 |
if ($this->current_resumption >= 9 && $this->newresumption_scheduled == false && $percent > ( $this->current_resumption - 9)) {
|
277 |
-
$
|
278 |
-
if (!is_numeric($resume_interval) || $resume_interval<$this->minimum_resume_interval()) { $resume_interval = $this->minimum_resume_interval(); }
|
279 |
-
$schedule_for = time()+$resume_interval;
|
280 |
-
$this->newresumption_scheduled = $schedule_for;
|
281 |
-
$this->log("This is resumption ".$this->current_resumption.", but meaningful uploading is still taking place; so a new one will be scheduled");
|
282 |
-
wp_schedule_single_event($schedule_for, 'updraft_backup_resume', array($this->current_resumption + 1, $this->nonce));
|
283 |
}
|
284 |
}
|
285 |
|
286 |
function minimum_resume_interval() {
|
287 |
-
|
288 |
-
|
289 |
-
|
|
|
|
|
|
|
290 |
}
|
291 |
|
292 |
function backup_resume($resumption_no, $bnonce) {
|
@@ -307,8 +312,10 @@ class UpdraftPlus {
|
|
307 |
|
308 |
$updraft_dir = $this->backups_dir_location();
|
309 |
|
310 |
-
$
|
|
|
311 |
$this->current_resumption = $resumption_no;
|
|
|
312 |
|
313 |
// Schedule again, to run in 5 minutes again, in case we again fail
|
314 |
// The actual interval can be increased (for future resumptions) by other code, if it detects apparent overlapping
|
@@ -318,14 +325,20 @@ class UpdraftPlus {
|
|
318 |
// A different argument than before is needed otherwise the event is ignored
|
319 |
$next_resumption = $resumption_no+1;
|
320 |
if ($next_resumption < 10) {
|
321 |
-
$this->log("Scheduling a resumption ($next_resumption) after $resume_interval seconds in case this run gets aborted");
|
322 |
$schedule_for = time()+$resume_interval;
|
|
|
323 |
wp_schedule_single_event($schedule_for, 'updraft_backup_resume', array($next_resumption, $bnonce));
|
324 |
$this->newresumption_scheduled = $schedule_for;
|
325 |
} else {
|
326 |
$this->log("The current run is our tenth attempt - will not schedule a further attempt until we see something useful happening");
|
327 |
}
|
328 |
|
|
|
|
|
|
|
|
|
|
|
|
|
329 |
// This should be always called; if there were no files in this run, it returns us an empty array
|
330 |
$backup_array = $this->resumable_backup_of_files($resumption_no);
|
331 |
|
@@ -444,14 +457,15 @@ class UpdraftPlus {
|
|
444 |
$value = func_get_arg($i*2-1);
|
445 |
$this->jobdata[$key] = $value;
|
446 |
}
|
447 |
-
if ($this->nonce) set_transient("updraft_jobdata_".$this->nonce, $this->jobdata, UPDRAFT_TRANSTIME);
|
448 |
}
|
449 |
|
450 |
function jobdata_set($key, $value) {
|
451 |
if (is_array($this->jobdata)) {
|
452 |
$this->jobdata[$key] = $value;
|
453 |
} else {
|
454 |
-
$this->jobdata =
|
|
|
455 |
}
|
456 |
set_transient("updraft_jobdata_".$this->nonce, $this->jobdata, 14400);
|
457 |
}
|
@@ -498,6 +512,12 @@ class UpdraftPlus {
|
|
498 |
$this->backup_time_nonce();
|
499 |
$this->logfile_open($this->nonce);
|
500 |
|
|
|
|
|
|
|
|
|
|
|
|
|
501 |
// Some house-cleaning
|
502 |
$this->clean_temporary_files();
|
503 |
|
@@ -519,8 +539,8 @@ class UpdraftPlus {
|
|
519 |
}
|
520 |
|
521 |
$resume_interval = $this->minimum_resume_interval();
|
522 |
-
|
523 |
-
|
524 |
|
525 |
$initial_jobdata = array(
|
526 |
'resume_interval', $resume_interval,
|
@@ -536,7 +556,7 @@ class UpdraftPlus {
|
|
536 |
// Use of jobdata_set_multi saves around 200ms
|
537 |
call_user_func_array(array($this, 'jobdata_set_multi'), $initial_jobdata);
|
538 |
|
539 |
-
//
|
540 |
$this->backup_resume(0, $this->nonce);
|
541 |
|
542 |
}
|
@@ -817,22 +837,40 @@ class UpdraftPlus {
|
|
817 |
return true;
|
818 |
}
|
819 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
820 |
function reschedule($how_far_ahead) {
|
821 |
// Reschedule - remove presently scheduled event
|
822 |
-
|
|
|
823 |
// Add new event
|
824 |
if ($how_far_ahead < $this->minimum_resume_interval()) $how_far_ahead=$this->minimum_resume_interval();
|
825 |
$schedule_for = time() + $how_far_ahead;
|
826 |
-
|
|
|
827 |
$this->newresumption_scheduled = $schedule_for;
|
828 |
}
|
829 |
|
830 |
function increase_resume_and_reschedule($howmuch = 120) {
|
831 |
$resume_interval = $this->jobdata_get('resume_interval');
|
832 |
-
if (!is_numeric($resume_interval) || $resume_interval
|
833 |
-
if ($this->newresumption_scheduled
|
834 |
$this->jobdata_set('resume_interval', $resume_interval+$howmuch);
|
835 |
-
$this->log("To decrease the likelihood of overlaps, increasing resumption interval to: ".($resume_interval+$howmuch));
|
836 |
}
|
837 |
|
838 |
function create_zip($create_from_dir, $whichone, $create_in_dir, $backup_file_basename) {
|
@@ -842,9 +880,16 @@ class UpdraftPlus {
|
|
842 |
if ($whichone != "others") $this->log("Beginning creation of dump of $whichone");
|
843 |
|
844 |
$full_path = $create_in_dir.'/'.$backup_file_basename.'-'.$whichone.'.zip';
|
|
|
845 |
|
846 |
if (file_exists($full_path)) {
|
847 |
$this->log("$backup_file_basename-$whichone.zip: this file has already been created");
|
|
|
|
|
|
|
|
|
|
|
|
|
848 |
return basename($full_path);
|
849 |
}
|
850 |
|
@@ -852,7 +897,6 @@ class UpdraftPlus {
|
|
852 |
|
853 |
// Firstly, make sure that the temporary file is not already being written to - which can happen if a resumption takes place whilst an old run is still active
|
854 |
$zip_name = $full_path.'.tmp';
|
855 |
-
$time_now = time();
|
856 |
$time_mod = (int)@filemtime($zip_name);
|
857 |
if (file_exists($zip_name) && $time_mod>100 && ($time_now-$time_mod)<30) {
|
858 |
$file_size = filesize($zip_name);
|
@@ -881,6 +925,19 @@ class UpdraftPlus {
|
|
881 |
return basename($full_path);
|
882 |
}
|
883 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
884 |
// This function is resumable
|
885 |
function backup_dirs($transient_status) {
|
886 |
|
@@ -898,7 +955,7 @@ class UpdraftPlus {
|
|
898 |
$blog_name = preg_replace('/[^A-Za-z0-9_]/','', $blog_name);
|
899 |
if(!$blog_name) $blog_name = 'non_alpha_name';
|
900 |
|
901 |
-
$backup_file_basename = 'backup_'.
|
902 |
|
903 |
$backup_array = array();
|
904 |
|
@@ -911,10 +968,16 @@ class UpdraftPlus {
|
|
911 |
|
912 |
# Plugins, themes, uploads
|
913 |
foreach ($possible_backups as $youwhat => $whichdir) {
|
|
|
914 |
if (UpdraftPlus_Options::get_updraft_option("updraft_include_$youwhat", true)) {
|
|
|
|
|
|
|
|
|
|
|
915 |
if ($transient_status == 'finished') {
|
916 |
$backup_array[$youwhat] = $backup_file_basename.'-'.$youwhat.'.zip';
|
917 |
-
if (file_exists($
|
918 |
} else {
|
919 |
$created = $this->create_zip($whichdir, $youwhat, $updraft_dir, $backup_file_basename);
|
920 |
if ($created) {
|
@@ -930,9 +993,12 @@ class UpdraftPlus {
|
|
930 |
# Others
|
931 |
if (UpdraftPlus_Options::get_updraft_option('updraft_include_others', true)) {
|
932 |
|
|
|
|
|
|
|
933 |
if ($transient_status == 'finished') {
|
934 |
$backup_array['others'] = $backup_file_basename.'-others.zip';
|
935 |
-
if (file_exists($
|
936 |
} else {
|
937 |
$this->log("Beginning backup of other directories found in the content directory");
|
938 |
|
@@ -1065,7 +1131,8 @@ class UpdraftPlus {
|
|
1065 |
// Get the blog name and rip out all non-alphanumeric chars other than _
|
1066 |
$blog_name = preg_replace('/[^A-Za-z0-9_]/','', str_replace(' ','_', substr(get_bloginfo(), 0, 96)));
|
1067 |
if (!$blog_name) $blog_name = 'non_alpha_name';
|
1068 |
-
|
|
|
1069 |
$backup_file_base = $updraft_dir.'/'.$file_base;
|
1070 |
|
1071 |
if ('finished' == $already_done) return basename($backup_file_base.'-db.gz');
|
@@ -1406,19 +1473,40 @@ class UpdraftPlus {
|
|
1406 |
return $schedules;
|
1407 |
}
|
1408 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1409 |
function backups_dir_location() {
|
|
|
1410 |
if (!empty($this->backup_dir)) return $this->backup_dir;
|
|
|
1411 |
$updraft_dir = untrailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir'));
|
1412 |
$default_backup_dir = WP_CONTENT_DIR.'/updraft';
|
1413 |
-
//if the option isn't set, default it to /backups inside the upload dir
|
1414 |
$updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1415 |
//check for the existence of the dir and an enumeration preventer.
|
1416 |
-
|
|
|
1417 |
@mkdir($updraft_dir, 0775, true);
|
1418 |
-
@file_put_contents($updraft_dir.'/index.html',
|
1419 |
-
@file_put_contents($updraft_dir.'/.htaccess','deny from all');
|
1420 |
}
|
|
|
1421 |
$this->backup_dir = $updraft_dir;
|
|
|
1422 |
return $updraft_dir;
|
1423 |
}
|
1424 |
|
@@ -1834,7 +1922,7 @@ class UpdraftPlus {
|
|
1834 |
return $this->url_start($urls,'wordshell.net')."Check out WordShell".$this->url_end($urls,'www.wordshell.net')." - manage WordPress from the command line - huge time-saver";
|
1835 |
break;
|
1836 |
case 3:
|
1837 |
-
return "
|
1838 |
break;
|
1839 |
case 4:
|
1840 |
return $this->url_start($urls,'www.simbahosting.co.uk')."Need high-quality WordPress hosting from WordPress specialists? (Including automatic backups and 1-click installer). Get it from the creators of UpdraftPlus.".$this->url_end($urls,'www.simbahosting.co.uk');
|
@@ -2027,7 +2115,7 @@ class UpdraftPlus {
|
|
2027 |
jQuery('.expertmode').fadeIn();
|
2028 |
return false;
|
2029 |
});
|
2030 |
-
<?php if (
|
2031 |
setTimeout(function(){updraft_showlastlog();}, 1200);
|
2032 |
jQuery('.updraftplusmethod').hide();
|
2033 |
<?php
|
@@ -2064,15 +2152,16 @@ class UpdraftPlus {
|
|
2064 |
|
2065 |
<tr class="expertmode backupdirrow" style="display:none;">
|
2066 |
<th>Backup directory:</th>
|
2067 |
-
<td><input type="text" name="updraft_dir" style="width:525px" value="<?php echo htmlspecialchars($updraft_dir); ?>" /></td>
|
2068 |
</tr>
|
2069 |
<tr class="expertmode backupdirrow" style="display:none;">
|
2070 |
<td></td><td><?php
|
2071 |
|
2072 |
-
if
|
|
|
2073 |
$dir_info = '<span style="color:green">Backup directory specified is writable, which is good.</span>';
|
2074 |
} else {
|
2075 |
-
$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>';
|
2076 |
}
|
2077 |
|
2078 |
echo $dir_info ?> This is where UpdraftPlus will write the zip files it creates initially. This directory must be writable by your web server. Typically you'll want to have it inside your wp-content folder (this is the default). <b>Do not</b> place it inside your uploads dir, as that will cause recursion issues (backups of backups of backups of...).</td>
|
@@ -2206,7 +2295,7 @@ class UpdraftPlus {
|
|
2206 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') { $this->boot_backup(true,true); }
|
2207 |
elseif (isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_db') { $this->backup_db(); }
|
2208 |
elseif (isset($_POST['action']) && $_POST['action'] == 'updraft_wipesettings') {
|
2209 |
-
$settings = array('updraft_interval', 'updraft_interval_database', 'updraft_retain', 'updraft_retain_db', 'updraft_encryptionphrase', 'updraft_service', 'updraft_dropbox_appkey', 'updraft_dropbox_secret', '
|
2210 |
foreach ($settings as $s) {
|
2211 |
UpdraftPlus_Options::delete_updraft_option($s);
|
2212 |
}
|
@@ -2233,8 +2322,8 @@ class UpdraftPlus {
|
|
2233 |
<div style="color:orange">Your PHP memory limit is quite low. UpdraftPlus attempted to raise it but was unsuccessful. This plugin may not work properly with a memory limit of less than 96 Mb (though on the other hand, it has been used successfully with a 32Mb limit - your mileage may vary, but don't blame us!). Current limit is: <?php echo $this->memory_check_current(); ?> Mb</div>
|
2234 |
<?php
|
2235 |
}
|
2236 |
-
if(!$this->execution_time_check(60)) {?>
|
2237 |
-
<div style="color:orange">Your PHP max_execution_time is less than 60 seconds. This possibly means you're running in safe_mode. Either disable safe_mode or modify your php.ini to set max_execution_time to a higher number. If you do not, then longer will be needed to complete a backup. Present limit is: <?php echo ini_get('max_execution_time'); ?> seconds.</div>
|
2238 |
<?php
|
2239 |
}
|
2240 |
|
@@ -2356,7 +2445,7 @@ class UpdraftPlus {
|
|
2356 |
</tr>
|
2357 |
<tr>
|
2358 |
<td></td><td class="download-backups" style="display:none">
|
2359 |
-
<p><em><strong>Note</strong> - Pressing a button will make UpdraftPlus try to bring a backup file back from the remote storage (if any - e.g. Amazon S3, Dropbox, Google Drive, FTP) to your webserver, before then allowing you to download it to your computer. If the fetch from the remote storage stops progressing (wait 30 seconds to make sure), then click again to resume from where it left off. Remember that you can always visit the cloud storage website vendor's website directly.</em></p>
|
2360 |
<div id="ud_downloadstatus"></div>
|
2361 |
<script>
|
2362 |
var lastlog_lastmessage = "";
|
@@ -2498,6 +2587,9 @@ class UpdraftPlus {
|
|
2498 |
$updraft_dir = $this->backups_dir_location();
|
2499 |
|
2500 |
echo '<table>';
|
|
|
|
|
|
|
2501 |
foreach($backup_history as $key=>$value) {
|
2502 |
?>
|
2503 |
<tr>
|
@@ -2604,7 +2696,7 @@ class UpdraftPlus {
|
|
2604 |
// - No zip extension present and no relevant method present
|
2605 |
// The zip extension check is not redundant, because method_exists segfaults some PHP installs, leading to support requests
|
2606 |
|
2607 |
-
// Fallback to PclZip - which my tests show is 25% slower
|
2608 |
if ($this->zip_preferpcl || (!extension_loaded('zip') && !method_exists('ZipArchive', 'AddFile'))) {
|
2609 |
if(!class_exists('PclZip')) require_once(ABSPATH.'/wp-admin/includes/class-pclzip.php');
|
2610 |
$zip_object = new PclZip($destination);
|
@@ -2639,7 +2731,8 @@ class UpdraftPlus {
|
|
2639 |
$this->zipfiles_dirbatched = array();
|
2640 |
$this->zipfiles_batched = array();
|
2641 |
|
2642 |
-
|
|
|
2643 |
if (is_array($source)) {
|
2644 |
foreach ($source as $element) {
|
2645 |
$howmany = $this->makezip_recursive_add($destination, $element, basename($element), $element);
|
@@ -2662,8 +2755,8 @@ class UpdraftPlus {
|
|
2662 |
}
|
2663 |
}
|
2664 |
|
2665 |
-
if ($this->zipfiles_added > 0) {
|
2666 |
-
// ZipArchive::addFile sometimes fails
|
2667 |
if (filesize($destination) < 100) {
|
2668 |
// Retry with PclZip
|
2669 |
$this->log("Zip::addFile apparently failed - retrying with PclZip");
|
@@ -2682,27 +2775,75 @@ class UpdraftPlus {
|
|
2682 |
|
2683 |
// We batch up the files, rather than do them one at a time. So we are more efficient than open,one-write,close.
|
2684 |
function makezip_addfiles($zipfile) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2685 |
$zip = new ZipArchive();
|
2686 |
if (file_exists($zipfile)) {
|
2687 |
$opencode = $zip->open($zipfile);
|
|
|
2688 |
} else {
|
2689 |
$opencode = $zip->open($zipfile, ZIPARCHIVE::CREATE);
|
|
|
2690 |
}
|
|
|
2691 |
if ($opencode !== true) return array($opencode, 0);
|
2692 |
// Make sure all directories are created before we start creating files
|
2693 |
while ($dir = array_pop($this->zipfiles_dirbatched)) {
|
2694 |
$zip->addEmptyDir($dir);
|
2695 |
}
|
2696 |
foreach ($this->zipfiles_batched as $file => $add_as) {
|
2697 |
-
|
|
|
|
|
|
|
2698 |
$zip->addFile($file, $add_as);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2699 |
}
|
2700 |
$this->zipfiles_added++;
|
2701 |
-
if ($this->zipfiles_added % 100 == 0) $this->log("Zip: ".basename($zipfile).": ".$this->zipfiles_added." files added (size: ".round(filesize($zipfile)/1024,1)." Kb)");
|
2702 |
}
|
2703 |
// Reset the array
|
2704 |
$this->zipfiles_batched = array();
|
2705 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2706 |
}
|
2707 |
|
2708 |
// This function recursively packs the zip, dereferencing symlinks but packing into a single-parent tree for universal unpacking
|
@@ -2720,8 +2861,8 @@ class UpdraftPlus {
|
|
2720 |
|
2721 |
if(is_file($fullpath)) {
|
2722 |
if (is_readable($fullpath)) {
|
2723 |
-
$key = $use_path_when_storing.'/'.basename($fullpath);
|
2724 |
-
$this->zipfiles_batched[$fullpath] = $
|
2725 |
@touch($zipfile);
|
2726 |
} else {
|
2727 |
$this->log("$fullpath: unreadable file");
|
3 |
Plugin Name: UpdraftPlus - Backup/Restore
|
4 |
Plugin URI: http://updraftplus.com
|
5 |
Description: Backup and restore: your site can be backed up locally or to Amazon S3, Dropbox, Google Drive, (S)FTP, WebDAV & email, on automatic schedules.
|
6 |
+
Author: UpdraftPlus.Com, DavidAnderson
|
7 |
+
Version: 1.4.48
|
8 |
Donate link: http://david.dw-perspective.org.uk/donate
|
9 |
License: GPLv3 or later
|
10 |
Author URI: http://wordshell.net
|
11 |
*/
|
12 |
|
13 |
/*
|
14 |
+
TODO - some of these are out of date/done, needs pruning
|
15 |
+
//Allow use of /usr/bin/zip - since this can escape from PHP's memory limit. Can still batch as we do so, in order to monitor/measure progress
|
16 |
+
//Do an automated test periodically for the success of loop-back connections
|
17 |
//When a manual backup is run, use a timer to update the 'Download backups and logs' section, just like 'Last finished backup run'. Beware of over-writing anything that's in there from a resumable downloader.
|
18 |
//Change DB encryption to not require whole gzip in memory (twice)
|
19 |
//Add Rackspace, Box.Net, SugarSync and Microsoft Skydrive support??
|
27 |
// Resuming partial (S)FTP uploads
|
28 |
// Translations
|
29 |
// Make disk space check more intelligent (currently hard-coded at 35Mb)
|
|
|
30 |
// Provide backup/restoration for UpdraftPlus's settings, to allow 'bootstrap' on a fresh WP install - some kind of single-use code which a remote UpdraftPlus can use to authenticate
|
31 |
// Multiple jobs
|
32 |
// Multisite - a separate 'blogs' zip
|
33 |
// Allow connecting to remote storage, scanning + populating backup history from it
|
|
|
34 |
// GoogleDrive in-dashboard download resumption loads the whole archive into memory - should instead either chunk or directly stream fo the file handle
|
35 |
// Multisite add-on should allow restoring of each blog individually
|
36 |
// When looking for files to delete, is the current encryption setting used? Should not be.
|
88 |
if (!isset($updraftplus)) $updraftplus = new UpdraftPlus();
|
89 |
|
90 |
if (!$updraftplus->memory_check(192)) {
|
91 |
+
// Experience appears to show that the memory limit is only likely to be hit (unless it is very low) by single files that are larger than available memory (when compressed)
|
92 |
@ini_set('memory_limit', '192M'); //up the memory limit for large backup files
|
93 |
}
|
94 |
|
146 |
if (preg_match("/Version: ([\d\.]+)(\r|\n)/", $file_data, $matches)) {
|
147 |
$this->version = $matches[1];
|
148 |
}
|
149 |
+
fclose($fp);
|
150 |
}
|
151 |
|
152 |
# Create admin page
|
164 |
add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2);
|
165 |
add_action('init', array($this, 'handle_url_actions'));
|
166 |
|
167 |
+
if (defined('UPDRAFTPLUS_PREFERPCLZIP') && UPDRAFTPLUS_PREFERPCLZIP == true) { $this->zip_preferpcl = true; }
|
168 |
|
169 |
}
|
170 |
|
213 |
if ($file == plugin_basename(__FILE__)){
|
214 |
$settings_link = '<a href="'.site_url().'/wp-admin/options-general.php?page=updraftplus">'.__("Settings", "UpdraftPlus").'</a>';
|
215 |
array_unshift($links, $settings_link);
|
216 |
+
// $settings_link = '<a href="http://david.dw-perspective.org.uk/donate">'.__("Donate","UpdraftPlus").'</a>';
|
217 |
+
// array_unshift($links, $settings_link);
|
218 |
$settings_link = '<a href="http://updraftplus.com">'.__("Add-Ons / Pro Support","UpdraftPlus").'</a>';
|
219 |
array_unshift($links, $settings_link);
|
220 |
}
|
228 |
}
|
229 |
|
230 |
function logfile_open($nonce) {
|
231 |
+
|
232 |
//set log file name and open log file
|
233 |
$updraft_dir = $this->backups_dir_location();
|
234 |
$this->logfile_name = $updraft_dir. "/log.$nonce.txt";
|
235 |
+
|
236 |
// Use append mode in case it already exists
|
237 |
$this->logfile_handle = fopen($this->logfile_name, 'a');
|
238 |
$this->opened_log_time = microtime(true);
|
239 |
$this->log("Opened log file at time: ".date('r'));
|
240 |
global $wp_version;
|
241 |
+
$logline = "UpdraftPlus: ".$this->version." WP: ".$wp_version." PHP: ".phpversion()." (".php_uname().") max_execution_time: ".@ini_get("max_execution_time")." memory_limit: ".ini_get('memory_limit')." ZipArchive::addFile : ";
|
242 |
+
|
243 |
// method_exists causes some faulty PHP installations to segfault, leading to support requests
|
244 |
if (version_compare(phpversion(), '5.2.0', '>=') && extension_loaded('zip')) {
|
245 |
$logline .= 'Y';
|
253 |
|
254 |
# Logs the given line, adding (relative) time stamp and newline
|
255 |
function log($line) {
|
256 |
+
if ($this->logfile_handle) fwrite($this->logfile_handle, sprintf("%08.03f", round(microtime(true)-$this->opened_log_time, 3))." (".$this->current_resumption.") $line\n");
|
257 |
if ('download' == $this->jobdata_get('job_type')) {
|
258 |
// Download messages are keyed on the job (since they could be running several), and transient
|
259 |
// The values of the POST array were checked before
|
261 |
} else {
|
262 |
UpdraftPlus_Options::update_updraft_option('updraft_lastmessage', $line." (".date('M d H:i:s').")");
|
263 |
}
|
264 |
+
if (defined('UPDRAFTPLUS_CONSOLELOG')) print $line."\n";
|
265 |
}
|
266 |
|
267 |
// This function is used by cloud methods to provide standardised logging, but more importantly to help us detect that meaningful activity took place during a resumption run, so that we can schedule further resumptions if it is worthwhile
|
268 |
+
function record_uploaded_chunk($percent, $extra, $file_path = false) {
|
269 |
+
|
270 |
+
// Touch the original file, which helps prevent overlapping runs
|
271 |
+
if ($file_path) touch($file_path);
|
272 |
+
|
273 |
// Log it
|
274 |
$service = $this->jobdata_get('service');
|
275 |
$log = ucfirst($service)." chunked upload: $percent % uploaded";
|
281 |
// If they get 2 minutes on each run, and the file is 1Gb, then that equals 10.2Mb/120s = minimum 87Kb/s upload speed required
|
282 |
|
283 |
if ($this->current_resumption >= 9 && $this->newresumption_scheduled == false && $percent > ( $this->current_resumption - 9)) {
|
284 |
+
$this->something_useful_happened();
|
|
|
|
|
|
|
|
|
|
|
285 |
}
|
286 |
}
|
287 |
|
288 |
function minimum_resume_interval() {
|
289 |
+
// Bringing this down brings in more risk of undetectable overlaps than is worth it
|
290 |
+
return 300;
|
291 |
+
// $inter = (int)ini_get('max_execution_time');
|
292 |
+
// if (!$inter || $inter>300) $inter = 300;
|
293 |
+
// if ($inter<35) $inter=35;
|
294 |
+
// return $inter;
|
295 |
}
|
296 |
|
297 |
function backup_resume($resumption_no, $bnonce) {
|
312 |
|
313 |
$updraft_dir = $this->backups_dir_location();
|
314 |
|
315 |
+
$time_ago = time()-$btime;
|
316 |
+
|
317 |
$this->current_resumption = $resumption_no;
|
318 |
+
$this->log("Backup run: resumption=$resumption_no, nonce=$bnonce, begun at=$btime (${time_ago}s ago), job type: $job_type");
|
319 |
|
320 |
// Schedule again, to run in 5 minutes again, in case we again fail
|
321 |
// The actual interval can be increased (for future resumptions) by other code, if it detects apparent overlapping
|
325 |
// A different argument than before is needed otherwise the event is ignored
|
326 |
$next_resumption = $resumption_no+1;
|
327 |
if ($next_resumption < 10) {
|
|
|
328 |
$schedule_for = time()+$resume_interval;
|
329 |
+
$this->log("Scheduling a resumption ($next_resumption) after $resume_interval seconds ($schedule_for) in case this run gets aborted");
|
330 |
wp_schedule_single_event($schedule_for, 'updraft_backup_resume', array($next_resumption, $bnonce));
|
331 |
$this->newresumption_scheduled = $schedule_for;
|
332 |
} else {
|
333 |
$this->log("The current run is our tenth attempt - will not schedule a further attempt until we see something useful happening");
|
334 |
}
|
335 |
|
336 |
+
// Sanity check
|
337 |
+
if (empty($this->backup_time)) {
|
338 |
+
$this->log('Abort this run: the backup_time parameter appears to be empty');
|
339 |
+
return false;
|
340 |
+
}
|
341 |
+
|
342 |
// This should be always called; if there were no files in this run, it returns us an empty array
|
343 |
$backup_array = $this->resumable_backup_of_files($resumption_no);
|
344 |
|
457 |
$value = func_get_arg($i*2-1);
|
458 |
$this->jobdata[$key] = $value;
|
459 |
}
|
460 |
+
if (!empty($this->nonce)) set_transient("updraft_jobdata_".$this->nonce, $this->jobdata, UPDRAFT_TRANSTIME);
|
461 |
}
|
462 |
|
463 |
function jobdata_set($key, $value) {
|
464 |
if (is_array($this->jobdata)) {
|
465 |
$this->jobdata[$key] = $value;
|
466 |
} else {
|
467 |
+
$this->jobdata = get_transient("updraft_jobdata_".$this->nonce);
|
468 |
+
if (!is_array($this->jobdata)) $this->jobdata = array($key => $value);
|
469 |
}
|
470 |
set_transient("updraft_jobdata_".$this->nonce, $this->jobdata, 14400);
|
471 |
}
|
512 |
$this->backup_time_nonce();
|
513 |
$this->logfile_open($this->nonce);
|
514 |
|
515 |
+
if (!is_file($this->logfile_name)) {
|
516 |
+
$this->log('Failed to open log file ('.$this->logfile_name.') - you need to check your UpdraftPlus settings (your chosen directory for creating files in is not writable, or you ran out of disk space). Backup aborted.');
|
517 |
+
$this->error('Could not create files in the backup directory. Backup aborted - check your UpdraftPlus settings.');
|
518 |
+
return false;
|
519 |
+
}
|
520 |
+
|
521 |
// Some house-cleaning
|
522 |
$this->clean_temporary_files();
|
523 |
|
539 |
}
|
540 |
|
541 |
$resume_interval = $this->minimum_resume_interval();
|
542 |
+
// $max_execution_time = ini_get('max_execution_time');
|
543 |
+
// if ($max_execution_time >0 && $max_execution_time<300 && $resume_interval< $max_execution_time + 30) $resume_interval = $max_execution_time + 30;
|
544 |
|
545 |
$initial_jobdata = array(
|
546 |
'resume_interval', $resume_interval,
|
556 |
// Use of jobdata_set_multi saves around 200ms
|
557 |
call_user_func_array(array($this, 'jobdata_set_multi'), $initial_jobdata);
|
558 |
|
559 |
+
// Everything is now set up; now go
|
560 |
$this->backup_resume(0, $this->nonce);
|
561 |
|
562 |
}
|
837 |
return true;
|
838 |
}
|
839 |
|
840 |
+
// This function is not needed for backup success, according to the design, but it helps with efficient scheduling
|
841 |
+
function reschedule_if_needed() {
|
842 |
+
// If nothing is scheduled, then return
|
843 |
+
if (empty($this->newresumption_scheduled)) return;
|
844 |
+
$time_now = time();
|
845 |
+
$time_away = $this->newresumption_scheduled - $time_now;
|
846 |
+
// 30 is chosen because it is also used to detect recent activity on files (file mod times)
|
847 |
+
if ($time_away >1 && $time_away <= 30) {
|
848 |
+
$this->log('The scheduled resumption is within 30 seconds - will reschedule');
|
849 |
+
// Push 30 seconds into the future
|
850 |
+
// $this->reschedule(60);
|
851 |
+
// Increase interval generally by 30 seconds, on the assumption that our prior estimates were innaccurate (i.e. not just 30 seconds *this* time)
|
852 |
+
$this->increase_resume_and_reschedule(30);
|
853 |
+
}
|
854 |
+
}
|
855 |
+
|
856 |
function reschedule($how_far_ahead) {
|
857 |
// Reschedule - remove presently scheduled event
|
858 |
+
$next_resumption = $this->current_resumption + 1;
|
859 |
+
wp_clear_scheduled_hook('updraft_backup_resume', array($next_resumption, $this->nonce));
|
860 |
// Add new event
|
861 |
if ($how_far_ahead < $this->minimum_resume_interval()) $how_far_ahead=$this->minimum_resume_interval();
|
862 |
$schedule_for = time() + $how_far_ahead;
|
863 |
+
$this->log("Rescheduling resumption $next_resumption: moving to $how_far_ahead seconds from now ($schedule_for)");
|
864 |
+
wp_schedule_single_event($schedule_for, 'updraft_backup_resume', array($next_resumption, $this->nonce));
|
865 |
$this->newresumption_scheduled = $schedule_for;
|
866 |
}
|
867 |
|
868 |
function increase_resume_and_reschedule($howmuch = 120) {
|
869 |
$resume_interval = $this->jobdata_get('resume_interval');
|
870 |
+
if (!is_numeric($resume_interval) || $resume_interval < $this->minimum_resume_interval()) { $resume_interval = $this->minimum_resume_interval(); }
|
871 |
+
if (!empty($this->newresumption_scheduled)) $this->reschedule($resume_interval+$howmuch);
|
872 |
$this->jobdata_set('resume_interval', $resume_interval+$howmuch);
|
873 |
+
$this->log("To decrease the likelihood of overlaps, increasing resumption interval to: $resume_interval + $howmuch = ".($resume_interval+$howmuch));
|
874 |
}
|
875 |
|
876 |
function create_zip($create_from_dir, $whichone, $create_in_dir, $backup_file_basename) {
|
880 |
if ($whichone != "others") $this->log("Beginning creation of dump of $whichone");
|
881 |
|
882 |
$full_path = $create_in_dir.'/'.$backup_file_basename.'-'.$whichone.'.zip';
|
883 |
+
$time_now = time();
|
884 |
|
885 |
if (file_exists($full_path)) {
|
886 |
$this->log("$backup_file_basename-$whichone.zip: this file has already been created");
|
887 |
+
$time_mod = (int)@filemtime($full_path);
|
888 |
+
if ($time_mod>100 && ($time_now-$time_mod)<30) {
|
889 |
+
$this->log("Terminate: the zip $full_path already exists, and was modified within the last 30 seconds (time_mod=$time_mod, time_now=$time_now, diff=".($time_now-$time_mod).", size=".filesize($full_path)."). This likely means that another UpdraftPlus run is still at work; so we will exit.");
|
890 |
+
$this->increase_resume_and_reschedule(120);
|
891 |
+
die;
|
892 |
+
}
|
893 |
return basename($full_path);
|
894 |
}
|
895 |
|
897 |
|
898 |
// Firstly, make sure that the temporary file is not already being written to - which can happen if a resumption takes place whilst an old run is still active
|
899 |
$zip_name = $full_path.'.tmp';
|
|
|
900 |
$time_mod = (int)@filemtime($zip_name);
|
901 |
if (file_exists($zip_name) && $time_mod>100 && ($time_now-$time_mod)<30) {
|
902 |
$file_size = filesize($zip_name);
|
925 |
return basename($full_path);
|
926 |
}
|
927 |
|
928 |
+
// For detecting another run, and aborting if one was found
|
929 |
+
function check_recent_modification($file) {
|
930 |
+
if (file_exists($file)) {
|
931 |
+
$time_mod = (int)@filemtime($file);
|
932 |
+
$time_now = time();
|
933 |
+
if ($time_mod>100 && ($time_now-$time_mod)<30) {
|
934 |
+
$this->log("Terminate: the file $file already exists, and was modified within the last 30 seconds (time_mod=$time_mod, time_now=$time_now, diff=".($time_now-$time_mod).", size=".filesize($file)."). This likely means that another UpdraftPlus run is still at work; so we will exit.");
|
935 |
+
$this->increase_resume_and_reschedule(120);
|
936 |
+
die;
|
937 |
+
}
|
938 |
+
}
|
939 |
+
}
|
940 |
+
|
941 |
// This function is resumable
|
942 |
function backup_dirs($transient_status) {
|
943 |
|
955 |
$blog_name = preg_replace('/[^A-Za-z0-9_]/','', $blog_name);
|
956 |
if(!$blog_name) $blog_name = 'non_alpha_name';
|
957 |
|
958 |
+
$backup_file_basename = 'backup_'.get_date_from_gmt(gmdate('Y-m-d H:i:s', $this->backup_time), 'Y-m-d-Hi').'_'.$blog_name.'_'.$this->nonce;
|
959 |
|
960 |
$backup_array = array();
|
961 |
|
968 |
|
969 |
# Plugins, themes, uploads
|
970 |
foreach ($possible_backups as $youwhat => $whichdir) {
|
971 |
+
|
972 |
if (UpdraftPlus_Options::get_updraft_option("updraft_include_$youwhat", true)) {
|
973 |
+
|
974 |
+
$zip_file = $updraft_dir.'/'.$backup_file_basename.'-'.$youwhat.'.zip';
|
975 |
+
|
976 |
+
$this->check_recent_modification($zip_file);
|
977 |
+
|
978 |
if ($transient_status == 'finished') {
|
979 |
$backup_array[$youwhat] = $backup_file_basename.'-'.$youwhat.'.zip';
|
980 |
+
if (file_exists($zip_file)) $backup_array[$youwhat.'-size'] = filesize($zip_file);
|
981 |
} else {
|
982 |
$created = $this->create_zip($whichdir, $youwhat, $updraft_dir, $backup_file_basename);
|
983 |
if ($created) {
|
993 |
# Others
|
994 |
if (UpdraftPlus_Options::get_updraft_option('updraft_include_others', true)) {
|
995 |
|
996 |
+
$zip_file = $updraft_dir.'/'.$backup_file_basename.'-others.zip';
|
997 |
+
$this->check_recent_modification($zip_file);
|
998 |
+
|
999 |
if ($transient_status == 'finished') {
|
1000 |
$backup_array['others'] = $backup_file_basename.'-others.zip';
|
1001 |
+
if (file_exists($zip_file)) $backup_array['others-size'] = filesize($zip_file);
|
1002 |
} else {
|
1003 |
$this->log("Beginning backup of other directories found in the content directory");
|
1004 |
|
1131 |
// Get the blog name and rip out all non-alphanumeric chars other than _
|
1132 |
$blog_name = preg_replace('/[^A-Za-z0-9_]/','', str_replace(' ','_', substr(get_bloginfo(), 0, 96)));
|
1133 |
if (!$blog_name) $blog_name = 'non_alpha_name';
|
1134 |
+
|
1135 |
+
$file_base = 'backup_'.get_date_from_gmt(gmdate('Y-m-d H:i:s', $this->backup_time), 'Y-m-d-Hi').'_'.$blog_name.'_'.$this->nonce;
|
1136 |
$backup_file_base = $updraft_dir.'/'.$file_base;
|
1137 |
|
1138 |
if ('finished' == $already_done) return basename($backup_file_base.'-db.gz');
|
1473 |
return $schedules;
|
1474 |
}
|
1475 |
|
1476 |
+
// This options filter removes ABSPATH off the front of updraft_dir, if it is given absolutely and contained within it
|
1477 |
+
function prune_updraft_dir_prefix($updraft_dir) {
|
1478 |
+
if ('/' == substr($updraft_dir, 0, 1) || "\\" == substr($updraft_dir, 0, 1) || preg_match('/^[a-zA-Z]:/', $updraft_dir)) {
|
1479 |
+
if (strpos($updraft_dir, ABSPATH) === 0) {
|
1480 |
+
$updraft_dir = substr($updraft_dir, strlen(ABSPATH));
|
1481 |
+
}
|
1482 |
+
}
|
1483 |
+
return $updraft_dir;
|
1484 |
+
}
|
1485 |
+
|
1486 |
function backups_dir_location() {
|
1487 |
+
|
1488 |
if (!empty($this->backup_dir)) return $this->backup_dir;
|
1489 |
+
|
1490 |
$updraft_dir = untrailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir'));
|
1491 |
$default_backup_dir = WP_CONTENT_DIR.'/updraft';
|
|
|
1492 |
$updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir;
|
1493 |
+
|
1494 |
+
// Do a test for a relative path
|
1495 |
+
if ('/' != substr($updraft_dir, 0, 1) && "\\" != substr($updraft_dir, 0, 1) && !preg_match('/^[a-zA-Z]:/', $updraft_dir)) {
|
1496 |
+
$updraft_dir = ABSPATH.$updraft_dir;
|
1497 |
+
}
|
1498 |
+
|
1499 |
+
//if the option isn't set, default it to /backups inside the upload dir
|
1500 |
//check for the existence of the dir and an enumeration preventer.
|
1501 |
+
// index.php is for a sanity check - make sure that we're not somewhere unexpected
|
1502 |
+
if((!is_dir($updraft_dir) || !is_file($updraft_dir.'/index.html') || !is_file($updraft_dir.'/.htaccess')) && !is_file($updraft_dir.'/index.php')) {
|
1503 |
@mkdir($updraft_dir, 0775, true);
|
1504 |
+
@file_put_contents($updraft_dir.'/index.html',"<html><body><a href=\"http://updraftplus.com\">WordPress backups by UpdraftPlus</a></body></html>");
|
1505 |
+
if (!is_file($updraft_dir.'/.htaccess')) @file_put_contents($updraft_dir.'/.htaccess','deny from all');
|
1506 |
}
|
1507 |
+
|
1508 |
$this->backup_dir = $updraft_dir;
|
1509 |
+
|
1510 |
return $updraft_dir;
|
1511 |
}
|
1512 |
|
1922 |
return $this->url_start($urls,'wordshell.net')."Check out WordShell".$this->url_end($urls,'www.wordshell.net')." - manage WordPress from the command line - huge time-saver";
|
1923 |
break;
|
1924 |
case 3:
|
1925 |
+
return "Like UpdraftPlus and can spare one minute? ".$this->url_start($urls,'wordpress.org/support/view/plugin-reviews/updraftplus#postform')."Please help UpdraftPlus by giving a positive review at wordpress.org.".$this->url_end($urls,'wordpress.org/support/view/plugin-reviews/updraftplus#postform');
|
1926 |
break;
|
1927 |
case 4:
|
1928 |
return $this->url_start($urls,'www.simbahosting.co.uk')."Need high-quality WordPress hosting from WordPress specialists? (Including automatic backups and 1-click installer). Get it from the creators of UpdraftPlus.".$this->url_end($urls,'www.simbahosting.co.uk');
|
2115 |
jQuery('.expertmode').fadeIn();
|
2116 |
return false;
|
2117 |
});
|
2118 |
+
<?php if (!@is_writable($updraft_dir)) echo "jQuery('.backupdirrow').show();\n"; ?>
|
2119 |
setTimeout(function(){updraft_showlastlog();}, 1200);
|
2120 |
jQuery('.updraftplusmethod').hide();
|
2121 |
<?php
|
2152 |
|
2153 |
<tr class="expertmode backupdirrow" style="display:none;">
|
2154 |
<th>Backup directory:</th>
|
2155 |
+
<td><input type="text" name="updraft_dir" id="updraft_dir" style="width:525px" value="<?php echo htmlspecialchars($this->prune_updraft_dir_prefix($updraft_dir)); ?>" /></td>
|
2156 |
</tr>
|
2157 |
<tr class="expertmode backupdirrow" style="display:none;">
|
2158 |
<td></td><td><?php
|
2159 |
|
2160 |
+
// Suppress warnings, since if the user is dumping warnings to screen, then invalid JavaScript results and the screen breaks.
|
2161 |
+
if(@is_writable($updraft_dir)) {
|
2162 |
$dir_info = '<span style="color:green">Backup directory specified is writable, which is good.</span>';
|
2163 |
} else {
|
2164 |
+
$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, or <a href="#" onclick="jQuery(\'#updraft_dir\').val(\''.WP_CONTENT_DIR.'/updraft\'); return false;">here to reset this option</a>. 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>';
|
2165 |
}
|
2166 |
|
2167 |
echo $dir_info ?> This is where UpdraftPlus will write the zip files it creates initially. This directory must be writable by your web server. Typically you'll want to have it inside your wp-content folder (this is the default). <b>Do not</b> place it inside your uploads dir, as that will cause recursion issues (backups of backups of backups of...).</td>
|
2295 |
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') { $this->boot_backup(true,true); }
|
2296 |
elseif (isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_db') { $this->backup_db(); }
|
2297 |
elseif (isset($_POST['action']) && $_POST['action'] == 'updraft_wipesettings') {
|
2298 |
+
$settings = array('updraft_interval', 'updraft_interval_database', 'updraft_retain', 'updraft_retain_db', 'updraft_encryptionphrase', 'updraft_service', 'updraft_dropbox_appkey', 'updraft_dropbox_secret', 'updraft_googledrive_clientid', 'updraft_googledrive_secret', 'updraft_googledrive_remotepath', 'updraft_ftp_login', 'updraft_ftp_pass', 'updraft_ftp_remote_path', 'updraft_server_address', 'updraft_dir', 'updraft_email', 'updraft_delete_local', 'updraft_debug_mode', 'updraft_include_plugins', 'updraft_include_themes', 'updraft_include_uploads', 'updraft_include_others', 'updraft_include_others_exclude', 'updraft_lastmessage', 'updraft_googledrive_clientid', 'updraft_googledrive_token', 'updraft_dropboxtk_request_token', 'updraft_dropboxtk_access_token', 'updraft_dropbox_folder', 'updraft_last_backup', 'updraft_starttime_files', 'updraft_starttime_db', 'updraft_sftp_settings');
|
2299 |
foreach ($settings as $s) {
|
2300 |
UpdraftPlus_Options::delete_updraft_option($s);
|
2301 |
}
|
2322 |
<div style="color:orange">Your PHP memory limit is quite low. UpdraftPlus attempted to raise it but was unsuccessful. This plugin may not work properly with a memory limit of less than 96 Mb (though on the other hand, it has been used successfully with a 32Mb limit - your mileage may vary, but don't blame us!). Current limit is: <?php echo $this->memory_check_current(); ?> Mb</div>
|
2323 |
<?php
|
2324 |
}
|
2325 |
+
if(1==0 && !$this->execution_time_check(60)) {?>
|
2326 |
+
<div style="color:orange">Your PHP max_execution_time is less than 60 seconds. This possibly means you're running in safe_mode. Either disable safe_mode or modify your php.ini to set max_execution_time to a higher number. If you do not, then longer will be needed to complete a backup (but that is all). Present limit is: <?php echo ini_get('max_execution_time'); ?> seconds.</div>
|
2327 |
<?php
|
2328 |
}
|
2329 |
|
2445 |
</tr>
|
2446 |
<tr>
|
2447 |
<td></td><td class="download-backups" style="display:none">
|
2448 |
+
<p><em><strong>Note</strong> - Pressing a button will make UpdraftPlus try to bring a backup file back from the remote storage (if any - e.g. Amazon S3, Dropbox, Google Drive, FTP) to your webserver, before then allowing you to download it to your computer. If the fetch from the remote storage stops progressing (wait 30 seconds to make sure), then click again to resume from where it left off. Remember that you can always visit the cloud storage website vendor's website directly. <strong>If you are using the Opera web browser, </strong> then turn Turbo/Road mode off.</em></p>
|
2449 |
<div id="ud_downloadstatus"></div>
|
2450 |
<script>
|
2451 |
var lastlog_lastmessage = "";
|
2587 |
$updraft_dir = $this->backups_dir_location();
|
2588 |
|
2589 |
echo '<table>';
|
2590 |
+
|
2591 |
+
krsort($backup_history);
|
2592 |
+
|
2593 |
foreach($backup_history as $key=>$value) {
|
2594 |
?>
|
2595 |
<tr>
|
2696 |
// - No zip extension present and no relevant method present
|
2697 |
// The zip extension check is not redundant, because method_exists segfaults some PHP installs, leading to support requests
|
2698 |
|
2699 |
+
// Fallback to PclZip - which my tests show is 25% slower (and we can't resume)
|
2700 |
if ($this->zip_preferpcl || (!extension_loaded('zip') && !method_exists('ZipArchive', 'AddFile'))) {
|
2701 |
if(!class_exists('PclZip')) require_once(ABSPATH.'/wp-admin/includes/class-pclzip.php');
|
2702 |
$zip_object = new PclZip($destination);
|
2731 |
$this->zipfiles_dirbatched = array();
|
2732 |
$this->zipfiles_batched = array();
|
2733 |
|
2734 |
+
// Magic value, used later to detect no error occurring
|
2735 |
+
$last_error = 2349864;
|
2736 |
if (is_array($source)) {
|
2737 |
foreach ($source as $element) {
|
2738 |
$howmany = $this->makezip_recursive_add($destination, $element, basename($element), $element);
|
2755 |
}
|
2756 |
}
|
2757 |
|
2758 |
+
if ($this->zipfiles_added > 0 || $last_error == 2349864) {
|
2759 |
+
// ZipArchive::addFile sometimes fails
|
2760 |
if (filesize($destination) < 100) {
|
2761 |
// Retry with PclZip
|
2762 |
$this->log("Zip::addFile apparently failed - retrying with PclZip");
|
2775 |
|
2776 |
// We batch up the files, rather than do them one at a time. So we are more efficient than open,one-write,close.
|
2777 |
function makezip_addfiles($zipfile) {
|
2778 |
+
|
2779 |
+
// Short-circuit the null case, because we want to detect later if something useful happenned
|
2780 |
+
if (count($this->zipfiles_dirbatched) == 0 && count($this->zipfiles_batched) == 0) return true;
|
2781 |
+
|
2782 |
+
// 05-Mar-2013 - added a new check on the total data added; it appears that things fall over if too much data is contained in the cumulative total of files that were addFile'd without a close-open cycle; presumably data is being stored in memory. In the case in question, it was a batch of MP3 files of around 100Mb each - 25 of those equals 2.5Gb!
|
2783 |
+
|
2784 |
+
$data_added_since_reopen = 0;
|
2785 |
+
|
2786 |
$zip = new ZipArchive();
|
2787 |
if (file_exists($zipfile)) {
|
2788 |
$opencode = $zip->open($zipfile);
|
2789 |
+
$original_size = filesize($zipfile);
|
2790 |
} else {
|
2791 |
$opencode = $zip->open($zipfile, ZIPARCHIVE::CREATE);
|
2792 |
+
$original_size = 0;
|
2793 |
}
|
2794 |
+
|
2795 |
if ($opencode !== true) return array($opencode, 0);
|
2796 |
// Make sure all directories are created before we start creating files
|
2797 |
while ($dir = array_pop($this->zipfiles_dirbatched)) {
|
2798 |
$zip->addEmptyDir($dir);
|
2799 |
}
|
2800 |
foreach ($this->zipfiles_batched as $file => $add_as) {
|
2801 |
+
$fsize = filesize($file);
|
2802 |
+
if (!isset($this->existing_files[$add_as]) || $this->existing_files[$add_as] != $fsize) {
|
2803 |
+
|
2804 |
+
touch($file);
|
2805 |
$zip->addFile($file, $add_as);
|
2806 |
+
|
2807 |
+
$data_added_since_reopen += $fsize;
|
2808 |
+
# 25Mb - force a write-out and re-open
|
2809 |
+
if ($data_added_since_reopen > 26214400) {
|
2810 |
+
|
2811 |
+
$before_size = filesize($zipfile);
|
2812 |
+
|
2813 |
+
$this->log("Adding batch to zip file: over 25Mb added on this batch (".round($data_added_since_reopen/1048576,1)." Mb); re-opening (prior size: ".round($before_size/1024,1).' Kb)');
|
2814 |
+
if (!$zip->close()) {
|
2815 |
+
$this->log("zip::Close returned an error");
|
2816 |
+
}
|
2817 |
+
unset($zip);
|
2818 |
+
$zip = new ZipArchive();
|
2819 |
+
$opencode = $zip->open($zipfile);
|
2820 |
+
if ($opencode !== true) return array($opencode, 0);
|
2821 |
+
$data_added_since_reopen = 0;
|
2822 |
+
// Call here, in case we've got so many big files that we don't complete the whole routine
|
2823 |
+
if (filesize($zipfile) > $before_size) $this->something_useful_happened();
|
2824 |
+
}
|
2825 |
}
|
2826 |
$this->zipfiles_added++;
|
2827 |
+
if ($this->zipfiles_added % 100 == 0) $this->log("Zip: ".basename($zipfile).": ".$this->zipfiles_added." files added (on-disk size: ".round(filesize($zipfile)/1024,1)." Kb)");
|
2828 |
}
|
2829 |
// Reset the array
|
2830 |
$this->zipfiles_batched = array();
|
2831 |
+
$ret = $zip->close();
|
2832 |
+
if (filesize($zipfile) > $original_size) $this->something_useful_happened();
|
2833 |
+
return $ret;
|
2834 |
+
}
|
2835 |
+
|
2836 |
+
function something_useful_happened() {
|
2837 |
+
if ($this->current_resumption >= 9 && $this->newresumption_scheduled == false) {
|
2838 |
+
$resume_interval = $this->jobdata_get('resume_interval');
|
2839 |
+
if (!is_numeric($resume_interval) || $resume_interval<$this->minimum_resume_interval()) { $resume_interval = $this->minimum_resume_interval(); }
|
2840 |
+
$schedule_for = time()+$resume_interval;
|
2841 |
+
$this->newresumption_scheduled = $schedule_for;
|
2842 |
+
$this->log("This is resumption ".$this->current_resumption.", but meaningful activity is still taking place; so a new one will be scheduled");
|
2843 |
+
wp_schedule_single_event($schedule_for, 'updraft_backup_resume', array($this->current_resumption + 1, $this->nonce));
|
2844 |
+
} else {
|
2845 |
+
$this->reschedule_if_needed();
|
2846 |
+
}
|
2847 |
}
|
2848 |
|
2849 |
// This function recursively packs the zip, dereferencing symlinks but packing into a single-parent tree for universal unpacking
|
2861 |
|
2862 |
if(is_file($fullpath)) {
|
2863 |
if (is_readable($fullpath)) {
|
2864 |
+
$key = ($fullpath == $original_fullpath) ? basename($fullpath) : $use_path_when_storing.'/'.basename($fullpath);
|
2865 |
+
$this->zipfiles_batched[$fullpath] = $key;
|
2866 |
@touch($zipfile);
|
2867 |
} else {
|
2868 |
$this->log("$fullpath: unreadable file");
|