first commit
This commit is contained in:
349
wp-content/plugins/updraftplus/addons/anonymisation.php
Normal file
349
wp-content/plugins/updraftplus/addons/anonymisation.php
Normal file
@@ -0,0 +1,349 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: anonymisation:Anonymisation functions
|
||||
Description: Anonymise personal data in your database backups
|
||||
Version: 1.0
|
||||
Shop: /shop/anonymisation/
|
||||
Latest Change: 1.16.37
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('ABSPATH')) die('No direct access allowed');
|
||||
|
||||
UpdraftPlus_Anonymisation_Functions::add_hooks();
|
||||
|
||||
class UpdraftPlus_Anonymisation_Functions {
|
||||
|
||||
/**
|
||||
* Adds hooks for anonymization UI
|
||||
*/
|
||||
public static function add_hooks() {
|
||||
add_filter('updraft_backupnow_database_showmoreoptions', array('UpdraftPlus_Anonymisation_Functions', 'backupnow_database_showmoreoptions'), 10, 2);
|
||||
add_filter('updraftplus_migration_additional_ui', array('UpdraftPlus_Anonymisation_Functions', 'updraftplus_migration_anonymisation_options'));
|
||||
add_filter('updraftplus_clone_additional_ui', array('UpdraftPlus_Anonymisation_Functions', 'updraftplus_clone_anonymisation_options'));
|
||||
add_action('pre_database_backup_setup', array('UpdraftPlus_Anonymisation_Functions', 'setup_anonymisation_settings'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds database anonymisation options to the backup now modal
|
||||
*
|
||||
* @param String $ret this contains the upgrade to premium link and gets cleared here and replaced with table content
|
||||
*
|
||||
* @return String A string that contains HTML to be appended to the backup now modal
|
||||
*/
|
||||
public static function backupnow_database_showmoreoptions($ret) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$ret = '<em>'.__('These options can anonymize personal data in your database backup.', 'updraftplus').' '.__('N.B. Anonymized information cannot be recovered; the original non-anonymized data will be absent from the backup.', 'updraftplus').'</em><br>';
|
||||
|
||||
$ret .= '<input type="checkbox" id="backupnow_db_anon_all">
|
||||
<label for="backupnow_db_anon_all">'.__('Anonymize personal data for all users except the logged-in user', 'updraftplus').'</label><br>';
|
||||
$ret .= '<input type="checkbox" id="backupnow_db_anon_non_staff">
|
||||
<label for="backupnow_db_anon_non_staff">'.__('Anonymize personal data for all users except staff', 'updraftplus').' <a href="'.$updraftplus->get_url('anon_backups').'" target="_blank">'.__('Learn more', 'updraftplus').'</a></label><br>';
|
||||
|
||||
if (class_exists('WooCommerce')) {
|
||||
$ret .= '<input type="checkbox" id="backupnow_db_anon_wc_order_data">
|
||||
<label for="backupnow_db_anon_wc_order_data">'.__('Anonymize WooCommerce order data', 'updraftplus') .'</label><br><hr/>';
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds database anonymisation options to the migration UI
|
||||
*
|
||||
* @param string $output - the current migration options UI
|
||||
*
|
||||
* @return string - the altered migration options UI
|
||||
*/
|
||||
public static function updraftplus_migration_anonymisation_options($output) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$output = '<em>'.__('These options can anonymize personal data in your database backup.', 'updraftplus').' '.__('N.B. Anonymized information cannot be recovered; the original non-anonymized data will be absent from the backup.', 'updraftplus').'</em><br>';
|
||||
|
||||
$output .= '<label class="updraft_checkbox" for="updraftplus_migration_backupnow_db_anon_all"><input type="checkbox" id="updraftplus_migration_backupnow_db_anon_all">'.__('Anonymize personal data for all users except the logged-in user', 'updraftplus').'</label>';
|
||||
|
||||
$output .= '<label class="updraft_checkbox" for="updraftplus_migration_backupnow_db_anon_non_staff"><input type="checkbox" id="updraftplus_migration_backupnow_db_anon_non_staff">'.__('Anonymize personal data for all users except staff', 'updraftplus').' <a href="'.$updraftplus->get_url('anon_backups').'" target="_blank">'.__('Learn more', 'updraftplus').'</a></label>';
|
||||
|
||||
if (class_exists('WooCommerce')) {
|
||||
$output .= '<label class="updraft_checkbox" for="updraftplus_migration_backupnow_db_anon_wc_order_data"><input type="checkbox" id="updraftplus_migration_backupnow_db_anon_wc_order_data">'.__('Anonymize WooCommerce order data', 'updraftplus').'</label>';
|
||||
}
|
||||
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds database anonymisation options to the clone creation UI
|
||||
*
|
||||
* @param string $output - the current clone options UI
|
||||
*
|
||||
* @return string - the altered clone options UI
|
||||
*/
|
||||
public static function updraftplus_clone_anonymisation_options($output) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$output .= '<p class="updraftplus-option backupnow-db-anon-all">';
|
||||
$output .= '<input type="checkbox" id="updraftplus_clone_backupnow_db_anon_all">';
|
||||
$output .= '<label for="updraftplus_clone_backupnow_db_anon_all">'.__('Anonymize personal data for all users except the logged-in user', 'updraftplus').'</label>';
|
||||
$output .= '</p>';
|
||||
|
||||
$output .= '<p class="updraftplus-option backupnow-db-anon-all">';
|
||||
$output .= '<input type="checkbox" id="updraftplus_clone_backupnow_db_anon_non_staff">';
|
||||
$output .= '<label for="updraftplus_clone_backupnow_db_anon_non_staff">'.__('Anonymize personal data for all users except staff', 'updraftplus').' <a href="'.$updraftplus->get_url('anon_backups').'" target="_blank">'.__('Learn more', 'updraftplus').'</a></label>';
|
||||
$output .= '</p>';
|
||||
if (class_exists('WooCommerce')) {
|
||||
$output .= '<p class="updraftplus-option backupnow-db-anon-wc-orders">';
|
||||
$output .= '<input type="checkbox" id="updraftplus_clone_backupnow_db_anon_wc_order_data">';
|
||||
$output .= '<label for="updraftplus_clone_backupnow_db_anon_wc_order_data">'.__('Anonymize WooCommerce order data', 'updraftplus').'</label>';
|
||||
$output .= '</p>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will add data to the backup options that is needed for the anonymised backup job
|
||||
*
|
||||
* @param array $options - the backup options array
|
||||
* @param array $request - the extra data we want to add to the backup options
|
||||
*
|
||||
* @return array - the backup options array with the extra data added
|
||||
*/
|
||||
public static function updraftplus_backup_anonymisation_options($options, $request) {
|
||||
if (!is_array($options)) return $options;
|
||||
|
||||
if (isset($request['db_anon_all'])) $options['db_anon_all'] = $request['db_anon_all'];
|
||||
if (isset($request['db_anon_non_staff'])) $options['db_anon_non_staff'] = $request['db_anon_non_staff'];
|
||||
if (isset($request['db_anon_wc_orders'])) $options['db_anon_wc_orders'] = $request['db_anon_wc_orders'];
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides all the wc order fields that needs to be anonymized
|
||||
*
|
||||
* @return array - WooCommerc order meta fields which needs to be anonymized
|
||||
*/
|
||||
public static function get_wc_order_anonymize_fields() {
|
||||
return array(
|
||||
'_billing_address_index' => 'text',
|
||||
'_shipping_address_index' => 'text',
|
||||
'_customer_ip_address' => 'ip',
|
||||
'_customer_user_agent' => 'text',
|
||||
'_billing_first_name' => 'text',
|
||||
'_billing_last_name' => 'text',
|
||||
'_billing_company' => 'text',
|
||||
'_billing_address_1' => 'text',
|
||||
'_billing_address_2' => 'text',
|
||||
'_billing_city' => 'text',
|
||||
'_billing_postcode' => 'text',
|
||||
'_billing_state' => 'address_state',
|
||||
'_billing_country' => 'address_country',
|
||||
'_billing_phone' => 'phone',
|
||||
'_billing_email' => 'email',
|
||||
'_shipping_first_name' => 'text',
|
||||
'_shipping_last_name' => 'text',
|
||||
'_shipping_company' => 'text',
|
||||
'_shipping_address_1' => 'text',
|
||||
'_shipping_address_2' => 'text',
|
||||
'_shipping_city' => 'text',
|
||||
'_shipping_postcode' => 'text',
|
||||
'_shipping_state' => 'address_state',
|
||||
'_shipping_country' => 'address_country',
|
||||
'_shipping_phone' => 'phone',
|
||||
'_transaction_id' => 'numeric_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the backup job data for when we are starting a backup job with anonymisation settings.
|
||||
*
|
||||
* @param array $jobdata - the initial job data that we want to change
|
||||
* @param array $options - options sent from the front end
|
||||
*
|
||||
* @return array - the modified jobdata
|
||||
*/
|
||||
public static function updraftplus_backup_anonymisation_jobdata($jobdata, $options) {
|
||||
if (!is_array($jobdata)) return $jobdata;
|
||||
|
||||
$anonymisation_options = array();
|
||||
|
||||
if (isset($options['db_anon_all'])) $anonymisation_options['backup_anonymise_all_data'] = $options['db_anon_all'];
|
||||
if (isset($options['db_anon_non_staff'])) $anonymisation_options['backup_anonymise_non_staff_data'] = $options['db_anon_non_staff'];
|
||||
if (isset($options['db_anon_wc_orders'])) $anonymisation_options['backup_anonymise_wc_data'] = $options['db_anon_wc_orders'];
|
||||
|
||||
|
||||
if (!empty($anonymisation_options)) {
|
||||
$jobdata[] = 'anonymisation_options';
|
||||
$jobdata[] = $anonymisation_options;
|
||||
}
|
||||
|
||||
return $jobdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look through the backup anonymisation options and add the relevant filters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setup_anonymisation_settings() {
|
||||
global $updraftplus;
|
||||
|
||||
$anonymisation_options = $updraftplus->jobdata_get('anonymisation_options', array());
|
||||
|
||||
foreach ($anonymisation_options as $option_name => $value) {
|
||||
if ($value) add_filter('updraftplus_backup_table_results', 'UpdraftPlus_Anonymisation_Functions::'.$option_name, 10, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymise the personal data in the users and usermeta table for all users except the logged in user
|
||||
*
|
||||
* @param array $result - the data returned from the SQL call
|
||||
* @param string $table - the table this data is from
|
||||
* @param string $table_prefix - the table prefix
|
||||
* @param string $whichdb - which db we are working on
|
||||
*
|
||||
* @return array - the data with personal data anonymised
|
||||
*/
|
||||
public static function backup_anonymise_all_data($result, $table, $table_prefix, $whichdb) {
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
if (empty($user_id)) return $result;
|
||||
|
||||
if ('wp' == $whichdb && (!empty($table_prefix) && strtolower($table_prefix.'users') == strtolower($table))) {
|
||||
foreach ($result as $key => $data) {
|
||||
if ($user_id != $data['ID']) $result[$key]['user_email'] = md5(rand())."@example.com";
|
||||
}
|
||||
} elseif ('wp' == $whichdb && (!empty($table_prefix) && strtolower($table_prefix.'usermeta') == strtolower($table))) {
|
||||
foreach ($result as $key => $data) {
|
||||
if ($user_id != $data['user_id']) {
|
||||
if ('first_name' == $data['meta_key'] || 'last_name' == $data['meta_key']) $result[$key]['meta_value'] = md5(rand());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymise the personal data in the users and usermeta table for all users except staff (Admin, Editor, Shop Manager)
|
||||
*
|
||||
* @param array $result - the data returned from the SQL call
|
||||
* @param string $table - the table this data is from
|
||||
* @param string $table_prefix - the table prefix
|
||||
* @param string $whichdb - which db we are working on
|
||||
*
|
||||
* @return array - the data with personal data anonymised
|
||||
*/
|
||||
public static function backup_anonymise_non_staff_data($result, $table, $table_prefix, $whichdb) {
|
||||
|
||||
$user_ids = array();
|
||||
|
||||
static $user_data = false;
|
||||
if (!$user_data) {
|
||||
$user_query = new WP_User_Query(array('role__in' => apply_filters('updraftplus_anonymise_staff_data_roles', array('administrator', 'editor', 'shop_manager', 'fue_manager', 'plugin_manager', 'wpseo_editor', 'seo_manager', 'moderator'))));
|
||||
$user_data = $user_query->get_results();
|
||||
}
|
||||
|
||||
|
||||
foreach ($user_data as $data) {
|
||||
$user_ids[] = $data->ID;
|
||||
}
|
||||
|
||||
if (empty($user_ids)) return $result;
|
||||
|
||||
if ('wp' == $whichdb && (!empty($table_prefix) && strtolower($table_prefix.'users') == strtolower($table))) {
|
||||
foreach ($result as $key => $data) {
|
||||
if (!in_array($data['ID'], $user_ids)) $result[$key]['user_email'] = md5(rand())."@example.com";
|
||||
}
|
||||
} elseif ('wp' == $whichdb && (!empty($table_prefix) && strtolower($table_prefix.'usermeta') == strtolower($table))) {
|
||||
foreach ($result as $key => $data) {
|
||||
if (!in_array($data['user_id'], $user_ids)) {
|
||||
if ('first_name' == $data['meta_key'] || 'last_name' == $data['meta_key']) $result[$key]['meta_value'] = md5(rand());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymise the personal data in the postmeta table for orders
|
||||
*
|
||||
* @param array $result - the data returned from the SQL call
|
||||
* @param string $table - the table this data is from
|
||||
* @param string $table_prefix - the table prefix
|
||||
* @param string $whichdb - which db we are working on
|
||||
*
|
||||
* @return array - the data with personal data anonymised
|
||||
*/
|
||||
public static function backup_anonymise_wc_data($result, $table, $table_prefix, $whichdb) {
|
||||
$anonymisation_function = array('UpdraftPlus_Manipulation_Functions', 'anonymize_data');
|
||||
if ('wp' == $whichdb && ((!empty($table_prefix) && (strtolower($table_prefix . 'postmeta') == strtolower($table) || strtolower($table_prefix . 'wc_orders_meta') == strtolower($table))))) {
|
||||
$wc_anon_fields = self::get_wc_order_anonymize_fields();
|
||||
foreach ($result as $key => $data) {
|
||||
if (array_key_exists($data['meta_key'], $wc_anon_fields)) {
|
||||
$result[$key]['meta_value'] = call_user_func($anonymisation_function, $wc_anon_fields[$data['meta_key']], $data['meta_value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ('wp' == $whichdb && (!empty($table_prefix) && (strtolower($table_prefix . 'wc_orders') == strtolower($table) || strtolower($table_prefix . 'wc_order_addresses') == strtolower($table) ))) {
|
||||
foreach ($result as $key => $data) {
|
||||
if (!empty($data['billing_email'])) {
|
||||
$result[$key]['billing_email'] = call_user_func($anonymisation_function, 'email', $data['billing_email']);
|
||||
}
|
||||
if (!empty($data['ip_address'])) {
|
||||
$result[$key]['ip_address'] = call_user_func($anonymisation_function, 'ip', $data['ip_address']);
|
||||
}
|
||||
if (!empty($data['first_name'])) {
|
||||
$result[$key]['first_name'] = call_user_func($anonymisation_function, 'text', $data['first_name']);
|
||||
}
|
||||
if (!empty($data['last_name'])) {
|
||||
$result[$key]['last_name'] = call_user_func($anonymisation_function, 'text', $data['last_name']);
|
||||
}
|
||||
if (!empty($data['company'])) {
|
||||
$result[$key]['company'] = call_user_func($anonymisation_function, 'text', $data['company']);
|
||||
}
|
||||
if (!empty($data['address_1'])) {
|
||||
$result[$key]['address_1'] = call_user_func($anonymisation_function, 'text', $data['address_1']);
|
||||
}
|
||||
if (!empty($data['address_2'])) {
|
||||
$result[$key]['address_2'] = call_user_func($anonymisation_function, 'text', $data['address_2']);
|
||||
}
|
||||
if (!empty($data['state'])) {
|
||||
$result[$key]['state'] = call_user_func($anonymisation_function, 'text', $data['state']);
|
||||
}
|
||||
if (!empty($data['city'])) {
|
||||
$result[$key]['city'] = call_user_func($anonymisation_function, 'text', $data['city']);
|
||||
}
|
||||
if (!empty($data['postcode'])) {
|
||||
$result[$key]['postcode'] = call_user_func($anonymisation_function, 'text', $data['postcode']);
|
||||
}
|
||||
if (!empty($data['country'])) {
|
||||
$result[$key]['country'] = call_user_func($anonymisation_function, 'text', $data['country']);
|
||||
}
|
||||
if (!empty($data['email'])) {
|
||||
$result[$key]['email'] = call_user_func($anonymisation_function, 'email', $data['email']);
|
||||
}
|
||||
if (!empty($data['phone'])) {
|
||||
$result[$key]['phone'] = call_user_func($anonymisation_function, 'text', $data['text']);
|
||||
}
|
||||
|
||||
}
|
||||
if ('wp' == $whichdb && (!empty($table_prefix) && (strtolower($table_prefix . 'wc_order_operational_data') == strtolower($table)))) {
|
||||
if (!empty($data['created_via'])) {
|
||||
$result[$key]['created_via'] = call_user_func($anonymisation_function, 'text', $data['created_via']);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
1223
wp-content/plugins/updraftplus/addons/autobackup.php
Normal file
1223
wp-content/plugins/updraftplus/addons/autobackup.php
Normal file
File diff suppressed because it is too large
Load Diff
726
wp-content/plugins/updraftplus/addons/azure.php
Normal file
726
wp-content/plugins/updraftplus/addons/azure.php
Normal file
@@ -0,0 +1,726 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: azure:Microsoft Azure Support
|
||||
Description: Microsoft Azure Support
|
||||
Version: 1.5
|
||||
Shop: /shop/azure/
|
||||
Include: includes/azure
|
||||
IncludePHP: methods/addon-base-v2.php
|
||||
RequiresPHP: 5.6
|
||||
Latest Change: 1.13.12
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
if (!class_exists('UpdraftPlus_RemoteStorage_Addons_Base_v2')) updraft_try_include_file('methods/addon-base-v2.php', 'require_once');
|
||||
|
||||
class UpdraftPlus_Addons_RemoteStorage_azure extends UpdraftPlus_RemoteStorage_Addons_Base_v2 {
|
||||
|
||||
/**
|
||||
* Chunk count no.
|
||||
*
|
||||
* @var Integer
|
||||
*/
|
||||
private $block;
|
||||
|
||||
/**
|
||||
* Uploaded file size
|
||||
*
|
||||
* @var Integer
|
||||
*/
|
||||
private $uploaded_size;
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/azure/ee691964.aspx - maximum block size is 4MB
|
||||
private $chunk_size = 2097152;
|
||||
|
||||
public function __construct() {
|
||||
// 3rd parameter: chunking? 4th: Test button?
|
||||
parent::__construct('azure', 'Azure', true, true);
|
||||
// https://msdn.microsoft.com/en-us/library/azure/ee691964.aspx - maximum block size is 4MB
|
||||
if (defined('UPDRAFTPLUS_UPLOAD_CHUNKSIZE') && UPDRAFTPLUS_UPLOAD_CHUNKSIZE > 0) $this->chunk_size = max(UPDRAFTPLUS_UPLOAD_CHUNKSIZE, 4194304);
|
||||
}
|
||||
|
||||
public function do_upload($file, $from) {
|
||||
global $updraftplus;
|
||||
|
||||
$opts = $this->options;
|
||||
$storage = $this->get_storage();
|
||||
|
||||
if (is_wp_error($storage)) throw new Exception($storage->get_error_message()); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- The escaping should be happening when the exception is printed
|
||||
if (!is_object($storage)) throw new Exception("Azure service error");
|
||||
|
||||
$filesize = filesize($from);
|
||||
$directory = empty($opts['directory']) ? '' : trailingslashit($opts['directory']);
|
||||
|
||||
$account_name = $opts['account_name']; // Used here only for logging
|
||||
|
||||
// If the user is using OneDrive for Germany option
|
||||
if (isset($opts['endpoint']) && 'blob.core.cloudapi.de' === $opts['endpoint']) {
|
||||
$odg_warning = sprintf(__('Due to the shutdown of the %1$s endpoint, support for %1$s will be ending soon.', 'updraftplus'), 'Azure Germany').' '.__('You will need to migrate to the Global endpoint in your UpdraftPlus settings.', 'updraftplus').' '.sprintf(__('For more information, please see: %s', 'updraftplus'), 'https://www.microsoft.com/en-us/cloud-platform/germany-cloud-regions');
|
||||
// We only want to log this once per backup job
|
||||
$this->log($odg_warning, 'warning', 'azure_de_migrate');
|
||||
}
|
||||
|
||||
// Create/check container
|
||||
$container_name = $opts['container'];
|
||||
$container = $this->create_container($container_name);
|
||||
if (is_wp_error($container)) {
|
||||
$this->log("error: ".$container->get_error_message());
|
||||
$this->log("error: ".$container->get_error_message(), 'error');
|
||||
return false;
|
||||
} elseif (false == $container) {
|
||||
$this->log("error when attempting to access container ($container_name)");
|
||||
$this->log("error when attempting to access container ($container_name)", 'error');
|
||||
}
|
||||
|
||||
// Perhaps it already exists (if we didn't get the final confirmation
|
||||
try {
|
||||
$items = $this->listfiles($directory.$file);
|
||||
foreach ($items as $item) {
|
||||
if (basename($item['name']) == $file && $item['size'] >= $filesize) {
|
||||
$this->log("$file: already uploaded");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->log("file check: exception: ($file) (".$e->getMessage().") (line: ".$e->getLine().', file: '.$e->getFile().')');
|
||||
}
|
||||
|
||||
if (false != ($handle = fopen($from, 'rb'))) {
|
||||
if ($filesize <= $this->chunk_size) {
|
||||
$this->log("will upload file in one operation (azure://$account_name/$container_name/$directory$file)");
|
||||
$storage->createBlockBlob($opts['container'], $directory.$file, $handle);
|
||||
fclose($handle);
|
||||
} else {
|
||||
// Set up chunked upload
|
||||
|
||||
$hash_key = md5($directory.$file);
|
||||
$container = $opts['container'];
|
||||
|
||||
// Stored last uploaded block
|
||||
$block_ids = $this->jobdata_get('block_ids_'.$hash_key, array(), 'az_block_ids_'.$hash_key);
|
||||
|
||||
if (!is_array($block_ids)) $block_ids = array();
|
||||
$block = 1;
|
||||
while (isset($block_ids[$block])) {
|
||||
$block++;
|
||||
}
|
||||
|
||||
$uploaded_size = $this->chunk_size * ($block - 1);
|
||||
$this->block = $block;
|
||||
$this->uploaded_size = $uploaded_size;
|
||||
|
||||
if ($uploaded_size) {
|
||||
$this->log("Resuming upload to azure://$account_name/$container_name/$directory$file from byte: $uploaded_size; block/chunk: $block");
|
||||
} else {
|
||||
$this->log("Starting fresh upload to azure://$account_name/$container_name/$directory$file from byte: 0; block/chunk: 1");
|
||||
}
|
||||
|
||||
$ret = $updraftplus->chunked_upload($this, $file, "azure://$account_name/$container_name/$directory", $this->description, $this->chunk_size, $uploaded_size, false);
|
||||
|
||||
fclose($handle);
|
||||
return $ret;
|
||||
}
|
||||
} else {
|
||||
throw new Exception("Failed to open file for reading: $from"); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- The escaping should be happening when the exception is printed
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts as a WordPress options filter
|
||||
*
|
||||
* @param Array $azure an array of Azure options
|
||||
* @return Array - the returned array can either be the set of updated Azure settings or a WordPress error array
|
||||
*/
|
||||
public function options_filter($azure) {
|
||||
// Get the current options (and possibly update them to the new format)
|
||||
$opts = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('azure');
|
||||
|
||||
if (is_wp_error($opts)) {
|
||||
if ('recursion' !== $opts->get_error_code()) {
|
||||
$msg = "(".$opts->get_error_code()."): ".$opts->get_error_message();
|
||||
$this->log($msg);
|
||||
error_log("UpdraftPlus: Azure: $msg");
|
||||
}
|
||||
// The saved options had a problem; so, return the new ones
|
||||
return $azure;
|
||||
}
|
||||
|
||||
if (!is_array($azure)) return $opts;
|
||||
|
||||
if (!empty($opts['settings']) && is_array($opts['settings'])) {
|
||||
// Remove instances that no longer exist
|
||||
foreach ($opts['settings'] as $instance_id => $storage_options) {
|
||||
if (!isset($azure['settings'][$instance_id])) unset($opts['settings'][$instance_id]);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($azure['settings'])) return $opts;
|
||||
|
||||
foreach ($azure['settings'] as $instance_id => $storage_options) {
|
||||
foreach ($storage_options as $key => $value) {
|
||||
if ('folder' == $key) $value = trim(str_replace('\\', '/', $value), '/');
|
||||
// Only lower-case containers are permitted - enforce this
|
||||
if ('container' == $key) $value = strtolower($value);
|
||||
$opts['settings'][$instance_id][$key] = ('key' == $key || 'account_name' == $key) ? trim($value) : $value;
|
||||
// Convert one likely misunderstanding of the format to enter the account name in
|
||||
if ('account_name' == $key && preg_match('#^https?://(.*)\.blob\.core\.windows#i', $opts['settings'][$instance_id]['account_name'], $matches)) {
|
||||
$opts['settings'][$instance_id]['account_name'] = $matches[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunked Upload
|
||||
*
|
||||
* @param string $file FIle to be chunked
|
||||
* @param string $fp FTP URL
|
||||
* @param string $chunk_index This is the chunked index
|
||||
* @param string $upload_size This is the upload size
|
||||
* @param string $upload_start This is the upload start position
|
||||
* @param string $upload_end This is the Upload end positions
|
||||
* @return boolean
|
||||
*/
|
||||
public function chunked_upload($file, $fp, $chunk_index, $upload_size, $upload_start, $upload_end) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Filter use
|
||||
$opts = $this->options;
|
||||
$directory = !empty($opts['directory']) ? trailingslashit($opts['directory']) : "";
|
||||
$storage = $this->get_storage();
|
||||
|
||||
// Already done?
|
||||
$block_ids_key = 'block_ids_'.md5($directory.$file);
|
||||
$block_ids = $this->jobdata_get($block_ids_key, array(), 'az_'.$block_ids_key);
|
||||
if (!is_array($block_ids)) $block_ids = array();
|
||||
// Return 1, not true, to prevent expensive database logging of all the previous chunks on each resumption
|
||||
if (isset($block_ids[$chunk_index])) return 1;
|
||||
|
||||
// Each block needs id of the same length
|
||||
$block_id = str_pad($chunk_index, 6, "0", STR_PAD_LEFT);
|
||||
|
||||
try {
|
||||
$data = fread($fp, $upload_size);
|
||||
$storage->createBlobBlock($opts['container'], $directory.$file, base64_encode($block_id), $data);
|
||||
} catch (Exception $e) {
|
||||
$this->log("upload: exception (".get_class($e)."): ($file) (".$e->getMessage().") (line: ".$e->getLine().', file: '.$e->getFile().')');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the Block ID of uploaded block
|
||||
if (is_array($block_ids)) {
|
||||
$block_ids[$chunk_index] = $block_id;
|
||||
} else {
|
||||
$block_ids = array($chunk_index => $block_id);
|
||||
}
|
||||
|
||||
$this->jobdata_set($block_ids_key, $block_ids);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will send the final block of data to be written to file on the Azure remote storage location
|
||||
*
|
||||
* @param String $file - the file to read from
|
||||
* @return Boolean - a boolean value to indicate success or failure of the chunked upload finish call
|
||||
*/
|
||||
public function chunked_upload_finish($file) {
|
||||
$this->log("all chunks uploaded; now committing blob blocks");
|
||||
// Commit the blocks to create the blob
|
||||
|
||||
$opts = $this->get_options();
|
||||
$storage = $this->get_storage();
|
||||
$directory = !empty($opts['directory']) ? trailingslashit($opts['directory']) : "";
|
||||
$hash_key = md5($directory.$file);
|
||||
|
||||
$block_ids = $this->jobdata_get('block_ids_'.$hash_key, array(), 'az_block_ids_'.$hash_key);
|
||||
if (!is_array($block_ids)) return false;
|
||||
|
||||
$blocks = array();
|
||||
foreach ($block_ids as $b_id) {
|
||||
$block = new WindowsAzure\Blob\Models\Block();
|
||||
$block->setBlockId(base64_encode($b_id));
|
||||
$block->setType('Uncommitted');
|
||||
array_push($blocks, $block);
|
||||
}
|
||||
|
||||
try {
|
||||
$storage->commitBlobBlocks($opts['container'], $directory.$file, $blocks);
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')';
|
||||
$this->log("service error: ".$message);
|
||||
$this->log($message, 'error');
|
||||
|
||||
try {
|
||||
// Retrieves the list of blocks that have been uploaded as part of a block blob.
|
||||
$blob_blocks = $storage->listBlobBlocks($opts['container'], $directory.$file);
|
||||
|
||||
$block_list = array();
|
||||
foreach ($blob_blocks->getCommittedBlocks() as $key => $value) $block_list[] = base64_decode($key);
|
||||
$block_list = empty($block_list) ? '-' : implode(', ', $block_list);
|
||||
$this->log('committed blocks: '.$block_list);
|
||||
|
||||
$block_list = array();
|
||||
foreach ($blob_blocks->getUncommittedBlocks() as $key => $value) $block_list[] = base64_decode($key);
|
||||
$block_list = empty($block_list) ? '-' : implode(', ', $block_list);
|
||||
$this->log('uncommitted blocks: '.$block_list);
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')';
|
||||
$this->log("service error: ".$message);
|
||||
$this->log($message, 'error');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// Prevent bloat
|
||||
$this->jobdata_delete('block_ids_'.$hash_key, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function do_download($file, $fullpath) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Filter use
|
||||
global $updraftplus;
|
||||
|
||||
$opts = $this->options;
|
||||
$storage = $this->get_storage();
|
||||
|
||||
if (is_wp_error($storage)) throw new Exception($storage->get_error_message()); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- The escaping should be happening when the exception is printed
|
||||
if (!is_object($storage)) throw new Exception("Azure service error");
|
||||
|
||||
$container_name = $opts['container'];
|
||||
$directory = !empty($opts['directory']) ? trailingslashit($opts['directory']) : "";
|
||||
$this->azure_path = $directory.$file;
|
||||
|
||||
try {
|
||||
$blob_properties = $storage->getBlobProperties($container_name, $this->azure_path)->getProperties();
|
||||
} catch (WindowsAzure\Common\ServiceException $e) {
|
||||
if (404 == $e->getCode()) {
|
||||
$this->log("$file: ".sprintf(__("%s Error", 'updraftplus'), 'Azure').": ".__('File not found', 'updraftplus'), 'error');
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $updraftplus->chunked_download($file, $this, $blob_properties->getContentLength(), true, $container_name, $this->chunk_size);
|
||||
|
||||
}
|
||||
|
||||
public function chunked_download($file, $headers, $container_name) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Filter use
|
||||
|
||||
$storage = $this->get_storage();
|
||||
|
||||
if (is_array($headers) && !empty($headers['Range']) && preg_match('/bytes=(\d+)-(\d+)$/', $headers['Range'], $matches)) {
|
||||
$options = new WindowsAzure\Blob\Models\GetBlobOptions;
|
||||
$options->setRangeStart($matches[1]);
|
||||
$options->setRangeEnd($matches[2]);
|
||||
} else {
|
||||
$options = null;
|
||||
}
|
||||
|
||||
$blob = $storage->getBlob($container_name, $this->azure_path, $options);
|
||||
|
||||
$headers = $blob->getProperties();
|
||||
|
||||
// The Azure SDK turns the string into a stream. In the absence of other options, we change it back.
|
||||
$stream = $blob->getContentStream();
|
||||
return fread($stream, $headers->getContentLength());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single file from the service
|
||||
*
|
||||
* @param String $file - filename
|
||||
* @return Boolean|String - either a boolean or an error code string
|
||||
*/
|
||||
public function do_delete($file) {
|
||||
$opts = $this->options;
|
||||
$storage = $this->get_storage();
|
||||
|
||||
$directory = !empty($opts['directory']) ? trailingslashit($opts['directory']) : "";
|
||||
$azure_path = $directory.$file;
|
||||
|
||||
if (is_object($storage) && !is_wp_error($storage)) {
|
||||
// list blobs
|
||||
$blobs = $this->listfiles($file);
|
||||
|
||||
// check if needed blob is there
|
||||
foreach ($blobs as $blob) {
|
||||
if (isset($blob['name']) && basename($blob['name']) == $file) {
|
||||
try {
|
||||
// if match, delete file
|
||||
$storage->deleteBlob($opts['container'], $azure_path);
|
||||
return true;
|
||||
} catch (WindowsAzure\Common\ServiceException $e) {
|
||||
$this->log("File delete failed: Service Exception");
|
||||
return 'file_delete_error';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no, log an error
|
||||
$this->log("file does not exist");
|
||||
return 'file_delete_error';
|
||||
}
|
||||
|
||||
if (is_wp_error($storage)) {
|
||||
$this->log("service was not available (".$storage->get_error_message().")");
|
||||
return 'service_unavailable';
|
||||
}
|
||||
|
||||
$this->log("delete error");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to get a list of backup files for the remote storage option
|
||||
*
|
||||
* @param string $match - a string to match when looking for files
|
||||
* @return Array - returns an array of file locations or a WordPress error
|
||||
*/
|
||||
public function do_listfiles($match = 'backup_') {
|
||||
$opts = $this->get_options();
|
||||
|
||||
$directory = !empty($opts['directory']) ? trailingslashit($opts['directory']) : "";
|
||||
|
||||
try {
|
||||
$storage = $this->bootstrap();
|
||||
if (!is_object($storage)) throw new Exception('Azure service error');
|
||||
} catch (Exception $e) {
|
||||
$storage = $e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')';
|
||||
return $storage;
|
||||
}
|
||||
|
||||
try {
|
||||
$list_options = new WindowsAzure\Blob\Models\ListBlobsOptions;
|
||||
$list_options->setPrefix($directory.$match);
|
||||
$storage = $this->get_storage();
|
||||
$blob_list = $storage->listBlobs($opts['container'], $list_options);
|
||||
} catch (WindowsAzure\Common\ServiceException $e) {
|
||||
return new WP_Error('list_files_failed', 'List Files ServiceException');
|
||||
}
|
||||
|
||||
$blobs = $blob_list->getBlobs();
|
||||
|
||||
$results = array();
|
||||
foreach ($blobs as $blob) {
|
||||
$blob_name = basename($blob->getName());
|
||||
$blob_prop = $blob->getProperties();
|
||||
$blob_size = $blob_prop->getContentLength();
|
||||
$results[] = array('name' => $blob_name, 'size' => $blob_size);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of parameters required to be present for a credential tests, plus descriptions
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function get_credentials_test_required_parameters() {
|
||||
return array(
|
||||
'account_name' => 'Account Name',
|
||||
'key' => 'Account Key',
|
||||
'container' => 'Container',
|
||||
);
|
||||
}
|
||||
|
||||
protected function do_credentials_test($testfile, $posted_settings = array()) {
|
||||
$storage = $this->get_storage();
|
||||
|
||||
$container_name = $posted_settings['container'];
|
||||
|
||||
$directory = !empty($posted_settings['directory']) ? trailingslashit($posted_settings['directory']) : "";
|
||||
try {
|
||||
$exists = $this->create_container($container_name);
|
||||
|
||||
if (is_wp_error($exists)) {
|
||||
foreach ($exists->get_error_messages() as $msg) {
|
||||
echo esc_html("$msg\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo esc_html(__('Could not access container', 'updraftplus').': '.$e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$storage->createBlockBlob($container_name, $directory.$testfile, "UpdraftPlus temporary test file - you can remove this.");
|
||||
} catch (Exception $e) {
|
||||
echo 'Azure: '.esc_html(__('Upload failed', 'updraftplus').': '.$e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a temporary file use for a credentials test. Output can be echo-ed.
|
||||
*
|
||||
* @param String $testfile - the basename of the file to delete
|
||||
* @param Array $posted_settings - the settings to use
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function do_credentials_test_deletefile($testfile, $posted_settings) {
|
||||
$container_name = $posted_settings['container'];
|
||||
$directory = !empty($posted_settings['directory']) ? trailingslashit($posted_settings['directory']) : "";
|
||||
$storage = $this->get_storage();
|
||||
try {
|
||||
$storage->deleteBlob($container_name, $directory.$testfile);
|
||||
} catch (Exception $e) {
|
||||
echo esc_html(__('Delete failed:', 'updraftplus').' '.$e->getMessage().' ('.$e->getCode().', '.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method overrides the parent method and lists the supported features of this remote storage option.
|
||||
*
|
||||
* @return Array - an array of supported features (any features not mentioned are assumed to not be supported)
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
// This options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates', 'multi_storage', 'conditional_logic');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve default options for this remote storage module.
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
public function get_default_options() {
|
||||
return array(
|
||||
'account_name' => '',
|
||||
'key' => '',
|
||||
'container' => '',
|
||||
'endpoint' => 'blob.core.windows.net',
|
||||
);
|
||||
}
|
||||
|
||||
public function do_bootstrap($opts) {
|
||||
|
||||
// The Azure SDK requires PEAR modules - specifically, HTTP_Request2, Mail_mime, and Mail_mimeDecode; however, an analysis of the used code paths shows that we only need HTTP_Request2
|
||||
if (false === strpos(get_include_path(), UPDRAFTPLUS_DIR.'/includes/PEAR')) set_include_path(UPDRAFTPLUS_DIR.'/includes/PEAR'.PATH_SEPARATOR.get_include_path());
|
||||
updraft_try_include_file('includes/WindowsAzure/WindowsAzure.php', 'include_once');
|
||||
updraft_try_include_file('includes/azure-extensions.php', 'include_once');
|
||||
// use WindowsAzure\Common\ServicesBuilder;
|
||||
|
||||
// set up connection string
|
||||
// DefaultEndpointsProtocol=[http|https];AccountName=[yourAccount];AccountKey=[yourKey]
|
||||
if (empty($opts)) $opts = $this->get_options();
|
||||
|
||||
$protocol = isset($opts['nossl']) ? ($opts['nossl'] ? 'http' : 'https') : (UpdraftPlus_Options::get_updraft_option('updraft_ssl_nossl') ? 'http' : 'https');
|
||||
|
||||
$account_name = $opts['account_name'];
|
||||
$account_key = $opts['key'];
|
||||
|
||||
// Not implemented
|
||||
// $ssl_disableverify = isset($opts['ssl_disableverify']) ? $opts['ssl_disableverify'] : UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify');
|
||||
$ssl_useservercerts = isset($opts['ssl_useservercerts']) ? $opts['ssl_useservercerts'] : UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts');
|
||||
$ssl_ca_path = $ssl_useservercerts ? '' : UPDRAFTPLUS_DIR.'/includes/cacert.pem';
|
||||
|
||||
$connection_string = "DefaultEndpointsProtocol=$protocol;AccountName=$account_name;AccountKey=$account_key";
|
||||
// Non-standard element that our extended builder uses
|
||||
if ('https' == $protocol) $connection_string .=';SSLCAPath='.$ssl_ca_path;
|
||||
|
||||
$storage = $this->get_storage();
|
||||
$endpoint = empty($opts['endpoint']) ? 'blob.core.windows.net' : $opts['endpoint'];
|
||||
|
||||
if (empty($storage)) {
|
||||
try {
|
||||
$blob_rest_proxy = WindowsAzure\Common\UpdraftPlus_ServicesBuilder::getInstance()->createBlobService($connection_string, $endpoint);
|
||||
$storage = $blob_rest_proxy;
|
||||
$this->set_storage($storage);
|
||||
return $blob_rest_proxy;
|
||||
} catch (Exception $e) {
|
||||
return new WP_Error('blob_service_failed', 'Error when attempting to setup Azure access: '.$e->getMessage().' ('.$e->getCode().', '.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
}
|
||||
} else {
|
||||
return $storage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of container names. Currently unused method
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function list_containers() {
|
||||
$storage = $this->get_storage();
|
||||
try {
|
||||
$containers = $storage->listContainers();
|
||||
$container_list = $containers->getContainers();
|
||||
return $container_list;
|
||||
} catch (Exception $e) {
|
||||
return new WP_Error('container_list_failed', 'Could not list containers: '.$e->getMessage().' ('.$e->getCode().', '.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the container exists (using list_containers above) and if not creates the container. Returns the container properties.
|
||||
*
|
||||
* @param string $container_name The container name
|
||||
* @param boolean $create_on_404 Checks if need to create a 404
|
||||
* @return array
|
||||
*/
|
||||
protected function create_container($container_name, $create_on_404 = true) {
|
||||
$storage = $this->get_storage();
|
||||
try {
|
||||
$container_properties = $storage->getContainerProperties($container_name);
|
||||
return $container_properties;
|
||||
} catch (WindowsAzure\Common\ServiceException $e) {
|
||||
if ($create_on_404 && 404 == $e->getCode()) {
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return new WP_Error('container_create_failed', 'Could not create containers '.$e->getMessage().' ('.$e->getCode().', '.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
}
|
||||
|
||||
try {
|
||||
$create_container_options = new WindowsAzure\Blob\Models\CreateContainerOptions();
|
||||
$create_container_options->setPublicAccess(WindowsAzure\Blob\Models\PublicAccessType::NONE);
|
||||
// This does not return anything - it will throw an exception if there's a problem
|
||||
$storage->createContainer($container_name, $create_container_options);
|
||||
return $this->create_container($container_name, false);
|
||||
} catch (WindowsAzure\Common\ServiceException $e) {
|
||||
return new WP_Error('container_creation_failed', __('Could not create the container', 'updraftplus'));
|
||||
}
|
||||
|
||||
// Should not be possible to reach this point
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether options have been set up by the user, or not
|
||||
*
|
||||
* @param Array $opts - the potential options
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function options_exist($opts) {
|
||||
if (is_array($opts) && !empty($opts['account_name']) && !empty($opts['key'])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre configuration template
|
||||
*
|
||||
* @return String - the template
|
||||
*/
|
||||
public function get_pre_configuration_template() {
|
||||
?>
|
||||
<tr class="{{get_template_css_classes false}} azure_pre_config_container">
|
||||
<td colspan="2">
|
||||
<?php
|
||||
/*$site_host = parse_url(network_site_url(), PHP_URL_HOST);
|
||||
/*if ('127.0.0.1' == $site_host || '::1' == $site_host || 'localhost' == $site_host) {
|
||||
// Of course, there are other things that are effectively 127.0.0.1. This is just to help.
|
||||
$callback_text = '<p><strong>'.htmlspecialchars(sprintf(__('Microsoft Azure is not compatible with sites hosted on a localhost or 127.0.0.1 URL - their developer console forbids these (current URL is: %s).','updraftplus'), site_url())).'</strong></p>';
|
||||
} else {
|
||||
$callback_text = '<p>'.htmlspecialchars(__('You must add the following as the authorised redirect URI in your Azure console (under "API Settings") when asked','updraftplus')).': <kbd>'.UpdraftPlus_Options::admin_page_url().'</kbd></p>';
|
||||
}*/
|
||||
?>
|
||||
<img width="434" src="{{storage_image_url}}">
|
||||
{{{simplexmlelement_existence_label}}}
|
||||
<p><a href="https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade" target="_blank">{{credentials_creation_link_text}}</a></p>
|
||||
<p><a href="https://teamupdraft.com/documentation/updraftplus/topics/cloud-storage/microsoft-azure/how-to-add-microsoft-azure-to-your-updraftplus-account-settings?utm_source=udp-plugin&utm_medium=referral&utm_campaign=paac&utm_content=azure-instructions&utm_creative_format=text" target="_blank">{{configuration_helper_link_text}}</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
ob_start();
|
||||
?>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_account_name_label}}:</th>
|
||||
<td><input title="{{input_account_name_title}}" data-updraft_settings_test="account_name" type="text" autocomplete="off" id="{{get_template_input_attribute_value "id" "account_name"}}" name="{{get_template_input_attribute_value "name" "account_name"}}" value="{{account_name}}" class="updraft_input--wide udc-wd-600" /><br><em>{{input_account_name_title}}</em></td>
|
||||
</tr>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_key_label}}:</th>
|
||||
<td><input data-updraft_settings_test="key" type="{{input_key_type}}" autocomplete="off" class="updraft_input--wide udc-wd-600" id="{{get_template_input_attribute_value "id" "key"}}" name="{{get_template_input_attribute_value "name" "key"}}" value="{{key}}" /></td>
|
||||
</tr>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_container_label}}:</th>
|
||||
<td><input data-updraft_settings_test="container" title="" type="text" class="updraft_input--wide udc-wd-600" id="{{get_template_input_attribute_value "id" "container"}}" name="{{get_template_input_attribute_value "name" "container"}}" value="{{container}}"><br><a href="https://azure.microsoft.com/en-gb/documentation/articles/storage-php-how-to-use-blobs/" target="_blank"><em>{{input_container_link_text}}</a></em></td>
|
||||
</tr>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{{input_prefix_label}}}:</th>
|
||||
<td><input title="{{input_prefix_title}}" data-updraft_settings_test="directory" type="text" class="updraft_input--wide udc-wd-600" id="{{get_template_input_attribute_value "id" "directory"}}" name="{{get_template_input_attribute_value "name" "directory"}}" value="{{directory}}"></td>
|
||||
</tr>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_endpoint_label}}:</th>
|
||||
<td>
|
||||
<select data-updraft_settings_test="endpoint" id="{{get_template_input_attribute_value "id" "endpoint"}}" name="{{get_template_input_attribute_value "name" "endpoint"}}" style="width: 140px" class="updraft_input--wide udc-wd-600">
|
||||
{{#each input_endpoint_option_labels}}
|
||||
<option {{#ifeq ../endpoint @key}}selected="selected"{{/ifeq}} value="{{@key}}">{{this}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
{{{get_template_test_button_html "Azure"}}}
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of template properties by taking all the persistent variables and methods of the parent class and combining them with the ones that are unique to this module, also the necessary HTML element attributes and texts which are also unique only to this backup module
|
||||
* NOTE: Please sanitise all strings that are required to be shown as HTML content on the frontend side (i.e. wp_kses()), or any other technique to prevent XSS attacks that could come via WP hooks
|
||||
*
|
||||
* @return Array an associative array keyed by names that describe themselves as they are
|
||||
*/
|
||||
public function get_template_properties() {
|
||||
global $updraftplus_admin;
|
||||
$properties = array(
|
||||
'storage_image_url' => UPDRAFTPLUS_URL.'/images/azure.png',
|
||||
'simplexmlelement_existence_label' => !apply_filters('updraftplus_azure_simplexmlelement_exists', class_exists('SimpleXMLElement')) ? wp_kses($updraftplus_admin->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__("Your web server's PHP installation does not included a <strong>required</strong> (for %s) module (%s).", 'updraftplus'), 'Azure', 'php-xml - SimpleXMLElement').' '.__("Please contact your web hosting provider's support and ask for them to enable it.", 'updraftplus'), 'azure', false), $this->allowed_html_for_content_sanitisation()) : '',
|
||||
'credentials_creation_link_text' => __('Create Azure credentials in your Azure developer console.', 'updraftplus'),
|
||||
'configuration_helper_link_text' => __('For more detailed instructions, follow this link.', 'updraftplus'),
|
||||
'input_account_name_label' => sprintf(__('%s Account Name', 'updraftplus'), __('Azure', 'updraftplus')),
|
||||
'input_account_name_title' => __('This is not your Azure login - see the instructions if needing more guidance.', 'updraftplus'),
|
||||
'input_key_label' => sprintf(__('%s Key', 'updraftplus'), __('Azure', 'updraftplus')),
|
||||
'input_key_type' => apply_filters('updraftplus_admin_secret_field_type', 'password'),
|
||||
'input_container_label' => sprintf(__('%s Container', 'updraftplus'), __('Azure', 'updraftplus')),
|
||||
'input_container_title' => sprintf(__('Enter the path of the %s you wish to use here.', 'updraftplus'), 'container').' '.sprintf(__('If the %s does not already exist, then it will be created.'), 'container'),
|
||||
'input_container_link_text' => __("See Microsoft's guidelines on container naming by following this link.", 'updraftplus'),
|
||||
'input_prefix_label' => wp_kses(sprintf(__('%s Prefix', 'updraftplus'), __('Azure', 'updraftplus')).' <em>('.__('optional', 'updraftplus').')</em>', $this->allowed_html_for_content_sanitisation()),
|
||||
'input_prefix_title' => sprintf(__('You can enter the path of any %s virtual folder you wish to use here.', 'updraftplus'), 'Azure').' '.sprintf(__('If you leave it blank, then the backup will be placed in the root of your %s', 'updraftplus').'.', __('container', 'updraftplus')),
|
||||
'input_endpoint_label' => __('Azure Account', 'updraftplus'),
|
||||
'input_endpoint_option_labels' => array(
|
||||
'blob.core.windows.net' => __('Azure Global', 'updraftplus'),
|
||||
'blob.core.cloudapi.de' => __('Azure Germany', 'updraftplus'),
|
||||
'blob.core.usgovcloudapi.net' => __('Azure Government', 'updraftplus'),
|
||||
'blob.core.chinacloudapi.cn' => __('Azure China', 'updraftplus'),
|
||||
),
|
||||
'input_test_label' => sprintf(__('Test %s Settings', 'updraftplus'), 'Azure'),
|
||||
);
|
||||
return wp_parse_args($properties, $this->get_persistent_variables_and_methods());
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options
|
||||
*
|
||||
* @param array $opts - current options
|
||||
* @return array - Filtered handerbar template options
|
||||
*/
|
||||
protected function do_transform_options_for_template($opts) {
|
||||
$opts['container'] = empty($opts['container']) ? '' : strtolower($opts['container']);
|
||||
return $opts;
|
||||
}
|
||||
}
|
||||
|
||||
// Do *not* instantiate here; it is a storage module, so is instantiated on-demand
|
||||
// $updraftplus_addons_azure = new UpdraftPlus_Addons_RemoteStorage_azure;
|
||||
853
wp-content/plugins/updraftplus/addons/backblaze.php
Normal file
853
wp-content/plugins/updraftplus/addons/backblaze.php
Normal file
@@ -0,0 +1,853 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: backblaze:Backblaze Support
|
||||
Description: Backblaze Support
|
||||
Version: 1.3
|
||||
Shop: /shop/backblaze/
|
||||
Include: includes/backblaze
|
||||
IncludePHP: methods/addon-base-v2.php
|
||||
RequiresPHP: 5.3.3
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Error messages should be escaped when caught and printed.
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
if (!class_exists('UpdraftPlus_RemoteStorage_Addons_Base_v2')) updraft_try_include_file('methods/addon-base-v2.php', 'require_once');
|
||||
/**
|
||||
* Possible enhancements:
|
||||
* - Investigate porting to WP HTTP API so that curl is not required
|
||||
*/
|
||||
class UpdraftPlus_Addons_RemoteStorage_backblaze extends UpdraftPlus_RemoteStorage_Addons_Base_v2 {
|
||||
|
||||
/**
|
||||
* Maximum duration (in days) for an object can be locked (which is the maximum permitted by the Backblaze API)
|
||||
*/
|
||||
const MAX_OBJECT_LOCK_DURATION = 3000;
|
||||
|
||||
private $_large_file_id;
|
||||
|
||||
private $_sha1_of_parts;
|
||||
|
||||
private $_uploaded_size;
|
||||
|
||||
private $chunk_size = 5242880;
|
||||
|
||||
/**
|
||||
* Decides whether the upload bucket exists or not
|
||||
*
|
||||
* @var Boolean
|
||||
*/
|
||||
private $is_upload_bucket_exist;
|
||||
|
||||
/**
|
||||
* Variable to store bucket information.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $buckets = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
// 3rd parameter: chunking? 4th: Test button?
|
||||
parent::__construct('backblaze', 'Backblaze B2', true, true);
|
||||
// Set it any lower, and you will get an error when calling /b2_finish_large_file upon finishing: 400, Message: Part number 1 is smaller than 5000000 bytes"
|
||||
if (defined('UPDRAFTPLUS_UPLOAD_CHUNKSIZE') && UPDRAFTPLUS_UPLOAD_CHUNKSIZE > 0) $this->chunk_size = max(UPDRAFTPLUS_UPLOAD_CHUNKSIZE, 5000000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a single file
|
||||
*
|
||||
* @param String $file - the basename of the file to upload
|
||||
* @param String $local_path - the full path of the file
|
||||
*
|
||||
* @return Boolean - success status. Failures can also be thrown as exceptions.
|
||||
*/
|
||||
public function do_upload($file, $local_path) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$opts = $this->options;
|
||||
$storage = $this->get_storage();
|
||||
$instance_id = $this->get_instance_id();
|
||||
|
||||
if (is_wp_error($storage)) throw new Exception($storage->get_error_message());
|
||||
if (!is_object($storage)) throw new Exception("Backblaze service error (got a ".gettype($storage).")");
|
||||
|
||||
$backup_path = empty($opts['backup_path']) ? '' : trailingslashit($opts['backup_path']);
|
||||
$remote_path = $backup_path.$file;
|
||||
|
||||
$file_hash = md5($file);
|
||||
$this->_uploaded_size = $this->jobdata_get('total_bytes_sent_'.$file_hash, 0);
|
||||
|
||||
if (!file_exists($local_path) || !is_readable($local_path)) throw new Exception('Could not read file: '.$local_path);
|
||||
|
||||
// Backblaze bucket names are case insensitive
|
||||
$bucket_name = strtolower($opts['bucket_name']);
|
||||
// Create bucket if bucket doesn't exists
|
||||
if (!isset($this->is_upload_bucket_exist) && $this->is_valid_bucket_name($bucket_name)) {
|
||||
$buckets = $this->get_bucket_names_array();
|
||||
if (!in_array($bucket_name, $buckets)) {
|
||||
$new_bucket_created = $storage->createPrivateBucket($bucket_name);
|
||||
if ($new_bucket_created) {
|
||||
$this->is_upload_bucket_exist = true;
|
||||
$this->log("bucket was not found, but a new private bucket has now been created: ".$bucket_name);
|
||||
} else {
|
||||
$this->log("bucket was not found, and creation of a new private bucket failed: ".$bucket_name);
|
||||
}
|
||||
} else {
|
||||
$this->is_upload_bucket_exist = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (1 === ($ret = $updraftplus->chunked_upload($this, $file, "backblaze://".trailingslashit($bucket_name).$backup_path.$file, 'Backblaze', $this->chunk_size, $this->_uploaded_size))) {
|
||||
|
||||
if (!empty($opts['object_lock_duration'])) {
|
||||
$buckets = $this->get_bucket_names_array();
|
||||
|
||||
if (!in_array($bucket_name, $buckets)) $this->buckets[$instance_id] = $storage->listBuckets();
|
||||
|
||||
// Check if Object Lock is enabled for the bucket
|
||||
foreach ($this->buckets[$instance_id] as $bucket) {
|
||||
if ($bucket->getName() !== $bucket_name || $bucket->isObjectLockEnabled()) continue;
|
||||
// Update bucket to enable Object Lock
|
||||
$response = $storage->setObjectLockToBucket($bucket->getId());
|
||||
if (!isset($response['fileLockConfiguration']['value']['isFileLockEnabled']) || !$response['fileLockConfiguration']['value']['isFileLockEnabled']) {
|
||||
$this->log(sprintf(__('Error: unable to set object lock for bucket: %s', 'updraftplus'), $bucket->getName()), 'error');
|
||||
$this->log("Unable to set object lock for bucket: ".$bucket->getName());
|
||||
} else {
|
||||
$this->log("Object lock is enabled for bucket: ".$bucket->getName());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$result = $storage->upload(array(
|
||||
'BucketName' => $opts['bucket_name'],
|
||||
'FileName' => $remote_path,
|
||||
'Body' => file_get_contents($local_path),
|
||||
));
|
||||
|
||||
if (is_object($result) && is_callable(array($result, 'getSize')) && $result->getSize() > 1) {
|
||||
$ret = true;
|
||||
|
||||
$this->maybe_set_object_lock($opts, $result);
|
||||
} else {
|
||||
$ret = false;
|
||||
$this->log("all-in-one upload fail: ".serialize($result));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* N.B. If we ever use varying-size chunks, we must be careful as to what we do with $chunk_index
|
||||
*
|
||||
* @param String $file - Basename for the file being uploaded
|
||||
* @param Resource|String $fp - Data to send, or a file handle to read upload data from
|
||||
* @param Integer $chunk_index - Index of chunked upload
|
||||
* @param Integer $upload_size - Size of the upload, in bytes (this and the next are only used if a resource was given for $fp)
|
||||
* @param Integer $upload_start - How many bytes into the file the upload process has got
|
||||
* @param Integer $upload_end - How many bytes into the file we will be after this chunk is uploaded (not currently used)
|
||||
* @param Integer $total_file_size - Total file size (not currently used)
|
||||
*
|
||||
* @return Boolean|WP_Error
|
||||
*/
|
||||
public function chunked_upload($file, $fp, $chunk_index, $upload_size = 0, $upload_start = 0, $upload_end = 0, $total_file_size = 0) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Filter use
|
||||
|
||||
// Already done? This is not checked if we are sent data directly, as that implies forcing.
|
||||
if (is_resource($fp) && $upload_start < $this->_uploaded_size) return 1;
|
||||
|
||||
$storage = $this->get_storage();
|
||||
if (is_wp_error($storage)) return $storage;
|
||||
if (!is_object($storage)) return new WP_Error('no_backblaze_service', "Backblaze service error (got a ".gettype($storage).")");
|
||||
|
||||
$file_hash = md5($file);
|
||||
|
||||
$upload_state = $this->jobdata_get('upload_state_'.$file_hash, array());
|
||||
// An upload URL is valid for 24 hours. But, we'll only use them for 1 hour, in case something else happens to invalidate it (we don't want to wait a whole day before getting a new one).
|
||||
if (!empty($upload_state['saved_at']) && $upload_state['saved_at'] < time() - 3600) $upload_state = array();
|
||||
|
||||
$large_file_id = empty($upload_state['large_file_id']) ? false : $upload_state['large_file_id'];
|
||||
$upload_url = empty($upload_state['upload_url']) ? false : $upload_state['upload_url'];
|
||||
$auth_token = empty($upload_state['auth_token']) ? false : $upload_state['auth_token'];
|
||||
$need_new_state = ($large_file_id && $upload_url && $auth_token) ? false : true;
|
||||
|
||||
$opts = $this->options;
|
||||
$backup_path = empty($opts['backup_path']) ? '' : trailingslashit($opts['backup_path']);
|
||||
$remote_path = $backup_path.$file;
|
||||
|
||||
if (!$large_file_id) {
|
||||
$this->log("initiating multi-part upload");
|
||||
try {
|
||||
$response = $storage->uploadLargeStart(array(
|
||||
'FileName' => $remote_path,
|
||||
'BucketName' => $opts['bucket_name'],
|
||||
));
|
||||
|
||||
if (empty($response['fileId'])) {
|
||||
$this->log('Unexpected response to uploadLargeStart: '.serialize($response));
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->log('Unexpected chunk uploading exception ('.get_class($e).'): '.$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
return false;
|
||||
}
|
||||
|
||||
$large_file_id = $response['fileId'];
|
||||
|
||||
}
|
||||
|
||||
$this->_large_file_id = $large_file_id;
|
||||
|
||||
if (!$upload_url || !$auth_token) {
|
||||
try {
|
||||
$this->log("requesting multi-part file upload url (id $large_file_id)");
|
||||
$response = $storage->uploadLargeUrl(array(
|
||||
'FileId' => $large_file_id,
|
||||
));
|
||||
if (empty($response['authorizationToken']) || empty($response['uploadUrl'])) {
|
||||
$this->log('Unexpected response to uploadLargeUrl: '.serialize($response));
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->log('Unexpected error when getting upload URL ('.get_class($e).'): '.$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
return false;
|
||||
}
|
||||
$auth_token = $response['authorizationToken'];
|
||||
$upload_url = $response['uploadUrl'];
|
||||
}
|
||||
|
||||
if ($need_new_state) {
|
||||
$this->jobdata_set('upload_state_'.$file_hash, array(
|
||||
'large_file_id' => $large_file_id,
|
||||
'upload_url' => $upload_url,
|
||||
'auth_token' => $auth_token,
|
||||
// N.B. An upload URL is valid for 24 hours
|
||||
'saved_at' => time()
|
||||
));
|
||||
}
|
||||
|
||||
if (is_resource($fp)) {
|
||||
if (false === ($data = fread($fp, $upload_size))) {
|
||||
$this->log(__('Error: unexpected file read fail', 'updraftplus'), 'error');
|
||||
$this->log("File read fail (fread() returned false)");
|
||||
return false;
|
||||
}
|
||||
} elseif (is_string($fp)) {
|
||||
$data = $fp;
|
||||
} else {
|
||||
return new WP_Error('backblaze_chunk_data_error', __('Error:', 'updraftplus')." backblaze::chunked_upload() received invalid input");
|
||||
}
|
||||
|
||||
$sha1_of_parts = $this->jobdata_get('sha1_of_parts_'.$file_hash, array());
|
||||
$sha1_of_parts[$chunk_index - 1] = sha1($data);
|
||||
|
||||
try {
|
||||
$response = $storage->uploadLargePart(array(
|
||||
'AuthorizationToken' => $auth_token,
|
||||
'FilePartNo' => $chunk_index,
|
||||
'UploadUrl' => $upload_url,
|
||||
'Body' => $data,
|
||||
));
|
||||
if (!is_array($response) || !isset($response['partNumber'])) {
|
||||
$this->log("Unexpected response to uploadLargePart: ".serialize($response));
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
if ($e->getCode() >= 500 && $e->getCode() <= 599) {
|
||||
$this->jobdata_set('upload_state_'.$file_hash, array(
|
||||
'large_file_id' => $large_file_id,
|
||||
'upload_url' => '',
|
||||
'auth_token' => '',
|
||||
));
|
||||
}
|
||||
return new WP_Error('backblaze_chunk_upload_error', __('Error:', 'updraftplus')." {$e->getCode()}, Message: {$e->getMessage()}");
|
||||
}
|
||||
|
||||
$this->_sha1_of_parts = $sha1_of_parts;
|
||||
$this->jobdata_set('sha1_of_parts_'.$file_hash, $sha1_of_parts);
|
||||
|
||||
$this->jobdata_set('total_bytes_sent_'.$file_hash, $upload_end + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when all chunks have been uploaded, to allow any required finishing actions to be carried out
|
||||
*
|
||||
* @param String $file - the basename of the file being uploaded
|
||||
*
|
||||
* @return Integer|Boolean - success or failure state of any finishing actions
|
||||
*/
|
||||
public function chunked_upload_finish($file) {
|
||||
|
||||
$file_hash = md5($file);
|
||||
$opts = $this->options;
|
||||
$storage = $this->get_storage();
|
||||
|
||||
// This happens if chunked_upload_finish is called without chunked_upload having been called
|
||||
if (empty($this->_large_file_id)) {
|
||||
|
||||
$upload_state = $this->jobdata_get('upload_state_'.$file_hash, array());
|
||||
|
||||
// An upload URL is valid for 24 hours. But, we'll only use them for 1 hour, in case something else happens to invalidate it (we don't want to wait a whole day before getting a new one).
|
||||
if (!empty($upload_state['saved_at']) && $upload_state['saved_at'] < time() - 3600) $upload_state = array();
|
||||
|
||||
$this->_large_file_id = empty($upload_state['large_file_id']) ? false : $upload_state['large_file_id'];
|
||||
|
||||
$this->_sha1_of_parts = $this->jobdata_get('sha1_of_parts_'.$file_hash, array());
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $storage->uploadLargeFinish(array(
|
||||
'FileId' => $this->_large_file_id,
|
||||
'FilePartSha1Array' => $this->_sha1_of_parts,
|
||||
));
|
||||
|
||||
$this->maybe_set_object_lock($opts, $response);
|
||||
} catch (Exception $e) {
|
||||
global $updraftplus;
|
||||
|
||||
if (preg_match('/No active upload for: .*/', $e->getMessage())) {
|
||||
$this->log("upload: b2_finish_large_file has already been called ('".$e->getMessage()."')");
|
||||
return 1;
|
||||
} elseif (preg_match('/Part number (\d+) has not been uploaded/i', $e->getMessage(), $matches)) {
|
||||
$missing_chunk_index = $matches[1];
|
||||
$this->log("Exception in uploadLargeFinish(); will retry part $missing_chunk_index: {$e->getCode()}, Message: {$e->getMessage()} (line: {$e->getLine()}, file: {$e->getFile()})");
|
||||
$updraft_dir = $updraftplus->backups_dir_location();
|
||||
|
||||
// If more than this are needed, they will happen on the next resumption
|
||||
static $retries = 12;
|
||||
|
||||
if (false === ($data = file_get_contents($updraft_dir.'/'.$file, false, null, ($missing_chunk_index - 1 ) * $this->chunk_size, $this->chunk_size))) {
|
||||
$retry_part = new WP_Error('file_read_failed', "Could not read: $file");
|
||||
} elseif ($retries > 0) {
|
||||
$retries--;
|
||||
$retry_part = $this->chunked_upload($file, $data, $missing_chunk_index);
|
||||
// Missing part was uploaded; try the whole again
|
||||
if (true === $retry_part) {
|
||||
return $this->chunked_upload_finish($file);
|
||||
}
|
||||
// N.B. chunked_upload() does its own logging when returning false
|
||||
}
|
||||
|
||||
if (is_wp_error($retry_part)) {
|
||||
$this->log("Failed ".$retry_part->get_error_code().": ".$retry_part->get_error_message());
|
||||
}
|
||||
} else {
|
||||
$this->log("Exception in uploadLargeFinish(): {$e->getCode()}, Message: {$e->getMessage()} (line: {$e->getLine()}, file: {$e->getFile()})");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
global $updraftplus;
|
||||
$this->log('upload: success (b2_finish_large_file called successfully; chunks='.count($this->_sha1_of_parts).', file ID returned='.$response->getId().', size='.$response->getSize().')');
|
||||
|
||||
// Clean-up
|
||||
$this->jobdata_delete('upload_state_'.$file_hash);
|
||||
$this->jobdata_delete('sha1_of_parts_'.$file_hash);
|
||||
|
||||
// (int)1 means 'we already logged', as opposed to (boolean)true which does not
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a download of the requested file
|
||||
*
|
||||
* @param String $file - the file (basename) to download
|
||||
* @param String $fullpath - the full path to download it too
|
||||
* @param Integer $start_offset - byte marker to begin at (starting from 0)
|
||||
*
|
||||
* @return Boolean|Integer - success/failure, or a byte counter of how much has been downloaded. Exceptions can also be thrown for errors.
|
||||
*/
|
||||
public function do_download($file, $fullpath) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Filter use
|
||||
global $updraftplus;
|
||||
|
||||
$remote_files = $this->do_listfiles($file);
|
||||
|
||||
if (is_wp_error($remote_files)) {
|
||||
throw new Exception('Download error ('.$remote_files->get_error_code().'): '.$remote_files->get_error_message());
|
||||
}
|
||||
|
||||
foreach ($remote_files as $file_info) {
|
||||
if ($file_info['name'] == $file) {
|
||||
return $updraftplus->chunked_download($file, $this, $file_info['size'], true, null, 2*1048576);
|
||||
}
|
||||
}
|
||||
|
||||
$this->log("$file: file not found in listing of remote directory");
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used by by chunked downloading API
|
||||
*
|
||||
* @param String $file - the file (basename) to be downloaded
|
||||
* @param Array $headers - supplied headers
|
||||
* @return String - the data downloaded
|
||||
*/
|
||||
public function chunked_download($file, $headers) {
|
||||
|
||||
// $curl_options = array();
|
||||
// if (is_array($headers) && !empty($headers['Range']) && preg_match('/bytes=(.*)$/', $headers['Range'], $matches)) {
|
||||
// $curl_options[CURLOPT_RANGE] = $matches[1];
|
||||
|
||||
$opts = $this->options;
|
||||
$storage = $this->get_storage();
|
||||
|
||||
$backup_path = empty($opts['backup_path']) ? '' : trailingslashit($opts['backup_path']);
|
||||
|
||||
$options = array(
|
||||
'BucketName' => $opts['bucket_name'],
|
||||
'FileName' => $backup_path.$file,
|
||||
);
|
||||
|
||||
if (!empty($headers)) $options['headers'] = $headers;
|
||||
|
||||
$remote_file = $storage->download($options);
|
||||
|
||||
return is_string($remote_file) ? $remote_file : false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts as a WordPress options filter
|
||||
*
|
||||
* @param Array $settings - pre-filtered settings
|
||||
*
|
||||
* @return Array filtered settings
|
||||
*/
|
||||
public function options_filter($settings) {
|
||||
if (is_array($settings) && !empty($settings['version']) && !empty($settings['settings'])) {
|
||||
foreach ($settings['settings'] as $instance_id => $instance_settings) {
|
||||
if (!empty($instance_settings['backup_path'])) {
|
||||
$settings['settings'][$instance_id]['backup_path'] = trim($instance_settings['backup_path'], "/ \t\n\r\0\x0B");
|
||||
}
|
||||
if (!empty($instance_settings['object_lock_duration']) && $instance_settings['object_lock_duration'] > self::MAX_OBJECT_LOCK_DURATION) {
|
||||
$settings['settings'][$instance_id]['object_lock_duration'] = self::MAX_OBJECT_LOCK_DURATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an indicated file from remote storage
|
||||
*
|
||||
* @param Array $files - the files (basename) to delete
|
||||
*
|
||||
* @return Boolean|Array - success/failure status of the delete operation. Throwing exception is also permitted.
|
||||
*/
|
||||
public function do_delete($files) {
|
||||
|
||||
$opts = $this->options;
|
||||
|
||||
$storage = $this->get_storage();
|
||||
|
||||
$backup_path = empty($opts['backup_path']) ? '' : trailingslashit($opts['backup_path']);
|
||||
|
||||
try {
|
||||
if (count($files) > 1) {
|
||||
$multipleFiles = array();
|
||||
foreach ($files as $file) {
|
||||
$multipleFiles[] = array(
|
||||
'FileName' => $backup_path.$file,
|
||||
'BucketName' => $opts['bucket_name']
|
||||
);
|
||||
}
|
||||
$result = $storage->deleteMultipleFiles($multipleFiles, $opts['bucket_name'], $backup_path);
|
||||
} else {
|
||||
$fileName = $files[0];
|
||||
$result = $storage->deleteFile(array(
|
||||
'FileName' => $backup_path.$fileName,
|
||||
'BucketName' => $opts['bucket_name'],
|
||||
));
|
||||
}
|
||||
} catch (UpdraftPlus_Backblaze_NotFoundException $e) {
|
||||
// This exception should only be possible on the single file delete path
|
||||
$this->log("$fileName: file not found (so likely already deleted)");
|
||||
return true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to get a list of backup files for the remote storage option
|
||||
*
|
||||
* @param String $match - a string to match when looking for files
|
||||
*
|
||||
* @return Array|WP_Error - returns an array of files (arrays with keys 'name' (basename) and (optional) 'size' (in bytes)) or a WordPress error. Throwing an exception is also allowed.
|
||||
*/
|
||||
public function do_listfiles($match = 'backup_') {
|
||||
$opts = $this->get_options();
|
||||
$storage = $this->get_storage();
|
||||
|
||||
// When listing, paths in the root must not begin with a slash
|
||||
$backup_path = empty($opts['backup_path']) ? '' : trailingslashit($opts['backup_path']);
|
||||
|
||||
try {
|
||||
$remote_files = $storage->listFiles(array(
|
||||
'BucketName' => $opts['bucket_name'],
|
||||
'Prefix' => $backup_path.$match
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
return new WP_Error('backblaze_list_error', $e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
}
|
||||
|
||||
if (is_wp_error($remote_files)) return $remote_files;
|
||||
|
||||
$files = array();
|
||||
|
||||
foreach ($remote_files as $file) {
|
||||
$file_name = $file->getName();
|
||||
if ($backup_path && 0 !== strpos($file_name, $backup_path)) continue;
|
||||
$files[] = array(
|
||||
'name' => substr($file_name, strlen($backup_path)),
|
||||
'size' => $file->getSize(),
|
||||
// 'fid' => $file->getId(),
|
||||
);
|
||||
}
|
||||
|
||||
return $files;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of parameters required to be present for a credential tests, plus descriptions
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function get_credentials_test_required_parameters() {
|
||||
return array(
|
||||
'account_id' => __('Account ID', 'updraftplus'),
|
||||
'key' => __('Account Key', 'updraftplus')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a credentials test. Output can be echoed.
|
||||
*
|
||||
* @param String $testfile - basename to use for the test
|
||||
* @param Array $posted_settings - settings to use
|
||||
*
|
||||
* @return Array - 'result' indicating a success/failure status, and 'data' with returned data
|
||||
*/
|
||||
protected function do_credentials_test($testfile, $posted_settings = array()) {
|
||||
|
||||
$bucket_name = strtolower($posted_settings['bucket_name']);
|
||||
|
||||
$result = false;
|
||||
$data = null;
|
||||
$storage = $this->get_storage();
|
||||
|
||||
try {
|
||||
if (!$this->is_valid_bucket_name($bucket_name)) {
|
||||
echo esc_html(__('Invalid bucket name', 'updraftplus'))."\n";
|
||||
} else {
|
||||
$buckets = $this->get_bucket_names_array();
|
||||
$new_bucket_created = false;
|
||||
if (!in_array($bucket_name, $buckets)) {
|
||||
$new_bucket_created = $storage->createPrivateBucket($bucket_name);
|
||||
}
|
||||
|
||||
if (in_array($bucket_name, $buckets) || $new_bucket_created) {
|
||||
$backup_path = empty($posted_settings['backup_path']) ? '' : trailingslashit($posted_settings['backup_path']);
|
||||
|
||||
// Now try to write
|
||||
$result = $storage->upload(array(
|
||||
'BucketName' => $bucket_name,
|
||||
'FileName' => $backup_path.$testfile,
|
||||
'Body' => 'This is a test file resulting from pressing the "Test" button in UpdraftPlus, https://updraftplus.com. If it is still here afterwards, then something went wrong deleting it - you should delete it manually.',
|
||||
));
|
||||
|
||||
if (is_object($result) && is_callable(array($result, 'getSize')) && $result->getSize() > 1) {
|
||||
$result = true;
|
||||
}
|
||||
} elseif (!$new_bucket_created) {
|
||||
echo esc_html(__('Failure: We could not successfully access or create such a bucket', 'updraftplus').' '.sprintf(__('Please check your access credentials, and if those are correct then try another bucket name (as another %s user may already have taken your name).', 'updraftplus'), 'Backblaze'));
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo esc_html(get_class($e).': '.$e->getMessage().' ('.$e->getCode().', '.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile()).")\n";
|
||||
}
|
||||
|
||||
return array('result' => $result, 'data' => $data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a temporary file use for a credentials test. Output can be echo-ed.
|
||||
*
|
||||
* @param String $testfile - the basename of the file to delete
|
||||
* @param Array $posted_settings - the settings to use
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function do_credentials_test_deletefile($testfile, $posted_settings) {
|
||||
|
||||
try {
|
||||
$backup_path = empty($posted_settings['backup_path']) ? '' : trailingslashit($posted_settings['backup_path']);
|
||||
$storage = $this->get_storage();
|
||||
|
||||
$storage->deleteFile(array(
|
||||
'FileName' => $backup_path.$testfile,
|
||||
'BucketName' => strtolower($posted_settings['bucket_name']),
|
||||
));
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo esc_html(__('Delete failed:', 'updraftplus').' '.$e->getMessage().' ('.$e->getCode().', '.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of supported features for this storage method
|
||||
* This method should be over-ridden by methods supporting new
|
||||
* features.
|
||||
*
|
||||
* @see UpdraftPlus_BackupModule::get_supported_features()
|
||||
*
|
||||
* @return Array - an array of supported features (any features not
|
||||
* mentioned are assumed to not be supported)
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
// This options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates', 'multi_storage', 'conditional_logic', 'multi_delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve default options for this remote storage module.
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
public function get_default_options() {
|
||||
return array(
|
||||
'account_id' => '',
|
||||
'key' => '',
|
||||
'bucket_name' => '',
|
||||
'backup_path' => '',
|
||||
'single_bucket_key_id' => '',
|
||||
'object_lock_duration' => 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any boot-strapping functions, and return a client instance
|
||||
*
|
||||
* @param Array $opts - instance options
|
||||
* @param Boolean $connect - whether to also set up a connection (if supported by this method)
|
||||
*
|
||||
* @return UpdraftPlus_Backblaze_CurlClient|WP_Error - the storage object. It should also be stored as $this->storage.
|
||||
*/
|
||||
public function do_bootstrap($opts) {
|
||||
$storage = $this->get_storage();
|
||||
|
||||
if (!empty($storage) && !is_wp_error($storage)) return $storage;
|
||||
|
||||
try {
|
||||
|
||||
if (!is_array($opts)) $opts = $this->get_options();
|
||||
|
||||
if (!class_exists('UpdraftPlus_Backblaze_CurlClient')) updraft_try_include_file('includes/Backblaze/CurlClient.php', 'include_once');
|
||||
|
||||
if (empty($opts['account_id']) || empty($opts['key'])) return new WP_Error('no_settings', __('No settings were found', 'updraftplus').' (Backblaze)');
|
||||
|
||||
$backblaze_options = array(
|
||||
'ssl_verify' => empty($opts['disableverify']),
|
||||
'ssl_ca_certs' => empty($opts['useservercerts']) ? UPDRAFTPLUS_DIR.'/includes/cacert.pem' : false
|
||||
);
|
||||
|
||||
$storage = new UpdraftPlus_Backblaze_CurlClient($opts['account_id'], $opts['key'], $opts['single_bucket_key_id'], $backblaze_options);
|
||||
|
||||
$this->set_storage($storage);
|
||||
|
||||
} catch (Exception $e) {
|
||||
return new WP_Error('blob_service_failed', 'Error when attempting to setup Backblaze access (please check your credentials): '.$e->getMessage().' ('.$e->getCode().', '.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
}
|
||||
|
||||
return $storage;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether options have been set up by the user, or not
|
||||
*
|
||||
* @param Array $opts - the potential options
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function options_exist($opts) {
|
||||
if (is_array($opts) && !empty($opts['account_id']) && !empty($opts['key'])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre configuration template
|
||||
*
|
||||
* @return String - the template
|
||||
*/
|
||||
public function get_pre_configuration_template() {
|
||||
?>
|
||||
<tr class="{{get_template_css_classes false}} backblaze_pre_config_container">
|
||||
<td colspan="2">
|
||||
<img width="434" src="{{storage_image_url}}"><br>
|
||||
{{{curl_existence_label}}}
|
||||
<p><a href="https://teamupdraft.com/documentation/updraftplus/topics/cloud-storage/backblaze/faqs/how-to-configure-backblaze-cloud-storage-access-in-updraftplus/?utm_source=udp-plugin&utm_medium=referral&utm_campaign=paac&utm_content=configure-backblaze&utm_creative_format=text" target="_blank"><strong>{{configuration_helper_link_text}}</strong></a></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
ob_start();
|
||||
?>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_key_id_label}}:</th>
|
||||
<td><input type="text" size="40" data-updraft_settings_test="account_id" id="{{get_template_input_attribute_value "id" "account_id"}}" name="{{get_template_input_attribute_value "name" "account_id"}}" value="{{account_id}}"><br>
|
||||
<em>{{{input_key_id_title}}}</em><br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_application_key_label}}:</th>
|
||||
<td><input type="{{input_application_key_type}}" size="40" data-updraft_settings_test="key" id="{{get_template_input_attribute_value "id" "key"}}" name="{{get_template_input_attribute_value "name" "key"}}" value="{{key}}" /></td>
|
||||
</tr>
|
||||
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_bucket_key_id_label}}:</th>
|
||||
<td><input title="{{input_bucket_key_id_title}}" type="text" size="40" data-updraft_settings_test="single_bucket_key_id" id="{{get_template_input_attribute_value "id" "single_bucket_key_id"}}" name="{{get_template_input_attribute_value "name" "single_bucket_key_id"}}" value="{{single_bucket_key_id}}"><br>
|
||||
<em>{{input_bucket_key_id_title}}</em><br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_backup_path_label}}:</th>
|
||||
<td>/<input type="text" size="19" maxlength="50" placeholder="{{input_backup_path_name_placeholder}}" data-updraft_settings_test="bucket_name" id="{{get_template_input_attribute_value "id" "bucket_name"}}" name="{{get_template_input_attribute_value "name" "bucket_name"}}" value="{{bucket_name}}" />/<input type="text" size="19" maxlength="200" placeholder="{{input_backup_path_some_path_placeholder}}" data-updraft_settings_test="backup_path" id="{{get_template_input_attribute_value "id" "backup_path"}}" name="{{get_template_input_attribute_value "name" "backup_path"}}" value="{{backup_path}}" /><br>
|
||||
<em>{{{input_backup_path_title}}}</em><br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_object_lock_label}}:</th>
|
||||
<td><input type="number" min="0" step="1" max="{{input_object_lock_max_value}}" style="width:70px;" data-updraft_settings_test="object_lock_duration" id="{{get_template_input_attribute_value "id" "object_lock_duration"}}" name="{{get_template_input_attribute_value "name" "object_lock_duration"}}"
|
||||
{{#if object_lock_duration}}
|
||||
value="{{object_lock_duration}}" />
|
||||
{{else}}
|
||||
value="0" />
|
||||
{{/if}}
|
||||
<br>
|
||||
<em>{{input_object_lock_title}} <span style="color:red">{{input_object_lock_warning}}</span>{{{read_more_object_lock}}}</em>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{{{get_template_test_button_html "Backblaze"}}}
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of template properties by taking all the persistent variables and methods of the parent class and combining them with the ones that are unique to this module, also the necessary HTML element attributes and texts which are also unique only to this backup module
|
||||
* NOTE: Please sanitise all strings that are required to be shown as HTML content on the frontend side (i.e. wp_kses()), or any other technique to prevent XSS attacks that could come via WP hooks
|
||||
*
|
||||
* @return Array an associative array keyed by names that describe themselves as they are
|
||||
*/
|
||||
public function get_template_properties() {
|
||||
global $updraftplus, $updraftplus_admin;
|
||||
$properties = array(
|
||||
'storage_image_url' => UPDRAFTPLUS_URL.'/images/backblaze.png',
|
||||
'curl_existence_label' => wp_kses($updraftplus_admin->curl_check('Backblaze B2', false, 'backblaze hidden-in-updraftcentral', false), $this->allowed_html_for_content_sanitisation()),
|
||||
'configuration_helper_link_text' => sprintf(__('For help configuring %s, including screenshots, follow this link.', 'updraftplus'), 'Backblaze'),
|
||||
'input_key_id_label' => __('Master Application Key ID', 'updraftplus'),
|
||||
'input_key_id_title' => sprintf(__('Get these settings from %s, or sign up %s.', 'updraftplus'), '<a aria-label="secure.backblaze.com/b2_buckets.htm" target="_blank" href="https://secure.backblaze.com/b2_buckets.htm">'.__('here', 'updraftplus').'</a>', '<a aria-label="www.backblaze.com/b2/" target="_blank" href="https://www.backblaze.com/b2/">'.__('here', 'updraftplus').'</a>'),
|
||||
'input_application_key_label' => __('Application key', 'updraftplus'),
|
||||
'input_application_key_type' => apply_filters('updraftplus_admin_secret_field_type', 'password'),
|
||||
'input_bucket_key_id_label' => __('Bucket application key ID', 'updraftplus'),
|
||||
'input_bucket_key_id_title' => __('This is needed if, and only if, your application key was a bucket-specific application key (not a master key)', 'updraftplus'),
|
||||
'input_backup_path_label' => __('Backup path', 'updraftplus'),
|
||||
'input_object_lock_max_value' => self::MAX_OBJECT_LOCK_DURATION,
|
||||
'input_object_lock_label' => __('Object lock duration (days)', 'updraftplus'),
|
||||
'input_object_lock_title' => __('Object lock is a Backblaze B2 feature that prevents data from being changed or deleted for a given number of days.', 'updraftplus').' '.__('Use this to protect your data from hackers or for regulatory compliance reasons.', 'updraftplus').' '.__('0 days means no lock is applied.', 'updraftplus'),
|
||||
'read_more_object_lock' => ' <a target="_blank" href="https://www.backblaze.com/docs/cloud-storage-object-lock">'.__('Read more about the Backblaze Object Lock', 'updraftplus').'</a>.',
|
||||
'input_object_lock_warning' => __('A file which is locked cannot be deleted by any means until the lock time duration has expired.', 'updraftplus'),
|
||||
'input_backup_path_name_placeholder' => __('Bucket name', 'updraftplus'),
|
||||
'input_backup_path_title' => '<a target="_blank" href="https://help.backblaze.com/hc/en-us/articles/217666908-What-you-need-to-know-about-B2-Bucket-names">'.__('There are limits upon which path-names are valid.', 'updraftplus').' '.__('Spaces are not allowed.', 'updraftplus').'</a>',
|
||||
'input_backup_path_some_path_placeholder' => __('some/path', 'updraftplus'),
|
||||
'input_test_label' => sprintf(__('Test %s Settings', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]),
|
||||
);
|
||||
return wp_parse_args($properties, $this->get_persistent_variables_and_methods());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bucket name list array for current storage instance
|
||||
*
|
||||
* @return array Which contains bucket names as element values
|
||||
*/
|
||||
protected function get_bucket_names_array() {
|
||||
$bucket_names = array();
|
||||
$storage = $this->get_storage();
|
||||
$instance_id = $this->get_instance_id();
|
||||
if (empty($this->buckets[$instance_id])) $this->buckets[$instance_id] = $storage->listBuckets();
|
||||
|
||||
if (!empty($this->buckets[$instance_id]) && is_array($this->buckets[$instance_id])) {
|
||||
foreach ($this->buckets[$instance_id] as $bucket) {
|
||||
$bucket_names[] = $bucket->getName();
|
||||
}
|
||||
}
|
||||
return $bucket_names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether bucket name is valid as per backblaze standards
|
||||
*
|
||||
* @param string $bucket_name Backblaze bucket name
|
||||
* @return boolean If bucket name is valid, it returns true. Otherwise false
|
||||
*/
|
||||
protected function is_valid_bucket_name($bucket_name) {
|
||||
return preg_match('/^(?!b2-)[-0-9a-z]{6,50}$/i', $bucket_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to set an object lock for a file based on the provided options.
|
||||
*
|
||||
* @param array $opts An array of options.
|
||||
* @param object $file The file object on which the object lock will be set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function maybe_set_object_lock($opts, $file) {
|
||||
if (!empty($opts['object_lock_duration'])) {
|
||||
// Attempt to set the object lock for the file.
|
||||
$storage = $this->get_storage();
|
||||
$object_lock_response = $storage->setObjectLock($file->getId(), $file->getName(), $opts['object_lock_duration']);
|
||||
|
||||
if (is_array($object_lock_response) && !empty($object_lock_response['fileRetention']['retainUntilTimestamp'])) {
|
||||
// Object lock set successfully. Log the information.
|
||||
$retain_date = get_date_from_gmt(date('M d, Y G:i', $object_lock_response['fileRetention']['retainUntilTimestamp'] / 1000), 'M d, Y G:i');
|
||||
$this->log(sprintf('The file named %s has been successfully locked for %d day(s) and the lock will expire on %s.', $file->getName(), $opts['object_lock_duration'], $retain_date));
|
||||
} else {
|
||||
// Failed to set object lock. Log an error.
|
||||
$this->log(sprintf(__('Error: unable to set object lock for file: %s', 'updraftplus'), $file->getName()), 'error');
|
||||
$this->log("Unable to set object lock for file: ".$file->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
424
wp-content/plugins/updraftplus/addons/cloudfiles-enhanced.php
Normal file
424
wp-content/plugins/updraftplus/addons/cloudfiles-enhanced.php
Normal file
@@ -0,0 +1,424 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: cloudfiles-enhanced:Rackspace Cloud Files, enhanced
|
||||
Description: Adds enhanced capabilities for Rackspace Cloud Files users
|
||||
Version: 1.8
|
||||
RequiresPHP: 5.3.3
|
||||
Shop: /shop/cloudfiles-enhanced/
|
||||
Latest Change: 1.16.19
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
// Future possibility: sub-folders
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
// The new Rackspace SDK is PHP 5.3.3 or later
|
||||
if (version_compare(phpversion(), '5.3.3', '<') || (defined('UPDRAFTPLUS_CLOUDFILES_USEOLDSDK') && UPDRAFTPLUS_CLOUDFILES_USEOLDSDK)) return;
|
||||
|
||||
use OpenCloud\Rackspace;
|
||||
|
||||
$updraftplus_addon_cloudfilesenhanced = new UpdraftPlus_Addon_CloudFilesEnhanced;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Unused variable $updraftplus_addon_cloudfilesenhanced But it is used Globally in class-commands.php so ignoring
|
||||
|
||||
class UpdraftPlus_Addon_CloudFilesEnhanced {
|
||||
|
||||
private $accounts;
|
||||
|
||||
private $regions;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action('updraftplus_settings_page_init', array($this, 'updraftplus_settings_page_init'));
|
||||
add_action('updraft_cloudfiles_newuser', array($this, 'newuser'));
|
||||
add_filter('updraft_cloudfiles_apikeysetting', array($this, 'apikeysettings'));
|
||||
add_filter('updraft_cloudfiles_template_properties', array($this, 'partial_template_properties'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the WP action updraftplus_settings_page_init
|
||||
*/
|
||||
public function updraftplus_settings_page_init() {
|
||||
|
||||
$this->accounts = array(
|
||||
'us' => __('US (default)', 'updraftplus'),
|
||||
'uk' => __('UK', 'updraftplus')
|
||||
);
|
||||
|
||||
$this->regions = array(
|
||||
'DFW' => __('Dallas (DFW) (default)', 'updraftplus'),
|
||||
'SYD' => __('Sydney (SYD)', 'updraftplus'),
|
||||
'ORD' => __('Chicago (ORD)', 'updraftplus'),
|
||||
'IAD' => __('Northern Virginia (IAD)', 'updraftplus'),
|
||||
'HKG' => __('Hong Kong (HKG)', 'updraftplus'),
|
||||
'LON' => __('London (LON)', 'updraftplus')
|
||||
);
|
||||
|
||||
add_action('admin_footer', array($this, 'admin_footer'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose partial template that deals with apikeysettings
|
||||
*
|
||||
* @param String $msg A filterable partial templates
|
||||
* @return String the partial template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function apikeysettings($msg) {
|
||||
ob_start();
|
||||
?>
|
||||
<a href="{{updraftplus_current_clean_url}}" target="_blank" id="dw{{instance_id}}" class="updraft_cloudfiles_newapiuser" data-instance_id="{{instance_id}}"><em>{{api_key_setting_premium_label}}</em></a>
|
||||
<?php
|
||||
$msg = ob_get_clean();
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is hooked to a filter and going to be accessed by any code within WordPress environment, so instead of sanitising each value in this method and/or using any other technique to prevent XSS attacks, just make sure each partial template has all variables escaped
|
||||
*
|
||||
* @return Array an associative array keyed by names that describe themselves as they are
|
||||
*/
|
||||
public function partial_template_properties() {
|
||||
return array(
|
||||
'api_key_setting_premium_label' => __('Create a new API user with access to only this container (rather than your whole account)', 'updraftplus'),
|
||||
'updraftplus_current_clean_url' => UpdraftPlus::get_current_clean_url(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the Rackspace API to create a new API user
|
||||
*
|
||||
* @param Array $use_settings - expected keys are: adminuser, adminapikey, newuser, container, newemail; also allowed are location, region
|
||||
*
|
||||
* @return Array - the contents depend upon the outcome. 'e' will be present (0|1) to indicate failure or success
|
||||
*/
|
||||
public function create_api_user($use_settings) {
|
||||
|
||||
if (!isset($use_settings['adminuser'])) {
|
||||
return array('e' => 1, 'm' => __('You need to enter an admin username', 'updraftplus'));
|
||||
}
|
||||
if (empty($use_settings['adminapikey'])) {
|
||||
return array('e' => 1, 'm' => __('You need to enter an admin API key', 'updraftplus'));
|
||||
}
|
||||
if (!isset($use_settings['newuser'])) {
|
||||
return array('e' => 1, 'm' => __('You need to enter a new username', 'updraftplus'));
|
||||
}
|
||||
if (empty($use_settings['container'])) {
|
||||
return array('e' => 1, 'm' => __('You need to enter a container', 'updraftplus'));
|
||||
}
|
||||
// Here, 0 == catches both 0 and false
|
||||
if (empty($use_settings['newemail']) || 0 == strpos($use_settings['newemail'], '@')) {
|
||||
return array('e' => 1, 'm' => __('You need to enter a valid new email address', 'updraftplus'));
|
||||
}
|
||||
if (empty($use_settings['location'])) $use_settings['location'] = 'us';
|
||||
if (empty($use_settings['region'])) $use_settings['region'] = 'DFW';
|
||||
|
||||
updraft_try_include_file('methods/cloudfiles.php', 'include_once');
|
||||
updraft_try_include_file('vendor/autoload.php', 'include_once');
|
||||
$method = new UpdraftPlus_BackupModule_cloudfiles;
|
||||
$useservercerts = !empty($use_settings['useservercerts']);
|
||||
$disableverify = !empty($use_settings['disableverify']);
|
||||
$auth_url = ('uk' == $use_settings['location']) ? Rackspace::UK_IDENTITY_ENDPOINT : Rackspace::US_IDENTITY_ENDPOINT;
|
||||
|
||||
try {
|
||||
$storage = $method->get_openstack_service(
|
||||
array(
|
||||
'user' => $use_settings['adminuser'],
|
||||
'apikey' => $use_settings['adminapikey'],
|
||||
'authurl' => $auth_url,
|
||||
'region' => $use_settings['region']
|
||||
),
|
||||
$useservercerts,
|
||||
$disableverify
|
||||
);
|
||||
} catch (AuthenticationError $e) {
|
||||
global $updraftplus;
|
||||
$updraftplus->log('Cloud Files authentication failed ('.$e->getMessage().')');
|
||||
$updraftplus->log(__('Cloud Files authentication failed', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
return array('e' => 1, 'm' => __('Error:', 'updraftplus').' '.$e->getMessage());
|
||||
}
|
||||
|
||||
// Create the container (if necessary)
|
||||
// Get the container
|
||||
try {
|
||||
$container_object = $storage->getContainer($use_settings['container']);
|
||||
} catch (Guzzle\Http\Exception\ClientErrorResponseException $e) {
|
||||
$container_object = $storage->createContainer($use_settings['container']);
|
||||
} catch (Exception $e) {
|
||||
return array('e' => 1, 'm' => __('Cloud Files authentication failed', 'updraftplus').' ('.get_class($e).', '.$e->getMessage().')');
|
||||
}
|
||||
|
||||
if (!is_a($container_object, 'OpenCloud\ObjectStore\Resource\Container') && !is_a($container_object, 'Container')) {
|
||||
return array('e' => 1, 'm' => __('Cloud Files authentication failed', 'updraftplus').' ('.get_class($container_object).')');
|
||||
}
|
||||
|
||||
// Create the new user
|
||||
$json = json_encode(
|
||||
array(
|
||||
'user' => array(
|
||||
'username' => $use_settings['newuser'],
|
||||
'email' => $use_settings['newemail'],
|
||||
'enabled' => true
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$client = $method->get_client();
|
||||
|
||||
try {
|
||||
$response = $client->post($auth_url.'users', array('Content-Type' => 'application/json', 'Accept' => 'application/json'), $json)->send()->json();
|
||||
} catch (Guzzle\Http\Exception\ClientErrorResponseException $e) {
|
||||
$response = $e->getResponse();
|
||||
$code = $response->getStatusCode();
|
||||
$reason = $response->getReasonPhrase();
|
||||
if (403 == $code) {
|
||||
return array('e' => 1, 'm' => __('Authorisation failed (check your credentials)', 'updraftplus'));
|
||||
} elseif (409 == $code && 'Conflict' == $reason) {
|
||||
return array('e' => 1, 'm' => __('Conflict: that user or email address already exists', 'updraftplus'));
|
||||
} else {
|
||||
return array('e' => 1, 'm' => sprintf(__('Cloud Files operation failed (%s)', 'updraftplus'), 5)." (".$e->getMessage().') ('.get_class($e).')');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return array('e' => 1, 'm' => sprintf(__('Cloud Files operation failed (%s)', 'updraftplus'), 4).' ('.$e->getMessage().') ('.get_class($e).')');
|
||||
}
|
||||
|
||||
if (empty($response['user']['id']) || empty($response['user']['OS-KSADM:password']) || empty($response['user']['username'])) {
|
||||
return array('e' => 1, 'm' => sprintf(__('Cloud Files operation failed (%s)', 'updraftplus'), 3));
|
||||
}
|
||||
|
||||
$user = $response['user']['username'];
|
||||
$pass = $response['user']['OS-KSADM:password'];
|
||||
$id = $response['user']['id'];
|
||||
|
||||
// Add the user to the container
|
||||
try {
|
||||
// The 'X-Container-Write' and 'X-Container-Read' headers always override the previous ACL configurations on the existing container. We need to also include the other users who already have access to this container to ensure we don't remove their access.
|
||||
$container_metadata = $container_object->getMetadata();
|
||||
$container_read_users = (null !== $container_metadata->getProperty('read') && '' != $container_metadata->getProperty('read')) ? $container_metadata->getProperty('read') . ',' . $user : $user;
|
||||
$container_write_users = (null !== $container_metadata->getProperty('write') && '' != $container_metadata->getProperty('write')) ? $container_metadata->getProperty('write') . ',' . $user : $user;
|
||||
$headers = array('X-Container-Write' => $container_write_users, 'X-Container-Read' => $container_read_users);
|
||||
$container_object->getClient()->post($container_object->getUrl(), $headers)->send();
|
||||
} catch (Exception $e) {
|
||||
return array('e' => 1, 'm' => sprintf(__('Cloud Files operation failed (%s)', 'updraftplus'), 1).' ('.$e->getMessage().') ('.get_class($e).')');
|
||||
}
|
||||
|
||||
// Get an API key for the user
|
||||
try {
|
||||
$response = $container_object->getClient()->post($auth_url."users/$id/OS-KSADM/credentials/RAX-KSKEY:apiKeyCredentials/RAX-AUTH/reset", array())->send()->json();
|
||||
if (empty($response['RAX-KSKEY:apiKeyCredentials']['apiKey'])) {
|
||||
return array('e' => 1, 'm' => sprintf(__('Cloud Files operation failed (%s)', 'updraftplus'), 8));
|
||||
}
|
||||
$apikey = $response['RAX-KSKEY:apiKeyCredentials']['apiKey'];
|
||||
} catch (Exception $e) {
|
||||
return array('e' => 1, 'm' => sprintf(__('Cloud Files operation failed (%s)', 'updraftplus'), 7).' ('.$e->getMessage().') ('.get_class($e).')');
|
||||
}
|
||||
|
||||
return array(
|
||||
'e' => 0,
|
||||
'u' => htmlspecialchars($user),
|
||||
'p' => htmlspecialchars($pass),
|
||||
'k' => htmlspecialchars($apikey),
|
||||
'a' => $auth_url = ('uk' == $use_settings['location']) ? 'https://lon.auth.api.rackspacecloud.com' : 'https://auth.api.rackspacecloud.com',
|
||||
'r' => $use_settings['region'],
|
||||
'c' => $use_settings['container'],
|
||||
'm' => htmlspecialchars(sprintf(__("Username: %s", 'updraftplus'), $user))."<br>".htmlspecialchars(sprintf(__("Password: %s", 'updraftplus'), $pass))."<br>".htmlspecialchars(sprintf(__("API Key: %s", 'updraftplus'), $apikey))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user
|
||||
*
|
||||
* @uses self::create_api_user()
|
||||
*
|
||||
* @param Array $use_settings - user settings
|
||||
*/
|
||||
public function newuser($use_settings = array()) {
|
||||
$data = $this->create_api_user($use_settings);
|
||||
echo json_encode($data);
|
||||
die();
|
||||
}
|
||||
|
||||
public function admin_footer() {
|
||||
$this->modal_css();
|
||||
$this->modal_html();
|
||||
$this->modal_script();
|
||||
}
|
||||
|
||||
private function modal_css() {
|
||||
?>
|
||||
<style type="text/css">
|
||||
#updraft_cfnewapiuser_form label {
|
||||
float: left;
|
||||
clear:left;
|
||||
width: 200px;
|
||||
}
|
||||
#updraft_cfnewapiuser_form input[type="text"],
|
||||
#updraft_cfnewapiuser_form select {
|
||||
float: left;
|
||||
width: 230px;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function account_options() {
|
||||
return $this->accounts;
|
||||
}
|
||||
|
||||
private function get_account_options() {
|
||||
|
||||
$selaccount = 'us';
|
||||
foreach ($this->accounts as $acc => $desc) {
|
||||
?><option <?php if ($selaccount == $acc) echo 'selected="selected"'; ?> value="<?php echo esc_attr($acc); ?>"><?php echo esc_html($desc); ?></option><?php
|
||||
};
|
||||
}
|
||||
|
||||
public function region_options() {
|
||||
return $this->regions;
|
||||
}
|
||||
|
||||
private function get_region_options() {
|
||||
|
||||
$selregion = 'DFW';
|
||||
foreach ($this->regions as $reg => $desc) {
|
||||
?>
|
||||
<option <?php if ($selregion == $reg) echo 'selected="selected"'; ?> value="<?php echo esc_attr($reg); ?>"><?php echo esc_html($desc); ?></option>
|
||||
<?php
|
||||
};
|
||||
}
|
||||
|
||||
public function modal_html() {
|
||||
?>
|
||||
<div id="updraft-cfnewapiuser-modal" title="<?php esc_attr_e('Create new API user and container', 'updraftplus');?>" style="display:none;">
|
||||
<div id="updraft_cfnewapiuser_form">
|
||||
<p style="margin:1px; padding-top:0; clear: left; float: left;">
|
||||
<em><?php esc_html_e('Enter your Rackspace admin username/API key (so that Rackspace can authenticate your permission to create new users), and enter a new (unique) username and email address for the new user and a container name.', 'updraftplus');?></em>
|
||||
</p>
|
||||
<div id="updraft-cfnewapiuser-results" style="clear: left; float: left;">
|
||||
<p></p>
|
||||
</div>
|
||||
|
||||
<p style="margin-top:3px; padding-top:0; clear: left; float: left;">
|
||||
|
||||
<label for="updraft_cfnewapiuser_accountlocation"><?php esc_html_e('US or UK Rackspace Account', 'updraftplus');?></label>
|
||||
<select title="<?php esc_attr_e('Accounts created at rackspacecloud.com are US accounts; accounts created at rackspace.co.uk are UK accounts.', 'updraftplus');?>" id="updraft_cfnewapiuser_accountlocation">
|
||||
<?php $this->get_account_options();?>
|
||||
</select>
|
||||
|
||||
<label for="updraft_cfnewapiuser_adminusername"><?php esc_html_e('Admin Username', 'updraftplus');?></label>
|
||||
<input type="text" id="updraft_cfnewapiuser_adminusername" value="">
|
||||
|
||||
<label for="updraft_cfnewapiuser_adminapikey"><?php esc_html_e('Admin API Key', 'updraftplus');?></label>
|
||||
<input type="text" id="updraft_cfnewapiuser_adminapikey" value="">
|
||||
|
||||
<label for="updraft_cfnewapiuser_newuser"><?php esc_html_e("New User's Username", 'updraftplus');?></label>
|
||||
<input type="text" id="updraft_cfnewapiuser_newuser" value="">
|
||||
|
||||
<label for="updraft_cfnewapiuser_newemail"><?php esc_html_e("New User's Email Address", 'updraftplus');?></label>
|
||||
<input type="text" id="updraft_cfnewapiuser_newemail" value="">
|
||||
|
||||
<label for="updraft_cfnewapiuser_region"><?php esc_html_e('Cloud Files Storage Region', 'updraftplus');?>:</label>
|
||||
<select id="updraft_cfnewapiuser_region">
|
||||
<?php $this->get_region_options();?>
|
||||
</select>
|
||||
<label for="updraft_cfnewapiuser_container"><?php esc_html_e("Cloud Files Container", 'updraftplus');?></label> <input type="text" id="updraft_cfnewapiuser_container" value="">
|
||||
|
||||
</p>
|
||||
<fieldset>
|
||||
<input type="hidden" name="nonce" value="<?php echo esc_attr(wp_create_nonce('updraftplus-credentialtest-nonce')); ?>">
|
||||
<input type="hidden" name="action" value="updraft_ajax">
|
||||
<input type="hidden" name="subaction" value="cloudfiles_newuser">
|
||||
<input type="hidden" id="updraft_cfnewapiuser_instance_id" name="updraft_cfnewapiuser_instance_id" value="" />
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function modal_script() {
|
||||
?>
|
||||
<script>
|
||||
jQuery(function($) {
|
||||
|
||||
function set_allowable_regions() {
|
||||
var account_location = $('#updraft_cfnewapiuser_accountlocation').val();
|
||||
console.log(account_location);
|
||||
if ('uk' === account_location) {
|
||||
$('#updraft_cfnewapiuser_region option[value="LON"]').prop('disabled', false);
|
||||
} else {
|
||||
var region_selected = $('#updraft_cfnewapiuser_region').val();
|
||||
if ('LON' == region_selected) {
|
||||
$('#updraft_cfnewapiuser_region').val('DFW');
|
||||
}
|
||||
$('#updraft_cfnewapiuser_region option[value="LON"]').prop('disabled', true);
|
||||
}
|
||||
}
|
||||
|
||||
$('#updraft-navtab-settings-content').on('click', '.updraft_cloudfiles_newapiuser', function(e) {
|
||||
e.preventDefault();
|
||||
jQuery('#updraft_cfnewapiuser_instance_id').val(jQuery(this).data('instance_id'));
|
||||
jQuery('#updraft-cfnewapiuser-modal').dialog('open');
|
||||
set_allowable_regions();
|
||||
});
|
||||
|
||||
jQuery('#updraft_cfnewapiuser_accountlocation').on('change', function() {
|
||||
set_allowable_regions();
|
||||
});
|
||||
|
||||
var updraft_cfnewapiuser_modal_buttons = {};
|
||||
|
||||
updraft_cfnewapiuser_modal_buttons[updraftlion.cancel] = function() { jQuery(this).dialog("close"); };
|
||||
updraft_cfnewapiuser_modal_buttons[updraftlion.createbutton] = function() {
|
||||
jQuery('#updraft-cfnewapiuser-results').html('<p style="color:green">'+updraftlion.trying+'</p>');
|
||||
var data = {
|
||||
subsubaction: 'updraft_cloudfiles_newuser',
|
||||
adminuser: jQuery('#updraft_cfnewapiuser_adminusername').val(),
|
||||
adminapikey: jQuery('#updraft_cfnewapiuser_adminapikey').val(),
|
||||
newuser: jQuery('#updraft_cfnewapiuser_newuser').val(),
|
||||
newemail: jQuery('#updraft_cfnewapiuser_newemail').val(),
|
||||
container: jQuery('#updraft_cfnewapiuser_container').val(),
|
||||
location: jQuery('#updraft_cfnewapiuser_accountlocation').val(),
|
||||
region: jQuery('#updraft_cfnewapiuser_region').val(),
|
||||
useservercerts: jQuery('#updraft_ssl_useservercerts').val(),
|
||||
disableverify: jQuery('#updraft_ssl_disableverify').val()
|
||||
};
|
||||
|
||||
updraft_send_command('doaction', data, function(resp, status, response) {
|
||||
if (resp.e == 1) {
|
||||
jQuery('#updraft-cfnewapiuser-results').html('<p style="color:red;">'+resp.m+'</p>');
|
||||
} else if (resp.e == 0) {
|
||||
jQuery('#updraft-cfnewapiuser-results').html('<p style="color:green;">'+resp.m+'</p>');
|
||||
var instance_id = jQuery('#updraft_cfnewapiuser_instance_id').val();
|
||||
jQuery('#updraft_cloudfiles_user_'+instance_id).val(resp.u);
|
||||
jQuery('#updraft_cloudfiles_apikey_'+instance_id).val(resp.k);
|
||||
jQuery('#updraft_cloudfiles_authurl_'+instance_id).val(resp.a);
|
||||
jQuery('#updraft_cloudfiles_region_'+instance_id).val(resp.r);
|
||||
jQuery('#updraft_cloudfiles_path_'+instance_id).val(resp.c);
|
||||
jQuery('#dw'+instance_id).after('<p><strong>'+updraftlion.newuserpass+'</strong> '+resp.p+'<p>');
|
||||
jQuery('#updraft-cfnewapiuser-modal').dialog('close');
|
||||
}
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
if ('undefined' !== typeof resp && resp.hasOwnProperty('fatal_error')) {
|
||||
jQuery('#updraft-cfnewapiuser-results').html('<p style="color:red;">'+resp.fatal_error_message+'</p>');
|
||||
console.error(resp.fatal_error_message);
|
||||
} else {
|
||||
console.log("updraft_send_command: error: "+status+" ("+error_code+")");
|
||||
jQuery('#updraft-cfnewapiuser-results').html('<p style="color:red;">'+updraftlion.servererrorcode+'</p>');
|
||||
alert(updraftlion.unexpectedresponse+' '+response);
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
jQuery("#updraft-cfnewapiuser-modal").dialog({
|
||||
autoOpen: false, height: 465, width: 555, modal: true,
|
||||
buttons: updraft_cfnewapiuser_modal_buttons
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
96
wp-content/plugins/updraftplus/addons/dropbox-folders.php
Normal file
96
wp-content/plugins/updraftplus/addons/dropbox-folders.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: dropbox-folders:Dropbox folders
|
||||
Description: Allows Dropbox to use sub-folders - useful if you are backing up many sites into one Dropbox
|
||||
Version: 1.7
|
||||
Shop: /shop/dropbox-folders/
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
add_filter('updraftplus_options_dropbox_options', array('UpdraftPlus_Addon_DropboxFolders', 'transform_options_dropbox_options'));
|
||||
add_filter('updraftplus_dropbox_modpath', array('UpdraftPlus_Addon_DropboxFolders', 'change_path'), 10, 2);
|
||||
add_filter('updraft_dropbox_partial_templates', array('UpdraftPlus_Addon_DropboxFolders', 'get_partial_templates'));
|
||||
add_filter('updraft_dropbox_template_properties', array('UpdraftPlus_Addon_DropboxFolders', 'get_partial_template_properties'));
|
||||
|
||||
|
||||
class UpdraftPlus_Addon_DropboxFolders {
|
||||
|
||||
/**
|
||||
* Get Dropbox partial templates of the folders addon, the partial template is recognised by its name. To find out a name of partial template, look for the partial call syntax in the template, it's enclosed by double curly braces (i.e. {{> partial_template_name }})
|
||||
*
|
||||
* @param Array $partial_templates A collection of filterable partial templates
|
||||
* @return Array an associative array keyed by name of the partial templates
|
||||
*/
|
||||
public static function get_partial_templates($partial_templates) {
|
||||
if (!isset($partial_templates['dropbox_additional_configuration_top'])) $partial_templates['dropbox_additional_configuration_top'] = '';
|
||||
$partial_templates['dropbox_additional_configuration_top'] = self::get_configuration_template();
|
||||
return $partial_templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is hooked to a filter and going to be accessed by any code within WordPress environment, so instead of sanitising each value in this method and/or using any other technique to prevent XSS attacks, just make sure each partial template has all variables escaped
|
||||
*/
|
||||
public static function get_partial_template_properties() {
|
||||
return array(
|
||||
'input_store_at_label' => __('Store at', 'updraftplus'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Dropbox Folders addon HTML content to be displayed on the page
|
||||
*
|
||||
* @return [string] - the premium HTML content that will be displayed on the page
|
||||
*/
|
||||
private static function get_configuration_template() {
|
||||
ob_start();
|
||||
?>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_store_at_label}}:</th>
|
||||
<td>
|
||||
{{folder_path}}<input type="text" style="width: 292px" id="{{get_template_input_attribute_value "id" "folder"}}" name="{{get_template_input_attribute_value "name" "folder"}}" value="{{folder}}" />
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options
|
||||
*
|
||||
* @param array $opts handerbar template options
|
||||
* @return array - New handerbar template options
|
||||
*/
|
||||
public static function transform_options_dropbox_options($opts) {
|
||||
$key = empty($opts['appkey']) ? '' : $opts['appkey'];
|
||||
$folder_path = '';
|
||||
if ('dropbox:' != substr($key, 0, 8)) {
|
||||
$folder_path .= 'apps/';
|
||||
// "upgraded" means that an OAuth1 token was upgraded to OAuth2. It was only possible to have an OAuth1 token if they authenticated on the old app (since new authentications after we added the new app were all on that), so this indicates the old app.
|
||||
if (empty($opts['upgraded']) && empty($opts['tk_request_token'])) {
|
||||
$folder_path .= 'UpdraftPlus.Com';
|
||||
} else {
|
||||
$folder_path .= 'UpdraftPlus';
|
||||
}
|
||||
$folder_path .= '/';
|
||||
}
|
||||
$opts['folder_path'] = $folder_path;
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will construct the path to the file that is passed to this method and return the path to the caller ready to be used.
|
||||
*
|
||||
* @param [string] $file - the name of the file
|
||||
* @param [object] $backup_module_object - the backup module object this will allow us to get and use various functions
|
||||
* @return [string] the real path where the users Dropbox file is stored
|
||||
*/
|
||||
public static function change_path($file, $backup_module_object) {
|
||||
$opts = $backup_module_object->get_options();
|
||||
$folder = empty($opts['folder']) ? '' : $opts['folder'];
|
||||
$dropbox_folder = trailingslashit($folder);
|
||||
return ('/' == $dropbox_folder || './' == $dropbox_folder) ? $file : $dropbox_folder.$file;
|
||||
}
|
||||
}
|
||||
588
wp-content/plugins/updraftplus/addons/fixtime.php
Normal file
588
wp-content/plugins/updraftplus/addons/fixtime.php
Normal file
@@ -0,0 +1,588 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: fixtime:Time and Scheduling
|
||||
Description: Allows you to specify the exact time at which backups will run, and create more complex retention rules
|
||||
Version: 2.1
|
||||
Shop: /shop/fix-time/
|
||||
Latest Change: 1.12.3
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
new UpdraftPlus_AddOn_FixTime;
|
||||
|
||||
class UpdraftPlus_AddOn_FixTime {
|
||||
|
||||
public function __construct() {
|
||||
add_filter('updraftplus_schedule_firsttime_files', array($this, 'starttime_files'));
|
||||
add_filter('updraftplus_schedule_firsttime_db', array($this, 'starttime_db'));
|
||||
add_filter('updraftplus_schedule_showfileopts', array($this, 'schedule_showfileopts'), 10, 2);
|
||||
add_filter('updraftplus_schedule_showdbopts', array($this, 'schedule_showdbopts'), 10, 2);
|
||||
add_filter('updraftplus_fixtime_ftinfo', array($this, 'return_empty_string'));
|
||||
add_filter('updraftplus_schedule_sametimemsg', array($this, 'schedule_sametimemsg'));
|
||||
|
||||
// Retention rules
|
||||
add_filter('updraftplus_group_backups_for_pruning', array($this, 'group_backups_for_pruning'), 10, 3);
|
||||
add_action('updraftplus_after_filesconfig', array($this, 'after_filesconfig'));
|
||||
add_action('updraftplus_after_dbconfig', array($this, 'after_dbconfig'));
|
||||
add_filter('updraftplus_prune_or_not', array($this, 'prune_or_not'), 10, 7);
|
||||
add_filter('updraftplus_get_settings_meta', array($this, 'get_settings_meta'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Purpose of this function: place the backups into groups, where each backup in the same group is governed by the same pruning rule
|
||||
*
|
||||
* @param String $groups
|
||||
* @param Array $backup_history must be already sorted in date order (most recent first)
|
||||
* @param String $type 'db'|'files'
|
||||
* @return Array
|
||||
*/
|
||||
public function group_backups_for_pruning($groups, $backup_history, $type) {
|
||||
|
||||
if (is_array($groups)) return $groups;
|
||||
$groups = array();
|
||||
|
||||
if (empty($backup_history)) return $groups;
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$wp_cron_unreliability_margin = (defined('UPDRAFTPLUS_PRUNE_MARGIN') && is_numeric(UPDRAFTPLUS_PRUNE_MARGIN)) ? UPDRAFTPLUS_PRUNE_MARGIN : 900;
|
||||
|
||||
$retain_extrarules = UpdraftPlus_Options::get_updraft_option('updraft_retain_extrarules');
|
||||
|
||||
if (!is_array($retain_extrarules)) $retain_extrarules = array();
|
||||
if (!isset($retain_extrarules['db'])) $retain_extrarules['db'] = array();
|
||||
if (!isset($retain_extrarules['files'])) $retain_extrarules['files'] = array();
|
||||
|
||||
$extra_rules = $retain_extrarules[$type];
|
||||
|
||||
uasort($extra_rules, array($this, 'soonest_first'));
|
||||
|
||||
// For each backup set in the history, we go through it, and work out which is the 'latest' rule to apply to it - and put it in the corresponding group. Then, return the groups.
|
||||
|
||||
$backup_run_time = empty($updraftplus->backup_time) ? time() : $updraftplus->backup_time;
|
||||
|
||||
// We add on 15 minutes so that the vagaries of WP's cron system are less likely to intervene - backups that ran up to 10 minutes later than the exact time will be included
|
||||
|
||||
$last_rule = empty($extra_rules) ? false : max(array_keys($extra_rules));
|
||||
|
||||
$there_are_some_multiple_periods_in = false;
|
||||
|
||||
foreach ($backup_history as $backup_datestamp => $backup_to_examine) {
|
||||
|
||||
$backup_age = $backup_run_time - $backup_datestamp + $wp_cron_unreliability_margin;
|
||||
|
||||
// Find the relevant rule at this stage
|
||||
$latest_relevant_index = false;
|
||||
foreach ($extra_rules as $i => $rule) {
|
||||
|
||||
$rule_interpreted = $this->interpret_rule($rule);
|
||||
if (!is_array($rule_interpreted)) continue;
|
||||
|
||||
list ($after_howmany, $after_period, $every_howmany, $every_period) = $rule_interpreted;
|
||||
|
||||
// Get the times in seconds
|
||||
$after_time = $after_howmany * $after_period;
|
||||
if ($backup_age > $after_time) {
|
||||
$latest_relevant_index = $i;
|
||||
}
|
||||
}
|
||||
|
||||
$group_number = (false === $latest_relevant_index) ? 0 : $latest_relevant_index+1;
|
||||
|
||||
$process_order = 'keep_newest';
|
||||
|
||||
if (false !== $latest_relevant_index) {
|
||||
|
||||
// The last set needs splitting up into further sets - one set for each period specified in the rule. Only the final set-within-the-last-set should be keep_newest - that gets set later, once we know how many sets there actually are
|
||||
$process_order = 'keep_oldest';
|
||||
|
||||
if ($latest_relevant_index == $last_rule) {
|
||||
|
||||
$rule = $extra_rules[$latest_relevant_index];
|
||||
|
||||
$rule_interpreted = $this->interpret_rule($rule);
|
||||
if (is_array($rule_interpreted)) {
|
||||
list ($after_howmany, $after_period, $every_howmany, $every_period) = $rule_interpreted;
|
||||
// Get the times in seconds
|
||||
$after_time = $after_howmany * $after_period;
|
||||
$one_every = $every_howmany * $every_period;
|
||||
|
||||
$how_far_into_period = $backup_age - $after_time;
|
||||
$how_many_periods_in = floor($how_far_into_period / $one_every);
|
||||
|
||||
if ($how_many_periods_in > 0) {
|
||||
$there_are_some_multiple_periods_in = true;
|
||||
$group_number += $how_many_periods_in;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!isset($groups[$group_number])) $groups[$group_number] = array(
|
||||
'rule' => (false === $latest_relevant_index) ? null : $extra_rules[$latest_relevant_index],
|
||||
'process_order' => $process_order,
|
||||
'sets' => array(),
|
||||
);
|
||||
$groups[$group_number]['sets'][$backup_datestamp] = $backup_to_examine;
|
||||
|
||||
}
|
||||
|
||||
// If multiple rules exist, and if the final group got split, then in that group, the newest (not oldest) should be kept
|
||||
$highest_group_number = max(array_keys($groups));
|
||||
if ($highest_group_number > 0 && $there_are_some_multiple_periods_in) {
|
||||
$groups[$highest_group_number]['process_order'] = 'keep_newest';
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
private function interpret_rule($rule) {
|
||||
if (!is_array($rule) || !isset($rule['after-howmany']) || !isset($rule['after-period']) || !isset($rule['every-howmany']) || !isset($rule['every-period'])) return false;
|
||||
$after_howmany = $rule['after-howmany'];
|
||||
$after_period = $rule['after-period'];
|
||||
if (!is_numeric($after_howmany) || $after_howmany < 0) return false;
|
||||
// Fix historic bug - 'week' got saved as the number of seconds in 4 weeks instead...
|
||||
if (2419200 == $after_period) $after_period = 604800;
|
||||
if ($after_period < 3600) $after_period = 3600;
|
||||
$every_howmany = $rule['every-howmany'];
|
||||
$every_period = $rule['every-period'];
|
||||
// Fix historic bug - 'week' got saved as the number of seconds in 4 weeks instead...
|
||||
if (!is_numeric($every_howmany) || $every_howmany < 1) return false;
|
||||
if (2419200 == $every_period) $every_period = 604800;
|
||||
if ($every_period < 3600) $every_period = 3600;
|
||||
return array($after_howmany, $after_period, $every_howmany, $every_period);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Sort this doc block out
|
||||
* Backup sets will get run through this filter in "keep" order (i.e. within the same group, they are sent through in order of "keep first") - which is assumed (in the function below) to be by time, either ascending or descending
|
||||
* $type is 'files' or 'db', not to be confused with entity (plugins/themes/db/db1 etc.)
|
||||
*
|
||||
* @param string $prune_it
|
||||
* @param string $type
|
||||
* @param string $backup_datestamp
|
||||
* @param string $entity
|
||||
* @param string $entity_how_many
|
||||
* @param string $rule
|
||||
* @param string $group_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function prune_or_not($prune_it, $type, $backup_datestamp, $entity, $entity_how_many, $rule, $group_id) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameters are for future use.
|
||||
|
||||
$debug = UpdraftPlus_Options::get_updraft_option('updraft_debug_mode');
|
||||
|
||||
static $last_backup_seen_at = array();
|
||||
static $last_relevant_backup_kept_at = array();
|
||||
|
||||
if (!isset($last_backup_seen_at[$group_id])) $last_backup_seen_at[$group_id] = array();
|
||||
if (!isset($last_relevant_backup_kept_at[$group_id])) $last_relevant_backup_kept_at[$group_id] = array();
|
||||
|
||||
if (!isset($last_backup_seen_at[$group_id][$entity])) $last_backup_seen_at[$group_id][$entity] = false;
|
||||
if (!isset($last_relevant_backup_kept_at[$group_id][$entity])) $last_relevant_backup_kept_at[$group_id][$entity] = false;
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$wp_cron_unreliability_margin = (defined('UPDRAFTPLUS_PRUNE_MARGIN') && is_numeric(UPDRAFTPLUS_PRUNE_MARGIN)) ? UPDRAFTPLUS_PRUNE_MARGIN : 900;
|
||||
|
||||
if ($debug) $updraftplus->log("dbprune examine: $backup_datestamp, type=$type, entity=$entity, entry_prune_it=$prune_it, last_relevant_backup_kept_at=".$last_relevant_backup_kept_at[$group_id][$entity].", last_backup_seen_at=".$last_backup_seen_at[$group_id][$entity].", rule=".json_encode($rule), 'debug');
|
||||
|
||||
// If it's already being pruned, then we have nothing to do
|
||||
if ($prune_it) {
|
||||
$last_backup_seen_at[$group_id][$entity] = $backup_datestamp;
|
||||
return $prune_it;
|
||||
}
|
||||
|
||||
/*
|
||||
$backup_run_time = $updraftplus->backup_time;
|
||||
|
||||
static $retain_extrarules = false;
|
||||
if (!is_array($retain_extrarules)) {
|
||||
$retain_extrarules = UpdraftPlus_Options::get_updraft_option('updraft_retain_extrarules');
|
||||
if (!is_array($retain_extrarules)) $retain_extrarules = array();
|
||||
if (!isset($retain_extrarules['db'])) $retain_extrarules['db'] = array();
|
||||
if (!isset($retain_extrarules['files'])) $retain_extrarules['files'] = array();
|
||||
$db = $retain_extrarules['db'];
|
||||
$files = $retain_extrarules['files'];
|
||||
uasort($db, array($this, 'soonest_first'));
|
||||
uasort($files, array($this, 'soonest_first'));
|
||||
$retain_extrarules['db'] = $db;
|
||||
$retain_extrarules['files'] = $files;
|
||||
}
|
||||
|
||||
$extra_rules = (is_array($retain_extrarules) && isset($retain_extrarules[$type])) ? $retain_extrarules[$type] : array();
|
||||
|
||||
// We add on 15 minutes so that the vagaries of WP's cron system are less likely to intervene - backups that ran up to 10 minutes later than the exact time will be included
|
||||
$backup_age = $backup_run_time - $backup_datestamp + $wp_cron_unreliability_margin;
|
||||
|
||||
// Find the relevant rule at this stage
|
||||
$latest_relevant_index = false;
|
||||
foreach ($extra_rules as $i => $rule) {
|
||||
// Drop broken rules
|
||||
if (!is_array($rule) || !isset($rule['after-howmany']) || !isset($rule['after-period']) || !isset($rule['every-howmany']) || !isset($rule['every-period'])) continue;
|
||||
$after_howmany = $rule['after-howmany'];
|
||||
$after_period = $rule['after-period'];
|
||||
if (!is_numeric($after_howmany) || $after_howmany < 0) continue;
|
||||
// Fix historic bug - 'week' got saved as the number of seconds in 4 weeks instead...
|
||||
if ($after_period == 2419200) $after_period = 604800;
|
||||
if ($after_period < 3600) $after_period = 3600;
|
||||
$every_howmany = $rule['every-howmany'];
|
||||
$every_period = $rule['every-period'];
|
||||
// Fix historic bug - 'week' got saved as the number of seconds in 4 weeks instead...
|
||||
if (!is_numeric($every_howmany) || $every_howmany < 1) continue;
|
||||
if ($every_period == 2419200) $every_period = 604800;
|
||||
if ($every_period < 3600) $every_period = 3600;
|
||||
// Finally, get the times in seconds
|
||||
$after_time = $after_howmany * $after_period;
|
||||
if ($backup_age > $after_time) {
|
||||
$latest_relevant_index = $i;
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug) $updraftplus->log("backup_age=$backup_age, type=$type, entity=$entity, latest_relevant_index: ".serialize($latest_relevant_index), 'debug');
|
||||
|
||||
if (false === $latest_relevant_index) {
|
||||
// There are no rules which apply to this backup (it's not old enough)
|
||||
$last_backup_seen_at[$group_id][$entity] = $backup_datestamp;
|
||||
return false;
|
||||
}
|
||||
$rule = $extra_rules[$latest_relevant_index];
|
||||
*/
|
||||
if (empty($rule)) {
|
||||
// There are no rules which apply to this backup (which would usually mean, it's not old enough)
|
||||
$last_backup_seen_at[$group_id][$entity] = $backup_datestamp;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if ($debug) $updraftplus->log("last_relevant_backup_kept_at=$last_relevant_backup_kept_at[$group_id][$entity], last_backup_seen_at=".$last_backup_seen_at[$group_id][$entity].", rule=".serialize($rule), 'debug');
|
||||
|
||||
// Is this the first relevant (i.e. old enough) backup we've come across?
|
||||
if (!$last_backup_seen_at[$group_id][$entity] || !$last_relevant_backup_kept_at[$group_id][$entity]) {
|
||||
$last_backup_seen_at[$group_id][$entity] = $backup_datestamp;
|
||||
$last_relevant_backup_kept_at[$group_id][$entity] = $backup_datestamp;
|
||||
if ($debug) $updraftplus->log("Keeping this backup, as it is the first relevant (i.e. old enough) backup we've come across for the current rule");
|
||||
return false;
|
||||
}
|
||||
|
||||
$every_time = $rule['every-howmany'] * $rule['every-period'];
|
||||
|
||||
// At this stage, we know that the backup's age is relevant to the rule, and that a previous old-enough backup has been kept. Now we just need to kept the time between them.
|
||||
// We want an unsigned result, as potentially the backups may be being fed through in either forward or reverse order
|
||||
$time_from_backup_to_last_kept = $last_relevant_backup_kept_at[$group_id][$entity] - $backup_datestamp;
|
||||
$time_from_backup_to_last_kept_abs = absint($time_from_backup_to_last_kept);
|
||||
|
||||
// Again, apply a 15-minute margin
|
||||
if ($time_from_backup_to_last_kept_abs > $every_time - $wp_cron_unreliability_margin) {
|
||||
// Keep it - enough time has passed
|
||||
$last_backup_seen_at[$group_id][$entity] = $backup_datestamp;
|
||||
$last_relevant_backup_kept_at[$group_id][$entity] = $backup_datestamp;
|
||||
if ($debug) $updraftplus->log("Will keep - enough time different to the last backup. time_from_backup_to_last_kept=$time_from_backup_to_last_kept, every_time=$every_time", 'debug');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($debug) $updraftplus->log("Will prune ($entity): backup is older than ".$rule['after-howmany']." periods of ".$rule['after-period']." s, and a backup ".$time_from_backup_to_last_kept." s more recent was kept (which is within the configured ".$rule['every-howmany']." periods of ".$rule['every-period']." s = ".$every_time." s)", 'debug');
|
||||
|
||||
$last_backup_seen_at[$group_id][$entity] = $backup_datestamp;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* WP 3.7+ has __return_empty_string() - but we support 3.2+
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public function return_empty_string() {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function after_dbconfig() {
|
||||
echo '<div id="updraft_retain_db_rules"></div><div><a href="'.esc_url(UpdraftPlus::get_current_clean_url()).'" id="updraft_retain_db_addnew" class="updraft_icon_link" aria-label="'.esc_attr__('Add an additional database retention rule', 'updraftplus').'"><span class="dashicons dashicons-plus"></span>'.esc_html__('Add an additional retention rule...', 'updraftplus').'</a></div>';
|
||||
}
|
||||
|
||||
public function after_filesconfig() {
|
||||
add_action('admin_footer', array($this, 'admin_footer_extraretain_js'));
|
||||
echo '<div id="updraft_retain_files_rules"></div><div><a href="'.esc_url(UpdraftPlus::get_current_clean_url()).'" id="updraft_retain_files_addnew" class="updraft_icon_link" aria-label="'.esc_attr__('Add an additional file retention rule', 'updraftplus').'"><span class="dashicons dashicons-plus"></span>'.esc_html__('Add an additional retention rule...', 'updraftplus').'</a></div>';
|
||||
}
|
||||
|
||||
public function soonest_first($a, $b) {
|
||||
if (!is_array($a)) {
|
||||
if (!is_array($b)) return 0;
|
||||
return 1;
|
||||
} elseif (!is_array($b)) {
|
||||
return -1;
|
||||
}
|
||||
$after_howmany_a = isset($a['after-howmany']) ? absint($a['after-howmany']) : 0;
|
||||
$after_howmany_b = isset($b['after-howmany']) ? absint($b['after-howmany']) : 0;
|
||||
$after_period_a = isset($a['after-period']) ? absint($a['after-period']) : 0;
|
||||
$after_period_b = isset($b['after-period']) ? absint($b['after-period']) : 0;
|
||||
$after_a = $after_howmany_a * $after_period_a;
|
||||
$after_b = $after_howmany_b * $after_period_b;
|
||||
if ($after_a == $after_b) return 0;
|
||||
return ($after_a < $after_b) ? -1 : 1;
|
||||
}
|
||||
|
||||
public function get_settings_meta($meta) {
|
||||
if (!is_array($meta)) return $meta;
|
||||
$meta['retain_rules'] = array(
|
||||
'files' => $this->javascript_retain_rules('files', 'return'),
|
||||
'db' => $this->javascript_retain_rules('db', 'return'),
|
||||
);
|
||||
return $meta;
|
||||
}
|
||||
|
||||
public function admin_footer_extraretain_js() {
|
||||
?>
|
||||
<script>
|
||||
jQuery(function($) {
|
||||
<?php
|
||||
$this->javascript_retain_rules('files');
|
||||
$this->javascript_retain_rules('db');
|
||||
?>
|
||||
var db_index = 0;
|
||||
var files_index = 0;
|
||||
$.each(retain_rules_files, function(index, rule) {
|
||||
add_rule('files', rule.after_howmany, rule.after_period, rule.every_howmany, rule.every_period);
|
||||
});
|
||||
$.each(retain_rules_db, function(index, rule) {
|
||||
add_rule('db', rule.after_howmany, rule.after_period, rule.every_howmany, rule.every_period);
|
||||
});
|
||||
|
||||
$('#updraft_retain_db_addnew').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
add_rule('db', 12, 604800, 1, 604800);
|
||||
});
|
||||
$('#updraft_retain_files_addnew').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
add_rule('files', 12, 604800, 1, 604800);
|
||||
});
|
||||
$('#updraft_retain_db_rules, #updraft_retain_files_rules').on('click', '.updraft_retain_rules_delete', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).parent('.updraft_retain_rules').slideUp(function() {$(this).remove();});
|
||||
});
|
||||
function add_rule(type, howmany_after, period_after, howmany_every, period_every) {
|
||||
var selector = 'updraft_retain_'+type+'_rules';
|
||||
var index;
|
||||
if ('db' == type) {
|
||||
db_index++;
|
||||
index = db_index;
|
||||
} else {
|
||||
files_index++;
|
||||
index = files_index;
|
||||
}
|
||||
$('#'+selector).append(
|
||||
'<div class="updraft_retain_rules '+selector+'_entry">'+
|
||||
updraftlion.forbackupsolderthan+' '+rule_period_selector(type, index, 'after', howmany_after, period_after)+' keep no more than 1 backup every '+rule_period_selector(type, index, 'every', howmany_every, period_every)+
|
||||
' <a href="#" title="'+updraftlion.deletebutton+'" class="updraft_retain_rules_delete"><span class="dashicons dashicons-no"></span></a></div>'
|
||||
)
|
||||
}
|
||||
function rule_period_selector(type, index, which, howmany_value, period) {
|
||||
var nameprefix = "updraft_retain_extrarules["+type+"]["+index+"]["+which+"-";
|
||||
var ret = '<input type="number" min="1" step="1" class="additional-rule-width" name="'+nameprefix+'howmany]" value="'+howmany_value+'"> \
|
||||
<select name="'+nameprefix+'period]">\
|
||||
<option value="3600"';
|
||||
if (period == 3600) { ret += ' selected="selected"'; }
|
||||
ret += '>'+updraftlion.hours+'</option>\
|
||||
<option value="86400"';
|
||||
if (period == 86400) { ret += ' selected="selected"'; }
|
||||
ret += '>'+updraftlion.days+'</option>\
|
||||
<option value="604800"';
|
||||
if (period == 604800) { ret += ' selected="selected"'; }
|
||||
ret += '>'+updraftlion.weeks+'</option>\
|
||||
</select>';
|
||||
return ret;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function javascript_retain_rules($type, $format = 'printjs') {
|
||||
|
||||
$extra_rules = UpdraftPlus_Options::get_updraft_option('updraft_retain_extrarules');
|
||||
if (!is_array($extra_rules)) $extra_rules = array();
|
||||
$extra_rules = empty($extra_rules[$type]) ? array() : $extra_rules[$type];
|
||||
|
||||
uasort($extra_rules, array($this, 'soonest_first'));
|
||||
$processed_rules = array();
|
||||
foreach ($extra_rules as $i => $rule) {
|
||||
if (!is_array($rule) || !isset($rule['after-howmany']) || !isset($rule['after-period']) || !isset($rule['every-howmany']) || !isset($rule['every-period'])) continue;
|
||||
$after_howmany = $rule['after-howmany'];
|
||||
$after_period = $rule['after-period'];
|
||||
|
||||
// Fix historic bug - stored the value of 28 days' worth of seconds, instead of 7
|
||||
if (2419200 == $after_period) $after_period = 604800;
|
||||
|
||||
// Best not to just drop the rule if it is invalid
|
||||
if (!is_numeric($after_howmany) || $after_howmany < 0) continue;
|
||||
if ($after_period <3600) $after_period = 3600;
|
||||
if (3600 != $after_period && 86400 != $after_period && 604800 != $after_period) continue;
|
||||
$every_howmany = $rule['every-howmany'];
|
||||
$every_period = $rule['every-period'];
|
||||
|
||||
// Fix historic bug - stored the value of 28 days' worth of seconds, instead of 7
|
||||
if (2419200 == $every_period) $every_period = 604800;
|
||||
|
||||
// Best not to just drop the rule if it is invalid
|
||||
if (!is_numeric($every_howmany) || $every_howmany < 1) continue;
|
||||
if ($every_period <3600) $every_period = 3600;
|
||||
if (3600 != $every_period && 86400 != $every_period && 604800 != $every_period) continue;
|
||||
|
||||
$processed_rules[] = array('index' => $i, 'after_howmany' => $after_howmany, 'after_period' => $after_period, 'every_howmany' => $every_howmany, 'every_period' => $every_period);
|
||||
// echo "add_rule('$type', $i, $after_howmany, $after_period, $every_howmany, $every_period);\n";
|
||||
}
|
||||
if ('return' == $format) {
|
||||
return $processed_rules;
|
||||
} else {
|
||||
// wp_json_encode() was added in WP 4.1
|
||||
$processed_rules_json = function_exists('wp_json_encode') ? wp_json_encode($processed_rules) : json_encode($processed_rules);
|
||||
echo "var retain_rules_".esc_js($type)." = ".$processed_rules_json.";\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function schedule_sametimemsg() {
|
||||
return htmlspecialchars(__('(at same time as files backup)', 'updraftplus'));
|
||||
}
|
||||
|
||||
public function starttime_files() {
|
||||
return $this->compute('files');
|
||||
}
|
||||
|
||||
public function starttime_db() {
|
||||
return $this->compute('db');
|
||||
}
|
||||
|
||||
private function parse($start_time) {
|
||||
preg_match("/^(\d+):(\d+)$/", $start_time, $matches);
|
||||
if (empty($matches[1]) || !is_numeric($matches[1]) || $matches[1]>23) {
|
||||
$start_hour = 0;
|
||||
} else {
|
||||
$start_hour = (int) $matches[1];
|
||||
}
|
||||
if (empty($matches[2]) || !is_numeric($matches[2]) || $matches[1]>59) {
|
||||
$start_minute = 5;
|
||||
if ($start_minute>60) {
|
||||
$start_minute = $start_minute-60;
|
||||
$start_hour++;
|
||||
if ($start_hour>23) $start_hour =0;
|
||||
}
|
||||
} else {
|
||||
$start_minute = (int) $matches[2];
|
||||
}
|
||||
return array($start_hour, $start_minute);
|
||||
}
|
||||
|
||||
private function compute($whichtime) {
|
||||
// Returned value should be in UNIX time.
|
||||
|
||||
$unixtime_now = time();
|
||||
// Convert to date
|
||||
$now_timestring_gmt = gmdate('Y-m-d H:i:s', $unixtime_now);
|
||||
|
||||
// Convert to blog's timezone
|
||||
$now_timestring_blogzone = get_date_from_gmt($now_timestring_gmt, 'Y-m-d H:i:s');
|
||||
|
||||
$int_key = ('db' == $whichtime) ? '_database' : '';
|
||||
$sched = (isset($_POST['updraft_interval'.$int_key])) ? $_POST['updraft_interval'.$int_key] : 'manual';
|
||||
|
||||
// HH:MM, in blog time zone
|
||||
// This function is only called from the options validator, so we don't read the current option
|
||||
// $start_time = UpdraftPlus_Options::get_updraft_option('updraft_starttime_'.$whichtime);
|
||||
$start_time = (isset($_POST['updraft_starttime_'.$whichtime])) ? $_POST['updraft_starttime_'.$whichtime] : '00:00';
|
||||
|
||||
list ($start_hour, $start_minute) = $this->parse($start_time);
|
||||
|
||||
// Was a particular week-day specified?
|
||||
if (isset($_POST['updraft_startday_'.$whichtime]) && ('weekly' == $sched || 'monthly' == $sched || 'fortnightly' == $sched)) {
|
||||
// All the monthly stuff is done here, since it has different logic
|
||||
if ('monthly' == $sched) {
|
||||
// Get specified day of the month in range 1-28
|
||||
$startday = min(absint($_POST['updraft_startday_'.$whichtime]), 28);
|
||||
if ($startday < 1) $startday = 1;
|
||||
// Get today's day of month in range 1-31
|
||||
// $day_today_blogzone = get_date_from_gmt($now_timestring_gmt, 'j');
|
||||
|
||||
$thismonth_timestring = 'Y-m-'.sprintf("%02d", $startday).' '.sprintf("%02d:%02d", $start_hour, $start_minute).':00';
|
||||
|
||||
$thismonth_time = get_date_from_gmt($now_timestring_gmt, $thismonth_timestring);
|
||||
$thismonth_unixtime = get_gmt_from_date($thismonth_time, 'U');
|
||||
|
||||
// Is that in the past? If so, then wind on a month.
|
||||
if ($thismonth_unixtime < $unixtime_now) {
|
||||
return strtotime("@".$thismonth_unixtime." + 1 month");
|
||||
} else {
|
||||
return $thismonth_unixtime;
|
||||
}
|
||||
} else {
|
||||
// Get specified day of week in range 0-6
|
||||
$startday = min(absint($_POST['updraft_startday_'.$whichtime]), 6);
|
||||
// Get today's day of week in range 0-6
|
||||
$day_today_blogzone = get_date_from_gmt($now_timestring_gmt, 'w');
|
||||
if ($day_today_blogzone != $startday) {
|
||||
if ($startday<$day_today_blogzone) $startday +=7;
|
||||
$new_startdate_unix = $unixtime_now + ($startday-$day_today_blogzone)*86400;
|
||||
$now_timestring_blogzone = get_date_from_gmt(gmdate('Y-m-d H:i:s', $new_startdate_unix), 'Y-m-d H:i:s');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, convert the start time HH:MM from blog time to UNIX time
|
||||
$start_time_unix = get_gmt_from_date(substr($now_timestring_blogzone, 0, 11).sprintf('%02d', $start_hour).':'.sprintf('%02d', $start_minute).':00', 'U');
|
||||
|
||||
// That may have already passed for today
|
||||
if ($start_time_unix<time()) {
|
||||
if ('weekly' == $sched || 'fortnightly' == $sched) {
|
||||
$start_time_unix = $start_time_unix + 86400*7;
|
||||
} elseif ('monthly' == $sched) {
|
||||
error_log("This code path is impossible, or so it was thought!");
|
||||
} else {
|
||||
$start_time_unix =$start_time_unix+86400;
|
||||
}
|
||||
}
|
||||
|
||||
return $start_time_unix;
|
||||
}
|
||||
|
||||
private function day_selector($id, $selected_interval = 'manual') {
|
||||
global $wp_locale;
|
||||
|
||||
$day_selector = '<select title="'.__('Day to run backups', 'updraftplus').'" name="'.$id.'" id="'.$id.'">';
|
||||
|
||||
$opt = UpdraftPlus_Options::get_updraft_option($id, 0);
|
||||
|
||||
$start_from = ('monthly' == $selected_interval) ? 1 : 0;
|
||||
$go_to = ('monthly' == $selected_interval) ? 28 : 6;
|
||||
|
||||
for ($day_index = $start_from; $day_index <= $go_to; $day_index++) :
|
||||
$selected = ($opt == $day_index) ? 'selected="selected"' : '';
|
||||
$day_selector .= "\n\t<option value='" . $day_index . "' $selected>";
|
||||
$day_selector .= ('monthly' == $selected_interval) ? $day_index : $wp_locale->get_weekday($day_index);
|
||||
$day_selector .= '</option>';
|
||||
endfor;
|
||||
$day_selector .= '</select>';
|
||||
return $day_selector;
|
||||
}
|
||||
|
||||
public function starting_widget($start_hour, $start_minute, $day_selector_id, $time_selector_id, $selected_interval = 'manual') {
|
||||
return __('starting from next time it is', 'updraftplus').' '.$this->day_selector($day_selector_id, $selected_interval).'<input title="'.__('Start time', 'updraftplus').__('Enter in format HH:MM (e.g. 14:22).', 'updraftplus').' '.htmlspecialchars(__('The time zone used is that from your WordPress settings, in Settings -> General.', 'updraftplus')).'" type="text" class="fix-time" maxlength="5" name="'.$time_selector_id.'" value="'.sprintf('%02d', $start_hour).':'.sprintf('%02d', $start_minute).'">';
|
||||
}
|
||||
|
||||
public function schedule_showdbopts($disp, $selected_interval) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameters are for future use.
|
||||
$start_time = UpdraftPlus_Options::get_updraft_option('updraft_starttime_db');
|
||||
if (empty($start_time)) $start_time = '00:00';
|
||||
list ($start_hour, $start_minute) = $this->parse($start_time);
|
||||
return $this->starting_widget($start_hour, $start_minute, 'updraft_startday_db', 'updraft_starttime_db', $selected_interval);
|
||||
}
|
||||
|
||||
public function schedule_showfileopts($disp, $selected_interval) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameters are for future use.
|
||||
$start_time = UpdraftPlus_Options::get_updraft_option('updraft_starttime_files');
|
||||
if (empty($start_time)) $start_time = '00:00';
|
||||
list ($start_hour, $start_minute) = $this->parse($start_time);
|
||||
return $this->starting_widget($start_hour, $start_minute, 'updraft_startday_files', 'updraft_starttime_files', $selected_interval);
|
||||
}
|
||||
}
|
||||
216
wp-content/plugins/updraftplus/addons/google-enhanced.php
Normal file
216
wp-content/plugins/updraftplus/addons/google-enhanced.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: google-enhanced:Google Drive, enhanced
|
||||
Description: Adds enhanced capabilities for Google Drive users
|
||||
Version: 1.1
|
||||
Shop: /shop/google-drive-enhanced/
|
||||
Latest Change: 1.16.15
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
new UpdraftPlus_Addon_Google_Enhanced;
|
||||
|
||||
class UpdraftPlus_Addon_Google_Enhanced {
|
||||
|
||||
public function __construct() {
|
||||
add_filter('updraftplus_options_googledrive_others', array($this, 'options_googledrive_others'), 10, 2);
|
||||
add_filter('updraftplus_options_googledrive_options', array($this, 'transform_options_googledrive_options'));
|
||||
add_filter('updraftplus_googledrive_parent_id', array($this, 'googledrive_parent_id'), 10, 5);
|
||||
add_filter('updraftplus_options_googledrive_foldername', array($this, 'options_googledrive_foldername'), 10, 2);
|
||||
add_filter('updraftplus_jstree_googledrive', array($this, 'jstree_googledrive'), 10, 2);
|
||||
add_filter('updraft_googledrive_partial_templates', array($this, 'get_partial_templates'), 10);
|
||||
add_filter('updraft_googledrive_template_properties', array($this, 'partial_template_properties'));
|
||||
}
|
||||
|
||||
public function options_googledrive_foldername($opt, $orig) {
|
||||
return $orig;
|
||||
}
|
||||
|
||||
/**
|
||||
* WordPress filter updraftplus_googledrive_parent_id
|
||||
*
|
||||
* @param String|Boolean $parent_id - parent ID value prior to filtering
|
||||
* @param Array $opts - service options
|
||||
* @param Object $storage - service object
|
||||
* @param Object $module - UpdraftPlus_BackupModule_googledrive object
|
||||
* @param Boolean $one_only - whether to return all results or just the oldest
|
||||
*
|
||||
* @return String|Boolean - filtered value
|
||||
*/
|
||||
public function googledrive_parent_id($parent_id, $opts, $storage, $module, $one_only = true) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameters are for future use.
|
||||
|
||||
if (isset($opts['folder'])) {
|
||||
$folder = $opts['folder'];
|
||||
} else {
|
||||
if (isset($opts['parentid'])) {
|
||||
if (empty($opts['parentid'])) {
|
||||
$folder = '';
|
||||
} else {
|
||||
if (is_array($opts['parentid'])) {
|
||||
$folder = '#'.$opts['parentid']['id'];
|
||||
} else {
|
||||
$folder = '#'.$opts['parentid'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$folder = 'UpdraftPlus';
|
||||
}
|
||||
}
|
||||
|
||||
if ('#' === substr($folder, 0, 1)) {
|
||||
return substr($folder, 1);
|
||||
} else {
|
||||
return $module->id_from_path($folder, $one_only);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get partial templates of the Google Drive remote storage, the partial template is recognised by its name. To find out a name of partial template, look for the partial call syntax in the template, it's enclosed by double curly braces (i.e. {{> partial_template_name }})
|
||||
*
|
||||
* @param Array $partial_templates A collection of filterable partial templates
|
||||
* @return Array an associative array keyed by name of the partial templates
|
||||
*/
|
||||
public function get_partial_templates($partial_templates) {
|
||||
ob_start();
|
||||
?>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_folder_label}}:</th>
|
||||
<td>
|
||||
<input title="{{input_enhanced_folder_title}}" type="text" id="{{get_template_input_attribute_value "id" "folder"}}" name="{{get_template_input_attribute_value "name" "folder"}}" value="{{folder}}" class="updraft_input--wide">
|
||||
{{#if is_authenticate_with_google}}
|
||||
<a href="#" class="updraft_googledrive_select_folder">{{input_select_folder_label}}</a>
|
||||
{{/if}}
|
||||
<br>
|
||||
<em>{{input_enhanced_folder_label}}</em>
|
||||
{{#if is_authenticate_with_google}}
|
||||
<div class="updraft_googledrive_container hidden-in-updraftcentral" style="clear:left;">
|
||||
<div class="updraft_googledrive_jstree_container">
|
||||
<div class="updraft_googledrive_jstree"></div>
|
||||
</div>
|
||||
<div id="updraft_jstree_buttons_googledrive">
|
||||
<button class="button updraft_googledrive_jstree_cancel">{{input_cancel_label}}</button>
|
||||
<button class="button button-primary updraft_googledrive_jstree_confirm">{{input_confirm_label}}</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
if (!isset($partial_templates['gdrive_additional_configuration_top'])) $partial_templates['gdrive_additional_configuration_top'] = '';
|
||||
$partial_templates['gdrive_additional_configuration_top'] .= ob_get_clean();
|
||||
return $partial_templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is hooked to a filter and going to be accessed by any code within WordPress environment, so instead of sanitising each value in this method and/or using any other technique to prevent XSS attacks, just make sure each partial template has all variables escaped
|
||||
*/
|
||||
public function partial_template_properties() {
|
||||
return array(
|
||||
'input_enhanced_folder_title' => sprintf(__('Enter the path of the %s folder you wish to use here.', 'updraftplus'), 'Google Drive').' '.__('If the folder does not already exist, then it will be created.').' '.sprintf(__('e.g. %s', 'updraftplus'), 'MyBackups/WorkWebsite.').' '.sprintf(__('If you leave it blank, then the backup will be placed in the root of your %s', 'updraftplus'), 'Google Drive').' '.sprintf(__('In %s, path names are case sensitive.', 'updraftplus'), 'Google Drive'),
|
||||
'input_enhanced_folder_label' => sprintf(__('In %s, path names are case sensitive.', 'updraftplus'), 'Google Drive'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Google Drives addon HTML content to be displayed on the page
|
||||
* DEVELOPER NOTE: Please don't use/call this method anymore as it was used as a partial template of Google Drive storage, and it's consider to be removed in future versions. Once the Google Drive template is CSP-compliant, this should be removed and should be placed in the class child instead of the base class. @see get_partial_templates()
|
||||
*
|
||||
* @param [String] $folder_opts - the free HTML content that will be replaced by the content in this method
|
||||
* @param [Object] $backup_module_object - the backup module object this will allow us to get and use various functions
|
||||
* @return [String] the premium HTML content that will be displayed on the page
|
||||
*/
|
||||
public function options_googledrive_others($folder_opts, $backup_module_object) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameters are for future use.
|
||||
$classes = $backup_module_object->get_css_classes();
|
||||
return '<tr class="'.$classes.'">
|
||||
<th>'.__('Google Drive', 'updraftplus').' '.__('Folder', 'updraftplus').':</th>
|
||||
<td>
|
||||
<input title="'.esc_attr(sprintf(__('Enter the path of the %s folder you wish to use here.', 'updraftplus'), 'Google Drive').' '.__('If the folder does not already exist, then it will be created.').' '.sprintf(__('e.g. %s', 'updraftplus'), 'MyBackups/WorkWebsite.').' '.sprintf(__('If you leave it blank, then the backup will be placed in the root of your %s', 'updraftplus'), 'Google Drive')).' '.sprintf(__('In %s, path names are case sensitive.', 'updraftplus'), 'Google Drive').
|
||||
'" type="text" '.$backup_module_object->output_settings_field_name_and_id('folder', true).' value="{{folder}}" class="updraft_input--wide">
|
||||
{{#if is_authenticate_with_google}}
|
||||
<a href="#" class="updraft_googledrive_select_folder">{{input_select_folder_label}}</a>
|
||||
{{/if}}
|
||||
<br>
|
||||
<em>'.htmlspecialchars(sprintf(__('In %s, path names are case sensitive.', 'updraftplus'), 'Google Drive')).'</em>
|
||||
|
||||
{{#if is_authenticate_with_google}}
|
||||
<div class="updraft_googledrive_container hidden-in-updraftcentral" style="clear:left;">
|
||||
<div class="updraft_googledrive_jstree_container">
|
||||
<div class="updraft_googledrive_jstree"></div>
|
||||
</div>
|
||||
<div id="updraft_jstree_buttons_googledrive">
|
||||
<button class="button updraft_googledrive_jstree_cancel">{{input_cancel_label}}</button>
|
||||
<button class="button button-primary updraft_googledrive_jstree_confirm">{{input_confirm_label}}</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handlebar template options
|
||||
*
|
||||
* @param array $opts
|
||||
* @return array - New handerbar template options
|
||||
*/
|
||||
public function transform_options_googledrive_options($opts) {
|
||||
if (!isset($opts['folder'])) {
|
||||
if (isset($opts['parentid'])) {
|
||||
if (is_array($opts['parentid'])) {
|
||||
if (isset($opts['parentid']['name'])) {
|
||||
$opts['folder'] = $opts['parentid']['name'];
|
||||
} else {
|
||||
$opts['folder'] = empty($opts['parentid']['id']) ? '' : '#'.$opts['parentid']['id'];
|
||||
}
|
||||
} else {
|
||||
$opts['folder'] = empty($opts['parentid']) ? '' : '#'.$opts['parentid'];
|
||||
}
|
||||
} else {
|
||||
$opts['folder'] = 'UpdraftPlus';
|
||||
}
|
||||
}
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return all user directories in Google Drive.
|
||||
*
|
||||
* @param Array $node_array - The default value
|
||||
* @param Array $params - The search parameters
|
||||
* @return Array $node_array - Array results for JSTree
|
||||
*/
|
||||
public function jstree_googledrive($node_array, $params) {
|
||||
if (!isset($params['node']['id']) || !isset($params['instance_id'])) return $node_array;
|
||||
|
||||
$options = UpdraftPlus_Options::get_updraft_option('updraft_googledrive');
|
||||
$instance_id = $params['instance_id'];
|
||||
|
||||
if (!isset($options['settings'][$instance_id])) return $node_array;
|
||||
|
||||
if ('#' == $params['node']['id']) {
|
||||
$search = 'root';
|
||||
} else {
|
||||
$search = $params['node']['id'];
|
||||
}
|
||||
|
||||
$googledrive = UpdraftPlus_Storage_Methods_Interface::get_storage_object('googledrive');
|
||||
$googledrive->set_options($options['settings'][$instance_id], false, $instance_id);
|
||||
$folders = $googledrive->list_folders($search);
|
||||
|
||||
if ($folders) {
|
||||
foreach ($folders as $folder) {
|
||||
$node_array[] = array(
|
||||
'text' => $folder['name'],
|
||||
'parent' => $params['node']['id'],
|
||||
'children' => true,
|
||||
'id' => $folder['id'],
|
||||
'icon' => 'jstree-folder',
|
||||
'data' => $folder
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $node_array;
|
||||
}
|
||||
}
|
||||
1377
wp-content/plugins/updraftplus/addons/googlecloud.php
Normal file
1377
wp-content/plugins/updraftplus/addons/googlecloud.php
Normal file
File diff suppressed because it is too large
Load Diff
376
wp-content/plugins/updraftplus/addons/importer.php
Normal file
376
wp-content/plugins/updraftplus/addons/importer.php
Normal file
@@ -0,0 +1,376 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: importer:Import a WordPress backup made by another backup plugin
|
||||
Description: Import a backup made by other supported WordPress backup plugins (see shop page for a list of supported plugins)
|
||||
Version: 3.1
|
||||
Shop: /shop/importer/
|
||||
Latest Change: 1.12.19
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
new UpdraftPlus_Addons_Importer;
|
||||
|
||||
class UpdraftPlus_Addons_Importer {
|
||||
|
||||
public function __construct() {
|
||||
add_filter('updraftplus_accept_archivename', array($this, 'accept_archivename'));
|
||||
add_filter('updraftplus_accept_archivename_js', array($this, 'accept_archivename_js'));
|
||||
add_filter('updraftplus_accept_foreign', array($this, 'accept_foreign'), 10, 2);
|
||||
add_filter('updraftplus_importforeign_backupable_plus_db', array($this, 'importforeign_backupable_plus_db'), 10, 2);
|
||||
add_filter('updraftplus_foreign_gettime', array($this, 'foreign_gettime'), 10, 3);
|
||||
add_filter('updraftplus_foreign_dbfilename', array($this, 'foreign_dbfilename'), 10, 5);
|
||||
add_filter('updraftplus_accepted_foreign_index', array($this, 'accepted_foreign_index'), 10, 3);
|
||||
add_filter('updraftplus_foreign_allow_missing_entity', array($this, 'foreign_allow_missing_entity'), 10, 3);
|
||||
add_filter('updraftplus_select_wpcore_file_with_db', array($this, 'select_wpcore_file_with_db'), 10, 2);
|
||||
add_filter('updraftplus_if_foreign_then_premium_message', array($this, 'if_foreign_then_premium_message'));
|
||||
}
|
||||
|
||||
public function foreign_allow_missing_entity($allow, $type, $foreign) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
// This plugin splits the backup over various sets
|
||||
return ('dropbox-wpadm' == $foreign) ? true : $allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* This only does something with multi-zip sets
|
||||
*
|
||||
* @param string $files
|
||||
* @param string $foreign
|
||||
* @return string
|
||||
*/
|
||||
public function select_wpcore_file_with_db($files, $foreign) {
|
||||
switch ($foreign) {
|
||||
case 'dropbox-wpadm':
|
||||
// The database gets put in the last one. If they ever change this, we can just scan all the files instead.
|
||||
sort($files);
|
||||
$last_one = array_pop($files);
|
||||
return array($last_one);
|
||||
break;
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
public function accepted_foreign_index($index, $entry, $accepted_foreign) {
|
||||
switch ($accepted_foreign) {
|
||||
case 'dropbox-wpadm':
|
||||
// e.g. example_com-full-2015_10_21_10_41-69.zip - where the last numeral runs from 1 upwards (they use multi-zip sets)
|
||||
if (preg_match('/^(.*)-full-([0-9]{4})_([0-9]{2})_([0-9]{2})_([0-9]{2})_([0-9]{2})-([0-9]+)\.zip$/i', $entry, $tmatch)) {
|
||||
$index = $tmatch[7] - 1;
|
||||
}
|
||||
}
|
||||
return $index;
|
||||
}
|
||||
|
||||
public function if_foreign_then_premium_message() {
|
||||
|
||||
$plugins = $this->accept_archivename(array());
|
||||
$supported = '';
|
||||
$already_added = array();
|
||||
foreach ($plugins as $plug) {
|
||||
if (!empty($plug['desc']) && !in_array($plug['desc'], $already_added)) {
|
||||
$supported .= ($supported) ? ', '.$plug['desc'] : $plug['desc'];
|
||||
$already_added[] = $plug['desc'];
|
||||
}
|
||||
}
|
||||
|
||||
return '<p><a href="https://teamupdraft.com/documentation/updraftplus/premium-features/how-to-restore-from-other-backup-plugins/?utm_source=udp-plugin&utm_medium=referral&utm_campaign=paac&utm_content=unknown&utm_creative_format=unknown" target="_blank">'.__('Was this a backup created by a different backup plugin? If so, then you might first need to rename it so that it can be recognized - please follow this link.', 'updraftplus').'</a></p><p>'.sprintf(__('Supported backup plugins: %s', 'updraftplus'), $supported).'</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a backup type and filename, get the time
|
||||
*
|
||||
* @param string $btime
|
||||
* @param string $accepted_foreign
|
||||
* @param string $entry
|
||||
* @return string
|
||||
*/
|
||||
public function foreign_gettime($btime, $accepted_foreign, $entry) {
|
||||
$plugins = $this->accept_archivename(array());
|
||||
if (empty($plugins[$accepted_foreign])) return $btime;
|
||||
// mktime(): H, M, S, M, D, Y
|
||||
switch ($accepted_foreign) {
|
||||
case 'infinitewp':
|
||||
if (preg_match('/_(([0-9]{4})-([0-9]{2})-([0-9]{2})_).*\.zip$/i', $entry, $tmatch)) {
|
||||
return mktime(12, 0, 0, $tmatch[3], $tmatch[4], $tmatch[2]);
|
||||
}
|
||||
break;
|
||||
case 'backupwordpress':
|
||||
case 'backupwordpress2':
|
||||
// e.g. example-com-default-1-complete-2014-03-10-11-44-57.zip
|
||||
if (preg_match('/(([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]{2})-([0-9]{2})-([0-9]{2}))\.zip$/i', $entry, $tmatch)) {
|
||||
return mktime($tmatch[5], $tmatch[6], $tmatch[7], $tmatch[3], $tmatch[4], $tmatch[2]);
|
||||
}
|
||||
break;
|
||||
case 'simple_backup':
|
||||
// e.g. db_backup_2014-03-15_133344.sql.gz | backup-2014-03-15-133345.zip
|
||||
// Note that a backup of both files and DB started at the same time may not have the same timestamp on both entities
|
||||
// Can also do tar and tar.gz and tar.bz2
|
||||
if (preg_match('/^(db_)?backup.([0-9]{4})-([0-9]{2})-([0-9]{2}).([0-9]{2})([0-9]{2})([0-9]{2})\.(zip|tar(\.(bz2|gz))?|sql(\.(gz))?)$/i', $entry, $tmatch)) {
|
||||
$btime = mktime($tmatch[5], $tmatch[6], $tmatch[7], $tmatch[3], $tmatch[4], $tmatch[2]);
|
||||
return $btime - ($btime % 60);
|
||||
}
|
||||
break;
|
||||
case 'backwpup':
|
||||
// e.g. backwpup_430908_2014-03-30_11-41-05.tar
|
||||
if (preg_match('/^backwpup_[0-9a-f]+_([0-9]{4})-([0-9]{2})-([0-9]{2})_([0-9]{2})-([0-9]{2})-([0-9]{2})\.(zip|tar|tar\.gz|tar\.bz2)/i', $entry, $tmatch)) {
|
||||
return mktime($tmatch[4], $tmatch[5], $tmatch[6], $tmatch[2], $tmatch[3], $tmatch[1]);
|
||||
}
|
||||
break;
|
||||
case 'dropbox-wpadm':
|
||||
// e.g. example_com-full-2015_10_21_10_41-69.zip - where the last numeral runs from 1 upwards (they use multi-zip sets)
|
||||
if (preg_match('/^(.*)-full-([0-9]{4})_([0-9]{2})_([0-9]{2})_([0-9]{2})_([0-9]{2})-([0-9]+)\.zip$/i', $entry, $tmatch)) {
|
||||
return mktime($tmatch[5], $tmatch[6], 0, $tmatch[3], $tmatch[4], $tmatch[2]);
|
||||
}
|
||||
break;
|
||||
case 'wpb2d':
|
||||
if (!class_exists('UpdraftPlus_PclZip')) updraft_try_include_file('includes/class-zip.php', 'include_once');
|
||||
global $updraftplus;
|
||||
$updraft_dir = trailingslashit($updraftplus->backups_dir_location());
|
||||
if (file_exists($updraft_dir.$entry) && class_exists('UpdraftPlus_PclZip')) {
|
||||
|
||||
$transkey = 'ud_forgt_'.md5($entry.filesize($updraft_dir.$entry));
|
||||
$trans = get_transient($transkey);
|
||||
if ($trans > 0) return $trans;
|
||||
|
||||
$zip = new UpdraftPlus_PclZip();
|
||||
$zip->ud_include_mtime();
|
||||
if (!$zip->open($updraft_dir.$entry)) {
|
||||
$updraftplus->log("Could not open zip file to examine (".$zip->last_error."); will remove: ".$entry);
|
||||
$btime = time();
|
||||
} else {
|
||||
|
||||
// Don't put this in the for loop, or the magic __get() method gets called every time the loop goes round
|
||||
$numfiles = $zip->numFiles;
|
||||
|
||||
if (false === $numfiles) $updraftplus->log("foreign_gettime(): could not read any files from the zip: (".basename($entry).") Zip error: (".$zip->last_error.")");
|
||||
|
||||
$latest_mtime = -1;
|
||||
|
||||
for ($i=0; $i < $numfiles; $i++) {
|
||||
$si = $zip->statIndex($i);
|
||||
if ('wp-content/backups/wordpress-db-backup.sql' == $si['name']) {
|
||||
@$zip->close();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
$btime = $si['mtime'];
|
||||
} elseif (preg_match('#wp-content/backups/(.*)\.sql$#i', $si['name'], $matches)) {
|
||||
if ($si['mtime'] > $latest_mtime) {
|
||||
$latest_mtime = $si['mtime'];
|
||||
$btime = $si['mtime'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@$zip->close();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
}
|
||||
set_transient($transkey, $btime, 86400*365);
|
||||
|
||||
return (false !== $btime) ? $btime : filemtime($updraft_dir.$entry);
|
||||
}
|
||||
return time();
|
||||
break;
|
||||
case 'genericsql':
|
||||
global $updraftplus;
|
||||
$updraft_dir = $updraftplus->backups_dir_location();
|
||||
// Using filemtime prevents a new backup being discovered each time 'rescan' is pressed
|
||||
return file_exists(trailingslashit($updraft_dir).$entry) ? filemtime(trailingslashit($updraft_dir).$entry) : time();
|
||||
break;
|
||||
}
|
||||
return $btime;
|
||||
}
|
||||
|
||||
public function foreign_dbfilename($db_basename, $fsource, $backupinfo, $working_dir_localpath, $separatedb) {
|
||||
|
||||
if ('backupwordpress2' == $fsource || 'backupwordpress' == $fsource) {
|
||||
if (is_array($backupinfo)) {
|
||||
if ($separatedb) {
|
||||
$filename = (is_array($backupinfo['db'])) ? $backupinfo['db'][0] : $backupinfo['db'];
|
||||
} else {
|
||||
$filename = (is_array($backupinfo['wpcore'])) ? $backupinfo['wpcore'][0] : $backupinfo['wpcore'];
|
||||
}
|
||||
if (preg_match('/^(.*)-(\d+)-(database|complete)-\d/i', $filename, $matches)) {
|
||||
$try_filename = 'database-'.$matches[1].'-'.$matches[2].'.sql';
|
||||
if (file_exists($working_dir_localpath.'/'.$try_filename)) {
|
||||
$db_basename = $try_filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ('dropbox-wpadm' == $fsource) {
|
||||
// wp-content/Dropbox_Backup/mysqldump.sql in the last zip file
|
||||
return 'wp-content/Dropbox_Backup/mysqldump.sql';
|
||||
} elseif ('backwpup' == $fsource || 'infinitewp' == $fsource) {
|
||||
// Infinite WP has no manifest - that's only in BackWPUp
|
||||
if (is_file($working_dir_localpath.'/manifest.json')) {
|
||||
$manifest = file_get_contents($working_dir_localpath.'/manifest.json');
|
||||
if (false != $manifest) {
|
||||
$decode = json_decode($manifest);
|
||||
if (!empty($decode) && is_object($decode) && is_object($decode->job_settings)) {
|
||||
$js = $decode->job_settings;
|
||||
if (!empty($js->dbdumptype) && 'sql' == $js->dbdumptype && !empty($js->dbdumpfile) && file_exists($working_dir_localpath.'/'.$js->dbdumpfile.'.sql')) return $js->dbdumpfile.'.sql';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$found_sql = false;
|
||||
$add_dir = ('infinitewp' == $fsource) ? '/iwp_db' : '';
|
||||
if ($handle = opendir($working_dir_localpath.$add_dir)) {
|
||||
while (($file = readdir($handle)) !== false) {
|
||||
if (strtolower(substr($file, -4, 4)) == '.sql') {
|
||||
if (is_string($found_sql)) {
|
||||
trigger_error("Multiple .sql files found in backwpup backup - couldn't work out which to use ($found_sql, $file)", array(), E_USER_WARNING); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- The escaping should be happening when the exception is printed
|
||||
return false;
|
||||
} else {
|
||||
$found_sql = 'iwp_db/'.(string) $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
if (is_string($found_sql)) return $found_sql;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
} elseif ('wpb2d' == $fsource) {
|
||||
|
||||
$latest_mtime = -1;
|
||||
$found_sql = false;
|
||||
|
||||
// Rather hack-ish
|
||||
if (file_exists($working_dir_localpath.'/wp-config.php') || file_exists($working_dir_localpath.'/wpb2d/wp-config.php')) {
|
||||
|
||||
$wp_config_file = file_exists($working_dir_localpath.'/wp-config.php') ? $working_dir_localpath.'/wp-config.php' : $working_dir_localpath.'/wpb2d/wp-config.php';
|
||||
|
||||
$wp_config = file($wp_config_file);
|
||||
foreach ($wp_config as $line) {
|
||||
if (!defined('UPDRAFTPLUS_OVERRIDE_IMPORT_PREFIX') && preg_match("#\\\$table_prefix\s+=\s+'(.*)';#", $line, $matches)) {
|
||||
global $updraftplus;
|
||||
$updraftplus->log("Import table prefix is: ".$matches[1]);
|
||||
define('UPDRAFTPLUS_OVERRIDE_IMPORT_PREFIX', $matches[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$backups_dir = file_exists($working_dir_localpath.'/wpb2d/wp-content/backups') ? 'wpb2d/wp-content/backups' : 'wp-content/backups';
|
||||
|
||||
if ($handle = opendir($working_dir_localpath.'/'.$backups_dir)) {
|
||||
while (($file = readdir($handle)) !== false) {
|
||||
if (strtolower(substr($file, -4, 4)) == '.sql') {
|
||||
if (filemtime($working_dir_localpath.'/'.$backups_dir.'/'.$file) > $latest_mtime) {
|
||||
$latest_mtime = filemtime($working_dir_localpath.'/'.$backups_dir.'/'.$file);
|
||||
$found_sql = (string) $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
if (is_string($found_sql)) return $backups_dir.'/'.$found_sql;
|
||||
}
|
||||
|
||||
$db_basename = $backups_dir.'/wordpress-db-backup.sql';
|
||||
} elseif (!$separatedb) {
|
||||
$db_basename = $backupinfo['wpcore'];
|
||||
if (is_array($db_basename)) $db_basename = array_shift($db_basename);
|
||||
$db_basename = basename($db_basename, '.zip').'.sql';
|
||||
}
|
||||
return $db_basename;
|
||||
}
|
||||
|
||||
public function importforeign_backupable_plus_db($backupable_plus_db, $args) {
|
||||
$foinfo = $args[0];
|
||||
$mess = &$args[1];
|
||||
$mess[] = sprintf(__('Backup created by: %s.', 'updraftplus'), $foinfo['desc']);
|
||||
return array('wpcore' => $backupable_plus_db['wpcore']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan filename and see if we recognise its pattern
|
||||
*
|
||||
* @param string $accepted_foreign
|
||||
* @param string $entry
|
||||
* @return string
|
||||
*/
|
||||
public function accept_foreign($accepted_foreign, $entry) {
|
||||
|
||||
$accept = $this->accept_archivename(array());
|
||||
foreach ($accept as $fsource => $acc) {
|
||||
if (preg_match('/'.$acc['pattern'].'/i', $entry)) $accepted_foreign = $fsource;
|
||||
}
|
||||
return $accepted_foreign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of supported backup types
|
||||
*
|
||||
* @param string $x [description]
|
||||
* @return string
|
||||
*/
|
||||
public function accept_archivename($x) {
|
||||
if (!is_array($x)) return $x;
|
||||
|
||||
$x['backupwordpress'] = array(
|
||||
'desc' => 'BackUpWordPress',
|
||||
'pattern' => 'complete-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}\\.zip$',
|
||||
'separatedb' => false
|
||||
);
|
||||
|
||||
$x['backupwordpress2'] = array(
|
||||
'desc' => 'BackUpWordPress',
|
||||
'pattern' => 'database-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}\\.zip$',
|
||||
'separatedb' => true
|
||||
);
|
||||
|
||||
$x['simple_backup'] = array(
|
||||
'desc' => 'Simple Backup',
|
||||
'pattern' => '^(db_)?backup.([0-9]{4})-([0-9]{2})-([0-9]{2}).([0-9]{2})([0-9]{2})([0-9]{2})\\.(zip|tar(\\.(bz2|gz))?|sql(\\.(gz))?)$',
|
||||
'separatedb' => true
|
||||
);
|
||||
|
||||
$x['backwpup'] = array(
|
||||
'desc' => 'BackWPup',
|
||||
'pattern' => '^backwpup_[0-9a-f]+_([0-9]{4})-([0-9]{2})-([0-9]{2})_([0-9]{2})-([0-9]{2})-([0-9]{2})\\.(zip|tar(\\.(gz|bz2))?)$',
|
||||
'separatedb' => false
|
||||
);
|
||||
|
||||
$x['dropbox-wpadm'] = array(
|
||||
'desc' => 'Dropbox Backup by WPAdm',
|
||||
'pattern' => '^.*-full-([0-9]{4})_([0-9]{2})_([0-9]{2})_([0-9]{2})_([0-9]{2})-([0-9]+)\\.zip$',
|
||||
'separatedb' => false
|
||||
);
|
||||
|
||||
$x['wpb2d'] = array(
|
||||
'desc' => 'WordPress Backup To Dropbox',
|
||||
'pattern' => 'wpb2d.*\\.zip$',
|
||||
'separatedb' => false
|
||||
);
|
||||
|
||||
$x['infinitewp'] = array(
|
||||
'desc' => 'InfiniteWP',
|
||||
'pattern' => '^.*_backup_(files|db)_([0-9]{4})-([0-9]{2})-([0-9]{2})_([0-9a-f]+)\\.zip$',
|
||||
'separatedb' => true
|
||||
);
|
||||
|
||||
$x['genericsql'] = array(
|
||||
'desc' => '(Generic SQL backup)',
|
||||
'pattern' => '\\.sql(\.(bz2|gz))?$',
|
||||
'separatedb' => true
|
||||
);
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JavaScript array of supported backup types
|
||||
*
|
||||
* @param string $x [description]
|
||||
* @return string
|
||||
*/
|
||||
public function accept_archivename_js($x) {
|
||||
// backup_([\-0-9]{15})_.*_([0-9a-f]{12})-[\-a-z]+([0-9]+(of[0-9]+)?)?\.(zip|gz|gz\.crypt)
|
||||
$accepted = $this->accept_archivename(array());
|
||||
$x = '[ ';
|
||||
$ind = 0;
|
||||
foreach ($accepted as $acc) {
|
||||
if ($ind > 0) $x .= ', ';
|
||||
$x .= "/".esc_js($acc['pattern'])."/i";
|
||||
$ind++;
|
||||
}
|
||||
return $x.' ]';
|
||||
}
|
||||
}
|
||||
498
wp-content/plugins/updraftplus/addons/incremental.php
Normal file
498
wp-content/plugins/updraftplus/addons/incremental.php
Normal file
@@ -0,0 +1,498 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: incremental:Support for incremental backups
|
||||
Description: Allows UpdraftPlus to schedule incremental file backups, which use much less resources
|
||||
Version: 1.2
|
||||
Shop: /shop/incremental/
|
||||
Latest Change: 1.14.5
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
new UpdraftPlus_Addons_Incremental;
|
||||
|
||||
class UpdraftPlus_Addons_Incremental {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter('updraftplus_incremental_backup_link', array($this, 'incremental_backup_link'), 10, 1);
|
||||
// Priority 11 so that it loads after the filter that adds the backup label
|
||||
add_filter('updraftplus_showbackup_date', array($this, 'showbackup_date'), 11, 5);
|
||||
add_filter('updraftplus_files_altered_since', array($this, 'files_altered_since'), 10, 2);
|
||||
add_filter('updraft_backupnow_options', array($this, 'backupnow_options'), 8, 2);
|
||||
add_filter('updraftplus_initial_jobdata', array($this, 'initial_jobdata_incremental_jobdata'), 10, 2);
|
||||
add_filter('updraftplus_save_backup_history_timestamp', array($this, 'incremental_backup_timestamp'), 10, 1);
|
||||
add_filter('updraftplus_base_backup_timestamp', array($this, 'incremental_backup_timestamp'), 10, 1);
|
||||
add_filter('updraftplus_merge_backup_history', array($this, 'merge_backup_history'), 10, 2);
|
||||
add_filter('updraftplus_include_manifest', array($this, 'incremental_include_manifest'), 10, 1);
|
||||
add_filter('updraft_backupnow_modal_afterfileoptions', array($this, 'backupnow_modal_afterfileoptions'), 5, 1);
|
||||
add_filter('updraftplus_backupnow_file_entities', array($this, 'get_impossible_incremental_file_options'), 10, 1);
|
||||
add_filter('updraftplus_incremental_addon_installed', '__return_true');
|
||||
add_filter('updraftplus_prepare_incremental_run', array($this, 'prepare_incremental_run'), 10, 2);
|
||||
add_action('updraftplus_incremental_cell', array($this, 'incremental_cell'), 10, 2);
|
||||
add_action('updraft_backup_increments', array($this, 'backup_increments'));
|
||||
add_action('updraftplus_admin_enqueue_scripts', array($this, 'updraftplus_admin_enqueue_scripts'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WP action updraftplus_admin_enqueue_scripts
|
||||
*/
|
||||
public function updraftplus_admin_enqueue_scripts() {
|
||||
add_action('admin_footer', array($this, 'admin_footer_incremental_backups_js'));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called via a filter and will replace the incremental backup link in the free version
|
||||
*
|
||||
* @param string $link - the incremental backup link
|
||||
*
|
||||
* @return string - the premium backup link
|
||||
*/
|
||||
public function incremental_backup_link($link) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Unused parameter is present because the method is used as a WP filter.
|
||||
return '<p><a href="#" id="updraftplus_incremental_backup_link" onclick="updraft_backup_dialog_open(\'incremental\'); return false;" data-incremental="1">' . __('Add changed files (incremental backup) ...', 'updraftplus') . '</a></p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will add to the backup label information on when the last incremental set was created, it will also add to the title the dates for all the incremental sets in this backup.
|
||||
*
|
||||
* @param string $date - the date when the backup set was first created
|
||||
* @param array $backup - the backup set
|
||||
* @param array $jobdata - an array of information relating to the backup job
|
||||
* @param integer $backup_date - the timestamp of when the backup set was first created
|
||||
* @param boolean $simple_format - a boolean value to indicate if this should be a simple format date
|
||||
*
|
||||
* @return string - returns a string that is either the original backup date or the string that contains the incremental set data
|
||||
*/
|
||||
public function showbackup_date($date, $backup, $jobdata, $backup_date, $simple_format) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
|
||||
$incremental_sets = !empty($backup['incremental_sets']) ? $backup['incremental_sets'] : array();
|
||||
|
||||
// Check here that the backup set has the incremental set and that there is more than one set as we don't want the incremental backup UI showing for every user backup
|
||||
if (!empty($incremental_sets) && 1 < count($incremental_sets)) {
|
||||
|
||||
$latest_increment = key(array_slice($incremental_sets, -1, 1, true));
|
||||
|
||||
if ($latest_increment > $backup_date) {
|
||||
|
||||
$increment_times = '';
|
||||
|
||||
foreach ($incremental_sets as $inc_time => $entities) {
|
||||
if ($increment_times) $increment_times .= '; ';
|
||||
// Format the incremental backup time to users local time
|
||||
$formatted_date = get_date_from_gmt(date('M d, Y G:i', $inc_time), 'M d, Y G:i');
|
||||
$increment_times .= $formatted_date;
|
||||
}
|
||||
|
||||
if ($simple_format) {
|
||||
return $date.' '.sprintf(__('(latest increment: %s)', 'updraftplus'), $formatted_date);
|
||||
} else {
|
||||
return '<span title="'.sprintf(__('Increments exist at: %s', 'updraftplus'), $increment_times).'">'.$date.'<br>'.sprintf(__('(latest increment: %s)', 'updraftplus'), $formatted_date).'</span>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will get an return the files_enumerated_at array if it is set otherwise returns an empty array
|
||||
*
|
||||
* @param integer $altered_since - integer for files altered since this time default is -1
|
||||
* @param string $job_type - a string to indicate the job type
|
||||
*
|
||||
* @return integer|array - returns the default integer if this is not a backup job other wise returns the files_enumerated_at array or an empty array if not set
|
||||
*/
|
||||
public function files_altered_since($altered_since, $job_type) {
|
||||
global $updraftplus;
|
||||
|
||||
if ('incremental' !== $job_type) return $altered_since;
|
||||
|
||||
$backup_history = UpdraftPlus_Backup_History::get_backup_set_by_nonce($updraftplus->file_nonce);
|
||||
$files_enumerated_at = isset($backup_history['files_enumerated_at']) ? $backup_history['files_enumerated_at'] : array();
|
||||
|
||||
return is_array($files_enumerated_at) ? $files_enumerated_at : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will check to see if the incremental option is set and if so adds it to the backup job options
|
||||
*
|
||||
* @param array $options - the backup job options
|
||||
* @param array $request - the backup request array
|
||||
*
|
||||
* @return array - returns the modified backup job options
|
||||
*/
|
||||
public function backupnow_options($options, $request) {
|
||||
if (!is_array($options)) return $options;
|
||||
|
||||
if (!empty($request['incremental'])) {
|
||||
$options['incremental'] = $request['incremental'];
|
||||
if (!empty($request['backupnow_label'])) unset($request['backupnow_label']);
|
||||
// remove from the options array directly as it's already been added before we get here.
|
||||
if (!empty($options['always_keep'])) unset($options['always_keep']);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will set up the backup job data for when we are starting a incremental backup. It changes the initial jobdata so that UpdraftPlus knows to start a incremental backup job.
|
||||
*
|
||||
* @param array $jobdata - the initial job data that we want to change
|
||||
* @param array $options - options sent from the front end includes backup timestamp and nonce
|
||||
*
|
||||
* @return array - the modified jobdata
|
||||
*/
|
||||
public function initial_jobdata_incremental_jobdata($jobdata, $options) {
|
||||
|
||||
if (!is_array($jobdata) || empty($options['incremental'])) return $jobdata;
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
/*
|
||||
The initial job data is not set up in a key value array instead it is set up so key "x" is the name of the key and then key "y" is the value.
|
||||
e.g array[0] = 'backup_name' array[1] = 'my_backup'
|
||||
|
||||
Note: we use strict comparison here to avoid PHP treating (String)"value" == 1 as true and giving us the wrong keys
|
||||
*/
|
||||
$jobtype_key = array_search('job_type', $jobdata, true) + 1;
|
||||
$job_file_entities_key = array_search('job_file_entities', $jobdata, true) + 1;
|
||||
$job_backup_time = array_search('backup_time', $jobdata, true) + 1;
|
||||
$backup_database_key = array_search('backup_database', $jobdata, true) + 1;
|
||||
|
||||
$backup_history = UpdraftPlus_Backup_History::get_backup_set_by_nonce($updraftplus->file_nonce);
|
||||
$possible_backups = $updraftplus->get_backupable_file_entities(true);
|
||||
|
||||
$job_file_entities = $jobdata[$job_file_entities_key];
|
||||
$job_backup_files_array = array();
|
||||
|
||||
foreach ($possible_backups as $youwhat => $whichdir) {
|
||||
if (isset($job_file_entities[$youwhat]) && isset($backup_history[$youwhat])) {
|
||||
$job_file_entities[$youwhat]['index'] = count($backup_history[$youwhat]);
|
||||
$job_backup_files_array[$youwhat] = $backup_history[$youwhat];
|
||||
if (isset($backup_history[$youwhat.'-size'])) {
|
||||
$job_backup_files_array[$youwhat.'-size'] = $backup_history[$youwhat.'-size'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$previous_job_files_array = $job_backup_files_array;
|
||||
|
||||
$db_backups = $jobdata[$backup_database_key];
|
||||
|
||||
$db_backup_info = $updraftplus->update_database_jobdata($db_backups, $backup_history);
|
||||
|
||||
$jobdata[$jobtype_key] = 'incremental';
|
||||
$backup_time_was = $jobdata[$job_backup_time];
|
||||
$jobdata[$job_backup_time] = $backup_history['timestamp'];
|
||||
$jobdata[$job_file_entities_key] = $job_file_entities;
|
||||
$jobdata[] = 'backup_files_array';
|
||||
$jobdata[] = $job_backup_files_array;
|
||||
$jobdata[] = 'previous_backup_files_array';
|
||||
$jobdata[] = $previous_job_files_array;
|
||||
$jobdata[] = 'blog_name';
|
||||
$jobdata[] = $db_backup_info['blog_name'];
|
||||
$jobdata[$backup_database_key] = $db_backup_info['db_backups'];
|
||||
$jobdata[] = 'incremental_run_start';
|
||||
$jobdata[] = $backup_time_was;
|
||||
|
||||
if (isset($backup_history['morefiles_linked_indexes']) && isset($backup_history['morefiles_more_locations'])) {
|
||||
$jobdata[] = 'morefiles_linked_indexes';
|
||||
$jobdata[] = $backup_history['morefiles_linked_indexes'];
|
||||
$jobdata[] = 'morefiles_more_locations';
|
||||
$jobdata[] = $backup_history['morefiles_more_locations'];
|
||||
}
|
||||
|
||||
return $jobdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will merge the incremental backup array with the existing backup history. This is desirable because a manual incremental run (e.g. plugins only) won't contain job-data on the excluded entities; saving the current job-data would therefore result in that data being lost.
|
||||
*
|
||||
* @param array $job_backup_array - the incremental backup set
|
||||
* @param array $history_backup_array - the full backup history set
|
||||
*
|
||||
* @return array - returns the full backup history after the merge
|
||||
*/
|
||||
public function merge_backup_history($job_backup_array, $history_backup_array) {
|
||||
global $updraftplus;
|
||||
|
||||
if ('incremental' != $updraftplus->jobdata_get('job_type')) return $job_backup_array;
|
||||
|
||||
$history_backup_array = $this->recursive_backup_history_merge($history_backup_array, $job_backup_array);
|
||||
|
||||
return $history_backup_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will perform a recursive merge on the backup history using the passed in array to merge
|
||||
*
|
||||
* @param array $history_backup_array - the full backup history
|
||||
* @param array $job_backup_array - the array to merge into the backup history
|
||||
*
|
||||
* @return array - the new full backup history
|
||||
*/
|
||||
private function recursive_backup_history_merge($history_backup_array, $job_backup_array){
|
||||
|
||||
foreach ($job_backup_array as $key => $data) {
|
||||
if (is_array($data)) {
|
||||
$history_backup_array[$key] = isset($history_backup_array[$key]) ? $this->recursive_backup_history_merge($history_backup_array[$key], $data) : $data;
|
||||
} else {
|
||||
$history_backup_array[$key] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
return $history_backup_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will filter the passed in timestamp, it will check that this is an incremental run and will return the timestamp from the jobdata so that the increment will be saved in the original backup.
|
||||
*
|
||||
* @param string $timestamp - the backup timestamp
|
||||
*
|
||||
* @return string - returns the incremental backup timestamp
|
||||
*/
|
||||
public function incremental_backup_timestamp($timestamp) {
|
||||
global $updraftplus;
|
||||
|
||||
if ('incremental' != $updraftplus->jobdata_get('job_type')) return $timestamp;
|
||||
|
||||
$timestamp = $updraftplus->jobdata_get('backup_time');
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will filter and return a boolean to indicate if the backup should include a manifest or not
|
||||
*
|
||||
* @param boolean $include - a boolean to indicate if we should include a manifest in the backup
|
||||
*
|
||||
* @return boolean - returns a boolean to indicate if we should include a manifest in the backup
|
||||
*/
|
||||
public function incremental_include_manifest($include) {
|
||||
global $updraftplus;
|
||||
|
||||
if ('incremental' != $updraftplus->jobdata_get('job_type')) return $include;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will add a checkbox to the existing backupnow modal content, which allows the user to specify if this manual backup should be an incremental one or not, if the user does not have an existing backup that is suitable to add increments to then the checkbox will be disabled.
|
||||
*
|
||||
* @param string $ret - the backup now modal content
|
||||
*
|
||||
* @return string - content to add to the backupnow modal
|
||||
*/
|
||||
public function backupnow_modal_afterfileoptions($ret) {
|
||||
|
||||
$entities = UpdraftPlus_Backup_History::get_existing_backup_entities();
|
||||
|
||||
if (!empty($entities)) {
|
||||
$ret .= '<p id="incremental_container" class="incremental-backups-only"><input type="hidden" id="incremental" data-incremental="1" value="1"> <label for="incremental">' . __('Files changed since the last backup will be added as a new increment in that backup set.', 'updraftplus').' '.__('N.B. No backup of your database will be taken in an incremental backup; if you want a database backup as well, then take that separately.', 'updraftplus').'</label></p>';
|
||||
} else {
|
||||
$ret .= '<p id="incremental_container" class="incremental-backups-only"><input type="hidden" id="incremental" data-incremental="0" value="0"><span> <em>' . __("No incremental backup of your files is possible, as no suitable existing backup was found to add increments to.", 'updraftplus') . '</em></span></p>';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return an array of impossible file entities that we cannot add increments to
|
||||
*
|
||||
* @param array $file_entities - an array of file entities
|
||||
*
|
||||
* @return array - an array of file entities we cannot add an increment to
|
||||
*/
|
||||
public function get_impossible_incremental_file_options($file_entities) {
|
||||
global $updraftplus;
|
||||
|
||||
$entities = UpdraftPlus_Backup_History::get_existing_backup_entities();
|
||||
|
||||
$backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
|
||||
|
||||
foreach ($backupable_entities as $key => $info) {
|
||||
if (!in_array($key, $entities)) $file_entities[] = $key;
|
||||
}
|
||||
|
||||
return $file_entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of incremental backup intervals
|
||||
*
|
||||
* @return Array - keys are used as identifiers in the UI drop-down; values are user-displayed text describing the interval
|
||||
*/
|
||||
private function get_intervals() {
|
||||
global $updraftplus;
|
||||
if ($updraftplus->is_restricted_hosting('only_one_incremental_per_day')) {
|
||||
$intervals = array(
|
||||
'none' => __("None", 'updraftplus'),
|
||||
'daily' => __("Daily", 'updraftplus'),
|
||||
'weekly' => __("Weekly", 'updraftplus'),
|
||||
'fortnightly' => __("Fortnightly", 'updraftplus'),
|
||||
'monthly' => __("Monthly", 'updraftplus')
|
||||
);
|
||||
} else {
|
||||
$intervals = array(
|
||||
'none' => __("None", 'updraftplus'),
|
||||
'everyhour' => __("Every hour", 'updraftplus'),
|
||||
'every2hours' => sprintf(__("Every %s hours", 'updraftplus'), '2'),
|
||||
'every4hours' => sprintf(__("Every %s hours", 'updraftplus'), '4'),
|
||||
'every8hours' => sprintf(__("Every %s hours", 'updraftplus'), '8'),
|
||||
'twicedaily' => sprintf(__("Every %s hours", 'updraftplus'), '12'),
|
||||
'daily' => __("Daily", 'updraftplus'),
|
||||
'weekly' => __("Weekly", 'updraftplus'),
|
||||
'fortnightly' => __("Fortnightly", 'updraftplus'),
|
||||
'monthly' => __("Monthly", 'updraftplus')
|
||||
);
|
||||
}
|
||||
return apply_filters('updraftplus_backup_intervals_increments', $intervals);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called via the action updraftplus_incremental_cell and will add UI options to schedule incremental backups.
|
||||
*
|
||||
* @param string $selected_interval - the interval that is currently selected
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function incremental_cell($selected_interval) {
|
||||
?>
|
||||
<div>
|
||||
<?php esc_html_e('And then add an incremental backup', 'updraftplus'); ?>
|
||||
<select id="updraft_interval_increments" name="updraft_interval_increments">
|
||||
<?php
|
||||
$intervals = $this->get_intervals();
|
||||
$selected_interval = UpdraftPlus_Options::get_updraft_option('updraft_interval_increments', 'none');
|
||||
foreach ($intervals as $cronsched => $descrip) {
|
||||
echo "<option value=\"".esc_attr($cronsched)."\" ";
|
||||
if ($cronsched == $selected_interval) echo 'selected="selected"';
|
||||
echo ">".esc_html($descrip)."</option>\n";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<?php echo '<a href="' . esc_url(apply_filters('updraftplus_com_link', "https://teamupdraft.com/updraftplus/features/wordpress-incremental-backup?utm_source=udp-plugin&utm_medium=referral&utm_campaign=paac&utm_content=tell-me-more&utm_creative_format=text")) . '" aria-label="'. esc_attr__('Tell me more about incremental backups', 'updraftplus') .'" target="_blank">' . esc_html__('Tell me more', 'updraftplus') . '</a>'; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will setup and check that an incremental backup can be started. It is called by the WP action updraft_backup_increments (which gets scheduled)
|
||||
*/
|
||||
public function backup_increments() {
|
||||
global $updraftplus;
|
||||
|
||||
$selected_interval = UpdraftPlus_Options::get_updraft_option('updraft_interval_increments', 'none');
|
||||
|
||||
if ('none' === $selected_interval) {
|
||||
// Handle WP-Cron being inconsistent with the saved options
|
||||
$updraftplus->log("No incremental backup is configured in the saved settings; will not run");
|
||||
return;
|
||||
}
|
||||
|
||||
$running = $updraftplus->is_backup_running();
|
||||
if ($running) {
|
||||
$updraftplus->log($running);
|
||||
return;
|
||||
}
|
||||
|
||||
$backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
|
||||
|
||||
if (!function_exists('get_mu_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
|
||||
$mu_plugins = get_mu_plugins();
|
||||
|
||||
$entities = array();
|
||||
|
||||
foreach ($backupable_entities as $key => $info) {
|
||||
if (UpdraftPlus_Options::get_updraft_option("updraft_include_$key", false)) {
|
||||
if ('mu-plugins' == $key && !$mu_plugins) continue;
|
||||
$entities[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
// No incremental run is possible at this time, so bail out
|
||||
if (!$this->prepare_incremental_run(false, $entities)) return;
|
||||
|
||||
// The call to backup_time_nonce() allows us to know the nonce in advance, and return it
|
||||
$nonce = $updraftplus->backup_time_nonce();
|
||||
|
||||
$options = array('use_nonce' => $nonce);
|
||||
$request = array('incremental' => true);
|
||||
|
||||
$updraftplus->boot_backup(true, false, false, false, false, apply_filters('updraft_backupnow_options', $options, $request));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will prepare the incremental run by setting up the correct backup file nonce to use
|
||||
*
|
||||
* @param boolean $incremental - filter value to decide if we should run an incremental run or not
|
||||
* @param array $entities - an array of entities in this backup run
|
||||
*
|
||||
* @return boolean - to indicate whether or not (e.g. no full backup was found) an incremental run can proceed
|
||||
*/
|
||||
public function prepare_incremental_run($incremental = false, $entities = array()) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
global $updraftplus;
|
||||
|
||||
$nonce = UpdraftPlus_Backup_History::get_latest_backup($entities);
|
||||
if (empty($nonce)) return false;
|
||||
|
||||
$updraftplus->file_nonce = $nonce;
|
||||
add_filter('updraftplus_incremental_backup_file_nonce', array($updraftplus, 'incremental_backup_file_nonce'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will output any needed js for the incremental backup addon.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function admin_footer_incremental_backups_js() {
|
||||
?>
|
||||
<script>
|
||||
jQuery(function() {
|
||||
<?php
|
||||
$intervals = $this->get_intervals();
|
||||
// wp_json_encode() was added in WP 4.1
|
||||
$intervals_json = function_exists('wp_json_encode') ? wp_json_encode($intervals) : json_encode($intervals);
|
||||
echo 'var intervals = '.$intervals_json."\n";
|
||||
?>
|
||||
function updraft_update_incremental_selector() {
|
||||
var fileint = jQuery('#updraft-navtab-settings-content select.updraft_interval').val();
|
||||
var prevsel = jQuery('#updraft-navtab-settings-content select#updraft_interval_increments').val();
|
||||
|
||||
var newhtml = '';
|
||||
var adding = 1;
|
||||
for (var key in intervals) {
|
||||
if (key == fileint) { adding = 0; }
|
||||
if (1 == adding) {
|
||||
if ('manual' == fileint && 'none' != key) continue;
|
||||
var value = intervals[key];
|
||||
var sel = '';
|
||||
if (prevsel == key) { sel = 'selected="selected" '; }
|
||||
newhtml += '<option '+sel+'value="'+key+'">'+value+'</option>';
|
||||
}
|
||||
}
|
||||
var $increments_selector = jQuery('#updraft-navtab-settings-content select#updraft_interval_increments');
|
||||
$increments_selector.attr('disabled', false);
|
||||
$increments_selector.html(newhtml)
|
||||
if ($increments_selector.find("option").length <= 1) {
|
||||
$increments_selector.attr('disabled', true);
|
||||
}
|
||||
}
|
||||
|
||||
jQuery('#updraft-navtab-settings-content select.updraft_interval, #updraft-navtab-settings-content select#updraft_interval_increments').on('change', function() {
|
||||
updraft_update_incremental_selector();
|
||||
});
|
||||
|
||||
// Set initial values
|
||||
updraft_update_incremental_selector();
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
286
wp-content/plugins/updraftplus/addons/lockadmin.php
Normal file
286
wp-content/plugins/updraftplus/addons/lockadmin.php
Normal file
@@ -0,0 +1,286 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: lockadmin:Password-protect the UpdraftPlus Settings Screen
|
||||
Description: Provides the ability to lock the UpdraftPlus settings with a password
|
||||
Version: 1.3
|
||||
Shop: /shop/lockadmin/
|
||||
Latest Change: 1.14.3
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
if (defined('UPDRAFTPLUS_NOADMINLOCK') && UPDRAFTPLUS_NOADMINLOCK) return;
|
||||
|
||||
$GLOBALS['updraftplus_addon_lockadmin'] = new UpdraftPlus_Addon_LockAdmin;
|
||||
|
||||
class UpdraftPlus_Addon_LockAdmin {
|
||||
|
||||
private $correct_password_supplied = null;
|
||||
|
||||
private $default_support_url = 'https://updraftplus.com/faqs/locked-updraftplus-settings-page-forgotten-password-unlock/';
|
||||
|
||||
/**
|
||||
* Password set by user for locking UpdraftPlus settings page.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $old_password = '';
|
||||
|
||||
/**
|
||||
* Length of $this->opts['password'].
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $password_length = 0;
|
||||
|
||||
/**
|
||||
* Stores password, session and other data
|
||||
*
|
||||
* @var Array
|
||||
*/
|
||||
private $opts;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter('updraftplus_settings_page_render', array($this, 'settings_page_render'));
|
||||
add_action('updraftplus_settings_page_render_abort', array($this, 'settings_page_render_abort'));
|
||||
if ((!empty($_POST['updraft_unlockadmin_session_length']) || !empty($_POST['updraft_unlockadmin_password'])) && !empty($_POST['nonce'])) add_action('admin_init', array($this, 'admin_init'));
|
||||
add_action('updraftplus_debugtools_dashboard', array($this, 'debugtools_dashboard'), 10);
|
||||
}
|
||||
|
||||
private function check_user_cookie($password) {
|
||||
if (empty($password)) return true;
|
||||
// Value in seconds
|
||||
$session_length = $this->opts['session_length'];
|
||||
if (!$session_length) $session_length = 86400;
|
||||
|
||||
// A lock has been set. Has the user passed the test?
|
||||
if (empty($_COOKIE['updraft_unlockadmin'])) return false;
|
||||
|
||||
// Cookie in correct format?
|
||||
if (!preg_match('/^(\d+):(.*)$/', $_COOKIE['updraft_unlockadmin'], $matches)) return false;
|
||||
|
||||
$cookie_time = $matches[1]; // The time when the session began
|
||||
$cookie_hash = $matches[2];
|
||||
|
||||
$time_now = time();
|
||||
|
||||
// Cookie is older than session length
|
||||
if ($time_now > $cookie_time + $session_length) return false;
|
||||
|
||||
$cookie_session_began = $cookie_time - ($cookie_time % $session_length);
|
||||
|
||||
$user = wp_get_current_user();
|
||||
if (!is_a($user, 'WP_User')) return false;
|
||||
|
||||
// The cookie relies on the user ID, password and session time. So, someone stealing the cookie can't use it forever. They need the password to generate valid cookies.
|
||||
$correct_hash = hash('sha256', $user->ID.'-'.$password.'-'.$cookie_session_began);
|
||||
|
||||
if ($correct_hash != $cookie_hash) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function return_opts() {
|
||||
$this->get_opts();
|
||||
return $this->opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get standard session length options
|
||||
*
|
||||
* @return array Session length options with values and labels
|
||||
*/
|
||||
public function get_session_length_options() {
|
||||
return array(
|
||||
'3600' => __('1 hour', 'updraftplus'),
|
||||
'10800' => sprintf(__('%s hours', 'updraftplus'), 3),
|
||||
'86400' => sprintf(__('%s hours', 'updraftplus'), 24),
|
||||
'604800' => __('1 week', 'updraftplus'),
|
||||
'2419200' => sprintf(__('%s weeks', 'updraftplus'), 4),
|
||||
'31449600' => sprintf(__('%s weeks', 'updraftplus'), 52)
|
||||
);
|
||||
}
|
||||
|
||||
private function get_opts() {
|
||||
$this->opts = UpdraftPlus_Options::get_updraft_option('updraft_adminlocking');
|
||||
if (!is_array($this->opts)) $this->opts = array();
|
||||
if (!isset($this->opts['password'])) $this->opts['password'] = '';
|
||||
if (!isset($this->opts['session_length'])) $this->opts['session_length'] = 3600;
|
||||
if (!isset($this->opts['support_url'])) $this->opts['support_url'] = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WP action admin_init, but only if there's appropriate data in $_POST
|
||||
*/
|
||||
public function admin_init() {
|
||||
|
||||
if ((empty($_POST['updraft_unlockadmin_session_length']) && empty($_POST['updraft_unlockadmin_password'])) || empty($_POST['nonce'])) return;
|
||||
|
||||
if (!wp_verify_nonce($_POST['nonce'], 'updraftplus-unlockadmin-nonce')) return;
|
||||
|
||||
$user = wp_get_current_user();
|
||||
if (!is_a($user, 'WP_User')) return;
|
||||
|
||||
$this->get_opts();
|
||||
|
||||
if (!empty($_POST['updraft_unlockadmin_session_length']) && isset($_POST['updraft_unlockadmin_oldpassword']) && $_POST['updraft_unlockadmin_oldpassword'] == $this->opts['password']) {
|
||||
$this->old_password = $this->opts['password'];
|
||||
$this->opts['password'] = $_POST['updraft_unlockadmin_password'];
|
||||
$this->opts['support_url'] = $_POST['updraft_unlockadmin_support_url'];
|
||||
$this->opts['session_length'] = (int) $_POST['updraft_unlockadmin_session_length'];
|
||||
UpdraftPlus_Options::update_updraft_option('updraft_adminlocking', $this->opts);
|
||||
$this->password_length = strlen($this->opts['password']);
|
||||
add_action('all_admin_notices', array($this, 'show_admin_warning_passwordset'));
|
||||
}
|
||||
|
||||
// Note: this code also fires when the user sets a new password (because we don't want to immediately lock them)
|
||||
$password = $this->opts['password'];
|
||||
if ($password === (string) $_POST['updraft_unlockadmin_password']) {
|
||||
$session_length = (int) $this->opts['session_length'];
|
||||
if ($session_length<1) $session_length = 86400;
|
||||
// The cookie relies on the user ID, password and session time. So, someone stealing the cookie can't use it forever. They need the password to generate valid cookies.
|
||||
$time_now = time();
|
||||
$expire = $time_now + $session_length;
|
||||
$cookie_session_began = $time_now - ($time_now % $session_length);
|
||||
$correct_hash = hash('sha256', $user->ID.'-'.$password.'-'.$cookie_session_began);
|
||||
$secure = apply_filters('secure_auth_cookie', is_ssl(), $user->ID);
|
||||
setcookie('updraft_unlockadmin', $cookie_session_began.':'.$correct_hash, $expire, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, $secure, true);
|
||||
$this->correct_password_supplied = true;
|
||||
} else {
|
||||
$this->correct_password_supplied = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function show_admin_warning_passwordset() {
|
||||
$msg = '<strong>';
|
||||
if (strlen($this->old_password) >0 && 0 == $this->password_length) {
|
||||
$msg .= __('The admin password has now been removed.', 'updraftplus');
|
||||
} elseif (strlen($this->old_password) == 0 && $this->password_length > 0) {
|
||||
$msg .= __('An admin password has been set.', 'updraftplus');
|
||||
} elseif ($this->old_password !== $this->opts['password']) {
|
||||
$msg .= __('The admin password has been changed.', 'updraftplus');
|
||||
} else {
|
||||
$msg .= __('Settings saved.');
|
||||
}
|
||||
$msg .= '</strong>';
|
||||
global $updraftplus_admin;
|
||||
$updraftplus_admin->show_admin_warning($msg);
|
||||
}
|
||||
|
||||
public function settings_page_render($go) {
|
||||
if (!$go) return $go;
|
||||
if ($this->correct_password_supplied) return true;
|
||||
$this->get_opts();
|
||||
$password = $this->opts['password'];
|
||||
if ($this->check_user_cookie($password)) return $go;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WP action updraftplus_debugtools_dashboard
|
||||
*/
|
||||
public function debugtools_dashboard() {
|
||||
global $updraftplus_admin;
|
||||
$this->get_opts();
|
||||
?>
|
||||
<div class="advanced_tools lock_admin">
|
||||
<h3>
|
||||
<?php esc_html_e('Lock access to the UpdraftPlus settings page', 'updraftplus'); ?>
|
||||
</h3>
|
||||
<p>
|
||||
<a href="https://teamupdraft.com/documentation/updraftplus/premium-features/how-to-lock-updraftplus-settings/?utm_source=udp-plugin&utm_medium=referral&utm_campaign=paac&utm_content=read-about-lock-settings&utm_creative_format=tex" target="_blank">
|
||||
<em><?php esc_html_e('Read more about how this works...', 'updraftplus');?></em>
|
||||
</a>
|
||||
</p>
|
||||
<form id="lock_form" method="post" onsubmit="if (jQuery('#updraft_unlockadmin_password').val() != '') { return(confirm('<?php echo esc_js(__('Please make sure that you have made a note of the password!', 'updraftplus'));?>')); } else { return true; }">
|
||||
<input type="hidden" name="nonce" value="<?php echo esc_attr(wp_create_nonce('updraftplus-unlockadmin-nonce'));?>">
|
||||
<input type="hidden" name="page" value="updraftplus">
|
||||
<input type="hidden" name="tab" value="expert">
|
||||
<input id="updraft_unlockadmin_oldpassword" type="hidden" name="updraft_unlockadmin_oldpassword" value="<?php echo esc_attr($this->opts['password']);?>">
|
||||
<table>
|
||||
<?php
|
||||
|
||||
$updraftplus_admin->settings_debugrow('<label for="updraft_unlockadmin_password">'.esc_html__('Password', 'updraftplus').'</label>:', '<input type="text" id="updraft_unlockadmin_password" name="updraft_unlockadmin_password" value="'.esc_attr($this->opts['password']).'" style="width:230px;">');
|
||||
|
||||
$session_lengths = $this->get_session_length_options();
|
||||
|
||||
$session_options = '';
|
||||
foreach ($session_lengths as $length => $text) {
|
||||
$session_options .= "<option value=\"$length\"".(($this->opts['session_length'] == $length) ? ' selected="selected"' : '').">".htmlspecialchars($text)."</option>\n";
|
||||
}
|
||||
|
||||
$updraftplus_admin->settings_debugrow('<label for="updraft_unlockadmin_session_length">'.esc_html__('Require password again after', 'updraftplus').'</label>:', '<select id="updraft_unlockadmin_session_length" name="updraft_unlockadmin_session_length" style="width:230px;">'.$session_options.'</select>');
|
||||
|
||||
$updraftplus_admin->settings_debugrow('<label for="updraft_unlockadmin_support_url">'.esc_html__('Support URL', 'updraftplus').'</label>:', '<input id="updraft_unlockadmin_support_url" name="updraft_unlockadmin_support_url" type="'.apply_filters('updraftplus_admin_secret_field_type', 'text').'" value="'.esc_attr($this->opts['support_url']).'" style="width:230px;"><br><em>'.esc_html__('Anyone seeing the lock screen will be shown this URL for support - enter a website address or an email address.', 'updraftplus').' <a target="_blank" href="'.$this->default_support_url.'">'.esc_html__('Otherwise, the default link will be shown.', 'updraftplus').'</a></em>');
|
||||
|
||||
$updraftplus_admin->settings_debugrow('', '<input class="button-primary change_lock_settings" type="submit" value="'.esc_attr(__('Change Lock Settings', 'updraftplus')).'">');
|
||||
?>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function settings_page_render_abort() {
|
||||
global $updraftplus_admin;
|
||||
$updraftplus_admin->settings_header();
|
||||
|
||||
?>
|
||||
<style type="text/css">
|
||||
#updraft-lock-area {
|
||||
border: 4px dashed #ddd;
|
||||
height: 320px;
|
||||
margin: 36px 0 0 20px;
|
||||
width: 650px;
|
||||
}
|
||||
#updraft-lock-area p {
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="updraft-lock-area">
|
||||
<p>
|
||||
<img width="150" height="150" src="<?php echo esc_url(UPDRAFTPLUS_URL);?>/images/padlock-150.png" alt="<?php echo esc_attr(__('Unlock', 'updraftplus'));?>">
|
||||
</p>
|
||||
<form method="post">
|
||||
<input type="hidden" name="nonce" value="<?php echo esc_attr(wp_create_nonce('updraftplus-unlockadmin-nonce'));?>">
|
||||
<p>
|
||||
<input type="password" size="16" name="updraft_unlockadmin_password" value="">
|
||||
<input type="submit" value="<?php echo esc_attr(__('Unlock', 'updraftplus'));?>">
|
||||
</p>
|
||||
</form>
|
||||
<p>
|
||||
<?php
|
||||
if (false === $this->correct_password_supplied) {
|
||||
echo '<span style="color:red;">'.esc_html__('Password incorrect', 'updraftplus').'</span><br>';
|
||||
}
|
||||
?>
|
||||
<?php esc_attr_e('To access the UpdraftPlus settings, please enter your unlock password', 'updraftplus'); ?><br>
|
||||
<span style="font-size:85%;"><em>
|
||||
<?php
|
||||
$this->get_opts();
|
||||
$url = empty($this->opts['support_url']) ? $this->default_support_url : $this->opts['support_url'];
|
||||
if (preg_match('/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $url)) $url = 'mailto:'.$url;
|
||||
if (!empty($url)) {
|
||||
echo '<a href="'.esc_attr($url).'">';
|
||||
}
|
||||
esc_attr_e('For unlocking support, please contact whoever manages UpdraftPlus for you.', 'updraftplus');
|
||||
if (!empty($url)) {
|
||||
echo '</a>';
|
||||
}
|
||||
?>
|
||||
</em></span>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<?php
|
||||
// settings_header opens a div
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
947
wp-content/plugins/updraftplus/addons/migrator.php
Normal file
947
wp-content/plugins/updraftplus/addons/migrator.php
Normal file
@@ -0,0 +1,947 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: migrator:One-click migrate a WordPress site to a different location with multisite to selective site restore.
|
||||
Description: One click migration and selective site migration from multisite backups
|
||||
Version: 4.0
|
||||
Shop: /shop/migrator/
|
||||
Latest Change: 1.23.5
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
updraft_try_include_file('includes/migrator-lite.php', 'require_once');
|
||||
class UpdraftPlus_Addons_Migrator extends UpdraftPlus_Migrator_Lite {
|
||||
|
||||
/**
|
||||
* Constructor, called during UD initialisation
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action('updraftplus_restore_db_pre', array($this, 'updraftplus_restore_db_pre'));
|
||||
|
||||
add_action('updraftplus_restore_db_record_old_content', array($this, 'updraftplus_restore_db_record_old_content'));
|
||||
add_action('updraftplus_restore_db_record_old_uploads', array($this, 'updraftplus_restore_db_record_old_uploads'));
|
||||
|
||||
// MU
|
||||
add_action('updraftplus_restored_themes_one', array($this, 'restored_themes_one'));
|
||||
// Migrate tab output
|
||||
add_action('updraftplus_migrate_tab_output', array($this, 'updraftplus_migrate_tab_output'));
|
||||
|
||||
// MU
|
||||
add_filter('updraftplus_restore_set_table_prefix', array($this, 'restore_set_table_prefix'), 10, 2);
|
||||
|
||||
add_filter('updraftplus_get_history_status_result', array($this, 'get_history_status_result'));
|
||||
// Both MU and normal site
|
||||
// Actions/filters that need UD to be fully loaded before we can consider adding them
|
||||
add_action('plugins_loaded', array($this, 'plugins_loaded'));
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function plugins_loaded() {
|
||||
global $updraftplus;
|
||||
// We don't support restoring single sites into multisite until WP 3.5
|
||||
// Some (significantly out-dated) information on what import-into-multisite involves: http://iandunn.name/comprehensive-wordpress-multisite-migrations/
|
||||
if (is_a($updraftplus, 'UpdraftPlus') && method_exists($updraftplus, 'get_wordpress_version') && version_compare($updraftplus->get_wordpress_version(), '3.5', '>=')) {
|
||||
// Both MU and normal site
|
||||
add_filter('updraftplus_restore_all_downloaded_postscan', array($this, 'restore_all_downloaded_postscan'), 10, 7);
|
||||
// MU
|
||||
add_filter('updraftplus_restore_this_table', array($this, 'restore_this_table'), 10, 3);
|
||||
// MU
|
||||
add_filter('updraftplus_pre_restore_move_in', array($this, 'pre_restore_move_in'), 10, 7);
|
||||
add_action('updraftplus_restorer_restore_options', array($this, 'restorer_restore_options'));
|
||||
// MU
|
||||
add_filter('updraftplus_restore_delete_recursive', array($this, 'restore_delete_recursive'), 10, 4);
|
||||
add_action('updraftplus_admin_enqueue_scripts', array($this, 'updraftplus_admin_enqueue_scripts'));
|
||||
}
|
||||
}
|
||||
|
||||
public function updraftplus_admin_enqueue_scripts() {
|
||||
global $updraftplus;
|
||||
$updraftplus->enqueue_select2();
|
||||
}
|
||||
|
||||
public function restore_delete_recursive($recurse, $ud_foreign, $restore_options, $type) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
if ($recurse) return $recurse;
|
||||
// If doing a single-site-to-multisite import on the uploads, then we expect subdirectories to be around - they need deleting without raising any user-visible errors
|
||||
return ('uploads' == $type && !empty($this->new_blogid)) ? true : $recurse;
|
||||
}
|
||||
|
||||
public function pre_restore_move_in($now_done, $type, $working_dir, $info, $backup_info, $restorer, $wp_filesystem_dir) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
if ($now_done) return $now_done;
|
||||
|
||||
if (is_multisite() && 0 == $restorer->ud_backup_is_multisite && (('plugins' == $type || 'themes' == $type) || ('uploads' == $type && !empty($this->new_blogid) && !get_site_option('ms_files_rewriting')))) {
|
||||
|
||||
global $wp_filesystem, $updraftplus;
|
||||
|
||||
$skin = $restorer->ud_get_skin();
|
||||
|
||||
// Migrating a single site into a multisite
|
||||
if ('plugins' == $type || 'themes' == $type) {
|
||||
|
||||
$move_from = $restorer->get_first_directory($working_dir, array(basename($info['path']), $type));
|
||||
// Only move in entities that are not already there
|
||||
$move_mode = Updraft_Restorer::MOVEIN_DO_NOTHING_IF_EXISTING;
|
||||
|
||||
$skin->feedback('moving_backup');
|
||||
|
||||
$new_move_failed = (false === $move_from) ? true : false;
|
||||
if (false === $new_move_failed) {
|
||||
$move_in = $restorer->move_backup_in($move_from, trailingslashit($wp_filesystem_dir), $move_mode, array(), $type, true);
|
||||
if (is_wp_error($move_in)) return $move_in;
|
||||
if (!$move_in) $new_move_failed = true;
|
||||
}
|
||||
if ($new_move_failed) return new WP_Error('new_move_failed', $restorer->strings['new_move_failed']);
|
||||
@$wp_filesystem->delete($move_from);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
|
||||
// Nothing more needs doing
|
||||
$now_done = true;
|
||||
|
||||
} elseif ('uploads' == $type) {
|
||||
|
||||
$skin->feedback('moving_old');
|
||||
|
||||
switch_to_blog($this->new_blogid);
|
||||
|
||||
$ud = wp_upload_dir();
|
||||
$wpud = $ud['basedir'];
|
||||
$fsud = trailingslashit($wp_filesystem->find_folder($wpud));
|
||||
|
||||
restore_current_blog();
|
||||
|
||||
if (!is_string($fsud)) {
|
||||
$updraftplus->log("Could not find basedir folder for site ($wpud)");
|
||||
return new WP_Error('new_move_failed', $restorer->strings['new_move_failed']);
|
||||
}
|
||||
|
||||
$updraftplus->log("Will move into: ".$fsud);
|
||||
|
||||
return $fsud;
|
||||
|
||||
// This now drops through to the uploads section
|
||||
}
|
||||
}
|
||||
return $now_done;
|
||||
}
|
||||
|
||||
public function restorer_restore_options($restore_options) {
|
||||
$this->restore_options = $restore_options;
|
||||
}
|
||||
|
||||
public function restore_this_table($restore_or_not, $unprefixed_table_name, $restore_options) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Unused parameter is present because the method is used as a WP filter.
|
||||
|
||||
// We're only interested in filtering out the user/usermeta table when importing single site into multisite
|
||||
if (!$restore_or_not || empty($this->new_blogid)) return $restore_or_not;
|
||||
|
||||
// Don't restore these - they're not used
|
||||
if ('users' == $unprefixed_table_name || 'usermeta' == $unprefixed_table_name) return false;
|
||||
|
||||
return $restore_or_not;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WP filter updraftplus_get_history_status_result
|
||||
*
|
||||
* @param Array $result - the pre-filtered information
|
||||
*
|
||||
* @return Array - after our filtering
|
||||
*/
|
||||
public function get_history_status_result($result) {
|
||||
if (!is_array($result)) return $result;
|
||||
ob_start();
|
||||
$this->updraftplus_migrate_tab_output();
|
||||
$tab_output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
$result['migrate_tab'] = $tab_output;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display HTML for Migrate tab in 'Migrate/Clone' settings tab.
|
||||
*
|
||||
* @return void Display HTML.
|
||||
*/
|
||||
public function updraftplus_migrate_tab_output() {
|
||||
global $updraftplus, $updraftplus_admin;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Its used on line 187 but for some reason its flagged assuming becuase of the closed and open php tags ?>
|
||||
<div id="updraft_migrate_tab_main">
|
||||
|
||||
<?php $updraftplus_admin->include_template('wp-admin/settings/temporary-clone.php'); ?>
|
||||
|
||||
<h2><?php esc_html_e('Migrate (create a copy of a site on hosting you control)', 'updraftplus');?></h2>
|
||||
|
||||
<div id="updraft_migrate" class="postbox">
|
||||
<div class="updraft_migrate_intro">
|
||||
<p>
|
||||
<?php echo esc_html(__('A "migration" is ultimately the same as a restoration - but using backup archives that you import from another site.', 'updraftplus').' '.__('The UpdraftPlus Migrator modifies the restoration operation appropriately, to fit the backup data to the new site.', 'updraftplus')); ?>
|
||||
<a href="https://teamupdraft.com/documentation/updraftplus/topics/migration/faqs/how-to-migrate-a-wordpress-site-with-updraftplus?utm_source=udp-plugin&utm_medium=referral&utm_campaign=paac&utm_content=migrate-step-by-step&utm_creative_format=text" target="_blank"><?php esc_html_e('Read this article to see step-by-step how it\'s done.', 'updraftplus'); ?></a>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
$this->migrate_widget();
|
||||
do_action('updraft_migrate_after_widget');
|
||||
|
||||
?>
|
||||
<div id="updraft_migrate_tab_alt" style="display:none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Display HTML for `Restoring an existing backup set onto this site` in migrate tab.
|
||||
*
|
||||
* @param array|boolean $backup_history - the backup history list to use, or false to get the current list.
|
||||
*
|
||||
* @return void Display HTML.
|
||||
*/
|
||||
private function migrate_widget($backup_history = false) {
|
||||
|
||||
global $updraftplus, $updraftplus_admin;
|
||||
|
||||
if (false === $backup_history) $backup_history = UpdraftPlus_Backup_History::get_history();
|
||||
|
||||
// Save on SQL queries by using the method that batch-fetches
|
||||
$backup_history = UpdraftPlus_Backup_History::add_jobdata($backup_history);
|
||||
|
||||
echo '<div class="updraft_migrate_widget_module_content">';
|
||||
echo '<header>';
|
||||
echo '<button class="button button-link close"><span class="dashicons dashicons-arrow-left-alt2"></span>'.esc_html__('back', 'updraftplus').'</button>';
|
||||
echo '<h3><span class="dashicons dashicons-migrate"></span>'.esc_html__('Restore an existing backup set onto this site', 'updraftplus').'</h3>';
|
||||
echo '</header>';
|
||||
|
||||
echo '<a href="'.esc_url(UpdraftPlus::get_current_clean_url()).'" onclick="jQuery(\'#updraft-navtab-backups\').trigger(\'click\'); return false;">'.esc_html__('To import a backup set, go to the "Existing backups" section in the "Backup/Restore" tab', 'updraftplus')."</a>";
|
||||
|
||||
if (empty($backup_history)) {
|
||||
echo '<p><em>'.esc_html__('This site has no backups to restore from yet.', 'updraftplus').'</em></p>';
|
||||
echo '</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$incremental_set_found = false;
|
||||
|
||||
echo '<p class="updraft_migrate_select_backup">
|
||||
<select id="updraft_migrate_select_backup">';
|
||||
|
||||
krsort($backup_history);
|
||||
foreach ($backup_history as $key => $backup) {
|
||||
if (count($backup['incremental_sets']) > 1) $incremental_set_found = true;
|
||||
|
||||
// https://core.trac.wordpress.org/ticket/25331 explains why the following line is wrong
|
||||
// $pretty_date = date_i18n('Y-m-d G:i',$key);
|
||||
// Convert to blog time zone
|
||||
// $pretty_date = get_date_from_gmt(gmdate('Y-m-d H:i:s', (int)$key), 'Y-m-d G:i');
|
||||
$pretty_date = get_date_from_gmt(gmdate('Y-m-d H:i:s', (int) $key), 'M d, Y G:i');
|
||||
|
||||
$non = $backup['nonce'];
|
||||
|
||||
$jobdata = isset($backup['jobdata']) ? $backup['jobdata'] : $updraftplus->jobdata_getarray($non);
|
||||
|
||||
// $delete_button = $this->delete_button($key, $non, $backup);
|
||||
|
||||
$date_label = $updraftplus_admin->date_label($pretty_date, $key, $backup, $jobdata, $non, true);
|
||||
|
||||
echo '<option value="'.esc_attr($key).'">'.esc_html($date_label).'</option>';
|
||||
|
||||
}
|
||||
|
||||
|
||||
echo '</select>';
|
||||
|
||||
echo '<button id="updraft_migrate_select_backup_go" title="'.esc_attr__('After pressing this button, you will be given the option to choose which components you wish to migrate', 'updraftplus').'" type="button" class="button button-primary" onclick="var whichset=jQuery(\'#updraft_migrate_select_backup\').val(); updraft_initiate_restore(whichset);">'.esc_html__('Restore', 'updraftplus').'</button>';
|
||||
|
||||
echo '</p>';
|
||||
|
||||
if ($incremental_set_found) echo '<p>'.esc_html__('For incremental backups, you will be able to choose which increments to restore at a later stage.', 'updraftplus').'</p>';
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
public function restored_themes_one($theme) {
|
||||
// Network-activate
|
||||
$allowed_themes = get_site_option('allowedthemes');
|
||||
$allowed_themes[$theme] = true;
|
||||
update_site_option('allowedthemes', $allowed_themes);
|
||||
global $updraftplus;
|
||||
$updraftplus->log(__('Network activating theme:', 'updraftplus').' '.$theme, 'notice-restore');
|
||||
$updraftplus->log('Network activating theme: '.$theme);
|
||||
}
|
||||
|
||||
public function restore_set_table_prefix($import_table_prefix, $backup_is_multisite) {
|
||||
if (!is_multisite() || 0 !== $backup_is_multisite) return $import_table_prefix;
|
||||
|
||||
$new_blogid = $this->generate_new_blogid();
|
||||
|
||||
if (!is_integer($new_blogid)) return $new_blogid;
|
||||
|
||||
do_action('updraftplus_restore_set_table_prefix_multisite_got_new_blog_id', $new_blogid, $import_table_prefix);
|
||||
|
||||
$this->new_blogid = $new_blogid;
|
||||
|
||||
return (string) $import_table_prefix.$new_blogid.'_';
|
||||
}
|
||||
|
||||
/**
|
||||
* WordPress action updraftplus_restore_all_downloaded_postscan called during the restore process.
|
||||
*
|
||||
* The last four parameters can be edited in-place.
|
||||
*
|
||||
* @param Array $backups - list of backups
|
||||
* @param Integer $timestamp - the timestamp (epoch time) of the backup being restored
|
||||
* @param Array $elements - elements being restored (as the keys of the array)
|
||||
* @param Array $info - information about the backup being restored
|
||||
* @param Array $mess - array of informational-level messages
|
||||
* @param Array $warn - array of warning-level messages
|
||||
* @param Array $err - array of error-level messages
|
||||
*/
|
||||
public function restore_all_downloaded_postscan($backups, $timestamp, $elements, &$info, &$mess, &$warn, &$err) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
|
||||
if (is_array($info) && is_multisite() && isset($info['multisite']) && !$info['multisite']) {
|
||||
|
||||
$original_error_count = count($err);
|
||||
|
||||
if (!empty($elements['wpcore'])) {
|
||||
$err[] = sprintf(__('You selected %s to be included in the restoration - this cannot / should not be done when importing a single site into a network.', 'updraftplus'), __('WordPress core', 'updraftplus')).' <a href="https://updraftplus.com/information-on-importing-a-single-site-wordpress-backup-into-a-wordpress-network-i-e-multisite/" target="_blank">'.__('Go here for more information.', 'updraftplus').'</a>';
|
||||
}
|
||||
if (!empty($elements['others'])) {
|
||||
$err[] = sprintf(__('You selected %s to be included in the restoration - this cannot / should not be done when importing a single site into a network.', 'updraftplus'), __('other content from wp-content', 'updraftplus')).' <a href="https://updraftplus.com/information-on-importing-a-single-site-wordpress-backup-into-a-wordpress-network-i-e-multisite/" target="_blank">'.__('Go here for more information.', 'updraftplus').'</a>';
|
||||
}
|
||||
if (!empty($elements['mu-plugins'])) {
|
||||
$err[] = sprintf(__('You selected %s to be included in the restoration - this cannot / should not be done when importing a single site into a network.', 'updraftplus'), __('Must-use plugins', 'updraftplus')).' <a href="https://updraftplus.com/information-on-importing-a-single-site-wordpress-backup-into-a-wordpress-network-i-e-multisite/" target="_blank">'.__('Go here for more information.', 'updraftplus').'</a>';
|
||||
}
|
||||
|
||||
global $updraftplus;
|
||||
if (version_compare($updraftplus->get_wordpress_version(), '3.5', '<')) {
|
||||
$err[] = __('Importing a single site into a multisite install', 'updraftplus').': '.sprintf(__('This feature requires %s version %s or later', 'updraftplus'), 'WordPress', '3.5');
|
||||
} elseif (get_site_option('ms_files_rewriting')) {
|
||||
$err[] = __('Importing a single site into a multisite install', 'updraftplus').': '.sprintf(__('This feature is not compatible with %s', 'updraftplus'), 'pre-WordPress-3.5-style multisite uploads rewriting', 'updraftplus');
|
||||
}
|
||||
|
||||
if (count($err) > $original_error_count) return;
|
||||
|
||||
if (empty($info['addui'])) $info['addui'] = '';
|
||||
$info['addui'] .= '<p><strong>'.__('Information needed to continue:', 'updraftplus').'</strong><br>';
|
||||
$info['addui'] .= __('Enter details for where this new site is to live within your multisite install:', 'updraftplus').'<br>';
|
||||
|
||||
global $current_site;
|
||||
|
||||
if (!is_subdomain_install()) {
|
||||
$info['addui'] .= ' <label for="blogname">'.$current_site->domain.$current_site->path.'</label><input name="updraftplus_migrate_blogname" data-invalidpattern="'.esc_attr(__('You must use lower-case letters or numbers for the site path, only.', 'updraftplus')).'" pattern="^[a-z0-9]+$" type="text" id="blogname" class="required" value="" maxlength="60" /><br>';
|
||||
} else {
|
||||
$info['addui'] .= ' <input class="required" name="updraftplus_migrate_blogname" data-invalidpattern="'.esc_attr(__('You must use lower-case letters or numbers for the site path, only.', 'updraftplus')).'" pattern="^[a-z0-9]+$" type="text" id="blogname" value="" maxlength="60" />.<label for="blogname">' . (preg_replace('|^www\.|', '', $current_site->domain)) . '</label><br>';
|
||||
}
|
||||
|
||||
$info['addui'] .= '</p>';
|
||||
|
||||
if (!empty($elements['db'])) {
|
||||
|
||||
if (empty($info['addui'])) $info['addui'] = '';
|
||||
$info['addui'] .= '<p><label for="updraft_restore_content_to_user"><strong>'.__('Attribute imported content to user', 'updraftplus').':</strong></label><br>';
|
||||
|
||||
$class = (!defined('UPDRAFTPLUS_SELECT2_ENABLE') || UPDRAFTPLUS_SELECT2_ENABLE) ? 'updraft_select2' : '';
|
||||
|
||||
$info['addui'] .= '<selectx style="width:100%;" id="updraft_restore_content_to_user" name="updraft_restore_content_to_user" class="'.$class.'">';
|
||||
|
||||
// $main_site_id = $current_site->blog_id;
|
||||
$page = 0;
|
||||
|
||||
while (!isset($users) || count($users) > 0) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- The variable is defined inside the loop.
|
||||
|
||||
$users = get_users(array(
|
||||
// Not documented in codex, but the source reveals that to get "all sites", you use an ID of 0
|
||||
'blog_id' => 0,
|
||||
'offset' => $page * 500,
|
||||
'number' => 500,
|
||||
'fields' => array('ID', 'user_login', 'user_nicename'),
|
||||
));
|
||||
|
||||
if (!is_array($users)) $users = array();
|
||||
|
||||
foreach ($users as $user) {
|
||||
$info['addui'] .= '<option value="'.$user->ID.'">'.htmlspecialchars($user->user_login.' ('.$user->user_nicename.')');
|
||||
$info['addui'] .= '</option>';
|
||||
}
|
||||
$page++;
|
||||
}
|
||||
|
||||
$info['addui'] .= '</p>';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function generate_new_blogid() {
|
||||
|
||||
$blog_title = __('Migrated site (from UpdraftPlus)', 'updraftplus');
|
||||
|
||||
if (!isset($this->restore_options['updraftplus_migrate_blogname'])) {
|
||||
return new WP_Error('multisite_info_missing', sprintf(__('Required information for restoring this backup was not given (%s)', 'updraftplus'), 'new multisite import location'));
|
||||
}
|
||||
|
||||
// Verify value given
|
||||
$result = wpmu_validate_blog_signup($this->restore_options['updraftplus_migrate_blogname'], $blog_title);
|
||||
|
||||
if (!empty($result['errors']) && is_wp_error($result['errors']) && $result['errors']->get_error_code()) {
|
||||
return $result['errors'];
|
||||
}
|
||||
|
||||
global $wpdb, $updraftplus;
|
||||
if (domain_exists($result['domain'], $result['path'], $wpdb->siteid)) {
|
||||
return new WP_Error('already_taken', sprintf(__('Error: %s', 'updraftplus'), 'Site URL already taken'));
|
||||
}
|
||||
|
||||
$create = $this->create_empty_blog($result['domain'], $result['path'], $blog_title, $wpdb->siteid);
|
||||
|
||||
if (is_integer($create)) {
|
||||
$url = untrailingslashit($result['domain'].$result['path']);
|
||||
|
||||
$updraftplus->log(__('New site:', 'updraftplus').' '.$url, 'notice-restore');
|
||||
|
||||
switch_to_blog($create);
|
||||
// Update record of what we want to rewrite the URLs to in the search/replace operation
|
||||
$this->siteurl = untrailingslashit(site_url());
|
||||
$this->home = untrailingslashit(home_url());
|
||||
|
||||
// The next line can't work, because content_url() fetches from the constant WP_CONTENT_URL
|
||||
// $this->content = untrailingslashit(content_url());
|
||||
$wp_upload_dir = wp_upload_dir();
|
||||
$this->uploads = $wp_upload_dir['baseurl'];
|
||||
if (is_subdomain_install()) {
|
||||
// For some reason, wp_upload_dir() on a subdomain install tends to return a URL with the host set to a different site's domain, despite switch_to_blog() having been called. Try to detect + fix this (though, it also usually won't matter anyway).
|
||||
$uploads_host = parse_url($this->uploads, PHP_URL_HOST);
|
||||
$expected_uploads_host = parse_url($this->home, PHP_URL_HOST);
|
||||
if ($uploads_host && $expected_uploads_host && $uploads_host != $expected_uploads_host) {
|
||||
$this->uploads = UpdraftPlus_Manipulation_Functions::str_replace_once($uploads_host, $expected_uploads_host, $this->uploads);
|
||||
$updraftplus->log("wp_upload_dir() returned an unexpected uploads hosts on a subdomain multisite ($uploads_host, rather than $expected_uploads_host) - correcting; destination uploads URL is now: ".$this->uploads);
|
||||
}
|
||||
}
|
||||
// We have to assume that on the imported site, uploads is at /uploads relative to the content directory
|
||||
if (empty($this->old_uploads)) $this->old_uploads = $this->old_content.'/uploads';
|
||||
$this->content = false;
|
||||
$this->old_content = false;
|
||||
// $this->siteurl = 'http://'.$url;
|
||||
// $this->home = 'http://'.$url;
|
||||
// $this->content = // ??
|
||||
restore_current_blog();
|
||||
|
||||
return $create;
|
||||
|
||||
} elseif (is_wp_error($create)) {
|
||||
// Currently returns strings for errors, but being ready in case it improves doesn't hurt.
|
||||
return $create;
|
||||
} else {
|
||||
// Things like __('<strong>ERROR</strong>: problem creating site entry.' )
|
||||
$updraftplus->log(__('Error when creating new site at your chosen address:', 'updraftplus'), 'warning-restore');
|
||||
return new WP_Error('create_empty_blog_failed', __('Error when creating new site at your chosen address:', 'updraftplus').' '.(is_string($create) ? $create : print_r($create, true)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated in WP 4.4 - https://core.trac.wordpress.org/changeset/34753 - hence, folded into the plugin instead
|
||||
*
|
||||
* @param string $domain
|
||||
* @param string $path
|
||||
* @param string $weblog_title
|
||||
* @param integer $site_id
|
||||
*/
|
||||
public function create_empty_blog($domain, $path, $weblog_title, $site_id = 1) {
|
||||
|
||||
// Out of an abundance of caution, call the native, un-deprecated version if there is one
|
||||
global $updraftplus;
|
||||
$wp_version = $updraftplus->get_wordpress_version();
|
||||
if (version_compare($wp_version, '4.4', '<') && function_exists('create_empty_blog')) return create_empty_blog($domain, $path, $weblog_title, $site_id);
|
||||
|
||||
if (empty($path)) $path = '/';
|
||||
|
||||
// Check if the domain has been used already. We should return an error message.
|
||||
if (domain_exists($domain, $path, $site_id)) return __('<strong>ERROR</strong>: Site URL already taken.');
|
||||
|
||||
// Need to backup wpdb table names, and create a new wp_blogs entry for new blog.
|
||||
// Need to get blog_id from wp_blogs, and create new table names.
|
||||
// Must restore table names at the end of function.
|
||||
|
||||
// insert_blog() and install_blog() are deprecated as of WP 5.1.0.
|
||||
// This has also caused an error when using install_blog on 5.1+, so we have switched to the new 'wp_insert_site'
|
||||
if (version_compare($wp_version, '5.1', '<')) {
|
||||
if (!$blog_id = insert_blog($domain, $path, $site_id)) return __('<strong>ERROR</strong>: problem creating site entry.');
|
||||
|
||||
switch_to_blog($blog_id);
|
||||
install_blog($blog_id);
|
||||
restore_current_blog();
|
||||
} else {
|
||||
$admins = get_users(array(
|
||||
'login__in' => get_super_admins(),
|
||||
'fields' => array( 'user_ID')
|
||||
));
|
||||
|
||||
$user_id = 0;
|
||||
|
||||
if (is_array($admins) && !empty($admins)) {
|
||||
$user_id = $admins[0]->ID;
|
||||
}
|
||||
|
||||
$blog_data = array(
|
||||
'domain' => $domain,
|
||||
'path' => $path,
|
||||
'network_id' => $site_id,
|
||||
'title' => $weblog_title,
|
||||
'user_id' => $user_id,
|
||||
);
|
||||
$blog_id = wp_insert_site($blog_data);
|
||||
}
|
||||
|
||||
return $blog_id;
|
||||
}
|
||||
|
||||
public function updraftplus_restore_db_record_old_content($old_content) {
|
||||
// Only record once
|
||||
if (!empty($this->old_content)) return;
|
||||
$this->old_content = $old_content;
|
||||
}
|
||||
|
||||
public function updraftplus_restore_db_record_old_uploads($old_uploads) {
|
||||
// Only record once
|
||||
if (!empty($this->old_uploads)) return;
|
||||
$this->old_uploads = $old_uploads;
|
||||
}
|
||||
}
|
||||
|
||||
global $updraftplus_addons_migrator;
|
||||
if (!is_a($updraftplus_addons_migrator, 'UpdraftPlus_Addons_Migrator')) $updraftplus_addons_migrator = new UpdraftPlus_Addons_Migrator;
|
||||
|
||||
|
||||
if (!class_exists('UpdraftPlus_Addons_Migrator_RemoteSend_UI')) updraft_try_include_file('includes/class-remote-send.php', 'require_once');
|
||||
if (!class_exists('UpdraftPlus_Addons_Migrator_RemoteSend')) {
|
||||
class UpdraftPlus_Addons_Migrator_RemoteSend extends UpdraftPlus_RemoteSend {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
add_action('updraft_migrate_after_widget', array($this, 'updraft_migrate_after_widget'));
|
||||
add_action('admin_footer', array($this, 'admin_footer'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display HTML for sending/receiving backups from/to remote sites in Migrate Tab.
|
||||
* Callback registered for `updraft_migrate_after_widget` action.
|
||||
*
|
||||
* @return void Display HTML.
|
||||
*/
|
||||
public function updraft_migrate_after_widget() {
|
||||
?>
|
||||
<div class="updraft_migrate_widget_module_content">
|
||||
<header>
|
||||
<button class="button button-link close"><span class="dashicons dashicons-arrow-left-alt2"></span><?php esc_html_e('back', 'updraftplus');?></button>
|
||||
<h3><span class="dashicons dashicons-upload"></span><?php esc_html_e('Send a backup to another site', 'updraftplus');?></h3>
|
||||
</header>
|
||||
<div id="updraft_migrate_receivingsites" style="clear:both; margin-top:10px;">
|
||||
<?php $this->get_remotesites_selector(false, true);?>
|
||||
</div>
|
||||
<div class="updraft_migrate_add_site" style="display: none;">
|
||||
<p>
|
||||
<?php
|
||||
echo esc_html__("To add a site as a destination for sending to, enter that site's key below.", 'updraftplus').' <a href="'.esc_url(UpdraftPlus::get_current_clean_url()).'" onclick="alert(\''.esc_js(__('Keys for a site are created in the section "receive a backup from a remote site".', 'updraftplus').' '.__("So, to get the key for the remote site, open the 'Migrate Site' window on that site, and go to that section.", 'updraftplus')).'\'); return false;">'.esc_html__("How do I get a site's key?", 'updraftplus').'</a>';
|
||||
?>
|
||||
</p>
|
||||
<div class="input-field">
|
||||
<label><?php esc_html_e('Site key', 'updraftplus');?></label> <input type="text" id="updraft_migrate_receiving_new" placeholder="<?php esc_attr_e('Paste key here', 'updraftplus');?>"> <button class="button-primary" id="updraft_migrate_receiving_makenew" onclick="updraft_migrate_receiving_makenew();"><?php esc_html_e('Add site', 'updraftplus');?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="updraft_migrate_widget_module_content">
|
||||
<header>
|
||||
<button class="button button-link close"><span class="dashicons dashicons-arrow-left-alt2"></span><?php esc_html_e('back', 'updraftplus');?></button>
|
||||
<h3><span class="dashicons dashicons-download"></span><?php esc_html_e('Receive a backup from a remote site', 'updraftplus');?></h3>
|
||||
</header>
|
||||
<p><?php echo esc_html(__('To allow another site to send a backup to this site, create a key below.', 'updraftplus').' '.__("When you are shown the key, then press the 'Migrate' button on the other (sending) site, and copy-and-paste the key over there (in the 'Send a backup to another site' section).", 'updraftplus'));?></p>
|
||||
<p>
|
||||
<?php esc_html_e('Create a key: give this key a unique name (e.g. indicate the site it is for), then press "Create key":', 'updraftplus');?><br>
|
||||
<input id="updraft_migrate_receivingsites_keyname" type="text" placeholder="<?php esc_attr_e('Enter your chosen name', 'updraftplus');?>" value="<?php echo esc_attr(__('Key', 'updraftplus').' - '.date('Y-m-d'));?>">
|
||||
|
||||
<?php esc_html_e('Encryption key size:', 'updraftplus');?>
|
||||
<select id="updraft_migrate_receivingsites_keysize">
|
||||
<option value="1024"><?php echo esc_html(sprintf(__('%s bits', 'updraftplus').' - '.__('faster (possibility for slow PHP installs)', 'updraftplus'), '1024'));?></option>
|
||||
<option value="2048" selected="selected"><?php echo esc_html(sprintf(__('%s bytes', 'updraftplus').' - '.__('recommended', 'updraftplus'), '2048'));?></option>
|
||||
<option value="4096"><?php echo esc_html(sprintf(__('%s bits', 'updraftplus').' - '.__('slower, strongest', 'updraftplus'), '4096'));?></option>
|
||||
</select>
|
||||
|
||||
<button id="updraft_migrate_receivingsites_createkey" class="button button-primary" onclick="updraft_migrate_receivingsites_createkey();"><?php esc_html_e('Create key', 'updraftplus');?></button>
|
||||
|
||||
</p>
|
||||
|
||||
<div id="updraft_migrate_new_key_container" style="display:none;">
|
||||
<?php esc_html_e('Your new key:', 'updraftplus');?><br>
|
||||
<textarea id="updraft_migrate_new_key" onclick="this.select();" style="width:625px; height:235px; word-wrap:break-word; border: 1px solid #aaa; border-radius: 3px; padding:4px;"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="updraft_migrate_our_keys_container">
|
||||
<?php $this->list_our_keys(false, true);?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WP action admin_footer. If on a relevant page, we output some JavaScript.
|
||||
*
|
||||
* @return void Outputs javascript on relevant pages.
|
||||
*/
|
||||
public function admin_footer() {
|
||||
global $updraftplus, $pagenow;
|
||||
// Next, the actions that only come on the UpdraftPlus page
|
||||
if (UpdraftPlus_Options::admin_page() != $pagenow || empty($_REQUEST['page']) || 'updraftplus' != $_REQUEST['page']) return;
|
||||
|
||||
?>
|
||||
<script>
|
||||
|
||||
function updraft_migrate_receivingsites_createkey() {
|
||||
|
||||
var $ = jQuery;
|
||||
|
||||
// Remember to tell them that this key will never be shown again
|
||||
|
||||
var key_name = $('#updraft_migrate_receivingsites_keyname').val();
|
||||
var key_size = $('#updraft_migrate_receivingsites_keysize').val();
|
||||
|
||||
if ('' == key_name || false == key_name || null == key_name) {
|
||||
alert(updraftlion.nokeynamegiven);
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#updraft_migrate_new_key_container').show();
|
||||
$('#updraft_migrate_new_key').html(updraftlion.creating_please_allow);
|
||||
|
||||
var data = {
|
||||
subsubaction: 'updraft_migrate_key_create',
|
||||
name: key_name,
|
||||
size: key_size
|
||||
}
|
||||
|
||||
updraft_send_command('doaction', data, function(resp) {
|
||||
if (resp.hasOwnProperty('bundle')) {
|
||||
$('#updraft_migrate_receivingsites_keyname').val('');
|
||||
$('#updraft_migrate_new_key').html(resp.bundle);
|
||||
if (resp.hasOwnProperty('selector')) {
|
||||
$('#updraft_migrate_receivingsites').html(resp.selector);
|
||||
}
|
||||
if (resp.hasOwnProperty('r')) {
|
||||
alert(resp.r);
|
||||
}
|
||||
if (resp.hasOwnProperty('ourkeys')) {
|
||||
$('#updraft_migrate_our_keys_container').html(resp.ourkeys);
|
||||
}
|
||||
} else if (resp.hasOwnProperty('e')) {
|
||||
$('#updraft_migrate_new_key').html(resp.r);
|
||||
console.log(resp);
|
||||
} else {
|
||||
alert(updraftlion.servererrorcode);
|
||||
console.log(resp);
|
||||
console.log(response);
|
||||
$('#updraft_migrate_new_key_container').hide();
|
||||
}
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
var msg = '';
|
||||
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) {
|
||||
console.error(resp.fatal_error_message);
|
||||
msg = resp.fatal_error_message;
|
||||
}
|
||||
if (response.hasOwnProperty('responseText') && response.responseText) {
|
||||
msg = response.responseText;
|
||||
if (response.hasOwnProperty('statusText') && response.statusText) {
|
||||
msg += ' ('+response.statusText+')';
|
||||
}
|
||||
} else if (response.hasOwnProperty('statusText') && response.statusText) {
|
||||
msg = response.statusText;
|
||||
}
|
||||
$('#updraft_migrate_new_key').html(updraftlion.error+' '+msg);
|
||||
alert(updraftlion.error+' '+msg);
|
||||
console.log(response);
|
||||
console.log(status);
|
||||
}
|
||||
});
|
||||
|
||||
// Update (via AJAX) the list of existing keys
|
||||
// AJAX command to delete an existing key
|
||||
};
|
||||
|
||||
function updraft_migrate_local_key_delete(keyid) {
|
||||
|
||||
var $ = jQuery;
|
||||
|
||||
var $keylink = $('a.updraft_migrate_local_key_delete[data-keyid="'+keyid+']');
|
||||
|
||||
var data = {
|
||||
subsubaction: 'updraft_migrate_key_delete',
|
||||
keyid: keyid,
|
||||
}
|
||||
|
||||
$keylink.html(updraftlion.deleting);
|
||||
|
||||
updraft_send_command('doaction', data, function(resp) {
|
||||
if (resp.hasOwnProperty('ourkeys')) {
|
||||
$('#updraft_migrate_our_keys_container').html(resp.ourkeys);
|
||||
} else {
|
||||
alert(updraftlion.unexpectedresponse+' '+response);
|
||||
console.log(resp);
|
||||
console.log(response);
|
||||
}
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) {
|
||||
console.error(resp.fatal_error_message);
|
||||
alert(resp.fatal_error_message);
|
||||
} else {
|
||||
var error_message = "updraft_send_command: error: "+status+" ("+error_code+")";
|
||||
console.log(error_message);
|
||||
alert(error_message);
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updraft_migrate_send_backup_options() {
|
||||
|
||||
var $ = jQuery;
|
||||
|
||||
var site_url = $('#updraft_remotesites_selector option:selected').text();
|
||||
|
||||
$('#updraft_migrate .updraft_migrate_widget_module_content, .updraft_migrate_intro').hide();
|
||||
$('#updraft_migrate_tab_alt').html('<header><button class="button button-link close"><span class="dashicons dashicons-arrow-left-alt2"></span>'+updraftlion.back+'</button><h3><span class="dashicons dashicons-download"></span>'+updraftlion.send_to_another_site+'</h3></header><p><strong>'+updraftlion.sendtosite+'</strong> '+site_url+'</p><p>'+updraftlion.remote_send_backup_info+'</p><button class="button button-primary" id="updraft_migrate_send_existing_button" onclick="updraft_migrate_send_existing_backup();">'+updraftlion.send_existing_backup+'</button><button class="button button-primary" id="updraft_migrate_send_new_button" onclick="updraft_migrate_send_backup();">'+updraftlion.send_new_backup+'</button>').slideDown('fast');
|
||||
|
||||
}
|
||||
|
||||
function updraft_migrate_send_existing_backup() {
|
||||
|
||||
var $ = jQuery;
|
||||
|
||||
var site_url = $('#updraft_remotesites_selector option:selected').text();
|
||||
$('#updraft_migrate .updraft_migrate_widget_module_content, .updraft_migrate_intro').hide();
|
||||
$('#updraft_migrate_tab_alt').html('<header><button class="button button-link close"><span class="dashicons dashicons-arrow-left-alt2"></span>'+updraftlion.back+'</button><h3><span class="dashicons dashicons-download"></span>'+updraftlion.send_to_another_site+'</h3></header><p><strong>'+updraftlion.sendtosite+'</strong> '+site_url+'</p><p>'+updraftlion.remote_send_backup_info+'</p><p id="updraft_migrate_findingbackupsprogress">'+updraftlion.scanning_backups+'</p>').slideDown('fast');
|
||||
|
||||
updraft_send_command('get_backup_list', {}, function(resp, status, response) {
|
||||
$('#updraft_migrate_findingbackupsprogress').replaceWith('');
|
||||
$('#updraft_migrate_tab_alt').append(resp.data);
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) {
|
||||
$('#updraft_migrate_tab_alt').append('<p style="color:red;">'+resp.fatal_error_message+'</p>');
|
||||
console.error(resp.fatal_error_message);
|
||||
} else {
|
||||
$('#updraft_migrate_tab_alt').append('<p style="color:red;">'+updraftlion.unexpectedresponse+' '+response+'</p>');
|
||||
console.log(err);
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function updraft_migrate_send_backup() {
|
||||
|
||||
var $ = jQuery;
|
||||
|
||||
$('#updraft_migrate .updraft_migrate_widget_module_content, .updraft_migrate_intro').hide();
|
||||
var site_id = $('#updraft_remotesites_selector').val();
|
||||
var site_url = $('#updraft_remotesites_selector option:selected').text();
|
||||
$('#updraft_migrate_tab_alt').html('<header><button class="button button-link close"><span class="dashicons dashicons-arrow-left-alt2"></span>'+updraftlion.back+'</button><h3><span class="dashicons dashicons-download"></span>'+updraftlion.send_to_another_site+'</h3></header><p><strong>'+updraftlion.sendtosite+'</strong> '+site_url+'</p><p id="updraft_migrate_testinginprogress">'+updraftlion.testingconnection+'</p>').slideDown('fast');
|
||||
|
||||
var data = {
|
||||
subsubaction: 'updraft_remote_ping_test',
|
||||
id: site_id,
|
||||
url: site_url
|
||||
};
|
||||
updraft_send_command('doaction', data, function(resp, status, response) {
|
||||
try {
|
||||
if (resp.hasOwnProperty('e')) {
|
||||
console.log(resp);
|
||||
$('#updraft_migrate_tab_alt').append('<p style="color:red;">'+updraftlion.unexpectedresponse+' '+resp.r+'.<br>'+updraftlion.checkrpcsetup+'</p>');
|
||||
if (resp.hasOwnProperty('moreinfo')) {
|
||||
$('#updraft_migrate_tab_alt').append(resp.moreinfo);
|
||||
}
|
||||
// alert(updraftlion.unexpectedresponse+' '+resp.r+' ('+resp.code+'). '+updraftlion.checkrpcsetup);
|
||||
} else if (resp.hasOwnProperty('success')) {
|
||||
if (resp.hasOwnProperty('r')) {
|
||||
$('#updraft_migrate_testinginprogress').replaceWith('<p style="">'+resp.r+'</p>');
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
$('#updraft_migrate_tab_alt').append('<p style="color:red;">'+updraftlion.unexpectedresponse+' '+response+'</p>');
|
||||
console.log(err);
|
||||
console.log(response);
|
||||
return;
|
||||
}
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) {
|
||||
$('#updraft_migrate_tab_alt').append('<p style="color:red;">'+resp.fatal_error_message+'</p>');
|
||||
console.error(resp.fatal_error_message);
|
||||
} else {
|
||||
$('#updraft_migrate_tab_alt').append('<p style="color:red;">'+updraftlion.unexpectedresponse+' '+response+'</p>');
|
||||
console.log(err);
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updraft_migrate_go_existing_backup() {
|
||||
var $ = jQuery;
|
||||
|
||||
var site_id = $('#updraft_remotesites_selector').val();
|
||||
var backup_select = $('#updraft_migrate_tab_alt #updraftplus_remote_send_backup_options').find('option:selected');
|
||||
var nonce = backup_select.data('nonce');
|
||||
var timestamp = backup_select.data('timestamp');
|
||||
var services = 'remotesend';
|
||||
var extradata = {
|
||||
services: 'remotesend/'+site_id
|
||||
};
|
||||
|
||||
updraft_send_command('upload_local_backup', {
|
||||
use_nonce: nonce,
|
||||
use_timestamp: timestamp,
|
||||
services: services,
|
||||
extradata: extradata
|
||||
}, function (response) {
|
||||
jQuery('#updraft-navtab-backups').trigger('click');
|
||||
alert(updraftlion.local_upload_started);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate send a backup
|
||||
*/
|
||||
function updraft_migrate_go_backup() {
|
||||
var site_id = jQuery('#updraft_remotesites_selector').val();
|
||||
var entities = <?php echo function_exists('wp_json_encode') ? wp_json_encode($updraftplus->get_backupable_file_entities()) : json_encode($updraftplus->get_backupable_file_entities());?>;
|
||||
var onlythisfileentity = '';
|
||||
for (var entity_key in entities) {
|
||||
if (jQuery('#remotesend_updraft_include_'+entity_key).is(':checked')) {
|
||||
if (onlythisfileentity != '') { onlythisfileentity += ','; }
|
||||
onlythisfileentity += entity_key;
|
||||
}
|
||||
//Do something
|
||||
}
|
||||
|
||||
var backupnow_nodb = jQuery('#remotesend_backupnow_db').is(':checked') ? 0 : 1;
|
||||
|
||||
var backupnow_nofiles = 0;
|
||||
if ('' == onlythisfileentity) { backupnow_nofiles = 1; }
|
||||
|
||||
var backupnow_nocloud = 1;
|
||||
|
||||
var db_anon_all = jQuery('#updraft-navtab-migrate-content #updraftplus_migration_backupnow_db_anon_all').is(':checked') ? 1 : 0;
|
||||
var db_anon_non_staff = jQuery('#updraft-navtab-migrate-content #updraftplus_migration_backupnow_db_anon_non_staff').is(':checked') ? 1 : 0;
|
||||
var db_anon_wc_orders = jQuery('#updraft-navtab-migrate-content #updraftplus_migration_backupnow_db_anon_wc_order_data').is(':checked') ? 1 : 0;
|
||||
|
||||
var extradata = {
|
||||
services: 'remotesend/'+site_id,
|
||||
db_anon: {all: db_anon_all, non_staff: db_anon_non_staff, wc_orders: db_anon_wc_orders}
|
||||
};
|
||||
|
||||
if (jQuery('#remotesend_backupnow_cloud').is(':checked')) {
|
||||
backupnow_nocloud = 0;
|
||||
}
|
||||
|
||||
if (backupnow_nodb && backupnow_nofiles) {
|
||||
alert(updraftlion.excludedeverything);
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
jQuery('#updraft_lastlogmessagerow').fadeOut('slow', function() {
|
||||
jQuery(this).fadeIn('slow');
|
||||
});
|
||||
}, 1700);
|
||||
|
||||
updraft_backupnow_go(backupnow_nodb, backupnow_nofiles, backupnow_nocloud, onlythisfileentity, extradata, jQuery('#remotesend_backupnow_label').val(), '');
|
||||
jQuery('#updraft-navtab-backups').trigger('click');
|
||||
}
|
||||
|
||||
function updraft_migrate_receiving_makenew() {
|
||||
|
||||
var $ = jQuery;
|
||||
|
||||
var data = {
|
||||
subsubaction: 'updraft_migrate_newdestination',
|
||||
key: $('#updraft_migrate_receiving_new').val()
|
||||
}
|
||||
$('#updraft_migrate_receiving_makenew').html(updraftlion.addingsite);
|
||||
updraft_send_command('doaction', data, function(resp, status, response) {
|
||||
$('#updraft_migrate_receiving_makenew').html(updraftlion.addsite);
|
||||
|
||||
if (resp.hasOwnProperty('e')) {
|
||||
console.log(resp);
|
||||
alert(resp.e);
|
||||
} else if (resp.hasOwnProperty('r')) {
|
||||
if (resp.hasOwnProperty('selector')) {
|
||||
$('#updraft_migrate_receivingsites').html(resp.selector);
|
||||
}
|
||||
$('#updraft_migrate_receiving_new').val('');
|
||||
alert(resp.r);
|
||||
} else {
|
||||
alert(updraftlion.unexpectedresponse+' '+response);
|
||||
console.log(resp);
|
||||
console.log(response);
|
||||
}
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
$('#updraft_migrate_receiving_makenew').html(updraftlion.addsite);
|
||||
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) {
|
||||
console.error(resp.fatal_error_message);
|
||||
alert(resp.fatal_error_message);
|
||||
} else {
|
||||
var error_message = "updraft_send_command: error: "+status+" ("+error_code+")";
|
||||
console.log(error_message);
|
||||
alert(error_message);
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updraft_migrate_delete_existingsites(confirmation_message) {
|
||||
|
||||
if (confirm(confirmation_message)) {
|
||||
|
||||
var $ = jQuery;
|
||||
|
||||
var data = {
|
||||
subsubaction: 'updraft_migrate_delete_existingsites'
|
||||
}
|
||||
|
||||
updraft_send_command('doaction', data, function(resp) {
|
||||
if (resp.hasOwnProperty('success')) {
|
||||
alert(resp.success);
|
||||
}
|
||||
if (resp.hasOwnProperty('html')) {
|
||||
$('#updraft_migrate_receivingsites').html(resp.html);
|
||||
}
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
var msg = '';
|
||||
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) {
|
||||
console.error(resp.fatal_error_message);
|
||||
msg = resp.fatal_error_message;
|
||||
}
|
||||
if (response.hasOwnProperty('responseText') && response.responseText) {
|
||||
msg = response.responseText;
|
||||
if (response.hasOwnProperty('statusText') && response.statusText) {
|
||||
msg += ' ('+response.statusText+')';
|
||||
}
|
||||
} else if (response.hasOwnProperty('statusText') && response.statusText) {
|
||||
msg = response.statusText;
|
||||
}
|
||||
alert(msg);
|
||||
console.log(response);
|
||||
console.log(status);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new UpdraftPlus_Addons_Migrator_RemoteSend();
|
||||
606
wp-content/plugins/updraftplus/addons/moredatabase.php
Normal file
606
wp-content/plugins/updraftplus/addons/moredatabase.php
Normal file
@@ -0,0 +1,606 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: moredatabase:Multiple database backup options
|
||||
Description: Provides the ability to encrypt database backups, and to backup external databases
|
||||
Version: 1.7
|
||||
Shop: /shop/moredatabase/
|
||||
Latest Change: 1.16.24
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
new UpdraftPlus_Addon_MoreDatabase;
|
||||
|
||||
class UpdraftPlus_Addon_MoreDatabase {
|
||||
|
||||
private $database_tables;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter('updraft_backup_databases', array($this, 'backup_databases'));
|
||||
add_filter('updraft_database_encryption_config', array($this, 'database_encryption_config'));
|
||||
add_filter('updraft_encrypt_file', array($this, 'encrypt_file'), 10, 5);
|
||||
add_filter('updraft_database_moredbs_config', array($this, 'database_moredbs_config'));
|
||||
// This runs earlier than default, to allow users who were over-riding already with a filter to continue doing so
|
||||
add_filter('updraftplus_get_table_prefix', array($this, 'get_table_prefix'), 9);
|
||||
add_action('updraft_extradb_testconnection', array($this, 'extradb_testconnection'));
|
||||
// This one is used directly by UpdraftCentral
|
||||
add_filter('updraft_extradb_testconnection_go', array($this, 'extradb_testconnection_go'), 10, 2);
|
||||
add_action('updraftplus_restore_form_db', array($this, 'restore_form_db'), 9);
|
||||
add_filter('updraftplus_get_settings_meta', array($this, 'get_settings_meta'));
|
||||
add_filter('updraft_backupnow_database_showmoreoptions', array($this, 'backupnow_database_showmoreoptions'), 20, 2);
|
||||
add_filter('updraft_backupnow_options', array($this, 'backupnow_options'), 10, 2);
|
||||
add_filter('updraftplus_initial_jobdata', array($this, 'updraftplus_moredatabases_jobdata'), 10, 2);
|
||||
add_filter('updraftplus_backup_table', array($this, 'updraftplus_backup_table'), 10, 5);
|
||||
add_filter('updraftplus_job_option_cache', array($this, 'encryption_option_cache'), 10, 1);
|
||||
add_action('updraftplus_backup_db_begin', array($this, 'backup_db_begin'));
|
||||
}
|
||||
|
||||
public function get_settings_meta($meta) {
|
||||
if (!is_array($meta)) return $meta;
|
||||
|
||||
$extradbs = UpdraftPlus_Options::get_updraft_option('updraft_extradbs');
|
||||
if (!is_array($extradbs)) $extradbs = array();
|
||||
foreach ($extradbs as $i => $db) {
|
||||
if (!is_array($db) || empty($db['host'])) unset($extradbs[$i]);
|
||||
}
|
||||
$meta['extra_dbs'] = $extradbs;
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the restoration option fields
|
||||
*/
|
||||
public function restore_form_db() {
|
||||
|
||||
echo '<div class="updraft_restore_crypteddb updraft-hidden">'.esc_html__('Database decryption phrase', 'updraftplus').': ';
|
||||
|
||||
$updraft_encryptionphrase = UpdraftPlus_Options::get_updraft_option('updraft_encryptionphrase');
|
||||
|
||||
echo '<input type="'.esc_attr(apply_filters('updraftplus_admin_secret_field_type', 'text')).'" name="updraft_encryptionphrase" value="'.esc_attr($updraft_encryptionphrase).'"></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called via an action; it ensures the class is setup and ready for use
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function backup_db_begin() {
|
||||
global $updraftplus;
|
||||
if (empty($this->database_tables)) $this->database_tables = $updraftplus->jobdata_get('database_tables', array());
|
||||
}
|
||||
|
||||
public function get_table_prefix($prefix) {
|
||||
global $updraftplus;
|
||||
if (UpdraftPlus_Options::get_updraft_option('updraft_backupdb_nonwp')) {
|
||||
$updraftplus->log("All tables found will be backed up (indicated by backupdb_nonwp option)");
|
||||
return '';
|
||||
} else {
|
||||
|
||||
if (empty($this->database_tables)) return $prefix;
|
||||
|
||||
foreach ($this->database_tables as $database) {
|
||||
foreach ($database as $table) {
|
||||
if (0 !== strpos($table, $prefix) || 0 !== stripos($table, $prefix)) {
|
||||
$updraftplus->log("All tables found will be considered for backup (indicated by user selecting to backup some non-WP tables)");
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
public function extradb_testconnection($data) {
|
||||
echo json_encode($this->extradb_testconnection_go(array(), $data));
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is also used as a WP filter
|
||||
*
|
||||
* @param String $results_initial_value_ignored
|
||||
* @param String $posted_data
|
||||
* @return Array
|
||||
*/
|
||||
public function extradb_testconnection_go($results_initial_value_ignored, $posted_data) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
|
||||
if (empty($posted_data['user'])) return(array('r' => $posted_data['row'], 'm' => '<p>'.sprintf(__("Failure: No %s was given.", 'updraftplus').'</p>', __('user', 'updraftplus'))));
|
||||
|
||||
if (empty($posted_data['host'])) return(array('r' => $posted_data['row'], 'm' => '<p>'.sprintf(__("Failure: No %s was given.", 'updraftplus').'</p>', __('host', 'updraftplus'))));
|
||||
|
||||
if (empty($posted_data['name'])) return(array('r' => $posted_data['row'], 'm' => '<p>'.sprintf(__("Failure: No %s was given.", 'updraftplus').'</p>', __('database name', 'updraftplus'))));
|
||||
|
||||
global $updraftplus_admin;
|
||||
$updraftplus_admin->logged = array();
|
||||
|
||||
$ret = '';
|
||||
$failed = false;
|
||||
|
||||
$wpdb_obj = new UpdraftPlus_WPDB_OtherDB_Test($posted_data['user'], $posted_data['pass'], $posted_data['name'], $posted_data['host']);
|
||||
if (!empty($wpdb_obj->error)) {
|
||||
$failed = true;
|
||||
$ret .= '<p>'.$posted_data['user'].'@'.$posted_data['host'].'/'.$posted_data['name']." : ".__('database connection attempt failed', 'updraftplus')."</p>";
|
||||
if (is_wp_error($wpdb_obj->error) || is_string($wpdb_obj->error)) {
|
||||
$ret .= '<ul style="list-style: disc inside;">';
|
||||
if (is_wp_error($wpdb_obj->error)) {
|
||||
$codes = $wpdb_obj->error->get_error_codes();
|
||||
if (is_array($codes)) {
|
||||
foreach ($codes as $code) {
|
||||
if ('db_connect_fail' == $code) {
|
||||
$ret .= "<li>".__('Connection failed: check your access details, that the database server is up, and that the network connection is not firewalled.', 'updraftplus')."</li>";
|
||||
} else {
|
||||
$err = $wpdb_obj->error->get_error_message($code);
|
||||
$ret .= "<li>".$err."</li>";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$ret .= "<li>".$wpdb_obj->error."</li>";
|
||||
}
|
||||
$ret .= '</ul>';
|
||||
}
|
||||
}
|
||||
|
||||
$ret_info = '';
|
||||
if (!$failed) {
|
||||
$all_tables = $wpdb_obj->get_results("SHOW TABLES", ARRAY_N);
|
||||
$all_tables = array_map(array($this, 'cb_get_first_item'), $all_tables);
|
||||
if (empty($posted_data['prefix'])) {
|
||||
$ret_info .= sprintf(__('%s table(s) found.', 'updraftplus'), count($all_tables));
|
||||
} else {
|
||||
$our_prefix = 0;
|
||||
foreach ($all_tables as $table) {
|
||||
if (0 === strpos($table, $posted_data['prefix'])) $our_prefix++;
|
||||
}
|
||||
$ret_info .= sprintf(__('%s total table(s) found; %s with the indicated prefix.', 'updraftplus'), count($all_tables), $our_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
$ret_after = '';
|
||||
|
||||
if (count($updraftplus_admin->logged) >0) {
|
||||
$ret_after .= "<p>".__('Messages:', 'updraftplus');
|
||||
$ret_after .= '<ul style="list-style: disc inside;">';
|
||||
|
||||
foreach (array_unique($updraftplus_admin->logged) as $code => $err) {
|
||||
if ('db_connect_fail' === $code) $failed = true;
|
||||
$ret_after .= "<li><strong>$code:</strong> $err</li>";
|
||||
}
|
||||
$ret_after .= '</ul></p>';
|
||||
}
|
||||
|
||||
if (!$failed) {
|
||||
$ret = '<p>'.__('Connection succeeded.', 'updraftplus').' '.$ret_info.'</p>'.$ret;
|
||||
} else {
|
||||
$ret = '<p>'.__('Connection failed.', 'updraftplus').'</p>'.$ret;
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
return array('r' => $posted_data['row'], 'm' => $ret.$ret_after);
|
||||
|
||||
}
|
||||
|
||||
public function database_moredbs_config($ret) {
|
||||
global $updraftplus;
|
||||
$ret = '';
|
||||
$tp = $updraftplus->get_table_prefix(false);
|
||||
$updraft_backupdb_nonwp = UpdraftPlus_Options::get_updraft_option('updraft_backupdb_nonwp');
|
||||
|
||||
$ret .= '<input type="checkbox"'.(($updraft_backupdb_nonwp) ? ' checked="checked"' : '').' id="updraft_backupdb_nonwp" name="updraft_backupdb_nonwp" value="1"><label for="updraft_backupdb_nonwp" title="'.sprintf(__('This option will cause tables stored in the MySQL database which do not belong to WordPress (identified by their lacking the configured WordPress prefix, %s) to also be backed up.', 'updraftplus'), $tp).'">'.__('Backup non-WordPress tables contained in the same database as WordPress', 'updraftplus').'</label><br>';
|
||||
$ret .= '<p><em>'.__('If your database includes extra tables that are not part of this WordPress site (you will know if this is the case), then activate this option to also back them up.', 'updraftplus').'</em></p>';
|
||||
|
||||
$ret .= '<div id="updraft_backupextradbs"></div>';
|
||||
|
||||
$ret .= '<div id="updraft_backupextradbs_another_container"><a href="'.esc_url(UpdraftPlus::get_current_clean_url()).'" id="updraft_backupextradb_another" class="updraft_icon_link"><span class="dashicons dashicons-plus"></span>'.__('Add an external database to backup...', 'updraftplus').'</a></div>';
|
||||
|
||||
add_action('admin_footer', array($this, 'admin_footer'));
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function admin_footer() {
|
||||
?>
|
||||
<style type="text/css">
|
||||
|
||||
#updraft_backupextradbs_another_container {
|
||||
clear:both; float:left;
|
||||
}
|
||||
|
||||
#updraft_backupextradbs {
|
||||
clear:both;
|
||||
float:left;
|
||||
}
|
||||
|
||||
.updraft_backupextradbs_row {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
.updraft_backupextradbs_row h3 {
|
||||
margin-top: 0px; padding-top: 0px; margin-bottom: 3px;
|
||||
font-size: 90%;
|
||||
}
|
||||
.updraft_backupextradbs_row .updraft_backupextradbs_testresultarea {
|
||||
float: left; clear: both;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.updraft_backupextradbs_row .updraft_backupextradbs_row_label {
|
||||
float: left; width: 90px;
|
||||
padding-top:1px;
|
||||
}
|
||||
.updraft_backupextradbs_row .updraft_backupextradbs_row_textinput {
|
||||
float: left; width: calc(50% - 97px); clear:none; margin-right: 6px;
|
||||
}
|
||||
|
||||
.updraft_backupextradbs_row .updraft_backupextradbs_row_test {
|
||||
margin-left: 90px; width: 150px; padding: 6px 0; text-align:left;
|
||||
}
|
||||
|
||||
.updraft_backupextradbs_row .updraft_backupextradbs_row_host {
|
||||
clear:left;
|
||||
}
|
||||
|
||||
@media (max-width: 782px) {
|
||||
.updraft_backupextradbs_row .updraft_backupextradbs_row_test {
|
||||
margin-left: 0; width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
jQuery(function($) {
|
||||
var updraft_extra_dbs = 0;
|
||||
function updraft_extradbs_add(host, user, name, pass, prefix) {
|
||||
updraft_extra_dbs++;
|
||||
$('<div class="updraft_small_box updraft_backupextradbs_row updraft-hidden" style="display:none;" id="updraft_backupextradbs_row_'+updraft_extra_dbs+'">'+
|
||||
'<button type="button" class="updraft_box_delete_button updraft_backupextradbs_row_delete"><span title="<?php echo esc_attr(__('Remove', 'updraftplus'));?>" class="dashicons dashicons-no"></span></button>'+
|
||||
'<h3><?php echo esc_js(__('Backup external database', 'updraftplus'));?></h3>'+
|
||||
'<div class="updraft_backupextradbs_testresultarea"></div>'+
|
||||
'<div class="updraft_backupextradbs_row_label updraft_backupextradbs_row_host" title="<?php echo esc_attr(__('Enter host.', 'updraftplus'));?>"><?php echo esc_js(__('Host', 'updraftplus'));?>:</div><input class="updraft_backupextradbs_row_textinput extradb_host" title="<?php echo esc_attr(__('Enter host', 'updraftplus'));?>" type="text" name="updraft_extradbs['+updraft_extra_dbs+'][host]" value="'+host+'">'+
|
||||
'<div class="updraft_backupextradbs_row_label" title="<?php echo esc_attr(__('Enter username.', 'updraftplus'));?>"><?php echo esc_js(__('Username', 'updraftplus'));?>:</div><input class="updraft_backupextradbs_row_textinput extradb_user" title="<?php echo esc_attr(__('Enter username', 'updraftplus'));?>" type="text" name="updraft_extradbs['+updraft_extra_dbs+'][user]" value="'+user+'">'+
|
||||
'<div class="updraft_backupextradbs_row_label" title="<?php echo esc_attr(__('Enter password.', 'updraftplus'));?>"><?php echo esc_js(__('Password', 'updraftplus'));?>:</div><input class="updraft_backupextradbs_row_textinput extradb_pass" title="<?php echo esc_attr(__('Enter password', 'updraftplus'));?>" type="<?php echo esc_attr(apply_filters('updraftplus_admin_secret_field_type', 'text')); ?>" name="updraft_extradbs['+updraft_extra_dbs+'][pass]" value="'+pass+'">'+
|
||||
'<div class="updraft_backupextradbs_row_label" title="<?php echo esc_attr(__('Enter database.', 'updraftplus'));?>"><?php echo esc_js(__('Database', 'updraftplus'));?>:</div><input class="updraft_backupextradbs_row_textinput extradb_name" title="<?php echo esc_attr(__('Enter database', 'updraftplus'));?>" type="text" name="updraft_extradbs['+updraft_extra_dbs+'][name]" value="'+name+'">'+
|
||||
'<div class="updraft_backupextradbs_row_label" title="<?php echo esc_attr(__('Enter table prefix', 'updraftplus')).'. '.esc_attr(__('If you enter a table prefix, then only tables that begin with this prefix will be backed up.', 'updraftplus'));?>"><?php echo esc_js(__('Table prefix', 'updraftplus'));?>:</div><input class="updraft_backupextradbs_row_textinput extradb_prefix" title="<?php echo esc_attr(__('Enter table prefix', 'updraftplus')).'. '.esc_attr('If you enter a table prefix, then only tables that begin with this prefix will be backed up.', 'updraftplus');?>" type="text" name="updraft_extradbs['+updraft_extra_dbs+'][prefix]" value="'+prefix+'">'+
|
||||
'<div class="updraft_backupextradbs_row_label updraft_backupextradbs_row_test"><a href="<?php echo esc_url(UpdraftPlus::get_current_clean_url());?>" class="updraft_backupextradbs_row_testconnection"><?php echo esc_js(__('Test connection...', 'updraftplus'));?></a></div>'+
|
||||
'</div>').appendTo($('#updraft_backupextradbs')).fadeIn();
|
||||
}
|
||||
$('#updraft_backupextradb_another').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
updraft_extradbs_add('', '', '', '', '');
|
||||
});
|
||||
$('#updraft_backupextradbs').on('click', '.updraft_backupextradbs_row_delete', function() {
|
||||
$(this).parents('.updraft_backupextradbs_row').slideUp('slow').delay(400).remove();
|
||||
});
|
||||
$('#updraft_backupextradbs').on('click', '.updraft_backupextradbs_row_testconnection', function(e) {
|
||||
e.preventDefault();
|
||||
var row = $(this).parents('.updraft_backupextradbs_row');
|
||||
$(row).find('.updraft_backupextradbs_testresultarea').html('<p><em><?php echo esc_js(__('Testing...', 'updraftplus'));?></em></p>');
|
||||
var data = {
|
||||
subsubaction: 'updraft_extradb_testconnection',
|
||||
row: $(row).attr('id'),
|
||||
host: $(row).children('.extradb_host').val(),
|
||||
user: $(row).children('.extradb_user').val(),
|
||||
pass: $(row).children('.extradb_pass').val(),
|
||||
name: $(row).children('.extradb_name').val(),
|
||||
prefix: $(row).children('.extradb_prefix').val()
|
||||
};
|
||||
|
||||
updraft_send_command('doaction', data, function(resp, status, response) {
|
||||
try {
|
||||
if (resp.m && resp.r) {
|
||||
$('#'+resp.r).find('.updraft_backupextradbs_testresultarea').html(resp.m);
|
||||
} else {
|
||||
alert('<?php echo esc_js(__('Error: the server sent us a response (JSON) which we did not understand.', 'updraftplus'));?> '+resp);
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
console.log(data);
|
||||
}
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) {
|
||||
console.error(resp.fatal_error_message);
|
||||
$('#'+$(row).attr('id')).find('.updraft_backupextradbs_testresultarea').html('<p style="color:red;">'+resp.fatal_error_message+'</p>');
|
||||
alert(resp.fatal_error_message);
|
||||
} else {
|
||||
var error_message = "updraft_send_command: error: "+status+" ("+error_code+")";
|
||||
console.log(error_message);
|
||||
console.log(response);
|
||||
$('#'+$(row).attr('id')).find('.updraft_backupextradbs_testresultarea').html('<p style="color:red;">'+updraftlion.servererrorcode+'</p>');
|
||||
alert(updraftlion.unexpectedresponse+' '+response);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
<?php
|
||||
$extradbs = UpdraftPlus_Options::get_updraft_option('updraft_extradbs');
|
||||
if (is_array($extradbs)) {
|
||||
foreach ($extradbs as $db) {
|
||||
if (is_array($db) && !empty($db['host'])) echo "updraft_extradbs_add('".esc_js($db['host'])."', '".esc_js($db['user'])."', '".esc_js($db['name'])."', '".esc_js($db['pass'])."', '".esc_js($db['prefix'])."');\n";
|
||||
}
|
||||
}
|
||||
?>
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function database_encryption_config() {
|
||||
$updraft_encryptionphrase = UpdraftPlus_Options::get_updraft_option('updraft_encryptionphrase');
|
||||
|
||||
$ret = '';
|
||||
|
||||
if (!function_exists('mcrypt_encrypt') && !extension_loaded('openssl')) {
|
||||
$ret .= '<p><strong>'.sprintf(__('Your web-server does not have the %s module installed.', 'updraftplus'), 'PHP/mcrypt / PHP/OpenSSL').' '.__('Without it, encryption will be a lot slower.', 'updraftplus').'</strong></p>';
|
||||
}
|
||||
|
||||
$ret .= '<input type="'.apply_filters('updraftplus_admin_secret_field_type', 'text').'" name="updraft_encryptionphrase" id="updraft_encryptionphrase" value="'.esc_attr($updraft_encryptionphrase).'" class="updraft_input--wide">';
|
||||
|
||||
$ret .= '<p>'.__('If you enter text here, it is used to encrypt database backups (Rijndael).', 'updraftplus').' '.__('<strong>Do make a separate record of it and do not lose it, or all your backups <em>will</em> be useless.</strong>', 'updraftplus').' '.__('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).', 'updraftplus').'</p>';
|
||||
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
public function backup_databases($w) {
|
||||
|
||||
if (!is_array($w)) return $w;
|
||||
|
||||
$extradbs = UpdraftPlus_Options::get_updraft_option('updraft_extradbs');
|
||||
if (empty($extradbs) || !is_array($extradbs)) return $w;
|
||||
|
||||
$dbnum = 0;
|
||||
foreach ($extradbs as $db) {
|
||||
if (!is_array($db) || empty($db['host'])) continue;
|
||||
$dbnum++;
|
||||
$w[$dbnum] = array('dbinfo' => $db, 'status' => 'begun');
|
||||
}
|
||||
|
||||
return $w;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function encrypts the database when specified. Used in backup.php.
|
||||
*
|
||||
* @param array $result
|
||||
* @param string $file this is the file name of the db zip to be encrypted
|
||||
* @param string $encryption This is the encryption word (salting) to be used when encrypting the data
|
||||
* @param string $whichdb This specifies the correct DB
|
||||
* @param string $whichdb_suffix This specifies the DB suffix
|
||||
* @return string returns the encrypted file name
|
||||
*/
|
||||
public function encrypt_file($result, $file, $encryption, $whichdb, $whichdb_suffix) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
|
||||
global $updraftplus;
|
||||
$updraft_dir = $updraftplus->backups_dir_location();
|
||||
$updraftplus->jobdata_set('jobstatus', 'dbencrypting'.$whichdb_suffix);
|
||||
$time_started = microtime(true);
|
||||
$file_size = @filesize($updraft_dir.'/'.$file)/1024;// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
|
||||
|
||||
$memory_limit = ini_get('memory_limit');
|
||||
$memory_usage = round(@memory_get_usage(false)/1048576, 1);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
|
||||
$memory_usage2 = round(@memory_get_usage(true)/1048576, 1);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
|
||||
$updraftplus->log("Encryption being requested: file_size: ".round($file_size, 1)." KB memory_limit: $memory_limit (used: {$memory_usage}M | {$memory_usage2}M)");
|
||||
|
||||
$encrypted_file = UpdraftPlus_Encryption::encrypt($updraft_dir.'/'.$file, $encryption);
|
||||
|
||||
if (false !== $encrypted_file) {
|
||||
// return basename($file);
|
||||
$time_taken = max(0.000001, microtime(true)-$time_started);
|
||||
|
||||
$checksums = $updraftplus->which_checksums();
|
||||
|
||||
foreach ($checksums as $checksum) {
|
||||
$cksum = hash_file($checksum, $updraft_dir.'/'.$file.'.crypt');
|
||||
$updraftplus->jobdata_set($checksum.'-db'.(('wp' == $whichdb) ? '0' : $whichdb.'0').'.crypt', $cksum);
|
||||
$updraftplus->log("$file: encryption successful: ".round($file_size, 1)."KB in ".round($time_taken, 2)."s (".round($file_size/$time_taken, 1)."KB/s) ($checksum checksum: $cksum)");
|
||||
|
||||
}
|
||||
|
||||
// Delete unencrypted file
|
||||
@unlink($updraft_dir.'/'.$file);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist.
|
||||
|
||||
$updraftplus->jobdata_set('jobstatus', 'dbencrypted'.$whichdb_suffix);
|
||||
|
||||
return basename($file.'.crypt');
|
||||
} else {
|
||||
$updraftplus->log("Encryption error occurred when encrypting database. Encryption aborted.");
|
||||
$updraftplus->log(__('Encryption error occurred when encrypting database.', 'updraftplus').' '.__('Encryption aborted.', 'updraftplus'), 'error');
|
||||
return basename($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that gets a list of tables from the users databases and generates html using these values so that the user can select what tables they want to backup instead of a full database backup.
|
||||
*
|
||||
* @param String $ret this contains the upgrade to premium link and gets cleared here and replaced with table content
|
||||
* @param String $prefix currently unused here because these parameters are passed to the filter
|
||||
* @return String A string that contains HTML to be appended to the backup now modal
|
||||
*/
|
||||
public function backupnow_database_showmoreoptions($ret, $prefix) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$ret .= '<em>'.__('You should backup all tables unless you are an expert in the internals of the WordPress database.', 'updraftplus').'</em><br>';
|
||||
|
||||
// In future this can be passed an array of databases to support external databases
|
||||
$database_table_list = $updraftplus->get_database_tables();
|
||||
|
||||
$non_wp_tables = UpdraftPlus_Options::get_updraft_option('updraft_backupdb_nonwp');
|
||||
$table_prefix = $updraftplus->get_table_prefix(false);
|
||||
|
||||
foreach ($database_table_list as $database_name => $database) {
|
||||
|
||||
// If we are not backing up non WordPress databases and the key is not the main WordPress database then skip it.
|
||||
if (!$non_wp_tables && 'wp' != $database_name) continue;
|
||||
|
||||
$checkboxes = '';
|
||||
$non_wp_table_exists = false;
|
||||
foreach ($database as $value) {
|
||||
|
||||
$checked = 'checked="checked"';
|
||||
|
||||
// If we are not backing up non WordPress tables and the current table does not contain the WordPress table prefix then don't check it but add a data attribute.
|
||||
if (!$non_wp_tables && substr($value, 0, strlen($table_prefix)) !== $table_prefix) {
|
||||
$checked = 'data-non_wp_table="1"';
|
||||
if (!$non_wp_table_exists) $non_wp_table_exists = true;
|
||||
}
|
||||
|
||||
/*
|
||||
This outputs each table to the page setting the name to updraft_include_tables_ $database_name this allows the value to be trimmed later and to build an array of tables to be backed up
|
||||
*/
|
||||
$checkboxes .= '<input class="updraft_db_entity" id="'.$prefix.'updraft_db_'.$value.'" '.$checked.' type="checkbox" name="updraft_include_tables_'. $database_name . '" value="'.$value.'"> <label for="'.$prefix.'updraft_db_'.$value.'">'.$value.'</label><br>';
|
||||
}
|
||||
|
||||
$ret .= '<div class="backupnow-db-tables">';
|
||||
$links = '<a class="backupnow-select-all-table" href="#">'.__('Select all', 'updraftplus').'</a>';
|
||||
if ($non_wp_table_exists) $links .= ' | <a class="backupnow-select-all-this-site" href="#">'.__('Select all (this site)', 'updraftplus').'</a>';
|
||||
$links .= ' | <a class="backupnow-deselect-all-table" href="#">'.__('Deselect all', 'updraftplus').'</a><br>';
|
||||
$show_as = ('wp' == $database_name) ? __('WordPress database', 'updraftplus') : $database_name;
|
||||
$ret .= '<br>'.$links.'<em>'. $show_as . ' ' . __('tables', 'updraftplus') . '</em><br>';
|
||||
$ret .= $checkboxes;
|
||||
$ret .= '</div>';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks to see if all tables are selected to backup if they are not then it creates an array of tables to be backed up ready for the backup to use later to exclude them
|
||||
*
|
||||
* @param [array] $options an array of options that is being passed to the backup method
|
||||
* @param [array] $request an array of ajax request and extra parameters including the tables that are selected for backup
|
||||
*/
|
||||
public function backupnow_options($options, $request) {
|
||||
if (!is_array($options)) return $options;
|
||||
|
||||
// if onlythesetableentities is not an array then all tables are being backed up and we don't need to do this
|
||||
if (!empty($request['onlythesetableentities']) && is_array($request['onlythesetableentities'])) {
|
||||
$database_tables = array();
|
||||
$database_entities = $request['onlythesetableentities'];
|
||||
|
||||
foreach ($database_entities as $key => $value) {
|
||||
/*
|
||||
This name key inside the value array is the database name prefixed by 23 characters so we need to remove them to get the actual name, then the value key inside the value array has the table name.
|
||||
*/
|
||||
$database_tables[substr($value['name'], 23)][$key] = $value['value'];
|
||||
}
|
||||
|
||||
$this->database_tables = $database_tables;
|
||||
$options['database_tables'] = $database_tables;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will set up the backup job data for when we are starting a backup that does not include all the database tables. It changes the initial jobdata so that UpdraftPlus knows what database tables to backup or skip during a resumption.
|
||||
*
|
||||
* @param array $jobdata - the initial job data that we want to change
|
||||
* @param array $options - options sent from the front end
|
||||
*
|
||||
* @return array - the modified jobdata
|
||||
*/
|
||||
public function updraftplus_moredatabases_jobdata($jobdata, $options) {
|
||||
|
||||
if (!is_array($jobdata) || empty($options['database_tables'])) return $jobdata;
|
||||
|
||||
$jobdata[] = 'database_tables';
|
||||
$jobdata[] = $options['database_tables'];
|
||||
|
||||
return $jobdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called during a backup to check if the table is included in this classes database_tables array if it is then return true so it can be backed up otherwise return false so that it is skipped
|
||||
*
|
||||
* @param [boolean] $bool a boolean value
|
||||
* @param [string] $table a string containing the table
|
||||
* @param [string] $table_prefix a string containing the table prefix
|
||||
* @param [string|int] $whichdb a string or int indicating what database this table is from
|
||||
* @param [array] $dbinfo an array of information about the current database
|
||||
* @return [boolean] a boolean value indicating if a table should be included in the backup or not
|
||||
*/
|
||||
public function updraftplus_backup_table($bool, $table, $table_prefix, $whichdb, $dbinfo) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
|
||||
// Check this empty not to cause errors
|
||||
if (!empty($this->database_tables)) {
|
||||
// whichdb could be an int in which case to get the name of the database and the array key use the name from dbinfo
|
||||
if ('wp' !== $whichdb) {
|
||||
$key = $dbinfo['name'];
|
||||
} else {
|
||||
$key = $whichdb;
|
||||
}
|
||||
|
||||
// first check table_prefix is not empty and that the table does not already have the prefix attached
|
||||
if (!empty($table_prefix) && substr($table, 0, strlen($table_prefix)) === $table_prefix) {
|
||||
$table_name = $table;
|
||||
} else {
|
||||
$table_name = $table_prefix . $table;
|
||||
}
|
||||
|
||||
// check this is actually set not to cause any errors
|
||||
if (isset($this->database_tables[$key])) {
|
||||
if (in_array($table_name, $this->database_tables[$key])) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the member of the array with key (int)0. This function is used as a callback for array_map().
|
||||
*
|
||||
* @param Array $a - the array
|
||||
*
|
||||
* @return Mixed - the first item off the array
|
||||
*/
|
||||
private function cb_get_first_item($a) {
|
||||
return $a[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called via the filter updraftplus_job_option_cache it adds the encryption phrase to the option cache so it can be accessed during a job.
|
||||
*
|
||||
* @param Array $options_cache - the options cache
|
||||
*
|
||||
* @return Array - the updated options cache
|
||||
*/
|
||||
public function encryption_option_cache($options_cache) {
|
||||
$options_cache['updraft_encryptionphrase'] = UpdraftPlus_Options::get_updraft_option('updraft_encryptionphrase');
|
||||
return $options_cache;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Needs keeping in sync with the version in backup.php
|
||||
*/
|
||||
class UpdraftPlus_WPDB_OtherDB_Test extends wpdb {
|
||||
/**
|
||||
* This adjusted bail() does two things: 1) Never dies and 2) logs in the UD log
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $error_code
|
||||
* @return boolean
|
||||
*/
|
||||
public function bail($message, $error_code = 'updraftplus_default') {
|
||||
// global $updraftplus_admin;
|
||||
// if ('updraftplus_default' == $error_code) {
|
||||
// $updraftplus_admin->logged[] = $message;
|
||||
// } else {
|
||||
// $updraftplus_admin->logged[$error_code] = $message;
|
||||
// }
|
||||
// Now do the things that would have been done anyway
|
||||
if (class_exists('WP_Error'))
|
||||
$this->error = new WP_Error($error_code, $message);
|
||||
else $this->error = $message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
1259
wp-content/plugins/updraftplus/addons/morefiles.php
Normal file
1259
wp-content/plugins/updraftplus/addons/morefiles.php
Normal file
File diff suppressed because it is too large
Load Diff
303
wp-content/plugins/updraftplus/addons/morestorage.php
Normal file
303
wp-content/plugins/updraftplus/addons/morestorage.php
Normal file
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: morestorage:Multiple storage options
|
||||
Description: Provides the ability to backup to multiple remote storage facilities, not just one
|
||||
Version: 1.3
|
||||
Shop: /shop/morestorage/
|
||||
Latest Change: 1.11.28
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
new UpdraftPlus_Addon_MoreStorage;
|
||||
|
||||
class UpdraftPlus_Addon_MoreStorage {
|
||||
|
||||
public function __construct() {
|
||||
add_filter('updraftplus_storage_printoptions', array($this, 'storage_printoptions'), 10, 2);
|
||||
add_filter('updraftplus_storage_printoptions_multi', array($this, 'storage_printoptions_multi'), 10, 1);
|
||||
// add_action('updraftplus_config_print_after_storage', array($this, 'config_print_after_storage'));
|
||||
add_action('updraftplus_config_print_before_storage', array($this, 'config_print_before_storage'), 10, 2);
|
||||
add_action('updraftplus_config_print_add_multi_storage', array($this, 'config_print_add_multi_storage'), 10, 2);
|
||||
add_action('updraftplus_config_print_add_instance_label', array($this, 'config_print_add_instance_label'), 10, 2);
|
||||
add_action('updraftplus_config_print_add_conditional_logic', array($this, 'config_print_add_conditional_logic'), 10, 2);
|
||||
add_filter('updraftplus_savestorage', array($this, 'savestorage'), 10, 2);
|
||||
add_action('updraftplus_after_remote_storage_heading_message', array($this, 'after_remote_storage_heading_message'));
|
||||
add_filter('updraft_boot_backup_remote_storage_instance_include', array($this, 'boot_backup_remote_storage_instance_include'), 10, 5);
|
||||
}
|
||||
|
||||
public function after_remote_storage_heading_message() {
|
||||
return '<em>'.__('(as many as you like)', 'updraftplus').'</em>';
|
||||
}
|
||||
|
||||
public function admin_print_footer_scripts() {
|
||||
?>
|
||||
<script>
|
||||
jQuery(function() {
|
||||
|
||||
jQuery('.remote-tab').on('click', function(event) {
|
||||
//Close other tabs and open the clicked one
|
||||
event.preventDefault();
|
||||
var the_method = jQuery(this).attr('name');
|
||||
updraft_remote_storage_tab_activation(the_method);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will setup the HTML template that is added before each remote storage template.
|
||||
*
|
||||
* @param String $storage - the name of the remote storage method
|
||||
* @param Object $storage_object - the remote storage object
|
||||
* @return String - the HTML template
|
||||
*/
|
||||
public function config_print_before_storage($storage, $storage_object = null) {
|
||||
global $updraftplus;
|
||||
$tr_class = is_object($storage_object) ? $storage_object->get_css_classes() . ' ' . $storage . '_updraft_remote_storage_border' : "updraftplusmethod ".$storage;
|
||||
?>
|
||||
<tr class="<?php echo esc_attr($tr_class); ?>">
|
||||
<th>
|
||||
<?php
|
||||
if (is_object($storage_object) && $storage_object->supports_feature('multi_storage')) {
|
||||
?>
|
||||
<h3 class="updraft_edit_label_instance" data-instance_id="{{instance_id}}" data-method="<?php echo esc_attr($storage); ?>">{{instance_label}}<span class="dashicons dashicons-edit"></span></h3>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<h3><?php echo esc_html($updraftplus->backup_methods[$storage]); ?></h3>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</th>
|
||||
<td>
|
||||
<?php
|
||||
if (is_object($storage_object) && $storage_object->supports_feature('multi_storage')) {
|
||||
?>
|
||||
<div class="updraft_multi_storage_options">
|
||||
<input type="checkbox" class="updraft_instance_toggle" id="<?php echo esc_attr('updraft_' . $storage . '_instance_enabled' . '_{{instance_id}}');?>" name="<?php echo esc_attr('updraft_' . $storage . '[settings][{{instance_id}}][instance_enabled]');?>" value="1" {{#ifeq "1" instance_enabled}} checked="checked"{{/ifeq}}>
|
||||
<label for="<?php echo esc_attr('updraft_' . $storage . '_instance_enabled' . '_{{instance_id}}');?>" class="updraft_toggle_instance_label">{{#ifeq "1" instance_enabled}}<?php esc_html_e('Currently enabled', 'updraftplus'); ?>{{else}} <?php esc_html_e('Currently disabled', 'updraftplus'); ?>{{/ifeq}}</label>
|
||||
</div>
|
||||
<a href="<?php echo esc_url(UpdraftPlus::get_current_clean_url());?>" class="updraft_multi_storage_options updraft_delete_instance" data-instance_id="{{instance_id}}" data-method="<?php echo esc_attr($storage); ?>"><?php esc_html_e('Delete these settings', 'updraftplus'); ?></a>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup conditional logic HTML template
|
||||
*
|
||||
* @param String $storage - the name of the remote storage method
|
||||
* @param Object $storage_object - the remote storage object
|
||||
* @return Void|Null void on success, null if the given storage object doesn't support `conditional_logic` feature
|
||||
*/
|
||||
public function config_print_add_conditional_logic($storage, $storage_object = null) {
|
||||
if (!$storage_object->supports_feature('conditional_logic')) return;
|
||||
$tr_class = is_object($storage_object) ? $storage_object->get_css_classes() : "updraftplusmethod ".$storage;
|
||||
?>
|
||||
<tr class="<?php echo esc_attr($tr_class);?> conditional_logic_row">
|
||||
<th><?php esc_html_e('Send scheduled backups to this destination:', 'updraftplus'); ?></th>
|
||||
<td>
|
||||
{{#with instance_conditional_logic as | logic |}}
|
||||
<div class="conditional_remote_backup">
|
||||
<select class="logic_type" name="<?php echo esc_attr('updraft_' . $storage . '[settings][{{@root.instance_id}}][instance_conditional_logic][type]');?>">
|
||||
{{#each logic.logic_options}}
|
||||
<option value="{{this.value}}"{{#ifCond this.value "==" logic.type}} selected{{/ifCond}}>{{this.label}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<div class="logic"{{#ifCond "undefined" "typeof" logic.rules}} style="display: none"{{else}}{{#ifeq "0" (get_length logic.rules)}} style="display: none"{{/ifeq}}{{/ifCond}}>
|
||||
<ul class="rules" data-storage="<?php echo esc_attr($storage); ?>" data-instance_id="{{@root.instance_id}}" data-rules="{{#ifCond "0" "<" (get_length logic.rules)}}{{get_length logic.rules}}{{else}}1{{/ifCond}}">
|
||||
{{#ifCond "0" "<" (get_length logic.rules)}}
|
||||
{{#each logic.rules as | rule |}}
|
||||
<li>
|
||||
<select class="conditional_logic_operand" name="<?php echo esc_attr('updraft_' . $storage . '[settings][{{@root.instance_id}}][instance_conditional_logic][rules][{{@index}}][operand]');?>">
|
||||
{{#each logic.operand_options}}
|
||||
<option value="{{this.value}}"{{#ifCond this.value "==" rule.operand}} selected{{/ifCond}}>{{this.label}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<select name="<?php echo esc_attr('updraft_' . $storage . '[settings][{{@root.instance_id}}][instance_conditional_logic][rules][{{@index}}][operator]');?>">
|
||||
{{#each logic.operator_options}}
|
||||
<option value="{{this.value}}"{{#ifCond this.value "==" rule.operator}} selected{{/ifCond}}>{{this.label}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<select name="<?php echo esc_attr('updraft_' . $storage . '[settings][{{@root.instance_id}}][instance_conditional_logic][rules][{{@index}}][value]');?>">
|
||||
{{#ifCond "day_of_the_month" "==" rule.operand}}
|
||||
{{#for 1 31 1}}<option value="{{this}}"{{#ifCond this "==" rule.value}} selected{{/ifCond}}>{{this}}</option>{{/for}}
|
||||
{{/ifCond}}
|
||||
{{#ifCond "day_of_the_week" "==" rule.operand}}
|
||||
{{#each logic.day_of_the_week_options}}
|
||||
<option value="{{this.index}}"{{#ifCond this.index "==" rule.value}} selected{{/ifCond}}>{{this.value}}</option>
|
||||
{{/each}}
|
||||
{{/ifCond}}
|
||||
</select>
|
||||
</span>
|
||||
{{#ifCond "1" "<" (get_length logic.rules)}}
|
||||
<span class="remove-rule">
|
||||
<svg viewbox="0 0 25 25">
|
||||
<line x1="6.5" y1="18.5" x2="18.5" y2="6.5" fill="none" stroke="#FF6347" stroke-width="3" vector-effect="non-scaling-stroke" ></line>
|
||||
<line y1="6.5" x1="6.5" y2="18.5" x2="18.5" fill="none" stroke="#FF6347" stroke-width="3" vector-effect="non-scaling-stroke" ></line>
|
||||
</svg>
|
||||
</span>
|
||||
{{/ifCond}}
|
||||
</li>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<li>
|
||||
<select class="conditional_logic_operand" name="<?php echo esc_attr('updraft_' . $storage . '[settings][{{@root.instance_id}}][instance_conditional_logic][rules][0][operand]');?>" disabled>
|
||||
{{#each logic.operand_options}}
|
||||
{{#ifeq @index 0}}{{#set_var 'selected_rule_operand' this.value}}{{/set_var}}{{/ifeq}}
|
||||
<option value="{{this.value}}">{{this.label}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<select name="<?php echo esc_attr('updraft_' . $storage . '[settings][{{@root.instance_id}}][instance_conditional_logic][rules][0][operator]');?>" disabled>
|
||||
{{#each logic.operator_options}}
|
||||
<option value="{{this.value}}">{{this.label}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<select name="<?php echo esc_attr('updraft_' . $storage . '[settings][{{@root.instance_id}}][instance_conditional_logic][rules][0][value]');?>" disabled>
|
||||
{{#ifCond @root.selected_rule_operand "===" "day_of_the_week"}}
|
||||
{{#each logic.day_of_the_week_options}}
|
||||
<option value="{{this.index}}"{{#ifCond this.index "==" rule.value}} selected{{/ifCond}}>{{this.value}}</option>
|
||||
{{/each}}
|
||||
{{/ifCond}}
|
||||
{{#ifCond @root.selected_rule_operand "===" "day_of_the_month"}}
|
||||
{{#for 1 31 1}}<option value="{{this}}"{{#ifCond this "==" rule.value}} selected{{/ifCond}}>{{this}}</option>{{/for}}
|
||||
{{/ifCond}}
|
||||
</select>
|
||||
</li>
|
||||
{{/ifCond}}
|
||||
</ul>
|
||||
<input type="button" class="button-primary add-new-rule" value="Add new rule">
|
||||
</div>
|
||||
</div>
|
||||
{{/with}}
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will setup the HTML template for the add instance button
|
||||
*
|
||||
* @param String $storage - the name of the remote storage method
|
||||
* @param Object $storage_object - the remote storage object
|
||||
* @return String - the HTML template
|
||||
*/
|
||||
public function config_print_add_multi_storage($storage, $storage_object = null) {
|
||||
global $updraftplus;
|
||||
$tr_class = is_object($storage_object) ? $storage_object->get_css_classes(false) . " " . $storage . "_add_instance_container" : "updraftplusmethod ".$storage;
|
||||
?><tr class="<?php echo esc_attr($tr_class);?>">
|
||||
<td colspan="2">
|
||||
<a href="<?php echo esc_url(UpdraftPlus::get_current_clean_url()); ?>" class="updraft_add_instance" data-method="<?php echo esc_attr($storage); ?>"><?php echo esc_html(sprintf(__('Add another %s account...', 'updraftplus'), $updraftplus->backup_methods[$storage])); ?></a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will setup the HTML template for the instance label setting
|
||||
*
|
||||
* @param String $storage - the name of the remote storage method
|
||||
* @param Object $storage_object - the remote storage object
|
||||
* @return String - the HTML template
|
||||
*/
|
||||
public function config_print_add_instance_label($storage, $storage_object) {
|
||||
$input_class = is_object($storage_object) ? $storage_object->get_css_classes() : "updraftplusmethod ".$storage;
|
||||
$input_name_id_attr = is_object($storage_object) ? $storage_object->output_settings_field_name_and_id('instance_label', true) . ' ' . $storage . '_updraft_instance_label' : '';
|
||||
?>
|
||||
<input type="hidden" class="<?php echo esc_attr($input_class);?>" <?php echo wp_kses($input_name_id_attr, array()); ?> value="{{instance_label}}" />
|
||||
<?php
|
||||
}
|
||||
|
||||
public function savestorage($rinput, $input) {
|
||||
return $input;
|
||||
}
|
||||
|
||||
public function storage_printoptions_multi() {
|
||||
return 'multi';
|
||||
}
|
||||
|
||||
public function storage_printoptions($ret, $active_service) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Filter use
|
||||
global $updraftplus;
|
||||
add_action('admin_print_footer_scripts', array($this, 'admin_print_footer_scripts'));
|
||||
|
||||
?>
|
||||
</div></td></tr>
|
||||
<tr>
|
||||
<th colspan="2"><h2 class="updraft_settings_sectionheading"><?php esc_html_e('Remote Storage Options', 'updraftplus');?></h2>
|
||||
</tr>
|
||||
<tr id="remote_storage_tabs" style="border-bottom: 1px solid #ccc">
|
||||
<td colspan="2" style="padding:0px">
|
||||
<?php
|
||||
foreach ($updraftplus->backup_methods as $method => $description) {
|
||||
?>
|
||||
<a class="<?php echo esc_attr('updraftplus-nav-tab remote-tab updraft-hidden remote-tab-'.$method); ?>" id="<?php echo esc_attr('remote-tab-'.$method); ?>" name="<?php echo esc_attr($method); ?>" href="#" style="display:none"><?php echo esc_html($description);?></a>
|
||||
<?php
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform storages' conditional logic and screens out the storage instance if it is not to be included.
|
||||
*
|
||||
* @param Boolean $include_it - pre-filter value
|
||||
* @param Array $instance_settings - settings for the instance being looked at.
|
||||
* @param String $method_id - method identifier
|
||||
* @param String $instance_id - settings instance identifier
|
||||
* @param Boolean $is_scheduled_backup - whether the backup started is a scheduled one or not
|
||||
*
|
||||
* @return Boolean - filtered value
|
||||
*/
|
||||
public function boot_backup_remote_storage_instance_include($include_it, $instance_settings, $method_id, $instance_id, $is_scheduled_backup) {
|
||||
|
||||
// Don't further process anything if it is already excluded, not a scheduled backup, or if there are no (valid) rules
|
||||
if (!$include_it || !$is_scheduled_backup || empty($instance_settings['instance_conditional_logic']) || empty($instance_settings['instance_conditional_logic']['type']) || empty($instance_settings['instance_conditional_logic']['rules'])) return $include_it;
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$instance_settings = $instance_settings['instance_conditional_logic'];
|
||||
|
||||
// check the logic rules, and proceed when things match the rules
|
||||
|
||||
$current_day_of_the_month = get_date_from_gmt(gmdate('Y-m-d H:i:s'), 'j');
|
||||
$current_day_of_the_week = get_date_from_gmt(gmdate('Y-m-d H:i:s'), 'w');
|
||||
$current_day_of_the_week = "" !== $current_day_of_the_week ? $current_day_of_the_week : '';
|
||||
$result = 'any' === strtolower($instance_settings['type']) ? false : true;
|
||||
|
||||
foreach ((array) $instance_settings['rules'] as $rule) {
|
||||
|
||||
$operand = isset($rule['operand']) ? $rule['operand'] : '';
|
||||
if ('day_of_the_week' === $operand) $value1 = $current_day_of_the_week;
|
||||
if ('day_of_the_month' === $operand) $value1 = $current_day_of_the_month;
|
||||
$operator = isset($rule['operator']) ? $rule['operator'] : '';
|
||||
$value2 = isset($rule['value']) ? $rule['value'] : '';
|
||||
switch (strtolower($instance_settings['type'])) {
|
||||
case 'any':
|
||||
$result = $result || $updraftplus->if_cond((string) $value1, $operator, (string) $value2);
|
||||
if ($result) break 2;
|
||||
break;
|
||||
case 'all':
|
||||
$result = $result && $updraftplus->if_cond((string) $value1, $operator, (string) $value2);
|
||||
if (!$result) break 2;
|
||||
break;
|
||||
case 'default':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$result) {
|
||||
$updraftplus->log("This instance id ($method_id, $instance_id) has backup rules set up, but one or more conditions didn't match.");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
933
wp-content/plugins/updraftplus/addons/multisite.php
Normal file
933
wp-content/plugins/updraftplus/addons/multisite.php
Normal file
@@ -0,0 +1,933 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: multisite:Multisite/Network
|
||||
Description: Makes UpdraftPlus compatible with a WordPress Network (a.k.a. multi-site) and adds Network-related features
|
||||
Version: 3.8
|
||||
Shop: /shop/network-multisite/
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
// Options handling
|
||||
if (!defined('ABSPATH')) die('No direct access allowed');
|
||||
|
||||
if (class_exists('UpdraftPlus_Options')) return;
|
||||
|
||||
if (is_multisite()) {
|
||||
|
||||
class UpdraftPlus_Options {
|
||||
|
||||
/**
|
||||
* The url for UprdraftPlus page
|
||||
*/
|
||||
const PARENT_FILE = 'settings.php?page=updraftplus';
|
||||
|
||||
/**
|
||||
* Whether or not the current user has permission to manage UpdraftPlus
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public static function user_can_manage() {
|
||||
$user_can_manage = is_super_admin();
|
||||
// true: multisite add-on
|
||||
return apply_filters('updraft_user_can_manage', $user_can_manage, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* The suffix for the table to store options in
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static function options_table() {
|
||||
return 'sitemeta';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the last logged message
|
||||
*
|
||||
* @return Mixed - Value set for the option or the default message
|
||||
*/
|
||||
public static function get_updraft_lastmessage() {
|
||||
// Storing this in the single-row option does not combine well with SQL binary logging and frequent updates during a backup process
|
||||
return get_site_option('updraft_lastmessage', __('(Nothing has been logged yet)', 'updraftplus'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value for a specified option
|
||||
*
|
||||
* @param String $option - the option name
|
||||
* @param Mixed $default - the default option value
|
||||
*
|
||||
* @return Mixed
|
||||
*/
|
||||
public static function get_updraft_option($option, $default = false) {
|
||||
if ('updraft_lastmessage' == $option) {
|
||||
// Storing this in the single-row option does not combine well with SQL binary logging and frequent updates during a backup process
|
||||
$ret = get_site_option($option, $default);
|
||||
} else {
|
||||
$tmp = get_site_option('updraftplus_options');
|
||||
$ret = isset($tmp[$option]) ? $tmp[$option] : $default;
|
||||
}
|
||||
return apply_filters('updraftplus_get_option', $ret, $option, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an option. N.B. The $autoload parameter has no effect on a multisite install.
|
||||
*
|
||||
* @param String $option specify option name
|
||||
* @param String $value specify option value
|
||||
* @param Boolean $use_cache whether or not to use the WP options cache
|
||||
* @return Boolean - as from update_site_option()
|
||||
*/
|
||||
public static function update_updraft_option($option, $value, $use_cache = true) {
|
||||
$value = apply_filters('updraftplus_update_option', $value, $option, $use_cache);
|
||||
if ('updraft_lastmessage' == $option) {
|
||||
return update_site_option('updraft_lastmessage', $value);
|
||||
}
|
||||
$tmp = get_site_option('updraftplus_options', array(), $use_cache);
|
||||
if (!is_array($tmp)) $tmp = array();
|
||||
$tmp[$option] = $value;
|
||||
return update_site_option('updraftplus_options', $tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an option
|
||||
*
|
||||
* @param String $option - the option name
|
||||
*/
|
||||
public static function delete_updraft_option($option) {
|
||||
$tmp = get_site_option('updraftplus_options');
|
||||
if (!is_array($tmp)) $tmp = array();
|
||||
if (isset($tmp[$option])) unset($tmp[$option]);
|
||||
update_site_option('updraftplus_options', $tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL of the admin page
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static function admin_page_url() {
|
||||
return network_admin_url('settings.php');
|
||||
}
|
||||
|
||||
public static function admin_page() {
|
||||
return 'settings.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers and removes the UpdraftPlus submenu, keeping it accessible via the main menu.
|
||||
*
|
||||
* The $admin_page_hooks variable is a global associative array in WordPress. It is used internally
|
||||
* by WordPress to keep track of the registered admin pages and their corresponding hooks. Whenever
|
||||
* developers add new admin pages using functions like add_menu_page() or add_submenu_page(),
|
||||
* WordPress automatically adds an entry in this array. The key in the array represents the page slug
|
||||
* (a unique identifier for each admin page), and the corresponding value is the hook assigned to that
|
||||
* page.
|
||||
*
|
||||
* Since we are using the current URL (settings.php?page=updraftplus) as the link for our plugin in
|
||||
* the main admin menu, we need to ensure the page is properly registered and accessible:
|
||||
*
|
||||
* 1. First, we register the submenu item using add_submenu_page(). This step is essential to
|
||||
* register the function responsible for rendering the UpdraftPlus page. Without this, WordPress
|
||||
* would not associate the page with a valid hook or rendering function, making it inaccessible.
|
||||
*
|
||||
* 2. After the submenu is registered, we immediately remove it with remove_submenu_page(). This
|
||||
* prevents the UpdraftPlus page from appearing as a submenu under the Settings menu, avoiding
|
||||
* clutter and keeping the admin interface clean.
|
||||
*
|
||||
* By registering the submenu and then removing it, the page remains accessible through the main
|
||||
* menu link (settings.php?page=updraftplus), while avoiding redundant links in the Settings submenu.
|
||||
*
|
||||
* In a multisite setup, if the WP_ALLOW_MULTISITE constant is not defined or set to false, the
|
||||
* Settings main menu may not have any submenus. This results in the global $submenu array for the
|
||||
* Settings page being empty, which can cause issues with the user_can_access_admin_page() function.
|
||||
* Specifically, this happens because the get_admin_page_parent() function returns empty. To prevent
|
||||
* this, we unset $admin_page_hooks['settings.php'] to ensure users can still access the UpdraftPlus
|
||||
* page, which uses the current URL (settings.php?page=updraftplus) as its URL.
|
||||
*/
|
||||
public static function add_admin_pages() {
|
||||
if (!defined('UPDRAFTPLUS_DISABLE_TOP_LEVEL_MENU_ENTRY') || !UPDRAFTPLUS_DISABLE_TOP_LEVEL_MENU_ENTRY) {
|
||||
if (is_super_admin() || apply_filters('updraft_user_can_manage', false, true)) {
|
||||
$capability = apply_filters('option_page_capability_updraft-options-group', 'manage_options');
|
||||
|
||||
// Add "UpdraftPlus" as the main menu item
|
||||
add_menu_page(
|
||||
'UpdraftPlus',
|
||||
'UpdraftPlus',
|
||||
$capability,
|
||||
'settings.php?page=updraftplus',
|
||||
'', // Set the callback to empty string because it's unused
|
||||
trailingslashit(UPDRAFTPLUS_URL).'images/dashicon-white.png'
|
||||
);
|
||||
|
||||
global $admin_page_hooks, $title;
|
||||
|
||||
if (!defined('WP_ALLOW_MULTISITE') || !WP_ALLOW_MULTISITE) unset($admin_page_hooks['settings.php']);
|
||||
|
||||
if (isset($_GET['page']) && 'updraftplus' == $_GET['page']) $title = 'UpdraftPlus';
|
||||
|
||||
UpdraftPlus_Options::add_submenu();
|
||||
|
||||
remove_submenu_page('settings.php', 'updraftplus');
|
||||
}
|
||||
} else {
|
||||
UpdraftPlus_Options::add_submenu();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a submenu page under the "Settings" menu in the WordPress admin.
|
||||
*
|
||||
* The capability required to access this submenu is filtered through the
|
||||
* 'option_page_capability_updraft-options-group' filter, with a default value of 'manage_options'.
|
||||
*/
|
||||
public static function add_submenu() {
|
||||
$capability = apply_filters('option_page_capability_updraft-options-group', 'manage_options');
|
||||
|
||||
add_submenu_page(
|
||||
'settings.php',
|
||||
'UpdraftPlus',
|
||||
__('UpdraftPlus Backups', 'updraftplus'),
|
||||
$capability,
|
||||
'updraftplus',
|
||||
array('UpdraftPlus_Options', 'options_printpage')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called upon plugin activation
|
||||
*/
|
||||
public static function set_defaults() {
|
||||
$tmp = get_site_option('updraftplus_options');
|
||||
|
||||
if (is_array($tmp)) return;
|
||||
|
||||
$arr = array(
|
||||
'updraft_encryptionphrase' => '',
|
||||
'updraft_service' => '',
|
||||
|
||||
'updraftplus_dismissedautobackup' => 0, // Notice with information about the auto-backup add-on
|
||||
'updraftplus_dismisseddashnotice' => 0, // Notice on the main dashboard for free users who've been using the plugin for a few weeks
|
||||
'dismissed_general_notices_until' => 0, // Notices on the UD dashboard page
|
||||
'dismissed_review_notice' => 0, // Review notice on the UD dashboard page
|
||||
'dismissed_season_notices_until' => 0, // Seasonal notices on the UD dashboard page
|
||||
'dismissed_clone_php_notices_until' => 0, // Clone PHP notice on WP Dashboard
|
||||
'dismissed_clone_wc_notices_until' => 0, // Clone WC notice on WP Plugins page
|
||||
'updraftplus_dismissedexpiry' => 0, // Notice from the updates connector about support/updates expiry
|
||||
|
||||
'updraft_log_syslog' => 0,
|
||||
'updraft_ssl_nossl' => 0,
|
||||
'updraft_ssl_useservercerts' => 0,
|
||||
'updraft_ssl_disableverify' => 0,
|
||||
'updraft_split_every' => 400,
|
||||
|
||||
'updraft_dir' => '',
|
||||
'updraft_report_warningsonly' => array(),
|
||||
'updraft_report_wholebackup' => array(),
|
||||
'updraft_report_dbackup' => array(),
|
||||
|
||||
'updraft_databases' => array(),
|
||||
'updraft_backupdb_nonwp' => 0,
|
||||
|
||||
'updraft_remotesites' => array(),
|
||||
'updraft_migrator_localkeys' => array(),
|
||||
'updraft_central_localkeys' => array(),
|
||||
|
||||
'updraft_autobackup_default' => 0,
|
||||
'updraft_delete_local' => 1,
|
||||
'updraft_debug_mode' => 1,
|
||||
'updraft_include_plugins' => 1,
|
||||
'updraft_include_themes' => 1,
|
||||
'updraft_include_uploads' => 1,
|
||||
'updraft_include_others' => 1,
|
||||
'updraft_include_wpcore' => 0,
|
||||
'updraft_include_wpcore_exclude' => '',
|
||||
'updraft_include_more' => 0,
|
||||
'updraft_include_more_path' => '',
|
||||
'updraft_include_mu-plugins' => 1,
|
||||
'updraft_include_blogs' => 1,
|
||||
'updraft_include_others_exclude' => UPDRAFT_DEFAULT_OTHERS_EXCLUDE,
|
||||
'updraft_include_uploads_exclude' => UPDRAFT_DEFAULT_UPLOADS_EXCLUDE,
|
||||
'updraft_interval' => 'manual',
|
||||
'updraft_interval_increments' => 'none',
|
||||
'updraft_interval_database' => 'manual',
|
||||
'updraft_extradbs' => array(),
|
||||
'updraft_retain' => 1,
|
||||
'updraft_retain_db' => 1,
|
||||
'updraft_retain_extra' => array(),
|
||||
'updraft_starttime_files' => date('H:i', time()+600),
|
||||
'updraft_starttime_db' => date('H:i', time()+600),
|
||||
'updraft_startday_files' => date('w', time()+600),
|
||||
'updraft_startday_db' => date('w', time()+600)
|
||||
);
|
||||
|
||||
global $updraftplus;
|
||||
if (is_a($updraftplus, 'UpdraftPlus')) {
|
||||
$backup_methods = array_keys($updraftplus->backup_methods);
|
||||
|
||||
foreach ($backup_methods as $service) {
|
||||
$arr['updraft_'.$service] = array();
|
||||
}
|
||||
}
|
||||
|
||||
update_site_option('updraftplus_options', $arr);
|
||||
}
|
||||
|
||||
public static function options_form_begin($settings_fields = 'updraft-options-group', $allow_autocomplete = true, $get_params = array(), $classes = '') {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameters are for future use.
|
||||
|
||||
$page = '';
|
||||
if (!empty($get_params)) {
|
||||
$page .= '?';
|
||||
$first_one = true;
|
||||
foreach ($get_params as $k => $v) {
|
||||
if ($first_one) {
|
||||
$first_one = false;
|
||||
} else {
|
||||
$page .= '&';
|
||||
}
|
||||
$page .= urlencode($k).'='.urlencode($v);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$page) $page = '?page=updraftplus';
|
||||
|
||||
echo '<form method="post" action="'.esc_url($page).'"';
|
||||
if ('' != $classes) echo ' class="'.esc_attr($classes).'"';
|
||||
if (!$allow_autocomplete) echo ' autocomplete="off"';
|
||||
echo '>';
|
||||
}
|
||||
|
||||
public static function admin_init() {
|
||||
|
||||
static $already_inited = false;
|
||||
if ($already_inited) return;
|
||||
$already_inited = true;
|
||||
|
||||
global $updraftplus, $pagenow;
|
||||
$updraftplus->plugin_title .= " - ".__('Multisite Install', 'updraftplus');
|
||||
|
||||
if ((!defined('UPDRAFTPLUS_DISABLE_TOP_LEVEL_MENU_ENTRY') || !UPDRAFTPLUS_DISABLE_TOP_LEVEL_MENU_ENTRY) && 'settings.php' == $pagenow && isset($_REQUEST['page']) && 'updraftplus' == substr($_REQUEST['page'], 0, 11)) {
|
||||
add_filter('parent_file', array('UpdraftPlus', 'parent_file'), 99);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the function outputting the HTML for our options page
|
||||
*/
|
||||
public static function options_printpage() {
|
||||
if (!self::user_can_manage()) {
|
||||
wp_die(esc_html__('You do not have sufficient permissions to access this page.'));
|
||||
}
|
||||
|
||||
if (isset($_POST['action']) && 'update' == $_POST['action'] && isset($_POST['updraft_interval'])) {
|
||||
self::mass_options_update($_POST);
|
||||
}
|
||||
|
||||
global $updraftplus_admin;
|
||||
$updraftplus_admin->settings_output();
|
||||
|
||||
}
|
||||
|
||||
public static function mass_options_update($new_options) {
|
||||
|
||||
if (!self::user_can_manage()) wp_die(esc_html__('You do not have permission to access this page.'));
|
||||
|
||||
global $updraftplus, $updraftplus_admin;
|
||||
|
||||
$options = get_site_option('updraftplus_options');
|
||||
|
||||
$backup_methods = array_keys($updraftplus->backup_methods);
|
||||
|
||||
foreach ($new_options as $key => $value) {
|
||||
if ('updraft_include_others_exclude' == $key || 'updraft_include_uploads_exclude' == $key || 'updraft_include_wpcore_exclude' == $key) {
|
||||
$options[$key] = UpdraftPlus_Manipulation_Functions::strip_dirslash($value);
|
||||
} elseif ('updraft_include_more_path' == $key) {
|
||||
$options[$key] = UpdraftPlus_Manipulation_Functions::remove_empties($value);
|
||||
} elseif ('updraft_delete_local' == $key || 'updraft_debug_mode' == $key || (preg_match('/^updraft_include_/', $key))) {
|
||||
// Booleans/numeric
|
||||
$options[$key] = absint($value);
|
||||
} elseif ('updraft_split_every' == $key) {
|
||||
$options[$key] = $updraftplus_admin->optionfilter_split_every($value);
|
||||
} elseif ('updraft_retain' == $key || 'updraft_retain_db' == $key) {
|
||||
$options[$key] = UpdraftPlus_Manipulation_Functions::retain_range($value);
|
||||
} elseif ('updraft_interval' == $key) {
|
||||
$options[$key] = $updraftplus->schedule_backup($value);
|
||||
} elseif ('updraft_interval_database' == $key) {
|
||||
$options[$key] = $updraftplus->schedule_backup_database($value);
|
||||
} elseif ('updraft_interval_increments' == $key) {
|
||||
$options[$key] = $updraftplus->schedule_backup_increments($value);
|
||||
} elseif ('updraft_service' == $key) {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $subkey => $subvalue) {
|
||||
if ('0' == $subvalue) unset($value[$subkey]);
|
||||
}
|
||||
}
|
||||
$value = $updraftplus->just_one($value);
|
||||
if (null === $value) $value = '';
|
||||
$options[$key] = $value;
|
||||
} elseif ('updraft_starttime_files' == $key || 'updraft_starttime_db' == $key) {
|
||||
if (preg_match("/^([0-2]?[0-9]):([0-5][0-9])$/", $value, $matches)) {
|
||||
$options[$key] = sprintf("%02d:%s", $matches[1], $matches[2]);
|
||||
} elseif ('' == $value) {
|
||||
$options[$key] = date('H:i', time()+300);
|
||||
} else {
|
||||
$options[$key] = '00:00';
|
||||
}
|
||||
} elseif ('updraft_startday_files' == $key || 'updraft_startday_db' == $key) {
|
||||
$value=absint($value);
|
||||
if ($value>28) $value=1;
|
||||
$options[$key] = $value;
|
||||
} elseif ('updraft_dir' == $key) {
|
||||
$options[$key] = UpdraftPlus_Manipulation_Functions::prune_updraft_dir_prefix($value);
|
||||
} elseif (preg_match('/^updraft_(.*)$/', $key, $matches) && in_array($matches[1], $backup_methods)) {
|
||||
$options[$key] = call_user_func(array($updraftplus, 'storage_options_filter'), $value, $key);
|
||||
} elseif (preg_match("/^updraft_/", $key)) {
|
||||
$options[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array('updraft_delete_local', 'updraft_debug_mode', 'updraft_include_plugins', 'updraft_include_themes', 'updraft_include_uploads', 'updraft_include_others', 'updraft_include_blogs', 'updraft_include_wpcore', 'updraft_include_more', 'updraft_include_mu-plugins', 'updraft_ssl_useservercerts', 'updraft_ssl_disableverify', 'updraft_ssl_nossl', 'updraft_log_syslog', 'updraft_autobackup_default') as $key) {
|
||||
if (empty($new_options[$key])) $options[$key] = false;
|
||||
}
|
||||
|
||||
if (empty($new_options['updraft_service'])) $options['updraft_service'] = 'none';
|
||||
if (empty($new_options['updraft_email'])) $options['updraft_email'] = '';
|
||||
|
||||
if (empty($new_options['updraft_report_warningsonly'])) $new_options['updraft_report_warningsonly'] = array();
|
||||
if (empty($new_options['updraft_report_wholebackup'])) $new_options['updraft_report_wholebackup'] = array();
|
||||
if (empty($new_options['updraft_report_dbbackup'])) $new_options['updraft_report_dbbackup'] = array();
|
||||
|
||||
$options['updraft_report_warningsonly'] = $updraftplus_admin->return_array($new_options['updraft_report_warningsonly']);
|
||||
$options['updraft_report_wholebackup'] = $updraftplus_admin->return_array($new_options['updraft_report_wholebackup']);
|
||||
$options['updraft_report_dbbackup'] = $updraftplus_admin->return_array($new_options['updraft_report_dbbackup']);
|
||||
|
||||
update_site_option('updraftplus_options', $options);
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
|
||||
register_activation_hook('updraftplus', array('UpdraftPlus_Options', 'set_defaults'));
|
||||
|
||||
add_action('network_admin_menu', array('UpdraftPlus_Options', 'add_admin_pages'));
|
||||
add_action('admin_init', array('UpdraftPlus_Options', 'admin_init'), 15);
|
||||
|
||||
class UpdraftPlusAddOn_MultiSite {
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter('updraft_backupable_file_entities', array($this, 'add_backupable_file_entities'), 10, 2);
|
||||
add_filter('updraft_admin_menu_hook', array($this, 'updraft_admin_menu_hook'));
|
||||
add_action('wp_before_admin_bar_render', array($this, 'add_networkadmin_page'));
|
||||
add_filter('updraftplus_restore_all_downloaded_postscan', array($this, 'restore_all_downloaded_postscan'), 20, 7);
|
||||
add_action('updraftplus_admin_enqueue_scripts', array($this, 'updraftplus_admin_enqueue_scripts'));
|
||||
add_filter('updraft_restore_maintenance_mode', array($this, 'updraftplus_single_site_maintenance_mode'), 10, 4);
|
||||
|
||||
// Actions/filters that need UD to be fully loaded before we can consider adding them
|
||||
add_action('plugins_loaded', array($this, 'plugins_loaded'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WP action plugins_loaded
|
||||
*/
|
||||
public function plugins_loaded() {
|
||||
global $updraftplus;
|
||||
// We don't support restoring specific sites within a multisite until WP 3.5
|
||||
if (is_a($updraftplus, 'UpdraftPlus') && method_exists($updraftplus, 'get_wordpress_version') && version_compare($updraftplus->get_wordpress_version(), '3.5', '>=')) {
|
||||
add_filter('updraftplus_restore_this_table', array($this, 'restore_this_table'), 10, 3);
|
||||
add_filter('updraftplus_restore_this_site', array($this, 'restore_this_site'), 10, 4);
|
||||
add_filter('updraft_backupable_file_entities_on_restore', array($this, 'backupable_file_entities_on_restore'), 10, 3);
|
||||
add_filter('updraft_restore_backup_move_from', array($this, 'restore_backup_move_from'), 10, 4);
|
||||
add_filter('updraft_move_existing_to_old_short_circuit', array($this, 'move_existing_to_old_short_circuit'), 10, 3);
|
||||
add_filter('updraftplus_restore_delete_recursive', array($this, 'restore_delete_recursive'), 10, 3);
|
||||
add_filter('updraft_move_others_preserve_existing', array($this, 'move_others_preserve_existing'), 10, 4);
|
||||
add_filter('updraftplus_restore_move_old_mode', array($this, 'restore_move_old_mode'), 10, 3);
|
||||
add_action('updraftplus_restore_set_table_prefix_multisite_got_new_blog_id', array($this, 'drop_existing_non_wpcore_tables'), 10, 2);
|
||||
}
|
||||
}
|
||||
|
||||
public function restore_move_old_mode($move_old_destination, $type, $restore_options) {
|
||||
$selective_restore_site_id = (is_array($restore_options) && !empty($restore_options['updraft_restore_ms_whichsites']) && $restore_options['updraft_restore_ms_whichsites'] > 0) ? $restore_options['updraft_restore_ms_whichsites'] : false;
|
||||
if ($selective_restore_site_id && ('uploads' == $type && !get_site_option('ms_files_rewriting')) || ('others' == $type && get_site_option('ms_files_rewriting'))) {
|
||||
$move_old_destination = -1;
|
||||
}
|
||||
return $move_old_destination;
|
||||
}
|
||||
|
||||
public function move_others_preserve_existing($preserve_existing, $been_restored, $restore_options, $ud_backup_info) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Filter use for $ud_backup_info
|
||||
$selective_restore_site_id = (is_array($restore_options) && !empty($restore_options['updraft_restore_ms_whichsites']) && $restore_options['updraft_restore_ms_whichsites'] > 0) ? $restore_options['updraft_restore_ms_whichsites'] : false;
|
||||
if ($selective_restore_site_id && Updraft_Restorer::MOVEIN_MAKE_BACKUP_OF_EXISTING == $preserve_existing) {
|
||||
$preserve_existing = Updraft_Restorer::MOVEIN_OVERWRITE_NO_BACKUP;
|
||||
}
|
||||
return $preserve_existing;
|
||||
}
|
||||
|
||||
public function restore_delete_recursive($recurse, $ud_foreign, $restore_options) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
if ($recurse) return $recurse;
|
||||
$selective_restore_site_id = (is_array($restore_options) && !empty($restore_options['updraft_restore_ms_whichsites']) && $restore_options['updraft_restore_ms_whichsites'] > 0) ? $restore_options['updraft_restore_ms_whichsites'] : false;
|
||||
if ($selective_restore_site_id) return true;
|
||||
return $recurse;
|
||||
}
|
||||
|
||||
public function move_existing_to_old_short_circuit($short_circuit, $type, $restore_options) {
|
||||
if ($short_circuit) return $short_circuit;
|
||||
global $updraftplus;
|
||||
$selective_restore_site_id = (is_array($restore_options) && !empty($restore_options['updraft_restore_ms_whichsites']) && $restore_options['updraft_restore_ms_whichsites'] > 0) ? $restore_options['updraft_restore_ms_whichsites'] : false;
|
||||
|
||||
if (!$selective_restore_site_id) return $short_circuit;
|
||||
|
||||
// This filter isn't actually called when type is 'others'
|
||||
if ('uploads' != $type && 'others' != $type) return $short_circuit;
|
||||
if (('uploads' == $type && get_site_option('ms_files_rewriting')) || ('others' == $type && !get_site_option('ms_files_rewriting'))) return $short_circuit;
|
||||
|
||||
if (is_main_network() && is_main_site($selective_restore_site_id)) {
|
||||
if ('uploads' == $type) {
|
||||
$updraftplus->log("This is a selective restore of uploads for the main network and site - will not move existing data out of the way (in consequence of which, your final on-disk state may include files which were not in the backup)");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if ('others' == $type) {
|
||||
$updraftplus->log("This is a selective restore of other wp-content data for a site which is not the main network and site, on a pre-WP-3.5-type install - will not move existing data out of the way (in consequence of which, your final on-disk state may include files which were not in the backup)");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* $move_from is a WP_Filesystem path
|
||||
*
|
||||
* @param string $move_from
|
||||
* @param string $type
|
||||
* @param array $restore_options
|
||||
* @param array $backup_info
|
||||
* @return string
|
||||
*/
|
||||
public function restore_backup_move_from($move_from, $type, $restore_options, $backup_info) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Filter use for $backup_info
|
||||
|
||||
$selective_restore_site_id = (is_array($restore_options) && !empty($restore_options['updraft_restore_ms_whichsites']) && $restore_options['updraft_restore_ms_whichsites'] > 0) ? $restore_options['updraft_restore_ms_whichsites'] : false;
|
||||
|
||||
if (!$selective_restore_site_id || ('uploads' != $type && 'others' != $type) || !is_multisite()) return $move_from;
|
||||
|
||||
// Others is only interesting on pre-WP-3.5 style multisites
|
||||
if ('others' == $type && !get_site_option('ms_files_rewriting')) return $move_from;
|
||||
|
||||
// Uploads is not interesting on pre-WP-3.5 style multisites
|
||||
if ('uploads' == $type && get_site_option('ms_files_rewriting')) return $move_from;
|
||||
|
||||
global $updraftplus, $wp_filesystem;
|
||||
|
||||
if (is_main_network() && is_main_site($selective_restore_site_id)) {
|
||||
// Remove the other stuff from the backup working folder, so that we don't restore it
|
||||
$updraftplus->log_e("Restoring only the site with id=%s: removing other data (if any) from the unpacked backup", 'main');
|
||||
|
||||
if ('uploads' == $type) {
|
||||
@$wp_filesystem->delete($move_from.'/sites', true);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
} elseif ('others' == $type) {
|
||||
@$wp_filesystem->delete($move_from.'/blogs.dir', true);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$updraftplus->log_e("Restoring only the site with id=%s: removing other data (if any) from the unpacked backup", $selective_restore_site_id);
|
||||
|
||||
if ('uploads' == $type) {
|
||||
// Sanity check
|
||||
if (!$wp_filesystem->exists($move_from.'/sites')) {
|
||||
$updraftplus->log("Could not find sites directory: aborting ({$move_from}/sites)");
|
||||
return false;
|
||||
}
|
||||
// Remove stuff not in uploads/sites
|
||||
$potential_del_files = $wp_filesystem->dirlist($move_from, true, false);
|
||||
if (empty($potential_del_files)) $potential_del_files = array();
|
||||
foreach ($potential_del_files as $file => $filestruc) {
|
||||
if (empty($file)) continue;
|
||||
if ('sites' != strtolower($file)) @$wp_filesystem->delete($move_from.'/'.$file, true);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
}
|
||||
// Remove stuff in uploads/sites that does not match our ID
|
||||
$potential_del_files = $wp_filesystem->dirlist($move_from.'/sites', true, false);
|
||||
if (empty($potential_del_files)) $potential_del_files = array();
|
||||
foreach ($potential_del_files as $file => $filestruc) {
|
||||
if (empty($file)) continue;
|
||||
if ($file != $selective_restore_site_id) @$wp_filesystem->delete($move_from.'/sites/'.$file, true);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
}
|
||||
|
||||
return $move_from.'/sites/'.$selective_restore_site_id;
|
||||
|
||||
} elseif ('others' == $type) {
|
||||
// Sanity check
|
||||
if (!$wp_filesystem->exists($move_from.'/blogs.dir')) {
|
||||
$updraftplus->log("Could not find blogs.dir directory: aborting ({$move_from}/blogs.dir)");
|
||||
return false;
|
||||
}
|
||||
// Remove stuff not in wp-content/blogs.dir
|
||||
$potential_del_files = $wp_filesystem->dirlist($move_from, true, false);
|
||||
if (empty($potential_del_files)) $potential_del_files = array();
|
||||
foreach ($potential_del_files as $file => $filestruc) {
|
||||
if (empty($file)) continue;
|
||||
if ('blogs.dir' != strtolower($file)) @$wp_filesystem->delete($move_from.'/'.$file.'/files', true);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
}
|
||||
// Remove stuff in uploads/sites that does not match our ID
|
||||
$potential_del_files = $wp_filesystem->dirlist($move_from.'/blogs.dir', true, false);
|
||||
if (empty($potential_del_files)) $potential_del_files = array();
|
||||
foreach ($potential_del_files as $file => $filestruc) {
|
||||
if (empty($file)) continue;
|
||||
if ($file != $selective_restore_site_id) @$wp_filesystem->delete($move_from.'/blogs.dir/'.$file.'/files', true);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
}
|
||||
|
||||
return $move_from.'/blogs.dir/'.$selective_restore_site_id.'/files';
|
||||
|
||||
}
|
||||
}
|
||||
return $move_from;
|
||||
}
|
||||
|
||||
/**
|
||||
* $backupable_entities is in the 'full info' format
|
||||
*
|
||||
* @param array $backupable_entities [description]
|
||||
* @param array $restore_options [description]
|
||||
* @param array $backup_set [description]
|
||||
* @return array
|
||||
*/
|
||||
public function backupable_file_entities_on_restore($backupable_entities, $restore_options, $backup_set) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Filter use for $backup_set
|
||||
|
||||
if (!is_array($restore_options) || !is_array($backupable_entities) || empty($backupable_entities['uploads']) || empty($restore_options['updraft_restore_ms_whichsites']) || $restore_options['updraft_restore_ms_whichsites'] < 1 || !is_multisite()) return $backupable_entities;
|
||||
|
||||
// User has selected only one specific site to restore
|
||||
switch_to_blog($restore_options['updraft_restore_ms_whichsites']);
|
||||
|
||||
// Get WP to tell us where this particular site's uploads lives
|
||||
|
||||
$wp_upload_dir = wp_upload_dir();
|
||||
|
||||
// Re-populate the data with the new result
|
||||
$backupable_entities['uploads'] = array(
|
||||
'path' => untrailingslashit($wp_upload_dir['basedir']),
|
||||
'description' => __('Uploads', 'updraftplus')
|
||||
);
|
||||
|
||||
restore_current_blog();
|
||||
|
||||
return $backupable_entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* $site_id is >=2 in current implementations (not called otherwise)
|
||||
*
|
||||
* @param Boolean $restore_or_not - whether to restore the site
|
||||
* @param String $site_id
|
||||
* @param String $unprefixed_table_name
|
||||
* @param String $restore_options
|
||||
*
|
||||
* @return Boolean - filtered value
|
||||
*/
|
||||
public function restore_this_site($restore_or_not, $site_id, $unprefixed_table_name, $restore_options) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
|
||||
if (!empty($restore_options['updraft_restore_ms_whichsites']) && -1 != $restore_options['updraft_restore_ms_whichsites']) {
|
||||
|
||||
if ($site_id != $restore_options['updraft_restore_ms_whichsites']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $restore_or_not;
|
||||
|
||||
}
|
||||
|
||||
public function restore_this_table($restore_or_not, $unprefixed_table_name, $restore_options) {
|
||||
|
||||
if (!$restore_or_not) return $restore_or_not;
|
||||
|
||||
if (!empty($restore_options['updraft_restore_ms_whichsites'])) {
|
||||
|
||||
if (-1 == $restore_options['updraft_restore_ms_whichsites']) return true;
|
||||
|
||||
if (is_main_network() && is_main_site($restore_options['updraft_restore_ms_whichsites'])) {
|
||||
|
||||
$dont_restore_on_main = array('site', 'sitemeta', 'users', 'usermeta', 'blogs', 'blog_versions', 'signups', 'registration_log');
|
||||
|
||||
if (in_array($unprefixed_table_name, $dont_restore_on_main)) return false;
|
||||
|
||||
$require_prefix = '';
|
||||
|
||||
} else {
|
||||
$require_prefix = $restore_options['updraft_restore_ms_whichsites'].'_';
|
||||
}
|
||||
|
||||
$has_prefix = preg_match('/^(\d+_).*$/', $unprefixed_table_name, $matches) ? $matches[1] : '';
|
||||
$restore_or_not = ($require_prefix == $has_prefix);
|
||||
}
|
||||
|
||||
return $restore_or_not;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will try to turn on our maintenance mode if a single site in a multisite is being restored, if unsuccessful return true otherwise false
|
||||
*
|
||||
* @param Boolean $filter_value - the filter value being passed in, we return this in the event of an error
|
||||
* @param Boolean $active - boolean to indicate if we are turning maintenance mode off or on
|
||||
* @param Object $updraftplus_restorer - the updraftplus restorer object
|
||||
* @param Object $wp_upgrader - the wordpress upgrader object
|
||||
*
|
||||
* @return Boolean - returns a boolean to indicate if we should turn on WordPress maintenance mode or not
|
||||
*/
|
||||
public function updraftplus_single_site_maintenance_mode($filter_value, $active, $updraftplus_restorer, $wp_upgrader) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wp_upgrader Used in a Filter
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
// Check this is a multisite install and that we are trying to restore a single site
|
||||
if (!is_multisite() || false == $updraftplus_restorer->ud_multisite_selective_restore || 1 >= $updraftplus_restorer->ud_multisite_selective_restore) return $filter_value;
|
||||
|
||||
switch_to_blog($updraftplus_restorer->ud_multisite_selective_restore);
|
||||
|
||||
$wp_upload_dir = wp_upload_dir();
|
||||
$subsite_dir = $wp_upload_dir['basedir'].'/';
|
||||
|
||||
restore_current_blog();
|
||||
|
||||
if ($active) {
|
||||
if (!file_put_contents($subsite_dir.'.maintenance', time())) {
|
||||
$updraftplus->log("Failed to put subsite into maintenance mode, falling back to WordPress default");
|
||||
return $filter_value;
|
||||
}
|
||||
} else {
|
||||
if (file_exists($subsite_dir.'.maintenance')) unlink($subsite_dir.'.maintenance');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called upon the WP action updraftplus_admin_enqueue_scripts
|
||||
*/
|
||||
public function updraftplus_admin_enqueue_scripts() {
|
||||
global $updraftplus;
|
||||
$updraftplus->enqueue_select2();
|
||||
}
|
||||
|
||||
/**
|
||||
* After &$info, &$mess, &$warn, &$err are also available (but we are not using them currently)
|
||||
*
|
||||
* @param array $backups
|
||||
* @param timestamp $timestamp
|
||||
* @param array $entities
|
||||
* @param array $info
|
||||
* @return void
|
||||
*/
|
||||
public function restore_all_downloaded_postscan($backups, $timestamp, $entities, &$info) {
|
||||
global $updraftplus;
|
||||
|
||||
// "Others", because on pre-WP-3.5-style networks, uploads are in wp-content/blogs.dir
|
||||
// $entities is an set of index numbers, which in the common case of 1 zip only, will be '0' - so, don't test with empty()
|
||||
if (!is_array($info) || empty($info['multisite']) || !is_array($entities) || (!isset($entities['db']) && (!isset($entities['uploads']) || get_site_option('ms_files_rewriting')) && (!isset($entities['others']) || !get_site_option('ms_files_rewriting'))) || (isset($info['same_url']) && !$info['same_url'] && !$info['url_scheme_change'])) return;
|
||||
|
||||
// if (!is_array($info) || empty($info['multisite']) || empty($info['same_url'])) return $info;
|
||||
|
||||
// if (!defined('UPDRAFTPLUS_MULTISITE_EXPERIMENTAL_RESTOREONLYONE') || !UPDRAFTPLUS_MULTISITE_EXPERIMENTAL_RESTOREONLYONE) return $info;
|
||||
|
||||
// N.B. "If wp_is_large_network() returns TRUE, wp_get_sites() will return an empty array. By default wp_is_large_network() returns TRUE if there are 10,000 or more sites in your network." - https://codex.wordpress.org/Function_Reference/wp_get_sites (wp_is_large_network() is since WP 3.3)
|
||||
|
||||
$added_header = false;
|
||||
|
||||
// Are there any entities being restored except the potentially per-site ones?
|
||||
$other_entities = $entities;
|
||||
unset($other_entities['db']);
|
||||
unset($other_entities['uploads']);
|
||||
unset($other_entities['others']);
|
||||
|
||||
$wp_version = $updraftplus->get_wordpress_version();
|
||||
$page = 0;
|
||||
$sites = array();
|
||||
while (!$page || (!empty($sites) && 100 == count($sites))) {
|
||||
// N.B. get_current_site() actually gets the current network - the function is named using old terminology
|
||||
$current_network = get_current_site();
|
||||
$params = array('network_id' => $current_network->id, 'offset' => $page * 100, 'limit' => 100);
|
||||
// The wp_get_sites() function is deprecated since WP version 4.6.0!
|
||||
$sites = (function_exists('wp_get_sites') && version_compare($wp_version, '4.6.0', '<')) ? wp_get_sites($params) : $this->wp_get_sites($params);
|
||||
// Check that there's currently >1 site, because otherwise a) there's only one to restore anyway, and b) it may confuse the user if they're thinking they can selectively restore not-currently-existing sites (they can't)
|
||||
if (is_array($sites) && count($sites) > 1) {
|
||||
foreach ($sites as $si) {
|
||||
if (!$added_header) {
|
||||
if (empty($info['addui'])) $info['addui'] = '';
|
||||
$info['addui'] .= '<strong>'.__('Which site to restore', 'updraftplus').':</strong><br>';
|
||||
// Trying to use the 'multiple' attribute here brings into play bugs with Select2 in a jQuery modal
|
||||
$class = (!defined('UPDRAFTPLUS_SELECT2_ENABLE') || UPDRAFTPLUS_SELECT2_ENABLE) ? 'updraft_select2' : '';
|
||||
$info['addui'] .= '<select style="width:100%;" name="updraft_restore_ms_whichsites" class="'.$class.'">';
|
||||
$info['addui'] .= '<option selected="selected" value="-1">'.__('All sites', 'updraftplus').'</option>';
|
||||
$added_header = true;
|
||||
}
|
||||
// Ignoring site_id - that is not supported (i.e. no multi-networks)
|
||||
$info['addui'] .= '<option value="'.$si['blog_id'].'">'.htmlspecialchars($si['domain'].$si['path']);
|
||||
if (isset($entities['db']) && is_main_network() && is_main_site($si['blog_id'])) $info['addui'] .= ' ('.__('may include some site-wide data', 'updraftplus').')';
|
||||
$info['addui'] .= '</option>';
|
||||
}
|
||||
}
|
||||
$page++;
|
||||
}
|
||||
|
||||
if ($added_header) {
|
||||
$info['addui'] .= '</select>';
|
||||
if (!empty($other_entities)) $info['addui'] .= '<em>'.__('N.B. this option only affects the restoration of the database and uploads - other file entities (such as plugins) in WordPress are shared by the whole network.').'</em> <a href="https://updraftplus.com/restoring-single-sites-on-network/" target="_blank">'.__('Read more...', 'updraftplus').'</a>';
|
||||
}
|
||||
// return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function wp_get_sites doesn't exist until WP 3.7. We provide this for earlier versions.
|
||||
*
|
||||
* @param array $args
|
||||
* @return array
|
||||
*/
|
||||
private function wp_get_sites($args = array()) {
|
||||
global $wpdb;
|
||||
|
||||
// Does not exist on the versions we're targeting
|
||||
// if (wp_is_large_network())
|
||||
// return array();
|
||||
|
||||
$defaults = array(
|
||||
'network_id' => $wpdb->siteid,
|
||||
'public' => null,
|
||||
'archived' => null,
|
||||
'mature' => null,
|
||||
'spam' => null,
|
||||
'deleted' => null,
|
||||
'limit' => 100,
|
||||
'offset' => 0,
|
||||
);
|
||||
|
||||
$args = wp_parse_args($args, $defaults);
|
||||
|
||||
$query = "SELECT * FROM $wpdb->blogs WHERE 1=1 ";
|
||||
|
||||
if (isset($args['network_id']) && (is_array($args['network_id']) || is_numeric($args['network_id']))) {
|
||||
$network_ids = implode(',', wp_parse_id_list($args['network_id']));
|
||||
$query .= "AND site_id IN ($network_ids) ";
|
||||
}
|
||||
|
||||
if (isset($args['public']))
|
||||
$query .= $wpdb->prepare("AND public = %d ", $args['public']);
|
||||
|
||||
if (isset($args['archived']))
|
||||
$query .= $wpdb->prepare("AND archived = %d ", $args['archived']);
|
||||
|
||||
if (isset($args['mature']))
|
||||
$query .= $wpdb->prepare("AND mature = %d ", $args['mature']);
|
||||
|
||||
if (isset($args['spam']))
|
||||
$query .= $wpdb->prepare("AND spam = %d ", $args['spam']);
|
||||
|
||||
if (isset($args['deleted']))
|
||||
$query .= $wpdb->prepare("AND deleted = %d ", $args['deleted']);
|
||||
|
||||
if (isset($args['limit']) && $args['limit']) {
|
||||
if (isset($args['offset']) && $args['offset']) {
|
||||
$query .= $wpdb->prepare("LIMIT %d , %d ", $args['offset'], $args['limit']);
|
||||
} else {
|
||||
$query .= $wpdb->prepare("LIMIT %d ", $args['limit']);
|
||||
}
|
||||
}
|
||||
|
||||
$site_results = $wpdb->get_results($query, ARRAY_A);
|
||||
|
||||
return $site_results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds backupable file entities to an array based on specified conditions.
|
||||
*
|
||||
* This function is used to add information about backupable file entities to an array.
|
||||
* It considers whether the WordPress installation is using multisite and the value of the 'ms_files_rewriting' option.
|
||||
* If multisite is detected and 'ms_files_rewriting' is not enabled, it includes information about the blog uploads directory in the array.
|
||||
*
|
||||
* @param array $arr The array to which backupable file entities information will be added.
|
||||
* @param bool $full_info Whether to include detailed information about the backupable file entities or just their paths.
|
||||
*
|
||||
* @return array The modified array with backupable file entities information added.
|
||||
*/
|
||||
public function add_backupable_file_entities($arr, $full_info) {
|
||||
// Post-3.5, WordPress multisite puts uploads from blogs by default into the uploads directory (i.e. no separate location). This is indicated not by the WP version number, but by the option ms_files_rewriting (which won't exist pre-3.5). See wp_upload_dir()
|
||||
// This is a compatible way of getting the current blog's upload directory. Because of our access setup, that always resolves to the site owner's upload directory
|
||||
global $updraftplus;
|
||||
if ($full_info) {
|
||||
if (!get_option('ms_files_rewriting') && defined('UPLOADBLOGSDIR')) {
|
||||
$ud = $updraftplus->wp_upload_dir();
|
||||
if (strpos(UPLOADBLOGSDIR, false === $ud['basedir'])) {
|
||||
$arr['blogs'] = array(
|
||||
'path' => ABSPATH.UPLOADBLOGSDIR,
|
||||
'description' => __('Blog uploads', 'updraftplus')
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!get_option('ms_files_rewriting') && defined('UPLOADBLOGSDIR')) {
|
||||
$ud = $updraftplus->wp_upload_dir();
|
||||
if (strpos(UPLOADBLOGSDIR, false === $ud['basedir'])) {
|
||||
$arr['blogs'] = ABSPATH.UPLOADBLOGSDIR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP filter updraft_admin_menu_hook
|
||||
*
|
||||
* @param String $h - existing action to use for the menu hook
|
||||
*
|
||||
* @return String - filtered value
|
||||
*/
|
||||
public function updraft_admin_menu_hook() {
|
||||
return 'network_admin_menu';
|
||||
}
|
||||
|
||||
public function add_networkadmin_page() {
|
||||
global $wp_admin_bar;
|
||||
|
||||
if (!is_object($wp_admin_bar) || !is_super_admin() || !function_exists('is_admin_bar_showing') || !is_admin_bar_showing()) {
|
||||
if (!apply_filters('updraft_user_can_manage', false, true)) return;
|
||||
}
|
||||
|
||||
$wp_admin_bar->add_node(array(
|
||||
'parent' => 'network-admin',
|
||||
'id' => 'updraftplus-admin-settings',
|
||||
'title' => __('UpdraftPlus Backups', 'updraftplus'),
|
||||
'href' => UpdraftPlus_Options::admin_page_url().'?page=updraftplus'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform deletion of non WP core tables for the given Blog ID
|
||||
*
|
||||
* @param Integer $blog_id Site Blog ID
|
||||
* @param String $table_prefix System table prefix
|
||||
*/
|
||||
public function drop_existing_non_wpcore_tables($blog_id, $table_prefix) {
|
||||
if (!$blog_id) return;
|
||||
global $wpdb, $updraftplus;
|
||||
$tables = array();
|
||||
$blog_tables = $wpdb->tables('blog', true, (int) $blog_id);
|
||||
foreach ($wpdb->get_results("SHOW TABLES LIKE '".$table_prefix.$blog_id."_%'") as $table) {
|
||||
$tables = array_merge($tables, array_values(get_object_vars($table)));
|
||||
}
|
||||
$tables = array_unique($tables);
|
||||
$tables = array_diff($tables, $blog_tables);
|
||||
$suppress = $wpdb->suppress_errors();
|
||||
foreach ($tables as $table) {
|
||||
if (!$wpdb->query('DROP TABLE IF EXISTS '.UpdraftPlus_Manipulation_Functions::backquote(str_replace('`', '``', $table))) && !empty($wpdb->last_error)) {
|
||||
$updraftplus->log(__METHOD__.' : '.$wpdb->last_error.' - '.$wpdb->last_query);
|
||||
}
|
||||
}
|
||||
$wpdb->suppress_errors($suppress);
|
||||
}
|
||||
}
|
||||
|
||||
new UpdraftPlusAddOn_MultiSite;
|
||||
|
||||
}
|
||||
13
wp-content/plugins/updraftplus/addons/noadverts.php
Normal file
13
wp-content/plugins/updraftplus/addons/noadverts.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: noadverts:No Adverts
|
||||
Description: Removes all advertising from the UpdraftPlus settings and emails
|
||||
Version: 1.1
|
||||
Shop: /shop/no-adverts/
|
||||
Latest Change: 1.9.19
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
define('UPDRAFTPLUS_NOADS_B', true);
|
||||
1396
wp-content/plugins/updraftplus/addons/onedrive.php
Normal file
1396
wp-content/plugins/updraftplus/addons/onedrive.php
Normal file
File diff suppressed because it is too large
Load Diff
795
wp-content/plugins/updraftplus/addons/pcloud.php
Normal file
795
wp-content/plugins/updraftplus/addons/pcloud.php
Normal file
@@ -0,0 +1,795 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: pcloud:pCloud Support
|
||||
Description: pCloud Support
|
||||
Version: 1.0
|
||||
Shop: /shop/pcloud/
|
||||
Include: includes/pcloud
|
||||
IncludePHP: methods/backup-module.php
|
||||
Latest Change: 1.22.23
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
if (!class_exists('UpdraftPlus_BackupModule')) updraft_try_include_file('methods/backup-module.php', 'require_once');
|
||||
|
||||
/**
|
||||
* pCloud Backup module class
|
||||
*/
|
||||
class UpdraftPlus_Addons_RemoteStorage_pcloud extends UpdraftPlus_BackupModule {
|
||||
|
||||
private $client_id = '';
|
||||
|
||||
private $callback_url = '';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->client_id = defined('UPDRAFTPLUS_PCLOUD_CLIENT_ID') ? UPDRAFTPLUS_PCLOUD_CLIENT_ID : 'zrkDNwnlAGj';
|
||||
$this->callback_url = defined('UPDRAFTPLUS_PCLOUD_CALLBACK_URL') ? UPDRAFTPLUS_PCLOUD_CALLBACK_URL : 'https://auth.updraftplus.com/auth/pcloud';
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported features.
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
// These options format is handled via only accessing options via $this->get_options().
|
||||
return array(
|
||||
'multi_options',
|
||||
'config_templates',
|
||||
'multi_storage',
|
||||
'conditional_logic',
|
||||
'manual_authentication',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default options
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function get_default_options() {
|
||||
return array(
|
||||
'pclauth' => '',
|
||||
'pcllocation' => '1',
|
||||
'folderid' => 0,
|
||||
'uploadid' => 0,
|
||||
'folder' => ''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether options have been set up by the user, or not
|
||||
*
|
||||
* @param Array $opts - the potential options.
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function options_exist($opts) {
|
||||
if (is_array($opts) && !empty($opts['pclauth'])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts as a WordPress options filter
|
||||
*
|
||||
* @param Array $pcloud - An array of pCloud options.
|
||||
*
|
||||
* @return Array - the returned array can either be the set of updated pCloud settings or a WordPress error array
|
||||
*/
|
||||
public function options_filter($pcloud) {
|
||||
|
||||
$opts = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('pcloud');
|
||||
|
||||
if (is_wp_error($opts)) {
|
||||
if ('recursion' !== $opts->get_error_code()) {
|
||||
$msg = "(".$opts->get_error_code()."): ".$opts->get_error_message();
|
||||
$this->log($msg);
|
||||
error_log("UpdraftPlus: pCloud: $msg");
|
||||
}
|
||||
// The saved options had a problem; so, return the new ones
|
||||
return $pcloud;
|
||||
}
|
||||
|
||||
if (!is_array($pcloud)) return $opts;
|
||||
|
||||
// Handle only version field being saved in the plugin free version.
|
||||
if (!isset($opts['settings'])) return $pcloud;
|
||||
|
||||
// Remove instances that no longer exist
|
||||
foreach ($opts['settings'] as $instance_id => $storage_options) {
|
||||
if (!isset($pcloud['settings'][$instance_id])) unset($opts['settings'][$instance_id]);
|
||||
}
|
||||
|
||||
if (empty($pcloud['settings'])) return $opts;
|
||||
|
||||
foreach ($pcloud['settings'] as $instance_id => $storage_options) {
|
||||
// Now loop over the new options, and replace old options with them
|
||||
foreach ($storage_options as $key => $value) {
|
||||
// Reset "folderid" if folder name is changed
|
||||
if ('folder' == $key && isset($opts['settings'][$instance_id][$key]) && $opts['settings'][$instance_id][$key] != $value) $opts['settings'][$instance_id]['folderid'] = 0;
|
||||
|
||||
if (null === $value) {
|
||||
unset($opts['settings'][$instance_id][$key]);
|
||||
} else {
|
||||
if (!isset($opts['settings'][$instance_id])) $opts['settings'][$instance_id] = array();
|
||||
$opts['settings'][$instance_id][$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proceed with the backup
|
||||
*
|
||||
* @param Array $backup_array - Array of files to be backed up.
|
||||
*
|
||||
* @return false|void|null
|
||||
*/
|
||||
public function backup($backup_array) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
try {
|
||||
$pcloud = $this->bootstrap();
|
||||
$info = $pcloud->account_info();
|
||||
|
||||
if (is_wp_error($info)) {
|
||||
$this->log('pCloud ('.$info->get_error_code().'): '.$info->get_error_message(), 'error');
|
||||
$space_available = 0;
|
||||
} else {
|
||||
$space_available = $info['quota'] - $info['usedquota'];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->log('Exception ('.get_class($e).') when trying to backup: ' . $e->getMessage() . ' (line: ' . $e->getLine() . ', file: ' . $e->getFile() . ')');
|
||||
$this->log(sprintf(__('error: %s (see log file for more)', 'updraftplus'), $e->getMessage()), 'error');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$updraft_dir = $updraftplus->backups_dir_location();
|
||||
$opts = $this->get_options();
|
||||
|
||||
foreach ($backup_array as $file) {
|
||||
|
||||
$hash = md5($file);
|
||||
$file_success = false;
|
||||
|
||||
if (!file_exists($updraft_dir . '/' . $file)) {
|
||||
$file_success = true;
|
||||
}
|
||||
|
||||
$filesize = filesize($updraft_dir . '/' . $file);
|
||||
$microtime = microtime(true);
|
||||
$pcl_upload_id = $this->jobdata_get('upload_pclid_' . $hash, 'None');
|
||||
|
||||
if ('None' === $pcl_upload_id) {
|
||||
|
||||
$upload = $pcloud->create_upload();
|
||||
|
||||
if (is_wp_error($upload)) {
|
||||
$this->log('pCloud ('.$upload->get_error_code().'): '.$upload->get_error_message(), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$pcl_upload_id = $upload['uploadid'];
|
||||
|
||||
$this->jobdata_set('upload_pclid_' . $hash, $pcl_upload_id);
|
||||
} else {
|
||||
$pcl_upload_id = intval($pcl_upload_id);
|
||||
}
|
||||
|
||||
if ('None' !== $this->jobdata_get('upload_id_' . $hash, 'None')) {
|
||||
// Resume.
|
||||
$offset = $this->jobdata_get('upload_offset_' . $hash, 0);
|
||||
if ($offset) {
|
||||
$this->log("This is a resumption: $offset bytes had already been uploaded");
|
||||
}
|
||||
|
||||
$offset = intval($offset);
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
// We don't actually abort now - there's no harm in letting it try and then fail.
|
||||
if ($space_available < ($filesize - $offset)) {
|
||||
$this->log('File upload expected to fail: file data remaining to upload ($file) size is ' . (($filesize - $offset) / 1024) . ' Kb (overall file size; .' . $filesize . " b), whereas available quota is only $space_available b");
|
||||
}
|
||||
|
||||
$ufile = $file;
|
||||
|
||||
$this->log("Attempt to upload: $file to: $ufile");
|
||||
|
||||
$upload_tick = microtime(true);
|
||||
|
||||
$retries = 0;
|
||||
|
||||
if (false === $file_success) {
|
||||
|
||||
while (true) {
|
||||
|
||||
$prev_offset = $offset;
|
||||
|
||||
try {
|
||||
$new_offset = $pcloud->chunked_upload($updraft_dir . '/' . $file, $pcl_upload_id, $offset);
|
||||
|
||||
if (is_wp_error($new_offset)) {
|
||||
throw new Exception($new_offset->get_error_message());
|
||||
}
|
||||
|
||||
$offset = $new_offset;
|
||||
|
||||
if ($prev_offset === $offset) { // Failed, will retry.
|
||||
$retries++;
|
||||
}
|
||||
if (-2 === $offset) { // Success.
|
||||
$file_success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
$this->jobdata_set('upload_offset_' . $hash, $offset);
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
$this->log('chunked upload exception (' . get_class($e) . '): ' . $e->getMessage() . ' (line: ' . $e->getLine() . ', file: ' . $e->getFile() . ')');
|
||||
|
||||
if ($upload_tick > 0 && time() - $upload_tick > 800) {
|
||||
|
||||
UpdraftPlus_Job_Scheduler::reschedule(60);
|
||||
|
||||
$this->log('Select/poll returned after a long time: scheduling a resumption and terminating for now');
|
||||
UpdraftPlus_Job_Scheduler::record_still_alive();
|
||||
|
||||
$result = $pcloud->save($pcl_upload_id, $updraft_dir . '/' . $file, $opts['folderid']);
|
||||
if (is_wp_error($result)) $this->log('pCloud ('.$result->get_error_code().'): '.$result->get_error_message(), 'error');
|
||||
|
||||
die;
|
||||
}
|
||||
|
||||
$retries++;
|
||||
}
|
||||
|
||||
if (5 < $retries) {
|
||||
$this->log('chunked upload failed: too many failures.');
|
||||
$this->log(__('Chunked upload failed', 'updraftplus'), 'error');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($file_success) {
|
||||
|
||||
$updraftplus->uploaded_file($file);
|
||||
$microtime_elapsed = microtime(true) - $microtime;
|
||||
$speedps = ($microtime_elapsed > 0) ? $filesize / $microtime_elapsed : 0;
|
||||
$speed = sprintf('%.2d', ($filesize / 1024)) . ' KB in ' . sprintf('%.2d', $microtime_elapsed) . 's (' . sprintf('%.2d', $speedps) . ' KB/s)';
|
||||
|
||||
$this->log('File upload success (' . $file . "): $speed");
|
||||
$this->jobdata_delete('upload_id_' . $hash);
|
||||
$this->jobdata_delete('upload_pclid_' . $hash);
|
||||
$this->jobdata_delete('upload_offset_' . $hash);
|
||||
|
||||
$result = $pcloud->save($pcl_upload_id, $updraft_dir . '/' . $file, $opts['folderid']);
|
||||
if (is_wp_error($result)) $this->log('pCloud ('.$result->get_error_code().'): '.$result->get_error_message(), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets a list of files from the remote storage that match the string passed in and returns an array of backups
|
||||
*
|
||||
* @param String $match a substring to require (tested via strpos() !== false).
|
||||
*
|
||||
* @return Array|WP_Error
|
||||
*/
|
||||
public function listfiles($match = 'backup_') {
|
||||
|
||||
try {
|
||||
$opts = $this->get_options();
|
||||
if (!$this->options_exist($opts)) return new WP_Error('no_settings', sprintf(__('No %s settings were found', 'updraftplus'), 'pCloud'));
|
||||
$pcloud = $this->bootstrap();
|
||||
} catch (Exception $e) {
|
||||
$this->log($e->getMessage() . ' (line: ' . $e->getLine() . ', file: ' . $e->getFile() . ')');
|
||||
$this->log(__('Listing the files failed:', 'updraftplus').' '.$e->getMessage(), 'warning');
|
||||
return new WP_Error('listfiles', $e->getMessage());
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
$backups = $pcloud->list_backups();
|
||||
if (is_wp_error($backups)) {
|
||||
$this->log('pCloud ('.$backups->get_error_code().'): '.$backups->get_error_message(), 'error');
|
||||
return $backups;
|
||||
}
|
||||
foreach ($backups as $backup) {
|
||||
$regex = str_replace('/', '\/', $match);
|
||||
if (empty($match) || preg_match('/' . $regex . '/', $backup['path'])) {
|
||||
$results[] = array(
|
||||
'name' => $backup['name'],
|
||||
'size' => $backup['size'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete files from the service using the pCloud API
|
||||
*
|
||||
* @param Array $files - array of filenames to delete.
|
||||
*
|
||||
* @return Boolean|String - either a boolean true or an error code string
|
||||
*/
|
||||
public function delete($files) {
|
||||
|
||||
if (is_string($files)) {
|
||||
$files = array($files);
|
||||
}
|
||||
|
||||
try {
|
||||
$pcloud = $this->bootstrap();
|
||||
} catch (Exception $e) {
|
||||
|
||||
$this->log($e->getMessage() . ' (line: ' . $e->getLine() . ', file: ' . $e->getFile() . ')');
|
||||
$this->log(sprintf(__('Failed to access %s when deleting (see log file for more)', 'updraftplus'), 'pCloud'), 'warning');
|
||||
|
||||
return 'service_unavailable';
|
||||
}
|
||||
|
||||
$any_failures = false;
|
||||
|
||||
foreach ($files as $file) {
|
||||
|
||||
$fullpath = '/' . $pcloud->get_backup_dir() . '/' . $file;
|
||||
|
||||
$this->log("request deletion: $file");
|
||||
|
||||
$result = $pcloud->delete($fullpath);
|
||||
if (is_wp_error($result)) {
|
||||
$this->log('pCloud ('.$result->get_error_code().'): '.$result->get_error_message(), 'error');
|
||||
} else {
|
||||
$file_success = 1;
|
||||
}
|
||||
|
||||
if (!isset($file_success)) $any_failures = true;
|
||||
}
|
||||
|
||||
return $any_failures ? 'file_delete_error' : true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Download method
|
||||
*
|
||||
* @param string $file File to be downloaded.
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
public function download($file) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$opts = $this->get_options();
|
||||
$pclauth = !empty($opts['pclauth']) ? $opts['pclauth'] : '';
|
||||
|
||||
if (20 > strlen($pclauth)) {
|
||||
|
||||
$this->log('You are not authenticated with pCloud');
|
||||
$this->log(__('You are not authenticated with pCloud', 'updraftplus'), 'error');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$pcloud = $this->bootstrap();
|
||||
} catch (Exception $e) {
|
||||
|
||||
$this->log($e->getMessage() . ' (line: ' . $e->getLine() . ', file: ' . $e->getFile() . ')');
|
||||
$this->log($e->getMessage() . ' (line: ' . $e->getLine() . ', file: ' . $e->getFile() . ')', 'error');
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!$pcloud) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$remote_files = $this->listfiles($file);
|
||||
|
||||
foreach ($remote_files as $file_info) {
|
||||
if (basename($file_info['name']) === basename($file)) {
|
||||
|
||||
$fname = basename($file_info['name']);
|
||||
|
||||
return $updraftplus->chunked_download($fname, $this, $file_info['size'], apply_filters('updraftplus_pcloud_downloads_manually_break_up', false), null, 2 * 1048576);
|
||||
}
|
||||
}
|
||||
|
||||
$this->log("$file: file not found in listing of remote directory");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used by chunked downloading API
|
||||
*
|
||||
* @param string $file - the file (basename) to be downloaded.
|
||||
* @param array $headers - supplied headers.
|
||||
* @param mixed $data - pass-back from our call to the API (which we don't use).
|
||||
* @param resource $fh - the local file handle.
|
||||
*
|
||||
* @return bool - the data downloaded
|
||||
*/
|
||||
public function chunked_download($file, $headers, $data, $fh) {
|
||||
|
||||
try {
|
||||
$pcloud = $this->bootstrap();
|
||||
$needed_file = $pcloud->get_file_info(basename($file));
|
||||
|
||||
if (is_wp_error($needed_file)) {
|
||||
$this->log('pCloud ('.$needed_file->get_error_code().'): '.$needed_file->get_error_message(), 'error');
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
|
||||
$this->log($e->getMessage() . ' (line: ' . $e->getLine() . ', file: ' . $e->getFile() . ')');
|
||||
$this->log(sprintf(__('Failed to access %s when deleting (see log file for more)', 'updraftplus'), 'pCloud'), 'warning');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count($needed_file) < 2 || !isset($needed_file['fileid'])) {
|
||||
$this->log('The requested file is no longer in the pCloud backup folder.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$offset = 0;
|
||||
$retries = 0;
|
||||
|
||||
while (true) {
|
||||
|
||||
try {
|
||||
$offset = $pcloud->download($needed_file['fileid'], $fh, $headers, $offset);
|
||||
|
||||
if (is_wp_error($offset)) throw new Exception($offset->get_error_message());
|
||||
|
||||
if ($offset >= ($needed_file['size'] - 1)) {
|
||||
fclose($fh);
|
||||
$get = true;
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
|
||||
$this->log($e);
|
||||
$this->log($e->getMessage(), 'error');
|
||||
$get = false;
|
||||
|
||||
$retries++;
|
||||
if (40 < $retries) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Sometimes the server can not deliver the file content for some many reasons, we can wait a little and try again.
|
||||
sleep(2);
|
||||
}
|
||||
}
|
||||
return $get;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre configuration template
|
||||
*
|
||||
* @return String - the template
|
||||
*/
|
||||
public function get_pre_configuration_template() {
|
||||
?>
|
||||
<tr class="{{get_template_css_classes false}} {{method_id}}_pre_config_container">
|
||||
<td colspan="2">
|
||||
<img alt="{{storage_image_title}}" src="{{storage_image_url}}" width="250px">
|
||||
<br>
|
||||
<p>
|
||||
{{{storage_long_description}}}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
ob_start();
|
||||
?>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th><?php esc_html_e('Store at', 'updraftplus');?>:</th>
|
||||
<td>
|
||||
{{folder_path}}<input type="text" style="width: 292px" id="{{get_template_input_attribute_value "id" "folder"}}" name="{{get_template_input_attribute_value "name" "folder"}}" value="{{folder}}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{authentication_label}}:</th>
|
||||
<td>
|
||||
{{#if is_authenticated}}
|
||||
<p>
|
||||
<strong>{{already_authenticated_label}}</strong>
|
||||
<a class="updraft_deauthlink" href="{{admin_page_url}}?action=updraftmethod-{{method_id}}-auth&page=updraftplus&updraftplus_{{method_id}}auth=deauth&nonce={{deauthentication_nonce}}&updraftplus_instance={{instance_id}}" data-instance_id="{{instance_id}}" data-remote_method="{{method_id}}">{{deauthentication_link_text}}</a>
|
||||
</p>
|
||||
{{/if}}
|
||||
{{#if ownername_sentence}}
|
||||
<br>
|
||||
{{ownername_sentence}}
|
||||
{{/if}}
|
||||
<p><a class="updraft_authlink" href="{{admin_page_url}}?&action=updraftmethod-{{method_id}}-auth&page=updraftplus&updraftplus_{{method_id}}auth=doit&nonce={{storage_auth_nonce}}&updraftplus_instance={{instance_id}}" data-instance_id="{{instance_id}}" data-remote_method="{{method_id}}">{{{authentication_link_text}}}</a></p>
|
||||
</td>
|
||||
<input type="hidden" id="{{get_template_input_attribute_value "id" "pcllocation"}}" name="{{get_template_input_attribute_value "name" "pcllocation"}}" value="{{pcllocation}}">
|
||||
</tr>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of template properties by taking all the persistent variables and methods of the parent class and combining them with the ones that are unique to this module, also the necessary HTML element attributes and texts which are also unique only to this backup module
|
||||
* NOTE: Please sanitise all strings that are required to be shown as HTML content on the frontend side (i.e. wp_kses()), or any other technique to prevent XSS attacks that could come via WP hooks
|
||||
*
|
||||
* @return Array an associative array keyed by names that describe themselves as they are
|
||||
*/
|
||||
public function get_template_properties() {
|
||||
global $updraftplus;
|
||||
$properties = array(
|
||||
'storage_image_url' => UPDRAFTPLUS_URL.'/images/pcloud-logo.png',
|
||||
'storage_image_title' => __(sprintf(__('%s logo', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()])),
|
||||
'storage_long_description' => wp_kses(sprintf(__('Please read %s for use of our %s authorization app (none of your backup data is sent to us).', 'updraftplus'), '<a target="_blank" href="https://teamupdraft.com/privacy?utm_source=udp-plugin&utm_medium=referral&utm_campaign=paac&utm_content=pcloud-privacy&utm_creative_format=text">'.__('this privacy policy', 'updraftplus').'</a>', $updraftplus->backup_methods[$this->get_id()]), $this->allowed_html_for_content_sanitisation()),
|
||||
'authentication_label' => sprintf(__('Authenticate with %s', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]),
|
||||
'already_authenticated_label' => __('(You are already authenticated).', 'updraftplus'),
|
||||
'deauthentication_link_text' => sprintf(__("Follow this link to remove these settings for %s.", 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]),
|
||||
'authentication_link_text' => wp_kses(sprintf(__("<strong>After</strong> you have saved your settings (by clicking 'Save Changes' below), then come back here and follow this link to complete authentication with %s.", 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]), $this->allowed_html_for_content_sanitisation()),
|
||||
'deauthentication_nonce' => wp_create_nonce($this->get_id().'_deauth_nonce'),
|
||||
);
|
||||
return wp_parse_args($properties, $this->get_persistent_variables_and_methods());
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options
|
||||
*
|
||||
* @param array $opts
|
||||
* @return Array - Modified handerbar template options
|
||||
*/
|
||||
public function transform_options_for_template($opts) {
|
||||
if (!empty($opts['pclauth'])) {
|
||||
$opts['ownername'] = empty($opts['ownername']) ? '' : $opts['ownername'];
|
||||
if ($opts['ownername']) {
|
||||
$opts['ownername_sentence'] = sprintf(__("Account holder's name: %s.", 'updraftplus'), $opts['ownername']).' ';
|
||||
}
|
||||
$opts['is_authenticated'] = true;
|
||||
}
|
||||
$opts['folder_path'] = apply_filters('updraftplus_pcloud_backup_dir', 'UpdraftPlus').'/';
|
||||
$opts = apply_filters("updraftplus_options_pcloud_options", $opts);
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives settings keys which values should not passed to handlebarsjs context.
|
||||
* The settings stored in UD in the database sometimes also include internal information that it would be best not to send to the front-end (so that it can't be stolen by a man-in-the-middle attacker)
|
||||
*
|
||||
* @return Array - Settings array keys which should be filtered
|
||||
*/
|
||||
public function filter_frontend_settings_keys() {
|
||||
return array(
|
||||
'ownername',
|
||||
'pclauth',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Over-rides the parent to allow this method to output extra information about using the correct account for OAuth authentication
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
public function output_account_warning() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles various URL actions, as indicated by the updraftplus_pcloudauth URL parameter
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function action_auth() {
|
||||
if (isset($_GET['updraftplus_pcloudauth'])) {
|
||||
if ('doit' == stripslashes($_GET['updraftplus_pcloudauth'])) {
|
||||
$this->action_authenticate_storage();
|
||||
return;
|
||||
} elseif ('deauth' == stripslashes($_GET['updraftplus_pcloudauth'])) {
|
||||
$this->action_deauthenticate_storage();
|
||||
return;
|
||||
}
|
||||
} elseif (isset($_REQUEST['state'])) {
|
||||
|
||||
$parts = explode(':', stripslashes($_GET['state']));
|
||||
$state = $parts[0];
|
||||
|
||||
if ('success' == $state) {
|
||||
$raw_state = stripslashes($_GET['state']);
|
||||
if (isset($_GET['code'])) $raw_code = urldecode(stripslashes($_GET['code']));
|
||||
|
||||
$this->do_complete_authentication($raw_state, $raw_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire single-use authorization code from pCloud via OAuth 2.0
|
||||
*
|
||||
* @param String $instance_id - the instance id of the settings we want to authenticate
|
||||
*/
|
||||
public function do_authenticate_storage($instance_id) {
|
||||
$opts = $this->get_options();
|
||||
|
||||
// Set a flag so we know this authentication is in progress
|
||||
$opts['auth_in_progress'] = true;
|
||||
$this->set_options($opts, true);
|
||||
|
||||
$prefixed_instance_id = ':' . $instance_id;
|
||||
$token = 'token'.$prefixed_instance_id.UpdraftPlus_Options::admin_page_url().'?action=updraftmethod-pcloud-auth';
|
||||
|
||||
$params = array(
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->client_id,
|
||||
'redirect_uri' => $this->callback_url,
|
||||
'state' => $token,
|
||||
'access_type' => 'offline',
|
||||
'force_reapprove' => 'true',
|
||||
'returnqueryparams' => 1
|
||||
);
|
||||
|
||||
if (headers_sent()) {
|
||||
$this->log(sprintf(__('The %s authentication could not go ahead, because something else on your site is breaking it.', 'updraftplus'), 'pCloud').' '.__('Try disabling your other plugins and switching to a default theme.', 'updraftplus').' ('.__('Specifically, you are looking for the component that sends output (most likely PHP warnings/errors) before the page begins.', 'updraftplus').' '.__('Turning off any debugging settings may also help).', 'updraftplus').')', 'error');
|
||||
} else {
|
||||
header('Location: https://my.pcloud.com/oauth2/authorize?'.http_build_query($params, '', '&'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will complete the oAuth flow, if return_instead_of_echo is true then add the action to display the authed admin notice, otherwise echo this notice to page.
|
||||
*
|
||||
* @param string $state - the state
|
||||
* @param string $code - the oauth code
|
||||
* @param boolean $return_instead_of_echo - a boolean to indicate if we should return the result or echo it
|
||||
*
|
||||
* @return void|string - returns the authentication message if return_instead_of_echo is true
|
||||
*/
|
||||
public function do_complete_authentication($state, $code, $return_instead_of_echo = false) {
|
||||
|
||||
$code = json_decode(base64_decode($code), true);
|
||||
|
||||
if (!is_bool($code) && isset($code['access_token']) && 30 < strlen($code['access_token']) && isset($code['locationid'])) {
|
||||
$opts = $this->get_options();
|
||||
$opts['pclauth'] = $code['access_token'];
|
||||
$opts['pcllocation'] = $code['locationid'];
|
||||
// remove our flag so we know this authentication is complete
|
||||
if (isset($opts['auth_in_progress'])) unset($opts['auth_in_progress']);
|
||||
$this->set_options($opts, true);
|
||||
}
|
||||
|
||||
if ($return_instead_of_echo) {
|
||||
return $this->show_authed_admin_success($return_instead_of_echo);
|
||||
} else {
|
||||
add_action('all_admin_notices', array($this, 'show_authed_admin_success'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will setup the authenticated admin notice, it can either return this or echo it
|
||||
*
|
||||
* @param boolean $return_instead_of_echo - a boolean to indicate if we should return the result or echo it
|
||||
*
|
||||
* @return void|string - returns the authentication message if return_instead_of_echo is true
|
||||
*/
|
||||
public function show_authed_admin_success($return_instead_of_echo) {
|
||||
global $updraftplus_admin;
|
||||
|
||||
try {
|
||||
$pcloud = $this->bootstrap();
|
||||
$info = $pcloud->account_info();
|
||||
if (is_wp_error($info)) {
|
||||
$this->log('pCloud ('.$info->get_error_code().'): '.$info->get_error_message(), 'error');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$accountinfo_err = sprintf(__("%s error: %s", 'updraftplus'), 'pCloud', $e->getMessage()).' ('.$e->getCode().')';
|
||||
$this->log('pCloud error: ' . $e->getMessage() . ' (line: ' . $e->getLine() . ', file: ' . $e->getFile() . ')');
|
||||
$this->log(sprintf(__('error: %s (see log file for more)', 'updraftplus'), $e->getMessage()), 'error');
|
||||
}
|
||||
|
||||
$message = "<strong>".__('Success:', 'updraftplus').'</strong> '.sprintf(__('you have authenticated your %s account', 'updraftplus'), 'pCloud');
|
||||
|
||||
if (isset($info['quota']) || isset($info['usedquota']) || isset($info['email'])) {
|
||||
$available_quota = $info['quota'] - $info['usedquota'];
|
||||
$used_perc = round($info['usedquota']*100/$info['quota'], 1);
|
||||
|
||||
$opts = $this->get_options();
|
||||
$opts['ownername'] = $info['email'];
|
||||
$this->set_options($opts, true);
|
||||
|
||||
$message .= ". <br>".sprintf(__('Your %s account name: %s', 'updraftplus'), 'pCloud', htmlspecialchars($info['email']));
|
||||
|
||||
$message .= ' <br>'.sprintf(__('Your %s quota usage: %s %% used, %s available', 'updraftplus'), 'pCloud', $used_perc, round($available_quota/1048576, 1).' MB');
|
||||
} else {
|
||||
$message .= " (".__('though part of the returned information was not as expected - whether this indicates a real problem cannot be determined', 'updraftplus').")";
|
||||
if (!empty($accountinfo_err)) $message .= "<br>".htmlspecialchars($accountinfo_err);
|
||||
}
|
||||
|
||||
if ($return_instead_of_echo) {
|
||||
return "<div class='updraftmessage updated'><p>{$message}</p></div>";
|
||||
} else {
|
||||
$updraftplus_admin->show_admin_warning($message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This basically reproduces the relevant bits of bootstrap.php from the SDK
|
||||
*
|
||||
* @return object
|
||||
* @throws Exception Throws standard exception.
|
||||
*/
|
||||
public function bootstrap() {
|
||||
|
||||
if (!class_exists('UpdraftPlus_Pcloud_API')) {
|
||||
include_once UPDRAFTPLUS_DIR . '/includes/pcloud/UpdraftPlus_Pcloud_API.php';
|
||||
}
|
||||
|
||||
// if (false === $opts) $opts = $this->options;
|
||||
// $opts = $this->get_options();
|
||||
|
||||
$storage = $this->get_storage();
|
||||
if (!empty($storage) && !is_wp_error($storage)) {
|
||||
return $storage;
|
||||
}
|
||||
|
||||
$opts = $this->get_options();
|
||||
$pclauth = !empty($opts['pclauth']) ? $opts['pclauth'] : '';
|
||||
$pcllocation = !empty($opts['pcllocation']) ? intval($opts['pcllocation']) : '';
|
||||
|
||||
if (empty($pclauth) || 20 > strlen($pclauth)) {
|
||||
throw new Exception('You are not logged in.');
|
||||
}
|
||||
|
||||
$storage = new UpdraftPlus_Pcloud_API();
|
||||
$storage->set_auth($pclauth);
|
||||
$storage->set_location($pcllocation);
|
||||
$storage->set_folder($opts['folder']);
|
||||
|
||||
if (empty($opts['folderid'])) {
|
||||
|
||||
$folder_id = $storage->get_upload_dir_id();
|
||||
if (is_wp_error($folder_id)) $this->log('pCloud ('.$folder_id->get_error_code().'): '.$folder_id->get_error_message(), 'error');
|
||||
if (is_numeric($folder_id) && 0 < $folder_id) {
|
||||
$opts['folderid'] = $folder_id;
|
||||
}
|
||||
|
||||
$this->set_options($opts, true);
|
||||
|
||||
} else {
|
||||
$folder_id = intval($opts['folderid']);
|
||||
}
|
||||
|
||||
if (empty($folder_id) || 10 > $folder_id) $this->log('pCloud error: Failed to get folder id; backup will be uploaded to the root directory');
|
||||
|
||||
$this->set_storage($storage);
|
||||
|
||||
return $storage;
|
||||
}
|
||||
}
|
||||
593
wp-content/plugins/updraftplus/addons/reporting.php
Normal file
593
wp-content/plugins/updraftplus/addons/reporting.php
Normal file
@@ -0,0 +1,593 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: reporting:Sophisticated reporting options
|
||||
Description: Provides various new reporting capabilities
|
||||
Version: 2.7
|
||||
Shop: /shop/reporting/
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
// Future possibility: more reporting options; e.g. HTTP ping; tweet, etc.
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
new UpdraftPlus_Addon_Reporting;
|
||||
|
||||
class UpdraftPlus_Addon_Reporting {
|
||||
|
||||
private $emails;
|
||||
|
||||
private $warningsonly;
|
||||
|
||||
private $history;
|
||||
|
||||
private $syslog;
|
||||
|
||||
private $log_facility;
|
||||
|
||||
private $log_ident;
|
||||
|
||||
/**
|
||||
* Email reporting HTML content
|
||||
*
|
||||
* @var String
|
||||
*/
|
||||
private $html;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter('updraftplus_showbackup_date', array($this, 'showbackup_date'), 10, 5);
|
||||
add_filter('updraft_backupnow_options', array($this, 'backupnow_options'), 10, 2);
|
||||
add_filter('updraftplus_report_form', array($this, 'updraftplus_report_form'));
|
||||
add_action('updraftplus_configprint_expertoptions', array($this, 'configprint_expertoptions'), 9);
|
||||
add_filter('updraftplus_saveemails', array($this, 'saveemails'), 10, 2);
|
||||
add_filter('updraft_report_sendto', array($this, 'updraft_report_sendto'), 10, 5);
|
||||
add_filter('updraftplus_email_whichaddresses', array($this, 'email_whichaddresses'));
|
||||
add_filter('updraftplus_email_backup', array($this, 'email_backup'), 10, 4);
|
||||
add_filter('updraft_report_subject', array($this, 'updraft_report_subject'), 10, 3);
|
||||
add_filter('updraft_report_body', array($this, 'updraft_report_body'), 10, 6);
|
||||
add_filter('updraft_report_attachments', array($this, 'updraft_report_attachments'));
|
||||
add_filter('updraftplus_email_backup_skip_log_message', array($this, 'backup_skip_log_message'), 10, 4);
|
||||
add_filter('updraft_backupnow_modal_afteroptions', array($this, 'backupnow_modal_afteroptions'), 10, 2);
|
||||
add_filter('updraft_report_downloadable_file_link', array($this, 'generate_downloadable_file_link'), 10, 4);
|
||||
add_action('updraft_final_backup_history', array($this, 'final_backup_history'));
|
||||
add_action('updraft_report_finished', array($this, 'report_finished'));
|
||||
add_action('init', array($this, 'init'));
|
||||
$this->log_ident = (defined('UPDRAFTPLUS_LOG_IDENT')) ? UPDRAFTPLUS_LOG_IDENT : 'updraftplus';
|
||||
$this->log_facility = (defined('UPDRAFTPLUS_LOG_FACILITY')) ? UPDRAFTPLUS_LOG_FACILITY : LOG_USER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WordPress action 'init'
|
||||
*/
|
||||
public function init() {
|
||||
if (!class_exists('UpdraftPlus_Options')) return;
|
||||
if (!UpdraftPlus_Options::get_updraft_option('updraft_log_syslog', false) || !function_exists('openlog') || !function_exists('syslog')) return;
|
||||
if (false !== ($this->syslog = openlog($this->log_ident, LOG_ODELAY|LOG_PID, $this->log_facility))) add_filter('updraftplus_logline', array($this, 'logline'), 10, 3);
|
||||
}
|
||||
|
||||
public function showbackup_date($date, $backup, $jobdata, $key, $simple_format) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
if (!is_array($backup) || empty($backup['label'])) return $date;
|
||||
if ($simple_format) {
|
||||
return $date.' - '.htmlspecialchars($backup['label']);
|
||||
} else {
|
||||
return $date.'<span class="updraft-backup-label">'.htmlspecialchars($backup['label']).'</span>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WP filter updraft_backupnow_modal_afteroptions
|
||||
*
|
||||
* @param String $ret - unfiltered value to return
|
||||
* @param String $prefix - prefix to use
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public function backupnow_modal_afteroptions($ret, $prefix) {
|
||||
|
||||
$ret .= '<p id="'.$prefix.'backupnow_label_container" class="new-backups-only">
|
||||
<label for="'.$prefix.'backupnow_label">'.__('Your label for this backup (optional)', 'updraftplus').':</label> <input type="text" id="'.$prefix.'backupnow_label" name="label" size="40" maxlength="40" value="';
|
||||
|
||||
if ('remotesend_' == $prefix) {
|
||||
$label = preg_replace('#^https?://#i', '', network_site_url());
|
||||
|
||||
$backup_of = __('Backup of:', 'updraftplus').' ';
|
||||
|
||||
if (strlen($backup_of.$label) <= 40) $label = $backup_of.$label;
|
||||
|
||||
$ret .= esc_attr($label);
|
||||
}
|
||||
|
||||
$ret .= '"></p>';
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the backup-now options based on the incoming request
|
||||
*
|
||||
* @param Array $options - the current options
|
||||
* @param Array $request - the incoming request
|
||||
*
|
||||
* @return Array - the filtered options
|
||||
*/
|
||||
public function backupnow_options($options, $request) {
|
||||
if (!is_array($options)) return $options;
|
||||
// See: https://trello.com/c/NH83ZCnj/494
|
||||
if (!empty($request['backupnow_label']) && is_string($request['backupnow_label']))
|
||||
$options['label'] = substr($request['backupnow_label'], 0, 40);
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function logline($line, $nonce, $level) {
|
||||
// See https://php.net/manual/en/function.syslog.php for descriptions of the log level meanings
|
||||
if ('error' == $level) {
|
||||
$pri = LOG_WARNING;
|
||||
} elseif ('warning' == $level) {
|
||||
$pri = LOG_NOTICE;
|
||||
} else {
|
||||
$pri = LOG_INFO;
|
||||
}
|
||||
@syslog($pri, "($nonce) $line");// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
|
||||
return $line;
|
||||
}
|
||||
|
||||
public function final_backup_history($history) {
|
||||
$this->history = $history;
|
||||
}
|
||||
|
||||
public function updraft_report_attachments($attachments) {
|
||||
// Always attach the log file
|
||||
global $updraftplus;
|
||||
$attachments[0] = $updraftplus->logfile_name;
|
||||
return $attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Jobdata is passed in, rather than live, because the live jobdata may have moved on from the time which the point should reflectg (e.g. an incremental backup was subsequently started)
|
||||
*
|
||||
* @param string $report
|
||||
* @param string $final_message
|
||||
* @param string $contains
|
||||
* @param string $errors
|
||||
* @param string $warnings
|
||||
* @param array $jobdata
|
||||
* @return string
|
||||
*/
|
||||
public function updraft_report_body($report, $final_message, $contains, $errors, $warnings, $jobdata) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$error_count = 0;
|
||||
foreach ($errors as $err) {
|
||||
if ((is_string($err) || is_wp_error($err)) || (is_array($err) && 'error' == $err['level'])) {
|
||||
$error_count++;
|
||||
}
|
||||
}
|
||||
$warning_count = count($warnings);
|
||||
|
||||
$history = $this->history;
|
||||
$debug = UpdraftPlus_Options::get_updraft_option('updraft_debug_mode');
|
||||
|
||||
$errors_and_warns = sprintf(__('%d errors, %d warnings', 'updraftplus'), $error_count, $warning_count);
|
||||
|
||||
$file_entities = $updraftplus->get_backupable_file_entities(true, true);
|
||||
|
||||
$backup_time = empty($jobdata['incremental_run_start']) ? $jobdata['backup_time'] : $jobdata['incremental_run_start'];
|
||||
|
||||
$date = get_date_from_gmt(gmdate('Y-m-d H:i:s', $backup_time), 'Y-m-d H:i');
|
||||
|
||||
$time_taken = time() - $backup_time;
|
||||
$hrs = floor($time_taken/3600);
|
||||
$mins = floor(($time_taken-3600*$hrs)/60);
|
||||
$secs = $time_taken - 3600*$hrs - 60*$mins;
|
||||
|
||||
$services = empty($jobdata['service']) ? array('none') : $jobdata['service'];
|
||||
if (!is_array($services)) $services = array('none');
|
||||
|
||||
$time_taken = sprintf(__("%d hours, %d minutes, %d seconds", 'updraftplus'), $hrs, $mins, $secs);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<style type="text/css">h1, h2, h3, p, pre, ul { clear: both; margin: 0; padding: 15px 0 0;} h1, h3, ul { margin-top: 2px; margin-bottom: 0; }</style>
|
||||
<h1><?php echo esc_html(get_bloginfo('name').': '.__('Backup Report', 'updraftplus'));?></h1>
|
||||
<p style="float: left; clear: left; margin: 0 0 8px;"><em><?php printf(esc_html__('Backup made by %s', 'updraftplus'), '<a href="https://updraftplus.com" target="_blank">UpdraftPlus '.esc_html($updraftplus->version)); ?></a></em></p>
|
||||
<?php
|
||||
if (!class_exists('UpdraftPlus_Notices')) updraft_try_include_file('includes/updraftplus-notices.php', 'include_once');
|
||||
global $updraftplus_notices;
|
||||
$updraftplus_notices->do_notice(false, 'report', false);
|
||||
$backup_type = __('Manual backup began:', 'updraftplus');
|
||||
if (!empty($jobdata['is_scheduled_backup'])) $backup_type = __('Scheduled backup began:', 'updraftplus');
|
||||
if (!empty($jobdata['is_autobackup'])) $backup_type = __('Automatic backup began:', 'updraftplus');
|
||||
if ('incremental' === $jobdata['job_type']) $backup_type = __('Incremental backup began:', 'updraftplus');
|
||||
?>
|
||||
<div style="width: 100%; display: table; margin-bottom: 5px;"><div style="font-weight: bold; width: 200px; float: left;"><?php echo esc_html__('Backup of:', 'updraftplus'); ?></div> <div style="float: left;"><a href="<?php echo esc_url(site_url()); ?>"><?php echo esc_html(site_url());?></a></div></div>
|
||||
<div style="width: 100%; display: table; margin-bottom: 5px;"><div style="font-weight: bold; width: 200px; float: left;"><?php echo esc_html__('Latest status:', 'updraftplus');?></div> <div style="float: left;"><?php echo esc_html($final_message); ?></div></div>
|
||||
<div style="width: 100%; display: table; margin-bottom: 5px;"><div style="font-weight: bold; width: 200px; float: left;"><?php echo esc_html($backup_type);?></div> <div style="float: left;"><?php echo esc_html($date); ?></div></div>
|
||||
<div style="width: 100%; display: table; margin-bottom: 5px;"><div style="font-weight: bold; width: 200px; float: left;"><?php echo esc_html__('Contains:', 'updraftplus');?></div> <div style="float: left;"><?php echo esc_html($contains); ?></div></div>
|
||||
<?php
|
||||
$extra_messages = apply_filters('updraftplus_report_extramessages', array());
|
||||
if (is_array($extra_messages)) {
|
||||
foreach ($extra_messages as $msg) {
|
||||
?>
|
||||
<div style="width: 100%; display: table; margin-bottom: 5px;"><div style="font-weight: bold; width: 200px; float: left;"><?php echo esc_html($msg['key']);?></div> <div style="float: left;"><?php echo esc_html($msg['val']); ?></div></div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div style="width: 100%; display: table; margin-bottom: 5px;"><div style="font-weight: bold; width: 200px; float: left;"><?php echo esc_html__('Errors / warnings:', 'updraftplus');?></div> <div style="float: left;"><?php echo esc_html($errors_and_warns); ?></div></div>
|
||||
<?php
|
||||
if ($updraftplus->error_count() > 0) {
|
||||
echo '<h2>'.esc_html__('Errors', 'updraftplus')."</h2>\n<ul>";
|
||||
foreach ($updraftplus->errors as $err) {
|
||||
if (is_wp_error($err)) {
|
||||
foreach ($err->get_error_messages() as $msg) {
|
||||
echo "<li>".esc_html(rtrim($msg))."</li>\n";
|
||||
}
|
||||
} elseif (is_array($err) && 'error' == $err['level']) {
|
||||
echo "<li>".esc_html(rtrim($err['message']))."</li>\n";
|
||||
} elseif (is_string($err)) {
|
||||
echo "<li>".esc_html(rtrim($err))."</li>\n";
|
||||
}
|
||||
}
|
||||
echo "</ul>\n";
|
||||
}
|
||||
if (is_array($warnings) && count($warnings) >0) {
|
||||
echo '<h2>'.esc_html__('Warnings', 'updraftplus')."</h2>\n<ul>";
|
||||
foreach ($warnings as $err) {
|
||||
echo "<li>".esc_html(rtrim($err))."</li>\n";
|
||||
}
|
||||
echo "</ul>\n";
|
||||
echo '<p><em>'.esc_html(__('Note that warning messages are advisory - the backup process does not stop for them.', 'updraftplus').' '.__('Instead, they provide information that you might find useful, or that may indicate the source of a problem if the backup did not succeed.', 'updraftplus')).'</em></p>';
|
||||
}
|
||||
?>
|
||||
<p>
|
||||
<div style="width: 100%; display: table; margin-bottom: 5px;"><div style="font-weight: bold; width: 200px; float: left;"><?php echo esc_html__('Time taken:', 'updraftplus');?></div> <div style="float: left;"><?php echo esc_html($time_taken);?></div></div>
|
||||
<div style="width: 100%; display: table; margin-bottom: 5px;"><div style="font-weight: bold; width: 200px; float: left;"><?php echo esc_html__('Uploaded to:', 'updraftplus');?></div> <div style="float: left;"><?php
|
||||
|
||||
$show_services = '';
|
||||
foreach ($services as $serv) {
|
||||
if ('none' == $serv || '' == $serv) {
|
||||
$add_none = true;
|
||||
} else {
|
||||
|
||||
if (isset($updraftplus->backup_methods[$serv])) {
|
||||
$show_services .= ($show_services) ? ', '.$updraftplus->backup_methods[$serv] : $updraftplus->backup_methods[$serv];
|
||||
} else {
|
||||
$show_services .= ($show_services) ? ', '.$serv : $serv;
|
||||
}
|
||||
|
||||
if (isset($jobdata['remotestorage_extrainfo']) && !empty($jobdata['remotestorage_extrainfo'][$serv])) {
|
||||
|
||||
$show_services .= ' ('.$jobdata['remotestorage_extrainfo'][$serv]['pretty'].')';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if ('' == $show_services && $add_none) $show_services .= __('None', 'updraftplus');
|
||||
|
||||
echo wp_kses_post($show_services)."</div></div></p>\n\n";
|
||||
|
||||
$checksums = $updraftplus->which_checksums();
|
||||
|
||||
if (!empty($file_entities)) {
|
||||
foreach ($file_entities as $entity => $info) {
|
||||
echo $updraftplus->printfile($info['description'], $history, $entity, $checksums, $jobdata);// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- needs to be presented in html
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($history)) {
|
||||
foreach ($history as $key => $val) {
|
||||
if ('db' == strtolower(substr($key, 0, 2)) && '-size' != substr($key, -5, 5) && 'incremental' !== $jobdata['job_type']) {
|
||||
echo $updraftplus->printfile(__('Database', 'updraftplus'), $history, $key, $checksums, $jobdata);// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- needs to be presented in html
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo '<p>'.esc_html__('The log file has been attached to this email.', 'updraftplus')."</p>\n\n";
|
||||
|
||||
if ($debug) {
|
||||
echo '<h2>'.esc_html__('Debugging information', 'updraftplus')."</h2>\n<pre>";
|
||||
print esc_html(chunk_split(base64_encode(serialize($jobdata)), 76, "\n"));
|
||||
print "\n";
|
||||
print esc_html(chunk_split(base64_encode(serialize($history)), 76, "\n"));
|
||||
echo "</pre>";
|
||||
}
|
||||
|
||||
$this->html = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
// Lower priority: get there before other plugins which apply templates
|
||||
add_filter('wp_mail_content_type', array($this, 'wp_mail_content_type'), 8);
|
||||
|
||||
$report_body = $this->html;
|
||||
|
||||
|
||||
return str_replace("\n", "\r\n", strip_tags(preg_replace('#\<style([^\>]*)\>.*\</style\>#', '', $report_body)));
|
||||
|
||||
}
|
||||
|
||||
public function wp_mail_content_type($content_type) {
|
||||
// Only convert if the message is text/plain and the template is ok
|
||||
if ('text/plain' == $content_type && !empty($this->html)) {
|
||||
static $added_phpmailer_init_action;
|
||||
if (empty($added_phpmailer_init_action)) {
|
||||
$added_phpmailer_init_action = true;
|
||||
add_action('phpmailer_init', array($this, 'phpmailer_init'));
|
||||
}
|
||||
return 'text/html';
|
||||
}
|
||||
return $content_type;
|
||||
}
|
||||
|
||||
public function phpmailer_init($phpmailer) {
|
||||
if (empty($this->html)) return;
|
||||
$phpmailer->AltBody = wp_specialchars_decode($phpmailer->Body, ENT_QUOTES);
|
||||
$phpmailer->Body = $this->html;
|
||||
}
|
||||
|
||||
public function report_finished() {
|
||||
global $phpmailer;
|
||||
remove_filter('wp_mail_content_type', array($this, 'wp_mail_content_type'), 8);
|
||||
remove_action('phpmail_init', array($this, 'phpmailer_init'));
|
||||
if (empty($this->html)) return;
|
||||
if (is_object($phpmailer) && (is_a($phpmailer, 'PHPMailer') || is_a($phpmailer, 'PHPMailer\PHPMailer\PHPMailer'))) {
|
||||
// $phpmailer->AltBody = '';
|
||||
// $phpmailer->Body = '';
|
||||
// $phpmailer->ContentType = 'text/plain';
|
||||
// Best just to force WP to get the whole thing again from the beginning
|
||||
$phpmailer = null;
|
||||
}
|
||||
unset($this->html);
|
||||
}
|
||||
|
||||
public function updraft_report_subject($subject, $error_count, $warning_count) {
|
||||
$count = 0;
|
||||
if ($error_count > 0) {
|
||||
$count = $error_count;
|
||||
} elseif ($warning_count >0) {
|
||||
$count = $warning_count;
|
||||
}
|
||||
if ($count) $subject .= sprintf(' (%s)', $count);
|
||||
return $subject;
|
||||
}
|
||||
|
||||
public function updraft_report_sendto($send, $addr, $error_count, $warning_count, $ind) {
|
||||
if (null === $this->emails) {
|
||||
$this->emails = UpdraftPlus_Options::get_updraft_option('updraft_email', array());
|
||||
if (is_string($this->emails)) $this->emails = array($this->emails);
|
||||
}
|
||||
if (null === $this->warningsonly) {
|
||||
$this->warningsonly = UpdraftPlus_Options::get_updraft_option('updraft_report_warningsonly', array());
|
||||
if (!is_array($this->warningsonly)) $this->warningsonly = array();
|
||||
}
|
||||
|
||||
if (0 == $error_count + $warning_count && isset($this->emails[$ind]) && !empty($this->warningsonly[$ind])) {
|
||||
$send = false;
|
||||
global $updraftplus;
|
||||
$updraftplus->log("No report will be sent to this address, as it is configured to receive them only when there are errors or warnings: ".substr($addr, 0, 5).'...');
|
||||
}
|
||||
return $send;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for filter updraftplus_email_backup
|
||||
*
|
||||
* @param boolean $doit filter value of updraftplus_email_backup
|
||||
* @param string $addr email address
|
||||
* @param integer $ind index of report box
|
||||
* @param string $type backup entity types
|
||||
* @return boolean filtered value
|
||||
*/
|
||||
public function email_backup($doit, $addr, $ind, $type) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
$wholebackup = UpdraftPlus_Options::get_updraft_option('updraft_report_wholebackup', null);
|
||||
$dbbackup = UpdraftPlus_Options::get_updraft_option('updraft_report_dbbackup', null);
|
||||
if (is_array($wholebackup) && !empty($wholebackup[$ind]) && empty($dbbackup[$ind])) {
|
||||
return true;
|
||||
}
|
||||
if ('db' == strtolower(substr($type, 0, 2)) && is_array($dbbackup) && !empty($dbbackup[$ind])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for filter updraftplus_backup_skip_log_message
|
||||
*
|
||||
* @param string $log_message default log message of updraftplus_backup_skip_log_message filter
|
||||
* @param string $addr email address
|
||||
* @param integer $ind index of report box
|
||||
* @param string $descrip_type backup entity types
|
||||
* @return string log message
|
||||
*/
|
||||
public function backup_skip_log_message($log_message, $addr, $ind, $descrip_type) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
$wholebackup = UpdraftPlus_Options::get_updraft_option('updraft_report_wholebackup', null);
|
||||
if (!is_array($wholebackup) || empty($wholebackup[$ind])) {
|
||||
return 'You have chosen to not send the backup via the email remote storage option for '.$addr.'. '.$descrip_type.' will not be sent.';
|
||||
} else {
|
||||
return 'You have chosen to only send the database via the email remote storage option for '.$addr.'. '.$descrip_type.' will not be sent.';
|
||||
}
|
||||
}
|
||||
|
||||
public function email_whichaddresses() {
|
||||
return __('Use the "Reporting" section to configure the email addresses to be used.', 'updraftplus');
|
||||
}
|
||||
|
||||
public function admin_footer() {
|
||||
?>
|
||||
<script>
|
||||
jQuery(function($){
|
||||
|
||||
var reportbox_index = $('#updraft_report_cell .updraft_reportbox').length + 2;
|
||||
|
||||
$('#updraft_report_cell').on('click', '.updraft_reportbox .updraft_reportbox_delete', function() {
|
||||
$(this).closest('.updraft_reportbox').fadeOut('medium', function() { $(this).remove(); });
|
||||
});
|
||||
|
||||
$('#updraft-navtab-settings-content .updraft_report_another').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
$('#updraft-navtab-settings-content .updraft_report_another_p').before('<div id="updraft_reportbox_'+reportbox_index+'" class="updraft_reportbox updraft-hidden" style="display:none;"><button class="updraft_reportbox_delete" reportbox_index="'+reportbox_index+'" type="button"><span class="dashicons dashicons-no"></span></button>\
|
||||
<input type="text" title="'+updraftlion.enteremailhere+'" class="updraft_report_email" name="updraft_email['+reportbox_index+']" value="" />\
|
||||
<label for="updraft_report_warningsonly_'+reportbox_index+'" class="updraft_checkbox"><input class="updraft_report_checkbox" type="checkbox" id="updraft_report_warningsonly_'+reportbox_index+'" name="updraft_report_warningsonly['+reportbox_index+']"> '+updraftlion.sendonlyonwarnings+'</label><div class="updraft_report_wholebackup">\
|
||||
<label for="updraft_report_wholebackup_'+reportbox_index+'" title="'+updraftlion.emailsizelimits+'" class="updraft_checkbox"><input class="updraft_report_checkbox" type="checkbox" id="updraft_report_wholebackup_'+reportbox_index+'" name="updraft_report_wholebackup['+reportbox_index+']" title="'+updraftlion.emailsizelimits+'"> '+updraftlion.wholebackup+'</label></div><div class="updraft_report_dbbackup updraft_report_disabled">\
|
||||
<label for="updraft_report_dbbackup_'+reportbox_index+'" title="'+updraftlion.emailsizelimits+'" class="updraft_checkbox"><input class="updraft_report_checkbox" type="checkbox" id="updraft_report_dbbackup_'+reportbox_index+'" disabled name="updraft_report_dbbackup['+reportbox_index+']" title="'+updraftlion.emailsizelimits+'"> '+updraftlion.dbbackup+'</label></div></div>');
|
||||
$('#updraft_reportbox_'+reportbox_index).fadeIn();
|
||||
|
||||
reportbox_index++;
|
||||
|
||||
});
|
||||
$('#updraft_report_row').on('change', '.updraft_report_wholebackup .updraft_report_checkbox', function() {
|
||||
var reportbox = $(this).closest('.updraft_reportbox').find('.updraft_report_dbbackup');
|
||||
if ($(this).is(':checked')) {
|
||||
reportbox.removeClass('updraft_report_disabled').find('.updraft_report_checkbox').prop('disabled', false);
|
||||
} else {
|
||||
reportbox.find('.updraft_report_checkbox').prop('checked', false);
|
||||
reportbox.addClass('updraft_report_disabled').find('.updraft_report_checkbox').prop('disabled', true);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function updraftplus_report_form() {
|
||||
|
||||
add_action('admin_footer', array($this, 'admin_footer'));
|
||||
|
||||
// Columns: Email address | only send if no errors/warnings
|
||||
|
||||
$out = '<tr id="updraft_report_row">
|
||||
<th>'.__('Send reports', 'updraftplus').':</th>
|
||||
<td id="updraft_report_cell">';
|
||||
|
||||
// Could be multiple (separated by commas)
|
||||
$updraft_email = UpdraftPlus_Options::get_updraft_option('updraft_email');
|
||||
$updraft_report_warningsonly = UpdraftPlus_Options::get_updraft_option('updraft_report_warningsonly');
|
||||
$updraft_report_wholebackup = UpdraftPlus_Options::get_updraft_option('updraft_report_wholebackup');
|
||||
$updraft_report_dbbackup = UpdraftPlus_Options::get_updraft_option('updraft_report_dbbackup');
|
||||
|
||||
if (is_string($updraft_email)) {
|
||||
$utmp = $updraft_email;
|
||||
$updraft_email = array();
|
||||
$updraft_report_warningsonly = array();
|
||||
$updraft_report_wholebackup = array();
|
||||
foreach (explode(',', $utmp) as $email) {
|
||||
// Whole backup only takes effect if 'Email' is chosen as a storage option
|
||||
$updraft_email[] = $email;
|
||||
$updraft_report_warningsonly[] = false;
|
||||
$updraft_report_wholebackup[] = true;
|
||||
}
|
||||
} elseif (!is_array($updraft_email)) {
|
||||
$updraft_email = array();
|
||||
$updraft_report_warningsonly = array();
|
||||
$updraft_report_wholebackup = array();
|
||||
}
|
||||
|
||||
$out .= '<p>'.__('Enter addresses here to have a report sent to them when a backup job finishes.', 'updraftplus').'</p>';
|
||||
|
||||
$ind = 0;
|
||||
foreach ($updraft_email as $ikey => $destination) {
|
||||
$warningsonly = empty($updraft_report_warningsonly[$ikey]) ? false : true;
|
||||
$wholebackup = empty($updraft_report_wholebackup[$ikey]) ? false : true;
|
||||
$dbbackup = empty($updraft_report_dbbackup[$ikey]) ? false : true;
|
||||
if (!empty($destination)) {
|
||||
$ind++;
|
||||
$out .= $this->report_box_generator($destination, $ind, $warningsonly, $wholebackup, $dbbackup);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === $ind) $out .= $this->report_box_generator('', 0, false, false, false);
|
||||
|
||||
$out .= '<p class="updraft_report_another_p"><a class="updraft_report_another updraft_icon_link" href="'.esc_url(UpdraftPlus::get_current_clean_url()).'#updraft_report_row"><span class="dashicons dashicons-plus"></span>'.__('Add another address...', 'updraftplus').'</a></p>';
|
||||
|
||||
$out .= '</td>
|
||||
</tr>';
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders reporting expert settings
|
||||
*/
|
||||
public function configprint_expertoptions() {
|
||||
?>
|
||||
<tr class="expertmode updraft-hidden" style="display:none;">
|
||||
<th><?php esc_html_e('Log all messages to syslog', 'updraftplus');?>:</th>
|
||||
<td><input type="checkbox" id="updraft_log_syslog" name="updraft_log_syslog" value="1" <?php if (UpdraftPlus_Options::get_updraft_option('updraft_log_syslog')) echo 'checked="checked"'; ?>> <br><label for="updraft_log_syslog"><?php esc_html_e("Log all messages to syslog (only server admins are likely to want this)", 'updraftplus'); ?></label></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function saveemails($rinput, $input) {
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Email Report Box
|
||||
*
|
||||
* @param string $addr email address
|
||||
* @param integer $ind index of email report
|
||||
* @param boolean $warningsonly boolean boolean Whether send email of warnings
|
||||
* @param boolean $wholebackup boolean Whether email whole backup checkbox is checked or not
|
||||
* @param boolean $dbbackup boolean Whether email database backup checkbox is checked or not
|
||||
* @param string $out html
|
||||
*/
|
||||
private function report_box_generator($addr, $ind, $warningsonly, $wholebackup, $dbbackup) {
|
||||
|
||||
$out = '';
|
||||
|
||||
$out .='<div id="updraft_reportbox_'.$ind.'" class="updraft_reportbox">';
|
||||
|
||||
$out .= '<button class="updraft_reportbox_delete" type="button"><span title="'.__('Remove', 'updraftplus').'" class="dashicons dashicons-no"></span></button>';
|
||||
|
||||
$out .= '<input type="text" title="'.esc_attr(__('To send to more than one address, separate each address with a comma.', 'updraftplus')).'" class="updraft_report_email" name="updraft_email['.$ind.']" value="'.esc_attr($addr).'" />';
|
||||
|
||||
$out .= '<label for="updraft_report_warningsonly_'.$ind.'" class="updraft_checkbox"><input '.(($warningsonly) ? 'checked="checked" ' : '').' id="updraft_report_warningsonly_'.$ind.'" class="updraft_report_checkbox" type="checkbox" name="updraft_report_warningsonly['.$ind.']"> '.__('Send a report only when there are warnings/errors', 'updraftplus').'</label>';
|
||||
|
||||
$out .= '<div class="updraft_report_wholebackup"><label for="updraft_report_wholebackup_'.$ind.'" title="'.esc_attr(sprintf(__('Be aware that mail servers tend to have size limits; typically around %s MB; backups larger than any limits will likely not arrive.', 'updraftplus'), '10-20')).'" class="updraft_checkbox"><input '.(($wholebackup) ? 'checked="checked" ' : '').'class="updraft_report_checkbox" type="checkbox" id="updraft_report_wholebackup_'.$ind.'" name="updraft_report_wholebackup['.$ind.']" title="'.esc_attr(sprintf(__('Be aware that mail servers tend to have size limits; typically around %s MB; backups larger than any limits will likely not arrive.', 'updraftplus'), '10-20')).'"> '.__('When email storage method is enabled, and an email address is entered, also send the backup', 'updraftplus').'</label></div>';
|
||||
|
||||
$out .= '<div class="updraft_report_dbbackup'.((!$wholebackup) ? ' updraft_report_disabled' : '').'"><label for="updraft_report_dbbackup_'.$ind.'" title="'.esc_attr(sprintf(__('Be aware that mail servers tend to have size limits; typically around %s MB; backups larger than any limits will likely not arrive as a result UpdraftPlus will only send Database backups to email.', 'updraftplus'), '10-20')).'" class="updraft_checkbox"><input '.(($dbbackup) ? 'checked="checked" ' : '').'class="updraft_report_checkbox" type="checkbox" '.((!$wholebackup) ? 'disabled ' : '').'id="updraft_report_dbbackup_'.$ind.'" name="updraft_report_dbbackup['.$ind.']" title="'.esc_attr(sprintf(__('Be aware that mail servers tend to have size limits; typically around %s MB; backups larger than any limits will likely not arrive.', 'updraftplus').' '.__('Use this option to only send database backups when sending to email, and skip other components.', 'updraftplus'), '10-20')).'"> '.__('Only email the database backup', 'updraftplus').'</label></div>';
|
||||
|
||||
$out .= '</div>';
|
||||
|
||||
return $out;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a downloadable backup link
|
||||
*
|
||||
* @param String $link the unfiltered backup file name in plain text format
|
||||
* @param String $entity the backup entity (db, uploads, plugins, etc..)
|
||||
* @param Integer $index the index number of the backup file
|
||||
* @param Array $jobdata the jobdata for the currently running backup
|
||||
* @return String the filtered backup file name with its HTML link text attached
|
||||
*/
|
||||
public function generate_downloadable_file_link($link, $entity, $index, $jobdata) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$jobdata['service'] = empty($jobdata['service']) ? array() : $updraftplus->get_canonical_service_list($jobdata['service']);
|
||||
|
||||
// I was thinking not to check the the nonce first, but at this point I believe we should only generate a valid link
|
||||
$download_link = is_array($jobdata) && !empty($jobdata['backup_time']) && !empty($this->file_nonce) && empty($jobdata['service']);
|
||||
|
||||
if ($download_link) {
|
||||
$link = '<a href="'.esc_url(add_query_arg(array(
|
||||
'page' => 'updraftplus',
|
||||
'type' => $entity,
|
||||
'timestamp' => $jobdata['backup_time'],
|
||||
'nonce' => $this->file_nonce,
|
||||
'findex' => $index,
|
||||
'action' => 'updraft_download_backup',
|
||||
), UpdraftPlus_Options::admin_page_url())).'">'.$link.'</a>';
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
}
|
||||
569
wp-content/plugins/updraftplus/addons/s3-enhanced.php
Normal file
569
wp-content/plugins/updraftplus/addons/s3-enhanced.php
Normal file
@@ -0,0 +1,569 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
UpdraftPlus Addon: s3-enhanced:Amazon S3, enhanced
|
||||
Description: Adds enhanced capabilities for Amazon S3 users
|
||||
Version: 1.8
|
||||
Shop: /shop/s3-enhanced/
|
||||
RequiresPHP: 5.5
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
new UpdraftPlus_Addon_S3_Enhanced;
|
||||
|
||||
class UpdraftPlus_Addon_S3_Enhanced {
|
||||
|
||||
public function __construct() {
|
||||
add_filter('updraft_s3_extra_storage_options_configuration_template', array($this, 'extra_storage_options_configuration_template'), 10, 2);
|
||||
add_filter('updraftplus_options_s3_options', array($this, 'transform_options_s3_options'));
|
||||
add_filter('updraft_s3_storageclass', array($this, 'storageclass'), 10, 3);
|
||||
add_action('updraftplus_settings_page_init', array($this, 'updraftplus_settings_page_init'));
|
||||
add_action('updraft_s3_newuser', array($this, 'newuser'));
|
||||
add_filter('updraft_s3_apikeysetting', array($this, 'apikeysettings'));
|
||||
add_action('updraft_s3_print_new_api_user_form', array($this, 's3_print_new_api_user_form'));
|
||||
add_filter('updraft_s3_newuser_go', array($this, 'newuser_go'), 10, 2);
|
||||
add_filter('updraft_s3_partial_templates', array($this, 'get_partial_templates'), 10);
|
||||
add_filter('updraft_s3_template_properties', array($this, 'partial_template_properties'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get partial templates of the S3 remote storage, the partial template is recognised by its name. To find out a name of partial template, look for the partial call syntax in the template, it's enclosed by double curly braces (i.e. {{> partial_template_name }})
|
||||
*
|
||||
* @param Array $partial_templates A collection of filterable partial templates
|
||||
* @return Array an associative array keyed by name of the partial templates
|
||||
*/
|
||||
public function get_partial_templates($partial_templates) {
|
||||
ob_start();
|
||||
?>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<td colspan="2">
|
||||
{{#> updraft_s3_apikeysetting}}
|
||||
<a href="{{updraftplus_premium_url}}" target="_blank"><em>{{api_key_setting_default_label}}</em></a>
|
||||
{{/updraft_s3_apikeysetting}}
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
if (!isset($partial_templates['s3_additional_configuration_top'])) $partial_templates['s3_additional_configuration_top'] = '';
|
||||
$partial_templates['s3_additional_configuration_top'] .= ob_get_clean();
|
||||
$updraft_s3_apikeysetting = apply_filters('updraft_s3_apikeysetting', '');
|
||||
if ('' !== $updraft_s3_apikeysetting) {
|
||||
if (!isset($partial_templates['updraft_s3_apikeysetting'])) $partial_templates['updraft_s3_apikeysetting'] = '';
|
||||
$partial_templates['updraft_s3_apikeysetting'] .= $updraft_s3_apikeysetting;
|
||||
}
|
||||
$extra_storage_options_configuration_template = apply_filters('updraft_s3_extra_storage_options_configuration_template', '');
|
||||
if ('' !== $extra_storage_options_configuration_template) {
|
||||
if (!isset($partial_templates['s3_additional_configuration_bottom'])) $partial_templates['s3_additional_configuration_bottom'] = '';
|
||||
$partial_templates['s3_additional_configuration_bottom'] .= $extra_storage_options_configuration_template;
|
||||
}
|
||||
return $partial_templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is hooked to a filter and going to be accessed by any code within WordPress environment, so instead of sanitising each value in this method and/or using any other technique to prevent XSS attacks, just make sure each partial template has all variables escaped
|
||||
*/
|
||||
public function partial_template_properties() {
|
||||
global $updraftplus;
|
||||
return array(
|
||||
'api_key_setting_default_label' => __('To create a new IAM sub-user and access key that has access only to this bucket, upgrade to Premium.', 'updraftplus'),
|
||||
'api_key_setting_premium_label' => __('If you have an AWS admin user, then you can use this wizard to quickly create a new AWS (IAM) user with access to only this bucket (rather than your whole account)', 'updraftplus'),
|
||||
'input_storage_class_label' => __('Storage class', 'updraftplus'),
|
||||
'input_storage_class_aria' => __('Read more about storage classes', 'updraftplus'),
|
||||
'input_storage_class_text' => __('(Read more)', 'updraftplus'),
|
||||
'input_storage_class_option_labels' => array(
|
||||
'STANDARD' => __('Standard', 'updraftplus'),
|
||||
'STANDARD_IA' => __('Standard (infrequent access)', 'updraftplus'),
|
||||
'INTELLIGENT_TIERING' => __('Intelligent Tiering', 'updraftplus'),
|
||||
),
|
||||
'input_server_encryption_label' => __('Server-side encryption', 'updraftplus'),
|
||||
'input_server_encryption_aria' => __('Read more about server-side encryption', 'updraftplus'),
|
||||
'input_server_encryption_text' => __('(Read more)', 'updraftplus'),
|
||||
'input_server_encryption_title' => __("Check this box to use Amazon's server-side encryption", 'updraftplus'),
|
||||
'updraftplus_current_clean_url' => esc_url(UpdraftPlus::get_current_clean_url()),
|
||||
'updraftplus_premium_url' => $updraftplus->get_url('premium'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* WordPress filter updraft_s3_storageclass
|
||||
*
|
||||
* @param String $class - suggested storage class
|
||||
* @param Object $storage - storage object
|
||||
* @param Array $opts - options
|
||||
*
|
||||
* @return String - filtered value
|
||||
*/
|
||||
public function storageclass($class, $storage, $opts) {
|
||||
|
||||
if (((is_a($storage, 'UpdraftPlus_S3') || is_a($storage, 'UpdraftPlus_S3_Compat')) && is_array($opts) && !empty($opts['rrs']) && in_array($opts['rrs'], array('STANDARD', 'STANDARD_IA', 'INTELLIGENT_TIERING')))) $class = $opts['rrs'];
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gives template string to the page for the extra storage options.
|
||||
*
|
||||
* @param Object $existing_partial_template_str - partial template string to which this outputted template appended
|
||||
*
|
||||
* @return String - the partial template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function extra_storage_options_configuration_template($existing_partial_template_str) {
|
||||
ob_start();
|
||||
?>
|
||||
{{! Any value in the below template should be escaped using double curly braces, so please make sure no value is an raw format that is triple-stashed }}
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_storage_class_label}}:<br><a aria-label="{{input_storage_class_aria}}" href="https://aws.amazon.com/s3/storage-classes/" target="_blank"><em>{{input_storage_class_text}}</em></a></th>
|
||||
<td>
|
||||
<select id="{{get_template_input_attribute_value "id" "rrs"}}" name="{{get_template_input_attribute_value "name" "rrs"}}" data-updraft_settings_test="rrs">
|
||||
{{#each input_storage_class_option_labels}}
|
||||
<option {{#ifeq ../rrs @key}}selected="selected"{{/ifeq}} value="{{@key}}">{{this}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="{{get_template_css_classes true}}">
|
||||
<th>{{input_server_encryption_label}}:<br><a aria-label="{{input_server_encryption_aria}}" href="https://aws.amazon.com/blogs/aws/new-amazon-s3-server-side-encryption/" target="_blank"><em>{{input_server_encryption_text}}</em></a></th>
|
||||
<td><input data-updraft_settings_test="server_side_encryption" title="{{input_server_encryption_title}}" type="checkbox" id="{{get_template_input_attribute_value "id" "server_side_encryption"}}" name="{{get_template_input_attribute_value "name" "server_side_encryption"}}" value="1" {{#ifeq "1" server_side_encryption}}checked="checked"{{/ifeq}}/></td>
|
||||
</tr>
|
||||
<?php
|
||||
$existing_partial_template_str = ob_get_clean();
|
||||
return $existing_partial_template_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options
|
||||
*
|
||||
* @param array $opts handerbar template options
|
||||
* @return array - New handerbar template options
|
||||
*/
|
||||
public function transform_options_s3_options($opts) {
|
||||
$rrs = empty($opts['rrs']) ? 'STANDARD' : $opts['rrs'];
|
||||
if (!empty($rrs) && 'STANDARD_IA' != $rrs && 'INTELLIGENT_TIERING' != $rrs) $rrs = 'STANDARD';
|
||||
$opts['rrs'] = $rrs;
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WP action updraftplus_settings_page_init
|
||||
*/
|
||||
public function updraftplus_settings_page_init() {
|
||||
add_action('admin_footer', array($this, 'admin_footer'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose partial template that deals with apikeysettings
|
||||
*
|
||||
* @param String $msg A filterable partial templates
|
||||
* @return String the partial template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function apikeysettings($msg) {
|
||||
ob_start();
|
||||
?>
|
||||
<a href="{{updraftplus_current_clean_url}}" id="updraft_s3_newapiuser_{{instance_id}}" class="updraft_s3_newapiuser" data-instance_id="{{instance_id}}">{{api_key_setting_premium_label}}</a>
|
||||
<?php
|
||||
$msg = ob_get_clean();
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called upon the WP action updraft_s3_newuser. Dies.
|
||||
*
|
||||
* @param array $data - the posted data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function newuser($data) {
|
||||
echo json_encode($this->newuser_go(array(), stripslashes_deep($data)));
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user
|
||||
*
|
||||
* @param Array $initial_value - present because this method is used as a WP filter
|
||||
* @param Array $settings_values - various keys indicating the access and desired bucket details
|
||||
*
|
||||
* @return Array - results (with keys dependent upon the outcome)
|
||||
*/
|
||||
public function newuser_go($initial_value = array(), $settings_values = array()) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
|
||||
if (empty($settings_values['adminaccesskey'])) {
|
||||
return array('e' => 1, 'm' => __('You need to enter an admin access key', 'updraftplus'));
|
||||
}
|
||||
|
||||
if (empty($settings_values['adminsecret'])) {
|
||||
return array('e' => 1, 'm' => __('You need to enter an admin secret key', 'updraftplus'));
|
||||
}
|
||||
|
||||
if (empty($settings_values['newuser'])) {
|
||||
return array('e' => 1, 'm' => __('You need to enter a new IAM username', 'updraftplus'));
|
||||
}
|
||||
|
||||
if (empty($settings_values['bucket'])) {
|
||||
return array('e' => 1, 'm' => __('You need to enter a bucket', 'updraftplus'));
|
||||
}
|
||||
|
||||
if (empty($settings_values['region'])) $settings_values['region'] = 'us-east-1';
|
||||
|
||||
if (empty($settings_values['rrs'])) $settings_values['rrs'] = false;
|
||||
|
||||
$allow_download = empty($settings_values['allowdownload']) ? false : true;
|
||||
$allow_delete = empty($settings_values['allowdelete']) ? false : true;
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
updraft_try_include_file('methods/s3.php', 'include_once');
|
||||
|
||||
$method = new UpdraftPlus_BackupModule_s3;
|
||||
|
||||
$useservercerts = !empty($settings_values['useservercerts']);
|
||||
$disableverify = !empty($settings_values['disableverify']);
|
||||
$nossl = !empty($settings_values['nossl']);
|
||||
|
||||
$adminaccesskey = $settings_values['adminaccesskey'];
|
||||
$adminsecret = $settings_values['adminsecret'];
|
||||
$region = $settings_values['region'];
|
||||
|
||||
$return_error = false;
|
||||
|
||||
try {
|
||||
$storage = $method->getS3($adminaccesskey, $adminsecret, $useservercerts, $disableverify, $nossl);
|
||||
if (!is_a($storage, 'UpdraftPlus_S3_Compat') && !is_a($storage, 'UpdraftPlus_S3')) {
|
||||
$msg = __('Cannot create new AWS user, since an unknown AWS toolkit is being used.', 'updraftplus');
|
||||
$updraftplus->log('Cannot create new AWS user, since an unknown AWS toolkit is being used.');
|
||||
$updraftplus->log($msg, 'error');
|
||||
$return_error = array('e' => 1, 'm' => __('Error:', 'updraftplus').' '.$msg);
|
||||
}
|
||||
} catch (AuthenticationError $e) {
|
||||
$updraftplus->log('AWS authentication failed ('.$e->getMessage().')');
|
||||
$updraftplus->log(__('AWS authentication failed', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
$return_error = array('e' => 1, 'm' => __('Error:', 'updraftplus').' '.$e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
$return_error = array('e' => 1, 'm' => __('Error:', 'updraftplus').' '.$e->getMessage());
|
||||
}
|
||||
|
||||
if (is_array($return_error)) return $return_error;
|
||||
|
||||
// Get the bucket
|
||||
$path = $settings_values['bucket'];
|
||||
|
||||
if (preg_match("#^/*([^/]+)/(.*)$#", $path, $bmatches)) {
|
||||
$bucket = $bmatches[1];
|
||||
$path = trailingslashit($bmatches[2]);
|
||||
} else {
|
||||
$bucket = $path;
|
||||
$path = "";
|
||||
}
|
||||
|
||||
$location = @$storage->getBucketLocation($bucket);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
if ($location) {
|
||||
$bucket_exists = true;
|
||||
}
|
||||
|
||||
if (!isset($bucket_exists)) {
|
||||
$storage->useDNSBucketName(true);
|
||||
$gb = @$storage->getBucket($bucket, null, null, 1);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
if (false !== $gb) {
|
||||
$bucket_exists = true;
|
||||
$location = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($bucket_exists)) {
|
||||
$storage->setExceptions(true);
|
||||
try {
|
||||
$try_to_create_bucket = @$storage->putBucket($bucket, 'private', $region);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
|
||||
} catch (Exception $e) {
|
||||
$try_to_create_bucket = false;
|
||||
$s3_error = $e->getMessage();
|
||||
}
|
||||
$storage->setExceptions(false);
|
||||
if ($try_to_create_bucket) {
|
||||
$gb = $try_to_create_bucket;
|
||||
} else {
|
||||
$msg = __('Failure: We could not successfully access or create such a bucket.', 'updraftplus').' '.__('Please check your access credentials, and if those are correct then try another bucket name (as another AWS user may already have taken your name).', 'updraftplus');
|
||||
if (isset($s3_error)) $msg .= "\n\n".sprintf(__('The error reported by %s was:', 'updraftplus'), 'S3').' '.$s3_error;
|
||||
return array('e' => 1, 'm' => $msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the new IAM user
|
||||
|
||||
try {
|
||||
$response = $storage->createUser(array('Path' => '/updraftplus/', 'UserName' => $settings_values['newuser']));
|
||||
} catch (Exception $e) {
|
||||
return array('e' => 1, 'm' => sprintf(__('IAM operation failed (%s)', 'updraftplus'), 4).' ('.$e->getMessage().') ('.get_class($e).')');
|
||||
}
|
||||
|
||||
if (403 == $response['code']) {
|
||||
return array('e' => 1, 'm' => __('Authorisation failed (check your credentials)', 'updraftplus'));
|
||||
} elseif (409 == $response['code']) {
|
||||
return array('e' => 1, 'm' => __('Conflict: that user already exists', 'updraftplus'));
|
||||
}
|
||||
|
||||
if (empty($response['User']['UserId']) || empty($response['User']['CreateDate']) || empty($response['User']['UserName'])) {
|
||||
return array('e' => 1, 'm' => sprintf(__('IAM operation failed (%s)', 'updraftplus'), 5)." (".$response['error']['message'].')');
|
||||
}
|
||||
|
||||
$user = $response['User']['UserName'];
|
||||
|
||||
// Add the User to the bucket
|
||||
try {
|
||||
$response = $storage->createAccessKey($user);
|
||||
} catch (Exception $e) {
|
||||
return array('e' => 1, 'm' => __('Operation to create user Access Key failed', 'updraftplus'));
|
||||
}
|
||||
|
||||
if (empty($response['AccessKey']['UserName']) || empty($response['AccessKey']['AccessKeyId']) || empty($response['AccessKey']['SecretAccessKey'])) {
|
||||
return array('e' => 1, 'm' => __('Operation to create user Access Key failed', 'updraftplus').' (2)');
|
||||
}
|
||||
|
||||
$key = $response['AccessKey']['AccessKeyId'];
|
||||
$secret = $response['AccessKey']['SecretAccessKey'];
|
||||
|
||||
// policy document
|
||||
$pol_doc = '{
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:ListBucket",
|
||||
"s3:GetBucketLocation",
|
||||
"s3:ListBucketMultipartUploads"
|
||||
],
|
||||
"Resource": "arn:aws:s3:::'.$bucket.'",
|
||||
"Condition": {}
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:AbortMultipartUpload",';
|
||||
if ($allow_delete) $pol_doc .= '
|
||||
"s3:DeleteObject",
|
||||
"s3:DeleteObjectVersion",';
|
||||
if ($allow_download) $pol_doc .= '
|
||||
"s3:GetObject",
|
||||
"s3:GetObjectAcl",
|
||||
"s3:GetObjectVersion",
|
||||
"s3:GetObjectVersionAcl",';
|
||||
$pol_doc .= '
|
||||
"s3:PutObject",
|
||||
"s3:PutObjectAcl",
|
||||
"s3:PutObjectVersionAcl"
|
||||
],
|
||||
"Resource": "arn:aws:s3:::'.$bucket.'/*",
|
||||
"Condition": {}
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:ListAllMyBuckets",
|
||||
"Resource": "*",
|
||||
"Condition": {}
|
||||
}
|
||||
]
|
||||
}';
|
||||
|
||||
try {
|
||||
$response = $storage->putUserPolicy(array(
|
||||
'UserName' => $user,
|
||||
'PolicyName' => $user.'updraftpolicy',
|
||||
'PolicyDocument' => $pol_doc
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
return array('e' => 1, 'm' => __('Failed to apply User Policy'.$e->getMessage()));
|
||||
}
|
||||
|
||||
if (!empty($response['error'])) {
|
||||
return array('e' => 1, 'm' => __('Failed to apply User Policy', 'updraftplus')." (".$response['error']['message'].')');
|
||||
}
|
||||
|
||||
return array(
|
||||
'e' => 0,
|
||||
'u' => htmlspecialchars($user),
|
||||
'k' => htmlspecialchars($key),
|
||||
's' => htmlspecialchars($secret),
|
||||
'l' => $region,
|
||||
'c' => $bucket,
|
||||
'm' => htmlspecialchars(sprintf(__("Username: %s", 'updraftplus'), $user))."<br>".htmlspecialchars(sprintf(__("Access Key: %s", 'updraftplus'), $key))."<br>".htmlspecialchars(sprintf(__("Secret Key: %s", 'updraftplus'), $secret))
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called both directly, and made available as an action
|
||||
*
|
||||
* @param boolean $include_form_apparatus
|
||||
*/
|
||||
public function s3_print_new_api_user_form($include_form_apparatus = true) {
|
||||
?>
|
||||
<div id="updraft_s3newapiuser_form">
|
||||
<p class="updraft-s3newapiuser-first-para">
|
||||
<em><?php echo esc_html(__('Enter your administrative Amazon S3 access/secret keys (this needs to be a key pair with enough rights to create new users and buckets), and a new (unique) username for the new user and a bucket name.', 'updraftplus').' '.__('These will be used to create a new user and key pair with an IAM policy attached which will only allow it to access the indicated bucket.', 'updraftplus').' '.__('Then, these lower-powered access credentials can be used, instead of storing your administrative keys.', 'updraftplus'));?></em>
|
||||
</p>
|
||||
|
||||
<div id="updraft-s3newapiuser-results"><p></p></div>
|
||||
|
||||
<p class="updraft-s3newapiuser-settings-para">
|
||||
|
||||
<label for="updraft_s3newapiuser_adminaccesskey"><?php esc_html_e('Admin access key', 'updraftplus');?></label> <input type="text" id="updraft_s3newapiuser_adminaccesskey" value="">
|
||||
<label for="updraft_s3newapiuser_adminsecret"><?php esc_html_e('Admin secret key', 'updraftplus');?></label> <input type="text" id="updraft_s3newapiuser_adminsecret" value="">
|
||||
<label for="updraft_s3newapiuser_newuser"><?php esc_html_e("New IAM username", 'updraftplus');?></label> <input type="text" id="updraft_s3newapiuser_newuser" value="">
|
||||
|
||||
<label for="updraft_s3newapiuser_region"><?php esc_html_e('S3 storage region', 'updraftplus');?>:</label>
|
||||
<select id="updraft_s3newapiuser_region">
|
||||
<?php
|
||||
$regions = array(
|
||||
'us-east-1' => __('US East (N. Virginia) (default)', 'updraftplus'),
|
||||
'us-east-2' => __('US East (Ohio)', 'updraftplus'),
|
||||
'us-west-2' => __('US West (Oregon)', 'updraftplus'),
|
||||
'us-west-1' => __('US West (N. California)', 'updraftplus'),
|
||||
'us-gov-west-1' => __('US Government West (restricted)', 'updraftplus'),
|
||||
'ca-central-1' => __('Canada (Central)', 'updraftplus'),
|
||||
'eu-west-1' => __('Europe (Ireland)', 'updraftplus'),
|
||||
'eu-west-2' => __('Europe (London)', 'updraftplus'),
|
||||
'eu-west-3' => __('Europe (Paris)', 'updraftplus'),
|
||||
'eu-central-1' => __('Europe (Frankfurt)', 'updraftplus'),
|
||||
'eu-south-1' => __('Europe (Milan)', 'updraftplus'),
|
||||
'eu-north-1' => __('Europe (Stockholm)', 'updraftplus'),
|
||||
'me-south-1' => __('Middle East (Bahrain)', 'updraftplus'),
|
||||
'af-south-1' => __('Africa (Cape Town)', 'updraftplus'),
|
||||
'ap-northeast-2' => __('Asia Pacific (Seoul)', 'updraftplus'),
|
||||
'ap-southeast-1' => __('Asia Pacific (Singapore)', 'updraftplus'),
|
||||
'ap-southeast-2' => __('Asia Pacific (Sydney)', 'updraftplus'),
|
||||
'ap-south-1' => __('Asia Pacific (Mumbai)', 'updraftplus'),
|
||||
'ap-northeast-1' => __('Asia Pacific (Tokyo)', 'updraftplus'),
|
||||
'ap-northeast-3' => __('Asia Pacific (Osaka-Local) (restricted)', 'updraftplus'),
|
||||
'ap-east-1' => __('Asia Pacific (Hong Kong)', 'updraftplus'),
|
||||
'sa-east-1' => __('South America (São Paulo)', 'updraftplus'),
|
||||
'cn-northwest-1' => __('China (Ningxia) (restricted)', 'updraftplus'),
|
||||
'cn-north-1' => __('China (Beijing) (restricted)', 'updraftplus'),
|
||||
);
|
||||
$selregion = 'us-east-1';
|
||||
foreach ($regions as $reg => $desc) {
|
||||
?>
|
||||
<option <?php if ($selregion == $reg) echo 'selected="selected"'; ?> value="<?php echo esc_attr($reg);?>"><?php echo esc_html($desc); ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<label for="updraft_s3newapiuser_bucket"><?php esc_html_e("S3 bucket", 'updraftplus');?></label><span class="updraft_s3newapiuser_textexplain">s3://</span><input type="text" id="updraft_s3newapiuser_bucket" value="">
|
||||
|
||||
<label aria-label="<?php echo esc_attr(__("Allow download", 'updraftplus').'. '.__('Without this permission, you cannot directly download or restore using UpdraftPlus, and will instead need to visit the AWS website.', 'updraftplus')); ?>" for="updraft_s3newapiuser_allowdownload"><?php esc_html_e("Allow download", 'updraftplus');?></label>
|
||||
<input type="checkbox" id="updraft_s3newapiuser_allowdownload" value="1" checked="checked">
|
||||
<span class="updraft_s3newapiuser_checkboxexplain"><em><?php esc_html_e('Without this permission, you cannot directly download or restore using UpdraftPlus, and will instead need to visit the AWS website.', 'updraftplus');?></em></span>
|
||||
|
||||
<label aria-label="<?php echo esc_attr(__("Allow deletion", 'updraftplus').'. '.__("Without this permission, UpdraftPlus cannot delete backups - you should also set your 'retain' settings very high to prevent seeing deletion errors.", 'updraftplus'));?>" for="updraft_s3newapiuser_allowdelete"><?php esc_html_e("Allow deletion", 'updraftplus');?></label>
|
||||
<input type="checkbox" id="updraft_s3newapiuser_allowdelete" value="1" checked="checked">
|
||||
<span class="updraft_s3newapiuser_checkboxexplain"><em><?php esc_html_e("Without this permission, UpdraftPlus cannot delete backups - you should also set your 'retain' settings very high to prevent seeing deletion errors.", 'updraftplus');?></em></span>
|
||||
|
||||
</p>
|
||||
<?php if ($include_form_apparatus) { ?>
|
||||
<fieldset>
|
||||
<input type="hidden" name="nonce" value="<?php echo esc_attr(wp_create_nonce('updraftplus-credentialtest-nonce'));?>">
|
||||
<input type="hidden" name="action" value="updraft_ajax">
|
||||
<input type="hidden" name="subaction" value="s3_newuser">
|
||||
<input type="hidden" id="updraft_s3newapiuser_instance_id" name="updraft_s3newapiuser_instance_id" value="" />
|
||||
</fieldset>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function admin_footer() {
|
||||
?>
|
||||
<style type="text/css">
|
||||
#updraft_s3newapiuser_form label { float: left; clear:left; width: 170px;}
|
||||
#updraft_s3newapiuser_form input[type="text"], #updraft_s3newapiuser_form select { float: left; width: 310px; }
|
||||
#updraft_s3newapiuser_form input[type="checkbox"] { float: left; }
|
||||
#updraft_s3newapiuser_form p { padding-top:0; clear: left; float: left; }
|
||||
#updraft_s3newapiuser_form .updraft-s3newapiuser-first-para { margin:1px; }
|
||||
#updraft_s3newapiuser_form .updraft-s3newapiuser-settings-para { margin-top:3px; padding-top:0; clear: left; float: left; }
|
||||
#updraft_s3newapiuser_form #updraft-s3newapiuser-results { clear: left; float: left; }
|
||||
#updraft_s3newapiuser_form #updraft-s3newapiuser-results p { margin: 1px 0; padding: 1px 0; }
|
||||
#updraft_s3newapiuser_form .updraft_s3newapiuser_checkboxexplain { width:310px; float:left; }
|
||||
#updraft_s3newapiuser_form .updraft_s3newapiuser_textexplain { float:left; width:30px; position:relative; top:3px; }
|
||||
#updraft_s3newapiuser_form #updraft_s3newapiuser_bucket { width: 280px; }
|
||||
</style>
|
||||
<div id="updraft-s3newapiuser-modal" style="display:none;" title="<?php esc_attr_e('Create new IAM user and S3 bucket', 'updraftplus');?>">
|
||||
<?php $this->s3_print_new_api_user_form(); ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
jQuery(function($) {
|
||||
$('#updraft-navtab-settings-content').on('click', '.updraft_s3_newapiuser', function(e) {
|
||||
e.preventDefault();
|
||||
jQuery('#updraft_s3newapiuser_instance_id').val(jQuery(this).data('instance_id'));
|
||||
$('#updraft-s3newapiuser-modal').dialog('open');
|
||||
});
|
||||
|
||||
var updraft_s3newapiuser_modal_buttons = {};
|
||||
|
||||
updraft_s3newapiuser_modal_buttons[updraftlion.cancel] = function() { $(this).dialog("close"); };
|
||||
updraft_s3newapiuser_modal_buttons[updraftlion.createbutton] = function() {
|
||||
$('#updraft-s3newapiuser-results').html('<p style="color:green">'+updraftlion.trying+'</p>');
|
||||
|
||||
var data = {
|
||||
subsubaction: 'updraft_s3_newuser',
|
||||
adminaccesskey: $('#updraft_s3newapiuser_adminaccesskey').val(),
|
||||
adminsecret: $('#updraft_s3newapiuser_adminsecret').val(),
|
||||
newuser: $('#updraft_s3newapiuser_newuser').val(),
|
||||
bucket: $('#updraft_s3newapiuser_bucket').val(),
|
||||
region: $('#updraft_s3newapiuser_region').val(),
|
||||
useservercerts: $('#updraft_ssl_useservercerts').val(),
|
||||
disableverify: $('#updraft_ssl_disableverify').val(),
|
||||
nossl: $('#updraft_ssl_nossl').val(),
|
||||
allowdelete: $('#updraft_s3newapiuser_allowdelete').is(':checked') ? 1 : 0,
|
||||
allowdownload: $('#updraft_s3newapiuser_allowdownload').is(':checked') ? 1 : 0,
|
||||
};
|
||||
|
||||
updraft_send_command('doaction', data, function(resp, status, response) {
|
||||
if (resp.e == 1) {
|
||||
$('#updraft-s3newapiuser-results').html('<p style="color:red;">'+resp.m+'</p>');
|
||||
} else if (resp.e == 0) {
|
||||
var instance_id = jQuery('#updraft_s3newapiuser_instance_id').val();
|
||||
$('#updraft-s3newapiuser-results').html('<p style="color:green;">'+resp.m+'</p>');
|
||||
$('#updraft_s3_accesskey_'+instance_id).val(resp.k);
|
||||
$('#updraft_s3_secretkey_'+instance_id).val(resp.s);
|
||||
$('#updraft_s3_server_side_encryption_'+instance_id).attr('checked', resp.r);
|
||||
$('#updraft_s3_path_'+instance_id).val(resp.c);
|
||||
|
||||
//Clear Admin credentials
|
||||
$('#updraft_s3newapiuser_adminaccesskey').val("");
|
||||
$('#updraft_s3newapiuser_adminsecret').val("");
|
||||
$('#updraft_s3newapiuser_newuser').val("");
|
||||
$('#updraft_s3newapiuser_bucket').val("");
|
||||
|
||||
//Change link to open dialog to reflect that using IAM user
|
||||
$('#updraft_s3_newapiuser_'+instance_id).html('<?php echo esc_js(__('You are now using a IAM user account to access your bucket.', 'updraftplus')).' <strong>'.esc_js(__('Do remember to save your settings.', 'updraftplus')).'</strong>';?>');
|
||||
|
||||
$('#updraft-s3newapiuser-modal').dialog('close');
|
||||
}
|
||||
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) {
|
||||
console.error(resp.fatal_error_message);
|
||||
$('#updraft-s3newapiuser-results').html('<p style="color:red;">'+resp.fatal_error_message+'</p>');
|
||||
alert(resp.fatal_error_message);
|
||||
} else {
|
||||
var error_message = "updraft_send_command: error: "+status+" ("+error_code+")";
|
||||
console.log(error_message);
|
||||
console.log(response);
|
||||
$('#updraft-s3newapiuser-results').html('<p style="color:red;">'+updraftlion.servererrorcode+'</p>');
|
||||
alert(updraftlion.unexpectedresponse+' '+response);
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
$("#updraft-s3newapiuser-modal").dialog({
|
||||
autoOpen: false, height: 525, width: 555, modal: true,
|
||||
buttons: updraft_s3newapiuser_modal_buttons
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
1211
wp-content/plugins/updraftplus/addons/sftp.php
Normal file
1211
wp-content/plugins/updraftplus/addons/sftp.php
Normal file
File diff suppressed because it is too large
Load Diff
1601
wp-content/plugins/updraftplus/addons/webdav.php
Normal file
1601
wp-content/plugins/updraftplus/addons/webdav.php
Normal file
File diff suppressed because it is too large
Load Diff
1240
wp-content/plugins/updraftplus/addons/wp-cli.php
Normal file
1240
wp-content/plugins/updraftplus/addons/wp-cli.php
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user