first commit
19
wp-content/plugins/updraftplus/SECURITY.md
Normal file
@@ -0,0 +1,19 @@
|
||||
If you believe that you have found a security issue associated with the current release of this plugin, then please report it to the email address with local part security-reports-only and the domain updraftplus.com. If receipt of the email is not acknowledged within 3 working days, then you can try again and also use the inquiry form on the plugin's website.
|
||||
|
||||
Do not send emails on any other subject to this address. They will not be acknowledged, regardless of whether they contain pleas to do otherwise; there are inquiry forms and support forums available which are linked within the plugin and easy to find on the plugin website.
|
||||
|
||||
Please include as much of the information listed below as you can to help us better understand and resolve the issue:
|
||||
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Affected version(s)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* The location of the affected code
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Any log files that are related to this issue (if possible)
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
Thank you!
|
||||
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
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
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
@@ -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
@@ -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
@@ -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
@@ -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
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
@@ -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
@@ -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
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
@@ -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
@@ -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
1601
wp-content/plugins/updraftplus/addons/webdav.php
Normal file
1240
wp-content/plugins/updraftplus/addons/wp-cli.php
Normal file
7303
wp-content/plugins/updraftplus/admin.php
Normal file
4725
wp-content/plugins/updraftplus/backup.php
Normal file
9
wp-content/plugins/updraftplus/branch.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
commit 076f2849886cfc81f3f6b79b581ff85365157a1a
|
||||
Author: David Anderson <DavidAnderson684@users.noreply.github.com>
|
||||
Date: Wed Nov 12 15:00:21 2025 +0000
|
||||
|
||||
update changelog
|
||||
|
||||
changes/2044-restore-connect-to-your-account-link-to-open-the-premium-extensions-tab.txt
|
||||
src/readme.txt
|
||||
branch: 2055-add-product-registration-link-when-product-key-wp_option-present
|
||||
879
wp-content/plugins/updraftplus/central/bootstrap.php
Normal file
@@ -0,0 +1,879 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) die('No direct access.');
|
||||
|
||||
global $updraftcentral_host_plugin;
|
||||
if (!$updraftcentral_host_plugin->is_host_dir_set()) die('No access.');
|
||||
|
||||
// This file is included during plugins_loaded
|
||||
|
||||
// Load the listener class that we rely on to pick up messages
|
||||
if (!class_exists('UpdraftCentral_Listener')) require_once('listener.php');
|
||||
|
||||
// We exit if class already exists. More common if two or more plugins integrated
|
||||
// the same `UpdraftCentral` client folder.
|
||||
if (!class_exists('UpdraftCentral_Main')) :
|
||||
|
||||
class UpdraftCentral_Main {
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
add_action('udrpc_log', array($this, 'udrpc_log'), 10, 3);
|
||||
|
||||
add_action('wp_ajax_updraftcentral_receivepublickey', array($this, 'wp_ajax_updraftcentral_receivepublickey'));
|
||||
add_action('wp_ajax_nopriv_updraftcentral_receivepublickey', array($this, 'wp_ajax_updraftcentral_receivepublickey'));
|
||||
|
||||
// The host plugin's command class is registered in its "plugins_loaded" method (e.g. UpdraftPlus::plugins_loaded()).
|
||||
//
|
||||
// N.B. The new filter "updraftcentral_remotecontrol_command_classes" was introduced on Jan. 2021 and will soon replace the
|
||||
// old filter "updraftplus_remotecontrol_command_classes" (below). This was done in order to synchronize all available filters
|
||||
// and actions related to UpdraftCentral so that we can easily port the UpdraftCentral client code into our other plugins.
|
||||
//
|
||||
// If you happened to use the old filter from any of your projects then you might as well update it with the new filter as the
|
||||
// old filter has already been marked as deprecated, though currently supported as can be seen below but will soon be remove
|
||||
// from this code block.
|
||||
$command_classes = apply_filters('updraftcentral_remotecontrol_command_classes', array(
|
||||
'core' => 'UpdraftCentral_Core_Commands',
|
||||
'updates' => 'UpdraftCentral_Updates_Commands',
|
||||
'users' => 'UpdraftCentral_Users_Commands',
|
||||
'comments' => 'UpdraftCentral_Comments_Commands',
|
||||
'analytics' => 'UpdraftCentral_Analytics_Commands',
|
||||
'plugin' => 'UpdraftCentral_Plugin_Commands',
|
||||
'theme' => 'UpdraftCentral_Theme_Commands',
|
||||
'posts' => 'UpdraftCentral_Posts_Commands',
|
||||
'media' => 'UpdraftCentral_Media_Commands',
|
||||
'pages' => 'UpdraftCentral_Pages_Commands',
|
||||
'backups' => 'UpdraftCentral_Backups_Commands',
|
||||
'rest' => 'UpdraftCentral_REST_API_Access_Commands',
|
||||
'reporting' => 'UpdraftCentral_Reporting_Commands'
|
||||
));
|
||||
|
||||
// N.B. This "updraftplus_remotecontrol_command_classes" filter has been marked as deprecated and will be remove after May 2021.
|
||||
// Please see above code comment for further explanation and its alternative.
|
||||
$command_classes = apply_filters('updraftplus_remotecontrol_command_classes', $command_classes);
|
||||
|
||||
// If nothing was sent, then there is no incoming message, so no need to set up a listener (or CORS request, etc.). This avoids a DB SELECT query on the option below in the case where it didn't get autoloaded, which is the case when there are no keys.
|
||||
if (!empty($_SERVER['REQUEST_METHOD']) && ('GET' == $_SERVER['REQUEST_METHOD'] || 'POST' == $_SERVER['REQUEST_METHOD']) && (empty($_REQUEST['action']) || 'updraft_central' !== $_REQUEST['action']) && empty($_REQUEST['udcentral_action']) && empty($_REQUEST['udrpc_message'])) return;
|
||||
|
||||
// Remote control keys
|
||||
// These are different from the remote send keys, which are set up in the Migrator add-on
|
||||
$our_keys = $this->get_central_localkeys();
|
||||
|
||||
if (is_array($our_keys) && !empty($our_keys)) {
|
||||
new UpdraftCentral_Listener($our_keys, $command_classes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the needed styles and scripts for UpdraftCentral
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_central_scripts() {
|
||||
|
||||
// This is an additional check; the caller is assumed to have already run checks before painting its page in general
|
||||
if (!current_user_can('manage_options')) return;
|
||||
|
||||
global $updraftcentral_host_plugin;
|
||||
$version = $updraftcentral_host_plugin->get_version();
|
||||
|
||||
$enqueue_version = (defined('WP_DEBUG') && WP_DEBUG) ? $version.'.'.time() : $version;
|
||||
$min_or_not = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
|
||||
|
||||
// Fallback to unminified version if the minified version is not found.
|
||||
if (!empty($min_or_not) && !file_exists(UPDRAFTCENTRAL_CLIENT_DIR.'/js/central'.$min_or_not.'.js')) {
|
||||
$min_or_not = '';
|
||||
}
|
||||
|
||||
wp_enqueue_script('updraft-central', UPDRAFTCENTRAL_CLIENT_URL.'/js/central'.$min_or_not.'.js', array(), $enqueue_version);
|
||||
wp_enqueue_style('updraft-central', UPDRAFTCENTRAL_CLIENT_URL.'/css/central'.$min_or_not.'.css', array(), $enqueue_version);
|
||||
|
||||
$localize = array_merge(
|
||||
array(
|
||||
'central_url' => UPDRAFTCENTRAL_CLIENT_URL,
|
||||
'plugin_name' => $updraftcentral_host_plugin->get_plugin_name(),
|
||||
'updraftcentral_request_nonce' => wp_create_nonce('updraftcentral-request-nonce'),
|
||||
),
|
||||
$updraftcentral_host_plugin->translations
|
||||
);
|
||||
|
||||
wp_localize_script('updraft-central', 'uclion', apply_filters('updraftcentral_uclion', $localize));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves current clean url for anchor link where href attribute value is not url (for ex. #div) or empty. Output is not escaped (caller should escape).
|
||||
*
|
||||
* @return String - current clean url
|
||||
*/
|
||||
public function get_current_clean_url() {
|
||||
|
||||
// Within an UpdraftCentral context, there should be no prefix on the anchor link
|
||||
if (defined('UPDRAFTCENTRAL_COMMAND') && UPDRAFTCENTRAL_COMMAND || defined('WP_CLI') && WP_CLI) return '';
|
||||
|
||||
if (defined('DOING_AJAX') && DOING_AJAX && !empty($_SERVER['HTTP_REFERER'])) {
|
||||
$current_url = $_SERVER['HTTP_REFERER'];
|
||||
} else {
|
||||
$url_prefix = is_ssl() ? 'https' : 'http';
|
||||
$host = empty($_SERVER['HTTP_HOST']) ? parse_url(network_site_url(), PHP_URL_HOST) : $_SERVER['HTTP_HOST'];
|
||||
$current_url = $url_prefix."://".$host.wp_unslash($_SERVER['REQUEST_URI']);
|
||||
}
|
||||
$remove_query_args = array('state', 'action', 'oauth_verifier', 'nonce', 'updraftplus_instance', 'access_token', 'user_id', 'updraftplus_googledriveauth');
|
||||
|
||||
$query_string = remove_query_arg($remove_query_args, $current_url);
|
||||
return function_exists('wp_unslash') ? wp_unslash($query_string) : stripslashes_deep($query_string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WordPress version
|
||||
*
|
||||
* @return String - the version
|
||||
*/
|
||||
public function get_wordpress_version() {
|
||||
static $got_wp_version = false;
|
||||
if (!$got_wp_version) {
|
||||
@include(ABSPATH.WPINC.'/version.php');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
|
||||
$got_wp_version = $wp_version;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- The variable is defined inside the ABSPATH.WPINC.'/version.php'.
|
||||
}
|
||||
return $got_wp_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the UpdraftCentral generated keys
|
||||
*
|
||||
* @param Mixed $default default value to return when option is not found
|
||||
*
|
||||
* @return Mixed
|
||||
*/
|
||||
private function get_central_localkeys($default = null) {
|
||||
|
||||
$option = 'updraft_central_localkeys';
|
||||
$ret = get_option($option, $default);
|
||||
return apply_filters('updraftcentral_get_option', $ret, $option, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UpdraftCentral's keys
|
||||
*
|
||||
* @param string $value Specify option value
|
||||
* @param bool $use_cache Whether or not to use the WP options cache
|
||||
* @param string $autoload Whether to autoload (only takes effect on a change of value)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function update_central_localkeys($value, $use_cache = true, $autoload = 'yes') {
|
||||
$option = 'updraft_central_localkeys';
|
||||
|
||||
return update_option($option, apply_filters('updraftcentral_update_option', $value, $option, $use_cache), $autoload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive a new public key in $_GET, and echo a response. Will die() if called.
|
||||
*/
|
||||
public function wp_ajax_updraftcentral_receivepublickey() {
|
||||
global $updraftcentral_host_plugin;
|
||||
|
||||
// The actual nonce check is done in the method below
|
||||
if (empty($_GET['_wpnonce']) || empty($_GET['public_key']) || !isset($_GET['updraft_key_index'])) die;
|
||||
|
||||
$result = $this->receive_public_key();
|
||||
if (!is_array($result) || empty($result['responsetype'])) die;
|
||||
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<title>UpdraftCentral</title>
|
||||
<style>
|
||||
body {text-align: center;font-family: Helvetica,Arial,Lucida,sans-serif;background-color: #A64C1A;color: #FFF;height: 100%;width: 100%;margin: 0;padding: 0;}#main {height: 100%;width: 100%;display: table;}#wrapper {display: table-cell;height: 100%;vertical-align: middle;}h1 {margin-bottom: 5px;}h2 {margin-top: 0;font-size: 22px;color: #FFF;}#btn-close {color: #FFF;font-size: 20px;font-weight: 500;padding: .3em 1em;line-height: 1.7em !important;background-color: transparent;background-size: cover;background-position: 50%;background-repeat: no-repeat;border: 2px solid;border-radius: 3px;-webkit-transition-duration: .2s;transition-duration: .2s;-webkit-transition-property: all !important;transition-property: all !important;text-decoration: none;}#btn-close:hover {background-color: #DE6726;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="wrapper"><img src="<?php echo esc_url(UPDRAFTCENTRAL_CLIENT_URL).'/images/ud-logo.png'; ?>" width="60" /> <h1><?php $updraftcentral_host_plugin->retrieve_show_message('updraftcentral_connection', true); ?></h1><h2><?php echo esc_html(network_site_url()); ?></h2><p>
|
||||
<?php
|
||||
if ('ok' == $result['responsetype']) {
|
||||
$updraftcentral_host_plugin->retrieve_show_message('updraftcentral_connection_successful', true);
|
||||
} else {
|
||||
?>
|
||||
<strong><span id="udc-connect-failed">
|
||||
<?php $updraftcentral_host_plugin->retrieve_show_message('updraftcentral_connection_failed', true); ?>
|
||||
</span></strong><br>
|
||||
<?php
|
||||
switch ($result['code']) {
|
||||
case 'unknown_key':
|
||||
$updraftcentral_host_plugin->retrieve_show_message('unknown_key', true);
|
||||
break;
|
||||
case 'not_logged_in':
|
||||
echo esc_html($updraftcentral_host_plugin->retrieve_show_message('not_logged_in')).' ';
|
||||
$updraftcentral_host_plugin->retrieve_show_message('must_visit_url', true);
|
||||
break;
|
||||
case 'nonce_failure':
|
||||
$updraftcentral_host_plugin->retrieve_show_message('security_check', true);
|
||||
$updraftcentral_host_plugin->retrieve_show_message('must_visit_link', true);
|
||||
break;
|
||||
case 'already_have':
|
||||
$updraftcentral_host_plugin->retrieve_show_message('connection_already_made', true);
|
||||
break;
|
||||
case 'insufficient_privilege':
|
||||
$updraftcentral_host_plugin->retrieve_show_message('insufficient_privilege', true);
|
||||
break;
|
||||
default:
|
||||
echo esc_html(print_r($result, true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
<p><a id="btn-close" href="<?php echo esc_url($this->get_current_clean_url()); ?>" onclick="window.close();"><?php $updraftcentral_host_plugin->retrieve_show_message('close', true); ?></a>
|
||||
</p></div></div>
|
||||
<?php
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks _wpnonce, and if successful, saves the public key found in $_GET
|
||||
*
|
||||
* @return Array - with keys responsetype (can be 'error' or 'ok') and code, indicating whether the parse was successful
|
||||
*/
|
||||
private function receive_public_key() {
|
||||
|
||||
if (!is_user_logged_in()) {
|
||||
return array('responsetype' => 'error', 'code' => 'not_logged_in');
|
||||
}
|
||||
|
||||
if (!wp_verify_nonce($_GET['_wpnonce'], 'updraftcentral_receivepublickey')) return array('responsetype' => 'error', 'code' => 'nonce_failure');
|
||||
|
||||
$updraft_key_index = $_GET['updraft_key_index'];
|
||||
$our_keys = $this->get_central_localkeys();
|
||||
|
||||
if (!is_array($our_keys)) $our_keys = array();
|
||||
|
||||
if (!isset($our_keys[$updraft_key_index])) {
|
||||
return array('responsetype' => 'error', 'code' => 'unknown_key');
|
||||
}
|
||||
|
||||
if (!empty($our_keys[$updraft_key_index]['publickey_remote'])) {
|
||||
return array('responsetype' => 'error', 'code' => 'already_have');
|
||||
}
|
||||
|
||||
$our_keys[$updraft_key_index]['publickey_remote'] = base64_decode(stripslashes($_GET['public_key']));
|
||||
$this->update_central_localkeys($our_keys, true, 'no');
|
||||
|
||||
return array('responsetype' => 'ok', 'code' => 'ok');
|
||||
}
|
||||
|
||||
/**
|
||||
* Action parameters, from udrpc: $message, $level, $this->key_name_indicator, $this->debug, $this
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param string $level Log level
|
||||
* @param string $key_name_indicator This indicates the key name
|
||||
*/
|
||||
public function udrpc_log($message, $level, $key_name_indicator) {
|
||||
|
||||
$udrpc_log = get_site_option('updraftcentral_client_log');
|
||||
if (!is_array($udrpc_log)) $udrpc_log = array();
|
||||
|
||||
$new_item = array(
|
||||
'time' => time(),
|
||||
'level' => $level,
|
||||
'message' => $message,
|
||||
'key_name_indicator' => $key_name_indicator
|
||||
);
|
||||
|
||||
if (!empty($_SERVER['REMOTE_ADDR'])) {
|
||||
$new_item['remote_ip'] = $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
if (!empty($_SERVER['HTTP_USER_AGENT'])) {
|
||||
$new_item['http_user_agent'] = $_SERVER['HTTP_USER_AGENT'];
|
||||
}
|
||||
if (!empty($_SERVER['HTTP_X_SECONDARY_USER_AGENT'])) {
|
||||
$new_item['http_secondary_user_agent'] = $_SERVER['HTTP_X_SECONDARY_USER_AGENT'];
|
||||
}
|
||||
|
||||
$udrpc_log[] = $new_item;
|
||||
|
||||
if (count($udrpc_log) > 50) array_shift($udrpc_log);
|
||||
|
||||
update_site_option('updraftcentral_client_log', $udrpc_log);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete UpdraftCentral Key
|
||||
*
|
||||
* @param array $key_id key_id of UpdraftCentral
|
||||
*
|
||||
* @return array which contains deleted flag and key table. If error, Returns array which contains fatal_error flag and fatal_error_message
|
||||
*/
|
||||
public function delete_key($key_id) {
|
||||
|
||||
$our_keys = $this->get_central_localkeys();
|
||||
if (is_array($key_id) && isset($key_id['key_id'])) {
|
||||
$key_id = $key_id['key_id'];
|
||||
}
|
||||
|
||||
if (!is_array($our_keys)) $our_keys = array();
|
||||
if (isset($our_keys[$key_id])) {
|
||||
unset($our_keys[$key_id]);
|
||||
$this->update_central_localkeys($our_keys);
|
||||
}
|
||||
return array('deleted' => 1, 'keys_table' => $this->get_keys_table());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get UpdraftCentral Log
|
||||
*
|
||||
* @return array which contains log_contents. If error, Returns array which contains fatal_error flag and fatal_error_message
|
||||
*/
|
||||
public function get_log() {
|
||||
|
||||
global $updraftcentral_host_plugin;
|
||||
|
||||
$udrpc_log = get_site_option('updraftcentral_client_log');
|
||||
if (!is_array($udrpc_log)) $udrpc_log = array();
|
||||
|
||||
$log_contents = '';
|
||||
|
||||
// Events are appended to the array in the order they happen. So, reversing the order gets them into most-recent-first order.
|
||||
rsort($udrpc_log);
|
||||
|
||||
if (empty($udrpc_log)) {
|
||||
$log_contents = '<em>'.$updraftcentral_host_plugin->retrieve_show_message('nothing_yet_logged').'</em>';
|
||||
}
|
||||
|
||||
foreach ($udrpc_log as $m) {
|
||||
|
||||
// Skip invalid data
|
||||
if (!isset($m['time'])) continue;
|
||||
|
||||
$time = gmdate('Y-m-d H:i:s O', $m['time']);
|
||||
// $level is not used yet. We could put the message in different colours for different levels, if/when it becomes used.
|
||||
|
||||
$key_name_indicator = empty($m['key_name_indicator']) ? '' : $m['key_name_indicator'];
|
||||
|
||||
$log_contents .= '<span title="'.esc_attr(print_r($m, true)).'">'."$time ";
|
||||
|
||||
if (!empty($m['remote_ip'])) $log_contents .= '['.htmlspecialchars($m['remote_ip']).'] ';
|
||||
|
||||
$log_contents .= "[".htmlspecialchars($key_name_indicator)."] ".htmlspecialchars($m['message'])."</span>\n";
|
||||
}
|
||||
|
||||
return array('log_contents' => $log_contents);
|
||||
|
||||
}
|
||||
|
||||
public function create_key($params) {
|
||||
|
||||
global $updraftcentral_host_plugin;
|
||||
|
||||
// Use the site URL - this means that if the site URL changes, communication ends; which is the case anyway
|
||||
$user = wp_get_current_user();
|
||||
|
||||
if (!is_object($user) || empty($user->ID)) return array('error' => $updraftcentral_host_plugin->retrieve_show_message('insufficient_privilege'));
|
||||
|
||||
if (!current_user_can('manage_options')) return array('error' => $updraftcentral_host_plugin->retrieve_show_message('insufficient_privilege'));
|
||||
|
||||
$where_send = empty($params['where_send']) ? '' : (string) $params['where_send'];
|
||||
|
||||
if ('__updraftpluscom' != $where_send) {
|
||||
$purl = parse_url($where_send);
|
||||
if (empty($purl) || !array($purl) || empty($purl['scheme']) || empty($purl['host'])) return array('error' => $updraftcentral_host_plugin->retrieve_show_message('invalid_url'));
|
||||
}
|
||||
|
||||
// ENT_HTML5 exists only on PHP 5.4+
|
||||
// @codingStandardsIgnoreLine
|
||||
$flags = defined('ENT_HTML5') ? ENT_QUOTES | ENT_HTML5 : ENT_QUOTES;
|
||||
|
||||
$extra_info = array(
|
||||
'user_id' => $user->ID,
|
||||
'user_login' => $user->user_login,
|
||||
'ms_id' => get_current_blog_id(),
|
||||
'site_title' => html_entity_decode(get_bloginfo('name'), $flags),
|
||||
);
|
||||
|
||||
if ($where_send) {
|
||||
$extra_info['mothership'] = $where_send;
|
||||
if (!empty($params['mothership_firewalled'])) {
|
||||
$extra_info['mothership_firewalled'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($params['key_description'])) {
|
||||
$extra_info['name'] = (string) $params['key_description'];
|
||||
}
|
||||
|
||||
$key_size = (empty($params['key_size']) || !is_numeric($params['key_size']) || $params['key_size'] < 512) ? 2048 : (int) $params['key_size'];
|
||||
|
||||
$extra_info['key_size'] = $key_size;
|
||||
|
||||
$created = $this->create_remote_control_key(false, $extra_info, $where_send);
|
||||
|
||||
if (is_array($created)) {
|
||||
$created['keys_table'] = $this->get_keys_table();
|
||||
|
||||
$created['keys_guide'] = '<h2 class="updraftcentral_wizard_success">'. $updraftcentral_host_plugin->retrieve_show_message('updraftcentral_key_created') .'</h2>';
|
||||
|
||||
if ('__updraftpluscom' != $where_send) {
|
||||
$created['keys_guide'] .= '<div class="updraftcentral_wizard_success"><p>'.sprintf($updraftcentral_host_plugin->retrieve_show_message('need_to_copy_key'), '<a href="'.$where_send.'" target="_blank">UpdraftCentral dashboard</a>').'</p><p>'.$updraftcentral_host_plugin->retrieve_show_message('press_add_site_button').'</p><p>'.sprintf($updraftcentral_host_plugin->retrieve_show_message('detailed_instructions'), '<a target="_blank" href="https://teamupdraft.com/documentation/updraftcentral/getting-started/how-to-add-a-site-to-updraftcentral/">teamupdraft.com</a>').'</p></div>';
|
||||
} else {
|
||||
$created['keys_guide'] .= '<div class="updraftcentral_wizard_success"><p>'. sprintf($updraftcentral_host_plugin->retrieve_show_message('control_this_site'), '<a target="_blank" href="https://teamupdraft.com/my-account/updraftcentral/">teamupdraft.com</a>').'</p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
return $created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an index, return the indicator name
|
||||
*
|
||||
* @param String $index
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
private function indicator_name_from_index($index) {
|
||||
return $index.'.central.updraftplus.com';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an RPC object, and sets some defaults on it that we always want
|
||||
*
|
||||
* @param string $indicator_name indicator name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_udrpc($indicator_name = 'migrator.updraftplus.com') {
|
||||
|
||||
global $updraftcentral_host_plugin, $updraftplus;
|
||||
|
||||
if ($updraftplus && is_a($updraftplus, 'UpdraftPlus')) $updraftplus->ensure_phpseclib();
|
||||
|
||||
if (!class_exists('UpdraftPlus_Remote_Communications_V2')) include_once($updraftcentral_host_plugin->get_host_dir().'/vendor/team-updraft/common-libs/src/updraft-rpc/class-udrpc2.php');
|
||||
$ud_rpc = new UpdraftPlus_Remote_Communications_V2($indicator_name);
|
||||
$ud_rpc->set_can_generate(true);
|
||||
|
||||
return $ud_rpc;
|
||||
}
|
||||
|
||||
private function create_remote_control_key($index = false, $extra_info = array(), $post_it = false) {
|
||||
global $updraftcentral_host_plugin;
|
||||
|
||||
$our_keys = $this->get_central_localkeys();
|
||||
if (!is_array($our_keys)) $our_keys = array();
|
||||
|
||||
if (false === $index) {
|
||||
if (empty($our_keys)) {
|
||||
$index = 0;
|
||||
} else {
|
||||
$index = max(array_keys($our_keys))+1;
|
||||
}
|
||||
}
|
||||
|
||||
$name_hash = $index;
|
||||
|
||||
if (isset($our_keys[$name_hash])) {
|
||||
unset($our_keys[$name_hash]);
|
||||
}
|
||||
|
||||
$indicator_name = $this->indicator_name_from_index($name_hash);
|
||||
$ud_rpc = $this->get_udrpc($indicator_name);
|
||||
|
||||
if ('__updraftpluscom' == $post_it) {
|
||||
$post_it = defined('UPDRAFTPLUS_OVERRIDE_UDCOM_DESTINATION') ? UPDRAFTPLUS_OVERRIDE_UDCOM_DESTINATION : 'https://updraftplus.com/?updraftcentral_action=receive_key';
|
||||
$post_it_description = 'UpdraftPlus.Com';
|
||||
} else {
|
||||
$post_it_description = $post_it;
|
||||
}
|
||||
|
||||
// Normally, key generation takes seconds, even on a slow machine. However, some Windows machines appear to have a setup in which it takes a minute or more. And then, if you're on a double-localhost setup on slow hardware - even worse. It doesn't hurt to just raise the maximum execution time.
|
||||
|
||||
if (function_exists('set_time_limit')) @set_time_limit(UPDRAFTCENTRAL_SET_TIME_LIMIT);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
|
||||
|
||||
$key_size = (empty($extra_info['key_size']) || !is_numeric($extra_info['key_size']) || $extra_info['key_size'] < 512) ? 2048 : (int) $extra_info['key_size'];
|
||||
|
||||
if (is_object($ud_rpc) && $ud_rpc->generate_new_keypair($key_size)) {
|
||||
|
||||
if ($post_it && empty($extra_info['mothership_firewalled'])) {
|
||||
|
||||
$p_url = parse_url($post_it);
|
||||
if (is_array($p_url) && !empty($p_url['user'])) {
|
||||
$http_username = $p_url['user'];
|
||||
$http_password = empty($p_url['pass']) ? '' : $p_url['pass'];
|
||||
$post_it = $p_url['scheme'].'://'.$p_url['host'];
|
||||
if (!empty($p_url['port'])) $post_it .= ':'.$p_url['port'];
|
||||
$post_it .= $p_url['path'];
|
||||
if (!empty($p_url['query'])) $post_it .= '?'.$p_url['query'];
|
||||
}
|
||||
|
||||
$post_options = array(
|
||||
'timeout' => 90,
|
||||
'body' => array(
|
||||
'updraftcentral_action' => 'receive_key',
|
||||
'key' => $ud_rpc->get_key_remote()
|
||||
)
|
||||
);
|
||||
|
||||
if (!empty($http_username)) {
|
||||
$post_options['headers'] = array(
|
||||
'Authorization' => 'Basic '.base64_encode($http_username.':'.$http_password)
|
||||
);
|
||||
}
|
||||
|
||||
// This option allows the key to be sent to the other side via a known-secure channel (e.g. http over SSL), rather than potentially allowing it to travel over an unencrypted channel (e.g. http back to the user's browser). As such, if specified, it is compulsory for it to work.
|
||||
|
||||
$updraftcentral_host_plugin->register_wp_http_option_hooks();
|
||||
|
||||
$sent_key = wp_remote_post(
|
||||
$post_it,
|
||||
$post_options
|
||||
);
|
||||
|
||||
$updraftcentral_host_plugin->register_wp_http_option_hooks(false);
|
||||
|
||||
$connection_troubleshooting_url = 'https://updraftplus.com/troubleshooting-updraftcentral-connection-issues/';
|
||||
if (is_wp_error($sent_key) || empty($sent_key)) {
|
||||
|
||||
$err_msg = sprintf($updraftcentral_host_plugin->retrieve_show_message('attempt_to_register_failed'), (string) $post_it_description, $connection_troubleshooting_url);
|
||||
if (is_wp_error($sent_key)) $err_msg .= ' '.$sent_key->get_error_message().' ('.$sent_key->get_error_code().')';
|
||||
return array(
|
||||
'r' => $err_msg
|
||||
);
|
||||
}
|
||||
|
||||
$response = json_decode(wp_remote_retrieve_body($sent_key), true);
|
||||
|
||||
if (!is_array($response) || !isset($response['key_id']) || !isset($response['key_public'])) {
|
||||
return array(
|
||||
'r' => sprintf($updraftcentral_host_plugin->retrieve_show_message('attempt_to_register_failed'), (string) $post_it_description, $connection_troubleshooting_url),
|
||||
'raw' => wp_remote_retrieve_body($sent_key)
|
||||
);
|
||||
}
|
||||
|
||||
$key_hash = hash('sha256', $ud_rpc->get_key_remote());
|
||||
|
||||
$local_bundle = $ud_rpc->get_portable_bundle('base64_with_count', $extra_info, array('key' => array('key_hash' => $key_hash, 'key_id' => $response['key_id'])));
|
||||
|
||||
} elseif ($post_it) {
|
||||
// Don't send; instead, include in the bundle info that the mothership is firewalled; this will then tell the mothership to try the reverse connection instead
|
||||
|
||||
if (is_array($extra_info)) {
|
||||
$extra_info['mothership_firewalled_callback_url'] = wp_nonce_url(admin_url('admin-ajax.php'), 'updraftcentral_receivepublickey');
|
||||
$extra_info['updraft_key_index'] = $index;
|
||||
}
|
||||
|
||||
|
||||
$local_bundle = $ud_rpc->get_portable_bundle('base64_with_count', $extra_info, array('key' => $ud_rpc->get_key_remote()));
|
||||
}
|
||||
|
||||
|
||||
if (isset($extra_info['name'])) {
|
||||
$name = (string) $extra_info['name'];
|
||||
unset($extra_info['name']);
|
||||
} else {
|
||||
$name = 'UpdraftCentral Remote Control';
|
||||
}
|
||||
|
||||
$our_keys[$name_hash] = array(
|
||||
'name' => $name,
|
||||
'key' => $ud_rpc->get_key_local(),
|
||||
'extra_info' => $extra_info,
|
||||
'created' => time(),
|
||||
);
|
||||
// Store the other side's public key
|
||||
if (!empty($response) && is_array($response) && !empty($response['key_public'])) {
|
||||
$our_keys[$name_hash]['publickey_remote'] = $response['key_public'];
|
||||
}
|
||||
$this->update_central_localkeys($our_keys, true, 'no');
|
||||
|
||||
return array(
|
||||
'bundle' => $local_bundle,
|
||||
'r' => $updraftcentral_host_plugin->retrieve_show_message('key_created_successfully').' '.$updraftcentral_host_plugin->retrieve_show_message('copy_paste_key'),
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves and processes UpdraftCentral connection keys data for display
|
||||
*
|
||||
* @return array Formatted connection keys data with user info and metadata
|
||||
*/
|
||||
public function get_connection_keys_data() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$our_keys = $this->get_central_localkeys();
|
||||
if (!is_array($our_keys)) $our_keys = array();
|
||||
|
||||
$keys_data = array();
|
||||
|
||||
foreach ($our_keys as $key_id => $key_data) {
|
||||
if (empty($key_data['extra_info'])) continue;
|
||||
|
||||
$user_id = isset($key_data['extra_info']['user_id']) ? $key_data['extra_info']['user_id'] : 0;
|
||||
$user = get_user_by('id', $user_id);
|
||||
|
||||
$reconstructed_url = '';
|
||||
if (!empty($key_data['extra_info']['mothership'])) {
|
||||
$mothership_url = $key_data['extra_info']['mothership'];
|
||||
if ('__updraftpluscom' == $mothership_url) {
|
||||
$reconstructed_url = 'https://updraftplus.com';
|
||||
} else {
|
||||
$purl = parse_url($mothership_url);
|
||||
$path = empty($purl['path']) ? '' : $purl['path'];
|
||||
$reconstructed_url = $purl['scheme'].'://'.$purl['host'].
|
||||
(!empty($purl['port']) ? ':'.$purl['port'] : '').$path;
|
||||
}
|
||||
}
|
||||
|
||||
$keys_data[$key_id] = array(
|
||||
'name' => isset($key_data['name']) ? $key_data['name'] : '',
|
||||
'user_login' => $user ? $user->user_login : '',
|
||||
'user_email' => $user ? $user->user_email : '',
|
||||
'user_display' => $user ? $user->user_login.' ('.$user->user_email.')' : 'Unknown',
|
||||
'created' => isset($key_data['created']) ? $key_data['created'] : '',
|
||||
'created_formatted' => isset($key_data['created']) ? date_i18n(get_option('date_format').' '.get_option('time_format'), $key_data['created']) : '',
|
||||
'reconstructed_url' => $reconstructed_url,
|
||||
'key_size' => isset($key_data['extra_info']['key_size']) ? $key_data['extra_info']['key_size'] : '',
|
||||
'key_id' => $key_id
|
||||
);
|
||||
}
|
||||
|
||||
return $keys_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML for the keys table
|
||||
*
|
||||
* @param Boolean $echo_instead_of_return Whether the result should be echoed or returned
|
||||
* @return String
|
||||
*/
|
||||
public function get_keys_table($echo_instead_of_return = false) {
|
||||
|
||||
// This is an additional check - it implies requirement for a dashboard context
|
||||
if (!current_user_can('manage_options')) return;
|
||||
|
||||
global $updraftcentral_host_plugin;
|
||||
|
||||
if (!$echo_instead_of_return) ob_start();
|
||||
|
||||
$keys_data = $this->get_connection_keys_data();
|
||||
|
||||
if (empty($keys_data)) {
|
||||
?>
|
||||
<tr><td colspan="2"><em><?php $updraftcentral_host_plugin->retrieve_show_message('no_updraftcentral_dashboards', true); ?></em></td></tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
?>
|
||||
<div id="updraftcentral_keys_content" style="margin: 10px 0;">
|
||||
<?php if (!empty($keys_data)) { ?>
|
||||
<a href="<?php echo esc_url($this->get_current_clean_url()); ?>" class="updraftcentral_keys_show hidden-in-updraftcentral"><?php echo wp_kses_post(sprintf($updraftcentral_host_plugin->retrieve_show_message('manage_keys'), count($keys_data))); ?></a>
|
||||
<?php } ?>
|
||||
<table id="updraftcentral_keys_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align:left;"><?php $updraftcentral_host_plugin->retrieve_show_message('key_description', true); ?></th>
|
||||
<th style="text-align:left;"><?php $updraftcentral_host_plugin->retrieve_show_message('details', true); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
foreach ($keys_data as $key_id => $key) {
|
||||
$user_display = 'Unknown' !== $key['user_display'] ? $key['user_display'] : $updraftcentral_host_plugin->retrieve_show_message('unknown');
|
||||
$reconstructed_url = !empty($key['reconstructed_url']) ? $key['reconstructed_url'] : $updraftcentral_host_plugin->retrieve_show_message('unknown');
|
||||
$reconstructed_url_display = 'https://updraftplus.com' == $reconstructed_url ? 'https://teamupdraft.com' : $reconstructed_url;
|
||||
$key_name_display = 'updraftplus.com' == $key['name'] ? 'teamupdraft.com' : $key['name'];
|
||||
?>
|
||||
<tr class="updraft_debugrow"><td style="vertical-align:top;"><?php echo esc_html($key_name_display.' ('.$key_id.')'); ?></td><td><?php $updraftcentral_host_plugin->retrieve_show_message('access_as_user', true); ?> <?php echo esc_html($user_display); ?> <br> <?php $updraftcentral_host_plugin->retrieve_show_message('public_key_sent', true); ?> <?php echo esc_html($reconstructed_url_display); ?><br>
|
||||
<?php
|
||||
if (!empty($key['created'])) {
|
||||
echo esc_html($updraftcentral_host_plugin->retrieve_show_message('created').' '.$key['created_formatted']).'.';
|
||||
if (!empty($key['key_size'])) {
|
||||
echo ' '.esc_html(sprintf($updraftcentral_host_plugin->retrieve_show_message('key_size'), $key['key_size'])).'.';
|
||||
}
|
||||
?>
|
||||
<br>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<a href="<?php echo esc_url($this->get_current_clean_url()); ?>" data-key_id="<?php echo esc_attr($key_id); ?>" class="updraftcentral_key_delete"><?php $updraftcentral_host_plugin->retrieve_show_message('delete', true); ?></a></td></tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php
|
||||
if (!$echo_instead_of_return) return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return HTML markup for the 'create key' section
|
||||
*
|
||||
* @param Boolean $echo_instead_of_return Whether the result should be echoed or returned
|
||||
* @return String|Void - the HTML
|
||||
*/
|
||||
private function create_key_markup($echo_instead_of_return = false) {
|
||||
global $updraftcentral_host_plugin;
|
||||
|
||||
if (!$echo_instead_of_return) ob_start();
|
||||
?>
|
||||
<div class="create_key_container">
|
||||
<h4 class="updraftcentral_wizard_stage1"> <?php $updraftcentral_host_plugin->retrieve_show_message('connect_to_updraftcentral_dashboard', true); ?></h4>
|
||||
<table style="width: 100%; table-layout:fixed;">
|
||||
<thead></thead>
|
||||
<tbody>
|
||||
<tr class="updraftcentral_wizard_stage1">
|
||||
<td>
|
||||
<div class="updraftcentral_wizard_mothership updraftcentral_wizard_option">
|
||||
<label class="button-primary" tabindex="0">
|
||||
<input checked="checked" type="radio" name="updraftcentral_mothership" id="updraftcentral_mothership_updraftpluscom" style="display: none;">
|
||||
TeamUpdraft.com
|
||||
</label><br>
|
||||
<div><?php echo wp_kses_post(sprintf(esc_html($updraftcentral_host_plugin->retrieve_show_message('in_example')), '<a target="_blank" href="https://teamupdraft.com/my-account/?utm_source=udp-plugin&utm_medium=referral&utm_campaign=paac&utm_content=an-account&utm_creative_format=text">'.esc_html($updraftcentral_host_plugin->retrieve_show_message('an_account')).'</a>')); ?></div>
|
||||
|
||||
</div>
|
||||
<div class="updraftcentral_wizard_self_hosted_stage1 updraftcentral_wizard_option">
|
||||
<label class="button-primary" tabindex="0">
|
||||
<input type="radio" name="updraftcentral_mothership" id="updraftcentral_mothership_other" style="display: none;">
|
||||
<?php $updraftcentral_host_plugin->retrieve_show_message('self_hosted_dashboard', true);?>
|
||||
</label><br>
|
||||
<div><?php echo wp_kses_post(sprintf(esc_html($updraftcentral_host_plugin->retrieve_show_message('website_installed')), '<a target="_blank" href="https://wordpress.org/plugins/updraftcentral/">UpdraftCentral</a>')); ?></div>
|
||||
</div>
|
||||
<div class="updraftcentral_wizard_self_hosted_stage2" style="float:left; clear:left;display:none;">
|
||||
<p style="font-size: 13px;"><?php $updraftcentral_host_plugin->retrieve_show_message('enter_url', true); ?></p>
|
||||
<p style="font-size: 13px;" id="updraftcentral_wizard_stage1_error"></p>
|
||||
<input disabled="disabled" id="updraftcentral_keycreate_mothership" type="text" size="40" placeholder="<?php $updraftcentral_host_plugin->retrieve_show_message('updraftcentral_dashboard_url', true); ?>" value="">
|
||||
<button type="button" class="button button-primary" id="updraftcentral_stage2_go"><?php $updraftcentral_host_plugin->retrieve_show_message('next', true); ?></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="updraft_debugrow updraftcentral_wizard_stage2" style="display: none;">
|
||||
<h4 class="updraftcentral_wizard_stage2" style="display: none;"><?php $updraftcentral_host_plugin->retrieve_show_message('updraftcentral_connection_details', true); ?></h4>
|
||||
<td class="updraftcentral_keycreate_description">
|
||||
<?php $updraftcentral_host_plugin->retrieve_show_message('description', true); ?>:
|
||||
<input id="updraftcentral_keycreate_description" type="text" size="20" placeholder="<?php $updraftcentral_host_plugin->retrieve_show_message('enter_description', true); ?>" value="" >
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="updraft_debugrow updraftcentral_wizard_stage2" style="display: none;">
|
||||
<td>
|
||||
<?php $updraftcentral_host_plugin->retrieve_show_message('encryption_key_size', true); ?>
|
||||
<select style="" id="updraftcentral_keycreate_keysize">
|
||||
<option value="512"><?php echo wp_kses_post(sprintf($updraftcentral_host_plugin->retrieve_show_message('bits').' - '.$updraftcentral_host_plugin->retrieve_show_message('easy_to_break'), '512')); ?></option>
|
||||
<option value="1024"><?php echo wp_kses_post(sprintf($updraftcentral_host_plugin->retrieve_show_message('bits').' - '.$updraftcentral_host_plugin->retrieve_show_message('faster'), '1024')); ?></option>
|
||||
<option value="2048" selected="selected"><?php echo wp_kses_post(sprintf($updraftcentral_host_plugin->retrieve_show_message('bytes').' - '.$updraftcentral_host_plugin->retrieve_show_message('recommended'), '2048')); ?></option>
|
||||
<option value="4096"><?php echo wp_kses_post(sprintf($updraftcentral_host_plugin->retrieve_show_message('bits').' - '.$updraftcentral_host_plugin->retrieve_show_message('slower'), '4096')); ?></option>
|
||||
</select>
|
||||
<br>
|
||||
<div id="updraftcentral_keycreate_mothership_firewalled_container">
|
||||
<label>
|
||||
<input id="updraftcentral_keycreate_mothership_firewalled" type="checkbox">
|
||||
<?php $updraftcentral_host_plugin->retrieve_show_message('use_alternative_method', true); ?>
|
||||
<a href="<?php echo esc_url($this->get_current_clean_url()); ?>" id="updraftcentral_keycreate_altmethod_moreinfo_get"><?php $updraftcentral_host_plugin->retrieve_show_message('more_information', true); ?></a>
|
||||
<p id="updraftcentral_keycreate_altmethod_moreinfo" style="display:none; border: 1px dotted; padding: 3px; margin: 2px 10px 2px 24px;">
|
||||
<em><?php $updraftcentral_host_plugin->retrieve_show_message('this_is_useful', true);?></em>
|
||||
</p>
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="updraft_debugrow updraftcentral_wizard_stage2" style="display: none;">
|
||||
<td>
|
||||
<button style="margin-top: 5px;" type="button" class="button button-primary" id="updraftcentral_keycreate_go"><?php $updraftcentral_host_plugin->retrieve_show_message('create', true); ?></button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="updraft_debugrow updraftcentral_wizard_stage2" style="display: none;">
|
||||
<td>
|
||||
<a id="updraftcentral_stage1_go"><?php $updraftcentral_host_plugin->retrieve_show_message('back', true); ?></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="updraft-copy-modal" title="<?php esc_html_e('Copy to clipboard', 'updraftplus');?>">
|
||||
<p>
|
||||
<?php echo esc_html__('Your web browser prevented the copy operation.', 'updraftplus').' '.'<a href="https://updraftplus.com/faqs/how-do-i-set-clipboard-permissions-for-different-browsers/" target="__blank">'.' '.esc_html__('Follow this link to read about how to set browser permission', 'updraftplus').'</a>'; ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
if (!$echo_instead_of_return) return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get log event viewer mark-up
|
||||
*
|
||||
* @param Boolean $echo_instead_of_return Whether the result should be echoed or returned
|
||||
* @return String - the HTML
|
||||
*/
|
||||
private function get_log_markup($echo_instead_of_return = false) {
|
||||
global $updraftcentral_host_plugin;
|
||||
|
||||
if (!$echo_instead_of_return) ob_start();
|
||||
?>
|
||||
<div id="updraftcentral_view_log_container" style="margin: 10px 0;">
|
||||
<a href="<?php echo esc_url($this->get_current_clean_url()); ?>" id="updraftcentral_view_log"><?php $updraftcentral_host_plugin->retrieve_show_message('view_log_events', true); ?>...</a><br>
|
||||
<pre id="updraftcentral_view_log_contents" style="min-height: 110px; padding: 0 4px;">
|
||||
</pre>
|
||||
</div>
|
||||
<?php
|
||||
if (!$echo_instead_of_return) return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo the debug-tools dashboard HTML. Called by the WP action updraftplus_debugtools_dashboard.
|
||||
*/
|
||||
public function debugtools_dashboard() {
|
||||
|
||||
$this->enqueue_central_scripts();
|
||||
|
||||
global $updraftcentral_host_plugin;
|
||||
|
||||
$including_desc = '';
|
||||
if (function_exists('get_current_screen')) {
|
||||
$screen = get_current_screen();
|
||||
$hosts = apply_filters('updraftcentral_host_plugins', array());
|
||||
$includes = $updraftcentral_host_plugin->retrieve_show_message('including_description');
|
||||
|
||||
foreach ($hosts as $plugin) {
|
||||
if (false !== stripos($screen->id, $plugin)) {
|
||||
$key = str_replace('-', '_', strtolower($plugin)).'_desc';
|
||||
if (isset($includes[$key])) {
|
||||
$including_desc = $includes[$key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$updraftcentral_description = preg_replace('/\s+/', ' ', sprintf($updraftcentral_host_plugin->retrieve_show_message('updraftcentral_description'), $including_desc));
|
||||
?>
|
||||
<div class="advanced_tools updraft_central">
|
||||
<h3><?php $updraftcentral_host_plugin->retrieve_show_message('updraftcentral_remote_control', true); ?></h3>
|
||||
<p>
|
||||
<?php echo esc_html($updraftcentral_description); ?> <a target="_blank" href="https://teamupdraft.com/updraftcentral/?utm_source=udp-plugin&utm_medium=referral&utm_campaign=paac&utm_content=read-more-here&utm_creative_format=text"><?php $updraftcentral_host_plugin->retrieve_show_message('read_more', true); ?></a>
|
||||
</p>
|
||||
<div style="min-height: 310px;" id="updraftcentral_keys">
|
||||
<?php $this->create_key_markup(true); ?>
|
||||
<?php $this->get_keys_table(true); ?>
|
||||
<button style="display: none;" type="button" class="button button-primary" id="updraftcentral_wizard_go"><?php $updraftcentral_host_plugin->retrieve_show_message('create_another_key', true); ?></button>
|
||||
<?php $this->get_log_markup(true); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
||||
|
||||
global $updraftcentral_main;
|
||||
$updraftcentral_main = new UpdraftCentral_Main();
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) die('No direct access.');
|
||||
|
||||
class Automatic_Upgrader_Skin extends Automatic_Upgrader_Skin_Main {
|
||||
|
||||
public function feedback($string, ...$args) { // phpcs:ignore PHPCompatibility.LanguageConstructs.NewLanguageConstructs.t_ellipsisFound, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- spread operator is not supported in PHP < 5.5 but WP 5.3 supports PHP 5.6 minimum
|
||||
parent::updraft_feedback($string);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreFile
|
||||
if (!defined('ABSPATH')) die('No direct access.');
|
||||
|
||||
// Extracted from 4.5.2/wordpress/wp-admin/includes/class-wp-upgrader-skins.php; with the bulk_*() methods added since they are not in the base class on all WP versions.
|
||||
// Needed only on WP < 3.7
|
||||
|
||||
/**
|
||||
* Upgrader Skin for Automatic WordPress Upgrades
|
||||
*
|
||||
* Extracted from 4.5.2/wordpress/wp-admin/includes/class-wp-upgrader-skins.php; with the bulk_*() methods added since they are not in the base class on all WP versions.
|
||||
* Needed only on WP < 3.7
|
||||
*
|
||||
* This skin is designed to be used when no output is intended, all output
|
||||
* is captured and stored for the caller to process and log/email/discard.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Upgrader
|
||||
* @since 3.7.0
|
||||
*/
|
||||
class Automatic_Upgrader_Skin_Main extends WP_Upgrader_Skin {
|
||||
|
||||
protected $messages = array();
|
||||
|
||||
/**
|
||||
* Request filesystem credentials
|
||||
*
|
||||
* @param bool $error Check if there is an error: default is false
|
||||
* @param string $context Context for credentials
|
||||
* @param bool $allow_relaxed_file_ownership Check if relaxed file ownership is allowed
|
||||
* @return bool
|
||||
*/
|
||||
public function request_filesystem_credentials($error = false, $context = '', $allow_relaxed_file_ownership = false) {
|
||||
if ($context) {
|
||||
$this->options['context'] = $context;
|
||||
}
|
||||
// TODO: fix up request_filesystem_credentials(), or split it, to allow us to request a no-output version
|
||||
// This will output a credentials form in event of failure, We don't want that, so just hide with a buffer
|
||||
ob_start();
|
||||
$result = parent::request_filesystem_credentials($error, $context, $allow_relaxed_file_ownership);
|
||||
ob_end_clean();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get update message
|
||||
*
|
||||
* @return array reti=urns an array of messages
|
||||
*/
|
||||
public function get_upgrade_messages() {
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feedback
|
||||
*
|
||||
* @param string|array|WP_Error $data THis is the data to be used for the feedback
|
||||
*/
|
||||
protected function updraft_feedback($data) {
|
||||
if (is_wp_error($data)) {
|
||||
$string = $data->get_error_message();
|
||||
} elseif (is_array($data)) {
|
||||
return;
|
||||
} else {
|
||||
$string = $data;
|
||||
}
|
||||
if (!empty($this->upgrader->strings[$string]))
|
||||
$string = $this->upgrader->strings[$string];
|
||||
|
||||
if (false !== strpos($string, '%')) {
|
||||
$args = func_get_args();
|
||||
$args = array_splice($args, 1);
|
||||
if (!empty($args))
|
||||
$string = vsprintf($string, $args);
|
||||
}
|
||||
|
||||
$string = trim($string);
|
||||
|
||||
// Only allow basic HTML in the messages, as it'll be used in emails/logs rather than direct browser output.
|
||||
$string = wp_kses($string, array(
|
||||
'a' => array(
|
||||
'href' => true
|
||||
),
|
||||
'br' => true,
|
||||
'em' => true,
|
||||
'strong' => true,
|
||||
));
|
||||
|
||||
if (empty($string))
|
||||
return;
|
||||
|
||||
$this->messages[] = $string;
|
||||
}
|
||||
|
||||
public function header() {
|
||||
ob_start();
|
||||
}
|
||||
|
||||
public function footer() {
|
||||
$output = ob_get_clean();
|
||||
if (!empty($output))
|
||||
$this->feedback($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @access public
|
||||
*/
|
||||
public function bulk_header() {}
|
||||
|
||||
public function bulk_footer() {
|
||||
}
|
||||
}
|
||||
|
||||
global $updraftcentral_main;
|
||||
$wp_version = $updraftcentral_main->get_wordpress_version();
|
||||
|
||||
if (version_compare($wp_version, '5.3', '>=')) {
|
||||
if (!class_exists('Automatic_Upgrader_Skin')) require_once(dirname(__FILE__).'/automatic-upgrader-skin-compatibility.php');
|
||||
} else {
|
||||
class Automatic_Upgrader_Skin extends Automatic_Upgrader_Skin_Main {
|
||||
|
||||
public function feedback($string) {
|
||||
parent::updraft_feedback($string);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH') || !defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* The extended class of Plugin_Upgrader that is mostly used for overriding some of the parent methods to short-circuit their native behaviour or to manipulate some data, parameters and/or method arguments
|
||||
*/
|
||||
class UpdraftCentral_Plugin_Upgrader extends Plugin_Upgrader {
|
||||
|
||||
/**
|
||||
* Run an upgrade/installation
|
||||
*
|
||||
* @param Array $options {
|
||||
* Array or string of arguments for upgrading/installing a package.
|
||||
*
|
||||
* @type bool $clear_destination Whether to delete any files already in the destination folder. Default false. (since 2.8.0)
|
||||
* }
|
||||
*
|
||||
* @return Array|False|WP_Error The result from self::install_package() on success, otherwise a WP_Error,
|
||||
* or false if unable to connect to the filesystem.
|
||||
*/
|
||||
public function run($options) {
|
||||
$options['clear_destination'] = true; // force overwritting the existing one, in case WP < 5.5.0 is in use where "overwrite_package" parameter doesn't exist
|
||||
return parent::run($options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The extended class of Plugin_Upgrader that is mostly used for overriding some of the parent methods to short-circuit their native behaviour or to manipulate some data, parameters and/or method arguments
|
||||
*/
|
||||
class UpdraftCentral_Theme_Upgrader extends Theme_Upgrader {
|
||||
|
||||
/**
|
||||
* Run an upgrade/installation
|
||||
*
|
||||
* @param Array $options {
|
||||
* Array or string of arguments for upgrading/installing a package.
|
||||
*
|
||||
* @type bool $clear_destination Whether to delete any files already in the destination folder. Default false. (since 2.8.0)
|
||||
* }
|
||||
*
|
||||
* @return Array|False|WP_Error The result from self::install_package() on success, otherwise a WP_Error,
|
||||
* or false if unable to connect to the filesystem.
|
||||
*/
|
||||
public function run($options) {
|
||||
$options['clear_destination'] = true; // force overwritting the existing one, in case WP < 5.5.0 is in use where "overwrite_package" parameter doesn't exist
|
||||
return parent::run($options);
|
||||
}
|
||||
}
|
||||
366
wp-content/plugins/updraftplus/central/commands.php
Normal file
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* - A container for all the RPC commands implemented. Commands map exactly onto method names (and hence this class should not implement anything else, beyond the constructor, and private methods)
|
||||
* - Return format is array('response' => (string - a code), 'data' => (mixed));
|
||||
*
|
||||
* RPC commands are not allowed to begin with an underscore. So, any private methods can be prefixed with an underscore.
|
||||
*/
|
||||
abstract class UpdraftCentral_Commands {
|
||||
|
||||
protected $rc;
|
||||
|
||||
protected $ud;
|
||||
|
||||
protected $installed_data;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $rc
|
||||
*/
|
||||
public function __construct($rc) {
|
||||
$this->rc = $rc;
|
||||
global $updraftplus;
|
||||
$this->ud = $updraftplus;
|
||||
$this->installed_data = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Include a file or files from wp-admin/includes
|
||||
*/
|
||||
final protected function _admin_include() {
|
||||
$files = func_get_args();
|
||||
foreach ($files as $file) {
|
||||
include_once(ABSPATH.'/wp-admin/includes/'.$file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Include a file or files from wp-includes
|
||||
*/
|
||||
final protected function _frontend_include() {
|
||||
$files = func_get_args();
|
||||
foreach ($files as $file) {
|
||||
include_once(ABSPATH.WPINC.'/'.$file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a response in the expected format
|
||||
*
|
||||
* @param Mixed $data
|
||||
* @param String $code
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
final protected function _response($data = null, $code = 'rpcok') {
|
||||
return array(
|
||||
'response' => $code,
|
||||
'data' => $data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an error in the expected format
|
||||
*
|
||||
* @param String $code
|
||||
* @param Mixed $data
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
final protected function _generic_error_response($code = 'central_unspecified', $data = null) {
|
||||
return $this->_response(
|
||||
array(
|
||||
'code' => $code,
|
||||
'data' => $data
|
||||
),
|
||||
'rpcerror'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a backup and a security credentials is required for the given request
|
||||
*
|
||||
* @param array $dir The directory location to check
|
||||
* @return array
|
||||
*/
|
||||
final protected function _get_backup_credentials_settings($dir) {
|
||||
|
||||
// Do we need to ask the user for filesystem credentials? when installing and/or deleting items in the given directory
|
||||
$filesystem_method = get_filesystem_method(array(), $dir);
|
||||
|
||||
ob_start();
|
||||
$filesystem_credentials_are_stored = request_filesystem_credentials(site_url(), $filesystem_method);
|
||||
ob_end_clean();
|
||||
$request_filesystem_credentials = ('direct' != $filesystem_method && !$filesystem_credentials_are_stored);
|
||||
|
||||
// Do we need to execute a backup process before installing/managing items
|
||||
$automatic_backups = (class_exists('UpdraftPlus_Options') && class_exists('UpdraftPlus_Addon_Autobackup') && UpdraftPlus_Options::get_updraft_option('updraft_autobackup_default')) ? true : false;
|
||||
|
||||
return array(
|
||||
'request_filesystem_credentials' => $request_filesystem_credentials,
|
||||
'automatic_backups' => $automatic_backups
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the information of the currently installed item (e.g. plugin or theme) through filter
|
||||
*
|
||||
* @param bool $response Indicates whether the installation was a success or failure
|
||||
* @param array $args Extra argument for the hook
|
||||
* @param array $data Contains paths used and other relevant information regarding the file
|
||||
* @return array
|
||||
*/
|
||||
final public function get_install_data($response, $args, $data) {
|
||||
|
||||
if ($response) {
|
||||
switch ($args['type']) {
|
||||
case 'plugin':
|
||||
$plugin_data = get_plugins('/'.$data['destination_name']);
|
||||
if (!empty($plugin_data)) {
|
||||
$info = reset($plugin_data);
|
||||
$key = key($plugin_data);
|
||||
|
||||
$info['slug'] = $data['destination_name'].'/'.$key;
|
||||
$this->installed_data = $info;
|
||||
}
|
||||
break;
|
||||
case 'theme':
|
||||
$theme = wp_get_theme($data['destination_name']);
|
||||
if ($theme->exists()) {
|
||||
// Minimalistic info here, if you need to add additional information
|
||||
// you can add them here. For now, the "Name" and "slug" fields will suffice
|
||||
// in the succeeding process.
|
||||
$this->installed_data = array(
|
||||
'Name' => $theme->get('Name'),
|
||||
'slug' => $data['destination_name'],
|
||||
'template' => $theme->get_template()
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs and activates either a plugin or theme through zip file upload
|
||||
*
|
||||
* @param array $params Parameter array containing information pertaining the currently uploaded item
|
||||
* @param string $type Indicates whether this current process is intended for a 'plugin' or a 'theme' item
|
||||
* @return array
|
||||
*/
|
||||
final protected function process_chunk_upload($params, $type) {
|
||||
global $updraftcentral_host_plugin, $updraftcentral_main;
|
||||
|
||||
if (!in_array($type, array('plugin', 'theme'))) {
|
||||
return $this->_generic_error_response('upload_type_not_supported');
|
||||
}
|
||||
|
||||
$permission_error = false;
|
||||
if ('plugin' === $type) {
|
||||
if (!current_user_can('install_plugins') || !current_user_can('activate_plugins')) $permission_error = true;
|
||||
} else {
|
||||
if (!current_user_can('install_themes') || !current_user_can('switch_themes')) $permission_error = true;
|
||||
}
|
||||
|
||||
if ($permission_error) {
|
||||
return $this->_generic_error_response($type.'_insufficient_permission');
|
||||
}
|
||||
|
||||
// Pull any available and writable directory where we can store
|
||||
// our data/file temporarily before running the installation process.
|
||||
$upload_dir = untrailingslashit(get_temp_dir());
|
||||
if (!is_writable($upload_dir)) {
|
||||
$upload_dir = WP_CONTENT_DIR.'/upgrade';
|
||||
|
||||
if (!is_dir($upload_dir)) {
|
||||
$wp_dir = wp_upload_dir();
|
||||
if (!empty($wp_dir['basedir'])) $upload_dir = $wp_dir['basedir'];
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't found any writable directory to temporarily store our file then
|
||||
// we bail and send an error back to the caller.
|
||||
if (!is_dir($upload_dir) || !is_writable($upload_dir)) {
|
||||
return $this->_generic_error_response('upload_dir_not_available');
|
||||
}
|
||||
|
||||
// Preloads the submitted credentials to the global $_POST variable
|
||||
if (!empty($params) && isset($params['filesystem_credentials'])) {
|
||||
parse_str($params['filesystem_credentials'], $filesystem_credentials);
|
||||
if (is_array($filesystem_credentials)) {
|
||||
foreach ($filesystem_credentials as $key => $value) {
|
||||
// Put them into $_POST, which is where request_filesystem_credentials() checks for them.
|
||||
$_POST[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save uploaded file
|
||||
$filename = basename($params['filename']).md5(get_home_url());
|
||||
$is_chunked = false;
|
||||
|
||||
if (isset($params['chunks']) && 1 < (int) $params['chunks']) {
|
||||
$filename .= '.part';
|
||||
$is_chunked = true;
|
||||
}
|
||||
|
||||
if (!$is_chunked || ($is_chunked && isset($params['chunk']) && 0 === (int) $params['chunk'])) {
|
||||
// if it's not a chunk upload or if it's a chunk upload operation and the current chunk variable is zero, then it means a new upload operation has just begun therefore we should remove previous left-over file (if any and due to error during the previous upload of the same file), because it can lead to a corrupt/invalid zip file (we use file_put_contents a few lines below with FILE_APPEND attribute)
|
||||
if (file_exists($upload_dir.'/'.$filename) && !unlink($upload_dir.'/'.$filename)) return $this->_generic_error_response('unable_to_delete_existing_file');
|
||||
}
|
||||
|
||||
if (empty($params['data'])) {
|
||||
return $this->_generic_error_response('data_empty_or_invalid');
|
||||
}
|
||||
|
||||
$result = file_put_contents($upload_dir.'/'.$filename, base64_decode($params['data']), FILE_APPEND | LOCK_EX);
|
||||
|
||||
if (false === $result) {
|
||||
return $this->_generic_error_response('unable_to_write_content');
|
||||
}
|
||||
|
||||
// Set $install_now to true for single upload and for the last chunk of a multi-chunks upload process
|
||||
$install_now = true;
|
||||
|
||||
if ($is_chunked) {
|
||||
if ($params['chunk'] == (int) $params['chunks'] - 1) {
|
||||
// If this is the last chunk of the request, then we're going to restore the
|
||||
// original filename of the file (without the '.part') since our upload is now complete.
|
||||
$orig_filename = basename($filename, '.part');
|
||||
$success = rename($upload_dir.'/'.$filename, $upload_dir.'/'.$orig_filename);
|
||||
|
||||
// If renaming the file was successful then restore the original name and override the $filename variable.
|
||||
// Overriding the $filename variable makes it easy for us to use the same variable for both
|
||||
// non-chunked and chunked zip file for the installation process.
|
||||
if ($success) {
|
||||
$filename = $orig_filename;
|
||||
} else {
|
||||
return $this->_generic_error_response('unable_to_rename_file');
|
||||
}
|
||||
} else {
|
||||
// Bypass installation for now since we're waiting for the last chunk to arrive
|
||||
// to complete the uploading of the zip file.
|
||||
$install_now = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Everything is already good (upload completed), thus, we proceed with the installation
|
||||
if ($install_now) {
|
||||
|
||||
// We have successfully uploaded the zip file in this location with its original filename intact.
|
||||
$zip_filepath = $upload_dir.'/'.$filename;
|
||||
|
||||
// Making sure that the file does actually exists, since we've just run through
|
||||
// a renaming process above.
|
||||
if (file_exists($zip_filepath)) {
|
||||
add_filter('upgrader_post_install', array($this, 'get_install_data'), 10, 3);
|
||||
|
||||
// WP < 3.7
|
||||
if (!class_exists('Automatic_Upgrader_Skin')) include_once(UPDRAFTCENTRAL_CLIENT_DIR.'/classes/class-automatic-upgrader-skin.php');
|
||||
require_once(UPDRAFTCENTRAL_CLIENT_DIR.'/classes/class-updraftcentral-wp-upgrader.php');
|
||||
|
||||
$skin = new Automatic_Upgrader_Skin();
|
||||
$upgrader = ('plugin' === $type) ? new UpdraftCentral_Plugin_Upgrader($skin) : new UpdraftCentral_Theme_Upgrader($skin);
|
||||
|
||||
$install_result = $upgrader->install($zip_filepath);
|
||||
remove_filter('upgrader_post_install', array($this, 'get_install_data'), 10, 3);
|
||||
|
||||
// Remove zip file on success and on error (cleanup)
|
||||
if ($install_result || is_null($install_result) || is_wp_error($install_result)) {
|
||||
@unlink($zip_filepath);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist.
|
||||
}
|
||||
|
||||
if (false === $install_result || is_wp_error($install_result)) {
|
||||
$message = $updraftcentral_host_plugin->retrieve_show_message('unable_to_connect');
|
||||
if (is_wp_error($install_result)) $message = $install_result->get_error_message();
|
||||
|
||||
return $this->_generic_error_response($type.'_install_failed', array('message' => $message));
|
||||
} else {
|
||||
// Pull installed data
|
||||
$data = $this->installed_data;
|
||||
|
||||
// For WP 3.4 the intended filter hook isn't working or not available
|
||||
// so we're going to pull the data manually.
|
||||
if ($install_result && empty($data)) {
|
||||
$result = $this->get_install_data($install_result, array('type' => $type), $skin->result);
|
||||
if ($result) {
|
||||
// Getting the installed data one more time after manually calling
|
||||
// the "get_install_data" function.
|
||||
$data = $this->installed_data;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($data)) {
|
||||
// Activate item if set
|
||||
$is_active = ('plugin' === $type) ? is_plugin_active($data['slug']) : ((wp_get_theme()->get('Name') === $data['Name']) ? true : false);
|
||||
|
||||
if ((bool) $params['activate'] && !$is_active) {
|
||||
if ('plugin' === $type) {
|
||||
if (is_multisite()) {
|
||||
$activate = activate_plugin($data['slug'], '', true);
|
||||
} else {
|
||||
$activate = activate_plugin($data['slug']);
|
||||
}
|
||||
} else {
|
||||
// In order to make it compatible with older versions of switch_theme which takes two
|
||||
// arguments we're going to pass two arguments instead of one. Latest versions have backward
|
||||
// compatibility so it's safe to do it this way.
|
||||
switch_theme($data['template'], $data['slug']);
|
||||
$activate = (wp_get_theme()->get_stylesheet() === $data['slug']) ? true : false;
|
||||
}
|
||||
|
||||
if (false === $activate || is_wp_error($activate)) {
|
||||
$wp_version = $updraftcentral_main->get_wordpress_version();
|
||||
|
||||
$message = is_wp_error($activate) ? array('message' => $activate->get_error_message()) : array('message' => sprintf($updraftcentral_host_plugin->retrieve_show_message('unable_to_activate'), $type, $type, $wp_version));
|
||||
return $this->_generic_error_response('unable_to_activate_'.$type, $message);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_response(
|
||||
array(
|
||||
'installed' => true,
|
||||
'installed_data' => $data,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (is_wp_error($skin->result)) {
|
||||
$code = $skin->result->get_error_code();
|
||||
$message = $skin->result->get_error_message();
|
||||
|
||||
$error_data = $skin->result->get_error_data($code);
|
||||
if (!empty($error_data)) {
|
||||
if (is_array($error_data)) $error_data = json_encode($error_data);
|
||||
|
||||
$message .= ' '.$error_data;
|
||||
}
|
||||
|
||||
return $this->_generic_error_response($code, $message);
|
||||
} else {
|
||||
return $this->_response(
|
||||
array(
|
||||
'installed' => false,
|
||||
'message' => sprintf($updraftcentral_host_plugin->retrieve_show_message('unable_to_install'), $type, $type, $type, $type, 'wp-content/'.$type.'s'),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Returning response to a chunk requests while still processing and
|
||||
// completing the file upload process. If we don't return a positive response
|
||||
// for every chunk requests then the caller will assumed an error has occurred
|
||||
// and will eventually stop the upload process.
|
||||
return $this->_response(array('in_progress' => true));
|
||||
}
|
||||
}
|
||||
}
|
||||
2
wp-content/plugins/updraftplus/central/css/central-2-25-9.min.css
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.updraftcentral_wizard_option{width:45%;float:left;text-align:center}.updraftcentral_wizard_option label{margin-bottom:8px}#updraftcentral_keys_table{display:none}.create_key_container{border:1px solid;border-radius:4px;padding:0 0 6px 6px;margin-bottom:8px}.updraftcentral-subheading{font-size:14px;margin-top:-10px;margin-bottom:20px}.updraftcentral-data-consent{font-size:13px;margin-bottom:10px}#updraftcentral_keys_table{width:100%}#updraftcentral_stage1_go{cursor:pointer}.updraftcentral_wizard_option>div{margin-top:5px}#updraftcentral_keys_table td{line-height:1.4em}#updraftcentral_keys_table .updraftcentral_key_delete{display:inline-block;margin-top:4px;margin-bottom:4px}#updraftcentral_keys_table .updraft_debugrow td{min-width:auto !important}
|
||||
/*# sourceMappingURL=central-2-25-9.min.css.map */
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["central/css/central.css"],"names":[],"mappings":"AAAA;CACC,UAAU;CACV,WAAW;CACX,kBAAkB;AACnB;;AAEA;CACC,kBAAkB;AACnB;;AAEA;CACC,aAAa;AACd;;AAEA;CACC,iBAAiB;CACjB,kBAAkB;CAClB,oBAAoB;CACpB,kBAAkB;AACnB;;AAEA;CACC,eAAe;CACf,iBAAiB;CACjB,mBAAmB;AACpB;;AAEA;CACC,eAAe;CACf,mBAAmB;AACpB;;AAEA;CACC,WAAW;AACZ;;AAEA;CACC,eAAe;AAChB;;AAEA;CACC,eAAe;AAChB;;AAEA;CACC,kBAAkB;AACnB;;AAEA;CACC,qBAAqB;CACrB,eAAe;CACf,kBAAkB;AACnB;;AAEA;CACC,0BAA0B;AAC3B","file":"central-2-25-9.min.css","sourcesContent":[".updraftcentral_wizard_option {\n\twidth: 45%;\n\tfloat: left;\n\ttext-align: center;\n}\n\n.updraftcentral_wizard_option label {\n\tmargin-bottom: 8px;\n}\n\n#updraftcentral_keys_table {\n\tdisplay: none;\n}\n\n.create_key_container {\n\tborder: 1px solid;\n\tborder-radius: 4px;\n\tpadding: 0 0 6px 6px;\n\tmargin-bottom: 8px;\n}\n\n.updraftcentral-subheading {\n\tfont-size: 14px;\n\tmargin-top: -10px;\n\tmargin-bottom: 20px;\n}\n\n.updraftcentral-data-consent {\n\tfont-size: 13px;\n\tmargin-bottom: 10px;\n}\n\n#updraftcentral_keys_table {\n\twidth: 100%;\n}\n\n#updraftcentral_stage1_go {\n\tcursor: pointer;\n}\n\n.updraftcentral_wizard_option > div {\n\tmargin-top: 5px;\n}\n\n#updraftcentral_keys_table td {\n\tline-height: 1.4em;\n}\n\n#updraftcentral_keys_table .updraftcentral_key_delete {\n\tdisplay: inline-block;\n\tmargin-top: 4px;\n\tmargin-bottom: 4px;\n}\n\n#updraftcentral_keys_table .updraft_debugrow td {\n\tmin-width: auto !important;\n}\n"]}
|
||||
57
wp-content/plugins/updraftplus/central/css/central.css
Normal file
@@ -0,0 +1,57 @@
|
||||
.updraftcentral_wizard_option {
|
||||
width: 45%;
|
||||
float: left;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.updraftcentral_wizard_option label {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#updraftcentral_keys_table {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.create_key_container {
|
||||
border: 1px solid;
|
||||
border-radius: 4px;
|
||||
padding: 0 0 6px 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.updraftcentral-subheading {
|
||||
font-size: 14px;
|
||||
margin-top: -10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.updraftcentral-data-consent {
|
||||
font-size: 13px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#updraftcentral_keys_table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#updraftcentral_stage1_go {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.updraftcentral_wizard_option > div {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#updraftcentral_keys_table td {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
#updraftcentral_keys_table .updraftcentral_key_delete {
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
#updraftcentral_keys_table .updraft_debugrow td {
|
||||
min-width: auto !important;
|
||||
}
|
||||
81
wp-content/plugins/updraftplus/central/factory.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) die('No direct access.');
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_SET_TIME_LIMIT')) define('UPDRAFTCENTRAL_SET_TIME_LIMIT', 900);
|
||||
|
||||
// We bypass the class declaration if the class already existed. This usually happens if two or more
|
||||
// plugins integrated the same `UpdraftCentral` client folder.
|
||||
if (!class_exists('UpdraftCentral_Factory')) :
|
||||
|
||||
/**
|
||||
* Returns an instance of the host plugin class where the UpdraftCentral "central" folder is being
|
||||
* integrated.
|
||||
*/
|
||||
class UpdraftCentral_Factory {
|
||||
|
||||
/**
|
||||
* Creates a host plugin instance
|
||||
*
|
||||
* @return object|null
|
||||
*/
|
||||
public static function create_host() {
|
||||
|
||||
// All other plugins that wish to integrate the "central" libraries into their
|
||||
// codebase must use this filter (see WP-Optimize plugin as an example).
|
||||
$hosts = apply_filters('updraftcentral_host_plugins', array());
|
||||
|
||||
// If $hosts is empty then we return null
|
||||
if (empty($hosts)) return null;
|
||||
|
||||
// N.B. If multiple host plugins (e.g. updraftplus, wp-optimize, etc.) are currently
|
||||
// active then we only select one to handle all incoming UpdraftCentral requests for
|
||||
// this website in order to avoid conflicts and confusion especially when tracing or
|
||||
// debugging issues.
|
||||
//
|
||||
// You will know which plugin is currently serving and handling the request by checking
|
||||
// the "get_plugin_name" method of the global variable "$updraftcentral_host_plugin"
|
||||
// (e.g. $updraftcentral_host_plugin->get_plugin_name())
|
||||
//
|
||||
// N.B. You can add additional host plugins here. Just make sure that you will create
|
||||
// a host class for that particular plugin (see central/wp-optimize.php as an example).
|
||||
$mapped_classes = array(
|
||||
'updraftplus' => 'UpdraftPlus_Host',
|
||||
'wp-optimize' => 'WP_Optimize_Host',
|
||||
);
|
||||
|
||||
$path = $host_class = '';
|
||||
foreach ($hosts as $plugin) {
|
||||
// Make sure that we have a registered host class with a valid file that exist
|
||||
$host_file = dirname(__FILE__).'/'.$plugin.'.php';
|
||||
if (isset($mapped_classes[$plugin]) && file_exists($host_file)) {
|
||||
$path = $host_file;
|
||||
$host_class = $mapped_classes[$plugin];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The host file was not found under this plugin thus, we let the other plugins
|
||||
// create or build the host plugin (global) variable instead.
|
||||
if (empty($path)) return null;
|
||||
|
||||
if (!class_exists($host_class)) include_once($path);
|
||||
|
||||
// Re-check host class once again just to make sure that we have the desired
|
||||
// class loaded before calling its instance method
|
||||
if (class_exists($host_class)) {
|
||||
return call_user_func(array($host_class, 'instance'));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
||||
|
||||
global $updraftcentral_host_plugin;
|
||||
$updraftcentral_host_plugin = UpdraftCentral_Factory::create_host();
|
||||
|
||||
if ($updraftcentral_host_plugin) {
|
||||
$updraftcentral_host_plugin->load_updraftcentral();
|
||||
}
|
||||
312
wp-content/plugins/updraftplus/central/host.php
Normal file
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) die('No direct access.');
|
||||
|
||||
/**
|
||||
* The template definition for UpdraftCentral host
|
||||
*/
|
||||
abstract class UpdraftCentral_Host {
|
||||
|
||||
public $plugin_name;
|
||||
|
||||
public $translations;
|
||||
|
||||
public $error_reporting_stop_when_logged = false;
|
||||
|
||||
public $no_deprecation_warnings = false;
|
||||
|
||||
private $jobdata;
|
||||
|
||||
abstract protected function load_updraftcentral();
|
||||
abstract protected function is_host_dir_set();
|
||||
abstract protected function get_host_dir();
|
||||
abstract protected function get_version();
|
||||
abstract public function log($line, $level = 'notice', $uniq_id = false);
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action('wp_ajax_updraft_central_ajax', array($this, 'updraft_central_ajax_handler'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plugin name associated with this host class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_plugin_name() {
|
||||
return $this->plugin_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or shows a message from the translations collection based on its identifier key
|
||||
*
|
||||
* @param string $key The ID of the the message
|
||||
* @param bool $echo Indicate whether the message is to be shown directly (echoed) or just for retrieval
|
||||
*
|
||||
* @return string/void
|
||||
*/
|
||||
public function retrieve_show_message($key, $echo = false) {
|
||||
if (empty($key) || !isset($this->translations[$key])) return '';
|
||||
|
||||
if ($echo) {
|
||||
echo esc_html($this->translations[$key]);
|
||||
return;
|
||||
}
|
||||
|
||||
return $this->translations[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a section to a designated area primarily used for generating UpdraftCentral keys
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function debugtools_dashboard() {
|
||||
if (!current_user_can('manage_options')) return;
|
||||
|
||||
global $updraftcentral_main;
|
||||
|
||||
if (!class_exists('UpdraftCentral_Main')) {
|
||||
if (defined('UPDRAFTCENTRAL_CLIENT_DIR') && file_exists(UPDRAFTCENTRAL_CLIENT_DIR.'/bootstrap.php')) {
|
||||
include_once(UPDRAFTCENTRAL_CLIENT_DIR.'/bootstrap.php');
|
||||
$updraftcentral_main = new UpdraftCentral_Main();
|
||||
}
|
||||
}
|
||||
|
||||
if ($updraftcentral_main) {
|
||||
$updraftcentral_main->debugtools_dashboard();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current user can perform key control AJAX actions
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function current_user_can_ajax() {
|
||||
return current_user_can('manage_options');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles ajax requests coming from the section or area generated by the
|
||||
* "debugtools_dashboard" method (below)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function updraft_central_ajax_handler() {
|
||||
global $updraftcentral_main;
|
||||
|
||||
$nonce = empty($_REQUEST['nonce']) ? '' : $_REQUEST['nonce'];
|
||||
if (empty($nonce) || !wp_verify_nonce($nonce, 'updraftcentral-request-nonce') || !$this->current_user_can_ajax() || empty($_REQUEST['subaction'])) die('Security check');
|
||||
|
||||
if (is_a($updraftcentral_main, 'UpdraftCentral_Main')) {
|
||||
|
||||
$subaction = $_REQUEST['subaction'];
|
||||
if ($this->is_action_whitelisted($subaction) && is_callable(array($updraftcentral_main, $subaction))) {
|
||||
|
||||
// Undo WP's slashing of POST data
|
||||
$data = $this->wp_unslash($_POST);
|
||||
|
||||
// TODO: Once all commands come through here and through updraft_send_command(), the data should always come from this attribute (once updraft_send_command() is modified appropriately).
|
||||
if (isset($data['action_data'])) $data = $data['action_data'];
|
||||
try {
|
||||
|
||||
$results = call_user_func(array($updraftcentral_main, $subaction), $data);
|
||||
} catch (Exception $e) {
|
||||
$log_message = 'PHP Fatal Exception error ('.get_class($e).') has occurred during '.$subaction.' subaction. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
|
||||
error_log($log_message);
|
||||
echo json_encode(array(
|
||||
'fatal_error' => true,
|
||||
'fatal_error_message' => $log_message
|
||||
));
|
||||
die;
|
||||
// @codingStandardsIgnoreLine
|
||||
} catch (Error $e) {
|
||||
$log_message = 'PHP Fatal error ('.get_class($e).') has occurred during '.$subaction.' subaction. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
|
||||
error_log($log_message);
|
||||
echo json_encode(array(
|
||||
'fatal_error' => true,
|
||||
'fatal_error_message' => $log_message
|
||||
));
|
||||
die;
|
||||
}
|
||||
if (is_wp_error($results)) {
|
||||
$results = array(
|
||||
'result' => false,
|
||||
'error_code' => $results->get_error_code(),
|
||||
'error_message' => $results->get_error_message(),
|
||||
'error_data' => $results->get_error_data(),
|
||||
);
|
||||
}
|
||||
|
||||
if (is_string($results)) {
|
||||
// A handful of legacy methods, and some which are directly the source for iframes, for which JSON is not appropriate.
|
||||
echo $results; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- We don't escape here to avoid double escaping and keep the HTML code needed by the receiver of this request.
|
||||
} else {
|
||||
echo json_encode($results);
|
||||
}
|
||||
die;
|
||||
}
|
||||
}
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies whether the submitted action is valid and allowed for execution over AJAX
|
||||
*
|
||||
* @param string $action The action to execute
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_action_whitelisted($action) {
|
||||
$allowed_actions = array('delete_key', 'get_log', 'create_key');
|
||||
return in_array($action, $allowed_actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the filter used by UpdraftCentral to log errors or certain events
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_logline_filter() {
|
||||
return 'updraftcentral_logline';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an RPC object, and sets some defaults on it that we always want
|
||||
*
|
||||
* @param string $indicator_name indicator name
|
||||
* @return array
|
||||
*/
|
||||
public function get_udrpc($indicator_name = 'migrator.updraftplus.com') {
|
||||
global $updraftplus;
|
||||
if ($updraftplus && is_a($updraftplus, 'UpdraftPlus')) $updraftplus->ensure_phpseclib();
|
||||
|
||||
if (!class_exists('UpdraftPlus_Remote_Communications_V2')) include_once($this->get_host_dir().'/vendor/team-updraft/common-libs/src/updraft-rpc/class-udrpc2.php');
|
||||
$ud_rpc = new UpdraftPlus_Remote_Communications_V2($indicator_name);
|
||||
$ud_rpc->set_can_generate(true);
|
||||
return $ud_rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Noop method.
|
||||
* Depending on the host plugin this method may or may not be used.
|
||||
*
|
||||
* N.B. The UpdraftPlus plugin is using and overriding this method in its host file.
|
||||
*
|
||||
* @param boolean $register Indicate whether to add or remote filter hooks
|
||||
* @ignore
|
||||
*/
|
||||
// @codingStandardsIgnoreLine
|
||||
public function register_wp_http_option_hooks($register = true) {}
|
||||
|
||||
/**
|
||||
* Remove slashes from a string or array of strings.
|
||||
*
|
||||
* The function wp_unslash() is WP 3.6+, so therefore we have a compatibility method here
|
||||
*
|
||||
* @param String|Array $value String or array of strings to unslash.
|
||||
* @return String|Array Unslashed $value
|
||||
*/
|
||||
public function wp_unslash($value) {
|
||||
return function_exists('wp_unslash') ? wp_unslash($value) : stripslashes_deep($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a log line based from the PHP error information
|
||||
*
|
||||
* @param Integer $errno Error number
|
||||
* @param String $errstr Error string
|
||||
* @param String $errfile Error file
|
||||
* @param String $errline Line number where the error occurred
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
public function php_error_to_logline($errno, $errstr, $errfile, $errline) {
|
||||
switch ($errno) {
|
||||
case 1:
|
||||
$e_type = 'E_ERROR';
|
||||
break;
|
||||
case 2:
|
||||
$e_type = 'E_WARNING';
|
||||
break;
|
||||
case 4:
|
||||
$e_type = 'E_PARSE';
|
||||
break;
|
||||
case 8:
|
||||
$e_type = 'E_NOTICE';
|
||||
break;
|
||||
case 16:
|
||||
$e_type = 'E_CORE_ERROR';
|
||||
break;
|
||||
case 32:
|
||||
$e_type = 'E_CORE_WARNING';
|
||||
break;
|
||||
case 64:
|
||||
$e_type = 'E_COMPILE_ERROR';
|
||||
break;
|
||||
case 128:
|
||||
$e_type = 'E_COMPILE_WARNING';
|
||||
break;
|
||||
case 256:
|
||||
$e_type = 'E_USER_ERROR';
|
||||
break;
|
||||
case 512:
|
||||
$e_type = 'E_USER_WARNING';
|
||||
break;
|
||||
case 1024:
|
||||
$e_type = 'E_USER_NOTICE';
|
||||
break;
|
||||
case 2048:
|
||||
$e_type = 'E_STRICT';
|
||||
break;
|
||||
case 4096:
|
||||
$e_type = 'E_RECOVERABLE_ERROR';
|
||||
break;
|
||||
case 8192:
|
||||
$e_type = 'E_DEPRECATED';
|
||||
break;
|
||||
case 16384:
|
||||
$e_type = 'E_USER_DEPRECATED';
|
||||
break;
|
||||
case 30719:
|
||||
$e_type = 'E_ALL';
|
||||
break;
|
||||
default:
|
||||
$e_type = "E_UNKNOWN ($errno)";
|
||||
break;
|
||||
}
|
||||
|
||||
if (false !== stripos($errstr, 'table which is not valid in this version of Gravity Forms')) return false;
|
||||
|
||||
if (!is_string($errstr)) $errstr = serialize($errstr);
|
||||
|
||||
if (0 === strpos($errfile, ABSPATH)) $errfile = substr($errfile, strlen(ABSPATH));
|
||||
|
||||
if ('E_DEPRECATED' == $e_type && !empty($this->no_deprecation_warnings)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return "PHP event: code $e_type: $errstr (line $errline, $errfile)";
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP error handler
|
||||
*
|
||||
* @param Integer $errno Error number
|
||||
* @param String $errstr Error string
|
||||
* @param String $errfile Error file
|
||||
* @param String $errline Line number where the error occurred
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function php_error($errno, $errstr, $errfile, $errline) {
|
||||
if (0 == error_reporting()) return true;
|
||||
$logline = $this->php_error_to_logline($errno, $errstr, $errfile, $errline);
|
||||
if (false !== $logline) $this->log($logline, 'notice', 'php_event');
|
||||
// Pass it up the chain
|
||||
return $this->error_reporting_stop_when_logged;
|
||||
}
|
||||
}
|
||||
BIN
wp-content/plugins/updraftplus/central/images/ud-logo.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
1
wp-content/plugins/updraftplus/central/js/central-2-25-9.min.js
vendored
Normal file
436
wp-content/plugins/updraftplus/central/js/central.js
Normal file
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
* Send an action over AJAX. A wrapper around jQuery.ajax. In future, all consumers can be reviewed to simplify some of the options, where there is historical cruft.
|
||||
* N.B. updraft_iframe_modal() below uses the AJAX URL for the iframe's src attribute
|
||||
*
|
||||
* @param {string} action - the action to send
|
||||
* @param {*} data - data to send
|
||||
* @param {Function} callback - will be called with the results
|
||||
* @param {object} options -further options. Relevant properties include:
|
||||
* - [json_parse=true] - whether to JSON parse the results
|
||||
* - [alert_on_error=true] - whether to show an alert box if there was a problem (otherwise, suppress it)
|
||||
* - [action='updraft_ajax'] - what to send as the action parameter on the AJAX request (N.B. action parameter to this function goes as the 'subaction' parameter on the AJAX request)
|
||||
* - [nonce=updraft_credentialtest_nonce] - the nonce value to send.
|
||||
* - [nonce_key='nonce'] - the key value for the nonce field
|
||||
* - [timeout=null] - set a timeout after this number of seconds (or if null, none is set)
|
||||
* - [async=true] - control whether the request is asynchronous (almost always wanted) or blocking (would need to have a specific reason)
|
||||
* - [type='POST'] - GET or POST
|
||||
*/
|
||||
function updraftcentral_send_command(action, data, callback, options) {
|
||||
|
||||
default_options = {
|
||||
json_parse: true,
|
||||
alert_on_error: true,
|
||||
action: 'updraft_central_ajax',
|
||||
nonce_key: 'nonce',
|
||||
timeout: null,
|
||||
async: true,
|
||||
type: 'POST'
|
||||
}
|
||||
|
||||
if ('undefined' !== typeof uclion.updraftcentral_request_nonce && uclion.updraftcentral_request_nonce) {
|
||||
default_options.nonce = uclion.updraftcentral_request_nonce;
|
||||
}
|
||||
|
||||
if ('undefined' === typeof options) options = {};
|
||||
|
||||
for (var opt in default_options) {
|
||||
if (!options.hasOwnProperty(opt)) { options[opt] = default_options[opt]; }
|
||||
}
|
||||
|
||||
var ajax_data = {
|
||||
action: options.action,
|
||||
subaction: action,
|
||||
};
|
||||
|
||||
ajax_data[options.nonce_key] = options.nonce;
|
||||
ajax_data.action_data = data;
|
||||
|
||||
var ajax_opts = {
|
||||
type: options.type,
|
||||
url: ajaxurl,
|
||||
data: ajax_data,
|
||||
success: function(response, status) {
|
||||
if (options.json_parse) {
|
||||
try {
|
||||
var resp = central_parse_json(response);
|
||||
} catch (e) {
|
||||
if ('function' == typeof options.error_callback) {
|
||||
return options.error_callback(response, e, 502, resp);
|
||||
} else {
|
||||
console.log(e);
|
||||
console.log(response);
|
||||
if (options.alert_on_error) { alert(uclion.unexpectedresponse+' '+response); }
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (resp.hasOwnProperty('fatal_error')) {
|
||||
if ('function' == typeof options.error_callback) {
|
||||
// 500 is internal server error code
|
||||
return options.error_callback(response, status, 500, resp);
|
||||
} else {
|
||||
console.error(resp.fatal_error_message);
|
||||
if (options.alert_on_error) { alert(resp.fatal_error_message); }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ('function' == typeof callback) callback(resp, status, response);
|
||||
} else {
|
||||
if ('function' == typeof callback) callback(response, status);
|
||||
}
|
||||
},
|
||||
error: function(response, status, error_code) {
|
||||
if ('function' == typeof options.error_callback) {
|
||||
options.error_callback(response, status, error_code);
|
||||
} else {
|
||||
console.log("updraftcentral_send_command: error: "+status+" ("+error_code+")");
|
||||
console.log(response);
|
||||
}
|
||||
},
|
||||
dataType: 'text',
|
||||
async: options.async
|
||||
};
|
||||
|
||||
if (null != options.timeout) { ajax_opts.timeout = options.timeout; }
|
||||
|
||||
jQuery.ajax(ajax_opts);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSON string, including automatically detecting unwanted extra input and skipping it
|
||||
*
|
||||
* @param {string} json_mix_str - JSON string which need to parse and convert to object
|
||||
* @param {boolean} analyse - if true, then the return format will contain information on the parsing, and parsing will skip attempting to JSON.parse() the entire string (will begin with trying to locate the actual JSON)
|
||||
*
|
||||
* @throws SyntaxError|String (including passing on what JSON.parse may throw) if a parsing error occurs.
|
||||
*
|
||||
* @returns Mixed parsed JSON object. Will only return if parsing is successful (otherwise, will throw). If analyse is true, then will rather return an object with properties (mixed)parsed, (integer)json_start_pos and (integer)json_end_pos
|
||||
*/
|
||||
function central_parse_json(json_mix_str, analyse) {
|
||||
|
||||
analyse = ('undefined' === typeof analyse) ? false : true;
|
||||
|
||||
// Just try it - i.e. the 'default' case where things work (which can include extra whitespace/line-feeds, and simple strings, etc.).
|
||||
if (!analyse) {
|
||||
try {
|
||||
var result = JSON.parse(json_mix_str);
|
||||
return result;
|
||||
} catch (e) {
|
||||
console.log(uclion.plugin_name+': Exception when trying to parse JSON (1) - will attempt to fix/re-parse based upon first/last curly brackets');
|
||||
console.log(json_mix_str);
|
||||
}
|
||||
}
|
||||
|
||||
var json_start_pos = json_mix_str.indexOf('{');
|
||||
var json_last_pos = json_mix_str.lastIndexOf('}');
|
||||
|
||||
// Case where some php notice may be added after or before json string
|
||||
if (json_start_pos > -1 && json_last_pos > -1) {
|
||||
var json_str = json_mix_str.slice(json_start_pos, json_last_pos + 1);
|
||||
try {
|
||||
var parsed = JSON.parse(json_str);
|
||||
if (!analyse) { console.log(uclion.plugin_name+': JSON re-parse successful'); }
|
||||
return analyse ? { parsed: parsed, json_start_pos: json_start_pos, json_last_pos: json_last_pos + 1 } : parsed;
|
||||
} catch (e) {
|
||||
console.log(uclion.plugin_name+': Exception when trying to parse JSON (2) - will attempt to fix/re-parse based upon bracket counting');
|
||||
|
||||
var cursor = json_start_pos;
|
||||
var open_count = 0;
|
||||
var last_character = '';
|
||||
var inside_string = false;
|
||||
|
||||
// Don't mistake this for a real JSON parser. Its aim is to improve the odds in real-world cases seen, not to arrive at universal perfection.
|
||||
while ((open_count > 0 || cursor == json_start_pos) && cursor <= json_last_pos) {
|
||||
|
||||
var current_character = json_mix_str.charAt(cursor);
|
||||
|
||||
if (!inside_string && '{' == current_character) {
|
||||
open_count++;
|
||||
} else if (!inside_string && '}' == current_character) {
|
||||
open_count--;
|
||||
} else if ('"' == current_character && '\\' != last_character) {
|
||||
inside_string = inside_string ? false : true;
|
||||
}
|
||||
|
||||
last_character = current_character;
|
||||
cursor++;
|
||||
}
|
||||
console.log("Started at cursor="+json_start_pos+", ended at cursor="+cursor+" with result following:");
|
||||
console.log(json_mix_str.substring(json_start_pos, cursor));
|
||||
|
||||
try {
|
||||
var parsed = JSON.parse(json_mix_str.substring(json_start_pos, cursor));
|
||||
console.log(uclion.plugin_name+': JSON re-parse successful');
|
||||
return analyse ? { parsed: parsed, json_start_pos: json_start_pos, json_last_pos: cursor } : parsed;
|
||||
} catch (e) {
|
||||
// Throw it again, so that our function works just like JSON.parse() in its behaviour.
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw uclion.plugin_name+": could not parse the JSON";
|
||||
|
||||
}
|
||||
|
||||
jQuery(function($) {
|
||||
$('#updraftcentral_keys').on('click', 'a.updraftcentral_keys_show', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).remove();
|
||||
$('#updraftcentral_keys_table').slideDown();
|
||||
});
|
||||
|
||||
$('#updraftcentral_keycreate_altmethod_moreinfo_get').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).remove();
|
||||
$('#updraftcentral_keycreate_altmethod_moreinfo').slideDown();
|
||||
});
|
||||
|
||||
function updraftcentral_keys_setupform(on_page_load) {
|
||||
var is_other = jQuery('#updraftcentral_mothership_other').is(':checked') ? true : false;
|
||||
if (is_other) {
|
||||
jQuery('#updraftcentral_keycreate_mothership').prop('disabled', false);
|
||||
if (on_page_load) {
|
||||
jQuery('#updraftcentral_keycreate_mothership_firewalled_container').show();
|
||||
} else {
|
||||
jQuery('.updraftcentral_wizard_self_hosted_stage2').show();
|
||||
jQuery('#updraftcentral_keycreate_mothership_firewalled_container').slideDown();
|
||||
jQuery('#updraftcentral_keycreate_mothership').trigger('focus');
|
||||
}
|
||||
} else {
|
||||
jQuery('#updraftcentral_keycreate_mothership').prop('disabled', true);
|
||||
if (!on_page_load) {
|
||||
jQuery('.updraftcentral_wizard_self_hosted_stage2').hide();
|
||||
updraftcentral_stage2_go();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updraftcentral_stage2_go() {
|
||||
// Reset the error message before we continue
|
||||
jQuery('#updraftcentral_wizard_stage1_error').text('');
|
||||
|
||||
var host = '';
|
||||
|
||||
if (jQuery('#updraftcentral_mothership_updraftpluscom').is(':checked')) {
|
||||
jQuery('.updraftcentral_keycreate_description').hide();
|
||||
host = 'updraftplus.com';
|
||||
} else if (jQuery('#updraftcentral_mothership_other').is(':checked')) {
|
||||
jQuery('.updraftcentral_keycreate_description').show();
|
||||
var mothership = jQuery('#updraftcentral_keycreate_mothership').val();
|
||||
if ('' == mothership) {
|
||||
jQuery('#updraftcentral_wizard_stage1_error').text(uclion.updraftcentral_wizard_empty_url);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var url = new URL(mothership);
|
||||
host = url.hostname;
|
||||
} catch (e) {
|
||||
// Try and grab the host name a different way if it failed because of no URL object (e.g. Firefox version 25 and below).
|
||||
if ('undefined' === typeof URL) {
|
||||
host = jQuery('<a>').prop('href', mothership).prop('hostname');
|
||||
}
|
||||
if (!host || 'undefined' !== typeof URL) {
|
||||
jQuery('#updraftcentral_wizard_stage1_error').text(uclion.updraftcentral_wizard_invalid_url);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jQuery('#updraftcentral_keycreate_description').val(host);
|
||||
|
||||
jQuery('.updraftcentral_wizard_stage1').hide();
|
||||
jQuery('.updraftcentral_wizard_stage2').show();
|
||||
}
|
||||
|
||||
jQuery('#updraftcentral_keys').on('click', 'input[type="radio"]', function() {
|
||||
updraftcentral_keys_setupform(false);
|
||||
});
|
||||
// Initial setup (for browsers, e.g. Firefox, that remember form selection state but not DOM state, which can leave an inconsistent state)
|
||||
updraftcentral_keys_setupform(true);
|
||||
|
||||
jQuery('#updraftcentral_keys').on('click', '#updraftcentral_view_log', function(e) {
|
||||
e.preventDefault();
|
||||
jQuery('#updraftcentral_view_log_container').block({ message: '<div style="margin: 8px; font-size:150%;"><img src="'+uclion.central_url+'/images/udlogo-rotating.gif" height="80" width="80" style="padding-bottom:10px;"><br>'+uclion.fetching+'</div>'});
|
||||
try {
|
||||
updraftcentral_send_command('get_log', null, function(response) {
|
||||
jQuery('#updraftcentral_view_log_container').unblock();
|
||||
if (response.hasOwnProperty('log_contents')) {
|
||||
jQuery('#updraftcentral_view_log_contents').html('<div style="border:1px solid;padding: 2px;max-height: 400px; overflow-y:scroll;">'+response.log_contents+'</div>');
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
jQuery('#updraftcentral_view_log_container').unblock();
|
||||
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) {
|
||||
console.error(resp.fatal_error_message);
|
||||
alert(resp.fatal_error_message);
|
||||
} else {
|
||||
var error_message = "updraftcentral_send_command: error: "+status+" ("+error_code+")";
|
||||
console.log(error_message);
|
||||
alert(error_message);
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
jQuery('#updraft_central_key').html();
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
|
||||
// UpdraftCentral
|
||||
jQuery('#updraftcentral_keys').on('click', '#updraftcentral_wizard_go', function(e) {
|
||||
jQuery('#updraftcentral_wizard_go').hide();
|
||||
jQuery('.updraftcentral_wizard_success').remove();
|
||||
jQuery('.create_key_container').show();
|
||||
});
|
||||
|
||||
jQuery('#updraftcentral_keys').on('click', '#updraftcentral_stage1_go', function(e) {
|
||||
e.preventDefault();
|
||||
jQuery('.updraftcentral_wizard_stage2').hide();
|
||||
jQuery('.updraftcentral_wizard_stage1').show();
|
||||
});
|
||||
|
||||
jQuery('#updraftcentral_keys').on('click', '#updraftcentral_stage2_go', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
updraftcentral_stage2_go();
|
||||
});
|
||||
|
||||
jQuery('#updraftcentral_keys').on('click', '#updraftcentral_keycreate_go', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var is_other = jQuery('#updraftcentral_mothership_other').is(':checked') ? true : false;
|
||||
|
||||
var key_description = jQuery('#updraftcentral_keycreate_description').val();
|
||||
var key_size = jQuery('#updraftcentral_keycreate_keysize').val();
|
||||
|
||||
var where_send = '__updraftpluscom';
|
||||
|
||||
data = {
|
||||
key_description: key_description,
|
||||
key_size: key_size,
|
||||
};
|
||||
|
||||
if (is_other) {
|
||||
where_send = jQuery('#updraftcentral_keycreate_mothership').val();
|
||||
if (where_send.substring(0, 4) != 'http') {
|
||||
alert(uclion.enter_mothership_url);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
data.mothership_firewalled = jQuery('#updraftcentral_keycreate_mothership_firewalled').is(':checked') ? 1 : 0;
|
||||
data.where_send = where_send;
|
||||
|
||||
jQuery('.create_key_container').hide();
|
||||
jQuery('.updraftcentral_wizard_stage1').show();
|
||||
jQuery('.updraftcentral_wizard_stage2').hide();
|
||||
|
||||
jQuery('#updraftcentral_keys').block({ message: '<div style="margin: 8px; font-size:150%;"><img src="'+uclion.central_url+'/images/udlogo-rotating.gif" height="80" width="80" style="padding-bottom:10px;"><br>'+uclion.creating_please_allow+'</div>'});
|
||||
|
||||
try {
|
||||
updraftcentral_send_command('create_key', data, function(resp) {
|
||||
jQuery('#updraftcentral_keys').unblock();
|
||||
try {
|
||||
if (resp.hasOwnProperty('error')) {
|
||||
alert(resp.error);
|
||||
console.log(resp);
|
||||
return;
|
||||
}
|
||||
alert(resp.r);
|
||||
|
||||
if (resp.hasOwnProperty('bundle') && resp.hasOwnProperty('keys_guide')) {
|
||||
jQuery('#updraftcentral_keys_content').html(resp.keys_guide);
|
||||
jQuery('#updraftcentral_keys_content').append('<div class="updraftcentral_wizard_success">'+resp.r+'<br><textarea id="updraftcentral-key" onclick="this.select();" style="width:620px; height:165px; word-wrap:break-word; border: 1px solid #aaa; border-radius: 3px; padding:4px;">'+resp.bundle+'</textarea><button id="updraftplus-copy" class="button button-secondary" style="display: block;">'+uclion.copy_to_clipboard+'</button></div>');
|
||||
} else {
|
||||
console.log(resp);
|
||||
}
|
||||
|
||||
if (resp.hasOwnProperty('keys_table')) {
|
||||
jQuery('#updraftcentral_keys_content').append(resp.keys_table);
|
||||
}
|
||||
|
||||
jQuery('#updraftcentral_wizard_go').show();
|
||||
|
||||
} catch (err) {
|
||||
alert(uclion.unexpectedresponse+' '+response);
|
||||
console.log(err);
|
||||
}
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
jQuery('#updraftcentral_keys').unblock();
|
||||
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) {
|
||||
console.error(resp.fatal_error_message);
|
||||
alert(resp.fatal_error_message);
|
||||
} else {
|
||||
var error_message = "updraftcentral_send_command: error: "+status+" ("+error_code+")";
|
||||
console.log(error_message);
|
||||
alert(error_message);
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
jQuery('#updraft_central_key').html();
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var updraft_copy_modal_buttons = {};
|
||||
updraft_copy_modal_buttons[updraftlion.close] = function() {
|
||||
jQuery(this).dialog("close");
|
||||
};
|
||||
|
||||
jQuery("#updraft-copy-modal").dialog({
|
||||
autoOpen: false,
|
||||
resizeOnWindowResize: true,
|
||||
scrollWithViewport: true,
|
||||
resizeAccordingToViewport: true,
|
||||
modal: true,
|
||||
buttons: updraft_copy_modal_buttons,
|
||||
});
|
||||
|
||||
jQuery('#updraftcentral_keys_content').on('click', '#updraftplus-copy', function(e) {
|
||||
e.preventDefault();
|
||||
var ele = jQuery('#updraftcentral-key');
|
||||
if (ele[0].value) {
|
||||
navigator.clipboard.writeText(ele[0].value).then(function() {
|
||||
alert(uclion.key_copied);
|
||||
}, function(err) {
|
||||
jQuery('#updraft-copy-modal').dialog('open');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
jQuery('#updraftcentral_keys').on('click', '.updraftcentral_key_delete', function(e) {
|
||||
e.preventDefault();
|
||||
var key_id = jQuery(this).data('key_id');
|
||||
if ('undefined' == typeof key_id) {
|
||||
console.log("UpdraftPlus: .updraftcentral_key_delete clicked, but no key ID found");
|
||||
return;
|
||||
}
|
||||
|
||||
jQuery('#updraftcentral_keys').block({ message: '<div style="margin: 8px; font-size:150%;"><img src="'+uclion.central_url+'/images/udlogo-rotating.gif" height="80" width="80" style="padding-bottom:10px;"><br>'+uclion.deleting+'</div>'});
|
||||
|
||||
updraftcentral_send_command('delete_key', { key_id: key_id }, function(response) {
|
||||
jQuery('#updraftcentral_keys').unblock();
|
||||
if (response.hasOwnProperty('keys_table')) {
|
||||
jQuery('#updraftcentral_keys_content').html(response.keys_table);
|
||||
}
|
||||
}, { error_callback: function(response, status, error_code, resp) {
|
||||
jQuery('#updraftcentral_keys').unblock();
|
||||
if (typeof resp !== 'undefined' && resp.hasOwnProperty('fatal_error')) {
|
||||
console.error(resp.fatal_error_message);
|
||||
alert(resp.fatal_error_message);
|
||||
} else {
|
||||
var error_message = "updraftcentral_send_command: error: "+status+" ("+error_code+")";
|
||||
console.log(error_message);
|
||||
alert(error_message);
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
372
wp-content/plugins/updraftplus/central/listener.php
Normal file
@@ -0,0 +1,372 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* This class is the basic glue between the lower-level Remote Communications (RPC) class in UpdraftCentral, and the host plugin. It does not contain actual commands themselves; the class names to use for actual commands are passed in as a parameter to the constructor.
|
||||
*/
|
||||
class UpdraftCentral_Listener {
|
||||
|
||||
public $udrpc_version;
|
||||
|
||||
private $host = null;
|
||||
|
||||
private $receivers = array();
|
||||
|
||||
private $extra_info = array();
|
||||
|
||||
private $php_events = array();
|
||||
|
||||
private $commands = array();
|
||||
|
||||
private $current_udrpc = null;
|
||||
|
||||
private $command_classes;
|
||||
|
||||
private $auto_logged_in_cookie;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param Array $keys - keys to set up listeners for
|
||||
* @param Array $command_classes - commands
|
||||
*/
|
||||
public function __construct($keys = array(), $command_classes = array()) {
|
||||
global $updraftcentral_host_plugin;
|
||||
$this->host = $updraftcentral_host_plugin;
|
||||
|
||||
// It seems impossible for this condition to result in a return; but it seems Plesk can do something odd within the control panel that causes a problem - see HS#6276
|
||||
if (!is_a($this->host, 'UpdraftCentral_Host')) return;
|
||||
|
||||
$this->command_classes = $command_classes;
|
||||
|
||||
foreach ($keys as $name_hash => $key) {
|
||||
// publickey_remote isn't necessarily set yet, depending on the key exchange method
|
||||
if (!is_array($key) || empty($key['extra_info']) || empty($key['publickey_remote'])) continue;
|
||||
$indicator = $name_hash.'.central.updraftplus.com';
|
||||
$ud_rpc = $this->host->get_udrpc($indicator);
|
||||
$this->udrpc_version = $ud_rpc->version;
|
||||
|
||||
// Only turn this on if you are comfortable with potentially anything appearing in your PHP error log
|
||||
if (defined('UPDRAFTCENTRAL_UDRPC_FORCE_DEBUG') && UPDRAFTCENTRAL_UDRPC_FORCE_DEBUG) $ud_rpc->set_debug(true);
|
||||
|
||||
$this->receivers[$indicator] = $ud_rpc;
|
||||
$this->extra_info[$indicator] = isset($key['extra_info']) ? $key['extra_info'] : null;
|
||||
$ud_rpc->set_key_local($key['key']);
|
||||
$ud_rpc->set_key_remote($key['publickey_remote']);
|
||||
// Create listener (which causes WP actions to be fired when messages are received)
|
||||
$ud_rpc->activate_replay_protection();
|
||||
if (!empty($key['extra_info']) && isset($key['extra_info']['mothership'])) {
|
||||
$mothership = $key['extra_info']['mothership'];
|
||||
$url = '';
|
||||
if ('__updraftpluscom' == $mothership) {
|
||||
$url = 'https://teamupdraft.com';
|
||||
} elseif (false != ($parsed = parse_url($key['extra_info']['mothership'])) && is_array($parsed)) {
|
||||
$url = $parsed['scheme'].'://'.$parsed['host'];
|
||||
}
|
||||
if (!empty($url)) $ud_rpc->set_allow_cors_from(array($url));
|
||||
}
|
||||
$ud_rpc->create_listener();
|
||||
}
|
||||
|
||||
// If we ever need to expand beyond a single GET action, this can/should be generalised and put into the commands class
|
||||
if (!empty($_GET['udcentral_action']) && 'login' == $_GET['udcentral_action']) {
|
||||
// auth_redirect() does not return, according to the documentation; but the code shows that it can
|
||||
// auth_redirect();
|
||||
|
||||
if (!empty($_GET['login_id']) && is_numeric($_GET['login_id']) && !empty($_GET['login_key'])) {
|
||||
$login_user = get_user_by('id', $_GET['login_id']);
|
||||
|
||||
// THis is included so we can get $wp_version
|
||||
include_once(ABSPATH.WPINC.'/version.php');
|
||||
|
||||
if (is_a($login_user, 'WP_User') || (version_compare($wp_version, '3.5', '<') && !empty($login_user->ID))) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- The variable is defined inside the ABSPATH.WPINC.'/version.php'.
|
||||
// Allow site implementers to disable this functionality
|
||||
$allow_autologin = apply_filters('updraftcentral_allow_autologin', true, $login_user);
|
||||
if ($allow_autologin) {
|
||||
$login_key = get_user_meta($login_user->ID, 'updraftcentral_login_key', true);
|
||||
if (is_array($login_key) && !empty($login_key['created']) && $login_key['created'] > time() - 60 && !empty($login_key['key']) && $login_key['key'] == $_GET['login_key']) {
|
||||
$autologin = empty($login_key['redirect_url']) ? network_admin_url() : $login_key['redirect_url'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($autologin)) {
|
||||
// Allow use once only
|
||||
delete_user_meta($login_user->ID, 'updraftcentral_login_key');
|
||||
$this->autologin_user($login_user, $autologin);
|
||||
}
|
||||
}
|
||||
|
||||
add_filter('udrpc_action', array($this, 'udrpc_action'), 10, 5);
|
||||
add_filter('updraftcentral_get_command_info', array($this, 'updraftcentral_get_command_info'), 10, 2);
|
||||
add_filter('updraftcentral_get_updraftplus_status', array($this, 'get_updraftplus_status'), 10, 1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the UpdraftPlus plugin status whether it has been installed or activated
|
||||
*
|
||||
* @param mixed $data Default data to return
|
||||
* @return array
|
||||
*/
|
||||
public function get_updraftplus_status($data) {
|
||||
|
||||
// Handle cases of users who rename their plugin folders
|
||||
if (class_exists('UpdraftPlus')) {
|
||||
$data['is_updraftplus_installed'] = true;
|
||||
$data['is_updraftplus_active'] = true;
|
||||
} else {
|
||||
if (!function_exists('get_plugins')) require_once(ABSPATH.'wp-admin/includes/plugin.php');
|
||||
$plugins = get_plugins();
|
||||
$key = 'updraftplus/updraftplus.php';
|
||||
|
||||
if (array_key_exists($key, $plugins)) {
|
||||
$data['is_updraftplus_installed'] = true;
|
||||
if (is_plugin_active($key)) $data['is_updraftplus_active'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves command class information and includes class file if class
|
||||
* is currently not available.
|
||||
*
|
||||
* @param mixed $response The default response to return if the submitted command does not exists
|
||||
* @param string $command The command to parse and check
|
||||
* @return array Contains the following command information "command_php_class", "class_prefix" and "command"
|
||||
*/
|
||||
public function updraftcentral_get_command_info($response, $command) {
|
||||
if (!preg_match('/^([a-z0-9]+)\.(.*)$/', $command, $matches)) return $response;
|
||||
$class_prefix = $matches[1];
|
||||
$command = $matches[2];
|
||||
|
||||
// Other plugins might have registered the filter rather later so we need to make
|
||||
// sure that we get all the commands intended for UpdraftCentral.
|
||||
$this->command_classes = apply_filters('updraftcentral_remotecontrol_command_classes', $this->command_classes);
|
||||
|
||||
// We only handle some commands - the others, we let something else deal with
|
||||
if (!isset($this->command_classes[$class_prefix])) return $response;
|
||||
|
||||
$command_php_class = $this->command_classes[$class_prefix];
|
||||
$command_base_class_at = apply_filters('updraftcentral_command_base_class_at', UPDRAFTCENTRAL_CLIENT_DIR.'/commands.php');
|
||||
|
||||
if (!class_exists('UpdraftCentral_Commands')) include_once($command_base_class_at);
|
||||
|
||||
// Second parameter has been passed since
|
||||
do_action('updraftcentral_command_class_wanted', $command_php_class);
|
||||
|
||||
if (!class_exists($command_php_class)) {
|
||||
if (file_exists(UPDRAFTCENTRAL_CLIENT_DIR.'/modules/'.$class_prefix.'.php')) {
|
||||
include_once(UPDRAFTCENTRAL_CLIENT_DIR.'/modules/'.$class_prefix.'.php');
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'command_php_class' => $command_php_class,
|
||||
'class_prefix' => $class_prefix,
|
||||
'command' => $command
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* In response to auto logging in the user; set a corresponding logged_in cookie to the $login_user_cookie class variable
|
||||
*
|
||||
* @param String $cookie Authentication cookie
|
||||
* @param Integer $user_id User ID
|
||||
* @param Integer $expiration The time the cookie expires as a UNIX timestamp
|
||||
* @param String $scheme Cookie scheme used. Accepts 'auth', 'secure_auth', or 'logged_in'
|
||||
*/
|
||||
public function set_global_logged_in_cookie($cookie, $user_id, $expiration, $scheme) {
|
||||
if ('logged_in' === $scheme) {
|
||||
$this->auto_logged_in_cookie = $cookie;
|
||||
}
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do verification before calling this method
|
||||
*
|
||||
* @param WP_User|Object $user user object for autologin
|
||||
* @param boolean $redirect_url Redirect URL
|
||||
*/
|
||||
private function autologin_user($user, $redirect_url = false) {
|
||||
if (!is_user_logged_in()) {
|
||||
// $user = get_user_by('id', $user_id);
|
||||
// Don't check that it's a WP_User - that's WP 3.5+ only
|
||||
if (!is_object($user) || empty($user->ID)) return;
|
||||
wp_set_current_user($user->ID, $user->user_login);
|
||||
add_filter('auth_cookie', array($this, 'set_global_logged_in_cookie'), 10, 4);
|
||||
wp_set_auth_cookie($user->ID);
|
||||
remove_filter('auth_cookie', array($this, 'set_global_logged_in_cookie'), 10, 4);
|
||||
do_action('wp_login', $user->user_login, $user);
|
||||
}
|
||||
if ($redirect_url) {
|
||||
// the wp_set_auth_cookie() above uses setcookie() function but the corresponding LOGGED_IN_COOKIE variable is visible and can only be accessible on the next page load
|
||||
// so we set the auth cookie into the superglobal $_COOKIE variable manually, we do this because the previously non logged-in user is now being auto-logged in and wp_create_nonce() needs the value of LOGGED_IN_COOKIE variable to produce a correct nonce
|
||||
if ($this->auto_logged_in_cookie) $_COOKIE[LOGGED_IN_COOKIE] = $this->auto_logged_in_cookie;
|
||||
$redirect_url = add_query_arg('restore_initiation_nonce', wp_create_nonce('updraftplus_udcentral_initiate_restore'), $redirect_url);
|
||||
if ($this->auto_logged_in_cookie) unset($_COOKIE[LOGGED_IN_COOKIE]);
|
||||
wp_safe_redirect($redirect_url);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WP filter udrpc_action
|
||||
*
|
||||
* @param Array $response - the unfiltered response that will be returned
|
||||
* @param String $command - the command being called
|
||||
* @param Array $data - the parameters to the command
|
||||
* @param String $key_name_indicator - the UC key that is in use
|
||||
* @param Object $ud_rpc - the UDRP object
|
||||
*
|
||||
* @return Array - filtered response
|
||||
*/
|
||||
public function udrpc_action($response, $command, $data, $key_name_indicator, $ud_rpc) {
|
||||
try {
|
||||
|
||||
if (empty($this->receivers[$key_name_indicator])) return $response;
|
||||
|
||||
// This can be used to detect an UpdraftCentral context
|
||||
if (!defined('UPDRAFTCENTRAL_COMMAND')) define('UPDRAFTCENTRAL_COMMAND', $command);
|
||||
|
||||
$this->initialise_listener_error_handling();
|
||||
|
||||
// UpdraftCentral needs this extra information especially now that the UpdraftCentral
|
||||
// libraries can be totally embedded in other plugins (e.g. WP-Optimize, etc.) thus,
|
||||
// that makes the UpdraftPlus plugin optional.
|
||||
//
|
||||
// This will give UpdraftCentral a proper way of disabling the backup feature
|
||||
// for this site if the UpdraftPlus plugin is currently not installed or activated.
|
||||
//
|
||||
// In addition, we need to attached the host plugin who is handling the UpdraftCentral requests
|
||||
global $updraftcentral_host_plugin;
|
||||
$extra = apply_filters('updraftcentral_get_updraftplus_status', array(
|
||||
'is_updraftplus_installed' => false,
|
||||
'is_updraftplus_active' => false,
|
||||
'host_plugin' => $updraftcentral_host_plugin->plugin_name,
|
||||
));
|
||||
|
||||
$command_info = apply_filters('updraftcentral_get_command_info', false, $command);
|
||||
if (!$command_info) {
|
||||
if (isset($response['data']) && is_array($response['data'])) $response['data']['extra'] = $extra;
|
||||
return $response;
|
||||
}
|
||||
|
||||
$class_prefix = $command_info['class_prefix'];
|
||||
$command = $command_info['command'];
|
||||
$command_php_class = $command_info['command_php_class'];
|
||||
|
||||
if (empty($this->commands[$class_prefix])) {
|
||||
if (class_exists($command_php_class)) {
|
||||
$this->commands[$class_prefix] = new $command_php_class($this);
|
||||
}
|
||||
}
|
||||
|
||||
$command_class = isset($this->commands[$class_prefix]) ? $this->commands[$class_prefix] : new stdClass;
|
||||
|
||||
if ('_' == substr($command, 0, 1) || !is_a($command_class, $command_php_class) || (!method_exists($command_class, $command) && !method_exists($command_class, '__call'))) {
|
||||
if (defined('UPDRAFTCENTRAL_UDRPC_FORCE_DEBUG') && UPDRAFTCENTRAL_UDRPC_FORCE_DEBUG) error_log("Unknown RPC command received: ".$command);
|
||||
|
||||
return $this->return_rpc_message(array('response' => 'rpcerror', 'data' => array('code' => 'unknown_rpc_command', 'data' => array('prefix' => $class_prefix, 'command' => $command, 'class' => $command_php_class))));
|
||||
}
|
||||
|
||||
$extra_info = isset($this->extra_info[$key_name_indicator]) ? $this->extra_info[$key_name_indicator] : null;
|
||||
|
||||
// Make it so that current_user_can() checks can apply + work
|
||||
if (!empty($extra_info['user_id'])) wp_set_current_user($extra_info['user_id']);
|
||||
|
||||
$this->current_udrpc = $ud_rpc;
|
||||
|
||||
do_action('updraftcentral_listener_pre_udrpc_action', $command, $command_class, $data, $extra_info);
|
||||
|
||||
// Allow the command class to perform any boiler-plate actions.
|
||||
if (is_callable(array($command_class, '_pre_action'))) call_user_func(array($command_class, '_pre_action'), $command, $data, $extra_info);
|
||||
|
||||
// Despatch
|
||||
$msg = apply_filters('updraftcentral_listener_udrpc_action', call_user_func(array($command_class, $command), $data, $extra_info), $command_class, $class_prefix, $command, $data, $extra_info);
|
||||
|
||||
if (is_callable(array($command_class, '_post_action'))) call_user_func(array($command_class, '_post_action'), $command, $data, $extra_info);
|
||||
|
||||
do_action('updraftcentral_listener_post_udrpc_action', $command, $command_class, $data, $extra_info);
|
||||
|
||||
if (isset($msg['data']) && is_array($msg['data'])) {
|
||||
$msg['data']['extra'] = $extra;
|
||||
}
|
||||
|
||||
return $this->return_rpc_message($msg);
|
||||
} catch (Exception $e) {
|
||||
$log_message = 'PHP Fatal Exception error ('.get_class($e).') has occurred during UpdraftCentral command execution. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
|
||||
error_log($log_message);
|
||||
|
||||
return $this->return_rpc_message(array('response' => 'rpcerror', 'data' => array('code' => 'rpc_fatal_error', 'data' => array('command' => $command, 'message' => $log_message))));
|
||||
// @codingStandardsIgnoreLine
|
||||
} catch (Error $e) {
|
||||
$log_message = 'PHP Fatal error ('.get_class($e).') has occurred during UpdraftCentral command execution. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
|
||||
error_log($log_message);
|
||||
|
||||
return $this->return_rpc_message(array('response' => 'rpcerror', 'data' => array('code' => 'rpc_fatal_error', 'data' => array('command' => $command, 'message' => $log_message))));
|
||||
}
|
||||
}
|
||||
|
||||
public function get_current_udrpc() {
|
||||
return $this->current_udrpc;
|
||||
}
|
||||
|
||||
private function initialise_listener_error_handling() {
|
||||
global $updraftcentral_host_plugin;
|
||||
|
||||
$this->host->error_reporting_stop_when_logged = true;
|
||||
$error_levels = version_compare(PHP_VERSION, '8.4.0', '>=') ? E_ALL : E_ALL & ~E_STRICT;
|
||||
set_error_handler(array($this->host, 'php_error'), $error_levels);
|
||||
$this->php_events = array();
|
||||
@ob_start();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Might be a bigger picture that I am missing but do we need to silence errors here?
|
||||
add_filter($updraftcentral_host_plugin->get_logline_filter(), array($this, 'updraftcentral_logline'), 10, 4);
|
||||
if (!$updraftcentral_host_plugin->get_debug_mode()) return;
|
||||
}
|
||||
|
||||
public function updraftcentral_logline($line, $nonce, $level, $uniq_id) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the method is used as a WP filter.
|
||||
if ('notice' === $level && 'php_event' === $uniq_id) {
|
||||
$this->php_events[] = $line;
|
||||
}
|
||||
return $line;
|
||||
}
|
||||
|
||||
public function return_rpc_message($msg) {
|
||||
if (is_array($msg) && isset($msg['response']) && 'error' == $msg['response']) {
|
||||
$this->host->log('Unexpected response code in remote communications: '.serialize($msg));
|
||||
}
|
||||
|
||||
$caught_output = @ob_get_contents();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Might be a bigger picture that I am missing but do we need to silence errors here?
|
||||
@ob_end_clean();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Might be a bigger picture that I am missing but do we need to silence errors here?
|
||||
// If turning output-catching off, turn this on instead:
|
||||
// $caught_output = ''; @ob_end_flush();
|
||||
|
||||
// If there's higher-level output buffering going on, then get rid of that
|
||||
if (ob_get_level()) ob_end_clean();
|
||||
|
||||
if ($caught_output) {
|
||||
if (!isset($msg['data'])) $msg['data'] = null;
|
||||
$msg['data'] = array('caught_output' => $caught_output, 'previous_data' => $msg['data']);
|
||||
$already_rearranged_data = true;
|
||||
}
|
||||
|
||||
if (!empty($this->php_events)) {
|
||||
if (!isset($msg['data'])) $msg['data'] = null;
|
||||
if (!empty($already_rearranged_data)) {
|
||||
$msg['data']['php_events'] = array();
|
||||
} else {
|
||||
$msg['data'] = array('php_events' => array(), 'previous_data' => $msg['data']);
|
||||
}
|
||||
foreach ($this->php_events as $logline) {
|
||||
$msg['data']['php_events'][] = $logline;
|
||||
}
|
||||
}
|
||||
restore_error_handler();
|
||||
|
||||
return $msg;
|
||||
}
|
||||
}
|
||||
440
wp-content/plugins/updraftplus/central/modules/analytics.php
Normal file
@@ -0,0 +1,440 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* Handles Analytics Commands
|
||||
*
|
||||
* @method array ga_checker()
|
||||
* @method array get_access_token()
|
||||
* @method array set_authorization_code()
|
||||
*/
|
||||
class UpdraftCentral_Analytics_Commands extends UpdraftCentral_Commands {
|
||||
|
||||
private $scope = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/analytics.readonly';
|
||||
|
||||
private $endpoint = 'https://accounts.google.com/o/oauth2/auth';
|
||||
|
||||
private $token_info_endpoint = 'https://www.googleapis.com/oauth2/v1/tokeninfo';
|
||||
|
||||
private $access_key = 'updraftcentral_auth_server_access';
|
||||
|
||||
private $auth_endpoint;
|
||||
|
||||
private $client_id;
|
||||
|
||||
private $view_key = 'updraftcentral_analytics_views';
|
||||
|
||||
private $tracking_id_key = 'updraftcentral_analytics_tracking_id';
|
||||
|
||||
private $expiration;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->auth_endpoint = defined('UPDRAFTPLUS_GOOGLE_ANALYTICS_CALLBACK_URL') ? UPDRAFTPLUS_GOOGLE_ANALYTICS_CALLBACK_URL : 'https://auth.updraftplus.com/auth/googleanalytics';
|
||||
$this->client_id = defined('UPDRAFTPLUS_GOOGLE_ANALYTICS_CLIENT_ID') ? UPDRAFTPLUS_GOOGLE_ANALYTICS_CLIENT_ID : '306245874349-6s896c3tjpra26ns3dpplhqcl6rv6qlb.apps.googleusercontent.com';
|
||||
|
||||
// Set transient expiration - default for 24 hours
|
||||
$this->expiration = 86400;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether Google Analytics (GA) is installed or setup
|
||||
*
|
||||
* N.B. This check assumes GA is installed either using "wp_head" or "wp_footer" (e.g. attached
|
||||
* to the <head/> or somewhere before </body>). It does not recursively check all the pages
|
||||
* of the website to find if GA is installed on each or one of those pages, but only on the main/root page.
|
||||
*
|
||||
* @return array $result An array containing "ga_installed" property which returns "true" if GA (Google Analytics) is installed, "false" otherwise.
|
||||
*/
|
||||
public function ga_checker() {
|
||||
|
||||
try {
|
||||
|
||||
// Retrieves the tracking code/id if available
|
||||
$tracking_id = $this->get_tracking_id();
|
||||
$installed = true;
|
||||
|
||||
// If tracking code/id is currently not available then we
|
||||
// parse the needed information from the buffered content through
|
||||
// the "wp_head" and "wp_footer" hooks.
|
||||
if (false === $tracking_id) {
|
||||
$info = $this->extract_tracking_id();
|
||||
|
||||
$installed = $info['installed'];
|
||||
$tracking_id = $info['tracking_id'];
|
||||
}
|
||||
|
||||
// Get access token to be use to generate the report.
|
||||
$access_token = $this->_get_token();
|
||||
|
||||
if (empty($access_token)) {
|
||||
// If we don't get a valid access token then that would mean
|
||||
// the access has been revoked by the user or UpdraftCentral was not authorized yet
|
||||
// to access the user's analytics data, thus, we're clearing
|
||||
// any previously stored user access so we're doing some housekeeping here.
|
||||
$this->clear_user_access();
|
||||
}
|
||||
|
||||
// Wrap and combined information for the requesting
|
||||
// client's consumption
|
||||
$result = array(
|
||||
'ga_installed' => $installed,
|
||||
'tracking_id' => $tracking_id,
|
||||
'client_id' => $this->client_id,
|
||||
'redirect_uri' => $this->auth_endpoint,
|
||||
'scope' => $this->scope,
|
||||
'access_token' => $access_token,
|
||||
'endpoint' => $this->endpoint
|
||||
);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts Google Tracking ID from contents rendered through the "wp_head" and "wp_footer" action hooks
|
||||
*
|
||||
* @internal
|
||||
* @return array $result An array containing the result of the extraction.
|
||||
*/
|
||||
private function extract_tracking_id() {
|
||||
|
||||
// Define result array
|
||||
$result = array();
|
||||
|
||||
// Retrieve header content
|
||||
ob_start();
|
||||
do_action('wp_head');
|
||||
$header_content = ob_get_clean();
|
||||
|
||||
// Extract analytics information if available.
|
||||
$output = $this->parse_content($header_content);
|
||||
$result['installed'] = $output['installed'];
|
||||
$result['tracking_id'] = $output['tracking_id'];
|
||||
|
||||
// If it was not found, then now try the footer
|
||||
if (empty($result['tracking_id'])) {
|
||||
// Retrieve footer content
|
||||
ob_start();
|
||||
do_action('wp_footer');
|
||||
$footer_content = ob_get_clean();
|
||||
$output = $this->parse_content($footer_content);
|
||||
$result['installed'] = $output['installed'];
|
||||
$result['tracking_id'] = $output['tracking_id'];
|
||||
}
|
||||
|
||||
if (!empty($result['tracking_id'])) {
|
||||
set_transient($this->tracking_id_key, $result['tracking_id'], $this->expiration);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets access token
|
||||
*
|
||||
* Validates whether the system currently have a valid token to use when connecting to Google Analytics API.
|
||||
* If not, then it will send a token request based on the authorization code we stored during the
|
||||
* authorization phase. Otherwise, it will return an empty token.
|
||||
*
|
||||
* @return array $result An array containing the Google Analytics API access token.
|
||||
*/
|
||||
public function get_access_token() {
|
||||
|
||||
try {
|
||||
|
||||
// Loads or request a valid token to use
|
||||
$access_token = $this->_get_token();
|
||||
|
||||
if (!empty($access_token)) {
|
||||
$result = array('access_token' => $access_token);
|
||||
} else {
|
||||
$result = array('error' => true, 'message' => 'ga_token_retrieval_failed', 'values' => array());
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any previously stored user access
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear_user_access() {
|
||||
return delete_option($this->access_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves user is and access token received from the auth server
|
||||
*
|
||||
* @param array $query Parameter array containing the user id and access token from the auth server.
|
||||
* @return array $result An array containing a "success" or "failure" message as a result of the current process.
|
||||
*/
|
||||
public function save_user_access($query) {
|
||||
|
||||
try {
|
||||
|
||||
$token = get_option($this->access_key, false);
|
||||
$result = array();
|
||||
|
||||
if (false === $token) {
|
||||
$token = array(
|
||||
'user_id' => base64_decode(urldecode($query['user_id'])),
|
||||
'access_token' => base64_decode(urldecode($query['access_token']))
|
||||
);
|
||||
|
||||
if (false !== update_option($this->access_key, $token)) {
|
||||
$result = array('error' => false, 'message' => 'ga_access_saved', 'values' => array());
|
||||
} else {
|
||||
$result = array('error' => true, 'message' => 'ga_access_saving_failed', 'values' => array($query['access_token']));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the tracking code/id manually (user input)
|
||||
*
|
||||
* @param array $query Parameter array containing the tracking code/id to save.
|
||||
* @return array $result An array containing the result of the process.
|
||||
*/
|
||||
public function save_tracking_id($query) {
|
||||
try {
|
||||
$tracking_id = $query['tracking_id'];
|
||||
$saved = false;
|
||||
|
||||
if (!empty($tracking_id)) {
|
||||
$saved = set_transient($this->tracking_id_key, $tracking_id, $this->expiration);
|
||||
}
|
||||
|
||||
$result = array('saved' => $saved);
|
||||
} catch (Exception $e) {
|
||||
$result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves any available access token either previously saved info or
|
||||
* from a new request from the Google Server.
|
||||
*
|
||||
* @internal
|
||||
* @return string $authorization_code
|
||||
*/
|
||||
private function _get_token() {
|
||||
|
||||
// Retrieves the tracking code/id if available
|
||||
$tracking_id = $this->get_tracking_id();
|
||||
$access_token = '';
|
||||
|
||||
$token = get_option($this->access_key, false);
|
||||
if (false !== $token) {
|
||||
$access_token = isset($token['access_token']) ? $token['access_token'] : '';
|
||||
$user_id = isset($token['user_id']) ? $token['user_id'] : '';
|
||||
|
||||
if ((!empty($access_token) && !$this->_token_valid($access_token)) || (!empty($user_id) && empty($access_token) && !empty($tracking_id))) {
|
||||
if (!empty($user_id)) {
|
||||
$args = array(
|
||||
'headers' => apply_filters('updraftplus_auth_headers', array())
|
||||
);
|
||||
|
||||
$response = wp_remote_get($this->auth_endpoint.'?user_id='.$user_id.'&code=ud_googleanalytics_code', $args);
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message()); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- The escaping should be happening when the exception is printed
|
||||
} else {
|
||||
if (is_array($response)) {
|
||||
|
||||
$body = json_decode($response['body'], true);
|
||||
$token_response = array();
|
||||
|
||||
if (is_array($body) && !isset($body['error'])) {
|
||||
$token_response = json_decode(base64_decode($body[0]), true);
|
||||
}
|
||||
|
||||
if (is_array($token_response) && isset($token_response['access_token'])) {
|
||||
$access_token = $token_response['access_token'];
|
||||
} else {
|
||||
// If we don't get any valid response then that would mean that the
|
||||
// permission was already revoked. Thus, we need to re-authorize the
|
||||
// user before using the analytics feature once again.
|
||||
$access_token = '';
|
||||
}
|
||||
|
||||
$token['access_token'] = $access_token;
|
||||
update_option($this->access_key, $token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $access_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies whether the access token is still valid for use
|
||||
*
|
||||
* @internal
|
||||
* @param string $token The access token to be check and validated
|
||||
* @return bool
|
||||
* @throws Exception If an error has occurred while connecting to the Google Server.
|
||||
*/
|
||||
private function _token_valid($token) {
|
||||
|
||||
$response = wp_remote_get($this->token_info_endpoint.'?access_token='.$token);
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message()); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- The escaping should be happening when the exception is printed
|
||||
} else {
|
||||
if (is_array($response)) {
|
||||
$response = json_decode($response['body'], true);
|
||||
if (!empty($response)) {
|
||||
if (!isset($response['error']) && !isset($response['error_description'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and extracts the google analytics information (NEEDED)
|
||||
*
|
||||
* @internal
|
||||
* @param string $content The content to parse
|
||||
* @return array An array containing the status of the process along with the tracking code/id
|
||||
*/
|
||||
private function parse_content($content) {
|
||||
|
||||
$installed = false;
|
||||
$gtm_installed = false;
|
||||
$tracking_id = '';
|
||||
$script_file_found = false;
|
||||
$tracking_id_found = false;
|
||||
|
||||
// Pull google analytics script file(s)
|
||||
preg_match_all('/<script\b[^>]*>([\s\S]*?)<\/script>/i', $content, $scripts);
|
||||
for ($i=0; $i < count($scripts[0]); $i++) {
|
||||
// Check for Google Analytics file
|
||||
if (stristr($scripts[0][$i], 'ga.js') || stristr($scripts[0][$i], 'analytics.js')) {
|
||||
$script_file_found = true;
|
||||
}
|
||||
|
||||
// Check for Google Tag Manager file
|
||||
// N.B. We are not checking for GTM but this check will be useful when
|
||||
// showing the notice to the user if we haven't found Google Analytics
|
||||
// directly being installed on the page.
|
||||
if (stristr($scripts[0][$i], 'gtm.js')) {
|
||||
$gtm_installed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Pull tracking code
|
||||
preg_match_all('/UA-[0-9]{5,}-[0-9]{1,}/i', $content, $codes);
|
||||
if (count($codes) > 0) {
|
||||
if (!empty($codes[0])) {
|
||||
$tracking_id_found = true;
|
||||
$tracking_id = $codes[0][0];
|
||||
}
|
||||
}
|
||||
|
||||
// If we found both the script and the tracking code then it is safe
|
||||
// to say that Google Analytics (GA) is installed. Thus, we're returning
|
||||
// "true" as a response.
|
||||
if ($script_file_found && $tracking_id_found) {
|
||||
$installed = true;
|
||||
}
|
||||
|
||||
// Return result of process.
|
||||
return array(
|
||||
'installed' => $installed,
|
||||
'gtm_installed' => $gtm_installed,
|
||||
'tracking_id' => $tracking_id
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the "analytics_tracking_id" transient
|
||||
*
|
||||
* @internal
|
||||
* @return mixed Returns the value of the saved transient. Returns "false" if the transient does not exist.
|
||||
*/
|
||||
private function get_tracking_id() {
|
||||
return get_transient($this->tracking_id_key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current tracking id
|
||||
*
|
||||
* @return array $result An array containing the Google Tracking ID.
|
||||
*/
|
||||
public function get_current_tracking_id() {
|
||||
try {
|
||||
|
||||
// Get current site transient stored for this key
|
||||
$tracking_id = get_transient($this->tracking_id_key);
|
||||
|
||||
// Checks whether we have a valid token
|
||||
$access_token = $this->_get_token();
|
||||
if (empty($access_token)) {
|
||||
$tracking_id = '';
|
||||
}
|
||||
|
||||
if (false === $tracking_id) {
|
||||
$result = $this->extract_tracking_id();
|
||||
} else {
|
||||
$result = array(
|
||||
'installed' => true,
|
||||
'tracking_id' => $tracking_id
|
||||
);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears user access from database
|
||||
*
|
||||
* @return array $result An array containing the "Remove" confirmation whether the action succeeded or not.
|
||||
*/
|
||||
public function remove_user_access() {
|
||||
try {
|
||||
|
||||
// Clear user access
|
||||
$is_cleared = $this->clear_user_access();
|
||||
|
||||
if (false !== $is_cleared) {
|
||||
$result = array('removed' => true);
|
||||
} else {
|
||||
$result = array('error' => true, 'message' => 'user_access_remove_failed', 'values' => array());
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
}
|
||||
391
wp-content/plugins/updraftplus/central/modules/backups.php
Normal file
@@ -0,0 +1,391 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* Handles Backups Commands
|
||||
*/
|
||||
class UpdraftCentral_Backups_Commands extends UpdraftCentral_Commands {
|
||||
|
||||
private $switched = false;
|
||||
|
||||
/**
|
||||
* Function that gets called before every action
|
||||
*
|
||||
* @param string $command a string that corresponds to UDC command to call a certain method for this class.
|
||||
* @param array $data an array of data post or get fields
|
||||
* @param array $extra_info extrainfo use in the udrpc_action, e.g. user_id
|
||||
*
|
||||
* link to udrpc_action main function in class UpdraftCentral_Listener
|
||||
*/
|
||||
public function _pre_action($command, $data, $extra_info) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- This function is called from listener.php and $extra_info is being sent.
|
||||
// Here we assign the current blog_id to a variable $blog_id
|
||||
$blog_id = get_current_blog_id();
|
||||
if (!empty($data['site_id'])) $blog_id = $data['site_id'];
|
||||
|
||||
if (function_exists('switch_to_blog') && is_multisite() && $blog_id) {
|
||||
$this->switched = switch_to_blog($blog_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that gets called after every action
|
||||
*
|
||||
* @param string $command a string that corresponds to UDC command to call a certain method for this class.
|
||||
* @param array $data an array of data post or get fields
|
||||
* @param array $extra_info extrainfo use in the udrpc_action, e.g. user_id
|
||||
*
|
||||
* link to udrpc_action main function in class UpdraftCentral_Listener
|
||||
*/
|
||||
public function _post_action($command, $data, $extra_info) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Unused parameter is present because the caller from UpdraftCentral_Listener class uses 3 arguments.
|
||||
// Here, we're restoring to the current (default) blog before we switched
|
||||
if ($this->switched) restore_current_blog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the UpdraftPlus plugin status, UpdraftVault storage usage status, Next backup
|
||||
* schedule, etc. Used primarily by UpdraftCentral background process.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_status() {
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
$response = array(
|
||||
'status' => 'error',
|
||||
'error_code' => 'insufficient_permission',
|
||||
);
|
||||
} else {
|
||||
|
||||
if (!function_exists('get_mu_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
|
||||
$mu_plugins = get_mu_plugins();
|
||||
|
||||
$is_premium = false;
|
||||
if (defined('UPDRAFTPLUS_DIR') && file_exists(UPDRAFTPLUS_DIR.'/udaddons')) $is_premium = true;
|
||||
|
||||
// Set default response
|
||||
$response = array(
|
||||
'updraftplus_version' => '',
|
||||
'is_premium' => $is_premium,
|
||||
'installed' => false,
|
||||
'active' => false,
|
||||
'backup_count' => 0,
|
||||
'has_mu_plugins' => !empty($mu_plugins) ? true : false,
|
||||
'last_backup' => array(
|
||||
'backup_nonce' => '',
|
||||
'has_errors' => false,
|
||||
'has_warnings' => false,
|
||||
'has_succeeded' => false,
|
||||
),
|
||||
'updraftvault' => array(
|
||||
'site_connected' => false,
|
||||
'storage' => array('quota_used' => '0 MB', 'quota' => '0 MB', 'percentage_usage' => '0.0%'),
|
||||
),
|
||||
'meta' => array(),
|
||||
);
|
||||
|
||||
if (class_exists('UpdraftPlus')) {
|
||||
global $updraftplus;
|
||||
|
||||
$response['updraftplus_version'] = $updraftplus->version;
|
||||
$response['updraftvault'] = $this->get_updraftvault_status();
|
||||
$response['installed'] = true;
|
||||
$response['active'] = true;
|
||||
$response['meta'] = $this->get_filesystem_credentials_info();
|
||||
|
||||
$schedule = $this->get_next_backup_schedule();
|
||||
if ($schedule) {
|
||||
$response['next_backup_schedule'] = $schedule;
|
||||
}
|
||||
|
||||
$backup_history = UpdraftPlus_Backup_History::add_jobdata(UpdraftPlus_Backup_History::get_history());
|
||||
|
||||
$response['backup_count'] = count($backup_history);
|
||||
|
||||
$updraft_last_backup = UpdraftPlus_Options::get_updraft_option('updraft_last_backup');
|
||||
if ($updraft_last_backup) {
|
||||
$response['last_backup']['backup_nonce'] = $updraft_last_backup['backup_nonce'];
|
||||
if (isset($updraft_last_backup['backup_time'])) {
|
||||
$response['last_backup']['backup_date'] = gmdate('n/j/Y', $updraft_last_backup['backup_time']);
|
||||
$response['last_backup']['backup_time'] = $updraft_last_backup['backup_time'];
|
||||
}
|
||||
|
||||
$errors = 0;
|
||||
$warnings = 0;
|
||||
|
||||
if (is_array($updraft_last_backup['errors'])) {
|
||||
foreach ($updraft_last_backup['errors'] as $err) {
|
||||
$level = (is_array($err)) ? $err['level'] : 'error';
|
||||
if ('warning' == $level) {
|
||||
$warnings++;
|
||||
} elseif ('error' == $level) {
|
||||
$errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors > 0) $response['last_backup']['has_errors'] = true;
|
||||
if ($warnings > 0) $response['last_backup']['has_warnings'] = true;
|
||||
if (isset($updraft_last_backup['success']) && $updraft_last_backup['success']) $response['last_backup']['has_succeeded'] = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!function_exists('get_plugins')) require_once(ABSPATH.'wp-admin/includes/plugin.php');
|
||||
$plugins = get_plugins();
|
||||
$key = 'updraftplus/updraftplus.php';
|
||||
|
||||
if (array_key_exists($key, $plugins)) {
|
||||
$response['installed'] = true;
|
||||
if (is_plugin_active($key)) $response['active'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_response($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the next backup schedule for Files and Database backups
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_next_backup_schedule() {
|
||||
|
||||
// Get the next (nearest) scheduled backups
|
||||
$files = wp_next_scheduled('updraft_backup');
|
||||
$db = wp_next_scheduled('updraft_backup_database');
|
||||
|
||||
if ($files && $db) {
|
||||
$timestamp = min($files, $db); // Get the nearest schedule among the two schedules
|
||||
} elseif ($files && !$db) {
|
||||
$timestamp = $files;
|
||||
} elseif (!$files && $db) {
|
||||
$timestamp = $db;
|
||||
} else {
|
||||
$timestamp = null;
|
||||
}
|
||||
|
||||
if (!empty($timestamp)) {
|
||||
return gmdate('g:i A - D', $timestamp);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the UpdrafVault storage usage status
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_updraftvault_status() {
|
||||
|
||||
if (!class_exists('UpdraftCentral_UpdraftVault_Commands')) {
|
||||
include_once(UPDRAFTPLUS_DIR.'/includes/updraftvault.php');
|
||||
}
|
||||
|
||||
$updraftvault = new UpdraftCentral_UpdraftVault_Commands($this->rc);
|
||||
$creds = $updraftvault->get_credentials();
|
||||
|
||||
$site_connected = false;
|
||||
$storage = array('quota_used' => '0 MB', 'quota' => '0 MB', 'percentage_usage' => '0.0%');
|
||||
$remote_service = false;
|
||||
|
||||
if (isset($creds['data'])) {
|
||||
if (!isset($creds['data']['error']) && isset($creds['data']['accesskey'])) {
|
||||
$site_connected = true;
|
||||
|
||||
$storage_objects_and_ids = UpdraftPlus_Storage_Methods_Interface::get_storage_objects_and_ids(array('updraftvault'));
|
||||
|
||||
if (isset($storage_objects_and_ids['updraftvault']['instance_settings'])) {
|
||||
$instance_settings = $storage_objects_and_ids['updraftvault']['instance_settings'];
|
||||
$instance_id = key($instance_settings);
|
||||
$opts = $instance_settings[$instance_id];
|
||||
|
||||
if (!class_exists('UpdraftPlus_BackupModule_updraftvault')) {
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/updraftvault.php');
|
||||
}
|
||||
|
||||
$vault = new UpdraftPlus_BackupModule_updraftvault();
|
||||
$vault->set_options($opts, false, $instance_id);
|
||||
|
||||
$quota_root = $opts['quota_root'];
|
||||
$quota = $opts['quota'];
|
||||
|
||||
if (empty($quota_root)) {
|
||||
// This next line is wrong: it lists the files *in this site's sub-folder*, rather than the whole Vault
|
||||
$current_files = $vault->listfiles('');
|
||||
} else {
|
||||
$current_files = $vault->listfiles_with_path($quota_root, '', true);
|
||||
}
|
||||
|
||||
if (!is_wp_error($current_files) && is_array($current_files)) {
|
||||
$quota_used = 0;
|
||||
foreach ($current_files as $file) {
|
||||
$quota_used += $file['size'];
|
||||
}
|
||||
|
||||
$storage = array(
|
||||
'quota_used' => round($quota_used / 1048576, 1).' MB',
|
||||
'quota' => round($quota / 1048576, 1).' MB',
|
||||
'percentage_usage' => sprintf('%.1f', 100*$quota_used / $quota).'%',
|
||||
);
|
||||
|
||||
$remote_service = array(
|
||||
'name' => 'updraft_include_remote_service_updraftvault',
|
||||
'value' => $instance_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'site_connected' => $site_connected,
|
||||
'storage' => $storage,
|
||||
'remote_service' => $remote_service,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves information whether filesystem credentials (e.g. FTP/SSH) are required
|
||||
* when updating plugins
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_filesystem_credentials_info() {
|
||||
|
||||
if (!function_exists('get_filesystem_method')) {
|
||||
include_once(ABSPATH.'/wp-admin/includes/file.php');
|
||||
}
|
||||
|
||||
$filesystem_method = get_filesystem_method(array(), WP_PLUGIN_DIR);
|
||||
|
||||
ob_start();
|
||||
$filesystem_credentials_are_stored = request_filesystem_credentials(site_url());
|
||||
$filesystem_form = strip_tags(ob_get_contents(), '<div><h2><p><input><label><fieldset><legend><span><em>');
|
||||
ob_end_clean();
|
||||
|
||||
$request_filesystem_credentials = ('direct' != $filesystem_method && !$filesystem_credentials_are_stored);
|
||||
|
||||
return array(
|
||||
'request_filesystem_credentials' => $request_filesystem_credentials,
|
||||
'filesystem_form' => base64_encode($filesystem_form),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the backup progress in terms of entities completed. Used primarily by UpdraftCentral
|
||||
* for polling backup progress in the background.
|
||||
*
|
||||
* @param array $params Submitted arguments for the current request
|
||||
* @return array
|
||||
*/
|
||||
public function get_backup_progress($params) {
|
||||
|
||||
$nonce = isset($params['nonce']) ? $params['nonce'] : false;
|
||||
$response = array('nonce' => $params['nonce']);
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
$response['status'] = 'error';
|
||||
$response['error_code'] = 'insufficient_permission';
|
||||
} else {
|
||||
global $updraftplus;
|
||||
|
||||
if ($nonce && $updraftplus && is_a($updraftplus, 'UpdraftPlus')) {
|
||||
|
||||
// Check the job is not still running.
|
||||
$jobdata = $updraftplus->jobdata_getarray($nonce);
|
||||
|
||||
if (!empty($jobdata)) {
|
||||
$response['status'] = 'in-progress';
|
||||
|
||||
$file_entities = 0;
|
||||
$db_entities = 0;
|
||||
$processed = 0;
|
||||
|
||||
if (isset($jobdata['backup_database']) && 'no' != $jobdata['backup_database']) {
|
||||
$backup_database = $jobdata['backup_database'];
|
||||
$db_entities += count($backup_database);
|
||||
|
||||
foreach ($backup_database as $whichdb => $info) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- In this check we only need the status contained in the $info for now.
|
||||
$status = $info; // For default: 'wp'
|
||||
if (is_array($info)) {
|
||||
$status = $info['status'];
|
||||
}
|
||||
|
||||
if ('finished' == $status) {
|
||||
$processed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($jobdata['backup_files']) && 'no' != $jobdata['backup_files']) {
|
||||
$file_entities = count($jobdata['job_file_entities']);
|
||||
|
||||
$backup_files = $jobdata['backup_files'];
|
||||
if ('finished' == $backup_files) {
|
||||
$processed += $file_entities;
|
||||
} elseif (isset($jobdata['filecreating_substatus'])) {
|
||||
$substatus = $jobdata['filecreating_substatus'];
|
||||
$processed += max(0, intval($substatus['i']) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
$response['progress'] = array(
|
||||
'file_entities' => $file_entities,
|
||||
'db_entities' => $db_entities,
|
||||
'total_entities' => $file_entities+$db_entities,
|
||||
'processed' => $processed,
|
||||
'percentage' => floor(($processed/($file_entities+$db_entities))*100),
|
||||
'nonce' => $nonce,
|
||||
);
|
||||
|
||||
UpdraftPlus_Options::update_updraft_option('updraft_central_last_backup_progress', $response['progress'], false);
|
||||
} else {
|
||||
$last_backup = UpdraftPlus_Options::get_updraft_option('updraft_last_backup');
|
||||
if ($nonce == $last_backup['backup_nonce']) {
|
||||
$response['status'] = 'finished';
|
||||
$response['progress'] = array('percentage' => 100);
|
||||
$response['progress']['errors'] = $last_backup['errors'];
|
||||
$response['progress']['backup_time'] = $last_backup['backup_time'];
|
||||
$response['progress']['completed_time'] = gmdate('g:ia', $last_backup['backup_time']);
|
||||
$response['progress']['completed_date'] = gmdate('M d, Y', $last_backup['backup_time']);
|
||||
|
||||
$errors = 0;
|
||||
$warnings = 0;
|
||||
|
||||
if (!empty($last_backup['errors']) && is_array($last_backup['errors'])) {
|
||||
foreach ($last_backup['errors'] as $err) {
|
||||
$level = (is_array($err)) ? $err['level'] : 'error';
|
||||
if ('warning' == $level) {
|
||||
$warnings++;
|
||||
} elseif ('error' == $level) {
|
||||
$errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response['progress']['has_errors'] = ($errors > 0) ? true : false;
|
||||
$response['progress']['has_warnings'] = ($warnings > 0) ? true : false;
|
||||
} else {
|
||||
// We might be too early to check the `updraft_last_backup` thus, we'll
|
||||
// give it a few rounds to check by setting the status to "in-progress"
|
||||
// and returning the last backup progress (if applicable).
|
||||
$last_progress = UpdraftPlus_Options::get_updraft_option('updraft_central_last_backup_progress');
|
||||
|
||||
$response['status'] = 'in-progress';
|
||||
if (!empty($last_progress) && isset($last_progress['nonce'])) {
|
||||
$response['progress'] = $last_progress;
|
||||
|
||||
if ($nonce == $last_progress['nonce']) {
|
||||
UpdraftPlus_Options::delete_updraft_option('updraft_central_last_backup_progress');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_response($response);
|
||||
}
|
||||
}
|
||||
842
wp-content/plugins/updraftplus/central/modules/comments.php
Normal file
@@ -0,0 +1,842 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
class UpdraftCentral_Comments_Commands extends UpdraftCentral_Commands {
|
||||
|
||||
/**
|
||||
* The _search_comments function searches all available comments based
|
||||
* on the following query parameters (type, status, search)
|
||||
*
|
||||
* Search Parameters/Filters:
|
||||
* type - comment types can be 'comment', 'trackback' and 'pingback', defaults to 'comment'
|
||||
* status - comment status can be 'hold' or unapprove, 'approve', 'spam', 'trash'
|
||||
* search - user generated content or keyword
|
||||
*
|
||||
* @param array $query The query to search comments
|
||||
* @return array
|
||||
*/
|
||||
private function _search_comments($query) {
|
||||
|
||||
// Basic parameters to the query and should display
|
||||
// the results in descending order (latest comments) first
|
||||
// based on their generated IDs
|
||||
|
||||
$args = array(
|
||||
'orderby' => 'ID',
|
||||
'order' => 'DESC',
|
||||
'type' => $query['type'],
|
||||
'status' => $query['status'],
|
||||
'search' => esc_attr($query['search']),
|
||||
);
|
||||
|
||||
$query = new WP_Comment_Query;
|
||||
$found_comments = $query->query($args);
|
||||
|
||||
$comments = array();
|
||||
foreach ($found_comments as $comment) {
|
||||
|
||||
// We're returning a collection of comment in an array,
|
||||
// in sync with the originator of the request on the ui side
|
||||
// so, we're pulling it one by one into the array before
|
||||
// returning it.
|
||||
|
||||
if (!in_array($comment, $comments)) {
|
||||
array_push($comments, $comment);
|
||||
}
|
||||
}
|
||||
|
||||
return $comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* The _calculate_pages function generates and builds the pagination links
|
||||
* based on the current search parameters/filters. Please see _search_comments
|
||||
* for the breakdown of these parameters.
|
||||
*
|
||||
* @param array $query Query to generate pagination links
|
||||
* @return array
|
||||
*/
|
||||
private function _calculate_pages($query) {
|
||||
$per_page_options = array(10, 20, 30, 40, 50);
|
||||
|
||||
if (!empty($query)) {
|
||||
if (!empty($query['search'])) {
|
||||
return array(
|
||||
'page_count' => 1,
|
||||
'page_no' => 1
|
||||
);
|
||||
}
|
||||
|
||||
$pages = array();
|
||||
$page_query = new WP_Comment_Query;
|
||||
|
||||
// Here, we're pulling the comments based on the
|
||||
// two parameters namely type and status.
|
||||
//
|
||||
// The number of results/comments found will then
|
||||
// be use to compute for the number of pages to be
|
||||
// displayed as navigation links when browsing all
|
||||
// comments from the frontend.
|
||||
|
||||
$comments = $page_query->query(array(
|
||||
'type' => $query['type'],
|
||||
'status' => $query['status']
|
||||
));
|
||||
|
||||
$total_comments = count($comments);
|
||||
$page_count = ceil($total_comments / $query['per_page']);
|
||||
|
||||
if ($page_count > 1) {
|
||||
for ($i = 0; $i < $page_count; $i++) {
|
||||
if ($i + 1 == $query['page_no']) {
|
||||
$paginator_item = array(
|
||||
'value' => $i+1,
|
||||
'setting' => 'disabled'
|
||||
);
|
||||
} else {
|
||||
$paginator_item = array(
|
||||
'value' => $i+1
|
||||
);
|
||||
}
|
||||
array_push($pages, $paginator_item);
|
||||
}
|
||||
|
||||
if ($query['page_no'] >= $page_count) {
|
||||
$page_next = array(
|
||||
'value' => $page_count,
|
||||
'setting' => 'disabled'
|
||||
);
|
||||
} else {
|
||||
$page_next = array(
|
||||
'value' => $query['page_no'] + 1
|
||||
);
|
||||
}
|
||||
|
||||
if (1 === $query['page_no']) {
|
||||
$page_prev = array(
|
||||
'value' => 1,
|
||||
'setting' => 'disabled'
|
||||
);
|
||||
} else {
|
||||
$page_prev = array(
|
||||
'value' => $query['page_no'] - 1
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'page_no' => $query['page_no'],
|
||||
'per_page' => $query['per_page'],
|
||||
'page_count' => $page_count,
|
||||
'pages' => $pages,
|
||||
'page_next' => $page_next,
|
||||
'page_prev' => $page_prev,
|
||||
'total_results' => $total_comments,
|
||||
'per_page_options' => $per_page_options
|
||||
);
|
||||
|
||||
} else {
|
||||
return array(
|
||||
'page_no' => $query['page_no'],
|
||||
'per_page' => $query['per_page'],
|
||||
'page_count' => $page_count,
|
||||
'total_results' => $total_comments,
|
||||
'per_page_options' => $per_page_options
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return array(
|
||||
'per_page_options' => $per_page_options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The get_blog_sites function pulls blog sites available for the current WP instance.
|
||||
* If Multisite is enabled on the server, then sites under the network will be pulled, otherwise, it will return an empty array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_blog_sites() {
|
||||
|
||||
if (!is_multisite()) return array();
|
||||
|
||||
// Initialize array container
|
||||
$sites = $network_sites = array();
|
||||
|
||||
// Check to see if latest get_sites (available on WP version >= 4.6) function is
|
||||
// available to pull any available sites from the current WP instance. If not, then
|
||||
// we're going to use the fallback function wp_get_sites (for older version).
|
||||
|
||||
if (function_exists('get_sites') && class_exists('WP_Site_Query')) {
|
||||
$network_sites = get_sites();
|
||||
} else {
|
||||
if (function_exists('wp_get_sites')) {
|
||||
$network_sites = wp_get_sites();
|
||||
}
|
||||
}
|
||||
|
||||
// We only process if sites array is not empty, otherwise, bypass
|
||||
// the next block.
|
||||
|
||||
if (!empty($network_sites)) {
|
||||
foreach ($network_sites as $site) {
|
||||
|
||||
// Here we're checking if the site type is an array, because
|
||||
// we're pulling the blog_id property based on the type of
|
||||
// site returned.
|
||||
// get_sites returns an array of object, whereas the wp_get_sites
|
||||
// function returns an array of array.
|
||||
|
||||
$blog_id = (is_array($site)) ? $site['blog_id'] : $site->blog_id;
|
||||
|
||||
|
||||
// We're saving the blog_id and blog name as an associative item
|
||||
// into the sites array, that will be used as "Sites" option in
|
||||
// the frontend.
|
||||
|
||||
$sites[$blog_id] = get_blog_details($blog_id)->blogname;
|
||||
}
|
||||
}
|
||||
|
||||
return $sites;
|
||||
}
|
||||
|
||||
/**
|
||||
* The get_wp_option function pulls current blog options
|
||||
* from the database using either following functions:
|
||||
* - get_blog_option (for multisite)
|
||||
* - get_option (for ordinary blog)
|
||||
*
|
||||
* @param array $blog_id This is the specific blog ID
|
||||
* @param array $setting specifies settings
|
||||
* @return array
|
||||
*/
|
||||
private function _get_wp_option($blog_id, $setting) {
|
||||
return is_multisite() ? get_blog_option($blog_id, $setting) : get_option($setting);
|
||||
}
|
||||
|
||||
/**
|
||||
* The get_comments function pull all the comments from the database
|
||||
* based on the current search parameters/filters. Please see _search_comments
|
||||
* for the breakdown of these parameters.
|
||||
*
|
||||
* @param array $query Specific query to pull comments
|
||||
* @return array
|
||||
*/
|
||||
public function get_comments($query) {
|
||||
|
||||
// Here, we're getting the current blog id. If blog id
|
||||
// is passed along with the parameters then we override
|
||||
// that current (default) value with the parameter blog id value.
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($query['blog_id'])) $blog_id = $query['blog_id'];
|
||||
|
||||
|
||||
// Here, we're switching to the actual blog that we need
|
||||
// to pull comments from.
|
||||
|
||||
$switched = false;
|
||||
if (function_exists('switch_to_blog')) {
|
||||
$switched = switch_to_blog($blog_id);
|
||||
}
|
||||
|
||||
if (!empty($query['search'])) {
|
||||
// If a search keyword is present, then we'll call the _search_comments
|
||||
// function to process the query.
|
||||
|
||||
$comments = $this->_search_comments($query);
|
||||
} else {
|
||||
// Set default parameter values if the designated
|
||||
// parameters are empty.
|
||||
|
||||
if (empty($query['per_page'])) {
|
||||
$query['per_page'] = 10;
|
||||
}
|
||||
if (empty($query['page_no'])) {
|
||||
$query['page_no'] = 1;
|
||||
}
|
||||
if (empty($query['type'])) {
|
||||
$query['type'] = '';
|
||||
}
|
||||
if (empty($query['status'])) {
|
||||
$query['status'] = '';
|
||||
}
|
||||
|
||||
// Since WP_Comment_Query parameters doesn't have a "page" attribute, we
|
||||
// need to compute for the offset to get the exact content based on the
|
||||
// current page and the number of items per page.
|
||||
|
||||
$offset = ((int) $query['page_no'] - 1) * (int) $query['per_page'];
|
||||
$args = array(
|
||||
'orderby' => 'ID',
|
||||
'order' => 'DESC',
|
||||
'number' => $query['per_page'],
|
||||
'offset' => $offset,
|
||||
'type' => $query['type'],
|
||||
'status' => $query['status']
|
||||
);
|
||||
|
||||
$comments_query = new WP_Comment_Query;
|
||||
$comments = $comments_query->query($args);
|
||||
}
|
||||
|
||||
// If no comments are found based on the current query then
|
||||
// we return with error.
|
||||
|
||||
if (empty($comments)) {
|
||||
$result = array('message' => 'comments_not_found');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Otherwise, we're going to process each comment
|
||||
// before we return it to the one issuing the request.
|
||||
//
|
||||
// Process in the sense that we add additional related info
|
||||
// such as the post tile where the comment belongs to, the
|
||||
// comment status, a formatted date field, and to which parent comment
|
||||
// does the comment was intended to be as a reply.
|
||||
|
||||
foreach ($comments as &$comment) {
|
||||
$comment = get_comment($comment->comment_ID, ARRAY_A);
|
||||
if ($comment) {
|
||||
$post = get_post($comment['comment_post_ID']);
|
||||
|
||||
if ($post) $comment['in_response_to'] = $post->post_title;
|
||||
if (!empty($comment['comment_parent'])) {
|
||||
$parent_comment = get_comment($comment['comment_parent'], ARRAY_A);
|
||||
if ($parent_comment) $comment['in_reply_to'] = $parent_comment['comment_author'];
|
||||
}
|
||||
|
||||
// We're formatting the comment_date to be exactly the same
|
||||
// with that of WP Comments table (e.g. 2016/12/21 at 10:30 PM)
|
||||
|
||||
$comment['comment_date'] = date('Y/m/d \a\t g:i a', strtotime($comment['comment_date']));
|
||||
|
||||
$status = wp_get_comment_status($comment['comment_ID']);
|
||||
if ($status) {
|
||||
$comment['comment_status'] = $status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We return the following to the one issuing
|
||||
// the request.
|
||||
|
||||
$result = array(
|
||||
'comments' => $comments,
|
||||
'paging' => $this->_calculate_pages($query)
|
||||
);
|
||||
|
||||
|
||||
// Here, we're restoring to the current (default) blog before we
|
||||
// do the switched.
|
||||
|
||||
if (function_exists('restore_current_blog') && $switched) {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The get_comment_filters function builds a array of options
|
||||
* to be use as filters for the search function on the frontend.
|
||||
*/
|
||||
public function get_comment_filters() {
|
||||
// Options for comment_types field
|
||||
$comment_types = apply_filters('admin_comment_types_dropdown', array(
|
||||
'comment' => __('Comments'),// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core.
|
||||
'pings' => __('Pings'),// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core.
|
||||
));
|
||||
|
||||
// Options for comment_status field
|
||||
$comment_statuses = array(
|
||||
'approve' => __('Approve'),// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core.
|
||||
'hold' => __('Hold or Unapprove', 'updraftplus'),
|
||||
'trash' => __('Trash', 'updraftplus'),
|
||||
'spam' => __('Spam', 'updraftplus'),
|
||||
);
|
||||
|
||||
// Pull sites options if available.
|
||||
$sites = $this->get_blog_sites();
|
||||
|
||||
$result = array(
|
||||
'sites' => $sites,
|
||||
'types' => $comment_types,
|
||||
'statuses' => $comment_statuses,
|
||||
'paging' => $this->_calculate_pages(null),
|
||||
);
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The get_settings function pulls the current discussion settings
|
||||
* option values.
|
||||
*
|
||||
* @param array $params Passing specific params for getting current discussion settings
|
||||
* @return array
|
||||
*/
|
||||
public function get_settings($params) {
|
||||
global $updraftcentral_main;
|
||||
|
||||
// Here, we're getting the current blog id. If blog id
|
||||
// is passed along with the parameters then we override
|
||||
// that current (default) value with the parameter blog id value.
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($params['blog_id'])) $blog_id = $params['blog_id'];
|
||||
|
||||
|
||||
// If user does not have sufficient privileges to manage and edit
|
||||
// WP options then we return with error.
|
||||
|
||||
if (!current_user_can_for_blog($blog_id, 'manage_options')) {
|
||||
$result = array('error' => true, 'message' => 'insufficient_permission');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Pull sites options if available.
|
||||
$sites = $this->get_blog_sites();
|
||||
|
||||
// Wrap current discussion settings values into an array item
|
||||
// named settings.
|
||||
|
||||
$result = array(
|
||||
'settings' => array(
|
||||
'default_pingback_flag' => $this->_get_wp_option($blog_id, 'default_pingback_flag'),
|
||||
'default_ping_status' => $this->_get_wp_option($blog_id, 'default_ping_status'),
|
||||
'default_comment_status' => $this->_get_wp_option($blog_id, 'default_comment_status'),
|
||||
'require_name_email' => $this->_get_wp_option($blog_id, 'require_name_email'),
|
||||
'comment_registration' => $this->_get_wp_option($blog_id, 'comment_registration'),
|
||||
'close_comments_for_old_posts' => $this->_get_wp_option($blog_id, 'close_comments_for_old_posts'),
|
||||
'close_comments_days_old' => $this->_get_wp_option($blog_id, 'close_comments_days_old'),
|
||||
'thread_comments' => $this->_get_wp_option($blog_id, 'thread_comments'),
|
||||
'thread_comments_depth' => $this->_get_wp_option($blog_id, 'thread_comments_depth'),
|
||||
'page_comments' => $this->_get_wp_option($blog_id, 'page_comments'),
|
||||
'comments_per_page' => $this->_get_wp_option($blog_id, 'comments_per_page'),
|
||||
'default_comments_page' => $this->_get_wp_option($blog_id, 'default_comments_page'),
|
||||
'comment_order' => $this->_get_wp_option($blog_id, 'comment_order'),
|
||||
'comments_notify' => $this->_get_wp_option($blog_id, 'comments_notify'),
|
||||
'moderation_notify' => $this->_get_wp_option($blog_id, 'moderation_notify'),
|
||||
'comment_moderation' => $this->_get_wp_option($blog_id, 'comment_moderation'),
|
||||
'comment_max_links' => $this->_get_wp_option($blog_id, 'comment_max_links'),
|
||||
'moderation_keys' => $this->_get_wp_option($blog_id, 'moderation_keys'),
|
||||
),
|
||||
'sites' => $sites,
|
||||
);
|
||||
|
||||
$wp_version = $updraftcentral_main->get_wordpress_version();
|
||||
if (version_compare($wp_version, '5.5.0', '<')) {
|
||||
$result['settings']['comment_whitelist'] = $this->_get_wp_option($blog_id, 'comment_whitelist');
|
||||
$result['settings']['blacklist_keys'] = $this->_get_wp_option($blog_id, 'blacklist_keys');
|
||||
} else {
|
||||
$result['settings']['comment_previously_approved'] = $this->_get_wp_option($blog_id, 'comment_previously_approved');
|
||||
$result['settings']['disallowed_keys'] = $this->_get_wp_option($blog_id, 'disallowed_keys');
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The update_settings function updates the discussion settings
|
||||
* basing on the user generated content/option from the frontend
|
||||
* form.
|
||||
*
|
||||
* @param array $params Specific params to update settings based on discussion
|
||||
* @return array
|
||||
*/
|
||||
public function update_settings($params) {
|
||||
|
||||
// Extract settings values from passed parameters.
|
||||
$settings = $params['settings'];
|
||||
|
||||
// Here, we're getting the current blog id. If blog id
|
||||
// is passed along with the parameters then we override
|
||||
// that current (default) value with the parameter blog id value.
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($params['blog_id'])) $blog_id = $params['blog_id'];
|
||||
|
||||
|
||||
// If user does not have sufficient privileges to manage and edit
|
||||
// WP options then we return with error.
|
||||
|
||||
if (!current_user_can_for_blog($blog_id, 'manage_options')) {
|
||||
$result = array('error' => true, 'message' => 'insufficient_permission');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Here, we're sanitizing the input fields before we save them to the database
|
||||
// for safety and security reason. The "explode" and "implode" functions are meant
|
||||
// to maintain the line breaks associated with a textarea input/value.
|
||||
|
||||
foreach ($settings as $key => $value) {
|
||||
|
||||
// We're using update_blog_option and update_option altogether to update the current
|
||||
// discussion settings.
|
||||
|
||||
if (is_multisite()) {
|
||||
update_blog_option($blog_id, $key, implode("\n", array_map('sanitize_text_field', explode("\n", $value))));
|
||||
} else {
|
||||
update_option($key, implode("\n", array_map('sanitize_text_field', explode("\n", $value))));
|
||||
}
|
||||
}
|
||||
|
||||
// We're not checking for errors here, but instead we're directly returning a success (error = false)
|
||||
// status always, because WP's update_option will return fail if values were not changed, meaning
|
||||
// previous values were not changed by the user's current request, not an actual exception thrown.
|
||||
// Thus, giving a false positive message or report to the frontend.
|
||||
|
||||
$result = array('error' => false, 'message' => 'settings_updated', 'values' => array());
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The get_comment function pulls a single comment based
|
||||
* on a comment ID.
|
||||
*
|
||||
* @param array $params Specific params for getting a single comment
|
||||
* @return array
|
||||
*/
|
||||
public function get_comment($params) {
|
||||
|
||||
// Here, we're getting the current blog id. If blog id
|
||||
// is passed along with the parameters then we override
|
||||
// that current (default) value with the parameter blog id value.
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($params['blog_id'])) $blog_id = $params['blog_id'];
|
||||
|
||||
|
||||
// If user does not have sufficient privileges to moderate or edit
|
||||
// a comment then we return with error.
|
||||
|
||||
if (!current_user_can_for_blog($blog_id, 'moderate_comments')) {
|
||||
$result = array('error' => true, 'message' => 'insufficient_permission');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Here, we're switching to the actual blog that we need
|
||||
// to pull comments from.
|
||||
|
||||
$switched = false;
|
||||
if (function_exists('switch_to_blog')) {
|
||||
$switched = switch_to_blog($blog_id);
|
||||
}
|
||||
|
||||
// Get comment by comment_ID parameter and return result as an array.
|
||||
$result = array(
|
||||
'comment' => get_comment($params['comment_id'], ARRAY_A)
|
||||
);
|
||||
|
||||
|
||||
// Here, we're restoring to the current (default) blog before we
|
||||
// do the switched.
|
||||
|
||||
if (function_exists('restore_current_blog') && $switched) {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The reply_comment function creates a new comment as a reply
|
||||
* to a certain/selected comment.
|
||||
*
|
||||
* @param array $params Specific params to create a new comment reply
|
||||
* @return array
|
||||
*/
|
||||
public function reply_comment($params) {
|
||||
|
||||
// Extract reply info from the passed parameters
|
||||
$reply = $params['comment'];
|
||||
|
||||
// Here, we're getting the current blog id. If blog id
|
||||
// is passed along with the parameters then we override
|
||||
// that current (default) value with the parameter blog id value.
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($params['blog_id'])) $blog_id = $params['blog_id'];
|
||||
|
||||
|
||||
// If user does not have sufficient privileges to moderate or edit
|
||||
// a comment then we return with error.
|
||||
|
||||
if (!current_user_can_for_blog($blog_id, 'moderate_comments')) {
|
||||
$result = array('error' => true, 'message' => 'comment_reply_no_permission');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Here, we're switching to the actual blog that we need
|
||||
// to apply our changes.
|
||||
|
||||
$switched = false;
|
||||
if (function_exists('switch_to_blog')) {
|
||||
$switched = switch_to_blog($blog_id);
|
||||
}
|
||||
|
||||
|
||||
// Get comment by comment_ID parameter.
|
||||
$comment = get_comment($reply['comment_id']);
|
||||
if ($comment) {
|
||||
|
||||
// Get the currently logged in user
|
||||
$user = wp_get_current_user();
|
||||
|
||||
// If the current comment was not approved yet then
|
||||
// we need to approve it before we create a reply to
|
||||
// to the comment, mimicking exactly the WP behaviour
|
||||
// in terms of creating a reply to a comment.
|
||||
|
||||
if (empty($comment->comment_approved)) {
|
||||
$update_data = array(
|
||||
'comment_ID' => $reply['comment_id'],
|
||||
'comment_approved' => 1
|
||||
);
|
||||
wp_update_comment($update_data);
|
||||
}
|
||||
|
||||
// Build new comment parameters based on current user info and
|
||||
// the target comment for the reply.
|
||||
$data = array(
|
||||
'comment_post_ID' => $comment->comment_post_ID,
|
||||
'comment_author' => $user->display_name,
|
||||
'comment_author_email' => $user->user_email,
|
||||
'comment_author_url' => $user->user_url,
|
||||
'comment_content' => $reply['message'],
|
||||
'comment_parent' => $reply['comment_id'],
|
||||
'user_id' => $user->ID,
|
||||
'comment_date' => current_time('mysql'),
|
||||
'comment_approved' => 1
|
||||
);
|
||||
|
||||
// Create new comment based on the parameters above, and return
|
||||
// the status accordingly.
|
||||
|
||||
if (wp_insert_comment($data)) {
|
||||
$result = array('error' => false, 'message' => 'comment_replied_with_comment_author', 'values' => array($comment->comment_author));
|
||||
} else {
|
||||
$result = array('error' => true, 'message' => 'comment_reply_failed_with_error', 'values' => array($comment->comment_ID));
|
||||
}
|
||||
} else {
|
||||
$result = array('error' => true, 'message' => 'comment_does_not_exists_error', 'values' => array($reply['comment_id']));
|
||||
}
|
||||
|
||||
|
||||
// Here, we're restoring to the current (default) blog before we
|
||||
// do the switched.
|
||||
|
||||
if (function_exists('restore_current_blog') && $switched) {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The edit_comment function saves new information for the
|
||||
* currently selected comment.
|
||||
*
|
||||
* @param array $params Specific params for editing a comment
|
||||
* @return array
|
||||
*/
|
||||
public function edit_comment($params) {
|
||||
|
||||
// Extract new comment info from the passed parameters
|
||||
$comment = $params['comment'];
|
||||
|
||||
// Here, we're getting the current blog id. If blog id
|
||||
// is passed along with the parameters then we override
|
||||
// that current (default) value with the parameter blog id value.
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($params['blog_id'])) $blog_id = $params['blog_id'];
|
||||
|
||||
|
||||
// If user does not have sufficient privileges to moderate or edit
|
||||
// a comment then we return with error.
|
||||
|
||||
if (!current_user_can_for_blog($blog_id, 'moderate_comments')) {
|
||||
$result = array('error' => true, 'message' => 'comment_edit_no_permission');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Here, we're switching to the actual blog that we need
|
||||
// to apply our changes.
|
||||
|
||||
$switched = false;
|
||||
if (function_exists('switch_to_blog')) {
|
||||
$switched = switch_to_blog($blog_id);
|
||||
}
|
||||
|
||||
|
||||
// Get current comment details
|
||||
$original_comment = get_comment($comment['comment_id']);
|
||||
if ($original_comment) {
|
||||
$data = array();
|
||||
|
||||
// Replace "comment_id" with "comment_ID" since WP does not recognize
|
||||
// the small case "id".
|
||||
$comment['comment_ID'] = $original_comment->comment_ID;
|
||||
unset($comment['comment_id']);
|
||||
|
||||
// Here, we're sanitizing the input fields before we save them to the database
|
||||
// for safety and security reason. The "explode" and "implode" functions are meant
|
||||
// to maintain the line breaks associated with a textarea input/value.
|
||||
|
||||
foreach ($comment as $key => $value) {
|
||||
$data[$key] = implode("\n", array_map('sanitize_text_field', explode("\n", $value)));
|
||||
}
|
||||
|
||||
// Update existing comment based on the passed parameter fields and
|
||||
// return the status accordingly.
|
||||
|
||||
if (wp_update_comment($data)) {
|
||||
$result = array('error' => false, 'message' => 'comment_edited_with_comment_author', 'values' => array($original_comment->comment_author));
|
||||
} else {
|
||||
$result = array('error' => true, 'message' => 'comment_edit_failed_with_error', 'values' => array($original_comment->comment_ID));
|
||||
}
|
||||
} else {
|
||||
$result = array('error' => true, 'message' => 'comment_does_not_exists_error', 'values' => array($comment['comment_id']));
|
||||
}
|
||||
|
||||
// Here, we're restoring to the current (default) blog before we
|
||||
// do the switched.
|
||||
|
||||
if (function_exists('restore_current_blog') && $switched) {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The update_comment_status function is a generic handler for the following
|
||||
* comment actions:
|
||||
*
|
||||
* - approve comment
|
||||
* - unapprove comment
|
||||
* - set comment as spam
|
||||
* - move comment to trash
|
||||
* - delete comment permanently
|
||||
* - unset comment as spam
|
||||
* - restore comment
|
||||
*
|
||||
* @param array $params Specific params to update comment status
|
||||
* @return array
|
||||
*/
|
||||
public function update_comment_status($params) {
|
||||
|
||||
// Here, we're getting the current blog id. If blog id
|
||||
// is passed along with the parameters then we override
|
||||
// that current (default) value with the parameter blog id value.
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($params['blog_id'])) $blog_id = $params['blog_id'];
|
||||
|
||||
|
||||
// If user does not have sufficient privileges to moderate or edit
|
||||
// a comment then we return with error.
|
||||
|
||||
if (!current_user_can_for_blog($blog_id, 'moderate_comments')) {
|
||||
$result = array('error' => true, 'message' => 'comment_change_status_no_permission');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Here, we're switching to the actual blog that we need
|
||||
// to apply our changes.
|
||||
|
||||
$switched = false;
|
||||
if (function_exists('switch_to_blog')) {
|
||||
$switched = switch_to_blog($blog_id);
|
||||
}
|
||||
|
||||
|
||||
// We make sure that we still have a valid comment from the server
|
||||
// before we apply the currently selected action.
|
||||
|
||||
$comment = get_comment($params['comment_id']);
|
||||
if ($comment) {
|
||||
$post = get_post($comment->comment_post_ID);
|
||||
|
||||
if ($post) $comment->in_response_to = $post->post_title;
|
||||
if (!empty($comment->comment_parent)) {
|
||||
$parent_comment = get_comment($comment->comment_parent);
|
||||
if ($parent_comment) $comment->in_reply_to = $parent_comment->comment_author;
|
||||
}
|
||||
|
||||
// We're formatting the comment_date to be exactly the same
|
||||
// with that of WP Comments table (e.g. 2016/12/21 at 10:30 PM)
|
||||
|
||||
$comment->comment_date = date('Y/m/d \a\t g:i a', strtotime($comment->comment_date));
|
||||
|
||||
$status = wp_get_comment_status($comment->comment_ID);
|
||||
if ($status) {
|
||||
$comment->comment_status = $status;
|
||||
}
|
||||
|
||||
$succeeded = false;
|
||||
$message = '';
|
||||
|
||||
// Here, we're using WP's wp_set_comment_status function to change the state
|
||||
// of the selected comment based on the current action, except for the "delete" action
|
||||
// where we use the wp_delete_comment to delete the comment permanently by passing
|
||||
// "true" to the second argument.
|
||||
|
||||
switch ($params['action']) {
|
||||
case 'approve':
|
||||
$succeeded = wp_set_comment_status($params['comment_id'], 'approve');
|
||||
$message = 'comment_approve_with_comment_author';
|
||||
break;
|
||||
case 'unapprove':
|
||||
$succeeded = wp_set_comment_status($params['comment_id'], 'hold');
|
||||
$message = 'comment_unapprove_with_comment_author';
|
||||
break;
|
||||
case 'spam':
|
||||
$succeeded = wp_set_comment_status($params['comment_id'], 'spam');
|
||||
$message = 'comment_spam_with_comment_author';
|
||||
break;
|
||||
case 'trash':
|
||||
$succeeded = wp_set_comment_status($params['comment_id'], 'trash');
|
||||
$message = 'comment_trash_with_comment_author';
|
||||
break;
|
||||
case 'delete':
|
||||
$succeeded = wp_delete_comment($params['comment_id'], true);
|
||||
$message = 'comment_delete_with_comment_author';
|
||||
break;
|
||||
case 'notspam':
|
||||
$succeeded = wp_set_comment_status($params['comment_id'], 'hold');
|
||||
$message = 'comment_not_spam_with_comment_author';
|
||||
break;
|
||||
case 'restore':
|
||||
$succeeded = wp_set_comment_status($params['comment_id'], 'hold');
|
||||
$message = 'comment_restore_with_comment_author';
|
||||
break;
|
||||
}
|
||||
|
||||
// If the current action succeeded, then we return a success message, otherwise,
|
||||
// we return an error message to the user issuing the request.
|
||||
|
||||
if ($succeeded) {
|
||||
$result = array('error' => false, 'message' => $message, 'values' => array($comment->comment_author), 'status' => $comment->comment_status, 'approved' => $comment->comment_approved);
|
||||
} else {
|
||||
$result = array('error' => true, 'message' => 'comment_change_status_failed_with_error', 'values' => array($comment->comment_ID));
|
||||
}
|
||||
} else {
|
||||
$result = array('error' => true, 'message' => 'comment_does_not_exists_error', 'values' => array($params['comment_id']));
|
||||
}
|
||||
|
||||
// Here, we're restoring to the current (default) blog before we
|
||||
// do the switched.
|
||||
|
||||
if (function_exists('restore_current_blog') && $switched) {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
}
|
||||
653
wp-content/plugins/updraftplus/central/modules/core.php
Normal file
@@ -0,0 +1,653 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* - A container for RPC commands (core UpdraftCentral commands). Commands map exactly onto method names (and hence this class should not implement anything else, beyond the constructor, and private methods)
|
||||
* - Return format is array('response' => (string - a code), 'data' => (mixed));
|
||||
*
|
||||
* RPC commands are not allowed to begin with an underscore. So, any private methods can be prefixed with an underscore.
|
||||
*/
|
||||
class UpdraftCentral_Core_Commands extends UpdraftCentral_Commands {
|
||||
|
||||
/**
|
||||
* Retrieve site icon (favicon)
|
||||
*
|
||||
* @return array An array containing the site icon (favicon) byte string if available
|
||||
*/
|
||||
public function get_site_icon() {
|
||||
|
||||
if (!function_exists('get_site_icon_url')) {
|
||||
include_once(ABSPATH.'wp-includes/general-template.php');
|
||||
}
|
||||
|
||||
if (!class_exists('UpdraftCentral_Media_Commands') && defined('UPDRAFTCENTRAL_CLIENT_DIR')) {
|
||||
include_once(UPDRAFTCENTRAL_CLIENT_DIR.'/modules/media.php');
|
||||
}
|
||||
|
||||
$media = new UpdraftCentral_Media_Commands($this);
|
||||
$icon_details = array();
|
||||
|
||||
$site_icon_url = get_site_icon_url();
|
||||
|
||||
// If none is set in WordPress, let's try to search for the default favicon
|
||||
// within the site's directory
|
||||
if (empty($site_icon_url)) {
|
||||
|
||||
if (!function_exists('get_site_url')) {
|
||||
include_once(ABSPATH.'wp-includes/link-template.php');
|
||||
}
|
||||
|
||||
// Common favicon locations to check
|
||||
$potential_locations = array(
|
||||
'/favicon.ico',
|
||||
'/favicon.png',
|
||||
'/favicon.svg',
|
||||
'/assets/favicon.ico',
|
||||
'/assets/images/favicon.ico',
|
||||
'/apple-touch-icon.png',
|
||||
'/apple-touch-icon-precomposed.png',
|
||||
);
|
||||
|
||||
foreach ($potential_locations as $location) {
|
||||
$path = rtrim(ABSPATH, '/\\').$location;
|
||||
if (file_exists($path)) {
|
||||
$site_icon_url = get_site_url().$location;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$site_icon_id = (int) get_option('site_icon');
|
||||
if ($site_icon_id) $icon_details = $media->get_media_item(array('id' => $site_icon_id), null, true);
|
||||
}
|
||||
|
||||
// We are returning the site icon as byte string instead of URL in order to avoid
|
||||
// any hotlink protection that might prevent us to show the icon in UpdraftCentral
|
||||
// dashboard successfully.
|
||||
$site_icon = '';
|
||||
if (!empty($site_icon_url)) {
|
||||
$content = file_get_contents($site_icon_url);
|
||||
|
||||
$mime_type = '';
|
||||
foreach ($http_response_header as $value) {
|
||||
if (false !== stripos($value, 'content-type:')) {
|
||||
list(, $mime_type) = explode(':', preg_replace('/\s+/', '', $value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($content && !empty($mime_type)) {
|
||||
$site_icon = 'data: '.$mime_type.';base64,'.base64_encode($content);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_response(array('site_icon' => $site_icon, 'icon_details' => $icon_details));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles site icon upload
|
||||
*
|
||||
* @param Array $query An array containing the image data.
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function handle_site_icon_upload($query) {
|
||||
if (!current_user_can('upload_files')) {
|
||||
return $this->_generic_error_response('insufficient_permission', array('error_message' => __('You do not have the necessary permissions to upload files.', 'updraftcentral')));
|
||||
}
|
||||
|
||||
$data_uri = sanitize_text_field($query['data_uri']);
|
||||
$filename = sanitize_text_field(basename($query['filename']));
|
||||
$file_ext = pathinfo($filename, PATHINFO_EXTENSION);
|
||||
$history = sanitize_text_field($query['history']);
|
||||
$preview = sanitize_text_field($query['preview']);
|
||||
$pattern = '/^data:(image\/[^;]+);base64,([A-Za-z0-9+\/=]+)$/i';
|
||||
|
||||
if (!empty($data_uri) && preg_match($pattern, $data_uri, $matches)) {
|
||||
list(, $data) = explode(';', $data_uri);
|
||||
list(, $data) = explode(',', $data);
|
||||
|
||||
$decoded_data = base64_decode($data);
|
||||
if (!class_exists('wp_tempnam')) include_once(ABSPATH.'/wp-admin/includes/file.php');
|
||||
$temp_img_file = wp_tempnam();
|
||||
@file_put_contents($temp_img_file, $decoded_data); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the call.
|
||||
$mime_type = wp_get_image_mime($temp_img_file);
|
||||
@unlink($temp_img_file); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist.
|
||||
$allowed_ext = array('jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif', 'webp', 'avif', 'heic');
|
||||
if (false !== $mime_type && !empty($matches[1]) && strtolower($mime_type) === strtolower($matches[1]) && !empty($file_ext) && in_array(strtolower($file_ext), $allowed_ext)) {
|
||||
$upload = wp_upload_bits($filename, null, $decoded_data);
|
||||
} else {
|
||||
$upload = array('error' => __("Couldn't verify the actual MIME type of the given site icon image data.", 'updraftcentral'));
|
||||
}
|
||||
|
||||
if (!$upload['error']) {
|
||||
$attachment = array(
|
||||
'guid' => $upload['url'],
|
||||
'post_mime_type' => $upload['type'],
|
||||
'post_title' => sanitize_file_name($filename),
|
||||
'post_content' => '',
|
||||
'post_status' => 'inherit',
|
||||
);
|
||||
|
||||
$attach_id = wp_insert_attachment($attachment, $upload['file']);
|
||||
|
||||
if (!function_exists('wp_generate_attachment_metadata')) {
|
||||
require_once(ABSPATH.'wp-admin/includes/image.php');
|
||||
}
|
||||
|
||||
// Generate metadata and thumbnails
|
||||
$attach_data = wp_generate_attachment_metadata($attach_id, $upload['file']);
|
||||
wp_update_attachment_metadata($attach_id, $attach_data);
|
||||
|
||||
if (update_option('site_icon', $attach_id)) {
|
||||
if (1 == intval($preview)) {
|
||||
if (!class_exists('UpdraftCentral_Media_Commands') && defined('UPDRAFTCENTRAL_CLIENT_DIR')) {
|
||||
include_once(UPDRAFTCENTRAL_CLIENT_DIR.'/modules/media.php');
|
||||
}
|
||||
|
||||
$media = new UpdraftCentral_Media_Commands($this);
|
||||
$icon_details = $media->get_media_item(array('id' => $attach_id), null, true);
|
||||
|
||||
$params = array(
|
||||
'_ajax_nonce' => $icon_details->nonce,
|
||||
'postid' => $attach_id,
|
||||
'history' => $history,
|
||||
'rand' => $icon_details->misc['rand'],
|
||||
);
|
||||
|
||||
$result = $media->image_preview($params);
|
||||
$result['data']['icon_details'] = $icon_details;
|
||||
|
||||
return $result;
|
||||
} else {
|
||||
return $this->get_site_icon();
|
||||
}
|
||||
} else {
|
||||
return $this->_generic_error_response('upload_error', array('error_message' => __('Unable to set uploaded file as site icon.', 'updraftcentral')));
|
||||
}
|
||||
} else {
|
||||
return $this->_generic_error_response('upload_error', array('error_message' => $upload['error']));
|
||||
}
|
||||
} else {
|
||||
return $this->_generic_error_response('data_uri_field_empty_or_invalid', array('error_message' => __('Required data URI is either missing or invalid.', 'updraftcentral')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls blog sites available
|
||||
* for the current WP instance.
|
||||
* If the site is a multisite, then sites under the network
|
||||
* will be pulled, otherwise, it will return an empty array.
|
||||
*
|
||||
* @return Array - an array of sites
|
||||
*/
|
||||
public function get_blog_sites() {
|
||||
|
||||
if (!is_multisite()) {
|
||||
return $this->_generic_error_response('not_multisite');
|
||||
}
|
||||
|
||||
$sites = array();
|
||||
$network_sites = array();
|
||||
|
||||
// Use `get_sites` for WP version >= 4.6 else use old `wp_get_sites`.
|
||||
if (function_exists('get_sites') && class_exists('WP_Site_Query')) {
|
||||
$network_sites = get_sites();
|
||||
} else {
|
||||
if (function_exists('wp_get_sites')) {
|
||||
$network_sites = wp_get_sites();
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($network_sites)) {
|
||||
foreach ($network_sites as $site) {
|
||||
|
||||
// Check if the site type is an array,
|
||||
// because `wp_get_sites` returns site data as associative array while,
|
||||
// `get_sites` returns the data as WP_Site object.
|
||||
$blog_id = is_array($site) ? $site['blog_id'] : $site->blog_id;
|
||||
|
||||
$sites[] = array(
|
||||
'site_id' => $blog_id,
|
||||
'name' => get_blog_details($blog_id)->blogname,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_response(array(
|
||||
'sites' => $sites,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a list of submitted commands (multiplexer)
|
||||
*
|
||||
* @param Array $query An array containing the commands to execute and a flag to indicate how to handle command execution failure.
|
||||
* @return Array An array containing the results of the process.
|
||||
*/
|
||||
public function execute_commands($query) {
|
||||
|
||||
try {
|
||||
|
||||
$commands = $query['commands'];
|
||||
$command_results = array();
|
||||
$error_count = 0;
|
||||
|
||||
/**
|
||||
* Should be one of the following options:
|
||||
* 1 = Abort on first failure
|
||||
* 2 = Abort if any command fails
|
||||
* 3 = Abort if all command fails (default)
|
||||
*/
|
||||
$error_flag = isset($query['error_flag']) ? (int) $query['error_flag'] : 3;
|
||||
|
||||
|
||||
foreach ($commands as $command => $params) {
|
||||
$command_info = apply_filters('updraftcentral_get_command_info', false, $command);
|
||||
if (!$command_info) {
|
||||
list($_prefix, $_command) = explode('.', $command);
|
||||
$command_results[$_prefix][$_command] = array('response' => 'rpcerror', 'data' => array('code' => 'unknown_rpc_command', 'data' => $command));
|
||||
|
||||
$error_count++;
|
||||
if (1 === $error_flag) break;
|
||||
} else {
|
||||
|
||||
$action = $command_info['command'];
|
||||
$command_php_class = $command_info['command_php_class'];
|
||||
|
||||
// Instantiate the command class and execute the needed action
|
||||
if (class_exists($command_php_class)) {
|
||||
$instance = new $command_php_class($this->rc);
|
||||
|
||||
if (method_exists($instance, $action) || is_a($instance, 'UpdraftCentral_UpdraftPlus_Commands') || is_a($instance, 'UpdraftCentral_WP_Optimize_Commands')) {
|
||||
$params = empty($params) ? array() : $params;
|
||||
$call_result = call_user_func(array($instance, $action), $params);
|
||||
|
||||
$command_results[$command] = $call_result;
|
||||
if ('rpcerror' === $call_result['response'] || (isset($call_result['data']['error']) && $call_result['data']['error'])) {
|
||||
$error_count++;
|
||||
if (1 === $error_flag) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 !== $error_count) {
|
||||
// N.B. These error messages should be defined in UpdraftCentral's translation file (dashboard-translations.php)
|
||||
// before actually using this multiplexer function.
|
||||
$message = 'general_command_execution_error';
|
||||
|
||||
switch ($error_flag) {
|
||||
case 1:
|
||||
$message = 'command_execution_aborted';
|
||||
break;
|
||||
case 2:
|
||||
$message = 'failed_to_execute_some_commands';
|
||||
break;
|
||||
case 3:
|
||||
if (count($commands) === $error_count) {
|
||||
$message = 'failed_to_execute_all_commands';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$result = array('error' => true, 'message' => $message, 'values' => $command_results);
|
||||
} else {
|
||||
$result = $command_results;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$result = array('error' => true, 'message' => $e->getMessage());
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the credentials entered by the user
|
||||
*
|
||||
* @param array $creds an array of filesystem credentials
|
||||
* @return array An array containing the result of the validation process.
|
||||
*/
|
||||
public function validate_credentials($creds) {
|
||||
|
||||
try {
|
||||
|
||||
$entity = $creds['entity'];
|
||||
if (isset($creds['filesystem_credentials'])) {
|
||||
parse_str($creds['filesystem_credentials'], $filesystem_credentials);
|
||||
if (is_array($filesystem_credentials)) {
|
||||
foreach ($filesystem_credentials as $key => $value) {
|
||||
// Put them into $_POST, which is where request_filesystem_credentials() checks for them.
|
||||
$_POST[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include the needed WP Core file(s)
|
||||
// template.php needed for submit_button() which is called by request_filesystem_credentials()
|
||||
$this->_admin_include('file.php', 'template.php');
|
||||
|
||||
// Directory entities that we currently need permissions
|
||||
// to update.
|
||||
$entity_directories = array(
|
||||
'plugins' => WP_PLUGIN_DIR,
|
||||
'themes' => WP_CONTENT_DIR.'/themes',
|
||||
'core' => untrailingslashit(ABSPATH)
|
||||
);
|
||||
|
||||
if ('translations' === $entity) {
|
||||
// 'en_US' don't usually have the "languages" folder, thus, we
|
||||
// check if there's a need to ask for filesystem credentials for that
|
||||
// folder if it exists, most especially for locale other than 'en_US'.
|
||||
$language_dir = WP_CONTENT_DIR.'/languages';
|
||||
if ('en_US' !== get_locale() && is_dir($language_dir)) {
|
||||
$entity_directories['translations'] = $language_dir;
|
||||
}
|
||||
}
|
||||
|
||||
$url = wp_nonce_url(site_url());
|
||||
|
||||
$passed = false;
|
||||
if (isset($entity_directories[$entity])) {
|
||||
$directory = $entity_directories[$entity];
|
||||
|
||||
// Check if credentials are valid and have sufficient
|
||||
// privileges to create and delete (e.g. write)
|
||||
ob_start();
|
||||
$credentials = request_filesystem_credentials($url, '', false, $directory);
|
||||
ob_end_clean();
|
||||
|
||||
// The "WP_Filesystem" will suffice in validating the inputted credentials
|
||||
// from UpdraftCentral, as it is already attempting to connect to the filesystem
|
||||
// using the chosen transport (e.g. ssh, ftp, etc.)
|
||||
$passed = WP_Filesystem($credentials, $directory);
|
||||
}
|
||||
|
||||
if ($passed) {
|
||||
$result = array('error' => false, 'message' => 'credentials_ok', 'values' => array());
|
||||
} else {
|
||||
// We're adding some useful error information to help troubleshooting any problems
|
||||
// that may arise in the future. If the user submitted a wrong password or username
|
||||
// it usually falls through here.
|
||||
global $wp_filesystem;
|
||||
|
||||
$errors = array();
|
||||
if (isset($wp_filesystem->errors) && is_wp_error($wp_filesystem->errors)) {
|
||||
$errors = $wp_filesystem->errors->errors;
|
||||
}
|
||||
|
||||
$result = array('error' => true, 'message' => 'failed_credentials', 'values' => array('errors' => $errors));
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$result = array('error' => true, 'message' => $e->getMessage(), 'values' => array());
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the FileSystem Credentials
|
||||
*
|
||||
* Extract the needed filesystem credentials (permissions) to be used
|
||||
* to update/upgrade the plugins, themes and the WP core.
|
||||
*
|
||||
* @return array $result - An array containing the creds form and some flags
|
||||
* to determine whether we need to extract the creds
|
||||
* manually from the user.
|
||||
*/
|
||||
public function get_credentials() {
|
||||
|
||||
try {
|
||||
|
||||
// Check whether user has enough permission to update entities
|
||||
if (!current_user_can('update_plugins') && !current_user_can('update_themes') && !current_user_can('update_core')) return $this->_generic_error_response('updates_permission_denied');
|
||||
|
||||
// Include the needed WP Core file(s)
|
||||
$this->_admin_include('file.php', 'template.php');
|
||||
|
||||
// A container that will hold the state (in this case, either true or false) of
|
||||
// each directory entities (plugins, themes, core) that will be used to determine
|
||||
// whether or not there's a need to show a form that will ask the user for their credentials
|
||||
// manually.
|
||||
$request_filesystem_credentials = array();
|
||||
|
||||
// A container for the filesystem credentials form if applicable.
|
||||
$filesystem_form = '';
|
||||
|
||||
// Directory entities that we currently need permissions
|
||||
// to update.
|
||||
$check_fs = array(
|
||||
'plugins' => WP_PLUGIN_DIR,
|
||||
'themes' => WP_CONTENT_DIR.'/themes',
|
||||
'core' => untrailingslashit(ABSPATH)
|
||||
);
|
||||
|
||||
// Here, we're looping through each entities and find output whether
|
||||
// we have sufficient permissions to update objects belonging to them.
|
||||
foreach ($check_fs as $entity => $dir) {
|
||||
|
||||
// We're determining which method to use when updating
|
||||
// the files in the filesystem.
|
||||
$filesystem_method = get_filesystem_method(array(), $dir);
|
||||
|
||||
// Buffering the output to pull the actual credentials form
|
||||
// currently being used by this WP instance if no sufficient permissions
|
||||
// is found.
|
||||
$url = wp_nonce_url(site_url());
|
||||
|
||||
ob_start();
|
||||
$filesystem_credentials_are_stored = request_filesystem_credentials($url, $filesystem_method);
|
||||
$form = strip_tags(ob_get_contents(), '<div><h2><p><input><label><fieldset><legend><span><em>');
|
||||
|
||||
if (!empty($form)) {
|
||||
$filesystem_form = $form;
|
||||
}
|
||||
ob_end_clean();
|
||||
|
||||
// Save the state whether or not there's a need to show the
|
||||
// credentials form to the user.
|
||||
$request_filesystem_credentials[$entity] = ('direct' !== $filesystem_method && !$filesystem_credentials_are_stored);
|
||||
}
|
||||
|
||||
// Wrapping the credentials info before passing it back
|
||||
// to the client issuing the request.
|
||||
$result = array(
|
||||
'request_filesystem_credentials' => $request_filesystem_credentials,
|
||||
'filesystem_form' => $filesystem_form
|
||||
);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$result = array('error' => true, 'message' => $e->getMessage(), 'values' => array());
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a browser-usable URL which will automatically log the user in to the site
|
||||
*
|
||||
* @param String $redirect_to - the URL to got to after logging in
|
||||
* @param Array $extra_info - valid keys are user_id, which should be a numeric user ID to log in as.
|
||||
*/
|
||||
public function get_login_url($redirect_to, $extra_info) {
|
||||
|
||||
if (is_array($extra_info) && !empty($extra_info['user_id']) && is_numeric($extra_info['user_id'])) {
|
||||
|
||||
$user_id = $extra_info['user_id'];
|
||||
|
||||
if (false == ($login_key = $this->_get_autologin_key($user_id))) return $this->_generic_error_response('user_key_failure');
|
||||
|
||||
// Default value
|
||||
$redirect_url = network_admin_url();
|
||||
if (is_array($redirect_to) && !empty($redirect_to['module'])) {
|
||||
switch ($redirect_to['module']) {
|
||||
case 'updraftplus':
|
||||
if ('initiate_restore' == $redirect_to['action'] && class_exists('UpdraftPlus_Options')) {
|
||||
$redirect_url = UpdraftPlus_Options::admin_page_url().'?page=updraftplus&udaction=initiate_restore&entities='.urlencode($redirect_to['data']['entities']).'&showdata='.urlencode($redirect_to['data']['showdata']).'&backup_timestamp='.(int) $redirect_to['data']['backup_timestamp'];
|
||||
|
||||
} elseif ('download_file' == $redirect_to['action']) {
|
||||
$findex = empty($redirect_to['data']['findex']) ? 0 : (int) $redirect_to['data']['findex'];
|
||||
// e.g. ?udcentral_action=dl&action=updraftplus_spool_file&backup_timestamp=1455101696&findex=0&what=plugins
|
||||
$redirect_url = site_url().'?udcentral_action=spool_file&action=updraftplus_spool_file&findex='.$findex.'&what='.urlencode($redirect_to['data']['what']).'&backup_timestamp='.(int) $redirect_to['data']['backup_timestamp'];
|
||||
}
|
||||
break;
|
||||
case 'direct_url':
|
||||
$redirect_url = $redirect_to['url'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$login_key = apply_filters('updraftplus_remotecontrol_login_key', array(
|
||||
'key' => $login_key,
|
||||
'created' => time(),
|
||||
'redirect_url' => $redirect_url
|
||||
), $redirect_to, $extra_info);
|
||||
|
||||
// Over-write any previous value - only one can be valid at a time)
|
||||
update_user_meta($user_id, 'updraftcentral_login_key', $login_key);
|
||||
|
||||
return $this->_response(array(
|
||||
'login_url' => network_site_url('?udcentral_action=login&login_id='.$user_id.'&login_key='.$login_key['key'])
|
||||
));
|
||||
|
||||
} else {
|
||||
return $this->_generic_error_response('user_unknown');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information derived from phpinfo()
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function phpinfo() {
|
||||
$phpinfo = $this->_get_phpinfo_array();
|
||||
|
||||
if (!empty($phpinfo)) {
|
||||
return $this->_response($phpinfo);
|
||||
}
|
||||
|
||||
return $this->_generic_error_response('phpinfo_fail');
|
||||
}
|
||||
|
||||
/**
|
||||
* The key obtained is only intended to be short-lived. Hence, there's no intention other than that it is random and only used once - only the most recent one is valid.
|
||||
*
|
||||
* @param Integer $user_id Specific user ID to get the autologin key
|
||||
* @return Array
|
||||
*/
|
||||
public function _get_autologin_key($user_id) {
|
||||
$secure_auth_key = defined('SECURE_AUTH_KEY') ? SECURE_AUTH_KEY : hash('sha256', DB_PASSWORD).'_'.rand(0, 999999999);
|
||||
if (!defined('SECURE_AUTH_KEY')) return false;
|
||||
$hash_it = $user_id.'_'.microtime(true).'_'.rand(0, 999999999).'_'.$secure_auth_key;
|
||||
$hash = hash('sha256', $hash_it);
|
||||
return $hash;
|
||||
}
|
||||
|
||||
public function site_info() {
|
||||
global $wpdb;
|
||||
|
||||
// THis is included so we can get $wp_version
|
||||
@include(ABSPATH.WPINC.'/version.php');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function.
|
||||
|
||||
$ud_version = is_a($this->ud, 'UpdraftPlus') ? $this->ud->version : 'none';
|
||||
|
||||
return $this->_response(array(
|
||||
'versions' => array(
|
||||
'ud' => $ud_version,
|
||||
'php' => PHP_VERSION,
|
||||
'wp' => $wp_version,// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- The variable is defined inside the ABSPATH.WPINC.'/version.php'.
|
||||
'mysql' => $wpdb->db_version(),
|
||||
'udrpc_php' => $this->rc->udrpc_version,
|
||||
),
|
||||
'bloginfo' => array(
|
||||
'url' => network_site_url(),
|
||||
'name' => get_bloginfo('name'),
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* This calls the WP_Action within WP
|
||||
*
|
||||
* @param array $data Array of Data to be used within call_wp_action
|
||||
* @return array
|
||||
*/
|
||||
public function call_wordpress_action($data) {
|
||||
if (false === ($updraftplus_admin = $this->_load_ud_admin())) return $this->_generic_error_response('no_updraftplus');
|
||||
$response = $updraftplus_admin->call_wp_action($data);
|
||||
|
||||
if (empty($data["wpaction"])) {
|
||||
return $this->_generic_error_response("error", "no command sent");
|
||||
}
|
||||
|
||||
return $this->_response(array(
|
||||
"response" => $response['response'],
|
||||
"status" => $response['status'],
|
||||
"log" => $response['log']
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get disk space used
|
||||
*
|
||||
* @uses UpdraftPlus_Filesystem_Functions::get_disk_space_used()
|
||||
*
|
||||
* @param String $entity - the entity to count (e.g. 'plugins', 'themes')
|
||||
*
|
||||
* @return Array - response
|
||||
*/
|
||||
public function count($entity) {
|
||||
if (!class_exists('UpdraftPlus_Filesystem_Functions')) return $this->_generic_error_response('no_updraftplus');
|
||||
$response = UpdraftPlus_Filesystem_Functions::get_disk_space_used($entity);
|
||||
|
||||
return $this->_response($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* https://secure.php.net/phpinfo
|
||||
*
|
||||
* @return null|array
|
||||
*/
|
||||
private function _get_phpinfo_array() {
|
||||
if (!function_exists('phpinfo')) return null;
|
||||
ob_start();
|
||||
phpinfo(INFO_GENERAL|INFO_CREDITS|INFO_MODULES);
|
||||
$phpinfo = array('phpinfo' => array());
|
||||
|
||||
if (preg_match_all('#(?:<h2>(?:<a name=".*?">)?(.*?)(?:</a>)?</h2>)|(?:<tr(?: class=".*?")?><t[hd](?: class=".*?")?>(.*?)\s*</t[hd]>(?:<t[hd](?: class=".*?")?>(.*?)\s*</t[hd]>(?:<t[hd](?: class=".*?")?>(.*?)\s*</t[hd]>)?)?</tr>)#s', ob_get_clean(), $matches, PREG_SET_ORDER)) {
|
||||
foreach ($matches as $match) {
|
||||
if (strlen($match[1])) {
|
||||
$phpinfo[$match[1]] = array();
|
||||
} elseif (isset($match[3])) {
|
||||
$keys1 = array_keys($phpinfo);
|
||||
$phpinfo[end($keys1)][$match[2]] = isset($match[4]) ? array($match[3], $match[4]) : $match[3];
|
||||
} else {
|
||||
$keys1 = array_keys($phpinfo);
|
||||
$phpinfo[end($keys1)][] = $match[2];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return $phpinfo;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an UpdraftPlus_Admin object
|
||||
*
|
||||
* @return UpdraftPlus_Admin|Boolean - false in case of failure
|
||||
*/
|
||||
private function _load_ud_admin() {
|
||||
if (!defined('UPDRAFTPLUS_DIR') || !is_file(UPDRAFTPLUS_DIR.'/admin.php')) return false;
|
||||
updraft_try_include_file('admin.php', 'include_once');
|
||||
global $updraftplus_admin;
|
||||
return $updraftplus_admin;
|
||||
}
|
||||
}
|
||||
601
wp-content/plugins/updraftplus/central/modules/media.php
Normal file
@@ -0,0 +1,601 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* Handles Media Commands
|
||||
*/
|
||||
class UpdraftCentral_Media_Commands extends UpdraftCentral_Commands {
|
||||
|
||||
private $switched = false;
|
||||
|
||||
/**
|
||||
* Function that gets called before every action
|
||||
*
|
||||
* @param string $command a string that corresponds to UDC command to call a certain method for this class.
|
||||
* @param array $data an array of data post or get fields
|
||||
* @param array $extra_info extrainfo use in the udrpc_action, e.g. user_id
|
||||
*
|
||||
* link to udrpc_action main function in class UpdraftCentral_Listener
|
||||
*/
|
||||
public function _pre_action($command, $data, $extra_info) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- This function is called from listener.php and $extra_info is being sent.
|
||||
// Here we assign the current blog_id to a variable $blog_id
|
||||
$blog_id = get_current_blog_id();
|
||||
if (!empty($data['site_id'])) $blog_id = $data['site_id'];
|
||||
|
||||
if (function_exists('switch_to_blog') && is_multisite() && $blog_id) {
|
||||
$this->switched = switch_to_blog($blog_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that gets called after every action
|
||||
*
|
||||
* @param string $command a string that corresponds to UDC command to call a certain method for this class.
|
||||
* @param array $data an array of data post or get fields
|
||||
* @param array $extra_info extrainfo use in the udrpc_action, e.g. user_id
|
||||
*
|
||||
* link to udrpc_action main function in class UpdraftCentral_Listener
|
||||
*/
|
||||
public function _post_action($command, $data, $extra_info) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Unused parameter is present because the caller from UpdraftCentral_Listener class uses 3 arguments.
|
||||
// Here, we're restoring to the current (default) blog before we switched
|
||||
if ($this->switched) restore_current_blog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and retrieves posts based from the submitted parameters
|
||||
*
|
||||
* @param array $params Containing all the needed information to filter the results of the current request
|
||||
* @return array
|
||||
*/
|
||||
public function get_media_items($params) {
|
||||
$error = $this->_validate_capabilities(array('upload_files', 'edit_posts'));
|
||||
if (!empty($error)) return $error;
|
||||
|
||||
// check paged parameter; if empty set to defaults
|
||||
$paged = !empty($params['paged']) ? (int) $params['paged'] : 1;
|
||||
$numberposts = !empty($params['numberposts']) ? (int) $params['numberposts'] : 10;
|
||||
$offset = ($paged - 1) * $numberposts;
|
||||
|
||||
$args = array(
|
||||
'posts_per_page' => $numberposts,
|
||||
'paged' => $paged,
|
||||
'offset' => $offset,
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => 'inherit',
|
||||
);
|
||||
|
||||
if (!empty($params['keyword'])) {
|
||||
$args['s'] = $params['keyword'];
|
||||
}
|
||||
|
||||
if (!empty($params['category'])) {
|
||||
if (in_array($params['category'], array('detached', 'unattached'))) {
|
||||
$attachment_ids = $this->get_unattached_ids();
|
||||
} else {
|
||||
$attachment_ids = $this->get_type_ids($params['category']);
|
||||
}
|
||||
|
||||
$args['post__in'] = $attachment_ids;
|
||||
}
|
||||
|
||||
if (!empty($params['date'])) {
|
||||
list($monthnum, $year) = explode(':', $params['date']);
|
||||
|
||||
$args['monthnum'] = $monthnum;
|
||||
$args['year'] = $year;
|
||||
}
|
||||
|
||||
$query = new WP_Query($args);
|
||||
$result = $query->posts;
|
||||
|
||||
$count_posts = (int) $query->found_posts;
|
||||
$page_count = 0;
|
||||
|
||||
if ($count_posts > 0) {
|
||||
$page_count = absint($count_posts / $numberposts);
|
||||
$remainder = absint($count_posts % $numberposts);
|
||||
$page_count = ($remainder > 0) ? ++$page_count : $page_count;
|
||||
}
|
||||
|
||||
$info = array(
|
||||
'page' => $paged,
|
||||
'pages' => $page_count,
|
||||
'results' => $count_posts,
|
||||
'items_from' => (($paged * $numberposts) - $numberposts) + 1,
|
||||
'items_to' => ($paged == $page_count) ? $count_posts : $paged * $numberposts,
|
||||
);
|
||||
|
||||
$media_items = array();
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $item) {
|
||||
$media = $this->get_media_item($item, null, true);
|
||||
if (!empty($media)) {
|
||||
array_push($media_items, $media);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'items' => $media_items,
|
||||
'info' => $info,
|
||||
'options' => array(
|
||||
'date' => $this->get_date_options(),
|
||||
'type' => $this->get_type_options()
|
||||
)
|
||||
);
|
||||
|
||||
return $this->_response($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we have an image editor (e.g. GD, Imagick, etc.) set in place to handle the basic editing
|
||||
* functions such as rotate, crop, etc. If not, then we hide that feature in UpdraftCentral
|
||||
*
|
||||
* @param object $media The media item/object to check
|
||||
* @return boolean
|
||||
*/
|
||||
private function has_image_editor($media) {
|
||||
// Most of the time image library are enabled by default in the php.ini but there's a possbility that users don't
|
||||
// enable them as they have no need for them at the moment or for some other reasons. Thus, we need to confirm
|
||||
// that here through the wp_get_image_editor method.
|
||||
$has_image_editor = true;
|
||||
if (!empty($media)) {
|
||||
if (!function_exists('wp_get_image_editor')) {
|
||||
require_once(ABSPATH.'wp-includes/media.php');
|
||||
}
|
||||
|
||||
if (!function_exists('_load_image_to_edit_path')) {
|
||||
require_once(ABSPATH.'wp-admin/includes/image.php');
|
||||
}
|
||||
|
||||
$image_editor = wp_get_image_editor(_load_image_to_edit_path($media->ID));
|
||||
if (is_wp_error($image_editor)) {
|
||||
$has_image_editor = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $has_image_editor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a single media item information
|
||||
*
|
||||
* @param array $params Containing all the needed information to filter the results of the current request
|
||||
* @param array|null $extra_info Additional information from the current request
|
||||
* @param boolean $raw If set, returns the result of the fetch process unwrapped by the response array
|
||||
* @return array
|
||||
*/
|
||||
public function get_media_item($params, $extra_info = null, $raw = false) {
|
||||
$error = $this->_validate_capabilities(array('upload_files', 'edit_posts'));
|
||||
if (!empty($error)) return $error;
|
||||
|
||||
// Raw means that we need to return the result without wrapping it
|
||||
// with the "$this->_response" function which indicates that the call
|
||||
// was done locally (within the class) and not directly from UpdraftCentral.
|
||||
if ($raw && is_object($params) && isset($params->ID)) {
|
||||
$media = $params;
|
||||
} elseif (is_array($params) && !empty($params['id'])) {
|
||||
$media = get_post($params['id']);
|
||||
}
|
||||
|
||||
if (!function_exists('get_post_mime_types')) {
|
||||
global $updraftcentral_main;
|
||||
|
||||
// For a much later version of WP the "get_post_mime_types" is located
|
||||
// in a different folder. So, we make sure that we have it loaded before
|
||||
// actually using it.
|
||||
if (version_compare($updraftcentral_main->get_wordpress_version(), '3.5', '>=')) {
|
||||
require_once(ABSPATH.WPINC.'/post.php');
|
||||
} else {
|
||||
// For WP 3.4, the "get_post_mime_types" is located in the location provided below.
|
||||
require_once(ABSPATH.'wp-admin/includes/post.php');
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('wp_image_editor')) {
|
||||
require_once(ABSPATH.'wp-admin/includes/image-edit.php');
|
||||
}
|
||||
|
||||
if (!function_exists('get_media_item')) {
|
||||
require_once(ABSPATH.'wp-admin/includes/template.php');
|
||||
require_once(ABSPATH.'wp-admin/includes/media.php');
|
||||
}
|
||||
|
||||
|
||||
if ($media) {
|
||||
$thumb = wp_get_attachment_image_src($media->ID, 'thumbnail', true);
|
||||
if (!empty($thumb)) $media->thumb_url = $thumb[0];
|
||||
|
||||
$media->url = wp_get_attachment_url($media->ID);
|
||||
$media->parent_post_title = get_the_title($media->post_parent);
|
||||
$media->author = get_the_author_meta('display_name', $media->post_author);
|
||||
$media->filename = basename($media->url);
|
||||
$media->date = date('Y/m/d', strtotime($media->post_date));
|
||||
$media->upload_date = mysql2date(get_option('date_format'), $media->post_date);
|
||||
|
||||
$media->filesize = 0;
|
||||
$file = get_attached_file($media->ID);
|
||||
if (!empty($file) && file_exists($file)) {
|
||||
$media->filesize = size_format(filesize($file));
|
||||
}
|
||||
|
||||
$media->nonce = wp_create_nonce('image_editor-'.$media->ID);
|
||||
if (false !== strpos($media->post_mime_type, 'image/')) {
|
||||
$meta = wp_get_attachment_metadata($media->ID);
|
||||
|
||||
$thumb = image_get_intermediate_size($media->ID, 'thumbnail');
|
||||
$sub_sizes = isset($meta['sizes']) && is_array($meta['sizes']);
|
||||
|
||||
// Pulling details
|
||||
$sizer = 1;
|
||||
if (isset($meta['width'], $meta['height'])) {
|
||||
$big = max($meta['width'], $meta['height']);
|
||||
$sizer = $big > 400 ? 400 / $big : 1;
|
||||
}
|
||||
|
||||
$constrained_dims = array();
|
||||
if ($thumb && $sub_sizes) {
|
||||
$constrained_dims = wp_constrain_dimensions($thumb['width'], $thumb['height'], 160, 120);
|
||||
}
|
||||
|
||||
$rotate_supported = false;
|
||||
if (function_exists('imagerotate') || wp_image_editor_supports(array('mime_type' => get_post_mime_type($media->ID), 'methods' => array('rotate')))) {
|
||||
$rotate_supported = true;
|
||||
}
|
||||
|
||||
// Check for alternative text if present
|
||||
$alt = get_post_meta($media->ID, '_wp_attachment_image_alt', true);
|
||||
$media->alt = !empty($alt) ? $alt : '';
|
||||
|
||||
// Check whether edited images are restorable
|
||||
$backup_sizes = get_post_meta($media->ID, '_wp_attachment_backup_sizes', true);
|
||||
$can_restore = !empty($backup_sizes) && isset($backup_sizes['full-orig']) && basename($meta['file']) != $backup_sizes['full-orig']['file'];
|
||||
|
||||
$image_edit_overwrite = (!defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE) ? 0 : 1;
|
||||
$media->misc = array(
|
||||
'sizer' => $sizer,
|
||||
'rand' => rand(1, 99999),
|
||||
'constrained_dims' => $constrained_dims,
|
||||
'rotate_supported' => (int) $rotate_supported,
|
||||
'thumb' => $thumb,
|
||||
'meta' => $meta,
|
||||
'alt_text' => $alt,
|
||||
'can_restore' => $can_restore,
|
||||
'image_edit_overwrite' => $image_edit_overwrite
|
||||
);
|
||||
|
||||
$media->has_image_editor = $this->has_image_editor($media);
|
||||
}
|
||||
}
|
||||
|
||||
return $raw ? $media : $this->_response(array('item' => $media));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and retrieves posts based from the submitted parameters
|
||||
*
|
||||
* @param array $params Containing all the needed information to filter the results of the current request
|
||||
* @return array
|
||||
*/
|
||||
public function get_posts($params) {
|
||||
$error = $this->_validate_capabilities(array('edit_posts'));
|
||||
if (!empty($error)) return $error;
|
||||
|
||||
// check paged parameter; if empty set to defaults
|
||||
$paged = !empty($params['paged']) ? (int) $params['paged'] : 1;
|
||||
$numberposts = !empty($params['numberposts']) ? (int) $params['numberposts'] : 10;
|
||||
$offset = ($paged - 1) * $numberposts;
|
||||
|
||||
$args = array(
|
||||
'posts_per_page' => $numberposts,
|
||||
'paged' => $paged,
|
||||
'offset' => $offset,
|
||||
'post_type' => 'post',
|
||||
'post_status' => 'publish,private,draft,pending,future',
|
||||
);
|
||||
|
||||
if (!empty($params['keyword'])) {
|
||||
$args['s'] = $params['keyword'];
|
||||
}
|
||||
|
||||
$query = new WP_Query($args);
|
||||
$result = $query->posts;
|
||||
|
||||
$count_posts = (int) $query->found_posts;
|
||||
$page_count = 0;
|
||||
|
||||
if ($count_posts > 0) {
|
||||
$page_count = absint($count_posts / $numberposts);
|
||||
$remainder = absint($count_posts % $numberposts);
|
||||
$page_count = ($remainder > 0) ? ++$page_count : $page_count;
|
||||
}
|
||||
|
||||
$info = array(
|
||||
'page' => $paged,
|
||||
'pages' => $page_count,
|
||||
'results' => $count_posts,
|
||||
'items_from' => (($paged * $numberposts) - $numberposts) + 1,
|
||||
'items_to' => ($paged == $page_count) ? $count_posts : $paged * $numberposts,
|
||||
);
|
||||
|
||||
$posts = array();
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $post) {
|
||||
array_push($posts, array('ID' => $post->ID, 'title' => $post->post_title));
|
||||
}
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'posts' => $posts,
|
||||
'info' => $info
|
||||
);
|
||||
return $this->_response($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves media changes from UpdraftCentral
|
||||
*
|
||||
* @param array $params Containing all the needed information to filter the results of the current request
|
||||
* @return array
|
||||
*/
|
||||
public function save_media_item($params) {
|
||||
$error = $this->_validate_capabilities(array('upload_files', 'edit_posts'));
|
||||
if (!empty($error)) return $error;
|
||||
|
||||
$args = array(
|
||||
'post_title' => $params['image_title'],
|
||||
'post_excerpt' => $params['image_caption'],
|
||||
'post_content' => $params['image_description']
|
||||
);
|
||||
|
||||
if (!empty($params['new'])) {
|
||||
$args['post_type'] = 'attachment';
|
||||
$media_id = wp_insert_post($args, true);
|
||||
} else {
|
||||
$args['ID'] = $params['id'];
|
||||
$args['post_modified'] = date('Y-m-d H:i:s');
|
||||
$args['post_modified_gmt'] = gmdate('Y-m-d H:i:s');
|
||||
|
||||
$media_id = wp_update_post($args, true);
|
||||
}
|
||||
|
||||
if (!empty($media_id)) {
|
||||
// Update alternative text if not empty
|
||||
if (!empty($params['image_alternative_text'])) {
|
||||
update_post_meta($media_id, '_wp_attachment_image_alt', $params['image_alternative_text']);
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'status' => 'success',
|
||||
'item' => $this->get_media_item(array('id' => $media_id), null, true)
|
||||
);
|
||||
} else {
|
||||
$result = array('status' => 'failed');
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes media action (e.g. attach, detach and delete)
|
||||
*
|
||||
* @param array $params Containing all the needed information to filter the results of the current request
|
||||
* @return array
|
||||
*/
|
||||
public function execute_media_action($params) {
|
||||
global $updraftcentral_host_plugin;
|
||||
|
||||
$error = $this->_validate_capabilities(array('upload_files', 'edit_posts'));
|
||||
if (!empty($error)) return $error;
|
||||
|
||||
$result = array();
|
||||
switch ($params['do']) {
|
||||
case 'attach':
|
||||
global $wpdb;
|
||||
$query_result = $wpdb->query($wpdb->prepare("UPDATE {$wpdb->posts} SET `post_parent` = %d WHERE `post_type` = 'attachment' AND ID = %d", $params['parent_id'], $params['id']));
|
||||
|
||||
if (false === $query_result) {
|
||||
$result['error'] = $updraftcentral_host_plugin->retrieve_show_message('failed_to_attach_media');
|
||||
} else {
|
||||
$result['msg'] = $updraftcentral_host_plugin->retrieve_show_message('media_attached');
|
||||
}
|
||||
break;
|
||||
case 'detach':
|
||||
global $wpdb;
|
||||
$query_result = $wpdb->query($wpdb->prepare("UPDATE {$wpdb->posts} SET `post_parent` = 0 WHERE `post_type` = 'attachment' AND ID = %d", $params['id']));
|
||||
|
||||
if (false === $query_result) {
|
||||
$result['error'] = $updraftcentral_host_plugin->retrieve_show_message('failed_to_detach_media');
|
||||
} else {
|
||||
$result['msg'] = $updraftcentral_host_plugin->retrieve_show_message('media_detached');
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
$failed_items = array();
|
||||
foreach ($params['ids'] as $id) {
|
||||
// Delete permanently
|
||||
if (false === wp_delete_attachment($id, true)) {
|
||||
$failed_items[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($failed_items)) {
|
||||
$result['error'] = $updraftcentral_host_plugin->retrieve_show_message('failed_to_delete_media');
|
||||
$result['items'] = $failed_items;
|
||||
} else {
|
||||
$result['msg'] = $updraftcentral_host_plugin->retrieve_show_message('selected_media_deleted');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a collection of formatted dates found for the given post statuses.
|
||||
* It will be used as options for the date filter when managing the media items in UpdraftCentral.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_date_options() {
|
||||
global $wpdb;
|
||||
$options = array();
|
||||
|
||||
$date_options = $wpdb->get_col("SELECT DATE_FORMAT(`post_date`, '%M %Y') as `formatted_post_date` FROM {$wpdb->posts} WHERE `post_type` = 'attachment' AND `post_status` = 'inherit' GROUP BY `formatted_post_date` ORDER BY `post_date` DESC");
|
||||
|
||||
if (!empty($date_options)) {
|
||||
foreach ($date_options as $monthyear) {
|
||||
$timestr = strtotime($monthyear);
|
||||
$options[] = array('label' => date('F Y', $timestr), 'value' => date('n:Y', $timestr));
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves mime types that will be use as filter option in UpdraftCentral
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_type_options() {
|
||||
global $wpdb, $updraftcentral_host_plugin, $updraftcentral_main;
|
||||
|
||||
$options = array();
|
||||
if (!function_exists('get_post_mime_types')) {
|
||||
// For a much later version of WP the "get_post_mime_types" is located
|
||||
// in a different folder. So, we make sure that we have it loaded before
|
||||
// actually using it.
|
||||
if (version_compare($updraftcentral_main->get_wordpress_version(), '3.5', '>=')) {
|
||||
require_once(ABSPATH.WPINC.'/post.php');
|
||||
} else {
|
||||
// For WP 3.4, the "get_post_mime_types" is located in the location provided below.
|
||||
require_once(ABSPATH.'wp-admin/includes/post.php');
|
||||
}
|
||||
}
|
||||
|
||||
$post_mime_types = get_post_mime_types();
|
||||
$type_options = $wpdb->get_col("SELECT `post_mime_type` FROM {$wpdb->posts} WHERE `post_type` = 'attachment' AND `post_status` = 'inherit' GROUP BY `post_mime_type` ORDER BY `post_mime_type` DESC");
|
||||
|
||||
foreach ($post_mime_types as $mime_type => $label) {
|
||||
if (!wp_match_mime_types($mime_type, $type_options)) continue;
|
||||
$options[] = array('label' => $label[0], 'value' => esc_attr($mime_type));
|
||||
}
|
||||
|
||||
$options[] = array('label' => $updraftcentral_host_plugin->retrieve_show_message('unattached'), 'value' => 'detached');
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves media items that haven't been attached to any posts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_unattached_ids() {
|
||||
global $wpdb;
|
||||
return $wpdb->get_col("SELECT `ID` FROM {$wpdb->posts} WHERE `post_type` = 'attachment' AND `post_status` = 'inherit' AND `post_parent` = '0'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves IDs of media items that has the given mime type
|
||||
*
|
||||
* @param string $type The mime type to search for
|
||||
* @return array
|
||||
*/
|
||||
private function get_type_ids($type) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_col($wpdb->prepare("SELECT `ID` FROM {$wpdb->posts} WHERE `post_type` = 'attachment' AND `post_status` = 'inherit' AND `post_mime_type` LIKE %s", $type.'/%'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether we have the required fields submitted and the user has
|
||||
* the capabilities to execute the requested action
|
||||
*
|
||||
* @param array $capabilities The capabilities to check and validate
|
||||
*
|
||||
* @return array|void
|
||||
*/
|
||||
private function _validate_capabilities($capabilities) {
|
||||
foreach ($capabilities as $capability) {
|
||||
if (!current_user_can($capability)) {
|
||||
return $this->_generic_error_response('insufficient_permission');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the $_REQUEST global variable with the submitted data
|
||||
*
|
||||
* @param array $params Submitted data received from UpdraftCentral
|
||||
* @return array
|
||||
*/
|
||||
private function populate_request($params) {
|
||||
if (!empty($params)) {
|
||||
foreach ($params as $key => $value) {
|
||||
$_REQUEST[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles image editing requests coming from UpdraftCentral
|
||||
*
|
||||
* @param array $params Containing all the needed information to filter the results of the current request
|
||||
* @return array
|
||||
*/
|
||||
public function image_editor($params) {
|
||||
$error = $this->_validate_capabilities(array('edit_posts'));
|
||||
if (!empty($error)) return $error;
|
||||
|
||||
$attachment_id = (int) $params['postid'];
|
||||
$this->populate_request($params);
|
||||
|
||||
if (!function_exists('load_image_to_edit')) {
|
||||
require_once(ABSPATH.'wp-admin/includes/image.php');
|
||||
}
|
||||
|
||||
include_once(ABSPATH.'wp-admin/includes/image-edit.php');
|
||||
$msg = false;
|
||||
switch ($params['do']) {
|
||||
case 'save':
|
||||
case 'scale':
|
||||
$msg = wp_save_image($attachment_id);
|
||||
break;
|
||||
case 'restore':
|
||||
$msg = wp_restore_image($attachment_id);
|
||||
break;
|
||||
}
|
||||
|
||||
$msg = (false !== $msg) ? json_encode($msg) : $msg;
|
||||
return $this->_response(array('content' => $msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles image preview requests coming from UpdraftCentral
|
||||
*
|
||||
* @param array $params Containing all the needed information to filter the results of the current request
|
||||
* @return array
|
||||
*/
|
||||
public function image_preview($params) {
|
||||
$error = $this->_validate_capabilities(array('edit_posts'));
|
||||
if (!empty($error)) return $error;
|
||||
|
||||
if (!function_exists('load_image_to_edit')) {
|
||||
require_once(ABSPATH.'wp-admin/includes/image.php');
|
||||
}
|
||||
|
||||
include_once(ABSPATH.'wp-admin/includes/image-edit.php');
|
||||
$this->populate_request($params);
|
||||
$post_id = (int) $params['postid'];
|
||||
|
||||
ob_start();
|
||||
stream_preview_image($post_id);
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
return $this->_response(array('content' => base64_encode($content)));
|
||||
}
|
||||
}
|
||||
15
wp-content/plugins/updraftplus/central/modules/pages.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
// Load the posts command class since we're going to be extending it for our page module service/command
|
||||
// class in order to minimize redundant shareable methods.
|
||||
if (!class_exists('UpdraftCentral_Posts_Commands')) require_once('posts.php');
|
||||
|
||||
/**
|
||||
* Handles Pages Commands
|
||||
*/
|
||||
class UpdraftCentral_Pages_Commands extends UpdraftCentral_Posts_Commands {
|
||||
|
||||
protected $post_type = 'page';
|
||||
}
|
||||
700
wp-content/plugins/updraftplus/central/modules/plugin.php
Normal file
@@ -0,0 +1,700 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* Handles UpdraftCentral Plugin Commands which basically handles
|
||||
* the installation and activation of a plugin
|
||||
*/
|
||||
class UpdraftCentral_Plugin_Commands extends UpdraftCentral_Commands {
|
||||
|
||||
private $switched = false;
|
||||
|
||||
/**
|
||||
* Function that gets called before every action
|
||||
*
|
||||
* @param string $command a string that corresponds to UDC command to call a certain method for this class.
|
||||
* @param array $data an array of data post or get fields
|
||||
* @param array $extra_info extrainfo use in the udrpc_action, e.g. user_id
|
||||
*
|
||||
* link to udrpc_action main function in class UpdraftCentral_Listener
|
||||
*/
|
||||
public function _pre_action($command, $data, $extra_info) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- This function is called from listener.php and $extra_info is being sent.
|
||||
// Here we assign the current blog_id to a variable $blog_id
|
||||
$blog_id = get_current_blog_id();
|
||||
if (!empty($data['site_id'])) $blog_id = $data['site_id'];
|
||||
|
||||
if (function_exists('switch_to_blog') && is_multisite() && $blog_id) {
|
||||
$this->switched = switch_to_blog($blog_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that gets called after every action
|
||||
*
|
||||
* @param string $command a string that corresponds to UDC command to call a certain method for this class.
|
||||
* @param array $data an array of data post or get fields
|
||||
* @param array $extra_info extrainfo use in the udrpc_action, e.g. user_id
|
||||
*
|
||||
* link to udrpc_action main function in class UpdraftCentral_Listener
|
||||
*/
|
||||
public function _post_action($command, $data, $extra_info) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Unused parameter is present because the caller from UpdraftCentral_Listener class uses 3 arguments.
|
||||
// Here, we're restoring to the current (default) blog before we switched
|
||||
if ($this->switched) restore_current_blog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->_admin_include('plugin.php', 'file.php', 'template.php', 'class-wp-upgrader.php', 'plugin-install.php', 'update.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs and activates a plugin through upload
|
||||
*
|
||||
* @param array $params Parameter array containing information pertaining the currently uploaded plugin
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function upload_plugin($params) {
|
||||
return $this->process_chunk_upload($params, 'plugin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the plugin is currently installed and activated.
|
||||
*
|
||||
* @param array $query Parameter array containing the name of the plugin to check
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function is_plugin_installed($query) {
|
||||
|
||||
if (!isset($query['plugin']))
|
||||
return $this->_generic_error_response('plugin_name_required');
|
||||
|
||||
|
||||
$result = $this->_get_plugin_info($query);
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies currently requested action for plugin processing
|
||||
*
|
||||
* @param string $action The action to apply (e.g. activate or install)
|
||||
* @param array $query Parameter array containing information for the currently requested action
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function _apply_plugin_action($action, $query) {
|
||||
|
||||
$result = array();
|
||||
switch ($action) {
|
||||
case 'activate':
|
||||
case 'network_activate':
|
||||
$info = $this->_get_plugin_info($query);
|
||||
if ($info['installed']) {
|
||||
$activate = activate_plugin($info['plugin_path']);
|
||||
if (is_wp_error($activate)) {
|
||||
$result = $this->_generic_error_response('generic_response_error', array(
|
||||
'plugin' => $query['plugin'],
|
||||
'error_code' => 'generic_response_error',
|
||||
'error_message' => $activate->get_error_message(),
|
||||
'info' => $this->_get_plugin_info($query)
|
||||
));
|
||||
} else {
|
||||
$result = array('activated' => true, 'info' => $this->_get_plugin_info($query), 'last_state' => $info);
|
||||
}
|
||||
} else {
|
||||
$result = $this->_generic_error_response('plugin_not_installed', array(
|
||||
'plugin' => $query['plugin'],
|
||||
'error_code' => 'plugin_not_installed',
|
||||
'error_message' => __('The plugin you wish to activate is either not installed or has been removed recently.', 'updraftplus'),
|
||||
'info' => $info
|
||||
));
|
||||
}
|
||||
break;
|
||||
case 'deactivate':
|
||||
case 'network_deactivate':
|
||||
$info = $this->_get_plugin_info($query);
|
||||
if ($info['active']) {
|
||||
deactivate_plugins($info['plugin_path']);
|
||||
if (!is_plugin_active($info['plugin_path'])) {
|
||||
$result = array('deactivated' => true, 'info' => $this->_get_plugin_info($query), 'last_state' => $info);
|
||||
} else {
|
||||
$result = $this->_generic_error_response('deactivate_plugin_failed', array(
|
||||
'plugin' => $query['plugin'],
|
||||
'error_code' => 'deactivate_plugin_failed',
|
||||
'error_message' => __('There appears to be a problem deactivating the intended plugin.', 'updraftplus').' '.__('Please check your permissions and try again.', 'updraftplus'),
|
||||
'info' => $this->_get_plugin_info($query)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$result = $this->_generic_error_response('not_active', array(
|
||||
'plugin' => $query['plugin'],
|
||||
'error_code' => 'not_active',
|
||||
'error_message' => __('The plugin you wish to deactivate is currently not active or is already deactivated.', 'updraftplus'),
|
||||
'info' => $info
|
||||
));
|
||||
}
|
||||
break;
|
||||
case 'install':
|
||||
$api = plugins_api('plugin_information', array(
|
||||
'slug' => $query['slug'],
|
||||
'fields' => array(
|
||||
'short_description' => false,
|
||||
'sections' => false,
|
||||
'requires' => false,
|
||||
'rating' => false,
|
||||
'ratings' => false,
|
||||
'downloaded' => false,
|
||||
'last_updated' => false,
|
||||
'added' => false,
|
||||
'tags' => false,
|
||||
'compatibility' => false,
|
||||
'homepage' => false,
|
||||
'donate_link' => false,
|
||||
)
|
||||
));
|
||||
|
||||
$info = $this->_get_plugin_info($query);
|
||||
if (is_wp_error($api)) {
|
||||
$result = $this->_generic_error_response('generic_response_error', array(
|
||||
'plugin' => $query['plugin'],
|
||||
'error_code' => 'generic_response_error',
|
||||
'error_message' => $api->get_error_message(),
|
||||
'info' => $info
|
||||
));
|
||||
} else {
|
||||
$installed = $info['installed'];
|
||||
|
||||
$error_code = $error_message = '';
|
||||
if (!$installed) {
|
||||
// WP < 3.7
|
||||
if (!class_exists('Automatic_Upgrader_Skin')) include_once(dirname(dirname(__FILE__)).'/classes/class-automatic-upgrader-skin.php');
|
||||
|
||||
$skin = new Automatic_Upgrader_Skin();
|
||||
$upgrader = new Plugin_Upgrader($skin);
|
||||
|
||||
$download_link = $api->download_link;
|
||||
$installed = $upgrader->install($download_link);
|
||||
|
||||
if (is_wp_error($installed)) {
|
||||
$error_code = $installed->get_error_code();
|
||||
$error_message = $installed->get_error_message();
|
||||
} elseif (is_wp_error($skin->result)) {
|
||||
$error_code = $skin->result->get_error_code();
|
||||
$error_message = $skin->result->get_error_message();
|
||||
|
||||
$error_data = $skin->result->get_error_data($error_code);
|
||||
if (!empty($error_data)) {
|
||||
if (is_array($error_data)) $error_data = json_encode($error_data);
|
||||
$error_message .= ' '.$error_data;
|
||||
}
|
||||
} elseif (is_null($installed) || !$installed) {
|
||||
global $wp_filesystem;
|
||||
$upgrade_messages = $skin->get_upgrade_messages();
|
||||
|
||||
if (!class_exists('WP_Filesystem_Base')) include_once(ABSPATH.'/wp-admin/includes/class-wp-filesystem-base.php');
|
||||
|
||||
// Pass through the error from WP_Filesystem if one was raised.
|
||||
if ($wp_filesystem instanceof WP_Filesystem_Base && is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
||||
$error_code = $wp_filesystem->errors->get_error_code();
|
||||
$error_message = $wp_filesystem->errors->get_error_message();
|
||||
} elseif (!empty($upgrade_messages)) {
|
||||
// We're only after for the last feedback that we received from the install process. Mostly,
|
||||
// that is where the last error has been inserted.
|
||||
$messages = $skin->get_upgrade_messages();
|
||||
$error_code = 'install_failed';
|
||||
$error_message = end($messages);
|
||||
} else {
|
||||
$error_code = 'unable_to_connect_to_filesystem';
|
||||
$error_message = __('Unable to connect to the filesystem.', 'updraftplus').' '.__('Please confirm your credentials.', 'updraftplus');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$installed || is_wp_error($installed)) {
|
||||
$result = $this->_generic_error_response('plugin_install_failed', array(
|
||||
'plugin' => $query['plugin'],
|
||||
'error_code' => $error_code,
|
||||
'error_message' => $error_message,
|
||||
'info' => $this->_get_plugin_info($query)
|
||||
));
|
||||
} else {
|
||||
$result = array('installed' => true, 'info' => $this->_get_plugin_info($query), 'last_state' => $info);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preloads the submitted credentials to the global $_POST variable
|
||||
*
|
||||
* @param array $query Parameter array containing information for the currently requested action
|
||||
*/
|
||||
private function _preload_credentials($query) {
|
||||
if (!empty($query) && isset($query['filesystem_credentials'])) {
|
||||
parse_str($query['filesystem_credentials'], $filesystem_credentials);
|
||||
if (is_array($filesystem_credentials)) {
|
||||
foreach ($filesystem_credentials as $key => $value) {
|
||||
// Put them into $_POST, which is where request_filesystem_credentials() checks for them.
|
||||
$_POST[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether we have the required fields submitted and the user has
|
||||
* the capabilities to execute the requested action
|
||||
*
|
||||
* @param array $query The submitted information
|
||||
* @param array $fields The required fields to check
|
||||
* @param array $capabilities The capabilities to check and validate
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
private function _validate_fields_and_capabilities($query, $fields, $capabilities) {
|
||||
|
||||
$error = '';
|
||||
if (!empty($fields)) {
|
||||
for ($i=0; $i<count($fields); $i++) {
|
||||
$field = $fields[$i];
|
||||
|
||||
if (!isset($query[$field])) {
|
||||
if ('keyword' === $field) {
|
||||
$error = $this->_generic_error_response('keyword_required');
|
||||
} else {
|
||||
$error = $this->_generic_error_response('plugin_'.$query[$field].'_required');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($error) && !empty($capabilities)) {
|
||||
for ($i=0; $i<count($capabilities); $i++) {
|
||||
if (!current_user_can($capabilities[$i])) {
|
||||
$error = $this->_generic_error_response('plugin_insufficient_permission');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processing an action for multiple items
|
||||
*
|
||||
* @param array $query Parameter array containing a list of plugins to process
|
||||
* @return array Contains the results of the bulk process
|
||||
*/
|
||||
public function process_action_in_bulk($query) {
|
||||
$action = isset($query['action']) ? $query['action'] : '';
|
||||
$items = isset($query['args']) ? $query['args']['items'] : array();
|
||||
|
||||
$results = array();
|
||||
if (!empty($action) && !empty($items) && is_array($items)) {
|
||||
foreach ($items as $value) {
|
||||
if (method_exists($this, $action)) {
|
||||
$results[] = $this->$action($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_response($results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the plugin
|
||||
*
|
||||
* @param array $query Parameter array containing the name of the plugin to activate
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function activate_plugin($query) {
|
||||
|
||||
$fields = array('plugin');
|
||||
$permissions = array('activate_plugins');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
$result = $this->_apply_plugin_action((!empty($query['multisite']) && (bool) $query['multisite']) ? 'network_activate' : 'activate', $query);
|
||||
if (empty($result['activated'])) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates the plugin
|
||||
*
|
||||
* @param array $query Parameter array containing the name of the plugin to deactivate
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function deactivate_plugin($query) {
|
||||
|
||||
$fields = array('plugin');
|
||||
$permissions = array('activate_plugins');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
$result = $this->_apply_plugin_action((!empty($query['multisite']) && (bool) $query['multisite']) ? 'network_deactivate' : 'deactivate', $query);
|
||||
if (empty($result['deactivated'])) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download, install and activates the plugin
|
||||
*
|
||||
* @param array $query Parameter array containing the filesystem credentials entered by the user along with the plugin name and slug
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function install_activate_plugin($query) {
|
||||
|
||||
$fields = array('plugin', 'slug');
|
||||
$permissions = array('install_plugins', 'activate_plugins');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
$result = $this->_apply_plugin_action('install', $query);
|
||||
if (!empty($result['installed']) && $result['installed']) {
|
||||
$result = $this->_apply_plugin_action((!empty($query['multisite']) && (bool) $query['multisite']) ? 'network_activate' : 'activate', $query);
|
||||
if (empty($result['activated'])) {
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download, install the plugin
|
||||
*
|
||||
* @param array $query Parameter array containing the filesystem credentials entered by the user along with the plugin name and slug
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function install_plugin($query) {
|
||||
|
||||
$fields = array('plugin', 'slug');
|
||||
$permissions = array('install_plugins');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
$result = $this->_apply_plugin_action('install', $query);
|
||||
if (empty($result['installed'])) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall/delete the plugin
|
||||
*
|
||||
* @param array $query Parameter array containing the filesystem credentials entered by the user along with the plugin name and slug
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function delete_plugin($query) {
|
||||
|
||||
$fields = array('plugin');
|
||||
$permissions = array('delete_plugins');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
$info = $this->_get_plugin_info($query);
|
||||
if ($info['installed']) {
|
||||
// Deactivate first before delete to invalidate the activate
|
||||
// state/status prior to deleting the item. Otherwise, WordPress will keep
|
||||
// that state, and as soon as you install the same plugin it will be automatically
|
||||
// activated since it's previous state was kept.
|
||||
deactivate_plugins($info['plugin_path']);
|
||||
|
||||
$deleted = delete_plugins(array($info['plugin_path']));
|
||||
if ($deleted) {
|
||||
$result = array('deleted' => true, 'info' => $this->_get_plugin_info($query), 'last_state' => $info);
|
||||
} else {
|
||||
return $this->_generic_error_response('delete_plugin_failed', array(
|
||||
'plugin' => $query['plugin'],
|
||||
'error_code' => 'delete_plugin_failed',
|
||||
'info' => $info
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return $this->_generic_error_response('plugin_not_installed', array(
|
||||
'plugin' => $query['plugin'],
|
||||
'error_code' => 'plugin_not_installed',
|
||||
'info' => $info
|
||||
));
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates/upgrade the plugin
|
||||
*
|
||||
* @param array $query Parameter array containing the filesystem credentials entered by the user along with the plugin name and slug
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function update_plugin($query) {
|
||||
|
||||
$fields = array('plugin', 'slug');
|
||||
$permissions = array('update_plugins');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
// Make sure that we still have the plugin installed before running
|
||||
// the update process
|
||||
$info = $this->_get_plugin_info($query);
|
||||
if ($info['installed']) {
|
||||
// Load the updates command class if not existed
|
||||
if (!class_exists('UpdraftCentral_Updates_Commands')) include_once('updates.php');
|
||||
$update_command = new UpdraftCentral_Updates_Commands($this->rc);
|
||||
|
||||
$result = $update_command->update_plugin($info['plugin_path'], $query['slug']);
|
||||
if (!empty($result['error'])) {
|
||||
$result['values'] = array('plugin' => $query['plugin'], 'info' => $info);
|
||||
}
|
||||
} else {
|
||||
return $this->_generic_error_response('plugin_not_installed', array(
|
||||
'plugin' => $query['plugin'],
|
||||
'error_code' => 'plugin_not_installed',
|
||||
'info' => $info
|
||||
));
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin information along with its active and install status
|
||||
*
|
||||
* @internal
|
||||
* @param array $query Contains either the plugin name or slug or both to be used when retrieving information
|
||||
* @return array
|
||||
*/
|
||||
private function _get_plugin_info($query) {
|
||||
|
||||
$info = array(
|
||||
'active' => false,
|
||||
'installed' => false
|
||||
);
|
||||
|
||||
// Clear plugin cache so that newly installed/downloaded plugins
|
||||
// gets reflected when calling "get_plugins"
|
||||
if (function_exists('wp_clean_plugins_cache')) {
|
||||
wp_clean_plugins_cache();
|
||||
}
|
||||
|
||||
// Gets all plugins available.
|
||||
$get_plugins = get_plugins();
|
||||
|
||||
// Loops around each plugin available.
|
||||
foreach ($get_plugins as $key => $value) {
|
||||
$slug = $this->extract_slug_from_info($key, $value);
|
||||
|
||||
// If the plugin name matches that of the specified name, it will gather details.
|
||||
// In case name check isn't enough, we'll use slug to verify if the plugin being queried is actually installed.
|
||||
//
|
||||
// Reason for name check failure:
|
||||
// Due to plugin name inconsistencies - where wordpress.org registered plugin name is different
|
||||
// from the actual plugin files's metadata (found inside the plugin's PHP file itself).
|
||||
if ((!empty($query['plugin']) && html_entity_decode($value['Name']) === html_entity_decode($query['plugin'])) || (!empty($query['slug']) && $slug === $query['slug'])) {
|
||||
$info['installed'] = true;
|
||||
$info['active'] = is_plugin_active($key);
|
||||
$info['plugin_path'] = $key;
|
||||
$info['data'] = $value;
|
||||
$info['name'] = $value['Name'];
|
||||
$info['slug'] = $slug;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the slug from the plugin data
|
||||
*
|
||||
* @param string $key They key of the current info
|
||||
* @param array $info Data pulled from the plugin file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function extract_slug_from_info($key, $info) {
|
||||
if (!is_array($info) || empty($info) || empty($key)) return '';
|
||||
|
||||
$temp = explode('/', $key);
|
||||
|
||||
// With WP standards textdomain must always be equal to the plugin's folder name
|
||||
// but for premium plugins this may not always be the case thus, we extract the folder
|
||||
// name from the key as the default slug.
|
||||
$slug = basename($temp[0], '.php');
|
||||
|
||||
if (!empty($info['TextDomain']) && 1 === count($temp)) {
|
||||
// For plugin without folder we compare the extracted slug with the 'TextDomain'
|
||||
// and if they're not equal then 'TextDomain' will assume as slug.
|
||||
if ($slug != $info['TextDomain']) $slug = $info['TextDomain'];
|
||||
}
|
||||
|
||||
// If in case the user kept the hello-dolly plugin then we'll make sure that it gets
|
||||
// the proper slug for it, otherwise, we'll end up with the wrong slug 'hello' instead of
|
||||
// 'hello-dolly'. Wrong slug will produce error in UpdraftCentral when running it against
|
||||
// wordpress.org for further information retrieval.
|
||||
$slug = ('Hello Dolly' === $info['Name']) ? 'hello-dolly' : $slug;
|
||||
|
||||
return $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all available plugins with additional attributes and settings needed by UpdraftCentral
|
||||
*
|
||||
* @param array $query Parameter array Any available parameters needed for this action
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function load_plugins($query) {
|
||||
|
||||
$permissions = array('install_plugins', 'activate_plugins');
|
||||
if (is_multisite() && !is_super_admin(get_current_user_id())) $permissions = array('activate_plugins');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, array(), $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$website = get_bloginfo('name');
|
||||
$results = array();
|
||||
|
||||
// Load the updates command class if not existed
|
||||
if (!class_exists('UpdraftCentral_Updates_Commands')) include_once('updates.php');
|
||||
$updates = new UpdraftCentral_Updates_Commands($this->rc);
|
||||
|
||||
// Get plugins for update
|
||||
$plugin_updates = $updates->get_item_updates('plugins');
|
||||
|
||||
// Get all plugins
|
||||
$plugins = get_plugins();
|
||||
|
||||
if (is_multisite() && !is_super_admin(get_current_user_id())) {
|
||||
|
||||
// If the "Plugins" menu is disabled for the subsites on a multisite
|
||||
// network then we return an empty "plugins" array.
|
||||
$menu_items = get_site_option('menu_items');
|
||||
if (empty($menu_items) || !isset($menu_items['plugins'])) {
|
||||
$plugins = array();
|
||||
} else {
|
||||
$show_network_active = apply_filters('show_network_active_plugins', current_user_can('manage_network_plugins'));
|
||||
|
||||
$filtered_plugins = array();
|
||||
foreach ($plugins as $file => $data) {
|
||||
if (is_network_only_plugin($file) && !is_plugin_active($file)) {
|
||||
if ($show_network_active) $filtered_plugins[$file] = $data;
|
||||
} elseif (is_plugin_active_for_network($file)) {
|
||||
if ($show_network_active) $filtered_plugins[$file] = $data;
|
||||
} else {
|
||||
$filtered_plugins[$file] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
$plugins = $filtered_plugins;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($plugins as $key => $value) {
|
||||
$slug = $this->extract_slug_from_info($key, $value);
|
||||
|
||||
$plugin = new stdClass();
|
||||
$plugin->name = $value['Name'];
|
||||
$plugin->description = $value['Description'];
|
||||
$plugin->slug = $slug;
|
||||
$plugin->version = $value['Version'];
|
||||
$plugin->author = $value['Author'];
|
||||
$plugin->status = is_plugin_active($key) ? 'active' : 'inactive';
|
||||
$plugin->website = $website;
|
||||
$plugin->multisite = is_multisite();
|
||||
$plugin->site_url = trailingslashit(get_bloginfo('url'));
|
||||
|
||||
if (!empty($plugin_updates[$key])) {
|
||||
$update_info = $plugin_updates[$key];
|
||||
|
||||
if (version_compare($update_info->Version, $update_info->update->new_version, '<')) {
|
||||
if (!empty($update_info->update->new_version)) $plugin->latest_version = $update_info->update->new_version;
|
||||
if (!empty($update_info->update->package)) $plugin->download_link = $update_info->update->package;
|
||||
if (!empty($update_info->update->sections)) $plugin->sections = $update_info->update->sections;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($plugin->short_description) && !empty($plugin->description)) {
|
||||
// Only pull the first sentence as short description, it should be enough rather than displaying
|
||||
// an empty description or a full blown one which the user can access anytime if they press on
|
||||
// the view details link in UpdraftCentral.
|
||||
$temp = explode('.', $plugin->description);
|
||||
$short_description = $temp[0];
|
||||
|
||||
// Adding the second sentence wouldn't hurt, in case the first sentence is too short.
|
||||
if (isset($temp[1])) $short_description .= '.'.$temp[1];
|
||||
|
||||
$plugin->short_description = $short_description.'.';
|
||||
}
|
||||
|
||||
$results[] = $plugin;
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'plugins' => $results,
|
||||
'is_super_admin' => is_super_admin(),
|
||||
);
|
||||
|
||||
$result = array_merge($result, $this->_get_backup_credentials_settings(WP_PLUGIN_DIR));
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the backup and security credentials settings for this website
|
||||
*
|
||||
* @param array $query Parameter array Any available parameters needed for this action
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function get_plugin_requirements() {
|
||||
return $this->_response($this->_get_backup_credentials_settings(WP_PLUGIN_DIR));
|
||||
}
|
||||
}
|
||||
2038
wp-content/plugins/updraftplus/central/modules/posts.php
Normal file
391
wp-content/plugins/updraftplus/central/modules/reporting.php
Normal file
@@ -0,0 +1,391 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* - A container for RPC commands (white label reporting UpdraftCentral commands). Commands map exactly onto method names (and hence this class should not implement anything else, beyond the constructor, and private methods)
|
||||
* - Return format is array('response' => (string - a code), 'data' => (mixed));
|
||||
*
|
||||
* RPC commands are not allowed to begin with an underscore. So, any private methods can be prefixed with an underscore.
|
||||
*/
|
||||
class UpdraftCentral_Reporting_Commands extends UpdraftCentral_Commands {
|
||||
|
||||
private $valid_statuses = array(
|
||||
'recurring',
|
||||
'manual',
|
||||
);
|
||||
|
||||
private $max_number_of_recipients = 100;
|
||||
|
||||
/**
|
||||
* Update existing reports.
|
||||
*
|
||||
* @param array $reports Reports data which will be saved.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function _update_existing_reports($reports) {
|
||||
update_option('updraftcentral_reporting_reports', $reports);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize email address - lowercase and sanitize.
|
||||
*
|
||||
* @param string $email Email address to normalize.
|
||||
*
|
||||
* @return string Normalized email.
|
||||
*/
|
||||
private function _normalize_email($email) {
|
||||
return strtolower(sanitize_email($email));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing reports.
|
||||
*
|
||||
* @return array List of all reports.
|
||||
*/
|
||||
private function _get_existing_reports() {
|
||||
return (array) get_option('updraftcentral_reporting_reports', array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update existing sent reports.
|
||||
*
|
||||
* @param array $sent_reports Sent reports data which will be saved.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function _update_existing_sent_reports($sent_reports) {
|
||||
update_option('updraftcentral_reporting_sent_reports', $sent_reports);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing sent reports.
|
||||
*
|
||||
* @return array List of all sent reports.
|
||||
*/
|
||||
private function _get_existing_sent_reports() {
|
||||
return (array) get_option('updraftcentral_reporting_sent_reports', array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the reports (scheduled or not scheduled).
|
||||
*
|
||||
* @param array $data Data array containing report IDs to fetch.
|
||||
*
|
||||
* @return array An array of reports.
|
||||
*/
|
||||
public function get_reports($data) {
|
||||
$all_reports = $this->_get_existing_reports();
|
||||
|
||||
$sent_reports = array_reverse($this->_get_existing_sent_reports());
|
||||
|
||||
foreach ($sent_reports as $key => $sent_report) {
|
||||
$sent_report['download_url'] = '';
|
||||
|
||||
if (!empty($sent_report['pdf_attachment_id'])) {
|
||||
$sent_report['download_url'] = wp_get_attachment_url(absint($sent_report['pdf_attachment_id']));
|
||||
}
|
||||
|
||||
$sent_reports[$key] = $sent_report;
|
||||
}
|
||||
|
||||
if (empty($data['report_ids']) || !is_array($data['report_ids'])) {
|
||||
return $this->_response(array(
|
||||
'reports' => $all_reports,
|
||||
'sent_reports' => $sent_reports,
|
||||
));
|
||||
}
|
||||
|
||||
$report_ids = array();
|
||||
|
||||
foreach ($data['report_ids'] as $report_id) {
|
||||
$report_ids[] = sanitize_key(strval($report_id));
|
||||
}
|
||||
|
||||
return $this->_response((array(
|
||||
'reports' => array_intersect_key($all_reports, array_flip($report_ids)),
|
||||
'sent_reports' => $sent_reports,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a report.
|
||||
*
|
||||
* @param array $data Data array containing report ID to delete.
|
||||
*
|
||||
* @return array An array containing the result of the current process
|
||||
*/
|
||||
public function delete_report($data) {
|
||||
// Permission check.
|
||||
if (!current_user_can('manage_options')) {
|
||||
$result = array("error" => true, "message" => "not_allowed");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Return early if valid data structure is not present.
|
||||
if (!is_array($data)) {
|
||||
$result = array("error" => true, "message" => "invalid_data");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
$report_id = empty($data['id']) ? '' : sanitize_key(strval($data['id']));
|
||||
|
||||
// Return early if ID not supplied.
|
||||
if (empty($report_id)) {
|
||||
$result = array('error' => true, 'message' => 'missing_id');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Get the reports from options table.
|
||||
$reports = $this->_get_existing_reports();
|
||||
|
||||
// Check if the ID to delete is present.
|
||||
if (!isset($reports[$report_id])) {
|
||||
$result = array('error' => true, 'message' => 'invalid_id');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
unset($reports[$report_id]);
|
||||
|
||||
$this->_update_existing_reports($reports);
|
||||
|
||||
$result = array("error" => false, "message" => "reports_updated");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new report.
|
||||
*
|
||||
* @param array $data Report information to add
|
||||
*
|
||||
* @return array An array containing the result of the current process
|
||||
*/
|
||||
public function add_report($data) {
|
||||
// Permission check.
|
||||
if (!current_user_can('manage_options')) {
|
||||
$result = array("error" => true, "message" => "not_allowed");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Return early if valid report structure is not present.
|
||||
if (!is_array($data)) {
|
||||
$result = array("error" => true, "message" => "invalid_report_data");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
$report_name = isset($data['name']) ? sanitize_text_field(strval($data['name'])) : '';
|
||||
|
||||
if ('' === $report_name) {
|
||||
$result = array("error" => true, "message" => "empty_name");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
$report_status = empty($data['status']) ? '' : sanitize_text_field(strval($data['status']));
|
||||
|
||||
if (!in_array($report_status, $this->valid_statuses)) {
|
||||
$result = array("error" => true, "message" => "status_invalid");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
$template_id = empty($data['template_id']) ? '' : sanitize_key($data['template_id']);
|
||||
|
||||
if (empty($template_id)) {
|
||||
$result = array("error" => true, "message" => "template_id_invalid");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
$recipients = (isset($data['recipients']) && is_array($data['recipients'])) ? $data['recipients'] : array();
|
||||
|
||||
if (count($recipients) > $this->max_number_of_recipients) {
|
||||
$result = array("error" => true, "message" => "max_number_of_recipients_exceeded");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
$recipients = array_unique(array_map(array($this, '_normalize_email'), $recipients));
|
||||
|
||||
// Sanity check for invalid email.
|
||||
foreach ($recipients as $email) {
|
||||
if (!is_email($email)) {
|
||||
$result = array(
|
||||
"error" => true,
|
||||
"message" => "recipients_invalid",
|
||||
"values" => array(
|
||||
'invalid_email' => $email,
|
||||
)
|
||||
);
|
||||
return $this->_response($result);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($recipients)) {
|
||||
$result = array("error" => true, "message" => "no_recipients_provided");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Get existing reports from options table.
|
||||
$existing_reports = $this->_get_existing_reports();
|
||||
|
||||
// Report.
|
||||
$report = array();
|
||||
|
||||
$report_id = empty($data['id']) ? '' : sanitize_key(strval($data['id']));
|
||||
|
||||
if (empty($report_id)) {
|
||||
// First report timestamp and formatted date.
|
||||
$next_report_timestamp = strtotime("+1 month", time());
|
||||
$next_report_formatted_date = date("j M, g:i a", $next_report_timestamp);
|
||||
|
||||
$report_id = UpdraftPlus_Manipulation_Functions::generate_random_string(10);
|
||||
|
||||
$report_id_generation_loops = 0;
|
||||
|
||||
// Sanity check to check if the report ID exists.
|
||||
while (isset($existing_reports[$report_id])) {
|
||||
$report_id = UpdraftPlus_Manipulation_Functions::generate_random_string(10);
|
||||
++$report_id_generation_loops;
|
||||
|
||||
// If we somehow exceed the max generation loops then return error - which will not happen in almost any case.
|
||||
if ($report_id_generation_loops > 10) {
|
||||
$result = array("error" => true, "message" => "report_id_generation_failed");
|
||||
return $this->_response($result);
|
||||
}
|
||||
}
|
||||
|
||||
$report = array(
|
||||
'id' => $report_id,
|
||||
'name' => $report_name,
|
||||
'status' => $report_status,
|
||||
'template_id' => $template_id,
|
||||
'recipients' => $recipients,
|
||||
'last_report_timestamp' => 0,
|
||||
'last_report_formatted_date' => __('N/A', 'updraftplus'),
|
||||
'next_report_timestamp' => $next_report_timestamp,
|
||||
'next_report_formatted_date' => $next_report_formatted_date,
|
||||
);
|
||||
} elseif (!empty($existing_reports[$report_id])) {
|
||||
$report = $existing_reports[$report_id];
|
||||
|
||||
$report['name'] = $report_name;
|
||||
$report['status'] = $report_status;
|
||||
$report['template_id'] = $template_id;
|
||||
$report['recipients'] = $recipients;
|
||||
} else {
|
||||
$result = array("error" => true, "message" => "report_does_not_exist");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Add the new report.
|
||||
$existing_reports[$report_id] = $report;
|
||||
|
||||
// Update the reports.
|
||||
$this->_update_existing_reports($existing_reports);
|
||||
|
||||
$result = array(
|
||||
"error" => false,
|
||||
"message" => "reports_updated",
|
||||
"values" => array(
|
||||
'report' => $report,
|
||||
)
|
||||
);
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a sent report.
|
||||
*
|
||||
* @param array $data Report information to add
|
||||
*
|
||||
* @return array An array containing the result of the current process
|
||||
*/
|
||||
public function add_sent_reports($data) {
|
||||
// Permission check.
|
||||
if (!current_user_can('manage_options')) {
|
||||
$result = array("error" => true, "message" => "not_allowed");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Return early if valid report structure is not present.
|
||||
if (!is_array($data) || empty($data['sent_reports_data'])) {
|
||||
$result = array("error" => true, "message" => "invalid_sent_report_data");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
$reports = $this->_get_existing_reports();
|
||||
$sent_reports = $this->_get_existing_sent_reports();
|
||||
|
||||
$return_data = array();
|
||||
|
||||
// Loop through all the sent reports.
|
||||
foreach ($data['sent_reports_data'] as $report_data) {
|
||||
$report_id = sanitize_key(strval($report_data['report_id']));
|
||||
|
||||
// Skip if no report of this ID exists.
|
||||
if (empty($reports[$report_id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$report_sent_at_timestamp = time();
|
||||
$report_sent_at_formatted_date = date("j M, g:i a", $report_sent_at_timestamp);
|
||||
|
||||
// Change the last report time of the report.
|
||||
$reports[$report_id]['last_report_timestamp'] = $report_sent_at_timestamp;
|
||||
$reports[$report_id]['last_report_formatted_date'] = $report_sent_at_formatted_date;
|
||||
|
||||
// Save the PDF as attachment.
|
||||
$pdf_attachment_id = 0;
|
||||
$uploads_dir = wp_upload_dir();
|
||||
$custom_upload_directory = trailingslashit($uploads_dir['basedir']) . 'updraftcentral-white-label-reporting-pdfs/';
|
||||
$custom_upload_url = trailingslashit($uploads_dir['baseurl']) . 'updraftcentral-white-label-reporting-pdfs/';
|
||||
$filename = sanitize_text_field($reports[$report_id]['name']) . '-' . $report_sent_at_timestamp . '-' . UpdraftPlus_Manipulation_Functions::generate_random_string(5) . '.pdf';
|
||||
$full_path = $custom_upload_directory . basename($filename);
|
||||
|
||||
// Sanity check to test directory exists.
|
||||
wp_mkdir_p(dirname($full_path));
|
||||
|
||||
file_put_contents($full_path, base64_decode($report_data['pdf_content']));
|
||||
|
||||
$wp_filetype = wp_check_filetype($filename, null);
|
||||
$attachment = array(
|
||||
'guid' => $custom_upload_url . basename($filename),
|
||||
'post_mime_type' => $wp_filetype['type'],
|
||||
'post_title' => sanitize_file_name(pathinfo($filename, PATHINFO_FILENAME)),
|
||||
'post_content' => '',
|
||||
'post_status' => 'inherit',
|
||||
);
|
||||
|
||||
$pdf_attachment_id = wp_insert_attachment($attachment, $full_path);
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/image.php');
|
||||
|
||||
$attach_data = wp_generate_attachment_metadata($pdf_attachment_id, $full_path);
|
||||
wp_update_attachment_metadata($pdf_attachment_id, $attach_data);
|
||||
|
||||
$new_sent_report = array(
|
||||
'report' => $reports[$report_id]['name'],
|
||||
'sent' => (bool) $report_data['sent'],
|
||||
'template_id_used' => sanitize_key($report_data['template_id']),
|
||||
'template_name_used' => sanitize_text_field($report_data['template_name']),
|
||||
'services' => array_map('sanitize_text_field', $report_data['services']),
|
||||
'sent_at' => $report_sent_at_formatted_date,
|
||||
'number_of_recipients' => absint($report_data['number_of_recipients']),
|
||||
'pdf_attachment_id' => $pdf_attachment_id,
|
||||
);
|
||||
|
||||
$sent_reports[] = $new_sent_report;
|
||||
$return_data[] = $new_sent_report;
|
||||
}
|
||||
|
||||
$this->_update_existing_reports($reports);
|
||||
$this->_update_existing_sent_reports($sent_reports);
|
||||
|
||||
$result = array(
|
||||
"error" => false,
|
||||
"message" => "sent_reports_updated",
|
||||
"data" => $return_data,
|
||||
);
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
}
|
||||
92
wp-content/plugins/updraftplus/central/modules/rest.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* Handles commands to access the REST API.
|
||||
*
|
||||
* This action is used to relay any REST API request through UDC command.
|
||||
* From UDC we don't have direct authentication to child site's REST APIs, therefore this middleware.
|
||||
*
|
||||
* UDC authentication logic automatically authenticates the user with which you have added the site to UDC dashboard.
|
||||
* And then the request is just relayed to the WordPress rest api handler, which takes care of permission callback and everything as usual.
|
||||
*/
|
||||
class UpdraftCentral_REST_API_Access_Commands extends UpdraftCentral_Commands {
|
||||
|
||||
protected $switched = false;
|
||||
|
||||
/**
|
||||
* Function that gets called before every action
|
||||
*
|
||||
* Link to udrpc_action main function in class UpdraftCentral_Listener
|
||||
*
|
||||
* @param string $command a string that corresponds to UDC command to call a certain method for this class.
|
||||
* @param array $data an array of data post or get fields
|
||||
*/
|
||||
public function _pre_action($command, $data) {
|
||||
$blog_id = get_current_blog_id();
|
||||
if (!empty($data['site_id'])) $blog_id = $data['site_id'];
|
||||
|
||||
if (function_exists('switch_to_blog') && is_multisite() && $blog_id) {
|
||||
$this->switched = switch_to_blog($blog_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that gets called after every action
|
||||
*
|
||||
* Link to udrpc_action main function in class UpdraftCentral_Listener
|
||||
*/
|
||||
public function _post_action() {
|
||||
// Here, we're restoring to the current (default) blog before we switched
|
||||
if ($this->switched) restore_current_blog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Relays the REST API request.
|
||||
*
|
||||
* @param array $params The parameters for the request.
|
||||
*
|
||||
* @return array The response from the REST API, wrapped in udrpc response structure.
|
||||
*/
|
||||
public function handle_request($params) {
|
||||
$route = untrailingslashit(!empty($params['route']) ? $params['route'] : '');
|
||||
$method = !empty($params['method']) ? $params['method'] : 'GET';
|
||||
$body = !empty($params['body']) ? $params['body'] : null;
|
||||
|
||||
// Return early if the route is empty.
|
||||
if (empty($route)) {
|
||||
return $this->_generic_error_response('route_empty', array(
|
||||
'prefix' => 'updraftcentral',
|
||||
'command' => 'handle_request',
|
||||
'class' => 'UpdraftCentral_REST_API_Access_Commands'
|
||||
));
|
||||
}
|
||||
|
||||
if (!class_exists('WP_REST_Request')) {
|
||||
return $this->_generic_error_response('rest_api_not_available_on_this_wordpress_version', array(
|
||||
'prefix' => 'updraftcentral',
|
||||
'command' => 'handle_request',
|
||||
'class' => 'UpdraftCentral_REST_API_Access_Commands'
|
||||
));
|
||||
}
|
||||
|
||||
$request = new WP_REST_Request($method, '/' . $route);
|
||||
|
||||
if (!empty($body)) {
|
||||
$request->set_body(json_encode($body));
|
||||
$request->set_header('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
// Do the request.
|
||||
$response = rest_do_request($request);
|
||||
|
||||
// Return if error.
|
||||
if (true === $response->is_error()) {
|
||||
return $this->_generic_error_response('rest_request_failed', $response->as_error());
|
||||
}
|
||||
|
||||
// `get_data` should always return JSON-serializable data.
|
||||
return $this->_response(array('rest_data' => $response->get_data(), 'headers' => $response->headers));
|
||||
}
|
||||
}
|
||||
964
wp-content/plugins/updraftplus/central/modules/theme.php
Normal file
@@ -0,0 +1,964 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* Handles UpdraftCentral Theme Commands which basically handles
|
||||
* the installation and activation of a theme
|
||||
*/
|
||||
class UpdraftCentral_Theme_Commands extends UpdraftCentral_Commands {
|
||||
|
||||
private $switched = false;
|
||||
|
||||
/**
|
||||
* Function that gets called before every action
|
||||
*
|
||||
* @param string $command a string that corresponds to UDC command to call a certain method for this class.
|
||||
* @param array $data an array of data post or get fields
|
||||
* @param array $extra_info extrainfo use in the udrpc_action, e.g. user_id
|
||||
*
|
||||
* link to udrpc_action main function in class UpdraftCentral_Listener
|
||||
*/
|
||||
public function _pre_action($command, $data, $extra_info) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- This function is called from listener.php and $extra_info is being sent.
|
||||
// Here we assign the current blog_id to a variable $blog_id
|
||||
$blog_id = get_current_blog_id();
|
||||
if (!empty($data['site_id'])) $blog_id = $data['site_id'];
|
||||
|
||||
if (function_exists('switch_to_blog') && is_multisite() && $blog_id) {
|
||||
$this->switched = switch_to_blog($blog_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that gets called after every action
|
||||
*
|
||||
* @param string $command a string that corresponds to UDC command to call a certain method for this class.
|
||||
* @param array $data an array of data post or get fields
|
||||
* @param array $extra_info extrainfo use in the udrpc_action, e.g. user_id
|
||||
*
|
||||
* link to udrpc_action main function in class UpdraftCentral_Listener
|
||||
*/
|
||||
public function _post_action($command, $data, $extra_info) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Unused parameter is present because the caller from UpdraftCentral_Listener class uses 3 arguments.
|
||||
// Here, we're restoring to the current (default) blog before we switched
|
||||
if ($this->switched) restore_current_blog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->_admin_include('theme.php', 'file.php', 'template.php', 'class-wp-upgrader.php', 'theme-install.php', 'update.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs and activates a theme through upload
|
||||
*
|
||||
* @param array $params Parameter array containing information pertaining the currently uploaded theme
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function upload_theme($params) {
|
||||
return $this->process_chunk_upload($params, 'theme');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the theme is currently installed and activated.
|
||||
*
|
||||
* @param array $query Parameter array containing the name of the theme to check
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function is_theme_installed($query) {
|
||||
|
||||
if (!isset($query['theme']))
|
||||
return $this->_generic_error_response('theme_name_required');
|
||||
|
||||
|
||||
$result = $this->_get_theme_info($query['theme']);
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies currently requested action for theme processing
|
||||
*
|
||||
* @param string $action The action to apply (e.g. activate or install)
|
||||
* @param array $query Parameter array containing information for the currently requested action
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function _apply_theme_action($action, $query) {
|
||||
|
||||
$result = array();
|
||||
switch ($action) {
|
||||
case 'activate':
|
||||
$info = $this->_get_theme_info($query['theme']);
|
||||
if ($info['installed']) {
|
||||
switch_theme($info['slug']);
|
||||
if (wp_get_theme()->get_stylesheet() === $info['slug']) {
|
||||
$result = array('activated' => true, 'info' => $this->_get_theme_info($query['theme']), 'last_state' => $info);
|
||||
} else {
|
||||
$result = $this->_generic_error_response('theme_not_activated', array(
|
||||
'theme' => $query['theme'],
|
||||
'error_code' => 'theme_not_activated',
|
||||
'error_message' => __('There appears to be a problem activating or switching to the intended theme.', 'updraftplus').' '.__('Please check your permissions and try again.', 'updraftplus'),
|
||||
'info' => $this->_get_theme_info($query['theme'])
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$result = $this->_generic_error_response('theme_not_installed', array(
|
||||
'theme' => $query['theme'],
|
||||
'error_code' => 'theme_not_installed',
|
||||
'error_message' => __('The theme you wish to activate is either not installed or has been removed recently.', 'updraftplus'),
|
||||
'info' => $info
|
||||
));
|
||||
}
|
||||
break;
|
||||
case 'network_enable':
|
||||
$info = $this->_get_theme_info($query['theme']);
|
||||
if ($info['installed']) {
|
||||
if (current_user_can('manage_network_themes')) {
|
||||
// Make sure that network_enable_theme is present and callable since
|
||||
// it is only available at 4.6. If not, we'll do things the old fashion way
|
||||
if (is_callable(array('WP_Theme', 'network_enable_theme'))) {
|
||||
WP_Theme::network_enable_theme($info['slug']);
|
||||
} else {
|
||||
$allowed_themes = get_site_option('allowedthemes');
|
||||
$allowed_themes[$info['slug']] = true;
|
||||
|
||||
update_site_option('allowedthemes', $allowed_themes);
|
||||
}
|
||||
}
|
||||
|
||||
$allowed = WP_Theme::get_allowed_on_network();
|
||||
if (is_array($allowed) && !empty($allowed[$info['slug']])) {
|
||||
$result = array('enabled' => true, 'info' => $this->_get_theme_info($query['theme']), 'last_state' => $info);
|
||||
} else {
|
||||
$result = $this->_generic_error_response('theme_not_enabled', array(
|
||||
'theme' => $query['theme'],
|
||||
'error_code' => 'theme_not_enabled',
|
||||
'error_message' => __('There appears to be a problem enabling the intended theme on your network.', 'updraftplus').' '.__('Please kindly check your permission and try again.', 'updraftplus'),
|
||||
'info' => $this->_get_theme_info($query['theme'])
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$result = $this->_generic_error_response('theme_not_installed', array(
|
||||
'theme' => $query['theme'],
|
||||
'error_code' => 'theme_not_installed',
|
||||
'error_message' => __('The theme you wish to enable on your network is either not installed or has been removed recently.', 'updraftplus'),
|
||||
'info' => $info
|
||||
));
|
||||
}
|
||||
break;
|
||||
case 'network_disable':
|
||||
$info = $this->_get_theme_info($query['theme']);
|
||||
if ($info['installed']) {
|
||||
if (current_user_can('manage_network_themes')) {
|
||||
// Make sure that network_disable_theme is present and callable since
|
||||
// it is only available at 4.6. If not, we'll do things the old fashion way
|
||||
if (is_callable(array('WP_Theme', 'network_disable_theme'))) {
|
||||
WP_Theme::network_disable_theme($info['slug']);
|
||||
} else {
|
||||
$allowed_themes = get_site_option('allowedthemes');
|
||||
if (isset($allowed_themes[$info['slug']])) {
|
||||
unset($allowed_themes[$info['slug']]);
|
||||
}
|
||||
|
||||
update_site_option('allowedthemes', $allowed_themes);
|
||||
}
|
||||
}
|
||||
|
||||
$allowed = WP_Theme::get_allowed_on_network();
|
||||
if (is_array($allowed) && empty($allowed[$info['slug']])) {
|
||||
$result = array('disabled' => true, 'info' => $this->_get_theme_info($query['theme']), 'last_state' => $info);
|
||||
} else {
|
||||
$result = $this->_generic_error_response('theme_not_disabled', array(
|
||||
'theme' => $query['theme'],
|
||||
'error_code' => 'theme_not_disabled',
|
||||
'error_message' => __('There appears to be a problem disabling the intended theme from your network.', 'updraftplus').' '.__('Please kindly check your permission and try again.', 'updraftplus'),
|
||||
'info' => $this->_get_theme_info($query['theme'])
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$result = $this->_generic_error_response('theme_not_installed', array(
|
||||
'theme' => $query['theme'],
|
||||
'error_code' => 'theme_not_installed',
|
||||
'error_message' => __('The theme you wish to disable from your network is either not installed or has been removed recently.', 'updraftplus'),
|
||||
'info' => $info
|
||||
));
|
||||
}
|
||||
break;
|
||||
case 'install':
|
||||
$api = themes_api('theme_information', array(
|
||||
'slug' => $query['slug'],
|
||||
'fields' => array(
|
||||
'description' => true,
|
||||
'sections' => false,
|
||||
'rating' => true,
|
||||
'ratings' => true,
|
||||
'downloaded' => true,
|
||||
'downloadlink' => true,
|
||||
'last_updated' => true,
|
||||
'screenshot_url' => true,
|
||||
'parent' => true,
|
||||
)
|
||||
));
|
||||
|
||||
$info = $this->_get_theme_info($query['theme']);
|
||||
if (is_wp_error($api)) {
|
||||
$result = $this->_generic_error_response('generic_response_error', array(
|
||||
'theme' => $query['theme'],
|
||||
'error_code' => 'theme_not_installed',
|
||||
'error_message' => $api->get_error_message(),
|
||||
'info' => $info
|
||||
));
|
||||
} else {
|
||||
$installed = $info['installed'];
|
||||
|
||||
$error_code = $error_message = '';
|
||||
if (!$installed) {
|
||||
// WP < 3.7
|
||||
if (!class_exists('Automatic_Upgrader_Skin')) include_once(dirname(dirname(__FILE__)).'/classes/class-automatic-upgrader-skin.php');
|
||||
|
||||
$skin = new Automatic_Upgrader_Skin();
|
||||
$upgrader = new Theme_Upgrader($skin);
|
||||
|
||||
$download_link = $api->download_link;
|
||||
$installed = $upgrader->install($download_link);
|
||||
|
||||
if (is_wp_error($installed)) {
|
||||
$error_code = $installed->get_error_code();
|
||||
$error_message = $installed->get_error_message();
|
||||
} elseif (is_wp_error($skin->result)) {
|
||||
$error_code = $skin->result->get_error_code();
|
||||
$error_message = $skin->result->get_error_message();
|
||||
|
||||
$error_data = $skin->result->get_error_data($error_code);
|
||||
if (!empty($error_data)) {
|
||||
if (is_array($error_data)) $error_data = json_encode($error_data);
|
||||
$error_message .= ' '.$error_data;
|
||||
}
|
||||
} elseif (is_null($installed) || !$installed) {
|
||||
global $wp_filesystem;
|
||||
$upgrade_messages = $skin->get_upgrade_messages();
|
||||
|
||||
if (!class_exists('WP_Filesystem_Base')) include_once(ABSPATH.'/wp-admin/includes/class-wp-filesystem-base.php');
|
||||
|
||||
// Pass through the error from WP_Filesystem if one was raised.
|
||||
if ($wp_filesystem instanceof WP_Filesystem_Base && is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
||||
$error_code = $wp_filesystem->errors->get_error_code();
|
||||
$error_message = $wp_filesystem->errors->get_error_message();
|
||||
} elseif (!empty($upgrade_messages)) {
|
||||
// We're only after for the last feedback that we received from the install process. Mostly,
|
||||
// that is where the last error has been inserted.
|
||||
$messages = $skin->get_upgrade_messages();
|
||||
$error_code = 'install_failed';
|
||||
$error_message = end($messages);
|
||||
} else {
|
||||
$error_code = 'unable_to_connect_to_filesystem';
|
||||
$error_message = __('Unable to connect to the filesystem.', 'updraftplus').' '.__('Please confirm your credentials.', 'updraftplus');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$installed || is_wp_error($installed)) {
|
||||
$result = $this->_generic_error_response('theme_install_failed', array(
|
||||
'theme' => $query['theme'],
|
||||
'error_code' => $error_code,
|
||||
'error_message' => $error_message,
|
||||
'info' => $this->_get_theme_info($query['theme'])
|
||||
));
|
||||
} else {
|
||||
$result = array('installed' => true, 'info' => $this->_get_theme_info($query['theme']), 'last_state' => $info);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preloads the submitted credentials to the global $_POST variable
|
||||
*
|
||||
* @param array $query Parameter array containing information for the currently requested action
|
||||
*/
|
||||
private function _preload_credentials($query) {
|
||||
if (!empty($query) && isset($query['filesystem_credentials'])) {
|
||||
parse_str($query['filesystem_credentials'], $filesystem_credentials);
|
||||
if (is_array($filesystem_credentials)) {
|
||||
foreach ($filesystem_credentials as $key => $value) {
|
||||
// Put them into $_POST, which is where request_filesystem_credentials() checks for them.
|
||||
$_POST[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether we have the required fields submitted and the user has
|
||||
* the capabilities to execute the requested action
|
||||
*
|
||||
* @param array $query The submitted information
|
||||
* @param array $fields The required fields to check
|
||||
* @param array $capabilities The capabilities to check and validate
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
private function _validate_fields_and_capabilities($query, $fields, $capabilities) {
|
||||
|
||||
$error = '';
|
||||
if (!empty($fields)) {
|
||||
for ($i=0; $i<count($fields); $i++) {
|
||||
$field = $fields[$i];
|
||||
|
||||
if (!isset($query[$field])) {
|
||||
if ('keyword' === $field) {
|
||||
$error = $this->_generic_error_response('keyword_required');
|
||||
} else {
|
||||
$error = $this->_generic_error_response('theme_'.$query[$field].'_required');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($error) && !empty($capabilities)) {
|
||||
for ($i=0; $i<count($capabilities); $i++) {
|
||||
if (!current_user_can($capabilities[$i])) {
|
||||
$error = $this->_generic_error_response('theme_insufficient_permission');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processing an action for multiple items
|
||||
*
|
||||
* @param array $query Parameter array containing a list of themes to process
|
||||
* @return array Contains the results of the bulk process
|
||||
*/
|
||||
public function process_action_in_bulk($query) {
|
||||
$action = isset($query['action']) ? $query['action'] : '';
|
||||
$items = isset($query['args']) ? $query['args']['items'] : array();
|
||||
|
||||
$results = array();
|
||||
if (!empty($action) && !empty($items) && is_array($items)) {
|
||||
foreach ($items as $value) {
|
||||
if (method_exists($this, $action)) {
|
||||
$results[] = $this->$action($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_response($results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the theme
|
||||
*
|
||||
* @param array $query Parameter array containing the name of the theme to activate
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function activate_theme($query) {
|
||||
|
||||
$fields = array('theme');
|
||||
$permissions = array('switch_themes');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
$result = $this->_apply_theme_action('activate', $query);
|
||||
if (empty($result['activated'])) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables theme for network
|
||||
*
|
||||
* @param array $query Parameter array containing the name of the theme to activate
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function network_enable_theme($query) {
|
||||
|
||||
$fields = array('theme');
|
||||
$permissions = array('switch_themes');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
$result = $this->_apply_theme_action('network_enable', $query);
|
||||
if (empty($result['enabled'])) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables theme from network
|
||||
*
|
||||
* @param array $query Parameter array containing the name of the theme to activate
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function network_disable_theme($query) {
|
||||
|
||||
$fields = array('theme');
|
||||
$permissions = array('switch_themes');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
$result = $this->_apply_theme_action('network_disable', $query);
|
||||
if (empty($result['disabled'])) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download, install and activates the theme
|
||||
*
|
||||
* @param array $query Parameter array containing the filesystem credentials entered by the user along with the theme name and slug
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function install_activate_theme($query) {
|
||||
|
||||
$fields = array('theme', 'slug');
|
||||
$permissions = array('install_themes', 'switch_themes');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
$result = $this->_apply_theme_action('install', $query);
|
||||
if (!empty($result['installed']) && $result['installed']) {
|
||||
$result = $this->_apply_theme_action('activate', $query);
|
||||
if (empty($result['activated'])) {
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download, install the theme
|
||||
*
|
||||
* @param array $query Parameter array containing the filesystem credentials entered by the user along with the theme name and slug
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function install_theme($query) {
|
||||
|
||||
$fields = array('theme', 'slug');
|
||||
$permissions = array('install_themes');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
$result = $this->_apply_theme_action('install', $query);
|
||||
if (empty($result['installed'])) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall/delete the theme
|
||||
*
|
||||
* @param array $query Parameter array containing the filesystem credentials entered by the user along with the theme name and slug
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function delete_theme($query) {
|
||||
|
||||
$fields = array('theme');
|
||||
$permissions = array('delete_themes');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
$info = $this->_get_theme_info($query['theme']);
|
||||
if ($info['installed']) {
|
||||
$deleted = delete_theme($info['slug']);
|
||||
|
||||
if ($deleted) {
|
||||
$result = array('deleted' => true, 'info' => $this->_get_theme_info($query['theme']), 'last_state' => $info);
|
||||
} else {
|
||||
return $this->_generic_error_response('delete_theme_failed', array(
|
||||
'theme' => $query['theme'],
|
||||
'error_code' => 'delete_theme_failed',
|
||||
'info' => $info
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return $this->_generic_error_response('theme_not_installed', array(
|
||||
'theme' => $query['theme'],
|
||||
'error_code' => 'theme_not_installed',
|
||||
'info' => $info
|
||||
));
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates/upgrade the theme
|
||||
*
|
||||
* @param array $query Parameter array containing the filesystem credentials entered by the user along with the theme name and slug
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function update_theme($query) {
|
||||
|
||||
$fields = array('theme');
|
||||
$permissions = array('update_themes');
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, $fields, $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$this->_preload_credentials($query);
|
||||
|
||||
// Make sure that we still have the theme installed before running
|
||||
// the update process
|
||||
$info = $this->_get_theme_info($query['theme']);
|
||||
if ($info['installed']) {
|
||||
// Load the updates command class if not existed
|
||||
if (!class_exists('UpdraftCentral_Updates_Commands')) include_once('updates.php');
|
||||
$update_command = new UpdraftCentral_Updates_Commands($this->rc);
|
||||
|
||||
$result = $update_command->update_theme($info['slug']);
|
||||
if (!empty($result['error'])) {
|
||||
$result['values'] = array('theme' => $query['theme'], 'info' => $info);
|
||||
}
|
||||
} else {
|
||||
return $this->_generic_error_response('theme_not_installed', array(
|
||||
'theme' => $query['theme'],
|
||||
'error_code' => 'theme_not_installed',
|
||||
'info' => $info
|
||||
));
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the theme information along with its active and install status
|
||||
*
|
||||
* @internal
|
||||
* @param array $theme The name of the theme to pull the information from
|
||||
* @return array Contains the theme information
|
||||
*/
|
||||
private function _get_theme_info($theme) {
|
||||
|
||||
$info = array(
|
||||
'active' => false,
|
||||
'installed' => false
|
||||
);
|
||||
|
||||
// Clear theme cache so that newly installed/downloaded themes
|
||||
// gets reflected when calling "get_themes"
|
||||
if (function_exists('wp_clean_themes_cache')) {
|
||||
wp_clean_themes_cache();
|
||||
}
|
||||
|
||||
// Gets all themes available.
|
||||
$themes = wp_get_themes();
|
||||
$current_theme_slug = basename(get_stylesheet_directory());
|
||||
|
||||
// Loops around each theme available.
|
||||
foreach ($themes as $slug => $value) {
|
||||
$name = $value->get('Name');
|
||||
$theme_name = !empty($name) ? $name : $slug;
|
||||
|
||||
// If the theme name matches that of the specified name, it will gather details.
|
||||
if ($theme_name === $theme) {
|
||||
$info['installed'] = true;
|
||||
$info['active'] = ($slug === $current_theme_slug) ? true : false;
|
||||
$info['slug'] = $slug;
|
||||
$info['data'] = $value;
|
||||
$info['name'] = $theme_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all available themes with additional attributes and settings needed by UpdraftCentral
|
||||
*
|
||||
* @param array $query Parameter array Any available parameters needed for this action
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function load_themes($query) {
|
||||
$permissions = array('install_themes', 'switch_themes');
|
||||
$args = array();
|
||||
if (is_multisite() && !is_super_admin(get_current_user_id())) {
|
||||
$permissions = array('switch_themes');
|
||||
$args = array('allowed' => true, 'blog_id' => get_current_blog_id());
|
||||
}
|
||||
|
||||
$error = $this->_validate_fields_and_capabilities($query, array(), $permissions);
|
||||
if (!empty($error)) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
// Get pagination parameters early
|
||||
$updates_only = isset($query['updates_only']) ? filter_var($query['updates_only'], FILTER_VALIDATE_BOOLEAN) : false;
|
||||
$include_updates_pagination = isset($query['include_updates_pagination']) && $query['include_updates_pagination'];
|
||||
$per_page = isset($query['per_page']) ? (int) $query['per_page'] : 10;
|
||||
$page = isset($query['page']) ? (int) $query['page'] : 1;
|
||||
$offset = ($page - 1) * $per_page;
|
||||
|
||||
// A call is considered legacy if neither updates_only nor include_updates_pagination flags are set
|
||||
$is_legacy_call = !isset($query['updates_only']) && !isset($query['include_updates_pagination']) && !isset($query['per_page']);
|
||||
$sort_direction = isset($query['sort_direction']) ? strtolower($query['sort_direction']) : 'asc';
|
||||
|
||||
// Initialize arrays
|
||||
$themes_with_updates = array();
|
||||
$themes_without_updates = array();
|
||||
$installed_slugs = array();
|
||||
$snoozed_themes_info = array();
|
||||
|
||||
// Get current theme info once
|
||||
$current_theme_slug = basename(get_stylesheet_directory());
|
||||
$website = get_bloginfo('name');
|
||||
|
||||
// Load updates
|
||||
if (!class_exists('UpdraftCentral_Updates_Commands')) include_once('updates.php');
|
||||
$updates = new UpdraftCentral_Updates_Commands($this->rc);
|
||||
$theme_updates = (array) $updates->get_item_updates('themes');
|
||||
|
||||
// Get all themes
|
||||
$themes = wp_get_themes($args);
|
||||
|
||||
// Sort themes by name
|
||||
if (!empty($themes)) {
|
||||
uasort($themes, array($this, '_sort_themes_by_name'));
|
||||
}
|
||||
|
||||
// Get list of snoozed theme slugs if provided
|
||||
$snoozed_slugs = isset($query['snoozed_themes']) ? $query['snoozed_themes'] : array();
|
||||
|
||||
// Prepare config object for theme data
|
||||
$config = array(
|
||||
'website' => $website,
|
||||
'current_theme_slug' => $current_theme_slug,
|
||||
'is_legacy_call' => $is_legacy_call,
|
||||
'snoozed_slugs' => $snoozed_slugs
|
||||
);
|
||||
|
||||
// Process themes efficiently
|
||||
foreach ($themes as $slug => $value) {
|
||||
// Skip processing if we're only looking for updates and this theme has none
|
||||
if ($updates_only && empty($theme_updates[$slug])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$config['slug'] = $slug;
|
||||
$theme = $this->_prepare_theme_data($value, $config);
|
||||
|
||||
// Track installed slugs if needed
|
||||
if (!empty($query['get_installed_slugs'])) {
|
||||
$installed_slugs[] = $slug;
|
||||
}
|
||||
|
||||
// Check for updates and categorize
|
||||
if (!empty($theme_updates[$slug]) && version_compare($theme->version, $theme_updates[$slug]->update['new_version'], '<')) {
|
||||
$theme = $this->_add_update_info($theme, $theme_updates[$slug], $is_legacy_call);
|
||||
|
||||
// Add to themes_with_updates regardless of snooze status
|
||||
if ($slug === $current_theme_slug) {
|
||||
$themes_with_updates = array($theme->slug => $theme) + $themes_with_updates;
|
||||
} else {
|
||||
$themes_with_updates[$theme->slug] = $theme;
|
||||
}
|
||||
|
||||
// Track snoozed status separately
|
||||
if (in_array($slug, $snoozed_slugs)) {
|
||||
$snoozed_themes_info[$slug] = $theme;
|
||||
}
|
||||
} else {
|
||||
if ($slug === $current_theme_slug) {
|
||||
$themes_without_updates = array($theme->slug => $theme) + $themes_without_updates;
|
||||
} else {
|
||||
$themes_without_updates[$theme->slug] = $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle response based on call type
|
||||
if ($is_legacy_call) {
|
||||
$result = $this->_prepare_legacy_response($themes_with_updates, $themes_without_updates, $theme_updates);
|
||||
} else {
|
||||
$result = $this->_prepare_paginated_response(
|
||||
$themes_with_updates,
|
||||
$themes_without_updates,
|
||||
$updates_only,
|
||||
$include_updates_pagination,
|
||||
$per_page,
|
||||
$page,
|
||||
$offset,
|
||||
$current_theme_slug,
|
||||
$sort_direction
|
||||
);
|
||||
}
|
||||
|
||||
// Add installed slugs if requested
|
||||
if (!empty($query['get_installed_slugs'])) {
|
||||
$result['installed_slugs'] = $installed_slugs;
|
||||
}
|
||||
|
||||
// Add snoozed themes info if any were requested
|
||||
if (!empty($snoozed_themes_info)) {
|
||||
$result['snoozed_themes_info'] = $snoozed_themes_info;
|
||||
}
|
||||
|
||||
return $this->_response(array_merge($result, $this->_get_backup_credentials_settings(get_theme_root())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare basic theme data
|
||||
*
|
||||
* @param WP_Theme $value Theme object
|
||||
* @param array $config Configuration array containing:
|
||||
* - slug: Theme slug
|
||||
* - website: Website name
|
||||
* - current_theme_slug: Current active theme slug
|
||||
* - is_legacy_call: Whether this is a legacy call
|
||||
* - snoozed_slugs: Array of snoozed theme slugs
|
||||
* @return stdClass Theme data object
|
||||
*/
|
||||
private function _prepare_theme_data($value, $config) {
|
||||
$theme = new stdClass();
|
||||
$theme->name = $value->get('Name') ? $value->get('Name') : $config['slug'];
|
||||
$theme->description = $value->get('Description');
|
||||
$theme->slug = $config['slug'];
|
||||
$theme->version = $value->get('Version');
|
||||
$theme->author = $value->get('Author');
|
||||
$theme->status = ($config['slug'] === $config['current_theme_slug']) ? 'active' : 'inactive';
|
||||
|
||||
// Set is_snoozed property for all themes
|
||||
$theme->is_snoozed = !empty($config['snoozed_slugs']) && in_array($config['slug'], $config['snoozed_slugs']);
|
||||
|
||||
if (!$config['is_legacy_call']) {
|
||||
$screenshot = $value->get_screenshot();
|
||||
$theme->screenshot = $screenshot ? $screenshot : null;
|
||||
$theme->has_update = false;
|
||||
}
|
||||
|
||||
$template = $value->get('Template');
|
||||
$theme->child_theme = !empty($template);
|
||||
$theme->website = $config['website'];
|
||||
$theme->multisite = is_multisite();
|
||||
$theme->site_url = trailingslashit(get_bloginfo('url'));
|
||||
|
||||
if ($theme->child_theme) {
|
||||
$parent_theme = wp_get_theme($template);
|
||||
$theme->parent = $parent_theme->get('Name') ? $parent_theme->get('Name') : $parent_theme->get_stylesheet();
|
||||
}
|
||||
|
||||
if (empty($theme->short_description) && !empty($theme->description)) {
|
||||
$temp = explode('.', $theme->description);
|
||||
$theme->short_description = $temp[0] . (isset($temp[1]) ? '.' . $temp[1] : '') . '.';
|
||||
}
|
||||
|
||||
return $theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add update information to theme object
|
||||
*
|
||||
* @param stdClass $theme Theme object
|
||||
* @param object $update_info Update information
|
||||
* @param bool $is_legacy_call Whether this is a legacy call
|
||||
* @return stdClass Updated theme object
|
||||
*/
|
||||
private function _add_update_info($theme, $update_info, $is_legacy_call) {
|
||||
$theme->latest_version = $update_info->update['new_version'];
|
||||
$theme->download_link = $update_info->update['package'];
|
||||
|
||||
if (!$is_legacy_call) {
|
||||
$theme->update_url = $update_info->update['url'];
|
||||
$theme->requires = $update_info->update['requires'];
|
||||
$theme->requires_php = $update_info->update['requires_php'];
|
||||
$theme->has_update = true;
|
||||
}
|
||||
|
||||
return $theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare legacy response format
|
||||
*
|
||||
* @param array $themes_with_updates Themes with updates
|
||||
* @param array $themes_without_updates Themes without updates
|
||||
* @param array $theme_updates Theme updates data
|
||||
* @return array Legacy format response
|
||||
*/
|
||||
private function _prepare_legacy_response($themes_with_updates, $themes_without_updates, $theme_updates) {
|
||||
return array(
|
||||
'themes' => array_merge(array_values($themes_with_updates), array_values($themes_without_updates)),
|
||||
'theme_updates' => $theme_updates,
|
||||
'is_super_admin' => is_super_admin(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare paginated response format
|
||||
*
|
||||
* @param array $themes_with_updates Themes with updates
|
||||
* @param array $themes_without_updates Themes without updates
|
||||
* @param bool $updates_only Whether only updates are requested
|
||||
* @param bool $include_updates_pagination Whether to include updates pagination
|
||||
* @param int $per_page Number of items per page
|
||||
* @param int $page Current page number
|
||||
* @param int $offset Offset for pagination
|
||||
* @param string $current_theme_slug Current active theme slug
|
||||
* @param string $sort_direction Sort direction
|
||||
* @return array Paginated response
|
||||
*/
|
||||
private function _prepare_paginated_response($themes_with_updates, $themes_without_updates, $updates_only, $include_updates_pagination, $per_page, $page, $offset, $current_theme_slug, $sort_direction) {
|
||||
|
||||
// If only requesting updates, filter out snoozed themes here
|
||||
if ($updates_only) {
|
||||
$filtered_updates = array_filter($themes_with_updates, array($this, '_filter_non_snoozed'));
|
||||
$results = array_slice($filtered_updates, $offset, $per_page);
|
||||
return array(
|
||||
'theme_updates' => $results,
|
||||
'pagination' => array(
|
||||
'total' => count($filtered_updates),
|
||||
'per_page' => $per_page,
|
||||
'current_page' => $page,
|
||||
'total_pages' => ceil(count($filtered_updates) / $per_page)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// For all themes view, merge and sort
|
||||
$all_themes = array_merge($themes_with_updates, $themes_without_updates);
|
||||
|
||||
// Sort themes
|
||||
global $current_theme_slug, $sort_direction;
|
||||
$current_theme_slug = $current_theme_slug;
|
||||
$sort_direction = $sort_direction;
|
||||
uasort($all_themes, array($this, '_sort_themes_with_active'));
|
||||
|
||||
// Get paginated results for all themes
|
||||
$results = array_slice($all_themes, $offset, $per_page);
|
||||
|
||||
// Filter out snoozed themes from updates list
|
||||
$filtered_updates = array_filter($themes_with_updates, array($this, '_filter_non_snoozed'));
|
||||
|
||||
// For theme_updates, use the filtered list
|
||||
$paginated_updates = $include_updates_pagination ? array_slice($filtered_updates, 0, $per_page) : $filtered_updates;
|
||||
|
||||
// Return with all the original data
|
||||
$total_themes = count($all_themes);
|
||||
$result = array(
|
||||
'themes' => $results, // Contains ALL themes including snoozed ones
|
||||
'theme_updates' => $paginated_updates, // Contains only non-snoozed themes with updates
|
||||
'is_super_admin' => is_super_admin(),
|
||||
'pagination' => array(
|
||||
'total' => $total_themes,
|
||||
'per_page' => $per_page,
|
||||
'current_page' => $page,
|
||||
'total_pages' => ceil($total_themes / $per_page)
|
||||
),
|
||||
'updates_count' => count($filtered_updates)
|
||||
);
|
||||
|
||||
if ($include_updates_pagination) {
|
||||
$result['updates_pagination'] = array(
|
||||
'total' => count($filtered_updates),
|
||||
'per_page' => $per_page,
|
||||
'current_page' => 1,
|
||||
'total_pages' => ceil(count($filtered_updates) / $per_page)
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort themes by name
|
||||
*
|
||||
* @param object $a Theme object
|
||||
* @param object $b Theme object
|
||||
* @return int Comparison result
|
||||
*/
|
||||
private function _sort_themes_by_name($a, $b) {
|
||||
global $sort_direction;
|
||||
$result = strcasecmp($a->name, $b->name);
|
||||
return 'desc' === $sort_direction ? -$result : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort all themes while keeping active theme first
|
||||
*
|
||||
* @param object $a Theme object
|
||||
* @param object $b Theme object
|
||||
* @return int Comparison result
|
||||
*/
|
||||
private function _sort_themes_with_active($a, $b) {
|
||||
global $current_theme_slug, $sort_direction;
|
||||
// Active theme always first
|
||||
if ($a->slug === $current_theme_slug) return -1;
|
||||
if ($b->slug === $current_theme_slug) return 1;
|
||||
|
||||
// Sort by name respecting direction
|
||||
$result = strcasecmp($a->name, $b->name);
|
||||
return 'desc' === $sort_direction ? -$result : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out snoozed themes
|
||||
*
|
||||
* @param object $theme Theme object
|
||||
* @return bool Whether the theme is not snoozed
|
||||
*/
|
||||
private function _filter_non_snoozed($theme) {
|
||||
return !$theme->is_snoozed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the backup and security credentials settings for this website
|
||||
*
|
||||
* @param array $query Parameter array Any available parameters needed for this action
|
||||
* @return array Contains the result of the current process
|
||||
*/
|
||||
public function get_theme_requirements() {
|
||||
return $this->_response($this->_get_backup_credentials_settings(get_theme_root()));
|
||||
}
|
||||
}
|
||||
1028
wp-content/plugins/updraftplus/central/modules/updates.php
Normal file
632
wp-content/plugins/updraftplus/central/modules/users.php
Normal file
@@ -0,0 +1,632 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
|
||||
|
||||
/**
|
||||
* Handles Users Commands
|
||||
*/
|
||||
class UpdraftCentral_Users_Commands extends UpdraftCentral_Commands {
|
||||
|
||||
/**
|
||||
* Compares two user object whether one is lesser than, equal to, greater than the other
|
||||
*
|
||||
* @internal
|
||||
* @param array $a First user in the comparison
|
||||
* @param array $b Second user in the comparison
|
||||
* @return integer Comparison results (0 = equal, -1 = less than, 1 = greater than)
|
||||
*/
|
||||
private function compare_user_id($a, $b) {
|
||||
if ($a->ID === $b->ID) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($a->ID < $b->ID) ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches users based from the keyword submitted
|
||||
*
|
||||
* @internal
|
||||
* @param array $query Parameter array containing the filter and keyword fields
|
||||
* @return array Contains the list of users found as well as the total users count
|
||||
*/
|
||||
private function _search_users($query) {
|
||||
$this->_admin_include('user.php');
|
||||
$query1 = new WP_User_Query(array(
|
||||
'orderby' => 'ID',
|
||||
'order' => 'ASC',
|
||||
'role'=> $query["role"],
|
||||
'search' => '*' . esc_attr($query["search"]) . '*',
|
||||
'search_columns' => array('user_login', 'user_email')
|
||||
));
|
||||
$query2 = new WP_User_Query(array(
|
||||
'orderby' => 'ID',
|
||||
'order' => 'ASC',
|
||||
'role'=> $query["role"],
|
||||
'meta_query'=>array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'key' => 'first_name',
|
||||
'value' => $query["search"],
|
||||
'compare' => 'LIKE'
|
||||
),
|
||||
array(
|
||||
'key' => 'last_name',
|
||||
'value' => $query["search"],
|
||||
'compare' => 'LIKE'
|
||||
),
|
||||
)
|
||||
));
|
||||
|
||||
if (empty($query1->results) && empty($query2->results)) {
|
||||
return array("message" => "users_not_found");
|
||||
} else {
|
||||
$found_users = array_merge($query1->results, $query2->results);
|
||||
$temp = array();
|
||||
foreach ($found_users as $new_user) {
|
||||
if (!isset($temp[$new_user->ID])) {
|
||||
$temp[$new_user->ID] = $new_user;
|
||||
}
|
||||
};
|
||||
|
||||
$users = array_values($temp);
|
||||
|
||||
// Sort users:
|
||||
usort($users, array($this, 'compare_user_id'));
|
||||
$offset = ((int) $query['page_no'] * (int) $query['per_page']) - (int) $query['per_page'];
|
||||
$user_list = array_slice($users, $offset, $query['per_page']);
|
||||
|
||||
return array(
|
||||
'users' => $user_list,
|
||||
'total_users' => count($users)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of pages needed to construct the pagination links
|
||||
*
|
||||
* @internal
|
||||
* @param array $query
|
||||
* @param array $total_users The total number of users found from the WP_User_Query query
|
||||
* @return array Contains information needed to construct the pagination links
|
||||
*/
|
||||
private function _calculate_pages($query, $total_users) {
|
||||
|
||||
$per_page_options = array(10, 20, 30, 40, 50);
|
||||
|
||||
if (!empty($query)) {
|
||||
|
||||
$pages = array();
|
||||
$page_count = ceil($total_users / $query["per_page"]);
|
||||
if ($page_count > 1) {
|
||||
|
||||
for ($i = 0; $i < $page_count; $i++) {
|
||||
if ($i + 1 == $query['page_no']) {
|
||||
$paginator_item = array(
|
||||
"value"=>$i+1,
|
||||
"setting"=>"disabled"
|
||||
);
|
||||
} else {
|
||||
$paginator_item = array(
|
||||
"value"=>$i+1
|
||||
);
|
||||
}
|
||||
array_push($pages, $paginator_item);
|
||||
};
|
||||
|
||||
if ($query['page_no'] >= $page_count) {
|
||||
$page_next = array(
|
||||
"value"=>$page_count,
|
||||
"setting"=>"disabled"
|
||||
);
|
||||
} else {
|
||||
$page_next = array(
|
||||
"value"=>$query['page_no'] + 1
|
||||
);
|
||||
};
|
||||
if (1 === $query['page_no']) {
|
||||
$page_prev = array(
|
||||
"value"=>1,
|
||||
"setting"=>"disabled"
|
||||
);
|
||||
} else {
|
||||
$page_prev = array(
|
||||
"value"=>$query['page_no'] - 1
|
||||
);
|
||||
};
|
||||
|
||||
return array(
|
||||
"page_no" => $query['page_no'],
|
||||
"per_page" => $query["per_page"],
|
||||
"page_count" => $page_count,
|
||||
"pages" => $pages,
|
||||
"page_next" => $page_next,
|
||||
"page_prev" => $page_prev,
|
||||
"total_results" => $total_users,
|
||||
"per_page_options" => $per_page_options
|
||||
);
|
||||
|
||||
} else {
|
||||
return array(
|
||||
"page_no" => $query['page_no'],
|
||||
"per_page" => $query["per_page"],
|
||||
"page_count" => $page_count,
|
||||
"total_results" => $total_users,
|
||||
"per_page_options" => $per_page_options
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return array(
|
||||
"per_page_options" => $per_page_options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates whether the username exists
|
||||
*
|
||||
* @param array $params Contains the user name to check and validate
|
||||
* @return array An array containing the result of the current process
|
||||
*/
|
||||
public function check_username($params) {
|
||||
$this->_admin_include('user.php');
|
||||
$username = $params['user_name'];
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (!empty($params['site_id'])) {
|
||||
$blog_id = $params['site_id'];
|
||||
}
|
||||
|
||||
|
||||
// Here, we're switching to the actual blog that we need
|
||||
// to pull users from.
|
||||
|
||||
$switched = function_exists('switch_to_blog') ? switch_to_blog($blog_id) : false;
|
||||
|
||||
if (username_exists($username) && is_user_member_of_blog(username_exists($username), $blog_id)) {
|
||||
$result = array("valid" => false, "message" => 'username_exists');
|
||||
return $this->_response($result);
|
||||
}
|
||||
if (!validate_username($username)) {
|
||||
$result = array("valid" => false, "message" => 'username_invalid');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
|
||||
// Here, we're restoring to the current (default) blog before we
|
||||
// do the switched.
|
||||
|
||||
if (function_exists('restore_current_blog') && $switched) {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
$result = array("valid" => true, "message" => 'username_valid');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls blog sites available
|
||||
* for the current WP instance.
|
||||
* If the site is a multisite, then sites under the network
|
||||
* will be pulled, otherwise, it will return an empty array.
|
||||
*
|
||||
* @return Array - an array of sites
|
||||
*/
|
||||
private function _get_blog_sites() {
|
||||
|
||||
if (!is_multisite()) return array();
|
||||
|
||||
// Initialize array container
|
||||
$sites = $network_sites = array();
|
||||
|
||||
// Check to see if latest get_sites (available on WP version >= 4.6) function is
|
||||
// available to pull any available sites from the current WP instance. If not, then
|
||||
// we're going to use the fallback function wp_get_sites (for older version).
|
||||
if (function_exists('get_sites') && class_exists('WP_Site_Query')) {
|
||||
$network_sites = get_sites();
|
||||
} else {
|
||||
if (function_exists('wp_get_sites')) {
|
||||
$network_sites = wp_get_sites();
|
||||
}
|
||||
}
|
||||
|
||||
// We only process if sites array is not empty, otherwise, bypass
|
||||
// the next block.
|
||||
if (!empty($network_sites)) {
|
||||
foreach ($network_sites as $site) {
|
||||
|
||||
// Here we're checking if the site type is an array, because
|
||||
// we're pulling the blog_id property based on the type of
|
||||
// site returned.
|
||||
// get_sites returns an array of object, whereas the wp_get_sites
|
||||
// function returns an array of array.
|
||||
$blog_id = is_array($site) ? $site['blog_id'] : $site->blog_id;
|
||||
|
||||
|
||||
// We're saving the blog_id and blog name as an associative item
|
||||
// into the sites array, that will be used as "Sites" option in
|
||||
// the frontend.
|
||||
$sites[$blog_id] = get_blog_details($blog_id)->blogname;
|
||||
}
|
||||
}
|
||||
|
||||
return $sites;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates whether the email exists
|
||||
*
|
||||
* @param array $params Contains the email to check and validate
|
||||
* @return array An array containing the result of the current process
|
||||
*/
|
||||
public function check_email($params) {
|
||||
$this->_admin_include('user.php');
|
||||
$email = $params['email'];
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($params['site_id']) && 0 !== $params['site_id']) {
|
||||
$blog_id = $params['site_id'];
|
||||
}
|
||||
|
||||
|
||||
// Here, we're switching to the actual blog that we need
|
||||
// to pull users from.
|
||||
|
||||
$switched = false;
|
||||
if (function_exists('switch_to_blog')) {
|
||||
$switched = switch_to_blog($blog_id);
|
||||
}
|
||||
|
||||
if (is_email($email) === false) {
|
||||
$result = array("valid" => false, "message" => 'email_invalid');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
if (email_exists($email) && is_user_member_of_blog(email_exists($email), $blog_id)) {
|
||||
$result = array("valid" => false, "message" => 'email_exists');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
// Here, we're restoring to the current (default) blog before we
|
||||
// do the switched.
|
||||
|
||||
if (function_exists('restore_current_blog') && $switched) {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
$result = array("valid" => true, "message" => 'email_valid');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The get_users function pull all the users from the database
|
||||
* based on the current search parameters/filters. Please see _search_users
|
||||
* for the breakdown of these parameters.
|
||||
*
|
||||
* @param array $query Parameter array containing the filter and keyword fields
|
||||
* @return array An array containing the result of the current process
|
||||
*/
|
||||
public function get_users($query) {
|
||||
$this->_admin_include('user.php');
|
||||
|
||||
// Here, we're getting the current blog id. If blog id
|
||||
// is passed along with the parameters then we override
|
||||
// that current (default) value with the parameter blog id value.
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($query['site_id']) && 0 !== $query['site_id']) $blog_id = $query['site_id'];
|
||||
|
||||
|
||||
// Here, we're switching to the actual blog that we need
|
||||
// to pull users from.
|
||||
|
||||
$switched = false;
|
||||
if (function_exists('switch_to_blog')) {
|
||||
$switched = switch_to_blog($blog_id);
|
||||
}
|
||||
|
||||
// Set default:
|
||||
if (empty($query["per_page"])) {
|
||||
$query["per_page"] = 10;
|
||||
}
|
||||
if (empty($query['page_no'])) {
|
||||
$query['page_no'] = 1;
|
||||
}
|
||||
if (empty($query["role"])) {
|
||||
$query["role"] = "";
|
||||
}
|
||||
|
||||
$users = array();
|
||||
$total_users = 0;
|
||||
|
||||
if (!empty($query["search"])) {
|
||||
$search_results = $this->_search_users($query);
|
||||
|
||||
if (isset($search_results['users'])) {
|
||||
$users = $search_results['users'];
|
||||
$total_users = $search_results['total_users'];
|
||||
}
|
||||
} else {
|
||||
$user_query = new WP_User_Query(array(
|
||||
'orderby' => 'ID',
|
||||
'order' => 'ASC',
|
||||
'number' => $query["per_page"],
|
||||
'paged'=> $query['page_no'],
|
||||
'role'=> $query["role"]
|
||||
));
|
||||
|
||||
if (empty($user_query->results)) {
|
||||
$result = array("message" => 'users_not_found');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
$users = $user_query->results;
|
||||
$total_users = $user_query->get_total();
|
||||
}
|
||||
|
||||
foreach ($users as &$user) {
|
||||
$user_object = get_userdata($user->ID);
|
||||
if (method_exists($user_object, 'to_array')) {
|
||||
$user = $user_object->to_array();
|
||||
$user["roles"] = $user_object->roles;
|
||||
$user["first_name"] = $user_object->first_name;
|
||||
$user["last_name"] = $user_object->last_name;
|
||||
$user["description"] = $user_object->description;
|
||||
} else {
|
||||
$user = $user_object;
|
||||
}
|
||||
}
|
||||
|
||||
$result = array(
|
||||
"users"=>$users,
|
||||
"paging" => $this->_calculate_pages($query, $total_users)
|
||||
);
|
||||
|
||||
// Here, we're restoring to the current (default) blog before we
|
||||
// do the switched.
|
||||
|
||||
if (function_exists('restore_current_blog') && $switched) {
|
||||
restore_current_blog();
|
||||
}
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new user for the current blog
|
||||
*
|
||||
* @param array $user User information to add
|
||||
* @return array An array containing the result of the current process
|
||||
*/
|
||||
public function add_user($user) {
|
||||
$this->_admin_include('user.php');
|
||||
// Here, we're getting the current blog id. If blog id
|
||||
// is passed along with the parameters then we override
|
||||
// that current (default) value with the parameter blog id value.
|
||||
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($user['site_id']) && 0 !== $user['site_id']) $blog_id = $user['site_id'];
|
||||
|
||||
|
||||
// Here, we're switching to the actual blog that we need
|
||||
// to pull users from.
|
||||
|
||||
$switched = false;
|
||||
if (function_exists('switch_to_blog')) {
|
||||
$switched = switch_to_blog($blog_id);
|
||||
}
|
||||
|
||||
if (!current_user_can('create_users') && !is_super_admin()) {
|
||||
$result = array('error' => true, 'message' => 'user_create_no_permission', 'data' => array('multisite' => is_multisite()));
|
||||
return $this->_response($result);
|
||||
}
|
||||
if (is_email($user["user_email"]) === false) {
|
||||
$result = array("error" => true, "message" => "email_invalid");
|
||||
return $this->_response($result);
|
||||
}
|
||||
if (email_exists($user["user_email"]) && is_user_member_of_blog(email_exists($user["user_email"]), $blog_id)) {
|
||||
$result = array("error" => true, "message" => "email_exists");
|
||||
return $this->_response($result);
|
||||
}
|
||||
if (username_exists($user["user_login"]) && is_user_member_of_blog(username_exists($user["user_login"]), $blog_id)) {
|
||||
$result = array("error" => true, "message" => "username_exists");
|
||||
return $this->_response($result);
|
||||
}
|
||||
if (!validate_username($user["user_login"])) {
|
||||
$result = array("error" => true, "message" => 'username_invalid');
|
||||
return $this->_response($result);
|
||||
}
|
||||
if (isset($user['site_id']) && !current_user_can('manage_network_users')) {
|
||||
$result = array("error" => true, "message" => 'user_create_no_permission');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
if (email_exists($user["user_email"]) && !is_user_member_of_blog(email_exists($user["user_email"]), $blog_id)) {
|
||||
$user_id = email_exists($user["user_email"]);
|
||||
} else {
|
||||
$user_id = wp_insert_user($user);
|
||||
}
|
||||
$role = $user['role'];
|
||||
if (is_multisite()) {
|
||||
add_existing_user_to_blog(array('user_id' => $user_id, 'role' => $role));
|
||||
}
|
||||
|
||||
// Here, we're restoring to the current (default) blog before we
|
||||
// do the switched.
|
||||
|
||||
if (function_exists('restore_current_blog') && $switched) {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
if ($user_id > 0) {
|
||||
$result = array("error" => false, "message" => "user_created_with_user_name", "values" => array($user['user_login']));
|
||||
return $this->_response($result);
|
||||
} else {
|
||||
$result = array("error" => true, "message" => "user_create_failed", "values" => array($user));
|
||||
}
|
||||
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* [delete_user - UCP: users.delete_user]
|
||||
*
|
||||
* This function is used to check to make sure the user_id is valid and that it has has user delete permissions.
|
||||
* If there are no issues, the user is deleted.
|
||||
*
|
||||
* current_user_can: This check the user permissions from UCP
|
||||
* get_userdata: This get the user data on the data from user_id in the $user_id array
|
||||
* wp_delete_user: Deleting users on the User ID (user_id) and, IF Specified, the Assigner ID (assign_user_id).
|
||||
*
|
||||
* @param [type] $params [description] THis is an Array of params sent over from UpdraftCentral
|
||||
* @return [type] Array [description] This will send back an error array along with message if there are any issues with the user_id
|
||||
*/
|
||||
public function delete_user($params) {
|
||||
$this->_admin_include('user.php');
|
||||
$user_id = $params['user_id'];
|
||||
$assign_user_id = $params["assign_user_id"];
|
||||
// Here, we're getting the current blog id. If blog id
|
||||
// is passed along with the parameters then we override
|
||||
// that current (default) value with the parameter blog id value.
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($params['site_id']) && 0 !== $params['site_id']) $blog_id = $params['site_id'];
|
||||
|
||||
$switched = false;
|
||||
if (function_exists('switch_to_blog')) {
|
||||
$switched = switch_to_blog($blog_id);
|
||||
}
|
||||
|
||||
if (!current_user_can('delete_users') && !is_super_admin()) {
|
||||
$result = array('error' => true, 'message' => 'user_delete_no_permission', 'data' => array('multisite' => is_multisite()));
|
||||
return $this->_response($result);
|
||||
}
|
||||
if (get_userdata($user_id) === false) {
|
||||
$result = array("error" => true, "message" => "user_not_found");
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
if (wp_delete_user($user_id, $assign_user_id)) {
|
||||
$result = array("error" => false, "message" => "user_deleted");
|
||||
} else {
|
||||
$result = array("error" => true, "message" => "user_delete_failed");
|
||||
}
|
||||
|
||||
// Here, we're restoring to the current (default) blog before we
|
||||
// do the switched.
|
||||
|
||||
if (function_exists('restore_current_blog') && $switched) {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits existing user information
|
||||
*
|
||||
* @param array $user User information to save
|
||||
* @return array An array containing the result of the current process
|
||||
*/
|
||||
public function edit_user($user) {
|
||||
$this->_admin_include('user.php');
|
||||
|
||||
// Here, we're getting the current blog id. If blog id
|
||||
// is passed along with the parameters then we override
|
||||
// that current (default) value with the parameter blog id value.
|
||||
|
||||
$blog_id = get_current_blog_id();
|
||||
if (isset($user['site_id']) && 0 !== $user['site_id']) $blog_id = $user['site_id'];
|
||||
|
||||
// Here, we're switching to the actual blog that we need
|
||||
// to apply our changes.
|
||||
|
||||
$switched = false;
|
||||
if (function_exists('switch_to_blog')) {
|
||||
$switched = switch_to_blog($blog_id);
|
||||
}
|
||||
|
||||
if (!current_user_can('edit_users') && !is_super_admin() && get_current_user_id() !== $user["ID"]) {
|
||||
$result = array('error' => true, 'message' => 'user_edit_no_permission', 'data' => array('multisite' => is_multisite()));
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
if (false === get_userdata($user["ID"])) {
|
||||
$result = array("error" => true, "message" => "user_not_found");
|
||||
return $this->_response($result);
|
||||
}
|
||||
if (get_current_user_id() == $user["ID"]) {
|
||||
unset($user["role"]);
|
||||
}
|
||||
|
||||
/* Validate Username*/
|
||||
if (!validate_username($user["user_login"])) {
|
||||
$result = array("error" => true, "message" => 'username_invalid');
|
||||
return $this->_response($result);
|
||||
}
|
||||
/* Validate Email if not the same*/
|
||||
|
||||
$remote_user = get_userdata($user["ID"]);
|
||||
$old_email = $remote_user->user_email;
|
||||
|
||||
if ($user['user_email'] !== $old_email) {
|
||||
if (is_email($user['user_email']) === false) {
|
||||
$result = array("error" => true, "message" => 'email_invalid');
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
if (email_exists($user['user_email'])) {
|
||||
$result = array("error" => true, "message" => 'email_exists');
|
||||
return $this->_response($result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$user_id = wp_update_user($user);
|
||||
if (is_wp_error($user_id)) {
|
||||
$result = array("error" => true, "message" => "user_edit_failed_with_error", "values" => array($user_id));
|
||||
} else {
|
||||
$result = array("error" => false, "message" => "user_edited_with_user_name", "values" => array($user["user_login"]));
|
||||
}
|
||||
|
||||
// Here, we're restoring to the current (default) blog before we
|
||||
// do the switched.
|
||||
|
||||
if (function_exists('restore_current_blog') && $switched) {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
return $this->_response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves available roles to be used as filter options
|
||||
*
|
||||
* @return array An array containing all available roles
|
||||
*/
|
||||
public function get_roles() {
|
||||
$this->_admin_include('user.php');
|
||||
$roles = array_reverse(get_editable_roles());
|
||||
return $this->_response($roles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves information to be use as filters
|
||||
*
|
||||
* @return array An array containing the filter fields and their data
|
||||
*/
|
||||
public function get_user_filters() {
|
||||
$this->_admin_include('user.php');
|
||||
|
||||
// Pull sites options if available.
|
||||
$sites = $this->_get_blog_sites();
|
||||
|
||||
$result = array(
|
||||
"sites" => $sites,
|
||||
"roles" => array_reverse(get_editable_roles()),
|
||||
"paging" => $this->_calculate_pages(null, 0),
|
||||
);
|
||||
return $this->_response($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('Security check');
|
||||
|
||||
// Translations for UpdraftCentral
|
||||
return array(
|
||||
'updraftcentral_connection' => __('UpdraftCentral Connection', 'updraftplus'),
|
||||
'updraftcentral_connection_successful' => __('An UpdraftCentral connection has been made successfully.', 'updraftplus'),
|
||||
'updraftcentral_connection_failed' => __('A new UpdraftCentral connection has not been made.', 'updraftplus'),
|
||||
'unknown_key' => __('The key referred to was unknown.', 'updraftplus'),
|
||||
'not_logged_in' => __('You are not logged into this WordPress site in your web browser.', 'updraftplus'),
|
||||
'must_visit_url' => __('You must visit this URL in the same browser and login session as you created the key in.', 'updraftplus'),
|
||||
'security_check' => __('Security check.', 'updraftplus'),
|
||||
'must_visit_link' => __('You must visit this link in the same browser and login session as you created the key in.', 'updraftplus'),
|
||||
'connection_already_made' => __('This connection appears to already have been made.', 'updraftplus'),
|
||||
'close' => __('Close', 'updraftplus'),
|
||||
'nothing_yet_logged' => __('(Nothing yet logged)', 'updraftplus'),
|
||||
'invalid_url' => __('An invalid URL was entered', 'updraftplus'),
|
||||
'updraftcentral_key_created' => __('UpdraftCentral key created successfully', 'updraftplus'),
|
||||
'need_to_copy_key' => __('You now need to copy the key below and enter it at your %s.', 'updraftplus'),
|
||||
'press_add_site_button' => __('At your UpdraftCentral dashboard you should press the "Add Site" button then paste the key in the input box.', 'updraftplus'),
|
||||
'detailed_instructions' => __('Detailed instructions for this can be found at %s', 'updraftplus'),
|
||||
'control_this_site' => __('You can now control this site via your UpdraftCentral dashboard at %s.', 'updraftplus'),
|
||||
'attempt_to_register_failed' => __('A key was created, but the attempt to register it with %1$s was unsuccessful.', 'updraftplus').' '.__('You can try again, or try using the alternative connection method if the problem persists.', 'updraftplus').' '.__('For more information visit %2$s', 'updraftplus'),
|
||||
'key_created_successfully' => __('Key created successfully.', 'updraftplus'),
|
||||
'copy_paste_key' => __('You must copy and paste this key now - it cannot be shown again.', 'updraftplus'),
|
||||
'no_updraftcentral_dashboards' => __('There are no UpdraftCentral dashboards that can currently control this site.', 'updraftplus'),
|
||||
'unknown' => __('Unknown', 'updraftplus'),
|
||||
'access_as_user' => __('Access this site as user:', 'updraftplus'),
|
||||
'public_key_sent' => __('Public key was sent to:', 'updraftplus'),
|
||||
'created' => __('Created:', 'updraftplus'),
|
||||
'key_size' => __('Key size: %d bits', 'updraftplus'),
|
||||
'delete' => __('Delete...', 'updraftplus'),
|
||||
'manage_keys' => __('Manage existing keys (%d)...', 'updraftplus'),
|
||||
'key_description' => __('Key description', 'updraftplus'),
|
||||
'details' => __('Details', 'updraftplus'),
|
||||
'connect_to_updraftcentral_dashboard' => __('Connect this site to an UpdraftCentral dashboard found at...', 'updraftplus'),
|
||||
'in_example' => __('i.e. if you have %s there', 'updraftplus'),
|
||||
'an_account' => __('an account', 'updraftplus'),
|
||||
'self_hosted_dashboard' => __('Self-hosted dashboard', 'updraftplus'),
|
||||
'website_installed' => __('A website where you have installed %s', 'updraftplus'),
|
||||
'enter_url' => __('Enter the URL where your self-hosted install of UpdraftCentral is located:', 'updraftplus'),
|
||||
'updraftcentral_dashboard_url' => __('URL for the site of your UpdraftCentral dashboard', 'updraftplus'),
|
||||
'next' => __('Next', 'updraftplus'),
|
||||
'updraftcentral_connection_details' => __('UpdraftCentral dashboard connection details', 'updraftplus'),
|
||||
'description' => __('Description', 'updraftplus'),
|
||||
'enter_description' => __('Enter any description', 'updraftplus'),
|
||||
'encryption_key_size' => __('Encryption key size:', 'updraftplus'),
|
||||
'bits' => __('%s bits', 'updraftplus'),
|
||||
'bytes' => __('%s bytes', 'updraftplus'),
|
||||
'easy_to_break' => __('easy to break, fastest', 'updraftplus'),
|
||||
'faster' => __('faster (possibility for slow PHP installs)', 'updraftplus'),
|
||||
'recommended' => __('recommended', 'updraftplus'),
|
||||
'slower' => __('slower, strongest', 'updraftplus'),
|
||||
'use_alternative_method' => __('Use the alternative method for making a connection with the dashboard.', 'updraftplus'),
|
||||
'more_information' => __('More information...', 'updraftplus'),
|
||||
'this_is_useful' => __('This is useful if the dashboard webserver cannot be contacted with incoming traffic by this website (for example, this is the case if this website is hosted on the public Internet, but the UpdraftCentral dashboard is on localhost, or on an Intranet, or if this website has an outgoing firewall), or if the dashboard website does not have a SSL certificate.', 'updraftplus'),
|
||||
'create' => __('Create', 'updraftplus'),
|
||||
'back' => __('Back...', 'updraftplus'),
|
||||
'view_log_events' => __('View recent UpdraftCentral log events', 'updraftplus'),
|
||||
'updraftcentral_remote_control' => __('UpdraftCentral (Remote Control)', 'updraftplus'),
|
||||
'updraftcentral_description' => __('UpdraftCentral enables control of your WordPress sites %s from a central dashboard.', 'updraftplus'),
|
||||
'including_description' => array(
|
||||
'wp_optimize_desc' => __('(including management of WP-Optimize)', 'updraftplus'),
|
||||
'updraftplus_desc' => __('(including management of backups and updates)', 'updraftplus'),
|
||||
),
|
||||
'read_more' => __('Read more about it here.', 'updraftplus'),
|
||||
'create_another_key' => __('Create another key', 'updraftplus'),
|
||||
'unable_to_connect' => __('Unable to connect to the filesystem', 'updraftplus'),
|
||||
'unable_to_activate' => __('Unable to activate %s successfully.', 'updraftplus').' '.__('Make sure that this %s is compatible with your remote WordPress version.', 'updraftplus').' '.__('WordPress version currently installed in your remote website is %s.', 'updraftplus'),
|
||||
'unable_to_install' => __('Unable to install %s.', 'updraftplus').' '.__('Make sure you upload the correct file and that the zip file is a valid %s file (not corrupted) and try uploading the file again.', 'updraftplus'),
|
||||
'failed_to_attach_media' => __('Failed to attach media.', 'updraftplus'),
|
||||
'media_attached' => __('Media has been attached to post.', 'updraftplus'),
|
||||
'failed_to_detach_media' => __('Failed to detach media.', 'updraftplus'),
|
||||
'media_detached' => __('Media has been detached from post.', 'updraftplus'),
|
||||
'failed_to_delete_media' => __('Failed to delete selected media.', 'updraftplus'),
|
||||
'selected_media_deleted' => __('Selected media has been deleted successfully.', 'updraftplus'),
|
||||
'unattached' => __('Unattached', 'updraftplus'),
|
||||
'default_template' => __('Default template', 'updraftplus'),
|
||||
'parameters_missing' => __('Expected parameter(s) missing.', 'updraftplus'),
|
||||
'fetching' => __('Fetching...', 'updraftplus'),
|
||||
'deleting' => __('Deleting...', 'updraftplus'),
|
||||
'enter_mothership_url' => __('Please enter a valid URL', 'updraftplus'),
|
||||
'creating_please_allow' => __('Creating...', 'updraftplus').(function_exists('openssl_encrypt') ? '' : ' ('.__('your PHP install lacks the openssl module; as a result, this can take minutes; if nothing has happened by then, then you should either try a smaller key size, or ask your web hosting company how to enable this PHP module on your setup.', 'updraftplus').')'),
|
||||
'unexpectedresponse' => __('Unexpected response:', 'updraftplus'),
|
||||
'updraftcentral_wizard_empty_url' => __('Please enter the URL where your UpdraftCentral dashboard is hosted.', 'updraftplus'),
|
||||
'updraftcentral_wizard_invalid_url' => __('Please enter a valid URL e.g http://example.com', 'updraftplus'),
|
||||
'insufficient_privilege' => __('Sorry, you do not have enough privilege to execute the requested action.', 'updraftplus'),
|
||||
'copy_to_clipboard' => __('Copy to clipboard', 'updraftplus'),
|
||||
'key_copied' => __('The key was copied to the clipboard.', 'updraftplus'),
|
||||
'unable_to_copy' => __('The attempt to copy to the clipboard failed.', 'updraftplus'),
|
||||
'wpo_not_active' => __('WP_Optimize is not installed or active.', 'updraftplus'),
|
||||
'log_file_not_exist' => __('Log file does not exist or could not be read.', 'updraftplus'),
|
||||
'security_check_failed' => __('Security check failed; try refreshing the page.', 'updraftplus').' '.__('If refreshing the page does not help then perhaps you do not have sufficient privilege to manage WP-Optimize.', 'updraftplus'),
|
||||
'no_such_command' => __('No such command found.', 'updraftplus'),
|
||||
'command_not_allowed' => __('You are not allowed to run this command.', 'updraftplus'),
|
||||
'command_not_found' => __('The command is either not found or not allowed.', 'updraftplus'),
|
||||
'network_admin_only' => __('The command can only be executed by a network admin.', 'updraftplus'),
|
||||
);
|
||||
282
wp-content/plugins/updraftplus/central/updraftplus.php
Normal file
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
|
||||
if (class_exists('UpdraftPlus_Host')) return;
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) define('UPDRAFTCENTRAL_CLIENT_DIR', dirname(__FILE__));
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_URL')) define('UPDRAFTCENTRAL_CLIENT_URL', plugins_url('', __FILE__));
|
||||
if (!class_exists('UpdraftCentral_Host')) {
|
||||
include_once(UPDRAFTCENTRAL_CLIENT_DIR.'/host.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is the basic bridge between UpdraftCentral and UpdraftPlus.
|
||||
*/
|
||||
class UpdraftPlus_Host extends UpdraftCentral_Host {
|
||||
|
||||
public $plugin_name = 'updraftplus';
|
||||
|
||||
public $translations = array();
|
||||
|
||||
protected static $_instance = null;
|
||||
|
||||
/**
|
||||
* Creates an instance of this class. Singleton Pattern
|
||||
*
|
||||
* @return object Instance of this class
|
||||
*/
|
||||
public static function instance() {
|
||||
if (empty(self::$_instance)) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
add_action('updraftplus_debugtools_dashboard', array($this, 'debugtools_dashboard'), 20);
|
||||
add_action('updraftplus_load_translations_for_udcentral', array($this, 'load_updraftplus_translations'));
|
||||
|
||||
$this->maybe_initialize_required_objects();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the UpdraftCentral_Main instance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function load_updraftcentral() {
|
||||
$central_path = $this->is_host_dir_set() ? trailingslashit(UPDRAFTPLUS_DIR) : '';
|
||||
|
||||
if (file_exists($central_path.'central/bootstrap.php')) {
|
||||
include_once($central_path.'central/bootstrap.php');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current user can perform key control AJAX actions
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function current_user_can_ajax() {
|
||||
return UpdraftPlus_Options::user_can_manage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Below are interface methods' implementations that are required by UpdraftCentral to function properly. Please
|
||||
* see the "interface.php" to check all the required interface methods.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks whether the plugin's DIR constant is currently define or not
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_host_dir_set() {
|
||||
return defined('UPDRAFTPLUS_DIR') ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the host plugin's dir path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_host_dir() {
|
||||
return defined('UPDRAFTPLUS_DIR') ? UPDRAFTPLUS_DIR : dirname(dirname(__FILE__));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the filter used by UpdraftPlus to log errors or certain events
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_logline_filter() {
|
||||
return 'updraftplus_logline';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether debug mod is set
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_debug_mode() {
|
||||
if (class_exists('UpdraftPlus_Options')) {
|
||||
return UpdraftPlus_Options::get_updraft_option('updraft_debug_mode');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used as a central location (to avoid repetition) to register or de-register hooks into the WP HTTP API
|
||||
*
|
||||
* @param bool $register True to register, false to de-register
|
||||
* @return void
|
||||
*/
|
||||
public function register_wp_http_option_hooks($register = true) {
|
||||
global $updraftplus;
|
||||
|
||||
if ($updraftplus) {
|
||||
$updraftplus->register_wp_http_option_hooks($register);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the class name of the host plugin
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
public function get_class_name() {
|
||||
global $updraftplus;
|
||||
|
||||
if ($updraftplus) {
|
||||
return get_class($updraftplus);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the host plugin
|
||||
*
|
||||
* @return object|bool
|
||||
*/
|
||||
public function get_instance() {
|
||||
global $updraftplus;
|
||||
|
||||
if ($updraftplus) {
|
||||
return $updraftplus;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the admin instance of the host plugin
|
||||
*
|
||||
* @return object|bool
|
||||
*/
|
||||
public function get_admin_instance() {
|
||||
global $updraftplus_admin;
|
||||
|
||||
if ($updraftplus_admin) {
|
||||
return $updraftplus_admin;
|
||||
} else {
|
||||
if (defined('UPDRAFTPLUS_DIR') && file_exists(UPDRAFTPLUS_DIR.'/admin.php')) {
|
||||
updraft_try_include_file('admin.php', 'include_once');
|
||||
$updraftplus_admin = new UpdraftPlus_Admin();
|
||||
return $updraftplus_admin;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the given line
|
||||
*
|
||||
* @param string $line The log line
|
||||
* @param string $level The log level: notice, warning, error, etc.
|
||||
* @param boolean|string $uniq_id Each of these will only be logged once
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log($line, $level = 'notice', $uniq_id = false) {
|
||||
global $updraftplus;
|
||||
|
||||
if ($updraftplus) {
|
||||
if (is_callable(array($updraftplus, 'log'))) {
|
||||
call_user_func(array($updraftplus, 'log'), $line, $level, $uniq_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current version of the host plugin
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
public function get_version() {
|
||||
global $updraftplus;
|
||||
|
||||
if ($updraftplus) {
|
||||
return $updraftplus->version;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filesystem class of the host's plugin
|
||||
*
|
||||
* @return class|bool
|
||||
*/
|
||||
public function get_filesystem_functions() {
|
||||
if ($this->has_filesystem_functions()) {
|
||||
return UpdraftPlus_Filesystem_Functions;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the filesystem class of the host plugin exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_filesystem_functions() {
|
||||
return class_exists('UpdraftPlus_Filesystem_Functions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether force debugging is set
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_force_debug() {
|
||||
return (defined('UPDRAFTPLUS_UDRPC_FORCE_DEBUG') && UPDRAFTPLUS_UDRPC_FORCE_DEBUG) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes required objects (if not yet initialized) for UpdraftCentral usage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function maybe_initialize_required_objects() {
|
||||
global $updraftplus;
|
||||
|
||||
if (!class_exists('UpdraftPlus')) {
|
||||
if (defined('UPDRAFTPLUS_DIR') && file_exists(UPDRAFTPLUS_DIR.'/class-updraftplus.php')) {
|
||||
updraft_try_include_file('class-updraftplus.php', 'include_once');
|
||||
if (empty($updraftplus) || !is_a($updraftplus, 'UpdraftPlus')) {
|
||||
$updraftplus = new UpdraftPlus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('UpdraftPlus_Options')) {
|
||||
if (defined('UPDRAFTPLUS_DIR') && file_exists(UPDRAFTPLUS_DIR.'/options.php')) {
|
||||
updraft_try_include_file('options.php', 'require_once');
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('UpdraftPlus_Filesystem_Functions')) {
|
||||
if (defined('UPDRAFTPLUS_DIR') && file_exists(UPDRAFTPLUS_DIR.'/includes/class-filesystem-functions.php')) {
|
||||
updraft_try_include_file('includes/class-filesystem-functions.php', 'require_once');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load translations which are based on UpdraftPlus domain text
|
||||
*/
|
||||
public function load_updraftplus_translations() {
|
||||
// Load updraftplus translations
|
||||
if (defined('UPDRAFTCENTRAL_CLIENT_DIR') && file_exists(UPDRAFTCENTRAL_CLIENT_DIR.'/translations-central.php')) {
|
||||
$this->translations = include(UPDRAFTCENTRAL_CLIENT_DIR.'/translations-central.php');
|
||||
}
|
||||
}
|
||||
}
|
||||
156
wp-content/plugins/updraftplus/central/wp-optimize.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
if (class_exists('WP_Optimize_Host')) return;
|
||||
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) define('UPDRAFTCENTRAL_CLIENT_DIR', dirname(__FILE__));
|
||||
if (!defined('UPDRAFTCENTRAL_CLIENT_URL')) define('UPDRAFTCENTRAL_CLIENT_URL', plugins_url('', __FILE__));
|
||||
if (!class_exists('UpdraftCentral_Host')) {
|
||||
include_once(UPDRAFTCENTRAL_CLIENT_DIR.'/host.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is the basic bridge between UpdraftCentral and WP_Optimize.
|
||||
*/
|
||||
class WP_Optimize_Host extends UpdraftCentral_Host {
|
||||
|
||||
public $plugin_name = 'wp-optimize';
|
||||
|
||||
public $translations = array();
|
||||
|
||||
protected static $_instance = null;
|
||||
|
||||
/**
|
||||
* Creates an instance of this class. Singleton Pattern
|
||||
*
|
||||
* @return object Instance of this class
|
||||
*/
|
||||
public static function instance() {
|
||||
if (empty(self::$_instance)) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
add_action('updraftplus_load_translations_for_udcentral', array($this, 'load_updraftplus_translations'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current user can perform key control AJAX actions
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function current_user_can_ajax() {
|
||||
return current_user_can(WP_Optimize()->capability_required());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the UpdraftCentral_Main instance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function load_updraftcentral() {
|
||||
$central_path = $this->is_host_dir_set() ? trailingslashit(WPO_PLUGIN_MAIN_PATH) : '';
|
||||
|
||||
if (!empty($central_path) && file_exists($central_path.'central/bootstrap.php')) {
|
||||
include_once($central_path.'central/bootstrap.php');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the plugin's DIR constant is currently define or not
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_host_dir_set() {
|
||||
return defined('WPO_PLUGIN_MAIN_PATH') ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the host plugin's dir path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_host_dir() {
|
||||
return defined('WPO_PLUGIN_MAIN_PATH') ? WPO_PLUGIN_MAIN_PATH : dirname(dirname(__FILE__));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current version of the host plugin
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
public function get_version() {
|
||||
return defined('WPO_VERSION') ? WPO_VERSION : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the host plugin
|
||||
*
|
||||
* @return object|bool
|
||||
*/
|
||||
public function get_instance() {
|
||||
global $wp_optimize;
|
||||
|
||||
if ($wp_optimize) {
|
||||
return $wp_optimize;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether debug mod is set
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_debug_mode() {
|
||||
return (defined('WP_OPTIMIZE_DEBUG_OPTIMIZATIONS') && WP_OPTIMIZE_DEBUG_OPTIMIZATIONS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the given line
|
||||
*
|
||||
* @param string $line The log line
|
||||
* @param string $level The log level: notice, warning, error, etc.
|
||||
* @param boolean|string $uniq_id Each of these will only be logged once
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log($line, $level = 'notice', $uniq_id = false) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Unused parameter is present because the the abstract UpdraftCentral_Host class uses 3 arguments.
|
||||
global $wp_optimize;
|
||||
|
||||
if ($wp_optimize) {
|
||||
if (is_callable(array($wp_optimize, 'log'))) {
|
||||
call_user_func(array($wp_optimize, 'log'), $line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load translations which are based on UpdraftPlus domain text
|
||||
*/
|
||||
public function load_updraftplus_translations() {
|
||||
// Load wp-optimize translations
|
||||
if (defined('UPDRAFTCENTRAL_CLIENT_DIR') && file_exists(UPDRAFTCENTRAL_CLIENT_DIR.'/translations-central.php')) {
|
||||
$this->translations = include(UPDRAFTCENTRAL_CLIENT_DIR.'/translations-central.php');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Developer Note:
|
||||
*
|
||||
* You can add your class methods below if ever you want to extend or modify
|
||||
* the module handlers of UpdraftCentral located at central/modules. Just be
|
||||
* sure to use this class to abstract any functionality that would link to the
|
||||
* wp-optimize plugin.
|
||||
*
|
||||
* N.B. All custom methods added here will then be available from the global
|
||||
* variable $updraftcentral_host_plugin (e.g. $updraftcentral_host_plugin->YOUR_METHOD)
|
||||
*/
|
||||
}
|
||||
1945
wp-content/plugins/updraftplus/changelog-old.txt
Normal file
6512
wp-content/plugins/updraftplus/class-updraftplus.php
Normal file
@@ -0,0 +1,5 @@
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top.shepherd-target-attached-right .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-target-attached-right .shepherd-content:before {
|
||||
display: none; }
|
||||
1
wp-content/plugins/updraftplus/css/tether-shepherd/shepherd-theme-arrows-fix.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top.shepherd-target-attached-left .shepherd-content:before,.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top.shepherd-target-attached-right .shepherd-content:before,.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-target-attached-left .shepherd-content:before,.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-target-attached-right .shepherd-content:before{display:none}
|
||||
@@ -0,0 +1,215 @@
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top.shepherd-target-attached-right .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-target-attached-right .shepherd-content:before {
|
||||
display: none; }
|
||||
|
||||
.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before {
|
||||
box-sizing: border-box; }
|
||||
|
||||
.shepherd-element {
|
||||
position: absolute;
|
||||
display: none; }
|
||||
.shepherd-element.shepherd-open {
|
||||
display: block; }
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons {
|
||||
max-width: 100%;
|
||||
max-height: 100%; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content {
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
background: #fff;
|
||||
color: #444;
|
||||
padding: 1em;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.5em;
|
||||
-webkit-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
-webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
|
||||
filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2)); }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-width: 16px;
|
||||
border-style: solid;
|
||||
pointer-events: none; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-top-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-bottom-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-left-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-right-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-left.shepherd-target-attached-center .shepherd-content {
|
||||
left: -32px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-right.shepherd-target-attached-center .shepherd-content {
|
||||
left: 32px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
top: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
top: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #fff; }
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before {
|
||||
border-bottom-color: #eee; }
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header {
|
||||
background: #eee;
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link {
|
||||
padding: 0;
|
||||
margin-bottom: 0; }
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-cancel-link .shepherd-content header h3 {
|
||||
float: left; }
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content {
|
||||
padding: 0; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header {
|
||||
*zoom: 1;
|
||||
border-radius: 5px 5px 0 0; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header h3 {
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
font-weight: normal; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header a.shepherd-cancel-link {
|
||||
float: right;
|
||||
text-decoration: none;
|
||||
font-size: 1.25em;
|
||||
line-height: .8em;
|
||||
font-weight: normal;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
opacity: 0.25;
|
||||
position: relative;
|
||||
top: .1em;
|
||||
padding: .8em;
|
||||
margin-bottom: -.8em; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header a.shepherd-cancel-link:hover {
|
||||
opacity: 1; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content .shepherd-text {
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content .shepherd-text p {
|
||||
margin: 0 0 .5em 0;
|
||||
line-height: 1.3em; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content .shepherd-text p:last-child {
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer {
|
||||
padding: 0 1em 1em; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons {
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li .shepherd-button {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
cursor: pointer;
|
||||
margin: 0 .5em 0 0;
|
||||
text-decoration: none; }
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button {
|
||||
margin-right: 0; }
|
||||
@@ -0,0 +1,229 @@
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top.shepherd-target-attached-right .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-target-attached-right .shepherd-content:before {
|
||||
display: none; }
|
||||
|
||||
.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before {
|
||||
box-sizing: border-box; }
|
||||
|
||||
.shepherd-element {
|
||||
position: absolute;
|
||||
display: none; }
|
||||
.shepherd-element.shepherd-open {
|
||||
display: block; }
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows {
|
||||
max-width: 100%;
|
||||
max-height: 100%; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content {
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
background: #fff;
|
||||
color: #444;
|
||||
padding: 1em;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.5em;
|
||||
-webkit-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
-webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
|
||||
filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2)); }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-width: 16px;
|
||||
border-style: solid;
|
||||
pointer-events: none; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-top-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-bottom-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-left-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-right-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-left.shepherd-target-attached-center .shepherd-content {
|
||||
left: -32px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-right.shepherd-target-attached-center .shepherd-content {
|
||||
left: 32px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
top: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
top: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #fff; }
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-arrows.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before {
|
||||
border-bottom-color: #eee; }
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-has-title .shepherd-content header {
|
||||
background: #eee;
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-has-title .shepherd-content header a.shepherd-cancel-link {
|
||||
padding: 0;
|
||||
margin-bottom: 0; }
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows.shepherd-has-cancel-link .shepherd-content header h3 {
|
||||
float: left; }
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content {
|
||||
padding: 0; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content * {
|
||||
font-size: inherit; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content header {
|
||||
*zoom: 1;
|
||||
border-radius: 5px 5px 0 0; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content header:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content header h3 {
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
font-weight: normal; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content header a.shepherd-cancel-link {
|
||||
float: right;
|
||||
text-decoration: none;
|
||||
font-size: 1.25em;
|
||||
line-height: .8em;
|
||||
font-weight: normal;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
opacity: 0.25;
|
||||
position: relative;
|
||||
top: .1em;
|
||||
padding: .8em;
|
||||
margin-bottom: -.8em; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content header a.shepherd-cancel-link:hover {
|
||||
opacity: 1; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content .shepherd-text {
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content .shepherd-text p {
|
||||
margin: 0 0 .5em 0;
|
||||
line-height: 1.3em; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content .shepherd-text p:last-child {
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content footer {
|
||||
padding: 0 1em 1em; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content footer .shepherd-buttons {
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content footer .shepherd-buttons li {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content footer .shepherd-buttons li .shepherd-button {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary {
|
||||
background: #eee;
|
||||
color: #888; }
|
||||
.shepherd-element.shepherd-theme-arrows .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button {
|
||||
margin-right: 0; }
|
||||
1
wp-content/plugins/updraftplus/css/tether-shepherd/shepherd-theme-arrows.min.css
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top.shepherd-target-attached-right .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-target-attached-right .shepherd-content:before {
|
||||
display: none; }
|
||||
|
||||
.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before {
|
||||
box-sizing: border-box; }
|
||||
|
||||
.shepherd-element {
|
||||
position: absolute;
|
||||
display: none; }
|
||||
.shepherd-element.shepherd-open {
|
||||
display: block; }
|
||||
|
||||
.shepherd-element.shepherd-theme-dark {
|
||||
max-width: 100%;
|
||||
max-height: 100%; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content {
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
background: #232323;
|
||||
color: #eee;
|
||||
padding: 1em;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.5em; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-width: 16px;
|
||||
border-style: solid;
|
||||
pointer-events: none; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-left-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-right-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-left.shepherd-target-attached-center .shepherd-content {
|
||||
left: -32px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-right.shepherd-target-attached-center .shepherd-content {
|
||||
left: 32px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
top: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
top: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #232323; }
|
||||
|
||||
.shepherd-element.shepherd-theme-dark {
|
||||
z-index: 9999;
|
||||
max-width: 24em;
|
||||
font-size: 1em; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before {
|
||||
border-bottom-color: #303030; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-has-title .shepherd-content header {
|
||||
background: #303030;
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-has-title .shepherd-content header a.shepherd-cancel-link {
|
||||
padding: 0;
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-dark.shepherd-has-cancel-link .shepherd-content header h3 {
|
||||
float: left; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content {
|
||||
box-shadow: 0 0 1em rgba(0, 0, 0, 0.2);
|
||||
padding: 0; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content * {
|
||||
font-size: inherit; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content header {
|
||||
*zoom: 1;
|
||||
border-radius: 5px 5px 0 0; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content header:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content header h3 {
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
font-weight: normal; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content header a.shepherd-cancel-link {
|
||||
float: right;
|
||||
text-decoration: none;
|
||||
font-size: 1.25em;
|
||||
line-height: .8em;
|
||||
font-weight: normal;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
opacity: 0.25;
|
||||
position: relative;
|
||||
top: .1em;
|
||||
padding: .8em;
|
||||
margin-bottom: -.8em; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content header a.shepherd-cancel-link:hover {
|
||||
opacity: 1; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content .shepherd-text {
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content .shepherd-text p {
|
||||
margin: 0 0 .5em 0;
|
||||
line-height: 1.3em; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content .shepherd-text p:last-child {
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer {
|
||||
padding: 0 1em 1em; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons {
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li .shepherd-button {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary {
|
||||
background: #eee;
|
||||
color: #888; }
|
||||
.shepherd-element.shepherd-theme-dark .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button {
|
||||
margin-right: 0; }
|
||||
|
||||
.shepherd-start-tour-button.shepherd-theme-dark {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
||||
1
wp-content/plugins/updraftplus/css/tether-shepherd/shepherd-theme-dark.min.css
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top.shepherd-target-attached-right .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-target-attached-right .shepherd-content:before {
|
||||
display: none; }
|
||||
|
||||
.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before {
|
||||
box-sizing: border-box; }
|
||||
|
||||
.shepherd-element {
|
||||
position: absolute;
|
||||
display: none; }
|
||||
.shepherd-element.shepherd-open {
|
||||
display: block; }
|
||||
|
||||
.shepherd-element.shepherd-theme-default {
|
||||
max-width: 100%;
|
||||
max-height: 100%; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content {
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
background: #f6f6f6;
|
||||
color: #444;
|
||||
padding: 1em;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.5em; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-width: 16px;
|
||||
border-style: solid;
|
||||
pointer-events: none; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-left-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-right-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-left.shepherd-target-attached-center .shepherd-content {
|
||||
left: -32px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-right.shepherd-target-attached-center .shepherd-content {
|
||||
left: 32px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
top: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
top: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #f6f6f6; }
|
||||
|
||||
.shepherd-element.shepherd-theme-default {
|
||||
z-index: 9999;
|
||||
max-width: 24em;
|
||||
font-size: 1em; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-default.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before {
|
||||
border-bottom-color: #e6e6e6; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-has-title .shepherd-content header {
|
||||
background: #e6e6e6;
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-has-title .shepherd-content header a.shepherd-cancel-link {
|
||||
padding: 0;
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-default.shepherd-has-cancel-link .shepherd-content header h3 {
|
||||
float: left; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content {
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17);
|
||||
padding: 0; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content * {
|
||||
font-size: inherit; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content header {
|
||||
*zoom: 1;
|
||||
border-radius: 5px 5px 0 0; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content header:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content header h3 {
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
font-weight: normal; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content header a.shepherd-cancel-link {
|
||||
float: right;
|
||||
text-decoration: none;
|
||||
font-size: 1.25em;
|
||||
line-height: .8em;
|
||||
font-weight: normal;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
opacity: 0.25;
|
||||
position: relative;
|
||||
top: .1em;
|
||||
padding: .8em;
|
||||
margin-bottom: -.8em; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content header a.shepherd-cancel-link:hover {
|
||||
opacity: 1; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content .shepherd-text {
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content .shepherd-text p {
|
||||
margin: 0 0 .5em 0;
|
||||
line-height: 1.3em; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content .shepherd-text p:last-child {
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer {
|
||||
padding: 0 1em 1em; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons {
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li .shepherd-button {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary {
|
||||
background: #eee;
|
||||
color: #888; }
|
||||
.shepherd-element.shepherd-theme-default .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button {
|
||||
margin-right: 0; }
|
||||
|
||||
.shepherd-start-tour-button.shepherd-theme-default {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
||||
1
wp-content/plugins/updraftplus/css/tether-shepherd/shepherd-theme-default.min.css
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top.shepherd-target-attached-right .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-target-attached-right .shepherd-content:before {
|
||||
display: none; }
|
||||
|
||||
.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before {
|
||||
box-sizing: border-box; }
|
||||
|
||||
.shepherd-element {
|
||||
position: absolute;
|
||||
display: none; }
|
||||
.shepherd-element.shepherd-open {
|
||||
display: block; }
|
||||
|
||||
.shepherd-element.shepherd-theme-square-dark {
|
||||
max-width: 100%;
|
||||
max-height: 100%; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content {
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
background: #232323;
|
||||
color: #eee;
|
||||
padding: 1em;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.5em; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-width: 16px;
|
||||
border-style: solid;
|
||||
pointer-events: none; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-left-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-right-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-left.shepherd-target-attached-center .shepherd-content {
|
||||
left: -32px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-right.shepherd-target-attached-center .shepherd-content {
|
||||
left: 32px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
top: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
top: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #232323; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #232323; }
|
||||
|
||||
.shepherd-element.shepherd-theme-square-dark {
|
||||
border-radius: 0;
|
||||
z-index: 9999;
|
||||
max-width: 24em;
|
||||
font-size: 1em; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-square-dark.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before {
|
||||
border-bottom-color: #303030; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-has-title .shepherd-content header {
|
||||
background: #303030;
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-has-title .shepherd-content header a.shepherd-cancel-link {
|
||||
padding: 0;
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-square-dark.shepherd-has-cancel-link .shepherd-content header h3 {
|
||||
float: left; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content {
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17);
|
||||
border-radius: 0;
|
||||
padding: 0; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content * {
|
||||
font-size: inherit; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content header {
|
||||
*zoom: 1;
|
||||
border-radius: 0; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content header:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content header h3 {
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
font-weight: normal; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content header a.shepherd-cancel-link {
|
||||
float: right;
|
||||
text-decoration: none;
|
||||
font-size: 1.25em;
|
||||
line-height: .8em;
|
||||
font-weight: normal;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
opacity: 0.25;
|
||||
position: relative;
|
||||
top: .1em;
|
||||
padding: .8em;
|
||||
margin-bottom: -.8em; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content header a.shepherd-cancel-link:hover {
|
||||
opacity: 1; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content .shepherd-text {
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content .shepherd-text p {
|
||||
margin: 0 0 .5em 0;
|
||||
line-height: 1.3em; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content .shepherd-text p:last-child {
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content footer {
|
||||
padding: 0 1em 1em; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content footer .shepherd-buttons {
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content footer .shepherd-buttons li {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content footer .shepherd-buttons li .shepherd-button {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary {
|
||||
background: #eee;
|
||||
color: #888; }
|
||||
.shepherd-element.shepherd-theme-square-dark .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button {
|
||||
margin-right: 0; }
|
||||
|
||||
.shepherd-start-tour-button.shepherd-theme-square-dark {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
||||
1
wp-content/plugins/updraftplus/css/tether-shepherd/shepherd-theme-square-dark.min.css
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top.shepherd-target-attached-right .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-target-attached-right .shepherd-content:before {
|
||||
display: none; }
|
||||
|
||||
.shepherd-element, .shepherd-element:after, .shepherd-element:before, .shepherd-element *, .shepherd-element *:after, .shepherd-element *:before {
|
||||
box-sizing: border-box; }
|
||||
|
||||
.shepherd-element {
|
||||
position: absolute;
|
||||
display: none; }
|
||||
.shepherd-element.shepherd-open {
|
||||
display: block; }
|
||||
|
||||
.shepherd-element.shepherd-theme-square {
|
||||
max-width: 100%;
|
||||
max-height: 100%; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content {
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
background: #f6f6f6;
|
||||
color: #444;
|
||||
padding: 1em;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.5em; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-width: 16px;
|
||||
border-style: solid;
|
||||
pointer-events: none; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-center .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-center .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-right.shepherd-element-attached-middle .shepherd-content:before {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-left-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-left.shepherd-element-attached-middle .shepherd-content:before {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
margin-top: -16px;
|
||||
border-right-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-left.shepherd-target-attached-center .shepherd-content {
|
||||
left: -32px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-right.shepherd-target-attached-center .shepherd-content {
|
||||
left: 32px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-middle .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
left: 16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content {
|
||||
margin-top: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom .shepherd-content:before {
|
||||
bottom: 100%;
|
||||
right: 16px;
|
||||
border-bottom-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
left: 16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content {
|
||||
margin-bottom: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top .shepherd-content:before {
|
||||
top: 100%;
|
||||
right: 16px;
|
||||
border-top-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
top: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
top: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content {
|
||||
margin-right: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-left .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
left: 100%;
|
||||
border-left-color: #f6f6f6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content {
|
||||
margin-left: 16px; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
bottom: 16px;
|
||||
right: 100%;
|
||||
border-right-color: #f6f6f6; }
|
||||
|
||||
.shepherd-element.shepherd-theme-square {
|
||||
border-radius: 0;
|
||||
z-index: 9999;
|
||||
max-width: 24em;
|
||||
font-size: 1em; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before, .shepherd-element.shepherd-theme-square.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before {
|
||||
border-bottom-color: #e6e6e6; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-has-title .shepherd-content header {
|
||||
background: #e6e6e6;
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-has-title .shepherd-content header a.shepherd-cancel-link {
|
||||
padding: 0;
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-square.shepherd-has-cancel-link .shepherd-content header h3 {
|
||||
float: left; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content {
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.17);
|
||||
border-radius: 0;
|
||||
padding: 0; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content * {
|
||||
font-size: inherit; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content header {
|
||||
*zoom: 1;
|
||||
border-radius: 0; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content header:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content header h3 {
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
font-weight: normal; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content header a.shepherd-cancel-link {
|
||||
float: right;
|
||||
text-decoration: none;
|
||||
font-size: 1.25em;
|
||||
line-height: .8em;
|
||||
font-weight: normal;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
opacity: 0.25;
|
||||
position: relative;
|
||||
top: .1em;
|
||||
padding: .8em;
|
||||
margin-bottom: -.8em; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content header a.shepherd-cancel-link:hover {
|
||||
opacity: 1; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content .shepherd-text {
|
||||
padding: 1em; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content .shepherd-text p {
|
||||
margin: 0 0 .5em 0;
|
||||
line-height: 1.3em; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content .shepherd-text p:last-child {
|
||||
margin-bottom: 0; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content footer {
|
||||
padding: 0 1em 1em; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content footer .shepherd-buttons {
|
||||
text-align: right;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content footer .shepherd-buttons li {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
margin: 0; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content footer .shepherd-buttons li .shepherd-button {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content footer .shepherd-buttons li .shepherd-button.shepherd-button-secondary {
|
||||
background: #eee;
|
||||
color: #888; }
|
||||
.shepherd-element.shepherd-theme-square .shepherd-content footer .shepherd-buttons li:last-child .shepherd-button {
|
||||
margin-right: 0; }
|
||||
|
||||
.shepherd-start-tour-button.shepherd-theme-square {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0 .5em 0 0;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
font-size: .8em;
|
||||
line-height: 1em;
|
||||
padding: .75em 2em;
|
||||
background: #3288e6;
|
||||
color: #fff; }
|
||||
1
wp-content/plugins/updraftplus/css/tether-shepherd/shepherd-theme-square.min.css
vendored
Normal file
2
wp-content/plugins/updraftplus/css/updraftplus-admin-2-25-9.min.css
vendored
Normal file
4344
wp-content/plugins/updraftplus/css/updraftplus-admin.css
Normal file
2
wp-content/plugins/updraftplus/css/updraftplus-notices-2-25-9.min.css
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.updraft_notice_container{height:auto;overflow:hidden}.updraft_review_notice_container{padding:12px;display:-webkit-box;display:-ms-flexbox;display:flex}.updraft_advert_button_container{margin-bottom:10px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.updraft_advert_button_container .dashicons{margin-left:10px}.updraft_advert_content_left{float:none;width:80px;padding-top:9px;margin-right:9px}.updraft_advert_content_left_extra{float:none;width:100px;padding-right:15px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.updraft_advert_content_left img{min-height:72px;min-width:72px}.updraft_advert_content_right{float:none;width:auto;overflow:hidden;font-size:16px}.updraft_advert_content_right p{font-size:16px !important}.updraft_advert_bottom{margin:10px 0;padding:10px;font-size:140%;background-color:white;border-color:#e6db55;border:1px solid;border-radius:4px}.updraft-advert-dismiss{float:right;font-size:13px;font-weight:normal}h3.updraft_advert_heading{margin-top:5px !important;margin-bottom:5px !important}h4.updraft_advert_heading{margin-top:2px !important;margin-bottom:3px !important}.updraft_center_content{text-align:center;margin-bottom:5px}.updraft_notice_link{padding-left:5px}.updraft_text_center{text-align:center}@media screen and (min-width:560px){.updraft_advert_content_left,.updraft_advert_content_left_extra{float:left}}
|
||||
/*# sourceMappingURL=updraftplus-notices-2-25-9.min.css.map */
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["css/updraftplus-notices.css"],"names":[],"mappings":"AAAA,oBAAoB;;AAEpB;CACC,YAAY;CACZ,gBAAgB;AACjB;;AAEA;CACC,aAAa;CACb,oBAAa;CAAb,oBAAa;CAAb,aAAa;AACd;;AAEA;CACC,mBAAmB;CACnB,oBAAa;CAAb,oBAAa;CAAb,aAAa;CACb,yBAAmB;KAAnB,sBAAmB;SAAnB,mBAAmB;AACpB;;AAEA;CACC,iBAAiB;AAClB;;AAEA;CACC,WAAW;CACX,WAAW;CACX,gBAAgB;CAChB,iBAAiB;AAClB;;AAEA;CACC,WAAW;CACX,YAAY;CACZ,mBAAmB;CACnB,oBAAa;CAAb,oBAAa;CAAb,aAAa;CACb,yBAAmB;KAAnB,sBAAmB;SAAnB,mBAAmB;AACpB;;AAEA;CACC,gBAAgB;CAChB,eAAe;AAChB;;AAEA;CACC,WAAW;CACX,WAAW;CACX,gBAAgB;CAChB,eAAe;AAChB;;AAEA;CACC,yBAAyB;AAC1B;;AAEA;CACC,cAAc;CACd,aAAa;CACb,eAAe;CACf,uBAAuB;CACvB,qBAAqB;CACrB,iBAAiB;CACjB,kBAAkB;AACnB;;AAEA;CACC,YAAY;CACZ,eAAe;CACf,mBAAmB;AACpB;;AAEA;CACC,0BAA0B;CAC1B,6BAA6B;AAC9B;;AAEA;CACC,0BAA0B;CAC1B,6BAA6B;AAC9B;;AAEA;CACC,kBAAkB;CAClB,kBAAkB;AACnB;;AAEA;CACC,iBAAiB;AAClB;;AAEA;CACC,kBAAkB;AACnB;;AAEA;;CAEC;EACC,WAAW;CACZ;;AAED","file":"updraftplus-notices-2-25-9.min.css","sourcesContent":["/* CSS for adverts */\n\n.updraft_notice_container {\n\theight: auto;\n\toverflow: hidden;\n}\n\n.updraft_review_notice_container {\n\tpadding: 12px;\n\tdisplay: flex;\n}\n\n.updraft_advert_button_container {\n\tmargin-bottom: 10px;\n\tdisplay: flex;\n\talign-items: center;\n}\n\n.updraft_advert_button_container .dashicons {\n\tmargin-left: 10px;\n}\n\n.updraft_advert_content_left {\n\tfloat: none;\n\twidth: 80px;\n\tpadding-top: 9px;\n\tmargin-right: 9px;\n}\n\n.updraft_advert_content_left_extra {\n\tfloat: none;\n\twidth: 100px;\n\tpadding-right: 15px;\n\tdisplay: flex;\n\talign-items: center;\n}\n\n.updraft_advert_content_left img {\n\tmin-height: 72px;\n\tmin-width: 72px;\n}\n\n.updraft_advert_content_right {\n\tfloat: none;\n\twidth: auto;\n\toverflow: hidden;\n\tfont-size: 16px;\n}\n\n.updraft_advert_content_right p {\n\tfont-size: 16px!important;\n}\n\n.updraft_advert_bottom {\n\tmargin: 10px 0;\n\tpadding: 10px;\n\tfont-size: 140%;\n\tbackground-color: white;\n\tborder-color: #E6DB55;\n\tborder: 1px solid;\n\tborder-radius: 4px;\n}\n\n.updraft-advert-dismiss {\n\tfloat: right;\n\tfont-size: 13px;\n\tfont-weight: normal;\n}\n\nh3.updraft_advert_heading {\n\tmargin-top: 5px !important;\n\tmargin-bottom: 5px !important;\n}\n\nh4.updraft_advert_heading {\n\tmargin-top: 2px !important;\n\tmargin-bottom: 3px !important;\n}\n\n.updraft_center_content {\n\ttext-align: center;\n\tmargin-bottom: 5px;\n}\n\n.updraft_notice_link {\n\tpadding-left: 5px;\n}\n\n.updraft_text_center {\n\ttext-align: center;\n}\n\n@media screen and (min-width: 560px) {\n\n\t.updraft_advert_content_left, .updraft_advert_content_left_extra {\n\t\tfloat: left;\n\t}\n\n}\n"]}
|
||||
109
wp-content/plugins/updraftplus/css/updraftplus-notices.css
Normal file
@@ -0,0 +1,109 @@
|
||||
/* CSS for adverts */
|
||||
|
||||
.updraft_notice_container {
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.updraft_review_notice_container {
|
||||
padding: 12px;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.updraft_advert_button_container {
|
||||
margin-bottom: 10px;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.updraft_advert_button_container .dashicons {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.updraft_advert_content_left {
|
||||
float: none;
|
||||
width: 80px;
|
||||
padding-top: 9px;
|
||||
margin-right: 9px;
|
||||
}
|
||||
|
||||
.updraft_advert_content_left_extra {
|
||||
float: none;
|
||||
width: 100px;
|
||||
padding-right: 15px;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.updraft_advert_content_left img {
|
||||
min-height: 72px;
|
||||
min-width: 72px;
|
||||
}
|
||||
|
||||
.updraft_advert_content_right {
|
||||
float: none;
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.updraft_advert_content_right p {
|
||||
font-size: 16px!important;
|
||||
}
|
||||
|
||||
.updraft_advert_bottom {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
font-size: 140%;
|
||||
background-color: white;
|
||||
border-color: #E6DB55;
|
||||
border: 1px solid;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.updraft-advert-dismiss {
|
||||
float: right;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h3.updraft_advert_heading {
|
||||
margin-top: 5px !important;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
|
||||
h4.updraft_advert_heading {
|
||||
margin-top: 2px !important;
|
||||
margin-bottom: 3px !important;
|
||||
}
|
||||
|
||||
.updraft_center_content {
|
||||
text-align: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.updraft_notice_link {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.updraft_text_center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 560px) {
|
||||
|
||||
.updraft_advert_content_left, .updraft_advert_content_left_extra {
|
||||
float: left;
|
||||
}
|
||||
|
||||
}
|
||||
2
wp-content/plugins/updraftplus/css/updraftplus-tour-2-25-9.min.css
vendored
Normal file
202
wp-content/plugins/updraftplus/css/updraftplus-tour.css
Normal file
@@ -0,0 +1,202 @@
|
||||
.shepherd-theme-arrows-plain-buttons {
|
||||
z-index: 9999;
|
||||
max-width: 390px!important;
|
||||
}
|
||||
|
||||
.shepherd-theme-arrows-plain-buttons.super-index {
|
||||
z-index: 999999;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content {
|
||||
border-radius: 3px;
|
||||
-webkit-filter: none;
|
||||
filter: none;
|
||||
-webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15), 0px 10px 40px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15), 0px 10px 40px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top.shepherd-target-attached-right .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-target-attached-right .shepherd-content:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before,
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before,
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before {
|
||||
border-bottom-color: #DD6823;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header {
|
||||
background-color: #DD6823;
|
||||
border-radius: 3px 3px 0 0;
|
||||
padding-right: 90px;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header h3 {
|
||||
color: #FFF;
|
||||
font-size: 1.2em;
|
||||
float: none;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link {
|
||||
opacity: 0.7;
|
||||
color: rgba(255, 255, 255, 0);
|
||||
font-size: 0.8em;
|
||||
border: 1px solid #FFF;
|
||||
border-radius: 50%;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
line-height: 20px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
float: none;
|
||||
position: absolute;
|
||||
right: 11px;
|
||||
top: 12px
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link::before {
|
||||
color: #FFF;
|
||||
content: attr(data-btntext);
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link::after {
|
||||
content: "\f335";
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: dashicons;
|
||||
color: #FFF;
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
line-height: 21px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link:hover,
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link:focus {
|
||||
border: 1px solid #A04E00;
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link:hover::before, .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link:focus::before {
|
||||
color: #A04E00;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link:hover::after, .shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link:focus::after {
|
||||
color: #A04E00;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
top: 44px;
|
||||
}
|
||||
|
||||
.shepherd-content .ud-notice {
|
||||
|
||||
background: #F0F0F0;
|
||||
padding: 14px;
|
||||
border-radius: 4px;
|
||||
font-size: 90% !important;
|
||||
line-height: 1.5;
|
||||
|
||||
}
|
||||
|
||||
.shepherd-content .ud-notice h3 {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content .shepherd-text p {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 1.3em;
|
||||
}
|
||||
|
||||
.ud-notice span.ud-special-offer {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
padding: 1px 6px;
|
||||
border-radius: 3px;
|
||||
background: rgba(217, 105, 0, 0.09);
|
||||
}
|
||||
|
||||
label[for=updraft_servicecheckbox_updraftvault] {
|
||||
|
||||
border: 1px solid rgba(204, 204, 204, 0.4);
|
||||
-webkit-transition: border .5s;
|
||||
transition: border .5s
|
||||
|
||||
}
|
||||
|
||||
label[for=updraft_servicecheckbox_updraftvault].emphasize {
|
||||
border-color: #DD6823;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li .shepherd-button.udp-tour-back,
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li .shepherd-button.udp-tour-end {
|
||||
float: left;
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li .shepherd-button.udp-tour-end {
|
||||
padding-left: 0;
|
||||
color: #B7B7B7;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li .shepherd-button.udp-tour-back::before {
|
||||
content: ' ';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
display: block;
|
||||
border-left: 1px solid;
|
||||
border-bottom: 1px solid;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 8px;
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
a.shepherd-button.udp-tour-end::before {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
content: "\f335";
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: dashicons;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
vertical-align: middle;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.updraftplus-welcome-logo {
|
||||
display: block;
|
||||
width: 70px;
|
||||
float: left;
|
||||
margin-top: -11px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.updraftplus-welcome-logo img {
|
||||
display: block;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.highlight-udp .plugins #the-list tr:not([data-slug="updraftplus"]) {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
@media(max-width: 790px) {
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
195
wp-content/plugins/updraftplus/css/updraftplus-tour.scss
Normal file
@@ -0,0 +1,195 @@
|
||||
$udp_primary: #DD6823;
|
||||
$wp_blue: #0073AA;
|
||||
.shepherd-theme-arrows-plain-buttons {
|
||||
z-index: 9999;
|
||||
max-width: 390px!important;
|
||||
}
|
||||
|
||||
.shepherd-theme-arrows-plain-buttons.super-index {
|
||||
z-index: 999999;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content {
|
||||
border-radius: 3px;
|
||||
filter: none;
|
||||
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15), 0px 10px 40px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-right.shepherd-target-attached-top.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-bottom.shepherd-element-attached-left.shepherd-target-attached-top.shepherd-target-attached-right .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-target-attached-left .shepherd-content:before,
|
||||
.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-target-attached-right .shepherd-content:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-center.shepherd-has-title .shepherd-content:before,
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-right.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before,
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-bottom.shepherd-has-title .shepherd-content:before {
|
||||
border-bottom-color: $udp_primary;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header {
|
||||
background-color: $udp_primary;
|
||||
border-radius: 3px 3px 0 0;
|
||||
padding-right: 90px;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content header h3 {
|
||||
color: #FFF;
|
||||
font-size: 1.2em;
|
||||
float: none;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link {
|
||||
opacity: 0.7;
|
||||
color: rgba(255, 255, 255, 0);
|
||||
font-size: 0.8em;
|
||||
border: 1px solid #FFF;
|
||||
border-radius: 50%;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
line-height: 20px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
float: none;
|
||||
position: absolute;
|
||||
right: 11px;
|
||||
top: 12px;
|
||||
|
||||
&::before {
|
||||
color: #FFF;
|
||||
content: attr(data-btntext);
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
&::after {
|
||||
content: "\f335";
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: dashicons;
|
||||
color: #FFF;
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
line-height: 21px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link:hover,
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-has-title .shepherd-content header a.shepherd-cancel-link:focus {
|
||||
border: 1px solid #A04E00;
|
||||
opacity: 1;
|
||||
&::before {
|
||||
color: #A04E00;
|
||||
}
|
||||
&::after {
|
||||
color: #A04E00;
|
||||
}
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons.shepherd-element-attached-top.shepherd-element-attached-left.shepherd-target-attached-right .shepherd-content:before {
|
||||
top: 44px;
|
||||
}
|
||||
|
||||
.shepherd-content .ud-notice {
|
||||
|
||||
background: #F0F0F0;
|
||||
padding: 14px;
|
||||
border-radius: 4px;
|
||||
font-size: 90% !important;
|
||||
line-height: 1.5;
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content .shepherd-text p {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 1.3em;
|
||||
}
|
||||
|
||||
.ud-notice span.ud-special-offer {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
padding: 1px 6px;
|
||||
border-radius: 3px;
|
||||
background: rgba(217, 105, 0, 0.09);
|
||||
}
|
||||
|
||||
label[for=updraft_servicecheckbox_updraftvault] {
|
||||
|
||||
border: 1px solid rgba(204, 204, 204, 0.4);
|
||||
transition: border .5s;
|
||||
&.emphasize {
|
||||
border-color: $udp_primary;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li .shepherd-button.udp-tour-back,
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li .shepherd-button.udp-tour-end {
|
||||
float: left;
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li .shepherd-button.udp-tour-end {
|
||||
padding-left: 0;
|
||||
color: #B7B7B7;
|
||||
}
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons .shepherd-content footer .shepherd-buttons li .shepherd-button.udp-tour-back::before {
|
||||
content: ' ';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
display: block;
|
||||
border-left: 1px solid;
|
||||
border-bottom: 1px solid;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 8px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
a.shepherd-button.udp-tour-end::before {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
content: "\f335";
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: dashicons;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
vertical-align: middle;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.updraftplus-welcome-logo {
|
||||
display: block;
|
||||
width: 70px;
|
||||
float: left;
|
||||
margin-top: -11px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.updraftplus-welcome-logo img {
|
||||
display: block;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.highlight-udp .plugins #the-list tr:not([data-slug="updraftplus"]) {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
@media(max-width: 790px) {
|
||||
|
||||
.shepherd-element.shepherd-theme-arrows-plain-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
43
wp-content/plugins/updraftplus/example-decrypt.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
/*
|
||||
To dump the decrypted file using the given key on stdout, call:
|
||||
|
||||
rijndael_decrypt_file('../path/to/file.crypt' , 'mykey');
|
||||
|
||||
Thus, here are the easy instructions:
|
||||
|
||||
1) Add a line like the above into this PHP file (not inside these comments, but outside)
|
||||
e.g.
|
||||
rijndael_decrypt_file('/home/myself/myfile.crypt' , 'MYKEY');
|
||||
|
||||
2) Run this file (and make sure that includes/Rijndael.php is available, if you are moving this file around)
|
||||
e.g.
|
||||
php /home/myself/example-decrypt.php >output.sql.gz
|
||||
|
||||
3) You may then want to gunzip the resulting file to have a standard SQL file.
|
||||
e.g.
|
||||
gunzip output.sql.gz
|
||||
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
/**
|
||||
* An example of how to decrypt a file
|
||||
*
|
||||
* @param String $file Full path to file to decrypt
|
||||
* @param String $key Key or salting to be used
|
||||
*/
|
||||
function rijndael_decrypt_file($file, $key) {
|
||||
|
||||
include_once(dirname(__FILE__).'/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php');
|
||||
|
||||
$rijndael = new Crypt_Rijndael();
|
||||
|
||||
$rijndael->setKey($key);
|
||||
|
||||
$ciphertext = file_get_contents($file);
|
||||
|
||||
print $rijndael->decrypt($ciphertext);// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- intentional binary output
|
||||
|
||||
}
|
||||
BIN
wp-content/plugins/updraftplus/images/addons-images/all.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 13 KiB |
BIN
wp-content/plugins/updraftplus/images/addons-images/azure.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 11 KiB |
BIN
wp-content/plugins/updraftplus/images/addons-images/fixtime.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
BIN
wp-content/plugins/updraftplus/images/addons-images/importer.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 8.1 KiB |