diff --git a/logs/UpdateProcessor.log b/logs/UpdateProcessor.log new file mode 100644 index 00000000..ef00a335 --- /dev/null +++ b/logs/UpdateProcessor.log @@ -0,0 +1,39 @@ +{"timestamp":"2025-04-30T06:17:25.965Z","level":"info","name":"UpdateProcessor","message":"Successfully read update report","details":{}} +{"timestamp":"2025-04-30T06:17:25.967Z","level":"info","name":"UpdateProcessor","message":"Created necessary directories","details":{}} +{"timestamp":"2025-04-30T06:17:25.968Z","level":"info","name":"UpdateProcessor","message":"Processing plugin update: bbpress","details":{}} +{"timestamp":"2025-04-30T06:17:27.092Z","level":"info","name":"UpdateProcessor","message":"Downloaded plugin: bbpress","details":{}} +{"timestamp":"2025-04-30T06:17:27.181Z","level":"info","name":"UpdateProcessor","message":"Extracted plugin: bbpress to bbpress","details":{}} +{"timestamp":"2025-04-30T06:17:27.181Z","level":"info","name":"UpdateProcessor","message":"Processing plugin update: breeze","details":{}} +{"timestamp":"2025-04-30T06:17:27.789Z","level":"info","name":"UpdateProcessor","message":"Downloaded plugin: breeze","details":{}} +{"timestamp":"2025-04-30T06:17:27.884Z","level":"info","name":"UpdateProcessor","message":"Extracted plugin: breeze to breeze","details":{}} +{"timestamp":"2025-04-30T06:17:27.885Z","level":"info","name":"UpdateProcessor","message":"Processing plugin update: contact-form-7","details":{}} +{"timestamp":"2025-04-30T06:17:28.285Z","level":"info","name":"UpdateProcessor","message":"Downloaded plugin: contact-form-7","details":{}} +{"timestamp":"2025-04-30T06:17:28.319Z","level":"info","name":"UpdateProcessor","message":"Extracted plugin: contact-form-7 to contact-form-7","details":{}} +{"timestamp":"2025-04-30T06:17:28.319Z","level":"info","name":"UpdateProcessor","message":"Processing plugin update: hcaptcha-for-forms-and-more","details":{}} +{"timestamp":"2025-04-30T06:17:30.327Z","level":"info","name":"UpdateProcessor","message":"Downloaded plugin: hcaptcha-for-forms-and-more","details":{}} +{"timestamp":"2025-04-30T06:17:30.487Z","level":"info","name":"UpdateProcessor","message":"Extracted plugin: hcaptcha-for-forms-and-more to hcaptcha-for-forms-and-more","details":{}} +{"timestamp":"2025-04-30T06:17:30.488Z","level":"info","name":"UpdateProcessor","message":"Processing plugin update: wp-maintenance-mode","details":{}} +{"timestamp":"2025-04-30T06:17:31.160Z","level":"info","name":"UpdateProcessor","message":"Downloaded plugin: wp-maintenance-mode","details":{}} +{"timestamp":"2025-04-30T06:17:31.280Z","level":"info","name":"UpdateProcessor","message":"Extracted plugin: wp-maintenance-mode to wp-maintenance-mode","details":{}} +{"timestamp":"2025-04-30T06:17:31.282Z","level":"info","name":"UpdateProcessor","message":"Processing plugin update: otter-blocks","details":{}} +{"timestamp":"2025-04-30T06:17:31.808Z","level":"info","name":"UpdateProcessor","message":"Downloaded plugin: otter-blocks","details":{}} +{"timestamp":"2025-04-30T06:17:32.148Z","level":"info","name":"UpdateProcessor","message":"Extracted plugin: otter-blocks to otter-blocks","details":{}} +{"timestamp":"2025-04-30T06:17:32.150Z","level":"info","name":"UpdateProcessor","message":"Processing plugin update: updraftplus","details":{}} +{"timestamp":"2025-04-30T06:17:32.910Z","level":"info","name":"UpdateProcessor","message":"Downloaded plugin: updraftplus","details":{}} +{"timestamp":"2025-04-30T06:17:33.313Z","level":"info","name":"UpdateProcessor","message":"Extracted plugin: updraftplus to updraftplus","details":{}} +{"timestamp":"2025-04-30T06:17:33.315Z","level":"info","name":"UpdateProcessor","message":"Processing plugin update: wordfence","details":{}} +{"timestamp":"2025-04-30T06:17:33.853Z","level":"info","name":"UpdateProcessor","message":"Downloaded plugin: wordfence","details":{}} +{"timestamp":"2025-04-30T06:17:34.084Z","level":"info","name":"UpdateProcessor","message":"Extracted plugin: wordfence to wordfence","details":{}} +{"timestamp":"2025-04-30T06:17:34.086Z","level":"info","name":"UpdateProcessor","message":"Processing plugin update: wp-security-audit-log","details":{}} +{"timestamp":"2025-04-30T06:17:34.601Z","level":"info","name":"UpdateProcessor","message":"Downloaded plugin: wp-security-audit-log","details":{}} +{"timestamp":"2025-04-30T06:17:34.742Z","level":"info","name":"UpdateProcessor","message":"Extracted plugin: wp-security-audit-log to wp-security-audit-log","details":{}} +{"timestamp":"2025-04-30T06:17:34.743Z","level":"info","name":"UpdateProcessor","message":"Processing plugin update: wpforms-lite","details":{}} +{"timestamp":"2025-04-30T06:17:35.342Z","level":"info","name":"UpdateProcessor","message":"Downloaded plugin: wpforms-lite","details":{}} +{"timestamp":"2025-04-30T06:17:36.290Z","level":"info","name":"UpdateProcessor","message":"Extracted plugin: wpforms-lite to wpforms-lite","details":{}} +{"timestamp":"2025-04-30T06:17:36.293Z","level":"info","name":"UpdateProcessor","message":"Processing plugin update: wordpress-seo","details":{}} +{"timestamp":"2025-04-30T06:17:36.780Z","level":"info","name":"UpdateProcessor","message":"Downloaded plugin: wordpress-seo","details":{}} +{"timestamp":"2025-04-30T06:17:37.105Z","level":"info","name":"UpdateProcessor","message":"Extracted plugin: wordpress-seo to wordpress-seo","details":{}} +{"timestamp":"2025-04-30T06:17:37.107Z","level":"info","name":"UpdateProcessor","message":"Processing theme update: twentytwentyfive","details":{}} +{"timestamp":"2025-04-30T06:17:38.636Z","level":"info","name":"UpdateProcessor","message":"Downloaded theme: twentytwentyfive","details":{}} +{"timestamp":"2025-04-30T06:17:38.711Z","level":"info","name":"UpdateProcessor","message":"Extracted theme: twentytwentyfive to twentytwentyfive","details":{}} +{"timestamp":"2025-04-30T06:17:38.713Z","level":"info","name":"UpdateProcessor","message":"Update processing completed","details":{}} diff --git a/processor_output.txt b/processor_output.txt index 241c8976..1fc81a7f 100644 --- a/processor_output.txt +++ b/processor_output.txt @@ -22,12 +22,18 @@ [INFO] Processing plugin update: otter-blocks [INFO] Downloaded plugin: otter-blocks [INFO] Extracted plugin: otter-blocks to otter-blocks +[INFO] Processing plugin update: updraftplus +[INFO] Downloaded plugin: updraftplus +[INFO] Extracted plugin: updraftplus to updraftplus [INFO] Processing plugin update: wordfence [INFO] Downloaded plugin: wordfence [INFO] Extracted plugin: wordfence to wordfence [INFO] Processing plugin update: wp-security-audit-log [INFO] Downloaded plugin: wp-security-audit-log [INFO] Extracted plugin: wp-security-audit-log to wp-security-audit-log +[INFO] Processing plugin update: wpforms-lite +[INFO] Downloaded plugin: wpforms-lite +[INFO] Extracted plugin: wpforms-lite to wpforms-lite [INFO] Processing plugin update: wordpress-seo [INFO] Downloaded plugin: wordpress-seo [INFO] Extracted plugin: wordpress-seo to wordpress-seo diff --git a/update_results.json b/update_results.json index 9764d096..d33b449b 100644 --- a/update_results.json +++ b/update_results.json @@ -22,7 +22,7 @@ "type": "plugin", "slug": "hcaptcha-for-forms-and-more", "from": "4.11.0", - "to": "4.12.0" + "to": "4.13.0" }, { "type": "plugin", @@ -36,6 +36,12 @@ "from": "3.0.10", "to": "3.0.11" }, + { + "type": "plugin", + "slug": "updraftplus", + "from": "2.23.3.26", + "to": "1.25.5" + }, { "type": "plugin", "slug": "wordfence", @@ -48,11 +54,17 @@ "from": "5.3.3", "to": "5.3.4.1" }, + { + "type": "plugin", + "slug": "wpforms-lite", + "from": "1.9.4.2", + "to": "1.9.5.1" + }, { "type": "plugin", "slug": "wordpress-seo", "from": "24.6", - "to": "24.9" + "to": "25.0" }, { "type": "theme", @@ -95,27 +107,22 @@ }, { "type": "plugin", - "slug": "limit-login-attempts-reloaded", - "reason": "No update available" - }, - { - "type": "plugin", - "slug": "spinupwp", + "slug": "google-analytics-for-wordpress", "reason": "No update available" }, { "type": "plugin", - "slug": "stop-emails", + "slug": "limit-login-attempts-reloaded", "reason": "No update available" }, { "type": "plugin", - "slug": "updraftplus", + "slug": "spinupwp", "reason": "No update available" }, { "type": "plugin", - "slug": "wpforms-lite", + "slug": "stop-emails", "reason": "No update available" }, { diff --git a/wp-content/plugins/bbpress/bbpress.php b/wp-content/plugins/bbpress/bbpress.php index f380e350..9c14bfb4 100644 --- a/wp-content/plugins/bbpress/bbpress.php +++ b/wp-content/plugins/bbpress/bbpress.php @@ -5,7 +5,7 @@ * * bbPress is forum software with a twist from the creators of WordPress. * - * $Id: bbpress.php 7273 2024-06-29 16:56:00Z johnjamesjacoby $ + * $Id: bbpress.php 7295 2025-04-16 22:53:45Z johnjamesjacoby $ * * @package bbPress * @subpackage Main @@ -23,8 +23,8 @@ * Domain Path: /languages/ * Requires PHP: 5.6.20 * Requires at least: 6.0 - * Tested up to: 6.5 - * Version: 2.6.12 + * Tested up to: 6.9 + * Version: 2.6.13 */ // Exit if accessed directly @@ -207,7 +207,7 @@ private function setup_environment() { /** Versions **********************************************************/ - $this->version = '2.6.12'; + $this->version = '2.6.13'; $this->db_version = '263'; /** Paths *************************************************************/ diff --git a/wp-content/plugins/bbpress/bbpress.pot b/wp-content/plugins/bbpress/bbpress.pot index 485f8364..81696f6b 100644 --- a/wp-content/plugins/bbpress/bbpress.pot +++ b/wp-content/plugins/bbpress/bbpress.pot @@ -2,9 +2,9 @@ # This file is distributed under the GNU General Public License v2 or later. msgid "" msgstr "" -"Project-Id-Version: bbPress 2.6.12\n" +"Project-Id-Version: bbPress 2.6.13\n" "Report-Msgid-Bugs-To: https://bbpress.trac.wordpress.org\n" -"POT-Creation-Date: 2025-02-26 18:24:08+00:00\n" +"POT-Creation-Date: 2025-04-16 23:16:11+00:00\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -2729,7 +2729,7 @@ msgstr "" msgid "All Topics" msgstr "" -#: includes/admin/tools/reset.php:36 includes/replies/functions.php:2154 +#: includes/admin/tools/reset.php:36 includes/replies/functions.php:2161 #: includes/replies/template.php:52 msgid "All Replies" msgstr "" @@ -4641,11 +4641,11 @@ msgstr "" msgid "Error: There was a problem deleting the reply." msgstr "" -#: includes/replies/functions.php:2152 +#: includes/replies/functions.php:2159 msgid "All Posts" msgstr "" -#: includes/replies/functions.php:2198 includes/topics/functions.php:3818 +#: includes/replies/functions.php:2205 includes/topics/functions.php:3818 msgid "Replies: %s" msgstr "" diff --git a/wp-content/plugins/bbpress/includes/replies/functions.php b/wp-content/plugins/bbpress/includes/replies/functions.php index fc3f5fd6..52091d70 100644 --- a/wp-content/plugins/bbpress/includes/replies/functions.php +++ b/wp-content/plugins/bbpress/includes/replies/functions.php @@ -2083,18 +2083,25 @@ function _bbp_has_replies_where( $where = '', $query = false ) { return $where; } - // Bail if no post_parent to replace - if ( ! is_numeric( $query->get( 'post_parent' ) ) ) { + // Bail if including specific post ID's + if ( $query->get( 'post__in' ) ) { return $where; } - // Bail if not a topic and reply query - if ( array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ) !== $query->get( 'post_type' ) ) { + // Get post_parent from query (used to get the topic ID below) + $post_parent = $query->get( 'post_parent' ); + + // Bail if post_parent is default '' (uses is_numeric() because WordPress does internally) + if ( ! is_numeric( $post_parent ) ) { return $where; } - // Bail if including specific post ID's - if ( $query->get( 'post__in' ) ) { + // Get post_type from query, define array to diff against + $queried_types = (array) $query->get( 'post_type' ); + $post_types = array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ); + + // Bail if query is not already for both topic and reply post types + if ( array_diff( $post_types, $queried_types ) ) { return $where; } @@ -2104,7 +2111,7 @@ function _bbp_has_replies_where( $where = '', $query = false ) { $table_name = bbp_db()->prefix . 'posts'; // Get the topic ID from the post_parent, set in bbp_has_replies() - $topic_id = bbp_get_topic_id( $query->get( 'post_parent' ) ); + $topic_id = bbp_get_topic_id( $post_parent ); // The texts to search for $search = array( diff --git a/wp-content/plugins/bbpress/readme.txt b/wp-content/plugins/bbpress/readme.txt index aeae0474..6c5b5666 100644 --- a/wp-content/plugins/bbpress/readme.txt +++ b/wp-content/plugins/bbpress/readme.txt @@ -5,8 +5,8 @@ License: GNU General Public License v2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Requires PHP: 5.6.20 Requires at least: 6.0 -Tested up to: 6.5 -Stable tag: 2.6.12 +Tested up to: 6.9 +Stable tag: 2.6.13 bbPress is forum software for WordPress. diff --git a/wp-content/plugins/breeze/breeze.php b/wp-content/plugins/breeze/breeze.php index ccbfda6b..59cf817f 100644 --- a/wp-content/plugins/breeze/breeze.php +++ b/wp-content/plugins/breeze/breeze.php @@ -2,7 +2,7 @@ /** * Plugin Name: Breeze * Description: Breeze is a WordPress cache plugin with extensive options to speed up your website. All the options including Varnish Cache are compatible with Cloudways hosting. - * Version: 2.2.7 + * Version: 2.2.9 * Text Domain: breeze * Domain Path: /languages * Author: Cloudways @@ -37,7 +37,7 @@ define( 'BREEZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); } if ( ! defined( 'BREEZE_VERSION' ) ) { - define( 'BREEZE_VERSION', '2.2.7' ); + define( 'BREEZE_VERSION', '2.2.9' ); } if ( ! defined( 'BREEZE_SITEURL' ) ) { define( 'BREEZE_SITEURL', get_site_url() ); @@ -115,6 +115,7 @@ // Load Store Local Files class. require_once BREEZE_PLUGIN_DIR . 'inc/class-breeze-store-files-locally.php'; +require_once BREEZE_PLUGIN_DIR . 'inc/class-breeze-bulk-update.php'; // Load Breeze Rollback Functionality. if ( isset( $_GET['page'] ) && 'breeze-rollback' === $_GET['page'] ) { diff --git a/wp-content/plugins/breeze/inc/breeze-admin.php b/wp-content/plugins/breeze/inc/breeze-admin.php index b2da8e45..74240985 100644 --- a/wp-content/plugins/breeze/inc/breeze-admin.php +++ b/wp-content/plugins/breeze/inc/breeze-admin.php @@ -647,6 +647,7 @@ private static function breeze_default_options_value(): array { 'breeze-gzip-compression' => '1', 'breeze-desktop-cache' => '1', 'breeze-mobile-cache' => '1', + 'breeze-b-ttl' => 1440, 'breeze-browser-cache' => '1', 'breeze-lazy-load' => '0', 'breeze-lazy-load-native' => '0', @@ -950,6 +951,19 @@ public static function plugin_active_hook( $network_wide ) { } } + /** + * Unschedules the 'breeze_purge_cache' event if it is scheduled. + * + * Identifies the next scheduled occurrence of the 'breeze_purge_cache' event + * and removes it from the WordPress cron schedule. + * + * @return void + */ + public static function unschedule_events() { + $timestamp = wp_next_scheduled( 'breeze_purge_cache' ); + wp_unschedule_event( $timestamp, 'breeze_purge_cache' ); + } + /* * Register deactivate plugin hook. */ @@ -961,7 +975,9 @@ public static function plugin_deactive_hook() { if ( ! class_exists( 'Breeze_Configuration' ) ) { require_once( BREEZE_PLUGIN_DIR . 'inc/breeze-configuration.php' ); } + Breeze_ConfigCache::factory()->clean_up(); + self::unschedule_events(); //Breeze_ConfigCache::factory()->clean_config(); Breeze_ConfigCache::factory()->toggle_caching( false ); Breeze_Configuration::update_htaccess( true ); @@ -974,6 +990,7 @@ public static function plugin_deactive_hook() { $sites = get_sites(); foreach ( $sites as $site ) { switch_to_blog( $site->blog_id ); + self::unschedule_events(); do_action( 'breeze_clear_varnish' ); restore_current_blog(); } diff --git a/wp-content/plugins/breeze/inc/cache/purge-cache.php b/wp-content/plugins/breeze/inc/cache/purge-cache.php index 566f6d4c..cb760b5b 100644 --- a/wp-content/plugins/breeze/inc/cache/purge-cache.php +++ b/wp-content/plugins/breeze/inc/cache/purge-cache.php @@ -417,8 +417,8 @@ private function clear_local_cache_for_urls( array $list_of_urls ) { * * @return void */ - public function purge_post_on_new_comment( int $comment_ID, int $approved, array $commentdata ) { - if ( empty( $approved ) ) { + public function purge_post_on_new_comment( $comment_ID, $approved, $commentdata ) { + if ( 1 !== $approved ) { return; } // File based caching only diff --git a/wp-content/plugins/breeze/inc/cache/purge-per-time.php b/wp-content/plugins/breeze/inc/cache/purge-per-time.php index ec5767c0..cc181525 100644 --- a/wp-content/plugins/breeze/inc/cache/purge-per-time.php +++ b/wp-content/plugins/breeze/inc/cache/purge-per-time.php @@ -26,16 +26,18 @@ class Breeze_PurgeCacheTime { protected $varnishcache = 0; public function __construct( $settings = null ) { - if ( isset( $settings['breeze-b-ttl'] ) ) { - $this->timettl = $settings['breeze-b-ttl']; - } - - if ( isset( $settings['breeze-active'] ) ) { - $this->normalcache = (int) $settings['breeze-active']; - } - - if ( isset( $settings['breeze-varnish-purge'] ) ) { - $this->varnishcache = (int) $settings['breeze-varnish-purge']; + if ( is_array( $settings ) ) { + if ( array_key_exists( 'breeze-b-ttl', $settings ) && ! is_null( $settings['breeze-b-ttl'] ) ) { + $this->timettl = $settings['breeze-b-ttl']; + } + + if ( isset( $settings['breeze-active'] ) ) { + $this->normalcache = (int) $settings['breeze-active']; + } + + if ( isset( $settings['breeze-varnish-purge'] ) ) { + $this->varnishcache = (int) $settings['breeze-varnish-purge']; + } } add_action( 'breeze_purge_cache', array( $this, 'schedule_varnish' ) ); @@ -64,20 +66,21 @@ public function schedule_events( $time = 0 ) { $timestamp = wp_next_scheduled( 'breeze_purge_cache' ); - if ( $time ) { - #error_log( 'Time: ' . $time ); - wp_schedule_event( $time * 60, 'breeze_varnish_time', 'breeze_purge_cache' ); + // If the timer exists and is set by the user to zero ( 0 ) then remove the cache. + if ( ! is_bool( $this->timettl ) && 0 === (int) $this->timettl ) { + wp_unschedule_event( $timestamp, 'breeze_purge_cache' ); return; } - // Expire cache never - if ( isset( $this->timettl ) && (int) $this->timettl === 0 ) { - wp_unschedule_event( $timestamp, 'breeze_purge_cache' ); + // If the next schedule does not exist, and we have custom value timer. + if ( ! $timestamp && $time ) { + wp_schedule_event( $time * 60, 'breeze_varnish_time', 'breeze_purge_cache' ); return; } + // If the scedule does not exist and we use current time to run the event. if ( ! $timestamp ) { wp_schedule_event( time(), 'breeze_varnish_time', 'breeze_purge_cache' ); } @@ -126,10 +129,15 @@ public static function factory() { } } +if ( ! class_exists( 'Breeze_Options_Reader' ) ) { + require_once( BREEZE_PLUGIN_DIR . 'inc/class-breeze-options-reader.php' ); +} + + //Enabled auto purge the varnish caching by time life $params = array( 'breeze-active' => (int) Breeze_Options_Reader::get_option_value( 'breeze-active' ), - 'breeze-b-ttl' => (int) Breeze_Options_Reader::get_option_value( 'breeze-b-ttl' ), + 'breeze-b-ttl' => Breeze_Options_Reader::get_option_value( 'breeze-b-ttl' ), 'breeze-varnish-purge' => (int) Breeze_Options_Reader::get_option_value( 'auto-purge-varnish' ), ); diff --git a/wp-content/plugins/breeze/inc/class-breeze-bulk-update.php b/wp-content/plugins/breeze/inc/class-breeze-bulk-update.php new file mode 100644 index 00000000..e1297282 --- /dev/null +++ b/wp-content/plugins/breeze/inc/class-breeze-bulk-update.php @@ -0,0 +1,84 @@ += $total_plugin_count ) { + // Delete options after all plugins have been updated + delete_option( 'plugins_to_be_updated_count' ); + delete_option( 'breeze_updated_plugin_count' ); + delete_option( 'breeze_all_plugins_update_flag' ); + do_action( 'breeze_clear_all_cache' ); + } + } + } + + function store_update_count() { + if ( isset( $_POST['count'] ) ) { + $count = $_POST['count']; + $total_plugin_count = get_option( 'plugins_to_be_updated_count', 0 ); + $total_plugin_count = intval( $total_plugin_count ); + if ( 0 === $total_plugin_count ) { + update_option( 'plugins_to_be_updated_count', $count, 'no' ); + } + } + echo 'count stored'; + wp_die(); + } + + function plugins_js_script() { + $screen = get_current_screen(); // Get the current screen + + if ( 'plugins' !== $screen->base && 'plugins-network' !== $screen->base ) { + return; + } + ?> + + v2119_upgrades(); } + // Making sure that "Purge Cache After" value is set in Basic tab option. + if ( $is_older_than_v2118 || version_compare( $this->breeze_version, '2.2.8', '<' ) ) { + $this->v228_upgrades(); + } + do_action( 'breeze_after_existing_upgrade_routine', $this->breeze_version ); update_option( 'breeze_version_upgraded_from', $this->breeze_version ); } + /** + * Performs the necessary upgrades for version 2.2.8. + * + * This function updates the "Purge Cache After" setting in Basic tab. + * Ensures that the 'breeze-b-ttl' key is present in both multisite and single-site configurations. + * For multisite setups, it handles both network options and individual blog options. + * + * @return void + */ + public function v228_upgrades() { + + if ( is_multisite() ) { + // Handle network options. + $breeze_basic_network = get_site_option( 'breeze_basic_settings', array() ); + if ( ! array_key_exists( 'breeze-b-ttl', $breeze_basic_network ) ) { + $breeze_basic_network['breeze-b-ttl'] = 1440; + update_site_option( 'breeze_basic_settings', $breeze_basic_network ); + } + + // Handle check and update for multisite blogs. + $blogs = get_sites( + array( + 'number' => 0, + ) + ); + + foreach ( $blogs as $blog ) { + $basic = get_blog_option( (int) $blog->blog_id, 'breeze_basic_settings', array() ); + if ( ! array_key_exists( 'breeze-b-ttl', $basic ) ) { + $basic['breeze-b-ttl'] = 1440; + update_blog_option( (int) $blog->blog_id, 'breeze_basic_settings', $basic ); + } + } + } else { + // Handle check for single site. + $basic = breeze_get_option( 'basic_settings', true ); + if ( ! array_key_exists( 'breeze-b-ttl', $basic ) ) { + $basic['breeze-b-ttl'] = 1440; + breeze_update_option( 'basic_settings', $basic, true ); + } + } + } + public function v2119_upgrades() { if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) { return; diff --git a/wp-content/plugins/breeze/readme.txt b/wp-content/plugins/breeze/readme.txt index 96d71d27..43ff5ba0 100644 --- a/wp-content/plugins/breeze/readme.txt +++ b/wp-content/plugins/breeze/readme.txt @@ -4,7 +4,7 @@ Tags: cache,caching, performance, wp-cache, cdn Requires at least: 6.0 Tested up to: 6.7 Requires PHP: 7.4 -Stable tag: 2.2.7 +Stable tag: 2.2.9 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -160,9 +160,19 @@ Using Gzip, Breeze compresses the request files, further reducing the size of th == Changelog == += 2.2.9 = + +* Fix: PHP error no longer occurs when a new comment is submitted. +* Enhancement: Breeze cache now automatically clears after one or more plugin updates to ensure accurate content rendering. + += 2.2.8 = + +* Fix: The cron event breeze_purge_cache will now be created when activating the Breeze plugin. +* Fix: The cron event breeze_purge_cache will now be removed from single site and multi-site upon Breeze plugin deactivation. + = 2.2.7 = -* Add: Breeze plugin cache now automatically purges when updating global Header/Footer in Elementor +* Add: Breeze plugin cache now automatically purges when updating global Header/Footer in Elementor. = 2.2.6 = diff --git a/wp-content/plugins/contact-form-7/modules/stripe/api.php b/wp-content/plugins/contact-form-7/modules/stripe/api.php index e93944cb..d65f73cf 100644 --- a/wp-content/plugins/contact-form-7/modules/stripe/api.php +++ b/wp-content/plugins/contact-form-7/modules/stripe/api.php @@ -3,7 +3,7 @@ /** * Class for the Stripe API. * - * @link https://stripe.com/docs/api + * @link https://docs.stripe.com/api */ class WPCF7_Stripe_API { @@ -40,7 +40,7 @@ private function log( $url, $request, $response ) { /** * Returns default set of HTTP request headers used for Stripe API. * - * @link https://stripe.com/docs/building-plugins#setappinfo + * @link https://docs.stripe.com/building-plugins#setappinfo * * @return array An associative array of headers. */ @@ -77,7 +77,7 @@ private function default_headers() { /** * Creates a Payment Intent. * - * @link https://stripe.com/docs/api/payment_intents/create + * @link https://docs.stripe.com/api/payment_intents/create * * @param string|array $args Optional. Arguments to control behavior. * @return array|bool An associative array if 200 OK, false otherwise. @@ -118,9 +118,9 @@ public function create_payment_intent( $args = '' ) { /** - * Retrieve a Payment Intent. + * Retrieves a Payment Intent. * - * @link https://stripe.com/docs/api/payment_intents/retrieve + * @link https://docs.stripe.com/api/payment_intents/retrieve * * @param string $id Payment Intent identifier. * @return array|bool An associative array if 200 OK, false otherwise. @@ -151,4 +151,41 @@ public function retrieve_payment_intent( $id ) { return $response_body; } + + /** + * Updates a Payment Intent. + * + * @link https://docs.stripe.com/api/payment_intents/update + * + * @param string $id Payment Intent identifier. + * @param array $parameters Parameters. + * @return array|bool An associative array if 200 OK, false otherwise. + */ + public function update_payment_intent( $id, $parameters ) { + $endpoint = sprintf( + 'https://api.stripe.com/v1/payment_intents/%s', + urlencode( $id ) + ); + + $request = array( + 'headers' => $this->default_headers(), + 'body' => wp_parse_args( $parameters, array() ), + ); + + $response = wp_remote_post( sanitize_url( $endpoint ), $request ); + + if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { + if ( WP_DEBUG ) { + $this->log( $endpoint, $request, $response ); + } + + return false; + } + + $response_body = wp_remote_retrieve_body( $response ); + $response_body = json_decode( $response_body, true ); + + return $response_body; + } + } diff --git a/wp-content/plugins/contact-form-7/modules/stripe/stripe.php b/wp-content/plugins/contact-form-7/modules/stripe/stripe.php index 45a2c387..124fba89 100644 --- a/wp-content/plugins/contact-form-7/modules/stripe/stripe.php +++ b/wp-content/plugins/contact-form-7/modules/stripe/stripe.php @@ -115,14 +115,31 @@ function wpcf7_stripe_skip_spam_check( $skip_spam_check, $submission ) { $pi_id = trim( $_POST['_wpcf7_stripe_payment_intent'] ); $payment_intent = $service->api()->retrieve_payment_intent( $pi_id ); - if ( isset( $payment_intent['status'] ) - and ( 'succeeded' === $payment_intent['status'] ) ) { + if ( + isset( $payment_intent['metadata']['wpcf7_submission_timestamp'] ) + ) { + // This PI has already been used. Ignore. + return $skip_spam_check; + } + + if ( + isset( $payment_intent['status'] ) and + 'succeeded' === $payment_intent['status'] + ) { $submission->push( 'payment_intent', $pi_id ); + + $service->api()->update_payment_intent( $pi_id, array( + 'metadata' => array( + 'wpcf7_submission_timestamp' => $submission->get_meta( 'timestamp' ), + ), + ) ); } } - if ( ! empty( $submission->pull( 'payment_intent' ) ) - and $submission->verify_posted_data_hash() ) { + if ( + ! empty( $submission->pull( 'payment_intent' ) ) and + $submission->verify_posted_data_hash() + ) { $skip_spam_check = true; } @@ -130,6 +147,46 @@ function wpcf7_stripe_skip_spam_check( $skip_spam_check, $submission ) { } +add_filter( + 'wpcf7_spam', + 'wpcf7_stripe_verify_payment_intent', + 6, 2 +); + +/** + * Verifies submitted Stripe Payment Intent ID. + */ +function wpcf7_stripe_verify_payment_intent( $spam, $submission ) { + $service = WPCF7_Stripe::get_instance(); + + if ( ! $service->is_active() ) { + return $spam; + } + + if ( ! empty( $_POST['_wpcf7_stripe_payment_intent'] ) ) { + $pi_id = trim( $_POST['_wpcf7_stripe_payment_intent'] ); + $payment_intent = $service->api()->retrieve_payment_intent( $pi_id ); + + if ( + ! $payment_intent or + isset( $payment_intent['metadata']['wpcf7_submission_timestamp'] ) + ) { + $spam = true; + + $submission->add_spam_log( array( + 'agent' => 'stripe', + 'reason' => __( + 'Invalid Stripe Payment Intent ID detected.', + 'contact-form-7' + ), + ) ); + } + } + + return $spam; +} + + add_action( 'wpcf7_before_send_mail', 'wpcf7_stripe_before_send_mail', diff --git a/wp-content/plugins/contact-form-7/readme.txt b/wp-content/plugins/contact-form-7/readme.txt index 315807f9..912cb4fe 100644 --- a/wp-content/plugins/contact-form-7/readme.txt +++ b/wp-content/plugins/contact-form-7/readme.txt @@ -2,10 +2,10 @@ Contributors: takayukister Donate link: https://contactform7.com/donate/ Tags: contact form, schema-woven validation -Tested up to: 6.7 +Tested up to: 6.8 Requires at least: 6.6 Requires PHP: 7.4 -Stable tag: 6.0.5 +Stable tag: 6.0.6 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -67,6 +67,10 @@ Do you have questions or issues with Contact Form 7? Use these support channels For more information, see [Releases](https://contactform7.com/category/releases/). += 6.0.6 = + +[https://contactform7.com/contact-form-7-606/](https://contactform7.com/contact-form-7-606/) + = 6.0.5 = [https://contactform7.com/contact-form-7-605/](https://contactform7.com/contact-form-7-605/) diff --git a/wp-content/plugins/contact-form-7/wp-contact-form-7.php b/wp-content/plugins/contact-form-7/wp-contact-form-7.php index 97f13b0a..7f85466b 100644 --- a/wp-content/plugins/contact-form-7/wp-contact-form-7.php +++ b/wp-content/plugins/contact-form-7/wp-contact-form-7.php @@ -7,12 +7,12 @@ * Author URI: https://ideasilo.wordpress.com/ * License: GPL v2 or later * License URI: https://www.gnu.org/licenses/gpl-2.0.html - * Version: 6.0.5 + * Version: 6.0.6 * Requires at least: 6.6 * Requires PHP: 7.4 */ -define( 'WPCF7_VERSION', '6.0.5' ); +define( 'WPCF7_VERSION', '6.0.6' ); define( 'WPCF7_REQUIRED_WP_VERSION', '6.6' ); diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/general.css b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/general.css index e6bc000f..45517628 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/general.css +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/general.css @@ -252,6 +252,42 @@ h3.closed .hcaptcha-section-header-toggle:after { width: 100%; } +/* Section Protect Content */ +.hcaptcha-section-content + table tbody { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-areas: + "content protected-urls"; + gap: 0 20px; + padding: 15px 20px; +} + +.hcaptcha-section-content + table tbody tr.hcaptcha-general-content { + grid-area: content; +} + +.hcaptcha-section-content + table tbody tr.hcaptcha-general-protected-urls { + grid-area: protected-urls; +} + +.hcaptcha-section-content + table tbody tr th { + padding: 0 0 10px 0; + width: auto; +} + +.hcaptcha-section-content + table tbody tr td { + width: max-content; +} + +.hcaptcha-section-content + table tbody tr.hcaptcha-general-protected-urls td { + width: auto; +} + +.hcaptcha-section-content + table tbody tr td textarea, +.hcaptcha-section-content + table tbody tr td input { + width: 100%; +} + /* Section Other */ .hcaptcha-section-other + table tbody { display: grid; @@ -260,7 +296,7 @@ h3.closed .hcaptcha-section-header-toggle:after { "logged whitelisted" "recaptcha whitelisted" "hide-login-errors whitelisted" - "network whitelisted" + "cleanup-on-uninstall network" "login-limit login-interval" "delay ."; gap: 0 20px; @@ -279,6 +315,10 @@ h3.closed .hcaptcha-section-header-toggle:after { grid-area: hide-login-errors; } +.hcaptcha-section-other + table tbody tr.hcaptcha-general-cleanup-on-uninstall { + grid-area: cleanup-on-uninstall; +} + .hcaptcha-section-other + table tbody tr.hcaptcha-general-login-limit { grid-area: login-limit; padding-top: 15px; @@ -309,6 +349,7 @@ h3.closed .hcaptcha-section-header-toggle:after { .hcaptcha-section-other + table tbody tr.hcaptcha-general-recaptcha-compat-off th, .hcaptcha-section-other + table tbody tr.hcaptcha-general-hide-login-errors th, +.hcaptcha-section-other + table tbody tr.hcaptcha-general-cleanup-on-uninstall th, .hcaptcha-section-other + table tbody tr.hcaptcha-general--network-wide th { padding: 0; } diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/general.min.css b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/general.min.css index ecf72402..27ad4df5 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/general.min.css +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/general.min.css @@ -1 +1 @@ -#hcaptcha-message,#setting-error-settings_updated{box-sizing:border-box;max-width:760px}#hcaptcha-options table tbody{background:#fff}#hcaptcha-options table tbody tr{align-self:start;display:grid}#hcaptcha-options table tbody tr th{color:#5c6f8a}#hcaptcha-options table tbody tr td{margin:0;padding:0;position:relative}#hcaptcha-options .h-captcha{margin-bottom:0}#hcaptcha-options.hcaptcha-general h3{background:#fff;box-sizing:border-box;color:#5c6f8a;cursor:pointer;display:flex;justify-content:space-between;margin:1.5em 0 0;max-width:760px;padding:15px 20px}#hcaptcha-options.hcaptcha-general h3.disabled{opacity:.6}.hcaptcha-section-header-toggle:after{content:"\f142";font:normal 20px dashicons;speak:never;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}h3.closed .hcaptcha-section-header-toggle:after{content:"\f140"}#hcaptcha-options.hcaptcha-general h3+table{margin-top:0;max-width:760px;position:relative}#hcaptcha-options.hcaptcha-general h3.disabled+table{opacity:.6}#hcaptcha-options.hcaptcha-general h3.closed+table{display:none}#hcaptcha-options.hcaptcha-general h3+table:before{border-bottom:1px solid #c3c4c7;content:"";margin:0 20px;max-width:720px;position:absolute;top:0;width:calc(100% - 40px)}.hcaptcha-section-keys+table tbody{display:grid;gap:10px 20px;grid-template-areas:"site-key site-key secret-key secret-key" "sample-hcaptcha sample-hcaptcha check-config reset-notifications";grid-template-columns:repeat(4,1fr);padding:15px 20px}.hcaptcha-section-keys+table tbody th{width:auto}.hcaptcha-section-keys+table tbody tr.hcaptcha-general-site-key{grid-area:site-key}.hcaptcha-section-keys+table tbody tr.hcaptcha-general-secret-key{grid-area:secret-key}.hcaptcha-section-keys+table tbody tr.hcaptcha-general-sample-hcaptcha{grid-area:sample-hcaptcha}.hcaptcha-section-keys+table tbody tr.hcaptcha-general-check-config{grid-area:check-config}.hcaptcha-section-keys+table tbody tr.hcaptcha-reset-notifications{grid-area:reset-notifications}.hcaptcha-section-keys+table tbody tr th{padding:0 0 10px}.hcaptcha-section-keys+table tbody tr td input{width:100%}.hcaptcha-section-appearance+table tbody{display:grid;gap:10px 20px;grid-template-areas:"theme size language mode" "force force position position";grid-template-columns:repeat(4,1fr);padding:15px 20px}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-theme{grid-area:theme}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-size{grid-area:size}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-language{grid-area:language}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-mode{grid-area:mode}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-force{grid-area:force}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-menu-position{grid-area:position}.hcaptcha-section-appearance+table tbody tr th{padding:0 0 10px;width:auto}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-force td,.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-menu-position td{width:max-content}.hcaptcha-section-appearance+table tbody tr td select{width:100%}.hcaptcha-section-custom+table tbody{display:grid;gap:10px 20px;grid-template-areas:"custom-themes custom-themes config-params config-params" "custom-prop custom-value config-params config-params";grid-template-columns:repeat(4,1fr);padding:15px 20px}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-custom-themes{grid-area:custom-themes}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-config-params{grid-area:config-params}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-custom-prop{grid-area:custom-prop}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-custom-value{grid-area:custom-value}.hcaptcha-section-custom+table tbody tr th{padding:0 0 10px;width:auto}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-custom-themes td{width:max-content}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-config-params td{width:auto}.hcaptcha-section-custom+table tbody tr td select,.hcaptcha-section-custom+table tbody tr td textarea{width:100%}.hcaptcha-section-custom+table tbody tr td select option:disabled{background:#f0f0f1;color:#2c3338}.hcaptcha-section-custom+table tbody tr td input{height:30px;width:100%}.hcaptcha-section-enterprise+table tbody{display:grid;gap:10px 20px;grid-template-columns:repeat(2,1fr);padding:15px 20px}.hcaptcha-section-enterprise+table tbody th{width:auto}.hcaptcha-section-enterprise+table tbody tr th{padding:0 0 10px}.hcaptcha-section-enterprise+table tbody tr td input{width:100%}.hcaptcha-section-other+table tbody{display:grid;gap:0 20px;grid-template-areas:"logged whitelisted" "recaptcha whitelisted" "hide-login-errors whitelisted" "network whitelisted" "login-limit login-interval" "delay .";grid-template-columns:repeat(2,1fr);padding:15px 20px}.hcaptcha-section-other+table tbody tr.hcaptcha-general-off-when-logged-in{grid-area:logged}.hcaptcha-section-other+table tbody tr.hcaptcha-general-recaptcha-compat-off{grid-area:recaptcha}.hcaptcha-section-other+table tbody tr.hcaptcha-general-hide-login-errors{grid-area:hide-login-errors}.hcaptcha-section-other+table tbody tr.hcaptcha-general-login-limit{grid-area:login-limit;padding-top:15px}.hcaptcha-section-other+table tbody tr.hcaptcha-general-login-interval{grid-area:login-interval;padding-top:15px}.hcaptcha-section-other+table tbody tr.hcaptcha-general-whitelisted-ips{grid-area:whitelisted}.hcaptcha-section-other+table tbody tr.hcaptcha-general--network-wide{grid-area:network}.hcaptcha-section-other+table tbody tr.hcaptcha-general-delay{grid-area:delay;padding-top:15px}.hcaptcha-section-other+table tbody tr th{padding:0 0 10px;width:auto}.hcaptcha-section-other+table tbody tr.hcaptcha-general--network-wide th,.hcaptcha-section-other+table tbody tr.hcaptcha-general-hide-login-errors th,.hcaptcha-section-other+table tbody tr.hcaptcha-general-recaptcha-compat-off th{padding:0}.hcaptcha-section-other+table tbody tr td{width:max-content}.hcaptcha-section-other+table tbody tr.hcaptcha-general-delay td,.hcaptcha-section-other+table tbody tr.hcaptcha-general-login-interval td,.hcaptcha-section-other+table tbody tr.hcaptcha-general-login-limit td,.hcaptcha-section-other+table tbody tr.hcaptcha-general-whitelisted-ips td{width:auto}.hcaptcha-section-other+table tbody tr td input,.hcaptcha-section-other+table tbody tr td textarea{width:100%}.hcaptcha-section-statistics+table tbody{display:grid;gap:0 20px;grid-template-areas:"statistics collect-ip" "anonymous collect-ua";grid-template-columns:repeat(2,1fr);padding:15px 20px}.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-statistics{grid-area:statistics}.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-anonymous{grid-area:anonymous}.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-collect-ip{grid-area:collect-ip}.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-collect-ua{grid-area:collect-ua}.hcaptcha-section-statistics+table tbody tr th{padding:0 0 10px;width:auto}.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-anonymous th,.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-collect-ua th{padding:0}.hcaptcha-section-statistics+table tbody tr td{width:max-content}@media (max-width:600px){.hcaptcha-section-keys+table tbody{grid-template-areas:"site-key site-key" "secret-key secret-key" "sample-hcaptcha sample-hcaptcha" "check-config reset-notifications";grid-template-columns:repeat(2,1fr)!important}.hcaptcha-section-appearance+table tbody{grid-template-areas:"theme size" "language mode" "force force" "position position";grid-template-columns:repeat(2,1fr)!important}.hcaptcha-section-appearance+table tbody tr td{width:unset!important}.hcaptcha-section-custom+table tbody{grid-template-areas:"custom-themes custom-themes" "custom-prop custom-value" "config-params config-params";grid-template-columns:repeat(2,1fr)!important}.hcaptcha-section-custom+table tbody tr td{width:unset!important}.hcaptcha-section-enterprise+table tbody{grid-template-columns:repeat(1,1fr)!important}.hcaptcha-section-enterprise+table tbody tr td{width:unset!important}.hcaptcha-section-other+table tbody{grid-template-areas:"logged" "recaptcha" "hide-login-errors" "network" "whitelisted" "login-limit" "login-interval" "delay";grid-template-columns:repeat(1,1fr)!important}.hcaptcha-section-other+table tbody tr td{width:unset!important}.hcaptcha-section-statistics+table tbody{grid-template-areas:"statistics" "anonymous" "collect-ip" "collect-ua";grid-template-columns:repeat(1,1fr)}.hcaptcha-section-statistics+table tbody tr td{width:unset!important}} \ No newline at end of file +#hcaptcha-message,#setting-error-settings_updated{box-sizing:border-box;max-width:760px}#hcaptcha-options table tbody{background:#fff}#hcaptcha-options table tbody tr{align-self:start;display:grid}#hcaptcha-options table tbody tr th{color:#5c6f8a}#hcaptcha-options table tbody tr td{margin:0;padding:0;position:relative}#hcaptcha-options .h-captcha{margin-bottom:0}#hcaptcha-options.hcaptcha-general h3{background:#fff;box-sizing:border-box;color:#5c6f8a;cursor:pointer;display:flex;justify-content:space-between;margin:1.5em 0 0;max-width:760px;padding:15px 20px}#hcaptcha-options.hcaptcha-general h3.disabled{opacity:.6}.hcaptcha-section-header-toggle:after{content:"\f142";font:normal 20px dashicons;speak:never;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}h3.closed .hcaptcha-section-header-toggle:after{content:"\f140"}#hcaptcha-options.hcaptcha-general h3+table{margin-top:0;max-width:760px;position:relative}#hcaptcha-options.hcaptcha-general h3.disabled+table{opacity:.6}#hcaptcha-options.hcaptcha-general h3.closed+table{display:none}#hcaptcha-options.hcaptcha-general h3+table:before{border-bottom:1px solid #c3c4c7;content:"";margin:0 20px;max-width:720px;position:absolute;top:0;width:calc(100% - 40px)}.hcaptcha-section-keys+table tbody{display:grid;gap:10px 20px;grid-template-areas:"site-key site-key secret-key secret-key" "sample-hcaptcha sample-hcaptcha check-config reset-notifications";grid-template-columns:repeat(4,1fr);padding:15px 20px}.hcaptcha-section-keys+table tbody th{width:auto}.hcaptcha-section-keys+table tbody tr.hcaptcha-general-site-key{grid-area:site-key}.hcaptcha-section-keys+table tbody tr.hcaptcha-general-secret-key{grid-area:secret-key}.hcaptcha-section-keys+table tbody tr.hcaptcha-general-sample-hcaptcha{grid-area:sample-hcaptcha}.hcaptcha-section-keys+table tbody tr.hcaptcha-general-check-config{grid-area:check-config}.hcaptcha-section-keys+table tbody tr.hcaptcha-reset-notifications{grid-area:reset-notifications}.hcaptcha-section-keys+table tbody tr th{padding:0 0 10px}.hcaptcha-section-keys+table tbody tr td input{width:100%}.hcaptcha-section-appearance+table tbody{display:grid;gap:10px 20px;grid-template-areas:"theme size language mode" "force force position position";grid-template-columns:repeat(4,1fr);padding:15px 20px}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-theme{grid-area:theme}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-size{grid-area:size}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-language{grid-area:language}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-mode{grid-area:mode}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-force{grid-area:force}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-menu-position{grid-area:position}.hcaptcha-section-appearance+table tbody tr th{padding:0 0 10px;width:auto}.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-force td,.hcaptcha-section-appearance+table tbody tr.hcaptcha-general-menu-position td{width:max-content}.hcaptcha-section-appearance+table tbody tr td select{width:100%}.hcaptcha-section-custom+table tbody{display:grid;gap:10px 20px;grid-template-areas:"custom-themes custom-themes config-params config-params" "custom-prop custom-value config-params config-params";grid-template-columns:repeat(4,1fr);padding:15px 20px}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-custom-themes{grid-area:custom-themes}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-config-params{grid-area:config-params}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-custom-prop{grid-area:custom-prop}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-custom-value{grid-area:custom-value}.hcaptcha-section-custom+table tbody tr th{padding:0 0 10px;width:auto}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-custom-themes td{width:max-content}.hcaptcha-section-custom+table tbody tr.hcaptcha-general-config-params td{width:auto}.hcaptcha-section-custom+table tbody tr td select,.hcaptcha-section-custom+table tbody tr td textarea{width:100%}.hcaptcha-section-custom+table tbody tr td select option:disabled{background:#f0f0f1;color:#2c3338}.hcaptcha-section-custom+table tbody tr td input{height:30px;width:100%}.hcaptcha-section-enterprise+table tbody{display:grid;gap:10px 20px;grid-template-columns:repeat(2,1fr);padding:15px 20px}.hcaptcha-section-enterprise+table tbody th{width:auto}.hcaptcha-section-enterprise+table tbody tr th{padding:0 0 10px}.hcaptcha-section-enterprise+table tbody tr td input{width:100%}.hcaptcha-section-content+table tbody{display:grid;gap:0 20px;grid-template-areas:"content protected-urls";grid-template-columns:repeat(2,1fr);padding:15px 20px}.hcaptcha-section-content+table tbody tr.hcaptcha-general-content{grid-area:content}.hcaptcha-section-content+table tbody tr.hcaptcha-general-protected-urls{grid-area:protected-urls}.hcaptcha-section-content+table tbody tr th{padding:0 0 10px;width:auto}.hcaptcha-section-content+table tbody tr td{width:max-content}.hcaptcha-section-content+table tbody tr.hcaptcha-general-protected-urls td{width:auto}.hcaptcha-section-content+table tbody tr td input,.hcaptcha-section-content+table tbody tr td textarea{width:100%}.hcaptcha-section-other+table tbody{display:grid;gap:0 20px;grid-template-areas:"logged whitelisted" "recaptcha whitelisted" "hide-login-errors whitelisted" "cleanup-on-uninstall network" "login-limit login-interval" "delay .";grid-template-columns:repeat(2,1fr);padding:15px 20px}.hcaptcha-section-other+table tbody tr.hcaptcha-general-off-when-logged-in{grid-area:logged}.hcaptcha-section-other+table tbody tr.hcaptcha-general-recaptcha-compat-off{grid-area:recaptcha}.hcaptcha-section-other+table tbody tr.hcaptcha-general-hide-login-errors{grid-area:hide-login-errors}.hcaptcha-section-other+table tbody tr.hcaptcha-general-cleanup-on-uninstall{grid-area:cleanup-on-uninstall}.hcaptcha-section-other+table tbody tr.hcaptcha-general-login-limit{grid-area:login-limit;padding-top:15px}.hcaptcha-section-other+table tbody tr.hcaptcha-general-login-interval{grid-area:login-interval;padding-top:15px}.hcaptcha-section-other+table tbody tr.hcaptcha-general-whitelisted-ips{grid-area:whitelisted}.hcaptcha-section-other+table tbody tr.hcaptcha-general--network-wide{grid-area:network}.hcaptcha-section-other+table tbody tr.hcaptcha-general-delay{grid-area:delay;padding-top:15px}.hcaptcha-section-other+table tbody tr th{padding:0 0 10px;width:auto}.hcaptcha-section-other+table tbody tr.hcaptcha-general--network-wide th,.hcaptcha-section-other+table tbody tr.hcaptcha-general-cleanup-on-uninstall th,.hcaptcha-section-other+table tbody tr.hcaptcha-general-hide-login-errors th,.hcaptcha-section-other+table tbody tr.hcaptcha-general-recaptcha-compat-off th{padding:0}.hcaptcha-section-other+table tbody tr td{width:max-content}.hcaptcha-section-other+table tbody tr.hcaptcha-general-delay td,.hcaptcha-section-other+table tbody tr.hcaptcha-general-login-interval td,.hcaptcha-section-other+table tbody tr.hcaptcha-general-login-limit td,.hcaptcha-section-other+table tbody tr.hcaptcha-general-whitelisted-ips td{width:auto}.hcaptcha-section-other+table tbody tr td input,.hcaptcha-section-other+table tbody tr td textarea{width:100%}.hcaptcha-section-statistics+table tbody{display:grid;gap:0 20px;grid-template-areas:"statistics collect-ip" "anonymous collect-ua";grid-template-columns:repeat(2,1fr);padding:15px 20px}.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-statistics{grid-area:statistics}.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-anonymous{grid-area:anonymous}.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-collect-ip{grid-area:collect-ip}.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-collect-ua{grid-area:collect-ua}.hcaptcha-section-statistics+table tbody tr th{padding:0 0 10px;width:auto}.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-anonymous th,.hcaptcha-section-statistics+table tbody tr.hcaptcha-general-collect-ua th{padding:0}.hcaptcha-section-statistics+table tbody tr td{width:max-content}@media (max-width:600px){.hcaptcha-section-keys+table tbody{grid-template-areas:"site-key site-key" "secret-key secret-key" "sample-hcaptcha sample-hcaptcha" "check-config reset-notifications";grid-template-columns:repeat(2,1fr)!important}.hcaptcha-section-appearance+table tbody{grid-template-areas:"theme size" "language mode" "force force" "position position";grid-template-columns:repeat(2,1fr)!important}.hcaptcha-section-appearance+table tbody tr td{width:unset!important}.hcaptcha-section-custom+table tbody{grid-template-areas:"custom-themes custom-themes" "custom-prop custom-value" "config-params config-params";grid-template-columns:repeat(2,1fr)!important}.hcaptcha-section-custom+table tbody tr td{width:unset!important}.hcaptcha-section-enterprise+table tbody{grid-template-columns:repeat(1,1fr)!important}.hcaptcha-section-enterprise+table tbody tr td{width:unset!important}.hcaptcha-section-other+table tbody{grid-template-areas:"logged" "recaptcha" "hide-login-errors" "network" "whitelisted" "login-limit" "login-interval" "delay";grid-template-columns:repeat(1,1fr)!important}.hcaptcha-section-other+table tbody tr td{width:unset!important}.hcaptcha-section-statistics+table tbody{grid-template-areas:"statistics" "anonymous" "collect-ip" "collect-ua";grid-template-columns:repeat(1,1fr)}.hcaptcha-section-statistics+table tbody tr td{width:unset!important}} \ No newline at end of file diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/settings-base.css b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/settings-base.css index f5a509ab..743a69f4 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/settings-base.css +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/settings-base.css @@ -280,6 +280,25 @@ body.settings_page_hcaptcha { text-align: center; } +#hcaptcha-lightbox-modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 110000; + background: rgba(0, 0, 0, 0.8); + justify-content: center; + align-items: center; +} + +#hcaptcha-lightbox-img { + max-width: calc(100% - 100px); + max-height: calc(100vh - 100px); + box-shadow: 0 0 20px #000; +} + @media (max-width: 782px) { #hcaptcha-options .wp-list-table .toggle-row { top: 15px; diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/settings-base.min.css b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/settings-base.min.css index df169199..66a1991f 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/settings-base.min.css +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/settings-base.min.css @@ -1 +1 @@ -body.settings_page_hcaptcha{background:#f0f2f5;color:#5c6f8a}.wrap h1.hcaptcha-settings-header{align-items:center;display:flex;font-size:34px;font-weight:700}.hcaptcha-logo{height:64px;margin-block-end:10px;margin-block-start:10px;margin-inline-end:5px;margin-inline-start:0}.hcaptcha-settings-tabs{background:#fff;display:flex;flex-wrap:wrap;justify-content:space-between;line-height:4.5em;margin:10px -20px 0;padding:0 20px;position:sticky;top:0;z-index:2}.hcaptcha-settings-tab{color:#646970;display:inline-block;font-size:1.1em;margin-inline-end:30px;text-decoration:none}.hcaptcha-settings-tab:hover{border-bottom-color:#025176!important;border-bottom:2px solid;color:#666}.hcaptcha-settings-tab.active{border-bottom:2px solid #0075ab}.hcaptcha-header-bar{align-items:center;background:#f0f2f5;display:flex;justify-content:space-between;margin:0 -20px;padding:0 20px;position:sticky;top:60px;z-index:2}#hcaptcha-options h2{color:#5c6f8a;font-size:1.5em}#hcaptcha-options h2~*{display:none}#hcaptcha-options h3{color:#5c6f8a;margin:1.5em 0 1em}#hcaptcha-options .notice-dismiss:before{color:#5c6f8a}#hcaptcha-options table tbody tr td{margin:0;padding:0;position:relative}#hcaptcha-options table tr td fieldset input[type=checkbox]{border:none;box-shadow:none;display:inline;height:1.25rem;margin-block-end:0;margin-block-start:-.125rem;margin-inline-end:.5rem;margin-inline-start:0;width:2.3611rem}#hcaptcha-options table tr td fieldset input[type=checkbox]:before{background:url(../images/checkbox-off.svg);background-size:cover;content:"";display:inline-block;height:1.25rem;margin:0;width:2.3611rem}#hcaptcha-options table tr td fieldset input[type=checkbox]:checked:before{background:no-repeat url(../images/checkbox-on.svg);background-size:cover}#hcaptcha-options fieldset:disabled{color:#dadada}#hcaptcha-options .button-primary{background-color:#026593;border-color:#026593;color:#fff}#hcaptcha-options .button-primary:hover{background-color:#025176}#hcaptcha-options .button-secondary{background-color:#fff;border-color:#026593;color:#026593}#hcaptcha-options .button-secondary:hover{background-color:#ccc}#hcaptcha-options a{color:#0075ab}#hcaptcha-navigation a{border-color:#0075ab}#hcaptcha-options a.hcaptcha-settings-tab{color:#5c6f8a}#hcaptcha-options .helper:before{background:#5c6f8a;border-radius:1.2em;color:#fff;content:"?";height:1.2em;inset-inline-end:0;position:absolute;text-align:center;top:0;transform:translateY(-27px);width:1.2em}#hcaptcha-options fieldset+.helper:before{top:50%;transform:translate(25px,-.7em)}.rtl #hcaptcha-options fieldset+.helper:before{transform:translate(-25px,-.8em)}#hcaptcha-options .helper .helper-content{background:#5c6f8a;box-sizing:border-box;color:#f0f2f5;display:none;inset-inline-end:0;padding:.5em 1em;position:absolute;top:0;transform:translate(1px,10px);width:100%}.rtl #hcaptcha-options .helper .helper-content{transform:translate(-1px,10px)}#hcaptcha-options fieldset+.helper .helper-content{top:50%;transform:translate(25px,25px);width:calc(100% + 25px)}#hcaptcha-options .helper:hover{cursor:help}#hcaptcha-options .helper:hover .helper-content{display:block;z-index:1}#hcaptcha-options .helper .helper-content:after{border:10px solid transparent;border-bottom-color:#5c6f8a;content:"";inset-inline-end:0;position:absolute;top:0;transform:translateY(-100%)}#hcaptcha-options .helper .helper-content a{color:#fff}#hcaptcha-message{box-sizing:border-box}#hcaptcha-message>p{font-size:13px;font-weight:600;line-height:1.5;margin:.5em 0}@keyframes blink{0%{opacity:1}16.7%{opacity:0}33.3%{opacity:1}50%{opacity:0}66.7%{opacity:1}83.3%{opacity:0}to{opacity:1}}.blink{animation:blink 3s linear}.hcaptcha-hide{display:none}.hcaptcha-excerpt{display:block;max-width:100%;overflow:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:max-content}.hcaptcha-excerpt:hover{overflow:visible}.hcaptcha-excerpt:hover .hcaptcha-hide{background:#5c6f8a;border:1px solid #c3c4c7;border-radius:6px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.1);color:#f0f2f5;display:block;inset-inline-start:50%;max-width:300px;padding:8px 10px;position:absolute;text-align:center;top:50%;transform:translate(-50%,-50%);white-space:normal;width:max-content;z-index:1}@media (max-width:782px){#hcaptcha-options .wp-list-table .toggle-row{top:15px}#hcaptcha-options .wp-list-table tr.is-expanded .toggle-row{top:6.5px}#hcaptcha-options .widefat tbody th.check-column{padding:0;vertical-align:middle}#hcaptcha-options .widefat thead td.check-column,.widefat tfoot td.check-column{padding-left:0}#hcaptcha-options .widefat th input[type=checkbox]{margin-bottom:0}#hcaptcha-options .wp-list-table .is-expanded td:not(.hidden){overflow:visible}.hcaptcha-settings-tabs{margin:unset;margin-block-end:0;margin-block-start:10px;margin-inline-end:-12px;margin-inline-start:-10px;padding-block-end:0;padding-block-start:0;padding-inline-end:12px;padding-inline-start:10px}.hcaptcha-excerpt{display:table-cell;height:44px;vertical-align:middle}tr.is-expanded .hcaptcha-excerpt{height:19.5px}}@media (max-width:600px){#hcaptcha-options table tbody{grid-template-columns:1fr}#hcaptcha-options table tbody tr{position:relative}#hcaptcha-options fieldset+.helper:before{transform:translateY(-.7em)}#hcaptcha-options fieldset+.helper .helper-content{transform:translateY(25px);width:100%}#hcaptcha-options .helper{z-index:1}.hcaptcha-settings-tab{inset-inline-end:15px}} \ No newline at end of file +body.settings_page_hcaptcha{background:#f0f2f5;color:#5c6f8a}.wrap h1.hcaptcha-settings-header{align-items:center;display:flex;font-size:34px;font-weight:700}.hcaptcha-logo{height:64px;margin-block-end:10px;margin-block-start:10px;margin-inline-end:5px;margin-inline-start:0}.hcaptcha-settings-tabs{background:#fff;display:flex;flex-wrap:wrap;justify-content:space-between;line-height:4.5em;margin:10px -20px 0;padding:0 20px;position:sticky;top:0;z-index:2}.hcaptcha-settings-tab{color:#646970;display:inline-block;font-size:1.1em;margin-inline-end:30px;text-decoration:none}.hcaptcha-settings-tab:hover{border-bottom-color:#025176!important;border-bottom:2px solid;color:#666}.hcaptcha-settings-tab.active{border-bottom:2px solid #0075ab}.hcaptcha-header-bar{align-items:center;background:#f0f2f5;display:flex;justify-content:space-between;margin:0 -20px;padding:0 20px;position:sticky;top:60px;z-index:2}#hcaptcha-options h2{color:#5c6f8a;font-size:1.5em}#hcaptcha-options h2~*{display:none}#hcaptcha-options h3{color:#5c6f8a;margin:1.5em 0 1em}#hcaptcha-options .notice-dismiss:before{color:#5c6f8a}#hcaptcha-options table tbody tr td{margin:0;padding:0;position:relative}#hcaptcha-options table tr td fieldset input[type=checkbox]{border:none;box-shadow:none;display:inline;height:1.25rem;margin-block-end:0;margin-block-start:-.125rem;margin-inline-end:.5rem;margin-inline-start:0;width:2.3611rem}#hcaptcha-options table tr td fieldset input[type=checkbox]:before{background:url(../images/checkbox-off.svg);background-size:cover;content:"";display:inline-block;height:1.25rem;margin:0;width:2.3611rem}#hcaptcha-options table tr td fieldset input[type=checkbox]:checked:before{background:no-repeat url(../images/checkbox-on.svg);background-size:cover}#hcaptcha-options fieldset:disabled{color:#dadada}#hcaptcha-options .button-primary{background-color:#026593;border-color:#026593;color:#fff}#hcaptcha-options .button-primary:hover{background-color:#025176}#hcaptcha-options .button-secondary{background-color:#fff;border-color:#026593;color:#026593}#hcaptcha-options .button-secondary:hover{background-color:#ccc}#hcaptcha-options a{color:#0075ab}#hcaptcha-navigation a{border-color:#0075ab}#hcaptcha-options a.hcaptcha-settings-tab{color:#5c6f8a}#hcaptcha-options .helper:before{background:#5c6f8a;border-radius:1.2em;color:#fff;content:"?";height:1.2em;inset-inline-end:0;position:absolute;text-align:center;top:0;transform:translateY(-27px);width:1.2em}#hcaptcha-options fieldset+.helper:before{top:50%;transform:translate(25px,-.7em)}.rtl #hcaptcha-options fieldset+.helper:before{transform:translate(-25px,-.8em)}#hcaptcha-options .helper .helper-content{background:#5c6f8a;box-sizing:border-box;color:#f0f2f5;display:none;inset-inline-end:0;padding:.5em 1em;position:absolute;top:0;transform:translate(1px,10px);width:100%}.rtl #hcaptcha-options .helper .helper-content{transform:translate(-1px,10px)}#hcaptcha-options fieldset+.helper .helper-content{top:50%;transform:translate(25px,25px);width:calc(100% + 25px)}#hcaptcha-options .helper:hover{cursor:help}#hcaptcha-options .helper:hover .helper-content{display:block;z-index:1}#hcaptcha-options .helper .helper-content:after{border:10px solid transparent;border-bottom-color:#5c6f8a;content:"";inset-inline-end:0;position:absolute;top:0;transform:translateY(-100%)}#hcaptcha-options .helper .helper-content a{color:#fff}#hcaptcha-message{box-sizing:border-box}#hcaptcha-message>p{font-size:13px;font-weight:600;line-height:1.5;margin:.5em 0}@keyframes blink{0%{opacity:1}16.7%{opacity:0}33.3%{opacity:1}50%{opacity:0}66.7%{opacity:1}83.3%{opacity:0}to{opacity:1}}.blink{animation:blink 3s linear}.hcaptcha-hide{display:none}.hcaptcha-excerpt{display:block;max-width:100%;overflow:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:max-content}.hcaptcha-excerpt:hover{overflow:visible}.hcaptcha-excerpt:hover .hcaptcha-hide{background:#5c6f8a;border:1px solid #c3c4c7;border-radius:6px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.1);color:#f0f2f5;display:block;inset-inline-start:50%;max-width:300px;padding:8px 10px;position:absolute;text-align:center;top:50%;transform:translate(-50%,-50%);white-space:normal;width:max-content;z-index:1}#hcaptcha-lightbox-modal{align-items:center;background:rgba(0,0,0,.8);display:none;height:100vh;justify-content:center;left:0;position:fixed;top:0;width:100vw;z-index:110000}#hcaptcha-lightbox-img{box-shadow:0 0 20px #000;max-height:calc(100vh - 100px);max-width:calc(100% - 100px)}@media (max-width:782px){#hcaptcha-options .wp-list-table .toggle-row{top:15px}#hcaptcha-options .wp-list-table tr.is-expanded .toggle-row{top:6.5px}#hcaptcha-options .widefat tbody th.check-column{padding:0;vertical-align:middle}#hcaptcha-options .widefat thead td.check-column,.widefat tfoot td.check-column{padding-left:0}#hcaptcha-options .widefat th input[type=checkbox]{margin-bottom:0}#hcaptcha-options .wp-list-table .is-expanded td:not(.hidden){overflow:visible}.hcaptcha-settings-tabs{margin:unset;margin-block-end:0;margin-block-start:10px;margin-inline-end:-12px;margin-inline-start:-10px;padding-block-end:0;padding-block-start:0;padding-inline-end:12px;padding-inline-start:10px}.hcaptcha-excerpt{display:table-cell;height:44px;vertical-align:middle}tr.is-expanded .hcaptcha-excerpt{height:19.5px}}@media (max-width:600px){#hcaptcha-options table tbody{grid-template-columns:1fr}#hcaptcha-options table tbody tr{position:relative}#hcaptcha-options fieldset+.helper:before{transform:translateY(-.7em)}#hcaptcha-options fieldset+.helper .helper-content{transform:translateY(25px);width:100%}#hcaptcha-options .helper{z-index:1}.hcaptcha-settings-tab{inset-inline-end:15px}} \ No newline at end of file diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/whats-new.css b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/whats-new.css new file mode 100644 index 00000000..826c1843 --- /dev/null +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/whats-new.css @@ -0,0 +1,173 @@ +.hcaptcha-whats-new-modal { + position: fixed; + z-index: 99999; + left: 0; + top: 0; + right: 0; + bottom: 0; + width: 100%; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; +} + +.hcaptcha-whats-new-modal-bg { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + background: rgba(30, 30, 35, 0.6); + backdrop-filter: blur(7px); +} + +.hcaptcha-whats-new-close { + position: absolute; + top: 10px; + right: 10px; + margin: 0; + padding: 0; + background: none; + border: none; + line-height: 0; + color: #787c82; + cursor: pointer; + z-index: 100001; +} + +.hcaptcha-whats-new-close:before { + background: none; + color: #787c82; + content: "\f153"; + display: block; + font: normal 16px / 20px dashicons; + speak: never; + height: 20px; + text-align: center; + width: 20px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.hcaptcha-whats-new-modal-popup { + position: relative; + display: flex; + flex-direction: column; + background: #fff; + border-radius: 12px; + box-shadow: 0 8px 40px rgba(0, 0, 0, 0.25); + min-width: 70vw; + max-width: 1000px; + max-height: calc(100vh - 100px); + margin: 50px; + padding: 0; + overflow-y: auto; + z-index: 100000; +} + +.hcaptcha-whats-new-header { + display: flex; + flex: 0 0 auto; + justify-content: center; + justify-items: start; + margin: 50px 13% 0 13%; + align-items: center; +} + +.hcaptcha-whats-new-icon { + margin-right: 11px; +} + +.hcaptcha-whats-new-icon img { + height: 40px; +} + +.hcaptcha-whats-new-title h1 { + margin: 0; + font-weight: 700; + font-size: 24px; + color: #3c434a; + line-height: normal; +} + +#hcaptcha-whats-new-version { + color: #0075ab; +} + +.hcaptcha-whats-new-content { + margin: 50px 0 0 0; + flex: 1 1 auto; + overflow-y: auto; + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE 10+, Edge */ +} + +/* Chrome, Safari and other WebKit-browsers */ +.popup-content::-webkit-scrollbar { + display: none; +} + +.hcaptcha-whats-new-block { + padding: 75px 13%; +} + +.hcaptcha-whats-new-block:first-of-type { + padding-top: 25px; +} + +.hcaptcha-whats-new-block:nth-child(odd) { + background: #f0f2f5; +} + +.hcaptcha-whats-new-block.center { + text-align: center; +} + +.hcaptcha-whats-new-badge { + display: inline-block; + margin-bottom: 10px; + color: #ffffff; + background: #4de1d2; + border: 1px #4de1d2 solid; + border-radius: 2px; + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + line-height: normal; + padding: 4px 8px; + cursor: default; +} + +.hcaptcha-whats-new-content h2 { + margin: 0; + font-weight: 500; + font-size: 32px; + color: #3c434a; + line-height: normal; +} + +.hcaptcha-whats-new-message, +.hcaptcha-whats-new-message p { + font-weight: 400; + font-size: 18px; + color: #3c434a; + line-height: normal; + margin: 15px 0; +} + +.hcaptcha-whats-new-button { + margin-bottom: 50px; +} + +.hcaptcha-whats-new-image img { + max-width: 100%; +} + +@media (max-width: 600px) { + .hcaptcha-whats-new-modal-popup { + max-width: calc(100% - 16px); + max-height: calc(100vh - 16px); + margin: 8px; + } +} diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/whats-new.min.css b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/whats-new.min.css new file mode 100644 index 00000000..0b1b7e56 --- /dev/null +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/css/whats-new.min.css @@ -0,0 +1 @@ +.hcaptcha-whats-new-modal{align-items:center;bottom:0;display:flex;height:100vh;justify-content:center;left:0;position:fixed;right:0;top:0;width:100%;z-index:99999}.hcaptcha-whats-new-modal-bg{backdrop-filter:blur(7px);background:rgba(30,30,35,.6);bottom:0;left:0;position:absolute;right:0;top:0}.hcaptcha-whats-new-close{background:none;border:none;color:#787c82;cursor:pointer;line-height:0;margin:0;padding:0;position:absolute;right:10px;top:10px;z-index:100001}.hcaptcha-whats-new-close:before{background:none;color:#787c82;content:"\f153";display:block;font:normal 16px/20px dashicons;speak:never;height:20px;text-align:center;width:20px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.hcaptcha-whats-new-modal-popup{background:#fff;border-radius:12px;box-shadow:0 8px 40px rgba(0,0,0,.25);display:flex;flex-direction:column;margin:50px;max-height:calc(100vh - 100px);max-width:1000px;min-width:70vw;overflow-y:auto;padding:0;position:relative;z-index:100000}.hcaptcha-whats-new-header{align-items:center;display:flex;flex:0 0 auto;justify-content:center;justify-items:start;margin:50px 13% 0}.hcaptcha-whats-new-icon{margin-right:11px}.hcaptcha-whats-new-icon img{height:40px}.hcaptcha-whats-new-title h1{color:#3c434a;font-size:24px;font-weight:700;line-height:normal;margin:0}#hcaptcha-whats-new-version{color:#0075ab}.hcaptcha-whats-new-content{flex:1 1 auto;margin:50px 0 0;overflow-y:auto;scrollbar-width:none;-ms-overflow-style:none}.popup-content::-webkit-scrollbar{display:none}.hcaptcha-whats-new-block{padding:75px 13%}.hcaptcha-whats-new-block:first-of-type{padding-top:25px}.hcaptcha-whats-new-block:nth-child(odd){background:#f0f2f5}.hcaptcha-whats-new-block.center{text-align:center}.hcaptcha-whats-new-badge{background:#4de1d2;border:1px solid #4de1d2;border-radius:2px;color:#fff;cursor:default;display:inline-block;font-size:10px;font-weight:700;line-height:normal;margin-bottom:10px;padding:4px 8px;text-transform:uppercase}.hcaptcha-whats-new-content h2{color:#3c434a;font-size:32px;font-weight:500;line-height:normal;margin:0}.hcaptcha-whats-new-message,.hcaptcha-whats-new-message p{color:#3c434a;font-size:18px;font-weight:400;line-height:normal;margin:15px 0}.hcaptcha-whats-new-button{margin-bottom:50px}.hcaptcha-whats-new-image img{max-width:100%}@media (max-width:600px){.hcaptcha-whats-new-modal-popup{margin:8px;max-height:calc(100vh - 16px);max-width:calc(100% - 16px)}} \ No newline at end of file diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/hcaptcha-icon-animated.svg b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/hcaptcha-icon-animated.svg new file mode 100644 index 00000000..d9f859f3 --- /dev/null +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/hcaptcha-icon-animated.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/logo/events-manager.svg b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/logo/events-manager.svg new file mode 100644 index 00000000..5ea7677f --- /dev/null +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/logo/events-manager.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Events + + + Manager + + diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/logo/password-protected.png b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/logo/password-protected.png new file mode 100644 index 00000000..c5a4543a Binary files /dev/null and b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/logo/password-protected.png differ diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/passive-mode-example.gif b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/passive-mode-example.gif new file mode 100644 index 00000000..45cd957a Binary files /dev/null and b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/passive-mode-example.gif differ diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/protect-content-example.gif b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/protect-content-example.gif new file mode 100644 index 00000000..f1505f4a Binary files /dev/null and b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/images/protect-content-example.gif differ diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/admin-cf7.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/admin-cf7.js index f1504d52..9a69c53d 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/admin-cf7.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/admin-cf7.js @@ -4,7 +4,6 @@ * @param HCaptchaCF7Object.updateFormAction * @param HCaptchaCF7Object.updateFormNonce * @param HCaptchaCF7Object.ajaxUrl - * @param hCaptcha.reset */ /** diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/apps/hcaptcha.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/apps/hcaptcha.js index 86d50e8c..15b13cfc 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/apps/hcaptcha.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/apps/hcaptcha.js @@ -1 +1 @@ -(()=>{"use strict";function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(t){return function(t){if(Array.isArray(t))return r(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||n(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function n(t,e){if(t){if("string"==typeof t)return r(t,e);var n={}.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?r(t,e):void 0}}function r(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n=t.length?{done:!0}:{done:!1,value:t[o++]}},e:function(t){throw t},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,u=!0,c=!1;return{s:function(){r=r.call(t)},n:function(){var t=r.next();return u=t.done,t},e:function(t){c=!0,a=t},f:function(){try{u||null==r.return||r.return()}finally{if(c)throw a}}}}(e);try{var i=function(){var e=r.value.oldValue,n=t.darkElement.getAttribute("class");e=e?e.split(" "):[],(n=n?n.split(" "):[]).filter((function(t){return!e.includes(t)})).concat(e.filter((function(t){return!n.includes(t)}))).includes(t.darkClass)&&t.bindEvents()};for(o.s();!(r=o.n()).done;)i()}catch(t){o.e(t)}finally{o.f()}})).observe(this.darkElement,{attributes:!0,attributeOldValue:!0}))}},{key:"getWidgetByToken",value:function(t){var n=e(document.querySelectorAll(this.responseSelector)).find((function(e){return e.value===t}));return n?n.closest(".h-captcha"):null}},{key:"callback",value:function(t){document.dispatchEvent(new CustomEvent("hCaptchaSubmitted",{detail:{token:t}}));var e=this.getParams(),n=this.getWidgetByToken(t),r=n?n.dataset.force:null;("invisible"===e.size||"true"===r&&this.isValidated())&&this.submit()}},{key:"applyAutoTheme",value:function(t){if("auto"!==t.theme)return t;if(t.theme="light",!this.darkElement)return t;var e=this.darkElement.getAttribute("class");return(e=e||"").includes(this.darkClass)&&(t.theme="dark"),t}},{key:"render",value:function(e){this.observeDarkMode();var n,r,o=this.getParams();return"object"===t(o.theme)?null!==(n=null===(r=o)||void 0===r||null===(r=r.theme)||void 0===r||null===(r=r.component)||void 0===r||null===(r=r.checkbox)||void 0===r||null===(r=r.main)||void 0===r?void 0:r.fill)&&void 0!==n&&n&&(e.dataset.theme="custom"):o.theme=e.dataset.theme,o.size=e.dataset.size,o=this.applyAutoTheme(o),hcaptcha.render(e,o)}},{key:"addSyncedEventListener",value:function(t){if("loading"===document.readyState){if(this.addedDCLCallbacks.has(t))return;this.addedDCLCallbacks.add(t),window.addEventListener("DOMContentLoaded",t)}else t()}},{key:"bindEvents",value:function(){var t=this;"undefined"!=typeof hcaptcha&&(this.formSelector=wp.hooks.applyFilters("hcaptcha.formSelector","form, section.cwginstock-subscribe-form, div.sdm_download_item, .gform_editor, #nf-builder, .wpforms-captcha-preview"),this.submitButtonSelector=wp.hooks.applyFilters("hcaptcha.submitButtonSelector",'*[type="submit"]:not(.quform-default-submit), #check_config, button[type="button"].ff-btn, a.et_pb_newsletter_button.et_pb_button, .forminator-button-submit, .frm_button_submit, a.sdm_download, .uagb-forms-main-submit-button'),this.responseSelector='textarea[name="h-captcha-response"]',this.getForms().map((function(e){var n=e.querySelector(".h-captcha");if(null===n)return e;if(n.classList.contains("hcaptcha-widget-id"))return e;n.innerHTML="";var r=t.generateID(),o=e.querySelectorAll(t.submitButtonSelector)[0],i=t.render(n);if(e.dataset.hCaptchaId=r,t.foundForms.push({hCaptchaId:r,submitButtonElement:o,widgetId:i}),!o)return e;var a=n.dataset;return"invisible"!==a.size&&"true"!==a.force||o.addEventListener("click",t.validate,!0),e}),this))}},{key:"isAjaxSubmitButton",value:function(t){var e=t.getAttribute("type"),n="submit"!==(e=e?e.toLowerCase():"");return wp.hooks.applyFilters("hcaptcha.ajaxSubmitButton",n,t)}},{key:"submit",value:function(){if(this.currentForm){var t=this.currentForm,e=t.formElement,n=t.submitButtonElement;if("form"!==e.tagName.toLowerCase()||this.isAjaxSubmitButton(n))return n.removeEventListener("click",this.validate,!0),void n.click();e.requestSubmit?e.requestSubmit(n):e.submit()}}}],i&&o(r.prototype,i),a&&o(r,a),Object.defineProperty(r,"prototype",{writable:!1}),r;var r,i,a}();window.HCaptchaMainObject=window.HCaptchaMainObject||{};const u=a;function c(t){return c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},c(t)}function l(t,e){for(var n=0;n{"use strict";function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(t,e){var n="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!n){if(Array.isArray(t)||(n=r(t))||e&&t&&"number"==typeof t.length){n&&(t=n);var o=0,a=function(){};return{s:a,n:function(){return o>=t.length?{done:!0}:{done:!1,value:t[o++]}},e:function(t){throw t},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,u=!0,c=!1;return{s:function(){n=n.call(t)},n:function(){var t=n.next();return u=t.done,t},e:function(t){c=!0,i=t},f:function(){try{u||null==n.return||n.return()}finally{if(c)throw i}}}}function n(t){return function(t){if(Array.isArray(t))return o(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||r(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function r(t,e){if(t){if("string"==typeof t)return o(t,e);var n={}.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?o(t,e):void 0}}function o(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n window.hCaptchaReset( document.querySelector( '.hcaptcha-general-sample-hcaptcha' ) ), + onAction: () => window.hCaptchaBindEvents(), } ); return; diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/general.min.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/general.min.js index 08006718..8b682622 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/general.min.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/general.min.js @@ -1 +1 @@ -(()=>{var t={};function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},e(t)}t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}();var a=function(a){var n="#hcaptcha-message",c=a(n),o=a("form.hcaptcha-general"),r=a('[name="hcaptcha_settings[site_key]"]'),i=a('[name="hcaptcha_settings[secret_key]"]'),s=a("#hcaptcha-options .h-captcha"),h=a("#check_config"),l=a("#reset_notifications"),p=a('[name="hcaptcha_settings[theme]"]'),d=a('[name="hcaptcha_settings[size]"]'),u=a('[name="hcaptcha_settings[language]"]'),f=a('[name="hcaptcha_settings[mode]"]'),m=a('[name="hcaptcha_settings[custom_themes][]"]'),v=a(".hcaptcha-general-custom-prop select"),g=a(".hcaptcha-general-custom-value input"),b=a('[name="hcaptcha_settings[config_params]"]'),y=a(".hcaptcha-section-enterprise + table input"),C=a('[name="hcaptcha_settings[recaptcha_compat_off][]"]'),j=o.find("#submit"),O={},k=r.val(),H=i.val(),G=A();O[HCaptchaGeneralObject.modeLive]=HCaptchaGeneralObject.siteKey,O[HCaptchaGeneralObject.modeTestPublisher]=HCaptchaGeneralObject.modeTestPublisherSiteKey,O[HCaptchaGeneralObject.modeTestEnterpriseSafeEndUser]=HCaptchaGeneralObject.modeTestEnterpriseSafeEndUserSiteKey,O[HCaptchaGeneralObject.modeTestEnterpriseBotDetected]=HCaptchaGeneralObject.modeTestEnterpriseBotDetectedSiteKey;var S=!1,_=!1,w=[];function T(t){var e={};return t.each((function(){var t=a(this),n=t.attr("name").replace(/hcaptcha_settings\[(.+)]/,"$1");e[n]=t.val()})),e}function A(){return T(y)}function x(){c.remove(),a('
').insertAfter(".hcaptcha-header-bar"),c=a(n)}function E(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(t=void 0===t?"":String(t),t=(t+="\n"+function(){for(var t=["recaptchacompat disabled","Missing sitekey - https://docs.hcaptcha.com/configuration#javascript-api"],e=[],a=0;a".concat(t,"

")}));c.html(n.join("")),a(document).trigger("wp-updates-notice-added"),a("html, body").animate({scrollTop:c.offset().top-hCaptchaSettingsBase.getStickyHeight()},1e3)}}function N(){E(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"","notice-success")}function B(){E(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"","notice-error")}function K(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},a=Object.assign({},hCaptcha.getParams(),t),n=m.prop("checked"),c="live"===f.val();for(var o in n&&c?s.attr("data-theme","custom"):s.attr("data-theme",p.val()),n&&"object"===e(t.theme)||!n&&"object"!==e(t.theme)?a.theme=t.theme:a.theme=hCaptcha.getParams().theme,hCaptcha.setParams(a),s.html(""),t)"object"!==e(t[o])&&s.attr("data-".concat(o),"".concat(t[o]));hCaptcha.bindEvents()}function L(t,a){var n=function(t){return t&&"object"===e(t)};return n(t)&&n(a)?(Object.keys(a).forEach((function(e){var c=t[e],o=a[e];Array.isArray(c)&&Array.isArray(o)?t[e]=c.concat(o):n(c)&&n(o)?t[e]=L(Object.assign({},c),o):t[e]=o})),t):a}function P(t){var a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";for(var n in t){var c=a?"".concat(a,"--").concat(n):n;if("object"===e(t[n])&&null!==t[n])P(t[n],c);else{var o=t[n],r=c.replace(/theme--/g,""),i="".concat(r,"=").concat(o),s=v.find('option[value*="'.concat(r,'="]'));1===s.length&&(s.attr("value",i),s.is(":selected")&&g.val(o))}}}function U(){var t,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},a=b.val().trim();a=a||null;try{t=JSON.parse(a)}catch(t){return b.css("background-color","#ffabaf"),j.attr("disabled",!0),void B("Bad JSON!")}t=L(t,e),b.val(JSON.stringify(t,null,2)),P(t),m.prop("checked")||(t={sitekey:r.val(),theme:p.val(),size:d.val(),hl:u.val()}),K(t)}function J(){r.val()===k&&i.val()===H?(S=!1,x(),j.attr("disabled",!1)):S||(S=!0,B(HCaptchaGeneralObject.checkConfigNotice),j.attr("disabled",!0))}function D(t){return t=t.replace(/(http|https):\/\//,""),"https://"+new URL("https://"+t).host}!function(){w=[];var t=console.log,e=console.warn,a=console.info,n=console.error,c=console.clear;console.log=function(e){w.push(["Console log:",arguments]),t.apply(console,arguments)},console.warn=function(t){w.push(["Console warn:",arguments]),e.apply(console,arguments)},console.info=function(t){w.push(["Console info:",arguments]),a.apply(console,arguments)},console.error=function(t){w.push(["Console error:",arguments]),n.apply(console,arguments)},console.clear=function(){w=[],c()}}(),document.addEventListener("hCaptchaLoaded",(function(){B()})),h.on("click",(function(t){t.preventDefault(),""!==a('.hcaptcha-general-sample-hcaptcha textarea[name="h-captcha-response"]').val()?function(){x(),j.attr("disabled",!0);var t={action:HCaptchaGeneralObject.checkConfigAction,nonce:HCaptchaGeneralObject.checkConfigNonce,mode:f.val(),siteKey:r.val(),secretKey:i.val(),"h-captcha-response":a('textarea[name="h-captcha-response"]').val(),"hcaptcha-widget-id":a('input[name="hcaptcha-widget-id"]').val()};a.post({url:HCaptchaGeneralObject.ajaxUrl,data:t,beforeSend:function(){return N(HCaptchaGeneralObject.checkingConfigMsg)}}).done((function(t){t.success?(k=r.val(),H=i.val(),G=T(y),_=!1,N(t.data),j.attr("disabled",!1)):B(t.data)})).fail((function(t){B(t.statusText)})).always((function(){K()}))}():kaggDialog.confirm({title:HCaptchaGeneralObject.completeHCaptchaTitle,content:HCaptchaGeneralObject.completeHCaptchaContent,type:"info",buttons:{ok:{text:HCaptchaGeneralObject.OKBtnText}},onAction:function(){return window.hCaptchaReset(document.querySelector(".hcaptcha-general-sample-hcaptcha"))}})})),r.on("change",(function(t){K({sitekey:a(t.target).val()}),J()})),i.on("change",(function(){J()})),p.on("change",(function(t){K({theme:a(t.target).val()})})),d.on("change",(function(t){var e=a("#hcaptcha-invisible-notice"),n=a(t.target).val();"invisible"===n?e.show():e.hide(),K({size:n})})),u.on("change",(function(t){K({hl:a(t.target).val()})})),f.on("change",(function(t){var e=a(t.target).val();O.hasOwnProperty(e)&&(e===HCaptchaGeneralObject.modeLive?(r.attr("disabled",!1),i.attr("disabled",!1)):(r.attr("disabled",!0),i.attr("disabled",!0)),K({sitekey:O[e]}))})),m.on("change",(function(){U()})),b.on("blur",(function(){U()})),b.on("focus",(function(){b.css("background-color","unset"),j.attr("disabled",!1)})),y.on("change",(function(){!function(){var e={onload:"hCaptchaOnLoad",render:"explicit"};C.prop("checked")&&(e.recaptchacompat="off"),m.prop("checked")&&(e.custom="true");var a={asset_host:"assethost",endpoint:"endpoint",host:"host",image_host:"imghost",report_api:"reportapi",sentry:"sentry"},n=A();for(var c in a){var o=n[c].trim();o&&(e[a[c]]=encodeURIComponent(D(o)))}var r=n.api_host.trim();r=D(r=r||"js.hcaptcha.com")+"/1/api.js";var i=new URL(r);for(var h in e)i.searchParams.append(h,e[h]);document.getElementById("hcaptcha-api").remove(),delete t.g.hcaptcha,s.html("");var l=document.getElementsByTagName("head")[0],p=document.createElement("script");p.type="text/javascript",p.id="hcaptcha-api",p.src=i.href,l.appendChild(p)}(),JSON.stringify(A())===JSON.stringify(G)?(_=!1,x(),j.attr("disabled",!1)):_||(_=!0,B(HCaptchaGeneralObject.checkConfigNotice),j.attr("disabled",!0))})),a(".hcaptcha-general h3").on("click",(function(t){var e=a(t.currentTarget);e.toggleClass("closed");var n={action:HCaptchaGeneralObject.toggleSectionAction,nonce:HCaptchaGeneralObject.toggleSectionNonce,section:e.attr("class").replaceAll(/(hcaptcha-section-|closed)/g,"").trim(),status:!e.hasClass("closed")};a.post({url:HCaptchaGeneralObject.ajaxUrl,data:n}).done((function(t){t.success||B(t.data)})).fail((function(t){B(t.statusText)}))})),h.removeAttr("name"),l.removeAttr("name"),v.removeAttr("name"),g.removeAttr("name"),v.find("option").each((function(){var t=a(this);t.val().split("=")[1]||t.attr("disabled",!0)})),g.val(""),v.on("change",(function(){var t=a(this).find("option:selected").val().split("="),e=t[0],n=t[1];"palette--mode"===e?(g.attr("type","text"),g.val(n)):(g.val(n),g.attr("type","color"))})),g.on("change",(function(t){var e=a(t.target).val(),n=v.find("option:selected"),c=n.val().split("="),o=c[0],r=e;n.val(o+"="+e),U(r=(o="theme--"+c[0]).split("--").reverse().reduce((function(t,e){var a={};return a[e]=t,a}),r))}))};window.hCaptchaGeneral=a,jQuery(document).ready(a)})(); \ No newline at end of file +(()=>{var t={};function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},e(t)}t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}();var a=function(a){var n="#hcaptcha-message",c=a(n),o=a("form.hcaptcha-general"),r=a('[name="hcaptcha_settings[site_key]"]'),i=a('[name="hcaptcha_settings[secret_key]"]'),s=a("#hcaptcha-options .h-captcha"),h=a("#check_config"),l=a("#reset_notifications"),p=a('[name="hcaptcha_settings[theme]"]'),d=a('[name="hcaptcha_settings[size]"]'),u=a('[name="hcaptcha_settings[language]"]'),f=a('[name="hcaptcha_settings[mode]"]'),v=a('[name="hcaptcha_settings[custom_themes][]"]'),m=a(".hcaptcha-general-custom-prop select"),g=a(".hcaptcha-general-custom-value input"),b=a('[name="hcaptcha_settings[config_params]"]'),y=a(".hcaptcha-section-enterprise + table input"),C=a('[name="hcaptcha_settings[recaptcha_compat_off][]"]'),j=o.find("#submit"),O={},k=r.val(),H=i.val(),G=A();O[HCaptchaGeneralObject.modeLive]=HCaptchaGeneralObject.siteKey,O[HCaptchaGeneralObject.modeTestPublisher]=HCaptchaGeneralObject.modeTestPublisherSiteKey,O[HCaptchaGeneralObject.modeTestEnterpriseSafeEndUser]=HCaptchaGeneralObject.modeTestEnterpriseSafeEndUserSiteKey,O[HCaptchaGeneralObject.modeTestEnterpriseBotDetected]=HCaptchaGeneralObject.modeTestEnterpriseBotDetectedSiteKey;var _=!1,S=!1,w=[];function T(t){var e={};return t.each((function(){var t=a(this),n=t.attr("name").replace(/hcaptcha_settings\[(.+)]/,"$1");e[n]=t.val()})),e}function A(){return T(y)}function E(){c.remove(),a('
').insertAfter(".hcaptcha-header-bar"),c=a(n)}function x(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(t=void 0===t?"":String(t),t=(t+="\n"+function(){for(var t=["recaptchacompat disabled","Missing sitekey - https://docs.hcaptcha.com/configuration#javascript-api"],e=[],a=0;a".concat(t,"

")}));c.html(n.join("")),a(document).trigger("wp-updates-notice-added"),a("html, body").animate({scrollTop:c.offset().top-hCaptchaSettingsBase.getStickyHeight()},1e3)}}function N(){x(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"","notice-success")}function B(){x(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"","notice-error")}function K(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},a=Object.assign({},hCaptcha.getParams(),t),n=v.prop("checked"),c="live"===f.val();for(var o in n&&c?s.attr("data-theme","custom"):s.attr("data-theme",p.val()),n&&"object"===e(t.theme)||!n&&"object"!==e(t.theme)?a.theme=t.theme:a.theme=hCaptcha.getParams().theme,hCaptcha.setParams(a),s.html(""),t)"object"!==e(t[o])&&s.attr("data-".concat(o),"".concat(t[o]));hCaptcha.bindEvents()}function L(t,a){var n=function(t){return t&&"object"===e(t)};return n(t)&&n(a)?(Object.keys(a).forEach((function(e){var c=t[e],o=a[e];Array.isArray(c)&&Array.isArray(o)?t[e]=c.concat(o):n(c)&&n(o)?t[e]=L(Object.assign({},c),o):t[e]=o})),t):a}function P(t){var a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";for(var n in t){var c=a?"".concat(a,"--").concat(n):n;if("object"===e(t[n])&&null!==t[n])P(t[n],c);else{var o=t[n],r=c.replace(/theme--/g,""),i="".concat(r,"=").concat(o),s=m.find('option[value*="'.concat(r,'="]'));1===s.length&&(s.attr("value",i),s.is(":selected")&&g.val(o))}}}function U(){var t,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},a=b.val().trim();a=a||null;try{t=JSON.parse(a)}catch(t){return b.css("background-color","#ffabaf"),j.attr("disabled",!0),void B("Bad JSON!")}t=L(t,e),b.val(JSON.stringify(t,null,2)),P(t),v.prop("checked")||(t={sitekey:r.val(),theme:p.val(),size:d.val(),hl:u.val()}),K(t)}function J(){r.val()===k&&i.val()===H?(_=!1,E(),j.attr("disabled",!1)):_||(_=!0,B(HCaptchaGeneralObject.checkConfigNotice),j.attr("disabled",!0))}function D(t){return t=t.replace(/(http|https):\/\//,""),"https://"+new URL("https://"+t).host}!function(){w=[];var t=console.log,e=console.warn,a=console.info,n=console.error,c=console.clear;console.log=function(e){w.push(["Console log:",arguments]),t.apply(console,arguments)},console.warn=function(t){w.push(["Console warn:",arguments]),e.apply(console,arguments)},console.info=function(t){w.push(["Console info:",arguments]),a.apply(console,arguments)},console.error=function(t){w.push(["Console error:",arguments]),n.apply(console,arguments)},console.clear=function(){w=[],c()}}(),document.addEventListener("hCaptchaLoaded",(function(){B()})),h.on("click",(function(t){t.preventDefault(),""!==a('.hcaptcha-general-sample-hcaptcha textarea[name="h-captcha-response"]').val()?function(){E(),j.attr("disabled",!0);var t={action:HCaptchaGeneralObject.checkConfigAction,nonce:HCaptchaGeneralObject.checkConfigNonce,mode:f.val(),siteKey:r.val(),secretKey:i.val(),"h-captcha-response":a('textarea[name="h-captcha-response"]').val(),"hcaptcha-widget-id":a('input[name="hcaptcha-widget-id"]').val()};a.post({url:HCaptchaGeneralObject.ajaxUrl,data:t,beforeSend:function(){return N(HCaptchaGeneralObject.checkingConfigMsg)}}).done((function(t){t.success?(k=r.val(),H=i.val(),G=T(y),S=!1,N(t.data),j.attr("disabled",!1)):B(t.data)})).fail((function(t){B(t.statusText)})).always((function(){K()}))}():kaggDialog.confirm({title:HCaptchaGeneralObject.completeHCaptchaTitle,content:HCaptchaGeneralObject.completeHCaptchaContent,type:"info",buttons:{ok:{text:HCaptchaGeneralObject.OKBtnText}},onAction:function(){return window.hCaptchaBindEvents()}})})),r.on("change",(function(t){K({sitekey:a(t.target).val()}),J()})),i.on("change",(function(){J()})),p.on("change",(function(t){K({theme:a(t.target).val()})})),d.on("change",(function(t){var e=a("#hcaptcha-invisible-notice"),n=a(t.target).val();"invisible"===n?e.show():e.hide(),K({size:n})})),u.on("change",(function(t){K({hl:a(t.target).val()})})),f.on("change",(function(t){var e=a(t.target).val();O.hasOwnProperty(e)&&(e===HCaptchaGeneralObject.modeLive?(r.attr("disabled",!1),i.attr("disabled",!1)):(r.attr("disabled",!0),i.attr("disabled",!0)),K({sitekey:O[e]}))})),v.on("change",(function(){U()})),b.on("blur",(function(){U()})),b.on("focus",(function(){b.css("background-color","unset"),j.attr("disabled",!1)})),y.on("change",(function(){!function(){var e={onload:"hCaptchaOnLoad",render:"explicit"};C.prop("checked")&&(e.recaptchacompat="off"),v.prop("checked")&&(e.custom="true");var a={asset_host:"assethost",endpoint:"endpoint",host:"host",image_host:"imghost",report_api:"reportapi",sentry:"sentry"},n=A();for(var c in a){var o=n[c].trim();o&&(e[a[c]]=encodeURIComponent(D(o)))}var r=n.api_host.trim();r=D(r=r||"js.hcaptcha.com")+"/1/api.js";var i=new URL(r);for(var h in e)i.searchParams.append(h,e[h]);document.getElementById("hcaptcha-api").remove(),delete t.g.hcaptcha,s.html("");var l=document.getElementsByTagName("head")[0],p=document.createElement("script");p.type="text/javascript",p.id="hcaptcha-api",p.src=i.href,l.appendChild(p)}(),JSON.stringify(A())===JSON.stringify(G)?(S=!1,E(),j.attr("disabled",!1)):S||(S=!0,B(HCaptchaGeneralObject.checkConfigNotice),j.attr("disabled",!0))})),a(".hcaptcha-general h3").on("click",(function(t){var e=a(t.currentTarget);e.toggleClass("closed");var n={action:HCaptchaGeneralObject.toggleSectionAction,nonce:HCaptchaGeneralObject.toggleSectionNonce,section:e.attr("class").replaceAll(/(hcaptcha-section-|closed)/g,"").trim(),status:!e.hasClass("closed")};a.post({url:HCaptchaGeneralObject.ajaxUrl,data:n}).done((function(t){t.success||B(t.data)})).fail((function(t){B(t.statusText)}))})),h.removeAttr("name"),l.removeAttr("name"),m.removeAttr("name"),g.removeAttr("name"),m.find("option").each((function(){var t=a(this);t.val().split("=")[1]||t.attr("disabled",!0)})),g.val(""),m.on("change",(function(){var t=a(this).find("option:selected").val().split("="),e=t[0],n=t[1];"palette--mode"===e?(g.attr("type","text"),g.val(n)):(g.val(n),g.attr("type","color"))})),g.on("change",(function(t){var e=a(t.target).val(),n=m.find("option:selected"),c=n.val().split("="),o=c[0],r=e;n.val(o+"="+e),U(r=(o="theme--"+c[0]).split("--").reverse().reduce((function(t,e){var a={};return a[e]=t,a}),r))}))};window.hCaptchaGeneral=a,jQuery(document).ready(a)})(); \ No newline at end of file diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-auto-verify.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-auto-verify.js index 8084c68a..0f90f0de 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-auto-verify.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-auto-verify.js @@ -1,4 +1,4 @@ -/* globals HCaptchaAutoVerifyObject */ +/* globals HCaptchaAutoVerifyObject, hCaptchaBindEvents */ document.addEventListener( 'DOMContentLoaded', () => { const formSelector = 'form'; @@ -55,9 +55,7 @@ document.addEventListener( 'DOMContentLoaded', () => { resultContainer.innerHTML = error; } - const currentHCaptcha = currentFormElement.querySelector( hCaptchaAjaxSelector ); - - window.hCaptchaReset( currentHCaptcha ); + hCaptchaBindEvents(); } ); return formElement; diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-auto-verify.min.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-auto-verify.min.js index a76844ad..a1cd3a86 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-auto-verify.min.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-auto-verify.min.js @@ -1 +1 @@ -(()=>{function t(r){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(r)}function r(){"use strict";/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */r=function(){return n};var e,n={},o=Object.prototype,i=o.hasOwnProperty,a=Object.defineProperty||function(t,r,e){t[r]=e.value},c="function"==typeof Symbol?Symbol:{},u=c.iterator||"@@iterator",f=c.asyncIterator||"@@asyncIterator",l=c.toStringTag||"@@toStringTag";function s(t,r,e){return Object.defineProperty(t,r,{value:e,enumerable:!0,configurable:!0,writable:!0}),t[r]}try{s({},"")}catch(e){s=function(t,r,e){return t[r]=e}}function h(t,r,e,n){var o=r&&r.prototype instanceof w?r:w,i=Object.create(o.prototype),c=new N(n||[]);return a(i,"_invoke",{value:T(t,e,c)}),i}function p(t,r,e){try{return{type:"normal",arg:t.call(r,e)}}catch(t){return{type:"throw",arg:t}}}n.wrap=h;var y="suspendedStart",v="suspendedYield",d="executing",m="completed",g={};function w(){}function b(){}function L(){}var x={};s(x,u,(function(){return this}));var E=Object.getPrototypeOf,S=E&&E(E(G([])));S&&S!==o&&i.call(S,u)&&(x=S);var O=L.prototype=w.prototype=Object.create(x);function j(t){["next","throw","return"].forEach((function(r){s(t,r,(function(t){return this._invoke(r,t)}))}))}function _(r,e){function n(o,a,c,u){var f=p(r[o],r,a);if("throw"!==f.type){var l=f.arg,s=l.value;return s&&"object"==t(s)&&i.call(s,"__await")?e.resolve(s.__await).then((function(t){n("next",t,c,u)}),(function(t){n("throw",t,c,u)})):e.resolve(s).then((function(t){l.value=t,c(l)}),(function(t){return n("throw",t,c,u)}))}u(f.arg)}var o;a(this,"_invoke",{value:function(t,r){function i(){return new e((function(e,o){n(t,r,e,o)}))}return o=o?o.then(i,i):i()}})}function T(t,r,n){var o=y;return function(i,a){if(o===d)throw Error("Generator is already running");if(o===m){if("throw"===i)throw a;return{value:e,done:!0}}for(n.method=i,n.arg=a;;){var c=n.delegate;if(c){var u=k(c,n);if(u){if(u===g)continue;return u}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(o===y)throw o=m,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);o=d;var f=p(t,r,n);if("normal"===f.type){if(o=n.done?m:v,f.arg===g)continue;return{value:f.arg,done:n.done}}"throw"===f.type&&(o=m,n.method="throw",n.arg=f.arg)}}}function k(t,r){var n=r.method,o=t.iterator[n];if(o===e)return r.delegate=null,"throw"===n&&t.iterator.return&&(r.method="return",r.arg=e,k(t,r),"throw"===r.method)||"return"!==n&&(r.method="throw",r.arg=new TypeError("The iterator does not provide a '"+n+"' method")),g;var i=p(o,t.iterator,r.arg);if("throw"===i.type)return r.method="throw",r.arg=i.arg,r.delegate=null,g;var a=i.arg;return a?a.done?(r[t.resultName]=a.value,r.next=t.nextLoc,"return"!==r.method&&(r.method="next",r.arg=e),r.delegate=null,g):a:(r.method="throw",r.arg=new TypeError("iterator result is not an object"),r.delegate=null,g)}function A(t){var r={tryLoc:t[0]};1 in t&&(r.catchLoc=t[1]),2 in t&&(r.finallyLoc=t[2],r.afterLoc=t[3]),this.tryEntries.push(r)}function P(t){var r=t.completion||{};r.type="normal",delete r.arg,t.completion=r}function N(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(A,this),this.reset(!0)}function G(r){if(r||""===r){var n=r[u];if(n)return n.call(r);if("function"==typeof r.next)return r;if(!isNaN(r.length)){var o=-1,a=function t(){for(;++o=0;--o){var a=this.tryEntries[o],c=a.completion;if("root"===a.tryLoc)return n("end");if(a.tryLoc<=this.prev){var u=i.call(a,"catchLoc"),f=i.call(a,"finallyLoc");if(u&&f){if(this.prev=0;--e){var n=this.tryEntries[e];if(n.tryLoc<=this.prev&&i.call(n,"finallyLoc")&&this.prev=0;--r){var e=this.tryEntries[r];if(e.finallyLoc===t)return this.complete(e.completion,e.afterLoc),P(e),g}},catch:function(t){for(var r=this.tryEntries.length-1;r>=0;--r){var e=this.tryEntries[r];if(e.tryLoc===t){var n=e.completion;if("throw"===n.type){var o=n.arg;P(e)}return o}}throw Error("illegal catch attempt")},delegateYield:function(t,r,n){return this.delegate={iterator:G(t),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=e),g}},n}function e(t,r,e,n,o,i,a){try{var c=t[i](a),u=c.value}catch(t){return void e(t)}c.done?r(u):Promise.resolve(u).then(n,o)}function n(t){return function(t){if(Array.isArray(t))return o(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,r){if(t){if("string"==typeof t)return o(t,r);var e={}.toString.call(t).slice(8,-1);return"Object"===e&&t.constructor&&(e=t.constructor.name),"Map"===e||"Set"===e?Array.from(t):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?o(t,r):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function o(t,r){(null==r||r>t.length)&&(r=t.length);for(var e=0,n=Array(r);e{function t(r){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(r)}function r(){"use strict";/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */r=function(){return n};var e,n={},o=Object.prototype,i=o.hasOwnProperty,a=Object.defineProperty||function(t,r,e){t[r]=e.value},c="function"==typeof Symbol?Symbol:{},u=c.iterator||"@@iterator",f=c.asyncIterator||"@@asyncIterator",l=c.toStringTag||"@@toStringTag";function s(t,r,e){return Object.defineProperty(t,r,{value:e,enumerable:!0,configurable:!0,writable:!0}),t[r]}try{s({},"")}catch(e){s=function(t,r,e){return t[r]=e}}function h(t,r,e,n){var o=r&&r.prototype instanceof w?r:w,i=Object.create(o.prototype),c=new N(n||[]);return a(i,"_invoke",{value:T(t,e,c)}),i}function p(t,r,e){try{return{type:"normal",arg:t.call(r,e)}}catch(t){return{type:"throw",arg:t}}}n.wrap=h;var y="suspendedStart",v="suspendedYield",d="executing",m="completed",g={};function w(){}function b(){}function L(){}var x={};s(x,u,(function(){return this}));var E=Object.getPrototypeOf,S=E&&E(E(G([])));S&&S!==o&&i.call(S,u)&&(x=S);var O=L.prototype=w.prototype=Object.create(x);function j(t){["next","throw","return"].forEach((function(r){s(t,r,(function(t){return this._invoke(r,t)}))}))}function _(r,e){function n(o,a,c,u){var f=p(r[o],r,a);if("throw"!==f.type){var l=f.arg,s=l.value;return s&&"object"==t(s)&&i.call(s,"__await")?e.resolve(s.__await).then((function(t){n("next",t,c,u)}),(function(t){n("throw",t,c,u)})):e.resolve(s).then((function(t){l.value=t,c(l)}),(function(t){return n("throw",t,c,u)}))}u(f.arg)}var o;a(this,"_invoke",{value:function(t,r){function i(){return new e((function(e,o){n(t,r,e,o)}))}return o=o?o.then(i,i):i()}})}function T(t,r,n){var o=y;return function(i,a){if(o===d)throw Error("Generator is already running");if(o===m){if("throw"===i)throw a;return{value:e,done:!0}}for(n.method=i,n.arg=a;;){var c=n.delegate;if(c){var u=k(c,n);if(u){if(u===g)continue;return u}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(o===y)throw o=m,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);o=d;var f=p(t,r,n);if("normal"===f.type){if(o=n.done?m:v,f.arg===g)continue;return{value:f.arg,done:n.done}}"throw"===f.type&&(o=m,n.method="throw",n.arg=f.arg)}}}function k(t,r){var n=r.method,o=t.iterator[n];if(o===e)return r.delegate=null,"throw"===n&&t.iterator.return&&(r.method="return",r.arg=e,k(t,r),"throw"===r.method)||"return"!==n&&(r.method="throw",r.arg=new TypeError("The iterator does not provide a '"+n+"' method")),g;var i=p(o,t.iterator,r.arg);if("throw"===i.type)return r.method="throw",r.arg=i.arg,r.delegate=null,g;var a=i.arg;return a?a.done?(r[t.resultName]=a.value,r.next=t.nextLoc,"return"!==r.method&&(r.method="next",r.arg=e),r.delegate=null,g):a:(r.method="throw",r.arg=new TypeError("iterator result is not an object"),r.delegate=null,g)}function A(t){var r={tryLoc:t[0]};1 in t&&(r.catchLoc=t[1]),2 in t&&(r.finallyLoc=t[2],r.afterLoc=t[3]),this.tryEntries.push(r)}function P(t){var r=t.completion||{};r.type="normal",delete r.arg,t.completion=r}function N(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(A,this),this.reset(!0)}function G(r){if(r||""===r){var n=r[u];if(n)return n.call(r);if("function"==typeof r.next)return r;if(!isNaN(r.length)){var o=-1,a=function t(){for(;++o=0;--o){var a=this.tryEntries[o],c=a.completion;if("root"===a.tryLoc)return n("end");if(a.tryLoc<=this.prev){var u=i.call(a,"catchLoc"),f=i.call(a,"finallyLoc");if(u&&f){if(this.prev=0;--e){var n=this.tryEntries[e];if(n.tryLoc<=this.prev&&i.call(n,"finallyLoc")&&this.prev=0;--r){var e=this.tryEntries[r];if(e.finallyLoc===t)return this.complete(e.completion,e.afterLoc),P(e),g}},catch:function(t){for(var r=this.tryEntries.length-1;r>=0;--r){var e=this.tryEntries[r];if(e.tryLoc===t){var n=e.completion;if("throw"===n.type){var o=n.arg;P(e)}return o}}throw Error("illegal catch attempt")},delegateYield:function(t,r,n){return this.delegate={iterator:G(t),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=e),g}},n}function e(t,r,e,n,o,i,a){try{var c=t[i](a),u=c.value}catch(t){return void e(t)}c.done?r(u):Promise.resolve(u).then(n,o)}function n(t){return function(t){if(Array.isArray(t))return o(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,r){if(t){if("string"==typeof t)return o(t,r);var e={}.toString.call(t).slice(8,-1);return"Object"===e&&t.constructor&&(e=t.constructor.name),"Map"===e||"Set"===e?Array.from(t):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?o(t,r):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function o(t,r){(null==r||r>t.length)&&(r=t.length);for(var e=0,n=Array(r);e { - form.addEventListener( 'wpcf7invalid', hCaptchaResetCF7, false ); - form.addEventListener( 'wpcf7spam', hCaptchaResetCF7, false ); - form.addEventListener( 'wpcf7mailsent', hCaptchaResetCF7, false ); - form.addEventListener( 'wpcf7mailfailed', hCaptchaResetCF7, false ); - form.addEventListener( 'wpcf7submit', hCaptchaResetCF7, false ); + form.addEventListener( 'wpcf7invalid', hCaptchaBindEvents, false ); + form.addEventListener( 'wpcf7spam', hCaptchaBindEvents, false ); + form.addEventListener( 'wpcf7mailsent', hCaptchaBindEvents, false ); + form.addEventListener( 'wpcf7mailfailed', hCaptchaBindEvents, false ); + form.addEventListener( 'wpcf7submit', hCaptchaBindEvents, false ); return form; } ); diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-cf7.min.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-cf7.min.js index 69b60846..9365378d 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-cf7.min.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-cf7.min.js @@ -1 +1 @@ -(()=>{function t(t){return function(t){if(Array.isArray(t))return e(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,n){if(t){if("string"==typeof t)return e(t,n);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?e(t,n):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function e(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n{function t(t){return function(t){if(Array.isArray(t))return n(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return n(t,e);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function n(t,n){(null==n||n>t.length)&&(n=t.length);for(var e=0,r=Array(n);e{jQuery(document).on("ajaxSuccess",(function(e,n,o){var t=new URLSearchParams(o.data);if("elementor_pro_forms_send_form"===t.get("action")){var a=t.get("form_id"),r=jQuery('input[name="form_id"][value="'+a+'"]').closest("form");window.hCaptchaReset(r[0])}}));var e=function(){"undefined"!=typeof elementorFrontend&&(wp.hooks.addFilter("hcaptcha.params","hcaptcha",(function(){var e,n;return null!==(e=null===(n=window)||void 0===n||null===(n=n.parent)||void 0===n||null===(n=n.HCaptchaMainObject)||void 0===n?void 0:n.params)&&void 0!==e?e:""})),elementorFrontend.hooks.addAction("frontend/element_ready/widget",(function(e){e[0].classList.contains("elementor-widget-form")&&hCaptchaBindEvents()})))};window.hCaptchaElementorPro=e,jQuery(document).ready(e)})(); \ No newline at end of file +(()=>{jQuery(document).on("ajaxSuccess",(function(n,e,t){"elementor_pro_forms_send_form"===new URLSearchParams(t.data).get("action")&&hCaptchaBindEvents()}));var n=function(){"undefined"!=typeof elementorFrontend&&(wp.hooks.addFilter("hcaptcha.params","hcaptcha",(function(){var n,e;return null!==(n=null===(e=window)||void 0===e||null===(e=e.parent)||void 0===e||null===(e=e.HCaptchaMainObject)||void 0===e?void 0:e.params)&&void 0!==n?n:""})),elementorFrontend.hooks.addAction("frontend/element_ready/widget",(function(n){n[0].classList.contains("elementor-widget-form")&&hCaptchaBindEvents()})))};window.hCaptchaElementorPro=n,jQuery(document).ready(n)})(); \ No newline at end of file diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-fluentform.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-fluentform.js index 7b93ac08..c7e8cfa1 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-fluentform.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-fluentform.js @@ -41,7 +41,7 @@ document.addEventListener( 'hCaptchaLoaded', function() { wrappingForm.appendChild( submitBtn ); submitBtn.before( hCaptcha ); hCaptcha.classList.remove( hCaptchaHiddenClass ); - hCaptcha.classList.add( hCaptchaClass ); + hCaptcha.querySelector( 'h-captcha' ).classList.add( hCaptchaClass ); hCaptcha.style.display = 'block'; window.hCaptchaBindEvents(); }; diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-fluentform.min.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-fluentform.min.js index bc2e79df..cfe64fbc 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-fluentform.min.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-fluentform.min.js @@ -1 +1 @@ -(()=>{function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(){"use strict";/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */e=function(){return n};var r,n={},o=Object.prototype,i=o.hasOwnProperty,a=Object.defineProperty||function(t,e,r){t[e]=r.value},c="function"==typeof Symbol?Symbol:{},u=c.iterator||"@@iterator",l=c.asyncIterator||"@@asyncIterator",f=c.toStringTag||"@@toStringTag";function s(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{s({},"")}catch(r){s=function(t,e,r){return t[e]=r}}function h(t,e,r,n){var o=e&&e.prototype instanceof b?e:b,i=Object.create(o.prototype),c=new P(n||[]);return a(i,"_invoke",{value:A(t,r,c)}),i}function y(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}n.wrap=h;var d="suspendedStart",p="suspendedYield",v="executing",m="completed",g={};function b(){}function w(){}function L(){}var E={};s(E,u,(function(){return this}));var S=Object.getPrototypeOf,x=S&&S(S(T([])));x&&x!==o&&i.call(x,u)&&(E=x);var _=L.prototype=b.prototype=Object.create(E);function O(t){["next","throw","return"].forEach((function(e){s(t,e,(function(t){return this._invoke(e,t)}))}))}function j(e,r){function n(o,a,c,u){var l=y(e[o],e,a);if("throw"!==l.type){var f=l.arg,s=f.value;return s&&"object"==t(s)&&i.call(s,"__await")?r.resolve(s.__await).then((function(t){n("next",t,c,u)}),(function(t){n("throw",t,c,u)})):r.resolve(s).then((function(t){f.value=t,c(f)}),(function(t){return n("throw",t,c,u)}))}u(l.arg)}var o;a(this,"_invoke",{value:function(t,e){function i(){return new r((function(r,o){n(t,e,r,o)}))}return o=o?o.then(i,i):i()}})}function A(t,e,n){var o=d;return function(i,a){if(o===v)throw Error("Generator is already running");if(o===m){if("throw"===i)throw a;return{value:r,done:!0}}for(n.method=i,n.arg=a;;){var c=n.delegate;if(c){var u=N(c,n);if(u){if(u===g)continue;return u}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(o===d)throw o=m,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);o=v;var l=y(t,e,n);if("normal"===l.type){if(o=n.done?m:p,l.arg===g)continue;return{value:l.arg,done:n.done}}"throw"===l.type&&(o=m,n.method="throw",n.arg=l.arg)}}}function N(t,e){var n=e.method,o=t.iterator[n];if(o===r)return e.delegate=null,"throw"===n&&t.iterator.return&&(e.method="return",e.arg=r,N(t,e),"throw"===e.method)||"return"!==n&&(e.method="throw",e.arg=new TypeError("The iterator does not provide a '"+n+"' method")),g;var i=y(o,t.iterator,e.arg);if("throw"===i.type)return e.method="throw",e.arg=i.arg,e.delegate=null,g;var a=i.arg;return a?a.done?(e[t.resultName]=a.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=r),e.delegate=null,g):a:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,g)}function k(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function q(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function P(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(k,this),this.reset(!0)}function T(e){if(e||""===e){var n=e[u];if(n)return n.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var o=-1,a=function t(){for(;++o=0;--o){var a=this.tryEntries[o],c=a.completion;if("root"===a.tryLoc)return n("end");if(a.tryLoc<=this.prev){var u=i.call(a,"catchLoc"),l=i.call(a,"finallyLoc");if(u&&l){if(this.prev=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&i.call(n,"finallyLoc")&&this.prev=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),q(r),g}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;q(r)}return o}}throw Error("illegal catch attempt")},delegateYield:function(t,e,n){return this.delegate={iterator:T(t),resultName:e,nextLoc:n},"next"===this.method&&(this.arg=r),g}},n}function r(t,e,r,n,o,i,a){try{var c=t[i](a),u=c.value}catch(t){return void r(t)}c.done?e(u):Promise.resolve(u).then(n,o)}function n(t){return function(){var e=this,n=arguments;return new Promise((function(o,i){var a=t.apply(e,n);function c(t){r(a,o,i,c,u,"next",t)}function u(t){r(a,o,i,c,u,"throw",t)}c(void 0)}))}}function o(t){return function(t){if(Array.isArray(t))return a(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||i(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function i(t,e){if(t){if("string"==typeof t)return a(t,e);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?a(t,e):void 0}}function a(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=Array(e);r=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,c=!0,u=!1;return{s:function(){r=r.call(t)},n:function(){var t=r.next();return c=t.done,t},e:function(t){u=!0,a=t},f:function(){try{c||null==r.return||r.return()}finally{if(u)throw a}}}}(t);try{for(f.s();!(e=f.n()).done;){var s=e.value;"attributes"===s.type&&"class"===s.attributeName&&s.oldValue&&s.oldValue.includes("q-is-inactive")&&(null!==s.target.querySelector(n)&&(o=void 0,a=void 0,c=void 0,u=void 0,l=void 0,o="h-captcha-hidden",a=document.getElementsByClassName(o)[0],c=r.querySelector(n),u=a.cloneNode(!0),(l=document.createElement("form")).setAttribute("method","POST"),c.parentNode.insertBefore(l,c),l.appendChild(c),c.before(u),u.classList.remove(o),u.classList.add("h-captcha"),u.style.display="block",window.hCaptchaBindEvents()))}}catch(t){f.e(t)}finally{f.f()}};if(!e()){var c=r.querySelectorAll(".q-form"),u={attributes:!0,attributeOldValue:!0};o(c).map((function(t){return new MutationObserver(a).observe(t,u),t}))}};var n=hcaptcha.render;hcaptcha.render=function(t,r){var o=window.hCaptcha.getParams();e()&&"invisible"===o.size&&(o.size="normal"),o.callback=r.callback,n(t,o)};var a,c=document.getElementsByTagName("script")[0],u=document.createElement("script");u.type="text/javascript",u.id=HCaptchaFluentFormObject.id,u.src=HCaptchaFluentFormObject.url,c.parentNode.insertBefore(u,c),(a=t+" .vff-footer",new Promise((function(t){if(document.querySelector(a))return t(document.querySelector(a));var e=new MutationObserver((function(){document.querySelector(a)&&(t(document.querySelector(a)),e.disconnect())}));e.observe(document.body,{childList:!0,subtree:!0})}))).then((function(){r()}))}));var c=window.fetch;window.fetch=n(e().mark((function t(){var r,n,o,i,a,u,l,f,s,h,y,d,p=arguments;return e().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:for(r=p.length,n=new Array(r),o=0;o{function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(){"use strict";/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */e=function(){return n};var r,n={},o=Object.prototype,i=o.hasOwnProperty,a=Object.defineProperty||function(t,e,r){t[e]=r.value},c="function"==typeof Symbol?Symbol:{},u=c.iterator||"@@iterator",l=c.asyncIterator||"@@asyncIterator",f=c.toStringTag||"@@toStringTag";function s(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{s({},"")}catch(r){s=function(t,e,r){return t[e]=r}}function h(t,e,r,n){var o=e&&e.prototype instanceof b?e:b,i=Object.create(o.prototype),c=new P(n||[]);return a(i,"_invoke",{value:A(t,r,c)}),i}function y(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}n.wrap=h;var d="suspendedStart",p="suspendedYield",v="executing",m="completed",g={};function b(){}function w(){}function L(){}var E={};s(E,u,(function(){return this}));var S=Object.getPrototypeOf,x=S&&S(S(T([])));x&&x!==o&&i.call(x,u)&&(E=x);var _=L.prototype=b.prototype=Object.create(E);function O(t){["next","throw","return"].forEach((function(e){s(t,e,(function(t){return this._invoke(e,t)}))}))}function j(e,r){function n(o,a,c,u){var l=y(e[o],e,a);if("throw"!==l.type){var f=l.arg,s=f.value;return s&&"object"==t(s)&&i.call(s,"__await")?r.resolve(s.__await).then((function(t){n("next",t,c,u)}),(function(t){n("throw",t,c,u)})):r.resolve(s).then((function(t){f.value=t,c(f)}),(function(t){return n("throw",t,c,u)}))}u(l.arg)}var o;a(this,"_invoke",{value:function(t,e){function i(){return new r((function(r,o){n(t,e,r,o)}))}return o=o?o.then(i,i):i()}})}function A(t,e,n){var o=d;return function(i,a){if(o===v)throw Error("Generator is already running");if(o===m){if("throw"===i)throw a;return{value:r,done:!0}}for(n.method=i,n.arg=a;;){var c=n.delegate;if(c){var u=N(c,n);if(u){if(u===g)continue;return u}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(o===d)throw o=m,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);o=v;var l=y(t,e,n);if("normal"===l.type){if(o=n.done?m:p,l.arg===g)continue;return{value:l.arg,done:n.done}}"throw"===l.type&&(o=m,n.method="throw",n.arg=l.arg)}}}function N(t,e){var n=e.method,o=t.iterator[n];if(o===r)return e.delegate=null,"throw"===n&&t.iterator.return&&(e.method="return",e.arg=r,N(t,e),"throw"===e.method)||"return"!==n&&(e.method="throw",e.arg=new TypeError("The iterator does not provide a '"+n+"' method")),g;var i=y(o,t.iterator,e.arg);if("throw"===i.type)return e.method="throw",e.arg=i.arg,e.delegate=null,g;var a=i.arg;return a?a.done?(e[t.resultName]=a.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=r),e.delegate=null,g):a:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,g)}function q(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function k(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function P(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(q,this),this.reset(!0)}function T(e){if(e||""===e){var n=e[u];if(n)return n.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var o=-1,a=function t(){for(;++o=0;--o){var a=this.tryEntries[o],c=a.completion;if("root"===a.tryLoc)return n("end");if(a.tryLoc<=this.prev){var u=i.call(a,"catchLoc"),l=i.call(a,"finallyLoc");if(u&&l){if(this.prev=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&i.call(n,"finallyLoc")&&this.prev=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),k(r),g}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;k(r)}return o}}throw Error("illegal catch attempt")},delegateYield:function(t,e,n){return this.delegate={iterator:T(t),resultName:e,nextLoc:n},"next"===this.method&&(this.arg=r),g}},n}function r(t,e,r,n,o,i,a){try{var c=t[i](a),u=c.value}catch(t){return void r(t)}c.done?e(u):Promise.resolve(u).then(n,o)}function n(t){return function(){var e=this,n=arguments;return new Promise((function(o,i){var a=t.apply(e,n);function c(t){r(a,o,i,c,u,"next",t)}function u(t){r(a,o,i,c,u,"throw",t)}c(void 0)}))}}function o(t){return function(t){if(Array.isArray(t))return a(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||i(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function i(t,e){if(t){if("string"==typeof t)return a(t,e);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?a(t,e):void 0}}function a(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=Array(e);r=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,c=!0,u=!1;return{s:function(){r=r.call(t)},n:function(){var t=r.next();return c=t.done,t},e:function(t){u=!0,a=t},f:function(){try{c||null==r.return||r.return()}finally{if(u)throw a}}}}(t);try{for(f.s();!(e=f.n()).done;){var s=e.value;"attributes"===s.type&&"class"===s.attributeName&&s.oldValue&&s.oldValue.includes("q-is-inactive")&&(null!==s.target.querySelector(n)&&(o=void 0,a=void 0,c=void 0,u=void 0,l=void 0,o="h-captcha-hidden",a=document.getElementsByClassName(o)[0],c=r.querySelector(n),u=a.cloneNode(!0),(l=document.createElement("form")).setAttribute("method","POST"),c.parentNode.insertBefore(l,c),l.appendChild(c),c.before(u),u.classList.remove(o),u.querySelector("h-captcha").classList.add("h-captcha"),u.style.display="block",window.hCaptchaBindEvents()))}}catch(t){f.e(t)}finally{f.f()}};if(!e()){var c=r.querySelectorAll(".q-form"),u={attributes:!0,attributeOldValue:!0};o(c).map((function(t){return new MutationObserver(a).observe(t,u),t}))}};var n=hcaptcha.render;hcaptcha.render=function(t,r){var o=window.hCaptcha.getParams();e()&&"invisible"===o.size&&(o.size="normal"),o.callback=r.callback,n(t,o)};var a,c=document.getElementsByTagName("script")[0],u=document.createElement("script");u.type="text/javascript",u.id=HCaptchaFluentFormObject.id,u.src=HCaptchaFluentFormObject.url,c.parentNode.insertBefore(u,c),(a=t+" .vff-footer",new Promise((function(t){if(document.querySelector(a))return t(document.querySelector(a));var e=new MutationObserver((function(){document.querySelector(a)&&(t(document.querySelector(a)),e.disconnect())}));e.observe(document.body,{childList:!0,subtree:!0})}))).then((function(){r()}))}));var c=window.fetch;window.fetch=n(e().mark((function t(){var r,n,o,i,a,u,l,f,s,h,y,d,p=arguments;return e().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:for(r=p.length,n=new Array(r),o=0;o { return submitButtonSelector + ', button.kb-forms-submit'; - } + }, ); wp.hooks.addFilter( @@ -17,18 +19,14 @@ wp.hooks.addFilter( } return isAjaxSubmitButton; - } + }, ); let originalStateChange; function modifyResponse() { if ( this.readyState === XMLHttpRequest.DONE ) { - [ ...document.getElementsByClassName( 'h-captcha' ) ].map( function( widget ) { - window.hCaptchaReset( widget.closest( 'form' ) ); - - return widget; - } ); + hCaptchaBindEvents(); } if ( originalStateChange ) { @@ -38,8 +36,13 @@ function modifyResponse() { const originalSend = XMLHttpRequest.prototype.send; -XMLHttpRequest.prototype.send = function() { +XMLHttpRequest.prototype.send = function( body ) { + if ( ! ( typeof body === 'string' && body.includes( 'h-captcha-response' ) ) ) { + return; + } + originalStateChange = this.onreadystatechange; this.onreadystatechange = modifyResponse; + originalSend.apply( this, arguments ); }; diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-kadence.min.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-kadence.min.js index 5436dc20..6fe3b4b0 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-kadence.min.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-kadence.min.js @@ -1 +1 @@ -(()=>{function t(t){return function(t){if(Array.isArray(t))return r(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return r(t,e);var n={}.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?r(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function r(t,r){(null==r||r>t.length)&&(r=t.length);for(var e=0,n=Array(r);e{var t;function a(){this.readyState===XMLHttpRequest.DONE&&hCaptchaBindEvents(),t&&t.apply(this,arguments)}wp.hooks.addFilter("hcaptcha.submitButtonSelector","hcaptcha",(function(t){return t+", button.kb-forms-submit"})),wp.hooks.addFilter("hcaptcha.ajaxSubmitButton","hcaptcha",(function(t,a){return!!a.classList.contains("kb-forms-submit")||t}));var e=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send=function(s){"string"==typeof s&&s.includes("h-captcha-response")&&(t=this.onreadystatechange,this.onreadystatechange=a,e.apply(this,arguments))}})(); \ No newline at end of file diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-quform.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-quform.js index 2b5c33d5..71eed4da 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-quform.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-quform.js @@ -1,4 +1,4 @@ -/* global jQuery */ +/* global jQuery, hCaptchaBindEvents */ jQuery( document ).on( 'ajaxSuccess', function( event, xhr, settings ) { const params = new URLSearchParams( settings.data ); @@ -7,21 +7,5 @@ jQuery( document ).on( 'ajaxSuccess', function( event, xhr, settings ) { return; } - let type; - - try { - const response = JSON.parse( xhr.responseText ); - type = response.type; - } catch ( e ) { - return; - } - - if ( type !== 'success' ) { - return; - } - - const formId = params.get( 'quform_form_id' ); - const form = jQuery( 'input[name="quform_form_id"][value="' + formId + '"]' ).closest( 'form' ); - - window.hCaptchaReset( form[ 0 ] ); + hCaptchaBindEvents(); } ); diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-quform.min.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-quform.min.js index 2d9b9835..800748ce 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-quform.min.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/hcaptcha-quform.min.js @@ -1 +1 @@ -jQuery(document).on("ajaxSuccess",(function(e,r,a){var t=new URLSearchParams(a.data);if("submit"===t.get("quform_submit")){var s;try{s=JSON.parse(r.responseText).type}catch(e){return}if("success"===s){var u=t.get("quform_form_id"),o=jQuery('input[name="quform_form_id"][value="'+u+'"]').closest("form");window.hCaptchaReset(o[0])}}})); \ No newline at end of file +jQuery(document).on("ajaxSuccess",(function(a,t,e){"submit"===new URLSearchParams(e.data).get("quform_submit")&&hCaptchaBindEvents()})); \ No newline at end of file diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/settings-base.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/settings-base.js index 97096c00..c697d8b6 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/settings-base.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/settings-base.js @@ -26,10 +26,14 @@ const settingsBase = ( function( $ ) { const msgSelector = '#hcaptcha-message'; let $message = $( msgSelector ); + /** + * Set header bar top position. + */ function setHeaderBarTop() { const isAbsolute = adminBar ? window.getComputedStyle( adminBar ).position === 'absolute' : true; const adminBarHeight = ( adminBar && ! isAbsolute ) ? adminBar.offsetHeight : 0; const tabsHeight = tabs ? tabs.offsetHeight : 0; + // The -1 to put header bar a bit under tabs. It is a precaution when heights are in fractional pixels. const totalHeight = adminBarHeight + tabsHeight - 1; @@ -53,23 +57,80 @@ const settingsBase = ( function( $ ) { return; } - const hash = window.location.hash; + const hash = window.location.hash.slice( 1 ); if ( ! hash ) { return; } - const $element = $( hash ); + // Try to find by id. + let element = document.getElementById( hash ); - if ( ! $element ) { + if ( ! element ) { + element = document.querySelector( `[name="hcaptcha_settings[${ hash }]"]` ); + } + + if ( ! element ) { return; } - if ( $element.is( ':checkbox' ) ) { - $element.closest( 'fieldset' ).addClass( 'blink' ); - } else { - $element.addClass( 'blink' ); + let target = element; + + if ( element?.type === 'checkbox' ) { + target = element.closest( 'fieldset' ); + } + + const table = target.closest( 'table' ); + let sectionHeader = null; + + let prev = table?.previousElementSibling; + + while ( prev ) { + if ( prev.tagName.toLowerCase() === 'h3' ) { + sectionHeader = prev; + + break; + } + + prev = prev.previousElementSibling; } + + if ( sectionHeader && sectionHeader.classList.contains( 'closed' ) ) { + setTimeout( function() { + sectionHeader.click(); + }, 100 ); + } + + setTimeout( function() { + target.classList.add( 'blink' ); + target.scrollIntoView( + { + behavior: 'smooth', + block: 'center', + }, + ); + }, 200 ); + } + + /** + * Setup lightbox. + */ + function setupLightBox() { + // Lightbox for images. + $( document ).on( 'click', '.hcaptcha-lightbox', function( e ) { + e.preventDefault(); + + const imgSrc = $( this ).attr( 'href' ); + + $( '#hcaptcha-lightbox-img' ).attr( 'src', imgSrc ); + $( '#hcaptcha-lightbox-modal' ).css( 'display', 'flex' ); + } ); + + // Close lightbox by click on the background. + $( '#hcaptcha-lightbox-modal' ).on( 'click', function() { + $( this ).css( 'display', 'none' ); + $( '#hcaptcha-lightbox-img' ).attr( 'src', '' ); + } ); } /** @@ -135,8 +196,11 @@ const settingsBase = ( function( $ ) { } ); setHeaderBarTop(); + highLight(); + setupLightBox(); + return app; }( jQuery ) ); diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/settings-base.min.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/settings-base.min.js index f63589be..44466b67 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/settings-base.min.js +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/settings-base.min.js @@ -1 +1 @@ -(()=>{var e=function(e){var t=document.querySelector("#wpadminbar"),o=document.querySelector(".hcaptcha-settings-tabs"),a=document.querySelector(".hcaptcha-header-bar"),s="#hcaptcha-message",i=e(s);function n(){var e=!t||"absolute"===window.getComputedStyle(t).position,s=t&&!e?t.offsetHeight:0,i=s+(o?o.offsetHeight:0)-1;o&&(o.style.top="".concat(s,"px")),a&&(a.style.top="".concat(i,"px"))}var c={getStickyHeight:function(){var e=!t||"absolute"===window.getComputedStyle(t).position;return(t&&!e?t.offsetHeight:0)+(o?o.offsetHeight:0)+(a?a.offsetHeight:0)},clearMessage:function(){i.remove(),e('
').insertAfter(".hcaptcha-header-bar"),i=e(s)},showMessage:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(t=void 0===t?"":String(t)){c.clearMessage(),i.addClass(o+" notice is-dismissible");var a=t.split("\n").map((function(e){return"

".concat(e,"

")}));i.html(a.join("")),e(document).trigger("wp-updates-notice-added"),e("html, body").animate({scrollTop:i.offset().top-c.getStickyHeight()},1e3)}},showSuccessMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";c.showMessage(e,"notice-success")},showErrorMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";c.showMessage(e,"notice-error")}};return e(".hcaptcha-header h2").siblings().appendTo(s),window.addEventListener("resize",(function(){n()})),n(),function(){var t=window.location.href,o=document.referrer;if(o&&o!==t){var a=window.location.hash;if(a){var s=e(a);s&&(s.is(":checkbox")?s.closest("fieldset").addClass("blink"):s.addClass("blink"))}}}(),c}(jQuery);window.hCaptchaSettingsBase=e,jQuery(document).ready(e)})(); \ No newline at end of file +(()=>{var e=function(e){var t=document.querySelector("#wpadminbar"),o=document.querySelector(".hcaptcha-settings-tabs"),i=document.querySelector(".hcaptcha-header-bar"),a="#hcaptcha-message",c=e(a);function n(){var e=!t||"absolute"===window.getComputedStyle(t).position,a=t&&!e?t.offsetHeight:0,c=a+(o?o.offsetHeight:0)-1;o&&(o.style.top="".concat(a,"px")),i&&(i.style.top="".concat(c,"px"))}var s={getStickyHeight:function(){var e=!t||"absolute"===window.getComputedStyle(t).position;return(t&&!e?t.offsetHeight:0)+(o?o.offsetHeight:0)+(i?i.offsetHeight:0)},clearMessage:function(){c.remove(),e('
').insertAfter(".hcaptcha-header-bar"),c=e(a)},showMessage:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(t=void 0===t?"":String(t)){s.clearMessage(),c.addClass(o+" notice is-dismissible");var i=t.split("\n").map((function(e){return"

".concat(e,"

")}));c.html(i.join("")),e(document).trigger("wp-updates-notice-added"),e("html, body").animate({scrollTop:c.offset().top-s.getStickyHeight()},1e3)}},showSuccessMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";s.showMessage(e,"notice-success")},showErrorMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";s.showMessage(e,"notice-error")}};return e(".hcaptcha-header h2").siblings().appendTo(a),window.addEventListener("resize",(function(){n()})),n(),function(){var e,t=window.location.href,o=document.referrer;if(o&&o!==t){var i=window.location.hash.slice(1);if(i){var a=document.getElementById(i);if(a||(a=document.querySelector('[name="hcaptcha_settings['.concat(i,']"]'))),a){var c=a;"checkbox"===(null===(e=a)||void 0===e?void 0:e.type)&&(c=a.closest("fieldset"));for(var n=c.closest("table"),s=null,r=null==n?void 0:n.previousElementSibling;r;){if("h3"===r.tagName.toLowerCase()){s=r;break}r=r.previousElementSibling}s&&s.classList.contains("closed")&&setTimeout((function(){s.click()}),100),setTimeout((function(){c.classList.add("blink"),c.scrollIntoView({behavior:"smooth",block:"center"})}),200)}}}}(),e(document).on("click",".hcaptcha-lightbox",(function(t){t.preventDefault();var o=e(this).attr("href");e("#hcaptcha-lightbox-img").attr("src",o),e("#hcaptcha-lightbox-modal").css("display","flex")})),e("#hcaptcha-lightbox-modal").on("click",(function(){e(this).css("display","none"),e("#hcaptcha-lightbox-img").attr("src","")})),s}(jQuery);window.hCaptchaSettingsBase=e,jQuery(document).ready(e)})(); \ No newline at end of file diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/whats-new.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/whats-new.js new file mode 100644 index 00000000..cc0ae774 --- /dev/null +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/whats-new.js @@ -0,0 +1,100 @@ +/* global jQuery, HCaptchaWhatsNewObject */ + +/** + * @typedef {Object} HCaptchaWhatsNewObject + * @property {string} ajaxUrl The URL to send AJAX requests to. + * @property {string} markShownAction The action to mark the popup as shown. + * @property {string} markShownNonce The nonce for the mark the popup as shown action. + */ + +/** + * What's New logic. + * + * @param {Object} $ jQuery instance. + */ +const whatsNew = ( $ ) => { + /** + * @typedef {jQuery} jQuery + * @property {Function} fadeOut Function to fade out the modal. + */ + + /** @type {jQuery} */ + const $modal = $( '#hcaptcha-whats-new-modal' ); + + if ( ! $modal.length ) { + return; + } + + if ( $modal.css( 'display' ) === 'flex' ) { + document.body.style.overflow = 'hidden'; + } + + function done() { + closePopup(); + markShown(); + } + + function closePopup() { + $modal.fadeOut( 200, function() { + document.body.style.overflow = ''; + $( this ).css( 'display', 'none' ); + } ); + } + + function markShown() { + const data = { + action: HCaptchaWhatsNewObject.markShownAction, + nonce: HCaptchaWhatsNewObject.markShownNonce, + version: $( '#hcaptcha-whats-new-version' ).text(), + }; + + $.post( { + url: HCaptchaWhatsNewObject.ajaxUrl, + data, + } ); + } + + $( document ).on( 'click', '#hcaptcha-whats-new-close, .hcaptcha-whats-new-modal-bg', function() { + done(); + } ); + + $( document ).on( 'keydown', function( e ) { + if ( e.key !== 'Escape' ) { + return; + } + + done(); + } ); + + $( document ).on( 'click', '.hcaptcha-whats-new-button a', function( e ) { + e.preventDefault(); + + const $btn = $( this ); + const href = $btn.attr( 'href' ); + const data = { + action: HCaptchaWhatsNewObject.markShownAction, + nonce: HCaptchaWhatsNewObject.markShownNonce, + version: $( '#hcaptcha-whats-new-version' ).text(), + }; + + $.post( { + url: HCaptchaWhatsNewObject.ajaxUrl, + data, + success() { + window.open( href, '_blank' ); + }, + } ); + } ); + + $( document ).on( 'click', '#hcaptcha-whats-new-link', function( e ) { + e.preventDefault(); + + document.body.style.overflow = 'hidden'; + $modal.fadeIn( 200 ).show().css( 'display', 'flex' ); + + // Some hack. Without it, background filter is not applied. + $modal.find( '.hcaptcha-whats-new-modal-bg' ).hide().show( 200 ); + } ); +}; + +jQuery( document ).ready( whatsNew ); diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/whats-new.min.js b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/whats-new.min.js new file mode 100644 index 00000000..5892ea3c --- /dev/null +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/assets/js/whats-new.min.js @@ -0,0 +1 @@ +jQuery(document).ready((function(t){var a=t("#hcaptcha-whats-new-modal");function n(){var n;a.fadeOut(200,(function(){document.body.style.overflow="",t(this).css("display","none")})),n={action:HCaptchaWhatsNewObject.markShownAction,nonce:HCaptchaWhatsNewObject.markShownNonce,version:t("#hcaptcha-whats-new-version").text()},t.post({url:HCaptchaWhatsNewObject.ajaxUrl,data:n})}a.length&&("flex"===a.css("display")&&(document.body.style.overflow="hidden"),t(document).on("click","#hcaptcha-whats-new-close, .hcaptcha-whats-new-modal-bg",(function(){n()})),t(document).on("keydown",(function(t){"Escape"===t.key&&n()})),t(document).on("click",".hcaptcha-whats-new-button a",(function(a){a.preventDefault();var n=t(this).attr("href"),e={action:HCaptchaWhatsNewObject.markShownAction,nonce:HCaptchaWhatsNewObject.markShownNonce,version:t("#hcaptcha-whats-new-version").text()};t.post({url:HCaptchaWhatsNewObject.ajaxUrl,data:e,success:function(){window.open(n,"_blank")}})})),t(document).on("click","#hcaptcha-whats-new-link",(function(t){t.preventDefault(),document.body.style.overflow="hidden",a.fadeIn(200).show().css("display","flex"),a.find(".hcaptcha-whats-new-modal-bg").hide().show(200)})))})); \ No newline at end of file diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/changelog.txt b/wp-content/plugins/hcaptcha-for-forms-and-more/changelog.txt index addde620..70d01841 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/changelog.txt +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/changelog.txt @@ -1,3 +1,45 @@ += 4.13.0 = +* Added site content protection. +* Added "Remove Data on Uninstall" option to improve user privacy. +* Added "What's New" popup on admin pages. +* Added Events Manager integration. +* Added Password Protected integration. +* Added compatibility with Formidable Forms Pro. +* Added support for Avada multistep forms. +* Improved support of the device color scheme. +* Fixed enqueuing hCaptcha scripts on every page when Fluent Forms integration is on. +* Fixed warning in with auto-verify forms, including Brevo. +* Fixed enqueuing script with Fluent Conversational Form. +* Fixed showing hCaptcha with the latest Fluent Forms version. +* Fixed Conversational forms support with the latest Fluent Forms version. +* Fixed race condition when highlighting admin elements. +* Tested with WordPress 6.7. +* Tested with WooCommerce 9.8. + += 4.12.0 = +* Added 'hcap_print_hcaptcha_scripts' filter. +* Added the ability to filter printing of dsn-prefetch link and inline styles. +* Added auto-forcing and prevent delaying of hCaptcha on login forms for 1Password compatibility. +* Added auto-forcing and prevent delaying of hCaptcha on login forms for LastPass compatibility. +* Added Privacy Policy to WordPress admin Privacy > Policy Guide page. +* Improved API script delay behavior. Now, scripts are loaded after a delay interval or any user interaction, whichever happens first. +* Improved scrolling behavior to highlighted elements in admin. +* Fixed broken submit button with ACF, Gravity Forms and input to button snippet. +* Fixed printing hCaptcha scripts on Essential Addons preview page. +* Fixed hCaptcha layout on wpDiscuz forms. +* Fixed race condition with Pro invisible hCaptcha. +* Fixed scroll on a page load with a Kadence form. +* Fixed scroll on a page load with a Kadence Advanced form. +* Fixed scrolling and focusing after submitting with CF7 form. +* Fixed scrolling and focusing after submitting with a Forminator form. +* Fixed scrolling and focusing after submitting with a Quform form. +* Fixed scrolling and focusing after submitting with an Elementor form. +* Fixed scrolling and focusing after submitting with Autoverify in Ajax. +* Fixed scrolling and focusing before checking the Site Config on the General page. +* Fixed fatal error on claiming action during migration to 4.11.0. +* Fixed fatal error when migrating to 4.0.0 via cron. +* Fixed WordPress database error on migrating to 4.11.0 in a rare case. + = 4.11.0 = * Added Really Simple CAPTCHA plugin integration. * Added compatibility with the UsersWP plugin v1.2.28. diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/composer.json b/wp-content/plugins/hcaptcha-for-forms-and-more/composer.json index 6645518d..88273ced 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/composer.json +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/composer.json @@ -31,19 +31,19 @@ }, "require": { "ext-json": "*", - "matthiasmullie/minify": "^1.3.73", + "matthiasmullie/minify": "^1.3.72", "woocommerce/action-scheduler": "^3.9.2" }, "require-dev": { "roave/security-advisories": "dev-latest", "antecedent/patchwork": "^2.2.1", - "10up/wp_mock": "0.4.2 - 1.0.1", + "10up/wp_mock": "0.4.2 - 1.1.0", "codeception/codeception": "4.2.2", - "codeception/module-db": "1.2.0 - 3.1.4", - "codeception/module-webdriver": "1.4.1 - 4.0.2", + "codeception/module-db": "1.2.0 - 3.2.2", + "codeception/module-webdriver": "1.4.1 - 4.0.3", "lucatume/function-mocker": "^2.0.0", - "lucatume/wp-browser": "3.7.11 - 4.4.1", - "squizlabs/php_codesniffer": "^3.11.2", + "lucatume/wp-browser": "3.7.11 - 4.4.2", + "squizlabs/php_codesniffer": "^3.12.0", "phpcompatibility/php-compatibility": "^9.3.5", "phpcompatibility/phpcompatibility-wp": "^2.1.6", "wp-coding-standards/wpcs": "^3.1.0" diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/hcaptcha.php b/wp-content/plugins/hcaptcha-for-forms-and-more/hcaptcha.php index d73f2c6e..a707b7ae 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/hcaptcha.php +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/hcaptcha.php @@ -10,7 +10,7 @@ * Plugin Name: hCaptcha for WP * Plugin URI: https://www.hcaptcha.com/ * Description: hCaptcha keeps out bots and spam while putting privacy first. It is a drop-in replacement for reCAPTCHA. - * Version: 4.11.0 + * Version: 4.13.0 * Requires at least: 5.3 * Requires PHP: 7.2 * Author: hCaptcha @@ -21,7 +21,7 @@ * Domain Path: /languages/ * * WC requires at least: 3.0 - * WC tested up to: 9.7 + * WC tested up to: 9.8 */ // phpcs:ignore Generic.Commenting.DocComment.MissingShort @@ -39,7 +39,7 @@ /** * Plugin version. */ -const HCAPTCHA_VERSION = '4.11.0'; +const HCAPTCHA_VERSION = '4.13.0'; /** * Path to the plugin dir. diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/readme.txt b/wp-content/plugins/hcaptcha-for-forms-and-more/readme.txt index fe9b9d26..dbaf0dd6 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/readme.txt +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/readme.txt @@ -2,9 +2,9 @@ Contributors: hcaptcha, kaggdesign Tags: captcha, hcaptcha, antispam, abuse, protect Requires at least: 5.3 -Tested up to: 6.7 +Tested up to: 6.8 Requires PHP: 7.2 -Stable tag: 4.11.0 +Stable tag: 4.13.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -32,6 +32,7 @@ hCaptcha for WP [makes security easy](https://www.hcaptcha.com/integration-hcapt * **Detailed Analytics:** Get detailed analytics on hCaptcha events and form submissions. * **Pro and Enterprise:** Supports Pro and Enterprise versions of hCaptcha. * **No Challenge Modes:** 99.9% passive and passive modes in Pro and Enterprise versions reduce user friction. +* **Protect Site Content:** Protects selected site URLs from bots with hCaptcha. Works best with Pro 99.9% passive mode. * **Logged-in Users:** Optionally turn off hCaptcha for logged-in users. * **Delayed API Loading:** Load the hCaptcha API instantly or on user interaction for zero page load impact. * **Allowlist IPs:** Allowlist certain IPs to skip hCaptcha verification. @@ -70,23 +71,24 @@ To use this plugin, install it and enter your sitekey and secret in the Settings 1. Login page with hCaptcha widget. 2. Login page with hCaptcha challenge. -3. WooCommerce Login/Register page. -4. Contact Form 7 with hCaptcha. -5. Contact Form 7 live form in the admin editor. -6. Elementor Pro Form. -7. Elementor Pro Form in admin editor. -8. General settings page. -9. Integrations settings page. -10. Activating plugin from the Integration settings page. -11. (Optional) Local Forms statistics. -12. (Optional) Local Events statistics. +3. Protected content. +4. WooCommerce Login/Register page. +5. Contact Form 7 with hCaptcha. +6. Contact Form 7 live form in the admin editor. +7. Elementor Pro Form. +8. Elementor Pro Form in admin editor. +9. General settings page. +10. Integrations' settings page. +11. Activating plugin from the Integration settings page. +12. (Optional) Local Forms statistics. +13. (Optional) Local Events statistics. == Installation == Sign up at [hCaptcha.com](https://www.hcaptcha.com/) to get your sitekey and secret, then: 1. Install hCaptcha either via the WordPress.org plugin repository (best) or by uploading the files to your server. ([Upload instructions](https://www.wpbeginner.com/beginners-guide/step-by-step-guide-to-install-a-wordpress-plugin-for-beginners/)) -2. Activate the hCaptcha plugin on the 'Plugins' admin page +2. Activate the hCaptcha plugin on Plugins admin page 3. Enter your site key and secret on the Settings→hCaptcha→General page 4. Enable desired Integrations on the Settings→hCaptcha→Integrations page @@ -193,22 +195,24 @@ Arbitrary forms can also be verified in ajax via the `ajax` argument. There is n [hcaptcha ajax="true"] ` -= How to block hCaptcha on a specific page? = += How to block hCaptcha entirely on a specific page? = hCaptcha starts early, so you cannot use standard WP functions to determine the page. For instance, to block it on `my-account` page, add the following code to your plugin's (or mu-plugin's) main file. This code won't work being added to a theme's functions.php file. ` /** -* Filter hCaptcha activation flag. -* -* @param bool $activate Activate flag. -* -* @return bool -*/ -function my_hcap_activate( $activate ) { + * Filter hCaptcha activation flag. + * + * @param bool|mixed $activate Activate flag. + * + * @return bool + */ +function my_hcap_activate( $activate ): bool { + $status = (bool) $status; + $url = isset( $_SERVER['REQUEST_URI'] ) ? - filter_var( wp_unslash( $_SERVER['REQUEST_URI'] ), FILTER_SANITIZE_FULL_SPECIAL_CHARS ) : - ''; + filter_var( wp_unslash( $_SERVER['REQUEST_URI'] ), FILTER_SANITIZE_FULL_SPECIAL_CHARS ) : + ''; if ( '/my-account/' === $url ) { return false; @@ -220,6 +224,53 @@ function my_hcap_activate( $activate ) { add_filter( 'hcap_activate', 'my_hcap_activate' ); ` += How do I block hCaptcha scripts everywhere except on a specific page? = + +An an example, to block hCaptcha scripts everywhere except on the `contact` page: + +` +/** + * Filter hCaptcha print hCaptcha scripts status. + * + * @param bool|mixed $status Current print status. + * + * @return bool + */ +function my_hcap_print_hcaptcha_scripts( $status ): bool { + if ( is_page( 'contact' ) ) { + return (bool) $status; + } + + return false; +} + +add_filter( 'hcap_print_hcaptcha_scripts', 'my_hcap_print_hcaptcha_scripts' ); +` + += How do I block hCaptcha scripts everywhere except on a specific page? = + +An an example, to block hCaptcha scripts everywhere except on the `contact` page: + +` +/** + * Block inline styles. + * + * @return void + */ +function hcap_block_inline_styles() { + if ( is_page( 'contact' ) ) { + return; + } + + $hcaptcha = hcaptcha(); + + remove_action( 'wp_head', [ $hcaptcha, 'print_inline_styles' ] ); + remove_filter( 'wp_resource_hints', [ $hcaptcha, 'prefetch_hcaptcha_dns' ] ); +} + +add_action( 'wp_head', 'hcap_block_inline_styles', 0 ); +` + = Skipping hCaptcha verification on a specific form = The plugin has a filter to skip adding and verifying hCaptcha on a specific form. The filter receives three parameters: current protection status ('true' by default), source and form_id. @@ -274,6 +325,10 @@ Elementor Pro `$source: 'elementor-pro/elementor-pro.php'` `$form_id: Form ID set for the form Content->Additional Options or 'login'` +Events Manager +`$source: 'events-manager/events-manager.php'` +`$form_id: event_id` + Jetpack `$source: 'jetpack/jetpack.php'` `$form_id: 'contact_$form_hash'` @@ -310,6 +365,10 @@ Passster `$source: 'content-protector/content-protector.php'` `$form_id: area_id` +Password Protected +`$source: 'password-protected/password-protected.php'` +`$form_id: 'protect'` + Profile Builder `$source: 'profile-builder/index.php'` `$form_id: 'login', 'lost_password' or 'register'` @@ -356,7 +415,7 @@ WPForms wpForo `$source: 'wpforo/wpforo.php'` -`$form_id: 'new_topic' for new topic form and topicid for reply form. Topicid can be found in HTML code searching for 'data-topicid' in Elements.` +`$form_id: 'new_topic' for a new topic form and topicid for a reply form. Topicid can be found in HTML code searching for 'data-topicid' in Elements.` Wordfence Login Security `$source: 'wordfence-login-security/wordfence-login-security.php'` @@ -380,25 +439,27 @@ Below is an example of how to skip the hCaptcha widget on a Gravity Form with id /** * Filters the protection status of a form. * - * @param string $value The protection status of a form. - * @param string[] $source Plugin(s) serving the form. - * @param int|string $form_id Form id. + * @param string|mixed $value The protection status of a form. + * @param string[] $source Plugin(s) serving the form. + * @param int|string $form_id Form id. * * @return bool */ -function hcap_protect_form_filter( $value, $source, $form_id ) { - if ( ! in_array( 'gravityforms/gravityforms.php', $source, true ) ) { - // The form is not sourced by Gravity Forms plugin. - return $value; - } +function hcap_protect_form_filter( $value, $source, $form_id ): bool { + $value = (bool) $value; - if ( 1 !== (int) $form_id ) { - // The form has id !== 1. - return $value; - } + if ( ! in_array( 'gravityforms/gravityforms.php', $source, true ) ) { + // The form is not sourced by Gravity Forms plugin. + return $value; + } - // Turn off protection for a Gravity form with id = 1. - return false; + if ( 1 !== (int) $form_id ) { + // The form has id !== 1. + return $value; + } + + // Turn off protection for a Gravity form with id = 1. + return false; } add_filter( 'hcap_protect_form', 'hcap_protect_form_filter', 10, 3 ); @@ -412,16 +473,16 @@ To load the hCaptcha widget instantly, you can use the following filter: ` /** -* Filters delay time for hCaptcha API script. -* -* Any negative value will prevent the API script from loading at all, -* until user interaction: mouseenter, click, scroll or touch. -* This significantly improves Google Pagespeed Insights score. -* -* @param int $delay Number of milliseconds to delay hCaptcha API script. -* Any negative value means delay until user interaction. -*/ -function my_hcap_delay_api( $delay ) { + * Filters delay time for hCaptcha API script. + * + * Any negative value will prevent the API script from loading at all, + * until user interaction: mouseenter, click, scroll or touch. + * This significantly improves Google Pagespeed Insights score. + * + * @param int|mixed $delay Number of milliseconds to delay hCaptcha API script. + * Any negative value means delay until user interaction. + */ +function my_hcap_delay_api( $delay ): int { return 0; } @@ -434,11 +495,13 @@ hCaptcha defaults to using the user's language as reported by the browser. Howev ` /** -* Filters hCaptcha language. -* -* @param string $language Language. -*/ -function my_hcap_language( $language ) { + * Filters hCaptcha language. + * + * @param string|mixed $language Language. + */ +function my_hcap_language( $language ): string { + $language = (string) $language; + // Detect page language and return it. $page_language = 'some lang'; // Detection depends on the multilingual plugin used. @@ -457,12 +520,13 @@ You can use the following filter. It should be added to your plugin's (or mu-plu * Filter user IP to check if it is allowlisted. * For allowlisted IPs, hCaptcha will not be shown. * - * @param bool $allowlisted Whether IP is allowlisted. - * @param string $ip IP. + * @param bool|mixed $allowlisted Whether IP is allowlisted. + * @param string $ip IP. * * @return bool */ -function my_hcap_allowlist_ip( $allowlisted, $ip ) { +function my_hcap_allowlist_ip( $allowlisted, $ip ): bool { + $allowlisted = (bool) $allowlisted; // Allowlist local IPs. if ( false === $ip ) { @@ -492,9 +556,11 @@ To do this, use the following filter to your plugin's (or mu-plugin's) main file /** * Filter the settings system initialization arguments. * - * @param array $args Settings system initialization arguments. + * @param array|mixed $args Settings system initialization arguments. */ -function hcap_settings_init_args_filter( $args ) { +function hcap_settings_init_args_filter( $args ): array { + $args = (array) $args; + $args['mode'] = 'tabs'; return $args; @@ -536,9 +602,12 @@ For more details, please see the hCaptcha privacy policy at: If you enable the optional plugin-local statistics feature, the following additional data will be recorded to your database: * counts of challenge verifications per form -* **only if you enable this optional feature:** the IP addresses challenged on each form +* **only if you enable this optional feature: **the IP address challenged on each form +* **only if you enable this optional feature: **the USer Agent challenged on each form -We recommend leaving IP recording off, which will make these statistics fully anonymous. +You can collect data anonymously but still distinguish sources. The hashed IP address and User Agent will be saved. + +We recommend leaving IP and User Agent recording off, which will make these statistics fully anonymous. If this feature is enabled, anonymized statistics on your plugin configuration, not including any end user data, will also be sent to us. This lets us see which modules and features are being used and prioritize development for them accordingly. @@ -548,7 +617,7 @@ If this feature is enabled, anonymized statistics on your plugin configuration, * ACF Extended Form * Affiliates Login and Register Forms * Asgaros Forum New Topic and Reply Form -* Avada Form +* Avada standard and multistep Forms * Back In Stock Notifier Form * bbPress New Topic, Reply, Login, Register and Lost Password Forms * Beaver Builder Contact and Login Forms @@ -566,6 +635,7 @@ If this feature is enabled, anonymized statistics on your plugin configuration, * Elementor Pro Form and Login Form * Essential Addons for Elementor Login and Register Forms * Essential Blocks Form +* Events Manager Booking Form * Extra Comment, Contact, Email Optin and Login Forms * Fluent Forms, including Login Form * Forminator Forms @@ -585,6 +655,7 @@ If this feature is enabled, anonymized statistics on your plugin configuration, * Otter Blocks Forms * Paid Memberships Pro Checkout and Login Forms * Passster Protection Form +* Password Protected Form * Profile Builder Login, Recover Password, and Register Forms * Really Simple CAPTCHA * Quform Forms @@ -628,6 +699,48 @@ Instructions for popular native integrations are below: == Changelog == += 4.13.0 = +* Added site content protection. +* Added "Remove Data on Uninstall" option to improve user privacy. +* Added "What's New" popup on admin pages. +* Added Events Manager integration. +* Added Password Protected integration. +* Added compatibility with Formidable Forms Pro. +* Added support for Avada multistep forms. +* Improved support of the device color scheme. +* Fixed enqueuing hCaptcha scripts on every page when Fluent Forms integration is on. +* Fixed warning in with auto-verify forms, including Brevo. +* Fixed enqueuing script with Fluent Conversational Form. +* Fixed showing hCaptcha with the latest Fluent Forms version. +* Fixed Conversational forms support with the latest Fluent Forms version. +* Fixed race condition when highlighting admin elements. +* Tested with WordPress 6.7. +* Tested with WooCommerce 9.8. + += 4.12.0 = +* Added 'hcap_print_hcaptcha_scripts' filter. +* Added the ability to filter printing of dsn-prefetch link and inline styles. +* Added auto-forcing and prevent delaying of hCaptcha on login forms for 1Password compatibility. +* Added auto-forcing and prevent delaying of hCaptcha on login forms for LastPass compatibility. +* Added Privacy Policy to WordPress admin Privacy > Policy Guide page. +* Improved API script delay behavior. Now, scripts are loaded after a delay interval or any user interaction, whichever happens first. +* Improved scrolling behavior to highlighted elements in admin. +* Fixed broken submit button with ACF, Gravity Forms and input to button snippet. +* Fixed printing hCaptcha scripts on Essential Addons preview page. +* Fixed hCaptcha layout on wpDiscuz forms. +* Fixed race condition with Pro invisible hCaptcha. +* Fixed scroll on a page load with a Kadence form. +* Fixed scroll on a page load with a Kadence Advanced form. +* Fixed scrolling and focusing after submitting with CF7 form. +* Fixed scrolling and focusing after submitting with a Forminator form. +* Fixed scrolling and focusing after submitting with a Quform form. +* Fixed scrolling and focusing after submitting with an Elementor form. +* Fixed scrolling and focusing after submitting with Autoverify in Ajax. +* Fixed scrolling and focusing before checking the Site Config on the General page. +* Fixed fatal error on claiming action during migration to 4.11.0. +* Fixed fatal error when migrating to 4.0.0 via cron. +* Fixed WordPress database error on migrating to 4.11.0 in a rare case. + = 4.11.0 = * Added Really Simple CAPTCHA plugin integration. * Added compatibility with the UsersWP plugin v1.2.28. diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Abstracts/LoginBase.php b/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Abstracts/LoginBase.php index aeeac438..10026dd9 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Abstracts/LoginBase.php +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Abstracts/LoginBase.php @@ -80,6 +80,8 @@ protected function init_hooks(): void { add_action( 'wp_login', [ $this, 'login' ], 10, 2 ); add_action( 'wp_login_failed', [ $this, 'login_failed' ] ); + + add_action( 'hcap_delay_api', [ $this, 'delay_api' ], 0 ); } /** @@ -334,4 +336,18 @@ public function login_base_verify( $user, string $password ) { return new WP_Error( $code, $error_message, 400 ); } + + /** + * Filters delay time for the hCaptcha API script. + * + * @param int|mixed $delay Number of milliseconds to delay hCaptcha API script. + * Any negative value means delay until user interaction. + * + * @return int + * @noinspection PhpUnusedParameterInspection + */ + public function delay_api( $delay ): int { + // Do not delay API request on login forms for compatibility with password managers. + return 0; + } } diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Admin/Notifications.php b/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Admin/Notifications.php index 7cd17b6a..d2ccc102 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Admin/Notifications.php +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Admin/Notifications.php @@ -7,17 +7,12 @@ namespace HCaptcha\Admin; -use HCaptcha\Settings\EventsPage; -use HCaptcha\Settings\FormsPage; -use HCaptcha\Settings\General; -use HCaptcha\Settings\Integrations; - /** * Class Notifications. * * Show notifications in the admin. */ -class Notifications { +class Notifications extends NotificationsBase { /** * Admin script handle. @@ -78,19 +73,6 @@ private function init_hooks(): void { add_action( 'wp_ajax_' . self::RESET_NOTIFICATIONS_ACTION, [ $this, 'reset_notifications' ] ); } - /** - * Get tab url. - * - * @param string $classname Tab class name. - * - * @return string - */ - private function tab_url( string $classname ): string { - $tab = hcaptcha()->settings()->get_tab( $classname ); - - return $tab ? $tab->tab_url( $tab ) : ''; - } - /** * Get notifications. * @@ -98,24 +80,8 @@ private function tab_url( string $classname ): string { * @noinspection HtmlUnknownTarget */ protected function get_notifications(): array { - $general_url = $this->tab_url( General::class ); - $integrations_url = $this->tab_url( Integrations::class ); - $forms_url = $this->tab_url( FormsPage::class ); - $events_url = $this->tab_url( EventsPage::class ); - $utm = '/?r=wp&utm_source=wordpress&utm_medium=wpplugin&utm_campaign='; - $utm_sk = $utm . 'sk'; - $utm_not = $utm . 'not'; - $hcaptcha_url = 'https://www.hcaptcha.com' . $utm_sk; - $register_url = 'https://www.hcaptcha.com/signup-interstitial' . $utm_sk; - $pro_url = 'https://www.hcaptcha.com/pro' . $utm_not; - $dashboard_url = 'https://dashboard.hcaptcha.com' . $utm_not; - $post_leadership_url = 'https://www.hcaptcha.com/post/hcaptcha-named-a-technology-leader-in-bot-management' . $utm_not; - $rate_url = 'https://wordpress.org/support/plugin/hcaptcha-for-forms-and-more/reviews/?filter=5#new-post'; - $search_integrations_url = $integrations_url . '#hcaptcha-integrations-search'; - $enterprise_features_url = 'https://www.hcaptcha.com/#enterprise-features' . $utm_not; - $statistics_url = $general_url . '#statistics_1'; - $force_url = $general_url . '#force_1'; - $elementor_edit_form_url = HCAPTCHA_URL . '/assets/images/elementor-edit-form.png'; + $settings = hcaptcha()->settings(); + $urls = $this->prepare_urls(); $notifications = [ 'register' => [ @@ -125,17 +91,17 @@ protected function get_notifications(): array { __( 'To use %1$s, please register %2$s to get your site and secret keys.', 'hcaptcha-for-forms-and-more' ), sprintf( '%2$s', - $hcaptcha_url, + $urls['hcaptcha'], __( 'hCaptcha', 'hcaptcha-for-forms-and-more' ) ), sprintf( '%2$s', - $register_url, + $urls['register'], __( 'here', 'hcaptcha-for-forms-and-more' ) ) ), 'button' => [ - 'url' => $register_url, + 'url' => $urls['register'], 'text' => __( 'Get site keys', 'hcaptcha-for-forms-and-more' ), ], ], @@ -146,17 +112,17 @@ protected function get_notifications(): array { __( 'Want low friction and custom themes? %1$s is for you. %2$s, no credit card required.', 'hcaptcha-for-forms-and-more' ), sprintf( '%2$s', - $pro_url, + $urls['pro'], __( 'hCaptcha Pro', 'hcaptcha-for-forms-and-more' ) ), sprintf( '%2$s', - $dashboard_url, + $urls['dashboard'], __( 'Start a free trial in your dashboard', 'hcaptcha-for-forms-and-more' ) ) ), 'button' => [ - 'url' => $pro_url, + 'url' => $urls['pro'], 'text' => __( 'Try Pro', 'hcaptcha-for-forms-and-more' ), ], ], @@ -164,7 +130,7 @@ protected function get_notifications(): array { 'title' => __( 'hCaptcha\'s Leadership', 'hcaptcha-for-forms-and-more' ), 'message' => __( 'hCaptcha Named a Technology Leader in Bot Management: 2023 SPARK Matrix™', 'hcaptcha-for-forms-and-more' ), 'button' => [ - 'url' => $post_leadership_url, + 'url' => $urls['post_leadership'], 'text' => __( 'Read post', 'hcaptcha-for-forms-and-more' ), ], ], @@ -173,27 +139,27 @@ protected function get_notifications(): array { 'message' => sprintf( /* translators: 1: plugin name, 2: wp.org review link with stars, 3: wp.org review link with text. */ __( 'Please rate %1$s %2$s on %3$s. Thank you!', 'hcaptcha-for-forms-and-more' ), - 'hCaptcha for WP', + '' . $settings->get_plugin_name() . '', sprintf( '★★★★★', - $rate_url + $urls['rate'] ), sprintf( 'WordPress.org', - $rate_url + $urls['rate'] ) ), 'button' => [ - 'url' => $rate_url, + 'url' => $urls['rate'], 'text' => __( 'Rate', 'hcaptcha-for-forms-and-more' ), ], ], // Added in 3.8.0. 'search-integrations' => [ 'title' => __( 'Search on Integrations page', 'hcaptcha-for-forms-and-more' ), - 'message' => __( 'Now you can search for plugin an themes on the Integrations page.', 'hcaptcha-for-forms-and-more' ), + 'message' => __( 'You can search for plugin an themes on the Integrations page.', 'hcaptcha-for-forms-and-more' ), 'button' => [ - 'url' => $search_integrations_url, + 'url' => $urls['search_integrations'], 'text' => __( 'Start search', 'hcaptcha-for-forms-and-more' ), ], ], @@ -202,7 +168,7 @@ protected function get_notifications(): array { 'title' => __( 'Support for Enterprise features', 'hcaptcha-for-forms-and-more' ), 'message' => __( 'The hCaptcha plugin commenced support for Enterprise features. Solve your fraud and abuse problem today.', 'hcaptcha-for-forms-and-more' ), 'button' => [ - 'url' => $enterprise_features_url, + 'url' => $urls['enterprise_features'], 'text' => __( 'Get started', 'hcaptcha-for-forms-and-more' ), ], ], @@ -214,17 +180,17 @@ protected function get_notifications(): array { __( '%1$s events statistics and %2$s how your forms are used.', 'hcaptcha-for-forms-and-more' ), sprintf( '%2$s', - $statistics_url, + $urls['statistics'], __( 'Turn on', 'hcaptcha-for-forms-and-more' ) ), sprintf( '%2$s', - $forms_url, + $urls['forms'], __( 'see', 'hcaptcha-for-forms-and-more' ) ) ), 'button' => [ - 'url' => $statistics_url, + 'url' => $urls['statistics'], 'text' => __( 'Turn on stats', 'hcaptcha-for-forms-and-more' ), ], ], @@ -236,22 +202,22 @@ protected function get_notifications(): array { __( '%1$s events statistics and %2$s to %3$s complete statistics on form events.', 'hcaptcha-for-forms-and-more' ), sprintf( '%2$s', - $statistics_url, + $urls['statistics'], __( 'Turn on', 'hcaptcha-for-forms-and-more' ) ), sprintf( '%2$s', - $dashboard_url, + $urls['dashboard'], __( 'upgrade to Pro', 'hcaptcha-for-forms-and-more' ) ), sprintf( '%2$s', - $events_url, + $urls['events'], __( 'see', 'hcaptcha-for-forms-and-more' ) ) ), 'button' => [ - 'url' => $statistics_url, + 'url' => $urls['statistics'], 'text' => __( 'Turn on stats', 'hcaptcha-for-forms-and-more' ), ], ], @@ -260,7 +226,7 @@ protected function get_notifications(): array { 'title' => __( 'Force hCaptcha', 'hcaptcha-for-forms-and-more' ), 'message' => __( 'Force hCaptcha check before submitting the form and simplify the user experience.', 'hcaptcha-for-forms-and-more' ), 'button' => [ - 'url' => $force_url, + 'url' => $urls['force'], 'text' => __( 'Turn on force', 'hcaptcha-for-forms-and-more' ), ], ], @@ -269,7 +235,7 @@ protected function get_notifications(): array { 'title' => __( 'Activation of dependent plugins', 'hcaptcha-for-forms-and-more' ), 'message' => __( 'Automatic activation of dependent plugins on the Integrations page. Try to activate Elementor or Woo Wishlists.', 'hcaptcha-for-forms-and-more' ), 'button' => [ - 'url' => $integrations_url, + 'url' => $urls['integrations'], 'text' => __( 'Try auto-activation', 'hcaptcha-for-forms-and-more' ), ], ], @@ -278,14 +244,59 @@ protected function get_notifications(): array { 'title' => __( 'Add hCaptcha to Elementor Pro Form', 'hcaptcha-for-forms-and-more' ), 'message' => __( 'Add hCaptcha to Elementor Pro Form in the Elementor admin editor.', 'hcaptcha-for-forms-and-more' ), 'button' => [ - 'url' => $elementor_edit_form_url, - 'text' => __( 'See an example', 'hcaptcha-for-forms-and-more' ), + 'url' => $urls['elementor_edit_form'], + 'text' => __( 'See an example', 'hcaptcha-for-forms-and-more' ), + 'lightbox' => true, + ], + ], + // Added in 4.12.0. + 'passive-mode' => [ + 'title' => __( 'Friction-free “No CAPTCHA” & 99.9% passive modes', 'hcaptcha-for-forms-and-more' ), + 'message' => sprintf( + /* translators: 1: Pro link, 2: size select link. */ + __( '%1$s and use %2$s. The hCaptcha widget will not appear, and the Challenge popup will be shown only to bots.', 'hcaptcha-for-forms-and-more' ), + sprintf( + '%2$s', + $urls['dashboard'], + __( 'Upgrade to Pro', 'hcaptcha-for-forms-and-more' ) + ), + sprintf( + '%2$s', + $urls['size'], + __( 'Invisible Size', 'hcaptcha-for-forms-and-more' ) + ) + ), + 'button' => [ + 'url' => $urls['passive_mode_example'], + 'text' => __( 'See an example', 'hcaptcha-for-forms-and-more' ), + 'lightbox' => true, + ], + ], + // Added in 4.13.0. + 'protect-content' => [ + 'title' => __( 'Protect Site Content', 'hcaptcha-for-forms-and-more' ), + 'message' => sprintf( + /* translators: 1: Pro link. */ + __( '%1$s selected site URLs from bots with hCaptcha. Works best with %2$s 99.9%% passive mode.', 'hcaptcha-for-forms-and-more' ), + sprintf( + '%2$s', + $urls['protect_content'], + __( 'Protect', 'hcaptcha-for-forms-and-more' ) + ), + sprintf( + '%2$s', + $urls['dashboard'], + __( 'Pro', 'hcaptcha-for-forms-and-more' ) + ) + ), + 'button' => [ + 'url' => $urls['protect_content_example'], + 'text' => __( 'See an example', 'hcaptcha-for-forms-and-more' ), + 'lightbox' => true, ], ], ]; - $settings = hcaptcha()->settings(); - if ( ! empty( $settings->get_site_key() ) && ! empty( $settings->get_secret_key() ) ) { unset( $notifications['register'] ); } @@ -310,6 +321,14 @@ protected function get_notifications(): array { unset( $notifications['admin-elementor'] ); } + if ( $settings->is_pro() && $settings->is( 'size', 'invisible' ) ) { + unset( $notifications['passive-mode'] ); + } + + if ( $settings->is_on( 'protect_content' ) ) { + unset( $notifications['protect-content'] ); + } + // Added in 4.4.0. return array_merge( $notifications, $this->cf7_admin_notification() ); } @@ -384,21 +403,28 @@ public function show(): void { } foreach ( $notifications as $id => $notification ) { - $title = $notification['title'] ?: ''; - $message = $notification['message'] ?? ''; - $button_url = $notification['button']['url'] ?? ''; - $button_text = $notification['button']['text'] ?? ''; - $button = ''; + $title = $notification['title'] ?: ''; + $message = $notification['message'] ?? ''; + $button_url = $notification['button']['url'] ?? ''; + $button_text = $notification['button']['text'] ?? ''; + $button_lightbox = $notification['button']['lightbox'] ?? ''; + $button = ''; if ( $button_url && $button_text ) { + $lightbox_class = $button_lightbox ? ' hcaptcha-lightbox' : ''; ob_start(); + ?> tab_url( General::class ); + $urls['integrations'] = $this->tab_url( Integrations::class ); + $urls['forms'] = $this->tab_url( FormsPage::class ); + $urls['events'] = $this->tab_url( EventsPage::class ); + $urls['hcaptcha'] = 'https://www.hcaptcha.com' . $utm_sk; + $urls['register'] = 'https://www.hcaptcha.com/signup-interstitial' . $utm_sk; + $urls['pro'] = 'https://www.hcaptcha.com/pro' . $utm_not; + $urls['dashboard'] = 'https://dashboard.hcaptcha.com' . $utm_not; + $urls['post_leadership'] = 'https://www.hcaptcha.com/post/hcaptcha-named-a-technology-leader-in-bot-management' . $utm_not; + $urls['rate'] = 'https://wordpress.org/support/plugin/hcaptcha-for-forms-and-more/reviews/?filter=5#new-post'; + $urls['search_integrations'] = $urls['integrations'] . '#hcaptcha-integrations-search'; + $urls['enterprise_features'] = 'https://www.hcaptcha.com/#enterprise-features' . $utm_not; + $urls['statistics'] = $urls['general'] . '#statistics_1'; + $urls['force'] = $urls['general'] . '#force_1'; + $urls['elementor_edit_form'] = HCAPTCHA_URL . '/assets/images/elementor-edit-form.png'; + $urls['size'] = $urls['general'] . '#size'; + $urls['passive_mode_example'] = HCAPTCHA_URL . '/assets/images/passive-mode-example.gif'; + $urls['protect_content'] = $urls['general'] . '#protect_content_1'; + $urls['protect_content_example'] = HCAPTCHA_URL . '/assets/images/protect-content-example.gif'; + } + + return $urls; + } + + /** + * Get tab url. + * + * @param string $classname Tab class name. + * + * @return string + */ + protected function tab_url( string $classname ): string { + $tab = hcaptcha()->settings()->get_tab( $classname ); + + return $tab ? $tab->tab_url( $tab ) : ''; + } +} diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Admin/Privacy.php b/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Admin/Privacy.php new file mode 100644 index 00000000..af01c8da --- /dev/null +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Admin/Privacy.php @@ -0,0 +1,149 @@ +name = hcaptcha()->settings()->get_plugin_name(); + + $this->init(); + } + + /** + * Init class. + * + * @return void + */ + public function init(): void { + $this->init_hooks(); + } + + /** + * Init class hooks. + * + * @return void + */ + private function init_hooks(): void { + add_action( 'admin_init', [ $this, 'add_privacy_message' ] ); + } + + /** + * Adds the privacy message on WC privacy page. + */ + public function add_privacy_message(): void { + if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) { + return; + } + + $content = $this->get_privacy_message(); + + if ( ! $content ) { + return; + } + + wp_add_privacy_policy_content( $this->name, $content ); + } + + /** + * Add privacy policy content for the privacy policy page. + */ + public function get_privacy_message(): string { + ob_start(); + + ?> +
+

+ +

+

+ https://www.hcaptcha.com/privacy', + 'https://www.hcaptcha.com/terms' + ) + ); + ?> +

+
+ init(); + } + + /** + * Initializes the class by setting up hooks. + * + * @return void + */ + public function init(): void { + $this->init_hooks(); + } + + /** + * Init class hooks. + * + * @return void + */ + private function init_hooks(): void { + add_action( 'kagg_settings_tab', [ $this, 'action_settings_tab' ] ); + add_action( 'admin_print_footer_scripts', [ $this, 'enqueue_assets' ] ); + add_action( 'admin_footer', [ $this, 'maybe_show_popup' ] ); + add_action( 'wp_ajax_' . self::MARK_SHOWN_ACTION, [ $this, 'mark_shown' ] ); + add_filter( 'update_footer', [ $this, 'update_footer' ], 1010 ); + } + + /** + * Settings tab action. + * + * @return void + */ + public function action_settings_tab(): void { + $this->allowed = true; + } + + /** + * Enqueue assets. + * + * @return void + */ + public function enqueue_assets(): void { + if ( ! $this->allowed ) { + return; + } + + $min = hcap_min_suffix(); + + wp_enqueue_style( + self::HANDLE, + constant( 'HCAPTCHA_URL' ) . "/assets/css/whats-new$min.css", + [], + constant( 'HCAPTCHA_VERSION' ) + ); + wp_enqueue_script( + self::HANDLE, + constant( 'HCAPTCHA_URL' ) . "/assets/js/whats-new$min.js", + [ 'jquery' ], + constant( 'HCAPTCHA_VERSION' ), + true + ); + wp_localize_script( + self::HANDLE, + self::OBJECT, + [ + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'markShownAction' => self::MARK_SHOWN_ACTION, + 'markShownNonce' => wp_create_nonce( self::MARK_SHOWN_ACTION ), + ] + ); + } + + /** + * Maybe show popup. + * + * @return void + */ + public function maybe_show_popup(): void { + if ( ! $this->allowed ) { + return; + } + + $prefix = self::PREFIX; + $shown = hcaptcha()->settings()->get( self::WHATS_NEW_KEY ); + $current = explode( '-', constant( 'HCAPTCHA_VERSION' ) )[0]; + $methods = array_filter( + get_class_methods( $this ), + static function ( $method ) use ( $prefix ) { + return 0 === strpos( $method, $prefix ); + } + ); + $versions = array_map( + static function ( $method ) use ( $prefix ) { + return str_replace( [ $prefix, '_' ], [ '', '.' ], $method ); + }, + $methods + ); + + usort( $versions, 'version_compare' ); + + $versions = array_reverse( $versions ); + $method = ''; + + foreach ( $versions as $version ) { + if ( version_compare( $current, $version, '>=' ) ) { + $method = $prefix . str_replace( '.', '_', $version ); + + break; + } + } + + $display = version_compare( $shown, $current, '<' ); + + $this->render_popup( $method, $display ); + } + + /** + * Ajax action to mark content as shown. + * + * @return void + */ + public function mark_shown(): void { + // Run a security check. + if ( ! check_ajax_referer( self::MARK_SHOWN_ACTION, 'nonce', false ) ) { + wp_send_json_error( esc_html__( 'Your session has expired. Please reload the page.', 'hcaptcha-for-forms-and-more' ) ); + } + + // Check for permissions. + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( esc_html__( 'You are not allowed to perform this action.', 'hcaptcha-for-forms-and-more' ) ); + } + + $version = isset( $_POST['version'] ) ? sanitize_text_field( wp_unslash( $_POST['version'] ) ) : ''; + + $this->update_whats_new( $version ); + wp_send_json_success(); + } + + /** + * Show a new features link in the update footer. + * + * @param string|mixed $content The content that will be printed. + * + * @return string|mixed + */ + public function update_footer( $content ) { + if ( ! $this->allowed ) { + return $content; + } + + $link = sprintf( + '%1$s', + __( 'See the new features!', 'hcaptcha-for-forms-and-more' ) + ); + + return $content . ' - ' . $link; + } + + /** + * Render popup. + * + * @param string $method Popup method. + * @param bool $display Display popup. + * + * @return void + */ + protected function render_popup( string $method, bool $display ): void { + if ( ! method_exists( $this, $method ) ) { + return; + } + + $display_attr = $display ? 'flex' : 'none'; + $version = str_replace( [ self::PREFIX, '_' ], [ '', '.' ], $method ); + + ?> +
+
+
+ +
+
+ Icon +
+
+

+ + +

+
+
+
+ $method(); ?> +
+
+
+
+ lightbox-image +
+ prepare_urls(); + + $block1 = [ + 'type' => 'center', + 'badge' => __( 'New Feature', 'hcaptcha-for-forms-and-more' ), + 'title' => __( 'Site Content Protection', 'hcaptcha-for-forms-and-more' ), + 'message' => sprintf( + '

%1$s

%2$s

', + sprintf( + /* translators: 1: Pro link. */ + __( 'Protect selected site URLs from bots with hCaptcha. Works best with %1$s 99.9%% passive mode.', 'hcaptcha-for-forms-and-more' ), + sprintf( + '%2$s', + $urls['dashboard'], + __( 'Pro', 'hcaptcha-for-forms-and-more' ) + ) + ), + __( 'Set up protected URLs to prevent these pages from being accessed by bots.', 'hcaptcha-for-forms-and-more' ) + ), + 'button' => [ + 'url' => $urls['protect_content'], + 'text' => __( 'Protect Content', 'hcaptcha-for-forms-and-more' ), + ], + 'image' => [ + 'url' => $urls['protect_content_example'], + 'lightbox' => true, + ], + ]; + + $block2 = [ + 'type' => 'center', + 'badge' => __( 'New Feature', 'hcaptcha-for-forms-and-more' ), + 'title' => __( 'Friction-free “No CAPTCHA” & 99.9% passive modes', 'hcaptcha-for-forms-and-more' ), + 'message' => + sprintf( + /* translators: 1: Pro link, 2: size select link. */ + __( '%1$s and use %2$s. The hCaptcha widget will not appear, and the Challenge popup will be shown only to bots.', 'hcaptcha-for-forms-and-more' ), + sprintf( + '%2$s', + $urls['dashboard'], + __( 'Upgrade to Pro', 'hcaptcha-for-forms-and-more' ) + ), + sprintf( + '%2$s', + $urls['size'], + __( 'Invisible Size', 'hcaptcha-for-forms-and-more' ) + ) + ), + 'button' => [ + 'url' => $urls['dashboard'], + 'text' => __( 'Upgrade to Pro', 'hcaptcha-for-forms-and-more' ), + ], + 'image' => [ + 'url' => $urls['passive_mode_example'], + 'lightbox' => true, + ], + ]; + + $this->show_block( $block1 ); + $this->show_block( $block2 ); + } + + /** + * Show block. + * + * @param array $block Block. + * + * @return void + */ + private function show_block( array $block ): void { + $badge = $block['badge'] ?? ''; + + if ( $badge ) { + ob_start(); + + ?> +
+ +
+ +
+ +

+ +

+
+ +
+
+ + + +
+
+ + + + What's New block image + + + What's New block image + + +
+
+ is_valid_version( $version ) ) { + return; + } + + $tab = hcaptcha()->settings()->get_tab( General::class ); + + if ( ! $tab ) { + // @codeCoverageIgnoreStart + return; + // @codeCoverageIgnoreEnd + } + + $tab->update_option( self::WHATS_NEW_KEY, $version ); + } + + /** + * Check if a version is valid. + * + * @param string $version Version. + * + * @return bool + */ + private function is_valid_version( string $version ): bool { + return (bool) preg_match( '/^\d+(\.\d+)*([a-zA-Z0-9\-._]*)?$/', $version ); + } +} diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/AutoVerify/AutoVerify.php b/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/AutoVerify/AutoVerify.php index 99c51871..cc881007 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/AutoVerify/AutoVerify.php +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/AutoVerify/AutoVerify.php @@ -337,19 +337,19 @@ protected function update_transient( array $forms_data ): void { foreach ( $forms_data as $form_data ) { $data = $form_data; - $action = $form_data['action']; + $action = $data['action'] ?? ''; unset( $data['action'] ); - $inputs = $data['inputs']; - $args = $data['args']; - $auto = $args['auto']; + $inputs = $data['inputs'] ?? []; + $args = $data['args'] ?? []; + $auto = $args['auto'] ?? false; $key = false; $action_forms = $registered_forms[ $action ] ?? []; foreach ( $action_forms as $index => $action_form ) { - if ( $inputs === $action_form['inputs'] ) { + if ( ( $action_form['inputs'] ?? [] ) === $inputs ) { $key = $index; break; } diff --git a/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Avada/Form.php b/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Avada/Form.php index ef671920..344ca618 100644 --- a/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Avada/Form.php +++ b/wp-content/plugins/hcaptcha-for-forms-and-more/src/php/Avada/Form.php @@ -36,7 +36,7 @@ public function __construct() { public function init_hooks(): void { add_action( 'fusion_form_after_open', [ $this, 'form_after_open' ], 10, 2 ); add_filter( 'fusion_builder_form_submission_data', [ $this, 'submission_data' ] ); - add_action( 'fusion_element_button_content', [ $this, 'add_hcaptcha' ], 10, 2 ); + add_action( 'fusion_element_form_content', [ $this, 'add_hcaptcha' ], 10, 2 ); add_filter( 'fusion_form_demo_mode', [ $this, 'verify' ] ); } @@ -82,10 +82,18 @@ public function submission_data( $data ): array { * @noinspection PhpUnusedParameterInspection */ public function add_hcaptcha( string $html, array $args ): string { - if ( false === strpos( $html, ''; - $ret .= '

'.__('Restore an existing backup set onto this site', 'updraftplus').'

'; - $ret .= ''; - - $ret .= ''.__('To import a backup set, go to the "Existing backups" section in the "Backup/Restore" tab', 'updraftplus').""; - - if (empty($backup_history)) { - $ret .= '

'.__('This site has no backups to restore from yet.', 'updraftplus').'

'; - $ret .= ''; - return $ret; - } - - $incremental_set_found = false; - - $ret .= '

- '; - - $ret .= ''; - - $ret .= '

'; - - if ($incremental_set_found) $ret .= '

'.__('For incremental backups, you will be able to choose which increments to restore at a later stage.', 'updraftplus').'

'; - - $ret .= ''; - -// $ret .= ''; - return $ret; - } - - /** - * Disable W3TC and WP Super Cache, etc. - */ - public function restored_plugins() { - if (true !== $this->is_migration) return; - global $updraftplus; - $active_plugins = maybe_unserialize($updraftplus->option_filter_get('active_plugins')); - if (!is_array($active_plugins)) return; - $disable_plugins = array( - 'w3-total-cache/w3-total-cache.php' => 'W3 Total Cache', - 'wp-super-cache/wp-cache.php' => 'W3 Super Cache', - 'quick-cache/quick-cache.php' => 'Quick Cache', - 'wp-fastest-cache/wpFastestCache.php' => 'WP Fastest Cache' - ); - foreach ($disable_plugins as $slug => $desc) { - // in_array is case sensitive - // if (in_array($slug, $active_plugins)) { - if (preg_grep("#".$slug."#i", $active_plugins)) { - unset($active_plugins[$slug]); - - $updraftplus->log("Disabled this plugin: %s: re-activate it manually when you are ready.", $desc); - $updraftplus->log(sprintf(__("Disabled this plugin: %s: re-activate it manually when you are ready.", 'updraftplus'), $desc), 'notice-restore'); - - } - } - update_option('active_plugins', $active_plugins); - } - - public function restorecachefiles($val, $file) { - // On a migration, we don't want to add cache files if they do not already exist (because usually they won't work until re-installed) - if (true !== $this->is_migration || false == $val) return $val; - $val = (is_file(WP_CONTENT_DIR.'/'.$file)) ? $val : false; - if (false == $val) { - global $updraftplus; - $updraftplus->log_e("%s: Skipping cache file (does not already exist)", $file); - } - return $val; - } - - public function adminaction_searchreplace($options = array()) { - - global $updraftplus_restorer; - - $options = wp_parse_args($options, array( - 'show_return_link' => true, - 'show_heading' => true, - )); - - if (!empty($options['show_heading'])) echo '

'.__('Search / replace database', 'updraftplus').'

'; - echo ''.__('Search for', 'updraftplus').': '.htmlspecialchars($_POST['search'])."
"; - echo ''.__('Replace with', 'updraftplus').': '.htmlspecialchars($_POST['replace'])."
"; - $this->page_size = (empty($_POST['pagesize']) || !is_numeric($_POST['pagesize'])) ? 5000 : $_POST['pagesize']; - $this->which_tables = (empty($_POST['whichtables'])) ? '' : explode(',', ($_POST['whichtables'])); - if (empty($_POST['search'])) { - echo sprintf(__("Failure: No %s was given.", 'updraftplus'), __('search term', 'updraftplus'))."
"; - - if (!empty($options['show_return_link'])) { - echo ''.__('Return to UpdraftPlus Configuration', 'updraftplus').''; - } - - return; - } - - if (empty($updraftplus_restorer) || !is_a($updraftplus_restorer, 'Updraft_Restorer')) { - // Needed for the UpdraftPlus_WPDB class and Updraft_Restorer::sql_exec() method - updraft_try_include_file('restorer.php', 'include_once'); - $updraftplus_restorer = new Updraft_Restorer(null, null, true); - add_filter('updraftplus_logline', array($updraftplus_restorer, 'updraftplus_logline'), 10, 5); - $updraftplus_restorer->search_replace_obj->updraftplus_restore_db_pre(); - } - $this->updraftplus_restore_db_pre(); - $this->tables_replaced = array(); - $this->updraftplus_restored_db_dosearchreplace($_POST['search'], $_POST['replace'], $this->base_prefix, false); - if (!empty($options['show_return_link'])) echo ''.__('Return to UpdraftPlus Configuration', 'updraftplus').''; - } - - /** - * This method will check if the newly created table has already been created before, if it has then we should mark it to be search and replaced again. - * - * @param String $table - the name of the newly created table - */ - public function updraftplus_creating_table($table) { - global $updraftplus; - - if (!empty($this->tables_replaced[$table]) && $this->tables_replaced[$table]) { - $this->tables_replaced[$table] = false; - $updraftplus->log('Warning: This database table has already been created once, now marking it to be search and replaced again - will try to continue but if errors are encountered then check that the backup is correct.', 'notice-restore'); - } - } - - public function debugtools_dashboard() { - global $updraftplus_admin; - ?> -
-

-

-
- - - - - settings_debugrow(':', ''); - echo $updraftplus_admin->settings_debugrow(':', ''); - echo $updraftplus_admin->settings_debugrow(':', ''); - echo $updraftplus_admin->settings_debugrow(':', ''); - ?> - settings_debugrow('', ''); ?> -
-
-
- log(__('Processed plugin:', 'updraftplus').' '.$plugin, 'notice-restore'); - $updraftplus->log("Processed plugin: $plugin"); - } - - 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 - - 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')).' '.__('Go here for more information.', 'updraftplus').''; - } - 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')).' '.__('Go here for more information.', 'updraftplus').''; - } - 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')).' '.__('Go here for more information.', 'updraftplus').''; - } - - 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'] .= '

'.__('Information needed to continue:', 'updraftplus').'
'; - $info['addui'] .= __('Enter details for where this new site is to live within your multisite install:', 'updraftplus').'
'; - - global $current_site; - - if (!is_subdomain_install()) { - $info['addui'] .= '
'; - } else { - $info['addui'] .= ' .
'; - } - - $info['addui'] .= '

'; - - if (!empty($elements['db'])) { - - if (empty($info['addui'])) $info['addui'] = ''; - $info['addui'] .= '


'; - - $class = (!defined('UPDRAFTPLUS_SELECT2_ENABLE') || UPDRAFTPLUS_SELECT2_ENABLE) ? 'updraft_select2' : ''; - - $info['addui'] .= ''; - -// $main_site_id = $current_site->blog_id; - $page = 0; - - while (!isset($users) || count($users) > 0) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable - - $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'] .= ''; - } - $page++; - } - - $info['addui'] .= '

'; - } - - } - - if (is_array($info) && isset($info['migration']) && true === $info['migration']) { - if (empty($info['addui'])) $info['addui'] = ''; - $info['addui'] .= '
'; - $info['addui'] .= '

' . __('Database restoration options:', 'updraftplus') . '

'; - $info['addui'] .= ''; - $info['addui'] .= '
'; - } -// return $info; - } - - 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 __('ERROR: 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 __('ERROR: 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 __('ERROR: problem creating site entry.'); - - switch_to_blog($blog_id); - install_blog($blog_id); - restore_current_blog(); - } else { - $blog_data = array( - 'domain' => $domain, - 'path' => $path, - 'network_id' => $site_id, - 'title' => $weblog_title, - ); - $blog_id = wp_insert_site($blog_data); - } - - return $blog_id; - } - - public function updraftplus_restore_db_record_old_siteurl($old_siteurl) { - // Only record once - if (!empty($this->old_siteurl)) return; - $this->old_siteurl = $old_siteurl; - } - - public function updraftplus_restore_db_record_old_home($old_home) { - // Only record once - if (!empty($this->old_home)) return; - $this->old_home = $old_home; - } - - 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; - } - - /** - * This function is called via a filter it saves the passed in old abspath value from restorer.php to a class variable for later use - * - * @param String $old_abspath - the old abspath - * - * @return void - */ - public function updraftplus_restore_db_record_old_abspath($old_abspath) { - if ('' !== $this->old_abspath) return; - $this->old_abspath = $old_abspath; - } - - public function updraftplus_restore_db_pre() { - - global $wpdb, $updraftplus, $updraftplus_restorer; - - $this->siteurl = untrailingslashit(site_url()); - $this->home = untrailingslashit(home_url()); - $this->content = untrailingslashit(content_url()); - $this->use_wpdb = $updraftplus_restorer->use_wpdb(); - - $this->base_prefix = $updraftplus->get_table_prefix(false); - - $mysql_dbh = false; - $use_mysqli = false; - - if (!$this->use_wpdb) { - // We have our own extension which drops lots of the overhead on the query - $wpdb_obj = $updraftplus_restorer->get_db_object(); - // Was that successful? - if (!$wpdb_obj->is_mysql || !$wpdb_obj->ready) { - $this->use_wpdb = true; - } else { - $this->wpdb_obj = $wpdb_obj; - $mysql_dbh = $wpdb_obj->updraftplus_get_database_handle(); - $use_mysqli = $wpdb_obj->updraftplus_use_mysqli(); - } - } - - $this->mysql_dbh = $mysql_dbh; - $this->use_mysqli = $use_mysqli; - - if (true == $this->use_wpdb) $updraftplus->log_e('Database access: Direct MySQL access is not available, so we are falling back to wpdb (this will be considerably slower)'); - - if (is_multisite()) { - $sites = $wpdb->get_results('SELECT id, domain, path FROM '.UpdraftPlus_Manipulation_Functions::backquote($this->base_prefix.'site'), ARRAY_N); - if (is_array($sites)) { - $nsites = array(); - foreach ($sites as $site) $nsites[$site[0]] = array($site[1], $site[2]); - $this->original_sites = $nsites; - } - } - - $this->report = array( - 'tables' => 0, - 'rows' => 0, - 'change' => 0, - 'updates' => 0, - 'timetaken' => 0, - 'errors' => array(), - ); - - } - - public function updraftplus_restored_db_table($table, $import_table_prefix, $engine = '') { - - global $updraftplus, $wpdb, $updraftplus_restorer; - - if (!empty($this->new_blogid) && !empty($this->restore_options['updraft_restore_content_to_user'])) { - if ($table == $import_table_prefix.'posts') { - $updraftplus->log("Setting all content (posts/post_author) to be owned by ID: ".$this->restore_options['updraft_restore_content_to_user']); - $posts_updated = $wpdb->query("UPDATE ".UpdraftPlus_Manipulation_Functions::backquote($table)." SET post_author=".(int) $this->restore_options['updraft_restore_content_to_user']); - if (is_numeric($posts_updated)) { - $updraftplus->log("Number of rows updated: ".$posts_updated); - } else { - $updraftplus->log("An error occurred when updating content ownership"); - } - } elseif ($table == $import_table_prefix.'postmeta') { - // Set WooCommerce orders to belong to guest - $keys_deleted = $wpdb->query("DELETE FROM ".UpdraftPlus_Manipulation_Functions::backquote($table)." WHERE meta_key='_customer_user'"); - if (is_numeric($keys_deleted)) { - $updraftplus->log("Number of WooCommerce orders re-assigned to Guest: ".$keys_deleted); - } - } - } - - // Anything else to do? - if (empty($this->restore_options['updraft_restorer_replacesiteurl'])) return; - - // Can only do something if the old siteurl is known - $old_siteurl = isset($this->old_siteurl) ? $this->old_siteurl : ''; - $old_home = isset($this->old_home) ? $this->old_home : ''; - $old_content = isset($this->old_content) ? $this->old_content : $old_siteurl.'/wp-content'; - // This wasn't stored in the backup header until 1.11.20. It's usually $old_content.'/uploads', but there's no need to force that, as on a default setup, the search/replace is caught by the content replace anyway - $old_uploads = isset($this->old_uploads) ? $this->old_uploads : false; - if (!$old_home && !$old_siteurl) return; - - $old_abspath = $this->old_abspath; - - if (empty($this->tables_replaced)) $this->tables_replaced = array(); - - // Already done? - if (!empty($this->tables_replaced[$table])) return; - - // If not done already, then search & replace this table, + record that it is done - if (function_exists('set_time_limit')) @set_time_limit(1800);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - - $stripped_table = substr($table, strlen($import_table_prefix)); - // Remove multisite site number prefix, if relevant - if (is_multisite() && preg_match('/^(\d+)_(.*)$/', $stripped_table, $matches)) $stripped_table = $matches[2]; - - // This array is for tables that a) we know don't need URL search/replacing and b) are likely to be sufficiently big that they could significantly delay the progress of the migrate (and increase the risk of timeouts on hosts that enforce them) - // The term_relationships table contains 3 columns, all integers. Therefore, we can skip it. It can easily get big, so this is a good time-saver. - $skip_tables = array('slim_stats', 'statpress', 'term_relationships', 'icl_languages_translations', 'icl_string_positions', 'icl_string_translations', 'icl_strings', 'redirection_logs', 'Counterize', 'Counterize_UserAgents', 'Counterize_Referers', 'adrotate_stats', 'login_security_solution_fail', 'wfHits', 'wfhits', 'wbz404_logs', 'wbz404_redirects', 'wfFileMods', 'wffilemods', 'tts_trafficstats', 'tts_referrer_stats', 'dmsguestbook', 'relevanssi', 'wponlinebackup_generations', 'svisitor_stat', 'simple_feed_stats', 'itsec_log', 'rp_tags', 'woocommerce_order_items', 'relevanssi_log', 'blc_instances', 'wysija_email_user_stat', 'woocommerce_sessions', 'et_bloom_stats', 'redirection_404', 'lbakut_activity_log', 'stream_meta', 'wfBlockedIPLog', 'wfblockediplog', 'page_visit_history', 'strack_st', 'eum_logs'); - - if (in_array($stripped_table, $skip_tables)) { - $this->tables_replaced[$table] = true; - $updraftplus->log_e("Skipping this table: data in this table (%s) should not be search/replaced", $table); - return; - } - - if ('ARCHIVE' == $engine) { - $this->tables_replaced[$table] = true; - $updraftplus->log_e("Skipping this table: this table (%s) should not be search/replaced, as it uses the %s engine", $table, $engine); - return; - } - - // Blogs table on multisite doesn't contain the full URL - if (is_multisite() && ($table == $this->base_prefix.'blogs' || $table == $this->base_prefix.'site') && (preg_match('#^https?://([^/]+)#i', $this->home, $matches) || preg_match('#^https?://([^/]+)#i', $this->siteurl, $matches)) && (preg_match('#^https?://([^/]+)#i', $old_home, $omatches) || preg_match('#^https?://([^/]+)#i', $old_siteurl, $omatches))) { - $from_array = strtolower($omatches[1]); - $to_array = strtolower($matches[1]); - $updraftplus->log_e("Replacing in blogs/site table: from: %s to: %s", htmlspecialchars($from_array), htmlspecialchars($to_array)); - $try_site_blog_replace = true; - } else { - - list($from_array, $to_array) = $this->build_searchreplace_array($old_siteurl, $old_home, $old_content, $old_uploads, $old_abspath); - - // This block is for multisite installs, to do the search/replace of each site's URL individually. We want to try to do it here for efficiency - i.e. so that we don't have to double-pass tables - if (!empty($this->restored_blogs) && preg_match('/^(\d+)_(.*)$/', substr($table, strlen($import_table_prefix)), $tmatches) && (preg_match('#^((https?://)([^/]+))#i', $this->home, $matches) || preg_match('#^((https?://)([^/]+))#i', $this->siteurl, $matches)) && (preg_match('#^((https?://)([^/]+))#i', $old_home, $omatches) || preg_match('#^((https?://)([^/]+))#i', $old_siteurl, $omatches))) { - $old_home_domain = strtolower($omatches[3]); - $new_home_domain = strtolower($matches[3]); - $blog_id = $tmatches[1]; - if ($old_home_domain == $this->restored_blogs[1]['domain'] && isset($this->restored_blogs[$blog_id])) { - $bdom = $this->restored_blogs[$blog_id]['domain']; - $bpath = $this->restored_blogs[$blog_id]['path']; - $sblog = $omatches[2].$bdom.untrailingslashit($bpath); - $rblog = $omatches[2].str_replace($old_home_domain, $new_home_domain, $bdom).untrailingslashit($bpath); - if (!in_array($sblog, $from_array)) { - $from_array[] = $sblog; - $to_array[] = $rblog; - } - } - } - } - - // The search/replace parameters are allowed to be either strings or arrays - $report = $updraftplus_restorer->search_replace_obj->icit_srdb_replacer($from_array, $to_array, array($table => $stripped_table), 5000); - - // If we just replaced either the blogs or site table, then populate our records of what is *now* (i.e. post-restore) in them - if (!empty($try_site_blog_replace)) { - if ($table == $this->base_prefix.'blogs') { - $blogs = $wpdb->get_results('SELECT blog_id, domain, path, site_id FROM '.UpdraftPlus_Manipulation_Functions::backquote($this->base_prefix.'blogs'), ARRAY_N); - if (is_array($blogs)) { - $nblogs = array(); - foreach ($blogs as $blog) { - $nblogs[$blog[0]] = array('domain' => $blog[1], 'path' => $blog[2], 'site_id' => $blog[3]); - } - $this->restored_blogs = $nblogs; - } - } elseif ($table == $this->base_prefix.'site') { - $sites = $wpdb->get_results('SELECT id, domain, path FROM '.UpdraftPlus_Manipulation_Functions::backquote($this->base_prefix.'site').' ORDER BY id ASC', ARRAY_N); - if (is_array($sites)) { - $nsites = array(); - foreach ($sites as $site) { - $nsites[$site[0]] = array($site[1], $site[2]); - } - $this->restored_sites = $nsites; - } - } - if (!empty($this->restored_sites) && !empty($this->restored_blogs) && !empty($this->original_sites)) { - // Adjust paths - // Domain, path - $any_site_changes = false; - foreach ($this->original_sites as $oid => $osite) { - if (empty($this->restored_sites[$oid])) continue; - $rsite = $this->restored_sites[$oid]; - // Task: 1) Replace the site path with the previous site path 2) Replace all the blog path prefixes from the same blog - if ($rsite[1] != $osite[1]) { - $any_site_changes = true; - $sitepath = $osite[1]; - $this->restored_sites[$oid][1] = $sitepath; - foreach ($this->restored_blogs as $blog_id => $blog) { - // From this site? - if ($blog['site_id'] != $oid) continue; - // Replace the prefix according to the change in prefix for the site - $this->restored_blogs[$blog_id] = array('domain' => $blog['domain'], 'path' => $sitepath.substr($blog['path'], strlen($rsite[1])), 'site_id' => $oid); - } - } - } - if ($any_site_changes) { - $updraftplus->log_e('Adjusting multisite paths'); - foreach ($this->restored_sites as $site_id => $osite) { - $wpdb->query($wpdb->prepare("UPDATE ".UpdraftPlus_Manipulation_Functions::backquote($this->base_prefix.'site')." SET path='%s' WHERE id=%d", array($osite[1], (int) $site_id))); - } - foreach ($this->restored_blogs as $blog_id => $blog) { - $wpdb->query($wpdb->prepare("UPDATE ".UpdraftPlus_Manipulation_Functions::backquote($this->base_prefix.'blogs')." SET path='%s' WHERE blog_id=%d", array($blog['path'], (int) $blog_id))); - } - } - } - } - - // Output any errors encountered during the db work. - if (!empty($report['errors']) && is_array($report['errors'])) { - $updraftplus->log(__('Error:', 'updraftplus'), 'warning-restore', 'restore-db-error'); - $processed_errors = array(); - foreach ($report['errors'] as $error) { - if (in_array($error, $processed_errors)) continue; - $processed_errors[] = $error; - $num = count(array_keys($report['errors'], $error)); - $err_string = $error; - if ($num > 1) $err_string .= ' (x'.$num.')'; - $updraftplus->log($err_string, 'warning-restore'); - } - } - - if (false == $report) { - $updraftplus->log(sprintf(__('Failed: the %s operation was not able to start.', 'updraftplus'), __('search and replace', 'updraftplus')), 'warning-restore'); - } elseif (!is_array($report)) { - $updraftplus->log(sprintf(__('Failed: we did not understand the result returned by the %s operation.', 'updraftplus'), __('search and replace', 'updraftplus')), 'warning-restore'); - } else { - - $this->tables_replaced[$table] = true; - - // Calc the time taken. - foreach (array('tables', 'rows', 'change', 'updates') as $key) { - $this->report[$key] += $report[$key]; - } - $this->report['timetaken'] += $report['end'] - $report['start']; - } - - } - - /** - * Displays admin notice if .htaccess have any old migrated site reference. - */ - public function migration_admin_notices() { - $updraftplus_migrated_site_domain = get_site_option('updraftplus_migrated_site_domain', false); - if ($updraftplus_migrated_site_domain) { - $htaccess_file_path = ABSPATH.'.htaccess'; - $htaccess_file_reference_line_num_arr = array(); - if (file_exists($htaccess_file_path) && is_file($htaccess_file_path)) { - $current_site_domain = rtrim(str_ireplace(array('http://', 'https://'), '', get_home_url()), '/'); - $htaccess_file_lines = file($htaccess_file_path); - if (false !== $htaccess_file_lines) { - foreach ($htaccess_file_lines as $num => $line) { - $migrated_site_domain_pos = stripos($line, $updraftplus_migrated_site_domain); - if (false !== $migrated_site_domain_pos && stripos($line, $current_site_domain) !== $migrated_site_domain_pos) { - $htaccess_file_reference_line_num_arr[] = $num + 1; - } - } - } - } - $count_old_site_references = count($htaccess_file_reference_line_num_arr); - if ($count_old_site_references > 0) { - ?> -

- '.__('Warning', 'updraftplus').': '._n('Your .htaccess has an old site reference on line number %s. You should remove it manually.', 'Your .htaccess has an old site references on line numbers %s. You should remove them manually.', $count_old_site_references, 'updraftplus'), implode(', ', $htaccess_file_reference_line_num_arr)); - ?> -

-
- (siteurl,home,content,uploads,abspath) - * - * @param String $old_siteurl - the old site url - * @param String $old_home - the old home url - * @param Boolean|String $old_content - the old content url - * @param Boolean|String $old_uploads - the old upload url - * @param String $old_abspath - the old abspath - * - * @return Array - itself containing two arrays, with corresponding 'search' and 'replace' items. - */ - private function build_searchreplace_array($old_siteurl, $old_home, $old_content = false, $old_uploads = false, $old_abspath = '') { - - // The uploads parameter, if === false, should be ignored - it is only intended to be used in the special case of single-into-multisite imports (only in that case with $this->uploads get set) - if (false === $old_content && false === $old_uploads) $old_content = $old_siteurl.'/wp-content'; - $from_array = array(); - $to_array = array(); - - if (!empty($old_siteurl) && $old_siteurl == $old_home) { - $from_array[] = $old_home; - // Used to be site until Sep 2016, but that is wrong. Most likely it was the best possibility before the upload URL was also recorded/known. - $to_array[] = $this->home; - } elseif (!empty($old_home) && strpos($old_siteurl, $old_home) === 0) { - // strpos: haystack, needle - i.e. old_home is a (proper, since they were not ==) substring of old_siteurl - $from_array[] = $old_siteurl; - $to_array[] = $this->siteurl; - $from_array[] = $old_home; - $to_array[] = $this->home; - // If the source home URL is also a proper substring of the destination site URL, then this should be skipped - if ($old_home != $this->siteurl && strpos($this->siteurl, $old_home) === 0) { - // Not pretty, but the only solution that can cope with content in posts that contains references to both site and home URLs in this case. This extra search URL un-does the adding of an unnecessary duplicate portion to site URLs in the case that is detected here. - $from_array[] = $this->home.substr($this->home, strlen($old_home)); - $to_array[] = $this->home; - } - } elseif (!empty($old_siteurl) && strpos($old_home, $old_siteurl) === 0) { - // old_siteurl is a substring of old_home (weird!) - $from_array[] = $old_home; - $to_array[] = $this->home; - $from_array[] = $old_siteurl; - $to_array[] = $this->siteurl; - } else { - // neither contains the other - if (!empty($old_siteurl)) { - $from_array[] = $old_siteurl; - $to_array[] = $this->siteurl; - } - if (!empty($old_home)) { - $from_array[] = $old_home; - $to_array[] = $this->home; - } - } - // We now have a minimal array based on the site_url and home settings - // The case we need to detect is: (site_url is a prefix of content_url and new_site_url is a prefix of new_content_url and the remains are the same. - // We do [0] of the existing array, to handle the weird case where old_siteurl is a substring of old_home (i.e. we get the shortest possible match) - // We will want to do the content URLs first, since they are likely to be longest - if (empty($old_content) || empty($this->content) || (!empty($from_array) && 0 === strpos($old_content, $from_array[0]) && 0 === strpos($this->content, $to_array[0]) && substr($old_content, strlen($from_array[0])) === substr($this->content, strlen($to_array[0])))) { - // OK - nothing to do - is already covered - } else { - // Search/replace needed - array_unshift($from_array, $old_content); - array_unshift($to_array, $this->content); - } - if (empty($old_uploads) || empty($this->uploads) || (!empty($from_array) && 0 === strpos($old_uploads, $from_array[0]) && 0 === strpos($this->uploads, $to_array[0]) && substr($old_uploads, strlen($from_array[0])) === substr($this->uploads, strlen($to_array[0])))) { - // OK - nothing to do - is already covered or no data is present - } else { - // Search/replace needed - array_unshift($from_array, $old_uploads); - array_unshift($to_array, $this->uploads); - } - - // Add the opposite http version so that sites with mixed links are caught - foreach ($from_array as $key => $value) { - if (0 === stripos($value, 'https://')) { - $from_array[] = 'http://'.substr($value, 8); - $to_array[] = $to_array[$key]; - } elseif (0 === stripos($value, 'http://')) { - $from_array[] = 'https://'.substr($value, 7); - $to_array[] = $to_array[$key]; - } - } - - if (rtrim($old_abspath, '/') !== '') { - $from_array[] = rtrim($old_abspath, '/'); - $to_array[] = rtrim(ABSPATH, '/'); - } - - return array($from_array, $to_array); - } - - public function updraftplus_restored_db($info, $import_table_prefix) { - - global $wpdb, $updraftplus; - - $updraftplus->log('Begin search and replace (updraftplus_restored_db)'); - $updraftplus->log(__('Database: search and replace site URL', 'updraftplus'), 'database-replace-site-url'); - - if (empty($this->restore_options['updraft_restorer_replacesiteurl'])) { - $updraftplus->log_e('This option was not selected.'); - return; - } - - $replace_this_siteurl = isset($this->old_siteurl) ? $this->old_siteurl : ''; - - // Don't call site_url() - the result may/will have been cached -// if (isset($this->new_blogid)) switch_to_blog($this->new_blogid); -// $db_siteurl_thissite = $wpdb->get_row("SELECT option_value FROM $wpdb->options WHERE option_name='siteurl'")->option_value; -// $db_home_thissite = $wpdb->get_row("SELECT option_value FROM $wpdb->options WHERE option_name='home'")->option_value; -// if (isset($this->new_blogid)) restore_current_blog(); - - // Until 1.12.25, we just used the main options table, which resulted in wrong results when importing a single site into a multisite - $options_table = empty($this->new_blogid) ? 'options' : $this->new_blogid.'_options'; - - $db_siteurl_thissite = $wpdb->get_row("SELECT option_value FROM ".UpdraftPlus_Manipulation_Functions::backquote($this->base_prefix.$options_table)." WHERE option_name='siteurl'")->option_value; - - $db_home_thissite = $wpdb->get_row("SELECT option_value FROM ".UpdraftPlus_Manipulation_Functions::backquote($this->base_prefix.$options_table)." WHERE option_name='home'")->option_value; - - if (!$replace_this_siteurl) { - $replace_this_siteurl = $db_siteurl_thissite; - } - - $replace_this_home = isset($this->old_home) ? $this->old_home : ''; - if (!$replace_this_home) { - $replace_this_home = $db_home_thissite; - } - - $replace_this_content = isset($this->old_content) ? $this->old_content : ''; - if (!$replace_this_content) { - $replace_this_content = $replace_this_siteurl.'/wp-content'; - } - - $replace_this_uploads = isset($this->old_uploads) ? $this->old_uploads : false; - - $replace_this_abspath = $this->old_abspath; - - // Sanity checks - if (empty($replace_this_siteurl)) { - $updraftplus->log(sprintf(__('Error: unexpected empty parameter (%s, %s)', 'updraftplus'), 'backup_siteurl', $this->siteurl), 'warning-restore'); - return; - } - if (empty($replace_this_home)) { - $updraftplus->log(sprintf(__('Error: unexpected empty parameter (%s, %s)', 'updraftplus'), 'backup_home', $this->home), 'warning-restore'); - return; - } - if (empty($replace_this_content)) { - $updraftplus->log(sprintf(__('Error: unexpected empty parameter (%s, %s)', 'updraftplus'), 'backup_content_url', $this->content), 'warning-restore'); - return; - } - - if (empty($this->siteurl)) { - $updraftplus->log(sprintf(__('Error: unexpected empty parameter (%s, %s)', 'updraftplus'), 'new_siteurl', $replace_this_siteurl), 'warning-restore'); - return; - } - if (empty($this->home)) { - $updraftplus->log(sprintf(__('Error: unexpected empty parameter (%s, %s)', 'updraftplus'), 'new_home', $replace_this_home), 'warning-restore'); - return; - } - // Only complain about the empty content parameter if it's not the case where we use the uploads parameter instead - if (empty($this->content) && empty($this->uploads)) { - $updraftplus->log(sprintf(__('Error: unexpected empty parameter (%s, %s)', 'updraftplus'), 'new_contenturl', $replace_this_content), 'warning-restore'); - return; - } - - // Remove any scheduled backup jobs on any imported-into-multisite site - if (!empty($this->new_blogid)) { - switch_to_blog($this->new_blogid); - wp_clear_scheduled_hook('updraft_backup'); - wp_clear_scheduled_hook('updraft_backup_database'); - wp_clear_scheduled_hook('updraft_backup_increments'); - restore_current_blog(); - } - - if ($replace_this_siteurl == $this->siteurl && $replace_this_home == $this->home && $replace_this_content == $this->content) { - $this->is_migration = false; - $updraftplus->log(sprintf(__('Nothing to do: the site URL is already: %s', 'updraftplus'), $this->siteurl), 'notice-restore'); - return; - } - - $this->is_migration = true; - - do_action('updraftplus_restored_db_is_migration'); - - // Detect situation where the database's siteurl in the header differs from that actual row data in the options table. This can occur if the options table was being over-ridden by a constant. In that case, the search/replace will have failed to set the option table's siteurl; and the result will be that that siteurl is hence wrong, leading to site breakage. The solution is to re-set it. - // $info['expected_oldsiteurl'] is from the db.gz file header - if (isset($info['expected_oldsiteurl']) && $info['expected_oldsiteurl'] != $db_siteurl_thissite && $db_siteurl_thissite != $this->siteurl) { - $updraftplus->log_e(sprintf(__("Warning: the database's site URL (%s) is different to what we expected (%s)", 'updraftplus'), $db_siteurl_thissite, $info['expected_oldsiteurl'])); - // Here, we change only the site URL entry; we don't run a full search/replace based on it. In theory, if someone developed using two different URLs, then this might be needed. - if (!empty($this->base_prefix) && !empty($this->siteurl)) { - $wpdb->query($wpdb->prepare("UPDATE ".UpdraftPlus_Manipulation_Functions::backquote($this->base_prefix.$options_table)." SET option_value='%s' WHERE option_name='siteurl'", array($this->siteurl))); - } - } - - if (isset($info['expected_oldhome']) && $info['expected_oldhome'] != $db_home_thissite && $db_home_thissite != $this->home) { - $updraftplus->log_e(sprintf(__("Warning: the database's home URL (%s) is different to what we expected (%s)", 'updraftplus'), $db_home_thissite, $info['expected_oldhome'])); - if (!empty($this->base_prefix) && !empty($this->home)) { - $wpdb->query($wpdb->prepare("UPDATE ".UpdraftPlus_Manipulation_Functions::backquote($this->base_prefix.$options_table)." SET option_value='%s' WHERE option_name='home'", array($this->home))); - } - } - - if (function_exists('set_time_limit')) @set_time_limit(1800);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - - list($from_array, $to_array) = $this->build_searchreplace_array($replace_this_siteurl, $replace_this_home, $replace_this_content, $replace_this_uploads, $replace_this_abspath); - - foreach ($from_array as $ind => $from_url) { - $updraftplus->log_e('Database search and replace: replace %s in backup dump with %s', $from_url, $to_array[$ind]); - } - - return $this->updraftplus_restored_db_dosearchreplace($from_array, $to_array, $import_table_prefix); - } - - private function updraftplus_restored_db_dosearchreplace($from_array, $to_array, $import_table_prefix, $examine_siteurls = true) { - - global $updraftplus, $wpdb, $updraftplus_restorer; - - // Now, get an array of tables and then send it off to $updraftplus_restorer->search_replace_obj->icit_srdb_replacer() - // Code modified from searchreplacedb2.php version 2.1.0 from http://www.davidcoveney.com - - // Do we have any tables and if so build the all tables array - $tables = array(); - - // We use $wpdb for non-performance-sensitive operations (e.g. one-time calls) - $tables_mysql = $wpdb->get_results('SHOW TABLES', ARRAY_N); - - $is_multisite = is_multisite(); - if ($examine_siteurls && $is_multisite && empty($this->new_blogid)) { - - $sites = $wpdb->get_results('SELECT id, domain, path FROM '.UpdraftPlus_Manipulation_Functions::backquote($import_table_prefix.'site').' ORDER BY id ASC', ARRAY_N); - $nsites = array(); - foreach ($sites as $site) { - $nsites[$site[0]] = array('dom' => $site[1], 'path' => $site[2]); - } - - $blogs = $wpdb->get_results('SELECT blog_id, domain, path, site_id FROM '.UpdraftPlus_Manipulation_Functions::backquote($import_table_prefix.'blogs').' ORDER BY blog_id ASC', ARRAY_N); - $nblogs = array(); - foreach ($blogs as $blog) { - $nblogs[$blog[0]] = array('dom' => $blog[1], 'path' => $blog[2], 'site_id' => $blog[3]); - } - } - - if (!$tables_mysql) { - $updraftplus->log(__('Error:', 'updraftplus').' '.__('Could not get list of tables', 'updraftplus'), 'warning-restore'); - $updraftplus->log('Could not get list of tables'); - $updraftplus_restorer->search_replace_obj->print_error('SHOW TABLES'); - return false; - } else { - // Run through the array - each element a numerically-indexed array - - $multisite_processed_sites = array(); - - foreach ($tables_mysql as $table) { - - // Type equality is necessary, as we don't want to match false - // "Warning: strpos(): Empty delimiter" means that the second parameter is a zero-length string - if (0 === strpos($table[0], $import_table_prefix)) { - $tablename = $table[0]; - - $stripped_table = substr($tablename, strlen($import_table_prefix)); - // Remove multisite site number prefix, if relevant - if (is_multisite() && preg_match('/^(\d+)_(.*)$/', $stripped_table, $matches)) $stripped_table = $matches[2]; - - if (!empty($this->which_tables) && is_array($this->which_tables)) { - if (!in_array($tablename, $this->which_tables)) { - $updraftplus->log(sprintf(__('Search and replacing table:', 'updraftplus')).$tablename.': '.__('skipped (not in list)', 'updraftplus'), 'notice-restore', 'restore-skipped-'.$tablename); - continue; - } - } - - $still_needs_doing = empty($this->tables_replaced[$tablename]); - - // Looking for site tables on multisite - if ($examine_siteurls && $is_multisite && !empty($this->restored_blogs) && preg_match('/^(\d+)_(.*)$/', substr($tablename, strlen($import_table_prefix)), $tmatches) && is_numeric($tmatches[1]) && !empty($this->restored_blogs[$tmatches[1]]) && !empty($nblogs[$tmatches[1]]) && (preg_match('#^((https?://)([^/]+))#i', $this->home, $matches) || preg_match('#^((https?://)([^/]+))#i', $this->siteurl, $matches))) { - // If the database file was not created by UD, then it may be out of order. Specifically, the 'blogs' table might have come *after* the tables for the individual sites. As a result, the tables for those sites may not have been fully searched + replaced... so we need to check that. - // What are we expecting the site_url to be? - $blog_id = $tmatches[1]; - if (empty($multisite_processed_sites[$blog_id])) { - $multisite_processed_sites[$blog_id] = true; - $site_url_current = $wpdb->get_var("SELECT option_value FROM ".UpdraftPlus_Manipulation_Functions::backquote($import_table_prefix.$blog_id)."_options WHERE option_name='siteurl'"); - if (is_string($site_url_current)) { - $bpath = $this->restored_blogs[$blog_id]['path']; - // Jan 2016: This line is old, and removes the main site's path, if present, from the front of this site's path - but why? I suspect it was so that images could be referenced directly without help from .htaccess - perhaps from when media used to be differently organised? - // $bpathroot = $this->restored_blogs[1]['path']; - // if (substr($bpath, 0, strlen($bpathroot)) == $bpathroot) $bpath = substr($bpath, strlen($bpathroot)-1); - - $proto = $matches[2]; - - $site_url_target = $proto.$nblogs[$blog_id]['dom'].untrailingslashit($bpath); - if ($site_url_target != $site_url_current) { - $updraftplus->log("Site url ($site_url_current) for this blog (blog_id=$blog_id) did not match the expected value ($site_url_target) - replacing"); - $multisite_processed_sites[$blog_id] = 1; - $still_needs_doing = true; - $from_array[] = $site_url_current; - $to_array[] = $site_url_target; - } - } - } elseif (!$still_needs_doing && 1 === $multisite_processed_sites[$blog_id]) { - $still_needs_doing = true; - } - } - - if ($still_needs_doing) { - $tables[$tablename] = $stripped_table; - } else { - $updraftplus->log(sprintf(__('Search and replacing table:', 'updraftplus')).' '.$tablename.': '.__('already done', 'updraftplus'), 'notice-restore', 'restore-table-already-done-'.$tablename); - $updraftplus->log('Search and replacing table: '.$tablename.': already done'); - } - } - } - } - - $final_report = $this->report; - - if (!empty($tables)) { - - $report = $updraftplus_restorer->search_replace_obj->icit_srdb_replacer($from_array, $to_array, $tables, $this->page_size); - - // Output any errors encountered during the db work. - if (!empty($report['errors']) && is_array($report['errors'])) { - - $updraftplus->log(__('Error:', 'updraftplus'), 'warning-restore', 'db-replace-error'); - - $processed_errors = array(); - foreach ($report['errors'] as $error) { - if (in_array($error, $processed_errors)) continue; - $processed_errors[] = $error; - $num = count(array_keys($report['errors'], $error)); - $error_msg = $error; - if ($num > 1) $error_msg .= ' (x'.$num.')'; - $updraftplus->log($error_msg, 'warning-restore'); - } - } - - if (false == $report) { - $updraftplus->log(sprintf(__('Failed: the %s operation was not able to start.', 'updraftplus'), 'search and replace'), 'warning-notice'); - } elseif (!is_array($report)) { - $updraftplus->log(sprintf(__('Failed: we did not understand the result returned by the %s operation.', 'updraftplus'), 'search and replace'), 'warning-notice'); - } - - // Calc the time taken. - foreach (array('tables', 'rows', 'change', 'updates') as $key) { - $final_report[$key] += $report[$key]; - } - $final_report['timetaken'] += $report['end'] - $report['start']; - foreach ($report['errors'] as $error) { - $final_report['errors'][] = $error; - } - - } - - $updraftplus->log(__('Tables examined:', 'updraftplus').' '.$final_report['tables'], 'notice-restore', 'restore-tables-examined'); - $updraftplus->log(__('Rows examined:', 'updraftplus').' '.$final_report['rows'], 'notice-restore', 'restore-rows-examined'); - $updraftplus->log(__('Changes made:', 'updraftplus').' '.$final_report['change'], 'notice-restore', 'restore-changes-made'); - $updraftplus->log(__('SQL update commands run:', 'updraftplus').' '.$final_report['updates'], 'notice-restore', 'restore-sql-commands-run'); - $updraftplus->log(__('Errors:', 'updraftplus').' '. count($final_report['errors']), 'notice-restore', 'restore-tables-errors'); - $updraftplus->log(__('Time taken (seconds):', 'updraftplus').' '.round($final_report['timetaken'], 3), 'notice-restore', 'restore-tables-time-taken'); - - // Here, We are saving migrated site url for scanning .htaccess file for migrated site url. if migrated site url exist in .htaccess file, plugin should prompt alert message for it. This site option stored if and if only Migrator addon is exist. It requires to add after search and replace. - if (!empty($this->old_siteurl)) update_site_option('updraftplus_migrated_site_domain', rtrim(str_ireplace(array('http://', 'https://'), '', $this->old_siteurl), '/')); - } - - /** - * Add js for dismiss migration old site references notice - * - * @return void - */ - public function dismiss_notice_for_old_site_references() { - global $pagenow; - if (UpdraftPlus_Options::admin_page() != $pagenow || empty($_REQUEST['page']) || 'updraftplus' != $_REQUEST['page']) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- $pagenow is undefined - $GLOBALS['updraftplus_admin']->admin_enqueue_scripts(); - ?> - - - - -
-
- -

-
-
- get_remotesites_selector();?> -
- -
- -
-
- -

-
-

-

-
- - - - - - - -

- - - -
- list_our_keys(); ?> -
-
- - - - $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 '
'.__('Database decryption phrase', 'updraftplus').': '; - - $updraft_encryptionphrase = UpdraftPlus_Options::get_updraft_option('updraft_encryptionphrase'); - - echo '
'; - } - - /** - * 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 - - if (empty($posted_data['user'])) return(array('r' => $posted_data['row'], 'm' => '

'.sprintf(__("Failure: No %s was given.", 'updraftplus').'

', __('user', 'updraftplus')))); - - if (empty($posted_data['host'])) return(array('r' => $posted_data['row'], 'm' => '

'.sprintf(__("Failure: No %s was given.", 'updraftplus').'

', __('host', 'updraftplus')))); - - if (empty($posted_data['name'])) return(array('r' => $posted_data['row'], 'm' => '

'.sprintf(__("Failure: No %s was given.", 'updraftplus').'

', __('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 .= '

'.$posted_data['user'].'@'.$posted_data['host'].'/'.$posted_data['name']." : ".__('database connection attempt failed', 'updraftplus')."

"; - if (is_wp_error($wpdb_obj->error) || is_string($wpdb_obj->error)) { - $ret .= '
    '; - 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 .= "
  • ".__('Connection failed: check your access details, that the database server is up, and that the network connection is not firewalled.', 'updraftplus')."
  • "; - } else { - $err = $wpdb_obj->error->get_error_message($code); - $ret .= "
  • ".$err."
  • "; - } - } - } - } else { - $ret .= "
  • ".$wpdb_obj->error."
  • "; - } - $ret .= '
'; - } - } - - $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 .= "

".__('Messages:', 'updraftplus'); - $ret_after .= '

    '; - - foreach (array_unique($updraftplus_admin->logged) as $code => $err) { - if ('db_connect_fail' === $code) $failed = true; - $ret_after .= "
  • $code: $err
  • "; - } - $ret_after .= '

'; - } - - if (!$failed) { - $ret = '

'.__('Connection succeeded.', 'updraftplus').' '.$ret_info.'

'.$ret; - } else { - $ret = '

'.__('Connection failed.', 'updraftplus').'

'.$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 .= '
'; - $ret .= '

'.__('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').'

'; - - $ret .= '
'; - - $ret .= ''; - - add_action('admin_footer', array($this, 'admin_footer')); - return $ret; - } - - public function admin_footer() { - ?> - - - '.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').'

'; - } - - $ret .= ''; - - $ret .= '

'.__('If you enter text here, it is used to encrypt database backups (Rijndael). Do make a separate record of it and do not lose it, or all your backups will be useless. 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').'

'; - - 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 spcifies 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 - - 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 - - $memory_limit = ini_get('memory_limit'); - $memory_usage = round(@memory_get_usage(false)/1048576, 1);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - $memory_usage2 = round(@memory_get_usage(true)/1048576, 1);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - $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 - - $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. 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 .= ''.__('You should backup all tables unless you are an expert in the internals of the WordPress database.', 'updraftplus').'
'; - - // 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 .= '
'; - } - - $ret .= '
'; - $links = ''.__('Select all', 'updraftplus').''; - if ($non_wp_table_exists) $links .= ' | '.__('Select all (this site)', 'updraftplus').''; - $links .= ' | '.__('Deselect all', 'updraftplus').'
'; - $show_as = ('wp' == $database_name) ? __('WordPress database', 'updraftplus') : $database_name; - $ret .= '
'.$links.''. $show_as . ' ' . __('tables', 'updraftplus') . '
'; - $ret .= $checkboxes; - $ret .= '
'; - } - - 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 - - // 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; - } -} diff --git a/wp-content/plugins/updraftplus/addons/morefiles.php b/wp-content/plugins/updraftplus/addons/morefiles.php deleted file mode 100644 index 9259a795..00000000 --- a/wp-content/plugins/updraftplus/addons/morefiles.php +++ /dev/null @@ -1,1252 +0,0 @@ -'._x('Download', '(verb)', 'updraftplus').''; - } - - public function updraftplus_command_get_zipfile_download($result, $params) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - global $updraftplus; - - $zip_object = $updraftplus->get_zip_object_name(); - - // Retrieve the information from our backup history - $backup_history = UpdraftPlus_Backup_History::get_history(); - - // Base name - $file = $backup_history[$params['timestamp']][$params['type']]; - - // Deal with multi-archive sets - if (is_array($file)) $file = $file[$params['findex']]; - - // Where it should end up being downloaded to - $fullpath = $updraftplus->backups_dir_location().'/'.$file; - - $path = substr($params['path'], strpos($params['path'], DIRECTORY_SEPARATOR) + 1); - - if (file_exists($fullpath) && is_readable($fullpath) && filesize($fullpath)>0) { - - $zip = new $zip_object; - - if (!$zip->open($fullpath)) { - return array('error' => 'UpdraftPlus: opening zip (' . $fullpath . '): failed to open this zip file.'); - } else { - - if ('UpdraftPlus_PclZip' == $zip_object) { - $extracted = $zip->extract($updraftplus->backups_dir_location() . DIRECTORY_SEPARATOR . 'ziptemp' . DIRECTORY_SEPARATOR, $path); - } else { - $replaced_dir_sep_path = str_replace(DIRECTORY_SEPARATOR, '/', $path); - $extracted = $zip->extractTo($updraftplus->backups_dir_location() . DIRECTORY_SEPARATOR . 'ziptemp' . DIRECTORY_SEPARATOR, $replaced_dir_sep_path); - } - - @$zip->close();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - - if ($extracted) { - return array('path' => 'ziptemp'.DIRECTORY_SEPARATOR.$path); - } else { - return array('error' => 'UpdraftPlus: failed to extract (' . $path . ')'); - } - } - } - - return array('error' => 'UpdraftPlus: no such file or diretory (' . $fullpath . '): if the file does exist please make sure it is readable by the server.'); - } - - public function fileinfo_more($data, $ind) { - if (!is_array($data) || !is_numeric($ind) || empty($this->more_paths) || !is_array($this->more_paths) || empty($this->more_paths[$ind])) return $data; - - global $updraftplus; - $file_entities = $updraftplus->jobdata_get('job_file_entities'); - if (!isset($file_entities['more'])) return $data; - - return array( - 'html'=> '
'.__('Contains:', 'updraftplus').' '.htmlspecialchars($this->more_paths[$ind]), - 'text'=> '\r\n'.__('Contains:', 'updraftplus').' '.$this->more_paths[$ind] - ); - } - - public function restore_form_wpcore() { - - ?> - - '.__('The above files comprise everything in a WordPress installation.', 'updraftplus').''; - } - - public function backupable_file_entities($arr, $full_info) { - if ($full_info) { - $arr['wpcore'] = array( - 'path' => untrailingslashit(ABSPATH), - 'description' => apply_filters('updraft_wpcore_description', __('WordPress core (including any additions to your WordPress root directory)', 'updraftplus')), - 'htmltitle' => sprintf(__('WordPress root directory server path: %s', 'updraftplus'), ABSPATH) - ); - } else { - $arr['wpcore'] = untrailingslashit(ABSPATH); - } - return $arr; - } - - /** - * N.B. &$err is also available as a fourth parameter if needed - * - * @param string $zipfile - * @param array $mess - * @param array $warn - * @return void - */ - public function checkzip_wpcore($zipfile, &$mess, &$warn) { - if (!empty($this->wpcore_foundyet) && 3 == $this->wpcore_foundyet) return; - - if (!is_readable($zipfile)) { - $warn[] = sprintf(__('Unable to read zip file (%s) - could not pre-scan it to check its integrity.', 'updraftplus'), basename($zipfile)); - return; - } - - if ('.zip' == strtolower(substr($zipfile, -4, 4))) { - - if (!class_exists('UpdraftPlus_PclZip')) updraft_try_include_file('includes/class-zip.php', 'include'); - $zip = new UpdraftPlus_PclZip; - - if (!$zip->open($zipfile)) { - $warn[] = sprintf(__('Unable to open zip file (%s) - could not pre-scan it to check its integrity.', 'updraftplus'), basename($zipfile)); - return; - } - - // 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) { - $warn[] = sprintf(__('Unable to read any files from the zip (%s) - could not pre-scan it to check its integrity. Zip error: (%s)', 'updraftplus'), basename($zipfile), $zip->last_error); - return; - } - - for ($i=0; $i < $numfiles; $i++) { - $si = $zip->statIndex($i); - if ('wp-admin/index.php' == $si['name']) { - $this->wpcore_foundyet = $this->wpcore_foundyet | 1; - if (3 == $this->wpcore_foundyet) return; - } - if ('xmlrpc.php' == $si['name'] || 'xmlrpc.php/xmlrpc.php' == $si['name']) { - $this->wpcore_foundyet = $this->wpcore_foundyet | 2; - if (3 == $this->wpcore_foundyet) return; - } - } - - @$zip->close();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - } elseif (preg_match('/\.tar(\.(gz|bz2))$/i', $zipfile)) { - - if (!class_exists('UpdraftPlus_Archive_Tar')) { - 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/PEAR/Archive/Tar.php', 'include_once'); - } - - $p_compress = null; - if ('.tar.gz' == strtolower(substr($zipfile, -7, 7))) { - $p_compress = 'gz'; - } elseif ('.tar.bz2' == strtolower(substr($zipfile, -8, 8))) { - $p_compress = 'bz2'; - } - - $tar = new UpdraftPlus_Archive_Tar($zipfile, $p_compress); - $list = $tar->listContent(); - - foreach ($list as $file) { - if (is_array($file) && isset($file['filename'])) { - if ('wp-admin/index.php' == $file['filename']) { - $this->wpcore_foundyet = $this->wpcore_foundyet | 1; - if (3 == $this->wpcore_foundyet) return; - } elseif ('xmlrpc.php' == $file['filename']) { - $this->wpcore_foundyet = $this->wpcore_foundyet | 2; - if (3 == $this->wpcore_foundyet) return; - } - } - } - } - } - - public function checkzip_end_wpcore(&$mess, &$warn, &$err) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - if (!empty($this->wpcore_foundyet) && 3 == $this->wpcore_foundyet) return; - if (0 == ($this->wpcore_foundyet & 1)) $warn[] = sprintf(__('This does not look like a valid WordPress core backup - the file %s was missing.', 'updraftplus'), 'wp-admin/index.php').' '.__('If you are not sure then you should stop; otherwise you may destroy this WordPress installation.', 'updraftplus'); - if (0 == ($this->wpcore_foundyet & 2)) $warn[] = sprintf(__('This does not look like a valid WordPress core backup - the file %s was missing.', 'updraftplus'), 'xmlrpc.php').' '.__('If you are not sure then you should stop; otherwise you may destroy this WordPress installation.', 'updraftplus'); - } - - public function backupable_file_entities_final($arr, $full_info) { - $path = UpdraftPlus_Options::get_updraft_option('updraft_include_more_path'); - if (is_array($path)) { - $path = array_map('untrailingslashit', $path); - if (1 == count($path)) $path = array_shift($path); - } else { - $path = untrailingslashit($path); - } - if ($full_info) { - $arr['more'] = array( - 'path' => $path, - 'description' => __('Any other file/directory on your server that you wish to backup', 'updraftplus'), - 'shortdescription' => __('More Files', 'updraftplus'), - 'restorable' => true - ); - } else { - $arr['more'] = $path; - } - return $arr; - } - - public function config_option_include_more($ret, $prefix) { - - if ($prefix) return $ret; - - $display = UpdraftPlus_Options::get_updraft_option('updraft_include_more') ? '' : 'style="display:none;"'; - $class = $display ? 'updraft-hidden' : ''; - - $paths = UpdraftPlus_Options::get_updraft_option('updraft_include_more_path'); - - if (!is_array($paths)) $paths = array($paths); - - $ret .= "

"; - - $ret .= __('If you are not sure what this option is for, then you will not want it, and should turn it off.', 'updraftplus').' '.__('If using it, select a path from the directory tree below and then press confirm selection.', 'updraftplus'); - - $ret .= ' '.__('Be careful what you select - if you select / then it really will try to create a zip containing your entire webserver.', 'updraftplus'); - - $ret .= '

'; - - $ret .= '

'; - - $ret .= '
'; - - // Stops default empty path input being output to screen - - if (empty($paths)) { - $paths = array(''); - } else { - foreach ($paths as $ind => $path) { - $ret .= '
'; - $ret .= ' '; - $ret .= '
'; - } - } - - $ret .= '
'; - - $ret .= $this->get_jstree_ui('options'); - - $ret .= '
'; - - return $ret; - } - - /** - * Gives html for the wp core exclude settings. Called by the updraftplus_config_option_include_wpcore filter - * - * @param String $ret the value passed by filter. by default, it is empty string - * @param String $prefix Prefix for the ID - * @return String html for exclude wp core - */ - public function config_option_include_wpcore($ret, $prefix) { - global $updraftplus, $updraftplus_admin; - - $for_updraftcentral = defined('UPDRAFTCENTRAL_COMMAND') && UPDRAFTCENTRAL_COMMAND; - - if ($prefix) return $ret; - - $display = UpdraftPlus_Options::get_updraft_option('updraft_include_wpcore') ? '' : 'style="display:none;"'; - $exclude_container_class = 'updraft_include_wpcore_exclude'; - if (!$for_updraftcentral) $exclude_container_class .= '_container'; - - $ret .= "
"; - - $ret .= ''; - - $exclude_input_type = $for_updraftcentral ? "text" : "hidden"; - $exclude_input_extra_attr = $for_updraftcentral ? 'title="'.__('If entering multiple files/directories, then separate them with commas. For entities at the top level, you can use a * at the start or end of the entry as a wildcard.', 'updraftplus').'" size="54"' : ''; - $ret .= ''; - if (!$for_updraftcentral) { - $backupable_file_entities = $updraftplus->get_backupable_file_entities(); - $path = UpdraftPlus_Manipulation_Functions::wp_normalize_path($backupable_file_entities['wpcore']); - $ret .= $updraftplus_admin->include_template('wp-admin/settings/file-backup-exclude.php', true, array( - 'prefix' => $prefix, - 'key' => 'wpcore', - 'include_exclude' => UpdraftPlus_Options::get_updraft_option('updraft_include_wpcore_exclude'), - 'path' => $path, - 'show_exclusion_options' => true - )); - } - $ret .= '
'; - - return $ret; - } - - /** - * Called via the WP filter updraftplus_dirlist_more - * - * @param String|Array $whichdirs - a path, or list of paths. Ultimately comes from the option updraft_include_more_path - * - * @return String|Array - filtered value - */ - public function backup_more_dirlist($whichdirs) { - // Need to properly analyse the plugins, themes, uploads, content paths in order to strip them out (they may have various non-default manual values) - - global $updraftplus; - - $possible_backups = $updraftplus->get_backupable_file_entities(false); - // We don't want to exclude the very thing we are backing up - unset($possible_backups['more']); - // We do want to exclude everything in WordPress and in wp-content - $possible_backups['wp-content'] = WP_CONTENT_DIR; - $possible_backups['wordpress'] = untrailingslashit(ABSPATH); - - $possible_backups_dirs = array(); - foreach ($possible_backups as $possback) { - if (is_array($possback)) { - foreach ($possback as $pb) $possible_backups_dirs[] = $pb; - } else { - $possible_backups_dirs[] = $possback; - } - } - - $possible_backups_dirs = array_unique($possible_backups_dirs); - // $possible_backups_dirs = array_flip($possible_backups); // old - - $orig_was_array = is_array($whichdirs); - if (!$orig_was_array) $whichdirs = array($whichdirs); - $dirlist = array(); - - foreach ($whichdirs as $whichdir) { - - if (!empty($whichdir) && (is_dir($whichdir) || is_file($whichdir))) { - // Removing the slash is important (though ought to be redundant by here); otherwise path matching does not work - $dirlist[] = $updraftplus->compile_folder_list_for_backup(untrailingslashit($whichdir), $possible_backups_dirs, array()); - } else { - $dirlist[] = array(); - if (!empty($whichdir)) { - $updraftplus->log("We expected to find something to back up at: ".$whichdir); - $updraftplus->log($whichdir.': '.__("No backup of location: there was nothing found to back up", 'updraftplus'), 'warning'); - } - } - - } - - return $orig_was_array ? $dirlist : array_shift($dirlist); - - } - - /** - * This function will build and keep track of a list of more files that will be backed up - * - * @param array $whichdirs - an array of directories that need to be backed up - * @param string $backup_file_basename - the backup file basename - * @param integer $index - the backup index - * - * @return array|boolean - returns an array of created more file zips or false if none are created - */ - public function backup_makezip_more($whichdirs, $backup_file_basename, $index) { - - global $updraftplus, $updraftplus_backup; - - if (!is_array($whichdirs)) $whichdirs = array($whichdirs); - - $this->more_paths = array(); - - $final_created = $updraftplus->jobdata_get('morefiles_temporary_final_created'); - if (!is_array($final_created)) $final_created = array(); - - $first_linked_index = 0; - - // Oct 2018: changed the way more files are tracked, there are now two arrays: - // more_locations: a numerical array of unique more file locations - // more_map: a numerical array where array keys match the backup file and array values match an array key in the more_locations array - // For tracking which "more files" configuration entry goes into which zip, to avoid useless activity (or worse, duplicate backups) - $more_map = $updraftplus->jobdata_get('morefiles_linked_indexes'); - $more_locations = $updraftplus->jobdata_get('morefiles_more_locations'); - if (!is_array($more_map)) $more_map = array(); - if (!is_array($more_locations)) $more_locations = array(); - - foreach ($whichdirs as $whichdir) { - if (in_array($whichdir, $more_locations)) continue; - - // Actually create the thing - $dirlist = $this->backup_more_dirlist($whichdir); - - if (count($dirlist)>0) { - $this->more_paths[] = $whichdir; - - if (!in_array($whichdir, $more_locations)) { - $more_locations[] = $whichdir; - $updraftplus->jobdata_set('morefiles_more_locations', $more_locations); - } - - if (!empty($more_map) && isset($more_map[$first_linked_index])) { - $first_linked_index = $index = count($more_map); - } - - $created = $updraftplus_backup->create_zip($dirlist, 'more', $backup_file_basename, $index, $first_linked_index); - - if (!empty($created)) { - - foreach ($created as $key => $name) { - $more_map[$key] = array_search($whichdir, $more_locations); - } - $updraftplus->jobdata_set('morefiles_linked_indexes', $more_map); - - $keys = array_keys($created); - $index = end($keys); - $index++; - $first_linked_index = $index; - } - - if (is_string($created)) { - $final_created[] = $created; - } elseif (is_array($created)) { - $final_created = array_merge($final_created, $created); - } else { - $updraftplus->log("$whichdir: More files backup: create_zip returned an error", 'warning', 'morefiles-'.md5($whichdir)); - // return false; - } - } else { - $updraftplus->log("$whichdir: No backup of 'more' directory: there was nothing found to back up", 'warning', 'morefiles-empty-'.md5($whichdir)); - // return false; - } - - $final_created = array_unique($final_created); - $updraftplus->jobdata_set('morefiles_temporary_final_created', $final_created); - } - - return (empty($final_created)) ? false : $final_created; - } - - public function include_wpcore_exclude() { - return explode(',', UpdraftPlus_Options::get_updraft_option('updraft_include_wpcore_exclude', '')); - } - - public function backup_wpcore_dirlist($whichdir, $logit = false) { - - // Need to properly analyse the plugins, themes, uploads, content paths in order to strip them out (they may have various non-default manual values) - - global $updraftplus; - - if (false !== ($wpcore_dirlist = apply_filters('updraftplus_dirlist_wpcore_override', false, $whichdir))) return $wpcore_dirlist; - - $possible_backups = $updraftplus->get_backupable_file_entities(false); - // We don't want to exclude the very thing we are backing up - unset($possible_backups['wpcore']); - // We do want to exclude everything in wp-content - $possible_backups['wp-content'] = WP_CONTENT_DIR; - - $possible_backups_dirs = array(); - - foreach ($possible_backups as $key => $dir) { - if (is_array($dir)) { - foreach ($dir as $ind => $rdir) { - if (!empty($rdir)) $possible_backups_dirs[$rdir] = $key.$ind; - } - } else { - if (!empty($dir)) $possible_backups_dirs[$dir] = $key; - } - } - - // Create an array of directories to be skipped - $exclude = UpdraftPlus_Options::get_updraft_option('updraft_include_wpcore_exclude', ''); - if ($logit) $updraftplus->log("Exclusion option setting (wpcore): ".$exclude); - // Make the values into the keys - $wpcore_skip = array_flip(preg_split("/,/", $exclude)); - $wpcore_skip['wp_content'] = 0; - - // Removing the slash is important (though ought to be redundant by here); otherwise path matching does not work - $wpcore_dirlist = $updraftplus->compile_folder_list_for_backup(untrailingslashit($whichdir), $possible_backups_dirs, $wpcore_skip); - - // This is not required to be a perfect test. The point is to make sure we do get WP core. - // Not using this approach for now. -// if (true == apply_filters('updraftplus_backup_wpcore_dirlist_strict', false)) { -// $wpcore_valid = array('wp-admin', 'wp-includes', 'index.php', 'xmlrpc.php'); -// foreach ($wpcore_dirlist as $dir) { -// -// } -// } - - return $wpcore_dirlist; - - } - - /** - * $whichdir will equal untrailingslashit(ABSPATH) (is ultimately sourced from our backupable_file_entities filter callback) - * - * @param string $whichdir - * @param string $backup_file_basename - * @param string $index - * @return array - */ - public function backup_makezip_wpcore($whichdir, $backup_file_basename, $index) { - - global $updraftplus, $updraftplus_backup; - - // Actually create the thing - - $wpcore_dirlist = $this->backup_wpcore_dirlist($whichdir, true); - - if (count($wpcore_dirlist)>0) { - $created = $updraftplus_backup->create_zip($wpcore_dirlist, 'wpcore', $backup_file_basename, $index); - if (is_string($created) || is_array($created)) { - return $created; - } else { - $updraftplus->log("WP Core backup: create_zip returned an error"); - return false; - } - } else { - $updraftplus->log("No backup of WP core directories: there was nothing found to back up"); - $updraftplus->log(sprintf(__("No backup of %s directories: there was nothing found to back up", 'updraftplus'), __('WordPress Core', ' updraftplus')), 'error'); - return false; - } - - } - - - /** - * $wp_dir is trailingslashit($wp_filesystem->abspath()) - * Must only use $wp_filesystem methods - * $working_dir is the directory which contains the backup entity/ies. It is a child of wp-content/upgrade - * We need to make sure we do not over-write any entities that are restored elsewhere. i.e. Don't touch plugins/themes etc. - but use backupable_file_entities in order to be fully compatible, but with an additional over-ride of touching nothing inside WP_CONTENT_DIR. Can recycle code from the 'others' handling to assist with this. - * - * @param string $working_dir - * @param string $wp_dir - * @return array - */ - public function restore_movein_wpcore($working_dir, $wp_dir) { - - global $updraftplus_restorer; - - // On subsequent archives of a multi-archive set, don't move anything; but do on the first - $preserve_existing = isset($updraftplus_restorer->been_restored['wpcore']) ? Updraft_Restorer::MOVEIN_COPY_IN_CONTENTS : Updraft_Restorer::MOVEIN_OVERWRITE_NO_BACKUP; - - return $updraftplus_restorer->move_backup_in($working_dir, $wp_dir, $preserve_existing, array(basename(WP_CONTENT_DIR)), 'wpcore'); - - } - - /** - * This function is called via a filter and will restore the more files backups - * Must only use $wp_filesystem methods - * We need to make sure we do not over-write any entities that are restored elsewhere. i.e. Don't touch plugins/themes etc. - but use backupable_file_entities in order to be fully compatible, but with an additional over-ride of touching nothing inside WP_CONTENT_DIR. Can recycle code from the 'others' handling to assist with this. - * - * @param string $working_dir - the directory which contains the backup entity/ies. it is a child of wp-content/upgrade - * @param string $wp_dir - is trailingslashit($wp_filesystem->abspath()) - * @param string $wp_filesystem_dir - the location we want to restore the more file backup - * - * @return boolean|WP_Error - boolean for success or a wordpress error - */ - public function restore_movein_more($working_dir, $wp_dir, $wp_filesystem_dir) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - - global $updraftplus_restorer; - - // On subsequent archives of a multi-archive set, don't move anything; but do on the first - $preserve_existing = isset($updraftplus_restorer->been_restored['more']) ? Updraft_Restorer::MOVEIN_COPY_IN_CONTENTS : Updraft_Restorer::MOVEIN_OVERWRITE_NO_BACKUP; - - return $updraftplus_restorer->move_backup_in($working_dir, trailingslashit($wp_filesystem_dir), $preserve_existing, array(basename(WP_CONTENT_DIR)), 'more'); - - } - - /** - * This function is called via a filter and will search the backup set for the correct more files path for that backup file - * - * @param array $path - the path to be filtered - * @param string $backup_file - the backup file we are restoring - * @param array $backup_set - the backup set being restored - * @param string $type - the type of backup file - * - * @return string - the filtered path - */ - public function restore_path_more($path, $backup_file, $backup_set, $type) { - - if ('more' != $type) return $path; - - if (!isset($backup_set['morefiles_linked_indexes']) || !isset($backup_set['morefiles_more_locations'])) return $path; - - if (false !== ($file_key = array_search($backup_file, $backup_set['more']))) { - - $location_key = $backup_set['morefiles_linked_indexes'][$file_key]; - - return $backup_set['morefiles_more_locations'][$location_key]; - } - - return $path; - } - - /** - * 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 - * @param string $whichone - the entity that this backup is - * - * @return boolean - returns a boolean to indicate if we should include a manifest in the backup - */ - public function more_include_manifest($include, $whichone) { - return ('more' == $whichone) ? true : $include; - } - - /** - * This function will rebuild the more files linked indexes and more locations array if the backup history is missing this information and return the backup history otherwise returns false - * - * @param array $backup_history - the backup history - * - * @return array|boolean - the modified backup history or false if theres no changes - */ - public function more_rebuild($backup_history) { - - $changes = false; - - foreach ($backup_history as $btime => $bdata) { - if (!isset($bdata['more'])) continue; - foreach ($bdata['more'] as $key => $filename) { - if (!isset($bdata['morefiles_linked_indexes'])) $bdata['morefiles_linked_indexes'] = array(); - if (!isset($bdata['morefiles_more_locations'])) $bdata['morefiles_more_locations'] = array(); - if (isset($bdata['morefiles_linked_indexes'][$key])) continue; - - $morefile_path = $this->more_manifest_file_directory('', $filename); - - if ('' == $morefile_path) continue; - - $changes = true; - - $morefile_path_key = array_search($morefile_path, $bdata['morefiles_more_locations']); - - if (false !== $morefile_path_key) { - $bdata['morefiles_linked_indexes'][$key] = $morefile_path_key; - } else { - if (!in_array($morefile_path, $bdata['morefiles_more_locations'])) $bdata['morefiles_more_locations'][] = $morefile_path; - $bdata['morefiles_linked_indexes'][$key] = count($bdata['morefiles_more_locations']) - 1; - } - } - - // We sort these here so that they appear in order in the backup history which makes for easier debugging - ksort($bdata['morefiles_more_locations']); - ksort($bdata['morefiles_linked_indexes']); - - $backup_history[$btime] = $bdata; - } - - if ($changes) return $backup_history; - - return false; - } - - /** - * This function will check the passed in more files zip to see if it includes a manifest file and if so it will extract the directory that them files belong to and return it, otherwise it will return the default value passed in. - * - * @param string $path - the default passed in path - * @param string $filename - the more file zip name - * - * @return string - the default path if no manifest is found or the location of the more files if it is found - */ - private function more_manifest_file_directory($path, $filename) { - global $updraftplus; - - $zip_object = $updraftplus->get_zip_object_name(); - $fullpath = $updraftplus->backups_dir_location() . DIRECTORY_SEPARATOR . $filename; - - if (file_exists($fullpath) && is_readable($fullpath) && filesize($fullpath) > 0) { - $zip = new $zip_object; - - $zip_opened = $zip->open($fullpath); - - if (true !== $zip_opened) { - return array('error' => 'UpdraftPlus: opening zip (' . $fullpath . '): failed to open this zip file (object='.$zip_object.', code: '.$zip_opened.')'); - } else { - - if ('UpdraftPlus_PclZip' == $zip_object) { - $zip->extract($updraftplus->backups_dir_location() . DIRECTORY_SEPARATOR . 'ziptemp' . DIRECTORY_SEPARATOR, 'updraftplus-manifest.json'); - } else { - $zip->extractTo($updraftplus->backups_dir_location() . DIRECTORY_SEPARATOR . 'ziptemp' . DIRECTORY_SEPARATOR, 'updraftplus-manifest.json'); - } - - $manifest_path = $updraftplus->backups_dir_location() . DIRECTORY_SEPARATOR . 'ziptemp' . DIRECTORY_SEPARATOR . 'updraftplus-manifest.json'; - $manifest = json_decode(file_get_contents($manifest_path), true); - - $path = $manifest['directory']; - - @$zip->close();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - } - } - - return $path; - } - - /** - * 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 $entities - 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 - * N.B. An extra parameter $err is also available after $warn - */ - public function restore_all_downloaded_postscan_more($backups, $timestamp, $entities, &$info, &$mess, &$warn) { - if (!isset($entities['more']) || !isset($backups[$timestamp]['more'])) return; - - $not_found = false; - $more_ui = '
'; - - if (empty($backups[$timestamp]['morefiles_linked_indexes']) || empty($backups[$timestamp]['morefiles_more_locations'])) { - $not_found = true; - foreach ($backups[$timestamp]['more'] as $key => $value) { - $more_ui .= $this->get_jstree_row_ui($key, $value, '', true, false, $key); - } - } else { - - $last_index = -1; - $split_set = false; - - foreach ($backups[$timestamp]['more'] as $key => $value) { - if (!isset($backups[$timestamp]['morefiles_linked_indexes'][$key])) { - $not_found = true; - $more_ui .= $this->get_jstree_row_ui($key, $value, '', true, false, $key); - } - - $index = $backups[$timestamp]['morefiles_linked_indexes'][$key]; - - $split_set = $last_index === $index ? true : false; - - if (!file_exists(dirname($backups[$timestamp]['morefiles_more_locations'][$index]))) { - $not_found = true; - $more_ui .= $this->get_jstree_row_ui($key, $value, $backups[$timestamp]['morefiles_more_locations'][$index], true, $split_set, $index); - } else { - $more_ui .= $this->get_jstree_row_ui($key, $value, $backups[$timestamp]['morefiles_more_locations'][$index], false, $split_set, $index); - } - - $last_index = $index; - } - } - $more_ui .= $this->get_jstree_ui('restore'); - $more_ui .= '
'; - - if ($not_found) { - $warn[] = __('The original filesystem location for some of the following items was not found. Please select where you want these backups to be restored to.', 'updraftplus'); - } - $mess[] = __('Please select the more files backups that you wish to restore:', 'updraftplus'); - $info['addui'] = empty($info['addui']) ? $more_ui : $info['addui'] . '
' . $more_ui; - } - - /** - * WordPress action updraftplus_restore_all_downloaded_postscan called during the restore process. - * - * The last three 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 $entities - 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 - * N.B. An extra parameter $err is also available after $warn - */ - public function restore_all_downloaded_postscan_selective_restore($backups, $timestamp, $entities, &$info, &$mess, &$warn) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - - $selective_restore_types = array( - 'plugins', - 'themes', - ); - - foreach ($selective_restore_types as $type) { - if (!isset($entities[$type]) || !isset($backups[$timestamp][$type])) continue; - - $backup_entities = $this->get_backup_contents_list($backups[$timestamp][$type]); - - if (empty($backup_entities)) continue; - $selective_restore_ui = $this->get_entity_selective_restore_ui($backup_entities, $type); - - $info['addui'] = empty($info['addui']) ? $selective_restore_ui : $info['addui'] . '
' . $selective_restore_ui; - } - } - - /** - * This function will build the selective entity restore UI and return it - * - * @param Array $entities - an array of entities to restore (plugins, themes) - * @param String $type - the type of entity (plugins, themes) - * - * @return String - returns the selective restore UI for this entity - */ - private function get_entity_selective_restore_ui($entities, $type) { - - global $updraftplus; - - $backupable_entities = $updraftplus->get_backupable_file_entities(false, true); - $description = isset($backupable_entities[$type]['singular_description']) ? $backupable_entities[$type]['singular_description'] : $type; - - $php_max_input_vars = ini_get("max_input_vars"); // phpcs:ignore PHPCompatibility.IniDirectives.NewIniDirectives.max_input_varsFound -- does not exist in PHP 5.2 - - $php_max_input_vars_exceeded = false; - if (!empty($php_max_input_vars) && count($entities) >= 0.90 * $php_max_input_vars) { - $php_max_input_vars_exceeded = true; - // If the amount of tables exceed 90% of the php max input vars then truncate the list to 50% of the php max input vars value - $entities = array_splice($entities, 0, $php_max_input_vars / 2); - } - - $selective_restore_ui = '
'; - $selective_restore_ui .= '

'.sprintf(__('If you do not want to restore all your %s files, then de-select the unwanted ones here. Files not chosen will not be replaced.', 'updraftplus'), strtolower($description)).'(...)

'; - - $selective_restore_ui .= '
'; - - return $selective_restore_ui; - } - - /** - * This function will get the top level folders (plugins, themes) from the passed in backup archives - * - * @param Array $backups - an array of backup archives - * - * @return Array - an array of top level entities inside the backups (plugin, themes) - */ - private function get_backup_contents_list($backups) { - - global $updraftplus; - - if (!class_exists('UpdraftPlus_PclZip')) updraft_try_include_file('includes/class-zip.php', 'include'); - - $updraft_dir = $updraftplus->backups_dir_location(); - - $entities = array(); - - foreach ($backups as $file) { - $zip = new UpdraftPlus_PclZip; - - $zipfile = $updraft_dir . '/' . $file; - - if (!$zip->open($zipfile)) return $entities; - - // 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("get_backup_contents_list(): could not read any files from the zip: (".basename($zipfile).") Zip error: (".$zip->last_error.")"); - - for ($i=0; $i < $numfiles; $i++) { - $si = $zip->statIndex($i); - $folders = explode('/', $si['name']); - $entity = isset($folders[1]) ? $folders[1] : false; - - // We don't want hidden file system files showing up in the list of entities to restore - if (!$entity || "." === substr($entity, 0, 1) || "index.php" == $entity) continue; - - if (!in_array($entity, $entities)) { - $entities[] = $entity; - } - } - - @$zip->close();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - } - - sort($entities); - - return $entities; - } - - /** - * This function is called via the filter updraft_backupable_file_entities_on_restore and is used to filter the restore paths found in $backupable_entities and replace them with the paths saved in the backup set. - * - * @param array $backupable_entities - an array of backupable entities and their restore paths - * @param array $restore_options - the restore options - * @param array $backup_set - the backup set being restored - * - * @return array - the filtered backupable_entities array - */ - public function backupable_file_entities_on_restore($backupable_entities, $restore_options, $backup_set) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - - if (isset($backupable_entities['more']) && isset($backup_set['morefiles_more_locations'])) $backupable_entities['more']['path'] = $backup_set['morefiles_more_locations']; - - return $backupable_entities; - } - - /** - * This function will output the more files jstree row ui for each entity thats not found - * - * @param integer $key - the backup file index - * @param string $file - the backup file name - * @param string $path - the path to the backup location - * @param boolean $not_found - a bool to indicate if the backup location was found or not - * @param boolean $split_set - a bool to indicate if this is part of a split set - * @param integer $index - the more files linked indexes index - * - * @return string - returns the ui - */ - public function get_jstree_row_ui($key, $file, $path, $not_found, $split_set, $index) { - - $html = ''; - $hidden = $split_set ? 'style="display:none;"' : ''; - $location = empty($path) ? $file : $path; - - $index_checkbox = ''; - - if ($split_set) return $index_checkbox; - - if ($not_found) { - $html .= '
'; - } else { - $html .= '
'; - } - $html .= '
'.$index_checkbox; - $html .= '
'; - - return $html; - } - - /** - * This function will output the more file jstree ui and assign unique id's using the passed in page - * - * @param string $page - the page we are displaying the ui on - * - * @return string - the more files jstree ui - */ - public function get_jstree_ui($page) { - $ret = ''; - if ('options' == $page) $ret .= ''; - $ret .= '
-
- -
-
-
- - -
-
'; - return $ret; - } - - /** - * This function will output any needed js for the more files addon. - * - * @return void - */ - public function admin_footer_more_files_js() { - ?> - - '.__('(as many as you like)', 'updraftplus').''; - } - - public function admin_print_footer_scripts() { - ?> - - - "> - - supports_feature('multi_storage')) { - ?> -

{{instance_label}}

- -

backup_methods[$storage]; ?>

- - - - supports_feature('multi_storage')) { - ?> -
- - -
- - - - - supports_feature('conditional_logic')) return; - ?> - conditional_logic_row"> - - - {{#with instance_conditional_logic as | logic |}} -
- - -
- {{/with}} - - - "> - - - backup_methods[$storage]); ?> - - - - " output_settings_field_name_and_id('instance_label') . ' ' . $storage . '_updraft_instance_label' : ''; ?> value="{{instance_label}}" /> - -
- -

- - - - backup_methods as $method => $description) { - echo "".htmlspecialchars($description)."\n"; - } - ?> - - 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; - } -} diff --git a/wp-content/plugins/updraftplus/addons/multisite.php b/wp-content/plugins/updraftplus/addons/multisite.php deleted file mode 100644 index d90979c2..00000000 --- a/wp-content/plugins/updraftplus/addons/multisite.php +++ /dev/null @@ -1,841 +0,0 @@ - '', - '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' => 1, - '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_muplugins' => 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 - - $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 '
'; - } - - public static function admin_init() { - - static $already_inited = false; - if ($already_inited) return; - $already_inited = true; - - global $updraftplus; - $updraftplus->plugin_title .= " - ".__('Multisite Install', 'updraftplus'); - } - - /** - * This is the function outputing the HTML for our options page - */ - public static function options_printpage() { - if (!self::user_can_manage()) { - wp_die(__('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(__('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 - 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 - } elseif ('others' == $type) { - @$wp_filesystem->delete($move_from.'/blogs.dir', true);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - } - - } 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 - } - // 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 - } - - 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 - } - // 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 - } - - 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 - - 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'] .= ''.__('Which site to restore', 'updraftplus').':
'; - // 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'] .= ''; - if (!empty($other_entities)) $info['addui'] .= ''.__('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.').' '.__('Read more...', 'updraftplus').''; - } - // 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; - } - - 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) { - $arr['mu-plugins'] = array( - 'path' => WPMU_PLUGIN_DIR, - 'description' => __('Must-use plugins', 'updraftplus') - ); - 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 { - $arr['mu-plugins'] = WPMU_PLUGIN_DIR; - 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; - -} diff --git a/wp-content/plugins/updraftplus/addons/noadverts.php b/wp-content/plugins/updraftplus/addons/noadverts.php deleted file mode 100644 index 0f50fa36..00000000 --- a/wp-content/plugins/updraftplus/addons/noadverts.php +++ /dev/null @@ -1,13 +0,0 @@ -the_client_id = defined('UPDRAFTPLUS_ONEDRIVE_CLIENT_ID') ? UPDRAFTPLUS_ONEDRIVE_CLIENT_ID : '276d9423-7d0c-41be-a3e1-4cdad89dc36f'; - // To do: Add Germany AAD app client id here - $this->the_germany_client_id = defined('UPDRAFTPLUS_ONEDRIVE_GERMANY_CLIENT_ID') ? UPDRAFTPLUS_ONEDRIVE_GERMANY_CLIENT_ID : '7cc3beb4-daab-4a59-b091-c4c2319d8d2d'; - $this->the_callback = defined('UPDRAFTPLUS_ONEDRIVE_CALLBACK_URL') ? UPDRAFTPLUS_ONEDRIVE_CALLBACK_URL : 'https://auth.updraftplus.com/auth/onedrive'; - - // 3rd parameter: chunking? 4th: Test button? - parent::__construct('onedrive', 'OneDrive', false, false); - - if (defined('UPDRAFTPLUS_UPLOAD_CHUNKSIZE') && UPDRAFTPLUS_UPLOAD_CHUNKSIZE>0) $this->chunk_size = max(UPDRAFTPLUS_UPLOAD_CHUNKSIZE, 320*1024); - } - - public function do_upload($file, $from) { - - global $updraftplus; - $opts = $this->get_options(); - - $message = " did not return the expected data"; - - if (!function_exists("curl_init") || !function_exists('curl_exec')) { - $this->log('The required Curl PHP module is not installed. This upload will abort'); - $this->log(sprintf(__('The required %s PHP module is not installed - ask your web hosting company to enable it.', 'updraftplus'), 'Curl'), 'error'); - return false; - } - - $endpoint_name = isset($opts['endpoint_tld']) && 'de' == $opts['endpoint_tld'] ? 'OneDrive Germany' : 'OneDrive International'; - - if ($this->use_msgraph_api($opts)) { - $this->log("begin cloud upload to {$endpoint_name} (using Microsoft Graph API)"); - } else { - $this->log("begin cloud upload {$endpoint_name} (using Live SDK API)"); - } - - // If the user is using OneDrive for Germany option - if (isset($opts['endpoint_tld']) && 'de' === $opts['endpoint_tld']) { - $odg_warning = sprintf(__('Due to the shutdown of the %1$s endpoint, support for %1$s will be ending soon. You will need to migrate to the Global endpoint in your UpdraftPlus settings. For more information, please see: %2$s', 'updraftplus'), 'OneDrive Germany', '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', 'onedrive_de_migrate'); - } - - try { - $storage = $this->bootstrap(); - if (is_wp_error($storage)) throw new Exception($storage->get_error_message()); - if (!is_object($storage)) throw new Exception("OneDrive service error"); - } 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; - } - - $folder = empty($opts['folder']) ? '' : $opts['folder']; - - $filesize = filesize($from); - $this->onedrive_file_size = $filesize; - - try { - // Check if enough storage space in quota - $quota = $storage->fetchQuota(); - - if (!is_object($quota)) { - - $this->log("quota fetching failed; object returned was a: ".gettype($quota)); - - } else { - - $total = $quota->total; - $available = $quota->remaining; - - if (is_numeric($total) && is_numeric($available)) { - $used = $total - $available; - $used_perc = $total ? round($used*100/$total, 1) : 'n/a'; - $message = sprintf('Your %s quota usage: %s %% used, %s available', 'OneDrive', $used_perc, round($available/1048576, 1).' MB'); - } - - if (isset($available) && -1 != $available && $available < $filesize) { - $this->log("File upload expected to fail: file data remaining to upload ($file) size is ".($filesize)." b (overall file size; $filesize b), whereas available quota is only $available b"); - $this->log(sprintf(__("Account full: your %s account has only %d bytes left, but the file to be uploaded has %d bytes remaining (total size: %d bytes)", 'updraftplus'), 'OneDrive', $available, $filesize, $filesize), 'warning', 'onedrive_expect_to_fail'); - } - } - - } catch (Exception $e) { - $message .= " ".get_class($e).": ".$e->getMessage(); - } - - $this->log($message.'. Upload folder: '.$folder); - - // Ensure directory exists - $pointer = $this->get_pointer($folder, $storage); - - // Perhaps it already exists? (if we didn't get the final confirmation) - try { - $items = $storage->fetchObjects($pointer); - foreach ($items as $item) { - if ($file == $item->getName()) { - if ($item->getSize() >= $filesize) { - $this->log("$file: already uploaded"); - return true; - } else { - $this->log("$file: partially uploaded (".$item->getSize()." < $filesize)"); - } - } - } - } catch (Exception $e) { - - $file_check_msg = "file check: exception: ($file) (".$e->getMessage().") (line: ".$e->getLine().', file: '.$e->getFile().')'; - - /* - 02/08/2018: There's no obvious documentation, but this error code appears to be a token expiry: https://github.com/microsoftgraph/python-sample-auth/issues/10 . - 24/06/2020: "Access token has expired." has been returned on a customer site - It would be nicer to request a new token immediately, but a swift resumption is not bad. - */ - if ((false !== stripos($file_check_msg, 'token') && false !== strpos($file_check_msg, '80049228')) || (false !== stripos($file_check_msg, 'Access token has expired.'))) { - $this->log($file_check_msg.' - matches a token expiry pattern; will schedule a resumption and terminate for now'); - UpdraftPlus_Job_Scheduler::reschedule(60); - UpdraftPlus_Job_Scheduler::record_still_alive(); - die; - } - - $this->log($file_check_msg); - - } - - try { - if (false != ($handle = fopen($from, 'rb'))) { - if ($filesize < $this->chunk_size) { - $storage->createFile($file, $pointer, $handle); - fclose($handle); - } else { - // https://dev.onedrive.com/items/upload_large_files.htm - $path = $folder ? $folder.'/'.$file : $file; - - // This is only used in a corner-case - $this->onedrive_folder = $folder; - - $session_key = "sess_".md5($path); - - $possible_session = $this->jobdata_get($session_key, false, '1d_'.$session_key); - - if (is_object($possible_session) && !empty($possible_session->uploadUrl)) { - $this->log("chunked upload: session appears to be underway/resumable; will attempt resumption"); - $session = $possible_session; - - $state = $storage->getState(); - $upload_status = $storage->apiGet($possible_session->uploadUrl, array()); - - if (!is_object($upload_status) || empty($upload_status->nextExpectedRanges)) { - // One retry - $this->log("Failed to get upload status; making second attempt to request prior to re-starting"); - $upload_status = $storage->apiGet($possible_session->uploadUrl, array()); - } - - if (is_object($upload_status) && !empty($upload_status->nextExpectedRanges)) { - if (is_array($upload_status->nextExpectedRanges)) { - $next_expected = $upload_status->nextExpectedRanges[0]; - } else { - $next_expected = $upload_status->nextExpectedRanges; - } - - if (preg_match('/^(\d+)/', $next_expected, $matches)) { - $uploaded_size = $matches[1]; - $this->log("Resuming OneDrive upload session from byte: $uploaded_size (".serialize($upload_status->nextExpectedRanges).")"); - } else { - $this->log("Could not parse next expected range: ".serialize($upload_status->nextExpectedRanges)); - } - } else { - $clean_state = $state; - if (is_object($state) && !empty($state->token->data->access_token)) $clean_state->token->data->access_token = substr($state->token->data->access_token, 0, 3).'...'; - $this->log("Failed to get upload status - will re-start this upload: service_state=".serialize($clean_state).", upload_status=".serialize($upload_status)); - $this->jobdata_delete($session_key); - } - } - - if (!isset($uploaded_size)) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable - $uploaded_size = 0; - if ($this->use_msgraph_api($opts)) { - $endpoint_tld = isset($opts['endpoint_tld']) ? $opts['endpoint_tld'] : 'com'; - $graph_url = \Onedrive\UpdraftPlus_OneDrive_Account::$types[$endpoint_tld]['graph_url']; - $session = $storage->apiPost($graph_url."/v1.0/me/drive/root:/". rawurlencode($path).':/createUploadSession'); - } else { - $session = $storage->apiPost("https://api.onedrive.com/v1.0/drive/root:/". rawurlencode($path).':/upload.createSession'); - } - if (!is_object($session) || empty($session->uploadUrl)) { - throw new Exception("Failed to create upload session (".serialize($session).")"); - } - $this->jobdata_set($session_key, $session); - } - - $this->onedrive_session = $session; - - $this->onedrive_uploaded_size = $uploaded_size; - - $ret = $updraftplus->chunked_upload($this, $file, $this->method."://".$folder."/".$file, $this->description, $this->chunk_size, $uploaded_size, false); - fclose($handle); - - // If chunked upload appears successful, clear the 'onedrive_expect_to_fail' warning - if (true === $ret) { - $updraftplus->log_remove_warning('onedrive_expect_to_fail'); - } else { - $this->jobdata_delete($session_key); - } - return $ret; - } - - } else { - throw new Exception("Failed to open file for reading: $from"); - } - } catch (Exception $e) { - $this->log($this->description." upload: error: ($file) (".$e->getMessage().") (line: ".$e->getLine().', file: '.$e->getFile().')'); - return false; - } - - // At this point, the upload has suceeded despite expectation of failure - $updraftplus->log_remove_warning('onedrive_expect_to_fail'); - return true; - } - - /** - * Acts as a WordPress options filter - * - * @param Array $onedrive - An array of OneDrive options - * @return Array - the returned array can either be the set of updated OneDrive settings or a WordPress error array - */ - public function options_filter($onedrive) { - - // Get the current options (and possibly update them to the new format) - $opts = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('onedrive'); - - 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: $msg"); - } - // The saved options had a problem; so, return the new ones - return $onedrive; - } - - if (!is_array($onedrive)) return $opts; - - // Remove instances that no longer exist - if (!empty($opts['settings']) && is_array($opts['settings'])) { - foreach ($opts['settings'] as $instance_id => $storage_options) { - if (!isset($onedrive['settings'][$instance_id])) unset($opts['settings'][$instance_id]); - } - } - - if (empty($onedrive['settings'])) return $opts; - - foreach ($onedrive['settings'] as $instance_id => $storage_options) { - $old_client_id = empty($opts['settings'][$instance_id]['clientid']) ? '' : $opts['settings'][$instance_id]['clientid']; - $now_client_id = empty($storage_options['clientid']) ? '' : $storage_options['clientid']; - if (!empty($opts['settings'][$instance_id]['refresh_token']) && $old_client_id != $now_client_id) { - unset($opts['settings'][$instance_id]['refresh_token']); - unset($opts['settings'][$instance_id]['tokensecret']); - unset($opts['settings'][$instance_id]['ownername']); - } - - foreach ($storage_options as $key => $value) { - if ('folder' == $key) $value = trim(str_replace('\\', '/', $value), '/'); - $opts['settings'][$instance_id][$key] = ('clientid' == $key || 'secret' == $key) ? trim($value) : $value; - } - } - return $opts; - } - - public function chunked_upload($file, $fp, $chunk_index, $upload_size, $upload_start, $upload_end) { - - // Already done? - if ($upload_start < $this->onedrive_uploaded_size) return 1; - - $storage = $this->get_storage(); - $opts = $this->get_options(); - - $headers = array( - "Content-Length: $upload_size", - "Content-Range: bytes $upload_start-$upload_end/".$this->onedrive_file_size, - ); - - $empty_object = new stdClass; - - try { - $put_chunk = $storage->apiPut($this->onedrive_session->uploadUrl, $fp, null, $upload_size, $headers); - } catch (Exception $e) { - $this->log($this->description." upload: exception (".get_class($e)."): ($file) (".$e->getMessage().") (line: ".$e->getLine().', file: '.$e->getFile().')'); - - // See HS#6320 and https://github.com/OneDrive/onedrive-api-docs/blob/master/items/upload_large_files.md#handle-commit-errors - if (false !== strpos($e->getMessage(), 'Optimistic concurrency failure during fragmented upload') && 0 == $this->onedrive_uploaded_size) { - - try { - - // It can be the case that the item was completely uploaded, but that $this->onedrive_uploaded_size was zero - - $already_there_perhaps = $this->do_listfiles($file); - foreach ($already_there_perhaps as $file_object) { - // This test is quite conservative - there are other things we could do (if there's ever a need) - if ($file_object['name'] == $file && !empty($file_object['size']) && $file_object['size'] > $this->onedrive_uploaded_size) { - $this->onedrive_uploaded_size = $file_object['size']; - if ($upload_start < $this->onedrive_uploaded_size) { - $this->log("More of file ($upload_start) is uploaded than previous API call indicated "); - return 1; - } - } - } - - // Tried this with just $file - which is what the doc suggests - but OneDrive returned an error with: The name in the provided oneDrive.item does not match the name in the URL - // Update: turned out that OneDrive's error was bogus; the upload was already complete -// $name = $this->onedrive_folder ? $this->onedrive_folder.'/'.$file : $file; - $name = $file; - - $put_url = 'https://api.onedrive.'.$opts['endpoint_tld'].'/v1.0/drive/root:/'. urlencode($this->onedrive_folder); - - $this->log("Trying to PUT probably-completed upload to OneDrive: name=$name, PUT to: $put_url"); - - $commit_body = json_encode(array( - 'name' => $name, - 'description' => null, - '@name.conflictBehavior' => 'replace', - '@content.sourceUrl' => $this->onedrive_session->uploadUrl - )); - - $commit_headers = array( - "Content-Type: application/json", - "Content-Length: ".strlen($commit_body), - ); - - $fp = fopen('php://temp', 'rw+b'); - - if (!$fp) { - $this->log('Trying to PUT probably-completed upload to OneDrive: failed to open php://temp'); - return false; - } - - fwrite($fp, $commit_body); - fseek($fp, 0); - - $commit = $storage->apiPut($put_url, $fp, null, strlen($commit_body), $commit_headers); - - if (is_object($commit) && (!empty($commit->expirationDateTime) || !empty($commit->id) || $commit === $empty_object)) { - $this->log('Trying to PUT probably-completed upload to OneDrive: success'); - return true; - } - $this->log('Trying to PUT probably-completed upload to OneDrive: appears to have failed ('.gettype($commit).')'); - - } catch (Exception $e) { - $this->log('upload commit: exception ('.get_class($e)."): ($file) (".$e->getMessage().") (line: ".$e->getLine().', file: '.$e->getFile().')'); - } - - } - - return false; - } - - // It seems we get an empty response object (but success - i.e. no exception thrown above) when a chunk was already previously uploaded - if (is_object($put_chunk) && (!empty($put_chunk->expirationDateTime) || !empty($put_chunk->id) || $put_chunk === $empty_object)) return true; - - $this->log("Unexpected response when putting chunk $chunk_index: ".serialize($put_chunk)); - return false; - - } - - /** - * Get the OneDrive internal pointer for an indicated folder path - * - * @param String $folder - folder path - * @param Object $storage - storage object that API calls can be made upon - * - * @return String - the pointer - */ - private function get_pointer($folder, $storage) { - - $pointer = null; - try { - $folder_array = explode('/', $folder); - - // Check if folder exists - foreach ($folder_array as $val) { - if ('' == $val) break; // If value is root break; - - $new_pointer = $pointer; - - // Fetch objects in dir - $dirs = $storage->fetchObjects($pointer); - foreach ($dirs as $dir) { - $dirname = $dir->getName(); - if (strtolower($dirname) == strtolower($val) && $dir->isFolder()) { - $new_pointer = $dir->getId(); - break; // This folder exists, we want to select this - } - } - - // If new_pointer is same, path doesn't exist, so create it - if ($pointer == $new_pointer) { - $newdir = $storage->createFolder($val, $pointer); - $new_pointer = $newdir->getId(); - } - $pointer = $new_pointer; - - } - return $pointer; - } catch (Exception $e) { - $this->log("get_pointer($folder) exception: backup may not go into desired folder: ".$e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')'); - return $pointer; - } - } - - public function do_download($file) { - - global $updraftplus; - $opts = $this->get_options(); - - $message = " did not return the expected data"; - - try { - $storage = $this->bootstrap(); - if (!is_object($storage)) throw new Exception('OneDrive service error'); - } catch (Exception $e) { - $message = $e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')'; - $this->log($message); - $this->log($message, 'error'); - return false; - } - - $folder = $opts['folder']; - $pointer = $this->get_pointer($folder, $storage); - - $objs = $storage->fetchObjects($pointer); - foreach ($objs as $obj) { - $obj_name = $obj->getName(); - if ($obj_name == $file && !$obj->isFolder()) { - return $updraftplus->chunked_download($file, $this, $obj->getSize(), true, array($storage, $obj)); - } - } - - $this->log("$file: ".sprintf("%s download: failed: file not found", 'OneDrive')); - $this->log("$file: ".sprintf(__("%s download: failed: file not found", 'updraftplus'), 'OneDrive'), 'error'); - return false; - - } - - public function chunked_download($file, $headers, $data) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - $file_obj = $data[1]; - - $options = array(); - - if (is_array($headers) && !empty($headers['Range']) && preg_match('/bytes=(.*)$/', $headers['Range'], $matches)) { - $options[CURLOPT_RANGE] = $matches[1]; - } - - return $file_obj->fetchContent($options); - - } - - /** - * This function will use the OneDrive SDK to perform the deletion of the files passed in - * - * @param array $files - an array of files to delete - * - * @return boolean|array - if there is an error returns false else returns true when deleting a single file or a response array when deleting multiple - */ - public function do_delete($files) { - $opts = $this->get_options(); - - try { - $storage = $this->bootstrap(); - } catch (Exception $e) { - $storage = $e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')'; - } - - if (is_object($storage) && !is_wp_error($storage)) { - // Get the folder from options - $folder = $opts['folder']; - $folder_array = explode('/', $folder); - - $pointer = null; - // Check if folder exists - foreach ($folder_array as $val) { - if ('' == $val) break; // If value is root break; - - $new_pointer = $pointer; - - // Fetch objects in dir - $dirs = $storage->fetchObjects($pointer); - foreach ($dirs as $dir) { - $dirname = $dir->getName(); - if ($dirname == $val && $dir->isFolder()) { - $new_pointer = $dir->getId(); - break; // This folder exists, we want to select this - } - } - - // If new_pointer is same, path doesn't exist, so can't delete - if ($pointer == $new_pointer) { - $this->log("folder does not exist"); - return 'container_access_error'; - } - $pointer = $new_pointer; - - } // End foreach(). - - $objs = $storage->fetchObjects($pointer); - $objectids = array(); - foreach ($objs as $obj) { - $obj_name = $obj->getName(); - if (in_array($obj_name, $files) && !$obj->isFolder()) $objectids[] = $obj->getID(); - } - - if (!empty($objectids)) { - if (1 == count($objectids)) { - $storage->deleteObject($objectids[0]); - return true; - } else { - return $storage->deleteObjectMulti($objectids); - } - } - - $this->log("file does not exist"); - return 'file_access_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; - } - - public function do_listfiles($match = 'backup_') { - - $opts = $this->get_options(); - - try { - $storage = $this->bootstrap(); - if (!is_object($storage)) throw new Exception('OneDrive service error'); - } catch (Exception $e) { - $storage = $e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')'; - return array(); - } - - // https://dev.onedrive.com/items/list.htm - // OneDrive doesn't (currently - 07-Jul-2016) allow filtering in the request; that has to be done client-side (i.e. here). So, we cache the result (because there are code paths in UD in which we call this multiple times). - - static $last_folder = null; - static $fetched_results = null; - - // Get the folder from options - $folder = $opts['folder']; - - if ($folder != $last_folder || empty($fetched_results)) { - $pointer = $this->get_pointer($folder, $storage); - $fetched_results = $storage->fetchObjects($pointer); - $last_folder = $folder; - } - - $results = array(); - - foreach ($fetched_results as $obj) { - if (!$obj->isFolder()) { - $res = array( - 'name' => $obj->getName(), - 'size' => $obj->getSize() - ); - if (!$match || 0 === strpos($res['name'], $match)) $results[] = $res; - } - } - - return $results; - - } - - /** - * Move the remote post to the redirect URL - * - * @param array $opts - * @return array - */ - public function do_bootstrap($opts) { - - updraft_try_include_file('includes/onedrive/onedrive.php', 'include_once'); - global $updraftplus; - - $opts = $this->get_options(); - - $use_master = $this->use_master($opts); - - /* - The redirect URI has been taken out of the below so that it no longer stores within OPTS - This check if this is the master (local call) or Auth call and sets URI's appropriately - */ - if ($this->use_msgraph_api($opts)) { - $redirect_uri = $this->the_callback; - } elseif ($use_master) { // For live sdk compatibility - $redirect_uri = $this->the_callback.'?ud_source_url='.urlencode(UpdraftPlus_Options::admin_page_url()); - } else { - $redirect_uri = UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-onedrive-auth'; - } - - /* - To save calls to the AUth server, Checking from the saved details (in $opts) from the last OneDrive call - and to see if there needs to be a new call or to re-use the values. This also double Checks to see - if the access_token_timeout is set. if this is a new setup, this would never be set and therfore - initial a first request in order to be saved back to $opts for future calls. - */ - if (!isset($opts['access_token_timeout']) || time() > $opts['access_token_timeout']) { - if ($use_master) { // use_master app - - $endpoint_tld = isset($opts['endpoint_tld']) ? $opts['endpoint_tld'] : 'com'; - $client_id = \Onedrive\UpdraftPlus_OneDrive_Account::get_client_id($endpoint_tld); - $refresh_token = empty($opts['refresh_token']) ? '' : $opts['refresh_token']; - - $args = array( - 'code' => 'ud_onedrive_bootstrap', - 'refresh_token' => $refresh_token, - 'endpoint_tld' => $opts['endpoint_tld'], - ); - // For live sdk compatibility - if ($use_master && !$this->use_msgraph_api($opts)) { - $args['ud_source_url'] = UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-onedrive-auth'; - } - $result = wp_remote_post($this->the_callback, array( - 'timeout' => 60, - 'headers' => apply_filters('updraftplus_auth_headers', ''), - 'body' => $args - )); - - $body = wp_remote_retrieve_body($result); - $result_body_json = base64_decode($body); - $result_body = json_decode($result_body_json); - - } else { // using own app - - $client_id = $opts['clientid']; - - // Obtain new token using refresh token - $args = array( - 'timeout' => 60, - 'body' => array( - 'client_id' => $client_id, - 'redirect_uri' => $redirect_uri, - 'client_secret' => $opts['secret'], - 'refresh_token' => empty($opts['refresh_token']) ? '' : $opts['refresh_token'], - 'grant_type' => 'refresh_token' - ) - ); - if ($this->use_msgraph_api($opts)) { - $token_url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'; - } else { - $token_url = 'https://login.live.com/oauth20_token.srf'; - } - $result = wp_remote_post($token_url, $args); - - $body = wp_remote_retrieve_body($result); - - $result_body = json_decode($body); - } - /** - * Before proceeding, check to make sure no errors returned from OneDrive or CloudFlare. - * If no refresh_token returned, disply Errrors - */ - $http_code = wp_remote_retrieve_response_code($result); - if ($http_code >= 400) { - - $code = 'not_authed'; - - $message = __('An error response was received; HTTP code:', 'updraftplus').' '.$http_code; - - $headers = wp_remote_retrieve_headers($result); - - if (!empty($headers['cf-ray'])) { - $message .= ' CF-Ray: '.$headers['cf-ray']; - } - - if (403 == $http_code) { - $ip_addr = $updraftplus->get_outgoing_ip_address(); - if (false !== $ip_addr && false !== filter_var($ip_addr, FILTER_VALIDATE_IP)) { - $message .= ' IP: '.htmlspecialchars($ip_addr); - - $message .= '
'.__('This most likely means that you share a webserver with a hacked website that has been used in previous attacks.', 'updraftplus').'
'.__('To remove any block, please go here.', 'updraftplus').' '.__('Your IP address:', 'updraftplus').' '.htmlspecialchars($ip_addr); - - $code = 'cloudflare_block'; - } - } - - $message .= ' ('.$code.')'; - - return new WP_Error($code, $message, $result_body); - } - - - if (empty($result_body->refresh_token)) { - global $updraftplus; - - if (is_string($result_body)) { - if (preg_match('/(?:has banned your IP address \(([\.:0-9a-f]+)\))|(?:Why do I have to complete a CAPTCHA\?)/', $result_body, $matches)) { - if (empty($matches[1])) { - $ip_addr = $updraftplus->get_outgoing_ip_address(); - if (false !== $ip_addr && false !== filter_var($ip_addr, FILTER_VALIDATE_IP)) { - $matches[1] = $ip_addr; - } - } - return new WP_Error('banned_ip', sprintf(__("UpdraftPlus.com has responded with 'Access Denied'.", 'updraftplus').'
'.__("It appears that your web server's IP Address (%s) is blocked.", 'updraftplus').' '.__('This most likely means that you share a webserver with a hacked website that has been used in previous attacks.', 'updraftplus').'
'.__('To remove the block, please go here.', 'updraftplus').' ', $matches[1])); - } - } - - $error_log = "Data: ".json_encode($body); - - $error_code = 'no_refresh_token'; - $error_message = sprintf(__('Please re-authorize the connection to your %s account.', 'updraftplus'), 'OneDrive'); - - if (isset($result_body->error)) { - $error_code = $result_body->error; - if (isset($result_body->error_description)) $error_message = $result_body->error_description; - } - - $this->log("no refresh token found: $error_code - $error_log"); - - return new WP_Error('no_refresh_token', 'OneDrive: '.sprintf(__('Account is not authorized (%s).', 'updraftplus'), $error_code).' '.$error_message); - } - - /* - If no errors returned, setup opts values extra details to be saved in $opts for less calls to Auth server - */ - - /* - Adding the expires_in value returned from OneDrive to the current time to to get the expired time - If no expires_in value returned, set to current time so it can bypass the IF check on access_token_timeout - */ - $opts['access_token_timeout'] = (isset($result_body->expires_in) ? (time()+$result_body->expires_in) : time()); - $opts['access_token'] = $result_body->access_token; - $opts['expires_in'] = $result_body->expires_in; - $opts['refresh_token'] = $result_body->refresh_token; - - // save details back to $opts - $this->set_options($opts, true); - } - - // setup array to be sent to oneDrive - - $client_id = (isset($opts['endpoint_tld']) && 'de' == $opts['endpoint_tld']) ? $this->the_germany_client_id : $this->the_client_id; - $onedrive_options = array( - 'client_id' => (empty($opts['clientid']) ? $client_id : $opts['clientid']), - 'state' => (object) array( - 'redirect_uri' => $redirect_uri, - 'token' => (object) array( - 'data' => (object) array( - 'obtained_at' => time(), - 'expires_in' => $opts['expires_in'], - 'access_token' => $opts['access_token'] - ) - ) - ), - 'ssl_verify' => true, - 'use_msgraph_api' => $this->use_msgraph_api($opts), - 'endpoint_tld' => $opts['endpoint_tld'], - ); - - if (UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify')) $onedrive_options['ssl_verify'] = false; - if (!UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts')) $onedrive_options['ssl_capath'] = UPDRAFTPLUS_DIR.'/includes/cacert.pem'; - - $storage = new \Onedrive\Client($onedrive_options); - - $this->set_storage($storage); - - return $storage; - } - - /** - * Whether or not to use the master app - * - * @param Array $opts - options - * - * @return Boolean - */ - protected function use_master($opts) { - if ((!empty($opts['clientid']) && ($opts['clientid'] != $this->the_client_id || $opts['clientid'] != $this->the_germany_client_id)) || (defined('UPDRAFTPLUS_CUSTOM_ONEDRIVE_APP') && UPDRAFTPLUS_CUSTOM_ONEDRIVE_APP)) return false; - return true; - } - - /** - * Whether or not to use msgraph_api - * - * @param Array $opts - options - * - * @return Boolean - */ - protected function use_msgraph_api($opts) { - if ($this->use_master($opts) && isset($opts['use_msgraph_api']) && $opts['use_msgraph_api']) { - return true; - } - return false; - } - - /** - * Whether or not options exist - * - * @param Array $opts - options - * - * @return Boolean - */ - public function options_exist($opts) { - if ((is_array($opts) && !empty($opts['clientid']) && !empty($opts['secret'])) || ($this->use_master($opts) && !empty($opts['refresh_token']))) return true; - return false; - } - - /** - * Is called by the authenticate link and calls auth_request or auth_token - * Is a multipurpose function for getting request - */ - public function action_auth() { - if (isset($_GET['code'])) { - // Shouldn't need to change this for user_master, as should never arrive here is that is set - $this->auth_token($_GET['code']); - } elseif (isset($_GET['state'])) { - $parts = explode(':', $_GET['state']); - $state = $parts[0]; - if ('success' == $state) { - add_action('all_admin_notices', array($this, 'show_authed_admin_warning')); - } elseif ('token' == $state) { - // For when master OneDrive app used - $encoded_token = stripslashes($_GET['token']); - $token = json_decode($encoded_token); - $this->do_complete_authentication($state, $token, false); - } - } elseif (isset($_GET['updraftplus_onedriveauth'])) { - if ('doit' == $_GET['updraftplus_onedriveauth']) { - $this->action_authenticate_storage(); - } elseif ('deauth' == $_GET['updraftplus_onedriveauth']) { - $this->action_deauthenticate_storage(); - } - } - } - - /** - * This method will reset any saved options and start the bootstrap process for an authentication - * - * @param String $instance_id - the instance id of the settings we want to authenticate - */ - public function do_authenticate_storage($instance_id) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- This is called from backup-module and think this can stay - // Clear out the existing credentials - $opts = $this->get_options(); - $opts['refresh_token'] = ''; - // Set a flag so we know this authentication is in progress - $opts['auth_in_progress'] = true; - $this->set_options($opts, true); - try { - $this->auth_request(); - } catch (Exception $e) { - $this->log(sprintf(__("%s error: %s", 'updraftplus'), __("Authentication", 'updraftplus'), $e->getMessage()), 'error'); - } - } - - /** - * 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) { - $opts = $this->get_options(); - return $this->auth_token_stage2($code, $opts, $return_instead_of_echo); - } - - public function show_authed_admin_warning($return_instead_of_echo = false) { - global $updraftplus_admin; - - $opts = $this->get_options(); - - if (empty($opts['refresh_token'])) return; - // $updraftplus_refresh_token = $opts['refresh_token']; - - $message = ''; - $warning_class = 'updated'; - try { - // Remove existing object - $this->set_storage(null); - - $storage = $this->bootstrap($opts); - - if (false != $storage && !is_wp_error($storage)) { - - $quota = $storage->fetchQuota(); - $total = $quota->total; - $available = $quota->remaining; - - if (is_numeric($total) && is_numeric($available)) { - $used = $total - $available; - $used_perc = $total ? round($used*100/$total, 1) : 'n/a'; - $message .= sprintf(__('Your %s quota usage: %s %% used, %s available', 'updraftplus'), 'OneDrive', $used_perc, round($available/1048576, 1).' MB'); - } - - $account_info = $storage->fetchAccountInfo(); - - $opts['ownername'] = ''; - if (!empty($account_info->user)) { - $opts['ownername'] = $account_info->user->displayName; - $message .= ".
".sprintf(__('Your %s account name: %s', 'updraftplus'), 'OneDrive', htmlspecialchars($account_info->user->displayName)); - } - $this->set_options($opts, true); - - } else { - if (is_wp_error($storage) && ('cloudflare_block' == $storage->get_error_code() || 'not_authed' == $storage->get_error_code())) { - $message .= __('However, subsequent access attempts failed:', 'updraftplus'); - $message .= '
'.$storage->get_error_message(); - $warning_class = 'error'; - } else { - if (is_wp_error($storage)) throw new Exception($storage->get_error_message()); - if (!is_object($storage)) throw new Exception("OneDrive service error"); - } - } - } catch (Exception $e) { -// $errs = $e->getErrors(); - $errs = array(array('reason' => $e->getCode(), 'message' => $e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')')); - $message .= __('However, subsequent access attempts failed:', 'updraftplus'); - if (is_array($errs)) { - $message .= '
    '; - foreach ($errs as $err) { - $message .= '
  • '; - if (!empty($err['reason'])) $message .= ''.htmlspecialchars($err['reason']).': '; - if (!empty($err['message'])) { - if ('cloudflare_block' == $err['reason'] || 'not_authed' == $err['reason']) $message .= $err['message']; - else $message .= htmlspecialchars($err['message']); - } else { - $message .= htmlspecialchars(serialize($err)); - } - $message .= '
  • '; - } - $message .= '
'; - } else { - $message .= htmlspecialchars(serialize($errs)); - } - $warning_class = 'error'; - } - - $final_message = __('Success', 'updraftplus').': '.sprintf(__('you have authenticated your %s account.', 'updraftplus'), __('OneDrive', 'updraftplus')).' '.$message; - - try { - if ($return_instead_of_echo) { - return "

{$final_message}

"; - } else { - $updraftplus_admin->show_admin_warning($final_message, $warning_class); - } - } catch (Exception $e) { - if ($return_instead_of_echo) { - return "

{$e->getMessage()}

"; - } else { - $updraftplus_admin->show_admin_warning($e->getMessage()); - } - } - } - - private function get_onedrive_perms() { - return json_encode(array( - 'profile' => array('read' => true), - 'filesystem' => array('read' => true, 'write' => true) - )); - } - - 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', 'multi_delete', 'conditional_logic', 'manual_authentication'); - } - - public function get_default_options() { - return array( - 'clientid' => '', - 'secret' => '', - 'url' => '', - 'folder' => '', - 'endpoint_tld' => 'com', - ); - } - - /** - * Over-rides the parent to allow this method to output extra information about using the correct account for OAuth authentication - * - * @return Boolean - return false so that no extra information is output - */ - public function output_account_warning() { - return true; - } - - /** - * Directs users to the login/authentication page - */ - private function auth_request() { - - updraft_try_include_file('includes/onedrive/onedrive.php', 'include_once'); - - $opts = $this->get_options(); - $use_master = $this->use_master($opts); - - // Get the client id - if ($use_master) { - $client_id = (isset($opts['endpoint_tld']) && 'de' == $opts['endpoint_tld']) ? $this->the_germany_client_id : $this->the_client_id; - } else { - $client_id = empty($opts['clientid']) ? '' : $opts['clientid']; - } - - $instance_id = isset($_GET['updraftplus_instance']) ? $_GET['updraftplus_instance'] : ''; - - if (!$use_master) { - $redirect_uri = UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-onedrive-auth'; - $callback_uri = ''; - } else { - $redirect_uri = $this->the_callback; - $callback_uri = UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-onedrive-auth&endpoint-tld='.$opts['endpoint_tld']; - } - - // For all permissions https://developer.microsoft.com/en-us/graph/docs/concepts/permissions_reference - if ($use_master) { - $scope = array( - 'openid', - // 'wl.basic', - // 'wl.contacts_skydrive', - // 'wl.skydrive_update', - // 'wl.offline_access', - 'offline_access', - // 'onedrive.readwrite', - 'files.readwrite.all', - ); - } else { - $scope = array( - 'wl.signin', - // 'wl.basic', - // 'wl.contacts_skydrive', - // 'wl.skydrive_update', - 'wl.offline_access', - 'onedrive.readwrite', - ); - } - - // Instantiate OneDrive client - $onedrive = new \Onedrive\Client(array( - 'client_id' => $client_id, - 'use_msgraph_api' => $use_master, - 'endpoint_tld' => $opts['endpoint_tld'], - )); - - $url = $onedrive->getLogInUrl($scope, $redirect_uri, array(), $instance_id, $callback_uri); - - if (headers_sent()) { - $this->log(sprintf(__('The %s authentication could not go ahead, because something else on your site is breaking it. Try disabling your other plugins and switching to a default theme. (Specifically, you are looking for the component that sends output (most likely PHP warnings/errors) before the page begins. Turning off any debugging settings may also help).', 'updraftplus'), 'OneDrive'), 'error'); - } else { - header('Location: '.esc_url_raw($url)); - } - } - - private function auth_token($code) { - - $opts = $this->get_options(); - $use_master = $this->use_master($opts); - - $secret = (empty($opts['secret'])) ? '' : $opts['secret']; - - if ($use_master) { - $client_id = (isset($opts['endpoint_tld']) && 'de' == $opts['endpoint_tld']) ? $this->the_germany_client_id : $this->the_client_id; - } else { - $client_id = (empty($opts['clientid'])) ? '' : $opts['clientid']; - } - - updraft_try_include_file('includes/onedrive/onedrive.php', 'include_once'); - - if (!$use_master) { - $callback = UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-onedrive-auth'; - } else { - $callback = $this->the_callback; - } - - $onedrive = new \Onedrive\Client(array( - 'client_id' => $client_id, - 'state' => (object) array('redirect_uri' => $callback), - // Control comes in this functions, if udp uses custom onedrive app, So it is false - 'use_msgraph_api' => false, - )); - - $onedrive->obtainAccessToken($secret, $code); - $token = $onedrive->getState(); - - $this->auth_token_stage2($token, $opts, false); - } - - /** - * Split off so can be accessed directly when using master UDP OneDrive app - * - * @param string $token - the oauth token array - * @param string $opts - the remote storage options - * @param boolean $return_instead_of_echo - a boolean to indicate if we should return the result or echo it - * - * @return void - */ - private function auth_token_stage2($token, $opts, $return_instead_of_echo = false) { - - if (!empty($token->token->data->refresh_token)) { - $opts['use_msgraph_api'] = true; - $opts['refresh_token'] = $token->token->data->refresh_token; - // 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_warning($return_instead_of_echo); - } else { - header('Location: '.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-onedrive-auth&state=success'); - } - - } else { - if (!empty($token->token->data->error)) { - $this->log(__('authorization failed:', 'updraftplus').' '.$token->token->data->error_description, 'error'); - } else { - $this->log(__('authorization failed:', 'updraftplus').' '."OneDrive service error: ".serialize($token), 'error'); - } - } - } - - /** - * Get the pre configuration template - * - * @return String - the template - */ - public function get_pre_configuration_template() { - ?> - - - - {{#unless use_master}} - {{#if is_ip_host}} - {{!-- Of course, there are other things that are effectively 127.0.0.1. This is just to help. --}} -

- - {{ip_host_label}} - -

-
- {{else}} -

- {{{non_ip_host_label}}} -

-
- {{/if}} -

- - {{developer_console_link_text}} - -

-

- - {{setup_guide_link_text}} - -

-
- {{/unless}} - - {{{curl_existence_label}}} -

- {{{privacy_policy}}} -

- - - - {{#unless use_master}} - - {{input_client_id_label}}: -
{{input_client_id_title}} - - - {{input_client_secret_label}}: - - - {{/unless}} - - {{input_folder_label}} - - - - - {{#if use_master}} - - {{input_endpoint_label}} - - - - - {{/if}} - - - {{authentication_label}} - - -

- {{#if is_already_authenticated}} - {{authentication_already_authenticated_label}} - {{deauthentication_link_text}} - {{/if}} - {{#if ownername_sentence}} - {{ownername_sentence}} - {{/if}} -

-

- {{account_warning_label}} - {{{authentication_link_text}}} -

- - - UPDRAFTPLUS_URL.'/images/onedrive.png', - 'curl_existence_label' => wp_kses($updraftplus_admin->curl_check('OneDrive', true, 'onedrive hide-in-udc', false), $this->allowed_html_for_content_sanitisation()), - 'privacy_policy' => wp_kses(sprintf(__('Please read %s for use of our %s authorization app (none of your backup data is sent to us).', 'updraftplus'), ''.__('this privacy policy', 'updraftplus').'', 'OneDrive'), $this->allowed_html_for_content_sanitisation()), - 'developer_console_link_text' => __('Create OneDrive credentials in your OneDrive developer console.', 'updraftplus'), - 'setup_guide_link_text' => __('For more detailed instructions, follow this link.', 'updraftplus'), - 'ip_host_label' => sprintf(__('This site uses a URL which is either non-HTTPS, or is localhost or 127.0.0.1 URL. As such, you must use the main %s %s App to authenticate with your account.', 'updraftplus'), 'UpdraftPlus', 'OneDrive'), - 'non_ip_host_label' => wp_kses(__('You must add the following as the authorized redirect URI in your OneDrive console (under "API Settings") when asked', 'updraftplus').': '.UpdraftPlus_Options::admin_page_url().'', $this->allowed_html_for_content_sanitisation()), - 'input_client_id_label' => __('OneDrive', 'updraftplus').' '.__('Client ID', 'updraftplus'), - 'input_client_id_title' => __('If OneDrive later shows you the message "unauthorized_client", then you did not enter a valid client ID here.', 'updraftplus'), - 'input_client_secret_label' => __('OneDrive', 'updraftplus').' '.__('Client Secret', 'updraftplus'), - 'input_client_secret_type' => apply_filters('updraftplus_admin_secret_field_type', 'password'), - 'input_folder_label' => 'OneDrive '.__('folder', 'updraftplus'), - 'input_folder_title' => sprintf(__('Enter the path of the %s folder you wish to use here.', 'updraftplus'), 'OneDrive').' '.__('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'), 'OneDrive account').' '.sprintf(__('N.B. %s is not case-sensitive.', 'updraftplus'), 'OneDrive'), - 'input_endpoint_label' => __('Account type', 'updraftplus'), - 'input_endpoint_option_labels' => array( - 'com' => __('OneDrive International', 'updraftplus'), - 'de' => __('OneDrive Germany', 'updraftplus'), - ), - 'authentication_label' => sprintf(__('Authenticate with %s', 'updraftplus'), 'OneDrive'), - 'authentication_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()]), - 'deauthentication_nonce' => wp_create_nonce($this->get_id().'_deauth_nonce'), - 'account_warning_label' => __('Ensure you are logged into the correct account before continuing.', 'updraftplus'), - 'authentication_link_text' => wp_kses(sprintf(__("After you have saved your settings (by clicking 'Save Changes' below), then come back here once and follow this link to complete authentication with %s.", 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]), $this->allowed_html_for_content_sanitisation()), - ); - return wp_parse_args($properties, $this->get_persistent_variables_and_methods()); - } - - /** - * Modifies handerbar template options - * - * @param array $opts - * @return array - Modified handerbar template options - */ - protected function do_transform_options_for_template($opts) { - $opts['use_master'] = (bool) apply_filters('updraftplus_onedrive_use_master', $this->use_master($opts)); - $site_host = parse_url(network_site_url(), PHP_URL_HOST); - $site_scheme = parse_url(network_site_url(), PHP_URL_SCHEME); - $opts['is_ip_host'] = (bool) apply_filters('updraftplus_onedrive_is_ip_host', ('127.0.0.1' == $site_host || '::1' == $site_host || 'localhost' == $site_host || 'https' != $site_scheme)); - $opts['folder'] = (empty($opts['folder'])) ? '' : untrailingslashit($opts['folder']); - $opts['endpoint_tld'] = (empty($opts['endpoint_tld'])) ? 'com' : untrailingslashit($opts['endpoint_tld']); - $opts['clientid'] = (empty($opts['clientid']) || $opts['use_master']) ? '' : $opts['clientid']; - $opts['secret'] = (empty($opts['secret']) || $opts['use_master']) ? '' : $opts['secret']; - $opts['is_already_authenticated'] = (!empty($opts['refresh_token'])); - if (!empty($opts['refresh_token']) && !empty($opts['ownername'])) { - $opts['ownername_sentence'] = sprintf(__("Account holder's name: %s.", 'updraftplus'), $opts['ownername']); - } - 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( - 'access_token', - 'refresh_token', - 'access_token_timeout', - ); - } -} - -// Do *not* instantiate here; it is a storage module, so is instantiated on-demand -// $updraftplus_addons_onedrive = new UpdraftPlus_Addons_RemoteStorage_onedrive; diff --git a/wp-content/plugins/updraftplus/addons/pcloud.php b/wp-content/plugins/updraftplus/addons/pcloud.php deleted file mode 100644 index 3cca142d..00000000 --- a/wp-content/plugins/updraftplus/addons/pcloud.php +++ /dev/null @@ -1,789 +0,0 @@ -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; - - // 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) { - 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 stoage 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'), $this->description)); - $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() { - ?> - - - {{storage_image_title}} -
-

- {{{storage_long_description}}} -

- - - - - : - - {{folder_path}} - - - - {{authentication_label}}: - - {{#if is_authenticated}} -

- {{already_authenticated_label}} - {{deauthentication_link_text}} -

- {{/if}} - {{#if ownername_sentence}} -
- {{ownername_sentence}} - {{/if}} -

{{{authentication_link_text}}}

- - - - 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'), ''.__('this privacy policy', 'updraftplus').'', $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(__("After 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. Try disabling your other plugins and switching to a default theme. (Specifically, you are looking for the component that sends output (most likely PHP warnings/errors) before the page begins. Turning off any debugging settings may also help).', ''), 'pCloud'), 'error'); - } else { - header('Location: https://my.pcloud.com/oauth2/authorize?'.http_build_query($params, null, '&')); - } - } - - /** - * 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 = "".__('Success:', 'updraftplus').' '.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 .= ".
".sprintf(__('Your %s account name: %s', 'updraftplus'), 'pCloud', htmlspecialchars($info['email'])); - - $message .= '
'.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 .= "
".htmlspecialchars($accountinfo_err); - } - - if ($return_instead_of_echo) { - return "

{$message}

"; - } else { - $updraftplus_admin->show_admin_warning($message); - } - - } - - /** - * This basically reproduces the relevant bits of bootstrap.php from the SDK - * - * @return object - * @throws Exception Throws standart 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; - } -} diff --git a/wp-content/plugins/updraftplus/addons/reporting.php b/wp-content/plugins/updraftplus/addons/reporting.php deleted file mode 100644 index 0dddae5e..00000000 --- a/wp-content/plugins/updraftplus/addons/reporting.php +++ /dev/null @@ -1,579 +0,0 @@ -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 - if (!is_array($backup) || empty($backup['label'])) return $date; - if ($simple_format) { - return $date.' - '.htmlspecialchars($backup['label']); - } else { - return $date.''.htmlspecialchars($backup['label']).''; - } - } - - /** - * 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 .= '

-

'; - - 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 - 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 - - 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(); - ?> - -

-

UpdraftPlus '.$updraftplus->version); ?>

-do_notice(false, 'report', true); - if ($ws_advert) { - echo '
'.$ws_advert.'
'; - } -?> -
-
-
-
-
'.htmlspecialchars($msg['key']).'
'.htmlspecialchars($msg['val']).'
'; - } - } - echo $extra_msg; -?> -
-error_count() > 0) { - echo '

'.__('Errors', 'updraftplus')."

\n
    "; - foreach ($updraftplus->errors as $err) { - if (is_wp_error($err)) { - foreach ($err->get_error_messages() as $msg) { - echo "
  • ".htmlspecialchars(rtrim($msg))."
  • \n"; - } - } elseif (is_array($err) && 'error' == $err['level']) { - echo "
  • ".htmlspecialchars(rtrim($err['message']))."
  • \n"; - } elseif (is_string($err)) { - echo "
  • ".htmlspecialchars(rtrim($err))."
  • \n"; - } - } - echo "
\n"; - } - if (is_array($warnings) && count($warnings) >0) { - echo '

'.__('Warnings', 'updraftplus')."

\n
    "; - foreach ($warnings as $err) { - echo "
  • ".rtrim($err)."
  • \n"; - } - echo "
\n"; - echo '

'.__('Note that warning messages are advisory - the backup process does not stop for them. 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').'

'; - } - ?> -

-

-
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 $show_services."

\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); - } - } - - if (!empty($history)) { - foreach ($history as $key => $val) { - if ('db' == strtolower(substr($key, 0, 2)) && '-size' != substr($key, -5, 5)) { - echo $updraftplus->printfile(__('Database', 'updraftplus'), $history, $key, $checksums, $jobdata); - } - } - } - - echo '

'.__('The log file has been attached to this email.', 'updraftplus')."

\n\n"; - - if ($debug) { - echo '

'.__('Debugging information', 'updraftplus')."

\n
";
-	print chunk_split(base64_encode(serialize($jobdata)), 76, "\n");
-	print "\n";
-	print chunk_split(base64_encode(serialize($history)), 76, "\n");
-	echo "
"; - } - - $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('#\]*)\>.*\#', '', $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)) { - if (empty($this->added_phpmailer_init_action)) { - $this->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) { - if ($error_count > 0) { - $subject .= sprintf(__(' (with errors (%s))'), $error_count); - } elseif ($warning_count >0) { - $subject .= sprintf(__(' (with warnings (%s))'), $warning_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 - $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 - $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() { - ?> - - - '.__('Send reports', 'updraftplus').': - '; - - // 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 .= '

'.__('Enter addresses here to have a report sent to them when a backup job finishes.', 'updraftplus').'

'; - - $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 .= '

'.__('Add another address...', 'updraftplus').'

'; - - $out .= ' - '; - - return $out; - } - - /** - * Renders reporting expert settings - */ - public function configprint_expertoptions() { - ?> - - : - >
- - '; - - $out .= ''; - - $out .= ''; - - $out .= ''; - - $out .= '
'; - - $out .= '
'; - - $out .= ''; - - 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 fisrt, 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 = ''.$link.''; - } - - return $link; - } -} diff --git a/wp-content/plugins/updraftplus/addons/s3-enhanced.php b/wp-content/plugins/updraftplus/addons/s3-enhanced.php deleted file mode 100644 index c5abddcb..00000000 --- a/wp-content/plugins/updraftplus/addons/s3-enhanced.php +++ /dev/null @@ -1,569 +0,0 @@ - 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(); - ?> - - - {{#> updraft_s3_apikeysetting}} - {{api_key_setting_default_label}} - {{/updraft_s3_apikeysetting}} - - - __('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 templete 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 }} - - {{input_storage_class_label}}:
{{input_storage_class_text}} - - - - - - {{input_server_encryption_label}}:
{{input_server_encryption_text}} - - - - {{api_key_setting_premium_label}} - 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 - - 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 - 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 - 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 - } 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. 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))."
".htmlspecialchars(sprintf(__("Access Key: %s", 'updraftplus'), $key))."
".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) { - ?> -
-

- -

- -

- -

- - - - - - - - s3:// - - - - - - - - - -

- -
- - - - -
- -
- - - - - - get_options(); - - if (!array($options)) return new WP_Error('no_settings', sprintf(__('No %s settings were found', 'updraftplus'), 'SCP/SFTP')); - - if (empty($options['host'])) return new WP_Error('no_settings', sprintf(__('No %s found', 'updraftplus'), __('SCP/SFTP host setting', 'updraftplus'))); - if (empty($options['user'])) return new WP_Error('no_settings', sprintf(__('No %s found', 'updraftplus'), __('SCP/SFTP user setting', 'updraftplus'))); - if (empty($options['pass']) && empty($options['key'])) return new WP_Error('no_settings', sprintf(__('No %s found', 'updraftplus'), __('SCP/SFTP password/key', 'updraftplus'))); - $host = $options['host']; - $user = $options['user']; - $pass = empty($options['pass']) ? '' : $options['pass']; - $key = empty($options['key']) ? '' : $options['key']; - $port = empty($options['port']) ? 22 : (int) $options['port']; - $fingerprint = empty($options['fingerprint']) ? '' : $options['fingerprint']; - $path = empty($options['path']) ? '' : $options['path']; - $scp = !empty($options['scp']); - - $this->path = $path; - - $sftp = $this->connect($host, $port, $fingerprint, $user, $pass, $key, $scp); - if (is_wp_error($sftp)) return $sftp; - - // So far, so good - if ($path) { - if ($scp) { - // May fail - e.g. if directory already exists, or if the remote shell is restricted - @$this->ssh->exec('mkdir '.$this->possibly_escapeshellarg($path));// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - // N.B. - have not changed directory (since cd may not be an available command) - } else { - @$sftp->mkdir($path);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - // See if the directory now exists - if (!$sftp->chdir($path)) { - @$sftp->disconnect();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - return new WP_Error('nochdir', __("Check your file permissions: Could not successfully create and enter directory:", 'updraftplus')." $path"); - } - } - } - if (!empty($fingerprint)) { - $match_fingerprint = $this->validate_fingerprint($fingerprint); - if (!$match_fingerprint) { - return new WP_Error('invalid_fingerprint', __("Fingerprints don't match.", 'updraftplus')); - } - } - - return $sftp; - - } - - /** - * Pass through to escapeshellarg() if the function is present or if the argument needs escaping. The purpose of this is that some hosts have pointlessly disabled escapeshellarg(); so we avoid the error that comes from calling it if it is not going to do anything non-trivial. - * - * @param String $arg - * - * @return String - */ - private function possibly_escapeshellarg($arg) { - - if (function_exists('escapeshellarg')) return escapeshellarg($arg); - - // If there is nothing to escape, then just add the quotes; see: https://www.php.net/manual/en/function.escapeshellarg.php . Note that whether we are running on Windows or not is irrelevant, since the command is being passed to the remote shell - if (!preg_match('#[\'"\%\!\\\\]#', $arg)) { - return "'".$arg."'"; - } - - // Since the function does not exist, and since escaping was needed, this will now report the necessary error to the user. - return escapeshellarg($arg); - - } - - public function upload_files($ret, $backup_array) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - - global $updraftplus; - $sftp = $this->do_connect_and_chdir(); - if (is_wp_error($sftp)) { - foreach ($sftp->get_error_messages() as $msg) { - $this->log($msg); - $this->log($msg, 'error'); - } - return false; - } - - if (empty($this->scp)) { - $this->log("Successfully logged in"); - } else { - $this->log("SCP: Successfully logged in"); - } - - $any_failures = false; - - $updraft_dir = $updraftplus->backups_dir_location().'/'; - - foreach ($backup_array as $file) { - $this->log("upload: attempt: $file"); - - $this->sftp_path = $updraft_dir.'/'.$file; - $this->sftp_size = max(filesize($updraft_dir.'/'.$file), 1); - - $this->last_logged_at = 0; - - if (empty($this->scp)) { - - // SFTP - - $this->sftp_began_at = 0; - - try { - $remote_stat = $sftp->stat($file); - $current_remote_size = (is_array($remote_stat) && isset($remote_stat['size']) && $remote_stat['size'] > 0) ? $remote_stat['size'] : 0; - if ($current_remote_size > 0) { - $this->sftp_began_at = $current_remote_size; - $this->log('File exists remotely; upload will resume; remote size is: '.round($current_remote_size/1024, 2).' KB'); - } - } catch (Exception $e) { - $this->log('Exception when stating remote file ('.get_class($e).'): '.$e->getMessage()); - $current_remote_size = 0; - } - - if ($current_remote_size >= $this->sftp_size || $sftp->put($file, $updraft_dir.'/'.$file, NET_SFTP_LOCAL_FILE, $current_remote_size, $current_remote_size, array($this, 'sftp_progress_callback'))) { - $updraftplus->uploaded_file($file); - } else { - $any_failures = true; - $this->log('ERROR: SFTP: Failed to upload file: '.$file); - $this->log(__('Error: Failed to upload', 'updraftplus').": $file", 'error'); - } - } else { - - // SCP - - $rfile = empty($this->path) ? $file : trailingslashit($this->path).$file; - if ($sftp->put($rfile, $updraft_dir.'/'.$file, NET_SCP_LOCAL_FILE, array($this, 'sftp_progress_callback'))) { - $updraftplus->uploaded_file($file); - } else { - $any_failures = true; - $this->log('ERROR: SCP: Failed to upload file: '.$file); - $this->log(sprintf(__('%s Error: Failed to upload', 'updraftplus'), 'SCP').": $file", 'error'); - } - } - } - - // In the array we used to pass (before 1.16.58) 'sftp_object' => $sftp; but this was not multi-instance compatible - return $any_failures ? null : array(); - - } - - public function sftp_progress_callback($sent) { - global $updraftplus; - $bytes_sent = empty($this->sftp_began_at) ? $sent : $this->sftp_began_at + $sent; - if ($bytes_sent > $this->last_logged_at + 1048576) { - $perc = empty($this->sftp_size) ? 0 : round(100*$bytes_sent / $this->sftp_size, 1); - $updraftplus->record_uploaded_chunk($perc, '', $this->sftp_path); - $this->last_logged_at = $bytes_sent; - } - } - - public function delete_files($ret, $files, $sftp_arr = false) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - - if (is_string($files)) $files = array($files); - - if ($sftp_arr && isset($sftp_arr['sftp_object'])) { - $sftp = $sftp_arr['sftp_object']; - } else { - $sftp = $this->do_connect_and_chdir(); - if (is_wp_error($sftp)) { - foreach ($sftp->get_error_messages() as $msg) { - $this->log($msg); - $this->log($msg, 'error'); - } - return false; - } - } - - $some_success = false; - - foreach ($files as $file) { - - if (empty($this->scp)) { - $this->log("Delete remote: $file"); - } else { - $this->log("SCP: Delete remote: $file"); - } - - if (empty($this->scp)) { - if (!$sftp->delete($file, false)) { - $this->log("Delete failed: $file"); - } else { - $some_success = true; - } - } else { - $rfile = empty($this->path) ? $file : trailingslashit($this->path).$file; - if (!$this->ssh->exec('rm -f '.$this->possibly_escapeshellarg($rfile))) { - $this->log("SCP: Delete failed: $rfile"); - } else { - $some_success = true; - } - } - } - - return $some_success; - - } - - public function listfiles($match = 'backup_') { - $sftp = $this->do_connect_and_chdir(); - if (is_wp_error($sftp)) return $sftp; - - $results = array(); - - if ($this->scp) { - - $cdcom = empty($this->path) ? '' : "cd ".trailingslashit($this->path)." && "; - - $nosizes = false; - - if (false == ($exec = $this->ssh->exec($cdcom."ls -l ${match}*"))) { - $nosizes = true; - $exec = $this->ssh->exec($cdcom."ls -1 ${match}*"); - } - if (false != $exec) { - foreach (explode("\n", $exec) as $str) { - if ($nosizes) { - if (0 === strpos($str, $match)) $results[] = array('name' => $str); - } elseif (!$nosizes && preg_match('/^[^dls].*\s(\d+)\s+\S+\s+\d+\s+([:0-9]+)\s+'.$match.'(.*)$/', $str, $matches)) { - $results[] = array('name' => $match.$matches[3], 'size' => $matches[1]); - } - } - } - - } else { - $dirlist = $sftp->rawlist(); - if (!is_array($dirlist)) return array(); - - foreach ($dirlist as $path => $stat) { - if (0 === strpos($path, $match)) $results[] = array('name' => $path, 'size' => $stat['size']); - unset($dirlist[$path]); - } - - } - - return $results; - } - - public function download_file($ret, $file) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - - global $updraftplus; - - $sftp = $this->do_connect_and_chdir(); - if (is_wp_error($sftp)) { - foreach ($sftp->get_error_messages() as $msg) { - $this->log($msg); - $this->log($msg, 'error'); - } - return false; - } - - $fullpath = $updraftplus->backups_dir_location().'/'.$file; - - $rfile = (empty($this->scp) || empty($this->path)) ? $file : trailingslashit($this->path).$file; - if (!$sftp->get($rfile, $fullpath)) { - $this->log("Error: Failed to download: $rfile"); - $this->log(__('Error: Failed to download', 'updraftplus').": $rfile", 'error'); - return false; - } - return true; - } - - /** - * Open a connection to the SSH server - * - * @param String $host - SSH server hostname - * @param Integer $port - TCP port to connect to - * @param String $fingerprint - fingerprint to check (not currently implemented) - * @param String $user - login username - * @param String $pass - login password - * @param String $key - RSA private key to use for logging in (an alternative to a password) - * @param Boolean $scp - if set, then SCP will be used; otherwise SFTP - * @param Boolean $debug - debugging mode: will ask phpseclib to log (which, being controlled by constants, may not be possible if they are already set) - * - * @return WP_Error|Net_SSH2|Net_SCP - */ - private function connect($host, $port = 22, $fingerprint = '', $user = '', $pass = '', $key = '', $scp = false, $debug = false) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - - global $updraftplus; - - $this->scp = $scp; - - $timeout = (defined('UPDRAFTPLUS_SFTP_TIMEOUT') && is_numeric(UPDRAFTPLUS_SFTP_TIMEOUT)) ? UPDRAFTPLUS_SFTP_TIMEOUT : 15; - - if ($scp) { - $ensure_phpseclib = $updraftplus->ensure_phpseclib('Net_SSH2'); - $updraftplus->ensure_phpseclib('Net_SCP'); - } else { - $ensure_phpseclib = $updraftplus->ensure_phpseclib('Net_SFTP'); - } - - if (is_wp_error($ensure_phpseclib)) return $ensure_phpseclib; - - // N.B. The same NET_SFTP_* constants exist; but as this point, we're only testing login, so will stick with SSH2 - if ($debug) { - if (!defined('NET_SSH2_LOGGING')) { - // Alternative: NET_SFTP_LOG_SIMPLE. phpseclib source says that NET_SSH2_LOG_COMPLEX is most useful for SSH2 - define('NET_SSH2_LOGGING', NET_SSH2_LOG_COMPLEX); - } elseif (NET_SSH2_LOGGING != NET_SSH2_LOG_COMPLEX) { - $this->log("NET_SSH2_LOGGING: constant was already set; value not as desired (value=".NET_SSH2_LOGGING.", desired=".NET_SSH2_LOG_COMPLEX.")"); - } - } - - $connection_class = $scp ? 'Net_SSH2' : 'Net_SFTP'; - - $this->ssh = new $connection_class($host, $port, $timeout); - - if (!empty($key)) { - $updraftplus->ensure_phpseclib('Crypt_RSA'); - $updraftplus->ensure_phpseclib('Math_BigInteger'); - $rsa = new Crypt_RSA(); - if (false === $rsa->loadKey($key)) { - if (preg_match('/Encryption: (.+)/i', $key, $matches)) { - $encryption = trim($matches[1]); - if ('none' !== $encryption) return new WP_Error('no_key_passphrase', __("The key provided is encrypted. You need to provide the unencrypted key (see: https://updraftplus.com/faqs/why-must-i-use-a-non-encrypted-sftp-key/).", 'updraftplus')); - } - if (empty($pass)) return new WP_Error('no_load_key', __('The key provided was not in a valid format, or was corrupt.', 'updraftplus')); - } else { - $pass = $rsa; - } - } - - // See: https://github.com/phpseclib/phpseclib/issues/1271#issuecomment-390417276 . Default is 10s. - $this->ssh->setTimeout(35); - - // Ensure phpseclib Crypt_Blowfish is loaded, over PEAR's - $updraftplus->ensure_phpseclib('Crypt_Blowfish'); - - if (!$this->ssh->login($user, $pass)) { - $error_data = null; - $message = 'SSH 2 login failed'; - if ($debug) { - $error_data = array("UpdraftPlus debug mode is on: detailed debugging data follows (some data may be base-64 encoded)\n"); - $errors = $this->ssh->getErrors(); - - if (is_array($errors)) { - foreach ($errors as $err) { - // Sending raw data to the browser makes JSON-decoding on the browser unhappy - $error_data[] = base64_encode($err); - } - } - - // "Returns a string if NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX, an array if NET_SSH2_LOGGING == NET_SSH2_LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')" - $ssh_log = $this->ssh->getLog(); - if (is_string($ssh_log)) { - $error_data[] = $ssh_log; - } elseif (is_array($ssh_log)) { - $error_data = array_merge($error_data, $ssh_log); - } - } - - return new WP_Error('ssh2_nologin', $message, $error_data); - } - - // if ($fingerprint) { - // $fingerprint = str_replace(':', '', $fingerprint); - // Fingerprint checking not yet supported by phpseclib - // return new WP_Error('debug', "Remove fingerprint: $remote_finger"); - // } - - return $scp ? new Net_SCP($this->ssh) : $this->ssh; - - } - - /** - * 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'); - } - - public function get_default_options() { - return array( - 'host' => '', - 'port' => '22', - 'user' => '', - 'pass' => '', - 'key' => '', - 'path' => '', - 'scp' => 0, - ); - } - - /** - * Get the pre configuration template - * - * @return String - the template - */ - public function get_pre_configuration_template() { - ?> - - -

{{method_display_name}}

-

{{description_label}}

- - - - - - - {{input_host_label}}: - - - - - - - {{input_port_label}}: - - - - - - - {{input_username_label}}: - - - - - - - {{input_password_label}}: - - -
{{input_password_title}} - - - - - {{input_key_label}}: - - -
{{input_key_title}} - - - - - {{input_rsa_fingerprint_label}}: - - -

{{{input_rsa_fingerprint_html_label}}}

- - - - - {{input_directory_path_label}}: - -
{{input_directory_path_title}} - - - - - SCP: - - - - - - {{{get_template_test_button_html "SFTP/SCP"}}} - __('Resuming partial uploads is supported for SFTP, but not for SCP. Thus, if using SCP then you will need to ensure that your webserver allows PHP processes to run long enough to upload your largest backup file.', 'updraftplus'), - 'input_host_label' => __('Host', 'updraftplus'), - 'input_port_label' => __('Port', 'updraftplus'), - 'input_username_label' => __('Username', 'updraftplus'), - 'input_password_label' => __('Password', 'updraftplus'), - 'input_key_label' => __('Key', 'updraftplus'), - 'input_rsa_fingerprint_label' => __('RSA fingerprint', 'updraftplus'), - 'input_directory_path_label' => __('Directory path', 'updraftplus'), - 'input_password_title' => __('Your login may be either password or key-based - you only need to enter one, not both.', 'updraftplus'), - 'input_password_type' => apply_filters('updraftplus_admin_secret_field_type', 'password'), - 'input_key_title' => __('PKCS1 (PEM header: BEGIN RSA PRIVATE KEY), XML and PuTTY format keys are accepted.', 'Do not translate BEGIN RSA PRIVATE KEY. PCKS1, XML, PEM and PuTTY are also technical acronyms which should not be translated.', 'updraftplus'), - 'input_rsa_fingerprint_plain_label' => sprintf($rsa_fingerprint_tooltip, __('MITM attacks', 'updraftplus')), - 'input_rsa_fingerprint_html_label' => sprintf($rsa_fingerprint_tooltip, ''.__('MITM attacks', 'updraftplus').''), - 'input_directory_path_title' => __('Where to change directory to after logging in - often this is relative to your home directory.', 'updraftplus'), - 'input_scp_label' => __('Use SCP instead of SFTP', '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()); - } - - /** - * Test the supplied credentials. Output to display to the user should be echoed. - * - * @param Array $posted_settings - the settings to test (including meta such as debug mode) - * - * @return Mixed|Void - any data to return with the test results (may be logged for debugging purposes) - */ - public function credentials_test($posted_settings) { - - if (empty($posted_settings['host'])) { - printf(__("Failure: No %s was given.", 'updraftplus'), __('host name', 'updraftplus')); - return; - } - if (empty($posted_settings['user'])) { - printf(__("Failure: No %s was given.", 'updraftplus'), __('username', 'updraftplus')); - return; - } - if (empty($posted_settings['pass']) && empty($posted_settings['key'])) { - printf(__("Failure: No %s was given.", 'updraftplus'), __('password/key', 'updraftplus')); - return; - } - $port = empty($posted_settings['port']) ? 22 : $posted_settings['port']; - if (!is_numeric($port)) { - _e("Failure: Port must be an integer.", 'updraftplus'); - return; - } - $path = empty($posted_settings['path']) ? '' : $posted_settings['path']; - - $fingerprint = empty($posted_settings['fingerprint']) ? '' : $posted_settings['fingerprint']; - - $scp = !empty($posted_settings['scp']); - - $host = $posted_settings['host']; - $user = $posted_settings['user']; - $pass = empty($posted_settings['pass']) ? '' : $posted_settings['pass']; - $key = empty($posted_settings['key']) ? '' : $posted_settings['key']; - $debug_mode = empty($posted_settings['debug_mode']) ? false : true; - - $sftp = $this->connect($host, $port, $fingerprint, $user, $pass, $key, $scp, $debug_mode); - - if (is_wp_error($sftp)) { - echo __("Failed", 'updraftplus').": "; - foreach ($sftp->get_error_messages() as $key => $msg) { - echo "$msg\n"; - } - $error_data = $sftp->get_error_data(); - return is_array($error_data) ? $error_data : null; - } - - // So far, so good - if (empty($scp)) { - if ($path) { - @$sftp->mkdir($path);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - // See if the directory now exists - if (!$sftp->chdir($path)) { - echo __('Check your file permissions: Could not successfully create and enter:', 'updraftplus')." (".htmlspecialchars($path).")"; - @$sftp->disconnect();// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - return; - } - } - } elseif ($path) { - $this->ssh->exec('mkdir '.$this->possibly_escapeshellarg($path)); - } - - $testfile = md5(time().rand()); - if (!empty($scp) && !empty($path)) $testfile = trailingslashit($path).$testfile; - // Now test uploading a file - $putfile = $sftp->put($testfile, 'test'); - if (empty($scp)) { - $sftp->delete($testfile); - } else { - $this->ssh->exec('rm -f '.$this->possibly_escapeshellarg($testfile)); - } - - $ret_arr = array(); - if ($putfile) { - $valid_fingerprints = $this->get_fingerprints($this->ssh); - if (empty($fingerprint)) { - $ret_arr['valid_md5_fingerprint'] = $valid_fingerprints['md5']; - _e('Success', 'updraftplus'); - } else { - $match_fingerprint = $this->validate_fingerprint($fingerprint, $valid_fingerprints); - if ($match_fingerprint) { - _e('Success', 'updraftplus'); - } else { - echo __("Failed: We are unable to match the fingerprint. However, we were able to log in and move to the indicated directory and successfully create a file in that location.", 'updraftplus'); - } - } - - printf(' '.__("The server's RSA key %s fingerprint: %s.", 'updraftplus').' ', 'MD5', $valid_fingerprints['md5']); - printf(__("The server's RSA key %s fingerprint: %s.", 'updraftplus'), 'SHA256', $valid_fingerprints['sha256']); - } else { - if (empty($scp)) { - echo __("Failed: We were able to log in and move to the indicated directory, but failed to successfully create a file in that location.", 'updraftplus'); - } else { - _e("Failed: We were able to log in, but failed to successfully create a file in that location.", 'updraftplus'); - } - } - - if ($this->scp) { - $this->ssh->disconnect(); - } else { - $sftp->disconnect(); - } - return $ret_arr; - } - - /** - * Get both md5 and sha256 fingerprints - * - * @param Object|String $ssh Net_SSH2 or it's subclass instace. If this is empty, $this->ssh will be $ssh - * - * @return Array An associative array has md5 and sha256 fingerprint - */ - private function get_fingerprints($ssh = '') { - global $updraftplus; - - if (empty($ssh)) $ssh = $this->ssh; - - $host_key = $ssh->getServerPublicHostKey(); - $updraftplus->ensure_phpseclib('Crypt_RSA'); - - $host_rsa = new Crypt_RSA(); - $host_rsa->loadKey($host_key); - return array( - 'md5' => $host_rsa->getPublicKeyFingerprint('md5'), - 'sha256' => $host_rsa->getPublicKeyFingerprint('sha256'), - ); - } - - /** - * Get both md5 and sha256 fingerprints - * - * @param String $fingerprint A fingerprint which need to be validated - * @param Array $valid_fingerprints Host's valid fingerprints - * - * @return Boolean Whether the given fingerprint is matched or not - */ - private function validate_fingerprint($fingerprint, $valid_fingerprints = array()) { - if (empty($valid_fingerprints)) $valid_fingerprints = $this->get_fingerprints($this->ssh); - - foreach ($valid_fingerprints as $valid_fingerprint) { - if ($fingerprint == $valid_fingerprint) { - return true; - } - } - 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['host']) && isset($opts['user']) && '' != $opts['user']) return true; - return false; - } -} - -/** - * Adapted from http://www.solutionbot.com/2009/01/02/php-ftp-class/ - * - * Our main tweaks to this class are to enable SSL with fallback for explicit encryption, and to provide rudimentary implicit support (the support for implicit is via Curl (since PHP's functions do not support it), and only extends to methods that we know we use). - * - * We somewhat crudely detect the request for implicit via use of port 990. But in the real world, it's unlikely we'll come across anything else - if we do, we can abstract a little more. - */ -class UpdraftPlus_ftp_wrapper { - - private $conn_id; - - private $host; - - private $username; - - private $password; - - private $port; - - public $timeout = 60; - - public $passive = true; - - // Whether to *allow* (not necessarily require) SSL - public $ssl = true; - - public $system_type = ''; - - public $login_type = 'non-encrypted'; - - public $use_server_certs = false; - - public $disable_verify = true; - - public $curl_handle; - - public function __construct($host, $username, $password, $port = 21) { - $this->host = $host; - $this->username = $username; - $this->password = $password; - $this->port = $port; - } - - public function connect() { - - // Implicit SSL - not handled via PHP's native ftp_ functions, so we use curl instead - if (990 == $this->port || (defined('UPDRAFTPLUS_FTP_USECURL') && UPDRAFTPLUS_FTP_USECURL)) { - if (false == $this->ssl) { - $this->port = 21; - } else { - $this->curl_handle = curl_init(); - if (!$this->curl_handle) { - $this->port = 21; - } else { - $options = array( - CURLOPT_USERPWD => $this->username . ':' . $this->password, - CURLOPT_PORT => $this->port, - CURLOPT_CONNECTTIMEOUT => 20, - // CURLOPT_TIMEOUT timeout is not just a "no-activity" timeout, but a total time limit on any Curl operation - undesirable - // CURLOPT_TIMEOUT => 20, - CURLOPT_FTP_CREATE_MISSING_DIRS => true - ); - $options[CURLOPT_FTP_SSL] = CURLFTPSSL_TRY; // CURLFTPSSL_ALL, // require SSL For both control and data connections - if (990 == $this->port) { - $options[CURLOPT_FTPSSLAUTH] = CURLFTPAUTH_SSL; // CURLFTPAUTH_DEFAULT, // let cURL choose the FTP authentication method (either SSL or TLS) - } else { - $options[CURLOPT_FTPSSLAUTH] = CURLFTPAUTH_DEFAULT; // let cURL choose the FTP authentication method (either SSL or TLS) - } - // Prints to STDERR by default - noisy - if (defined('WP_DEBUG') && WP_DEBUG && UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) { - $options[CURLOPT_VERBOSE] = true; - } - - // Provided for people who explicitly set the option to support their broken server - if ($this->disable_verify) { - $options[CURLOPT_SSL_VERIFYPEER] = false; - $options[CURLOPT_SSL_VERIFYHOST] = 0; - } else { - $options[CURLOPT_SSL_VERIFYPEER] = true; - } - - if (!$this->use_server_certs) { - $options[CURLOPT_CAINFO] = UPDRAFTPLUS_DIR.'/includes/cacert.pem'; - } - - if (true != $this->passive) $options[CURLOPT_FTPPORT] = '-'; - foreach ($options as $option_name => $option_value) { - if (!curl_setopt($this->curl_handle, $option_name, $option_value)) { - global $updraftplus; - if (is_a($updraftplus, 'UpdraftPlus')) { - $updraftplus->log("Curl exception: will revert to normal FTP"); - } - $this->port = 21; - $this->curl_handle = false; - } - } - } - // All done - leave - if ($this->curl_handle) { - $this->login_type = 'encrypted (implicit, port 990)'; - return true; - } - } - } - - $time_start = time(); - if (function_exists('ftp_ssl_connect') && false !== $this->ssl) { - $this->conn_id = ftp_ssl_connect($this->host, $this->port, 15); - $attempting_ssl = true; - } - - if ($this->conn_id) { - $this->login_type = 'encrypted'; - $this->ssl = true; - } else { - $this->conn_id = ftp_connect($this->host, $this->port, 15); - } - - if ($this->conn_id) $result = ftp_login($this->conn_id, $this->username, $this->password); - - if (!empty($result)) { - ftp_set_option($this->conn_id, FTP_TIMEOUT_SEC, $this->timeout); - ftp_pasv($this->conn_id, $this->passive); - $this->system_type = ftp_systype($this->conn_id); - return true; - } elseif (!empty($attempting_ssl)) { - global $updraftplus_admin; - if (isset($updraftplus_admin->logged) && is_array($updraftplus_admin->logged)) { - // Clear the previous PHP messages, so that we only show the user messages from the method that worked (or from both if both fail) - $save_array = $updraftplus_admin->logged; - $updraftplus_admin->logged = array(); - // trigger_error(__('Encrypted login failed; trying non-encrypted', 'updraftplus'), E_USER_NOTICE); - } - $this->ssl = false; - $this->login_type = 'non-encrypted'; - $time_start = time(); - $this->conn_id = ftp_connect($this->host, $this->port, 15); - if ($this->conn_id) $result = ftp_login($this->conn_id, $this->username, $this->password); - if (!empty($result)) { - ftp_set_option($this->conn_id, FTP_TIMEOUT_SEC, $this->timeout); - ftp_pasv($this->conn_id, $this->passive); - $this->system_type = ftp_systype($this->conn_id); - return true; - } else { - // Add back the previous PHP messages - if (isset($save_array)) $updraftplus_admin->logged = array_merge($save_array, $updraftplus_admin->logged); - } - } - - // If we got here, then we failed - if (time() - $time_start > 14) { - global $updraftplus_admin; - if (isset($updraftplus_admin->logged) && is_array($updraftplus_admin->logged)) { - $updraftplus_admin->logged[] = sprintf(__('The %s connection timed out; if you entered the server correctly, then this is usually caused by a firewall blocking the connection - you should check with your web hosting company.', 'updraftplus'), 'FTP'); - } else { - global $updraftplus; - $updraftplus->log(sprintf(__('The %s connection timed out; if you entered the server correctly, then this is usually caused by a firewall blocking the connection - you should check with your web hosting company.', 'updraftplus'), 'FTP'), 'error'); - } - } - - return false; - - } - - public function curl_progress_function($download_size, $downloaded_size, $upload_size, $uploaded_size) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - - if ($uploaded_size<1) return; - - global $updraftplus; - - $percent = 100*($uploaded_size+$this->upload_from)/$this->upload_size; - - // Log every megabyte or at least every 20% - if ($percent > $this->upload_last_recorded_percent + 20 || $uploaded_size > $this->uploaded_bytes + 1048576) { - $updraftplus->record_uploaded_chunk(round($percent, 1), '', $this->upload_local_path); - $this->upload_last_recorded_percent=floor($percent); - $this->uploaded_bytes = $uploaded_size; - } - - } - - public function put($local_file_path, $remote_file_path, $mode = FTP_BINARY, $resume = false, $updraftplus = false) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - - $file_size = filesize($local_file_path); - - $existing_size = 0; - if ($resume) { - - if ($this->curl_handle) { - if (true === $this->curl_handle) $this->connect(); - curl_setopt($this->curl_handle, CURLOPT_URL, 'ftps://'.$this->host.'/'.$remote_file_path); - curl_setopt($this->curl_handle, CURLOPT_NOBODY, true); - curl_setopt($this->curl_handle, CURLOPT_HEADER, false); - - // curl_setopt($this->curl_handle, CURLOPT_FORBID_REUSE, true); - - $getsize = curl_exec($this->curl_handle); - if ($getsize) { - $sizeinfo = curl_getinfo($this->curl_handle); - $existing_size = $sizeinfo['download_content_length']; - } else { - if (is_a($updraftplus, 'UpdraftPlus')) $updraftplus->log("Curl: upload error: ".curl_error($this->curl_handle)); - } - } else { - $existing_size = ftp_size($this->conn_id, $remote_file_path); - } - // In fact curl can return -1 as the value, for a non-existant file - if ($existing_size <=0) { - $resume = false; - $existing_size = 0; - } else { - if (is_a($updraftplus, 'UpdraftPlus')) $updraftplus->log("File already exists at remote site: size $existing_size. Will attempt resumption."); - if ($existing_size >= $file_size) { - if (is_a($updraftplus, 'UpdraftPlus')) $updraftplus->log("File is apparently already completely uploaded"); - return true; - } - } - } - - // From here on, $file_size is only used for logging calculations. We want to avoid divsion by zero. - $file_size = max($file_size, 1); - - if (!$fh = fopen($local_file_path, 'rb')) return false; - if ($existing_size) fseek($fh, $existing_size); - - // FTPS (i.e. implicit encryption) - if ($this->curl_handle) { - // Reset the curl object (because otherwise we get errors that make no sense) - $this->connect(); - if (version_compare(phpversion(), '5.3.0', '>=')) { - // @codingStandardsIgnoreLine - curl_setopt($this->curl_handle, CURLOPT_PROGRESSFUNCTION, array($this, 'curl_progress_function')); - curl_setopt($this->curl_handle, CURLOPT_NOPROGRESS, false); - } - $this->upload_local_path = $local_file_path; - $this->upload_last_recorded_percent = 0; - $this->upload_size = max($file_size, 1); - $this->upload_from = $existing_size; - $this->uploaded_bytes = $existing_size; - curl_setopt($this->curl_handle, CURLOPT_URL, 'ftps://'.$this->host.'/'.$remote_file_path); - if ($existing_size) curl_setopt($this->curl_handle, CURLOPT_FTPAPPEND, true); - - // DOn't set CURLOPT_UPLOAD=true before doing the size check - it results in a bizarre error - curl_setopt($this->curl_handle, CURLOPT_UPLOAD, true); - curl_setopt($this->curl_handle, CURLOPT_INFILE, $fh); - $output = curl_exec($this->curl_handle); - fclose($fh); - if (is_a($updraftplus, 'UpdraftPlus') && !$output) { - $updraftplus->log("FTPS: error: ".curl_error($this->curl_handle)); - } elseif (true === $updraftplus && !$output) { - echo __('Error:', 'updraftplus').' '.curl_error($this->curl_handle)."\n"; - } - // Mark as used - $this->curl_handle = true; - return $output; - } - - $ret = ftp_nb_fput($this->conn_id, $remote_file_path, $fh, FTP_BINARY, $existing_size); - - // $existing_size can now be re-purposed - - while (FTP_MOREDATA == $ret) { - if (is_a($updraftplus, 'UpdraftPlus')) { - $new_size = ftell($fh); - if ($new_size - $existing_size > 524288) { - $existing_size = $new_size; - $percent = round(100*$new_size/$file_size, 1); - $updraftplus->record_uploaded_chunk($percent, '', $local_file_path); - } - } - // Continue upload - $ret = ftp_nb_continue($this->conn_id); - } - - fclose($fh); - - if (FTP_FINISHED != $ret) { - if (is_a($updraftplus, 'UpdraftPlus')) $updraftplus->log("FTP upload: error ($ret)"); - return false; - } - - return true; - - } - - public function get($local_file_path, $remote_file_path, $mode = FTP_BINARY, $resume = false, $updraftplus = false) { - - $file_last_size = 0; - - if ($resume) { - if (!$fh = fopen($local_file_path, 'ab')) return false; - // @codingStandardsIgnoreLine - clearstatcache($local_file_path); - $file_last_size = filesize($local_file_path); - } else { - if (!$fh = fopen($local_file_path, 'wb')) return false; - } - - // Implicit FTP, for which we use curl (since PHP's native FTP functions don't handle implicit FTP) - if ($this->curl_handle) { - if ($resume) curl_setopt($this->curl_handle, CURLOPT_RESUME_FROM, $resume); - curl_setopt($this->curl_handle, CURLOPT_NOBODY, false); - curl_setopt($this->curl_handle, CURLOPT_URL, 'ftps://'.$this->host.'/'.$remote_file_path); - curl_setopt($this->curl_handle, CURLOPT_UPLOAD, false); - curl_setopt($this->curl_handle, CURLOPT_FILE, $fh); - $output = curl_exec($this->curl_handle); - if ($output) { - if ($updraftplus) $updraftplus->log("FTP fetch: fetch complete"); - } else { - if ($updraftplus) $updraftplus->log("FTP fetch: fetch failed"); - } - return $output; - } - - $ret = ftp_nb_fget($this->conn_id, $fh, $remote_file_path, $mode, $file_last_size); - - if (false == $ret) return false; - - while (FTP_MOREDATA == $ret) { - - if ($updraftplus) { - $file_now_size = filesize($local_file_path); - if ($file_now_size - $file_last_size > 524288) { - $updraftplus->log("FTP fetch: file size is now: ".sprintf("%0.2f", filesize($local_file_path)/1048576)." MB"); - $file_last_size = $file_now_size; - } - clearstatcache(); - } - - $ret = ftp_nb_continue($this->conn_id); - } - - fclose($fh); - - if (FTP_FINISHED == $ret) { - if ($updraftplus) $updraftplus->log("FTP fetch: fetch complete"); - return true; - } else { - if ($updraftplus) $updraftplus->log("FTP fetch: fetch failed"); - return false; - } - - } - - public function chmod($permissions, $remote_filename) { - if ($this->is_octal($permissions)) { - $result = ftp_chmod($this->conn_id, $permissions, $remote_filename); - return ($result) ? true : false; - } else { - throw new Exception('$permissions must be an octal number'); - } - } - - public function chdir($directory) { - ftp_chdir($this->conn_id, $directory); - } - - public function delete($remote_file_path) { - - if ($this->curl_handle) { - if (true === $this->curl_handle) $this->connect(); - curl_setopt($this->curl_handle, CURLOPT_URL, 'ftps://'.$this->host.'/'.$remote_file_path); - curl_setopt($this->curl_handle, CURLOPT_RETURNTRANSFER, true); - curl_setopt($this->curl_handle, CURLOPT_QUOTE, array('DELE '.$remote_file_path)); - // Unset some (possibly) previously-set options - curl_setopt($this->curl_handle, CURLOPT_UPLOAD, false); - curl_setopt($this->curl_handle, CURLOPT_INFILE, STDIN); - $output = curl_exec($this->curl_handle); - return $output; - } - - return (ftp_delete($this->conn_id, $remote_file_path)) ? true : false; - - } - - public function make_dir($directory) { - return ftp_mkdir($this->conn_id, $directory) ? true : false; - } - - public function rename($old_name, $new_name) { - return ftp_rename($this->conn_id, $old_name, $new_name) ? true : false; - } - - public function remove_dir($directory) { - return ftp_rmdir($this->conn_id, $directory); - } - - public function dir_list($directory) { - if ($this->curl_handle) { - // Can't get this to work - it might just be the vsftpd server I am testing on; it hangs strangely. But this means I can't test it. - return new WP_Error('unsupported_op', sprintf(__('The UpdraftPlus module for this file access method (%s) does not support listing files', 'updraftplus'), 'FTP (SSL/Implicit)')); - if (true === $this->curl_handle) $this->connect(); - curl_setopt($this->curl_handle, CURLOPT_URL, 'ftps://'.$this->host.'/'.trailingslashit($directory)); - curl_setopt($this->curl_handle, CURLOPT_RETURNTRANSFER, true); - curl_setopt($this->curl_handle, CURLOPT_TIMEOUT, 10); - $output = curl_exec($this->curl_handle); - return $output; - } - - return ftp_nlist($this->conn_id, $directory); - } - - public function cdup() { - return ftp_cdup($this->conn_id); - } - - public function size($f) { - return $this->curl_handle ? false : ftp_size($this->conn_id, $f); - } - - public function current_dir() { - return ftp_pwd($this->conn_id); - } - - private function is_octal($i) { - return decoct(octdec($i)) == $i; - } - - public function __destruct() { - if ($this->conn_id) ftp_close($this->conn_id); - } -} diff --git a/wp-content/plugins/updraftplus/addons/webdav.php b/wp-content/plugins/updraftplus/addons/webdav.php deleted file mode 100644 index fb2a9bef..00000000 --- a/wp-content/plugins/updraftplus/addons/webdav.php +++ /dev/null @@ -1,1577 +0,0 @@ -is_supress_initial_remote_404_log = true; - $this->method = 'webdav'; - $this->desc = 'WebDAV'; - $this->user_agent = 'UpdraftPlus/'.$updraftplus->version; - } - - /** - * Determine whether to use chunk for the backup operation based on a setting of a corresponding storage instance or a defined constant - * - * @return Boolean True if chunk is selected for the storage instance, false otherwise - */ - private function use_chunk() { - $options = $this->get_options(); // this ought to get options for a current instance being processed which has been specified via set_options before - // Prioritise constant to maintain backward compatibility. - if (defined('UPDRAFTPLUS_WEBDAV_NEVER_CHUNK') && UPDRAFTPLUS_WEBDAV_NEVER_CHUNK) { - return false; - } - return $options['enable_chunk']; - } - - /** - * Load required libraries - */ - public function load_libraries() { - set_include_path(UPDRAFTPLUS_DIR.'/includes/PEAR'.PATH_SEPARATOR.get_include_path()); - updraft_try_include_file('includes/PEAR/HTTP/Request2.php', 'require_once'); - updraft_try_include_file('includes/PEAR/HTTP/WebDAV/Tools/_parse_propfind_response.php', 'require_once'); - updraft_try_include_file('includes/PEAR/HTTP/WebDAV/Tools/_parse_lock_response.php', 'require_once'); - } - - /** - * 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( - 'url' => '', - 'enable_chunk' => 1, - ); - } - - /** - * 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['url'])) { - $url = parse_url($opts['url']); - if (!is_array($url)) return false; - if ("" !== $url['host'] && "" !== $url['user'] && "" !== $url['pass']) return true; - } - return false; - } - - /** - * Acts as a WordPress options filter - * - * @param Array $webdav - An array of WebDAV options - * - * @return Array - the returned array can either be the set of updated WebDAV settings or a WordPress error array - */ - public function options_filter($webdav) { - - // Get the current options (and possibly update them to the new format) - $opts = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('webdav'); - - 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: WebDAV $msg"); - } - // The saved options had a problem; so, return the new ones - return $webdav; - } - - // If the input is not as expected, then return the current options - if (!is_array($webdav)) return $opts; - - // Remove instances that no longer exist - if (!empty($opts['settings']) && is_array($opts['settings'])) { - foreach ($opts['settings'] as $instance_id => $storage_options) { - if (!isset($webdav['settings'][$instance_id])) unset($opts['settings'][$instance_id]); - } - } - - // WebDAV has a special case where the settings could be empty so we should check for this before proceeding - if (!empty($webdav['settings'])) { - - foreach ($webdav['settings'] as $instance_id => $storage_options) { - if (isset($storage_options['webdav'])) { - - $slash = "/"; - $host = ""; - $colon = ""; - $port_colon = ""; - - if ((80 == $storage_options['port'] && 'webdav' == $storage_options['webdav']) || (443 == $storage_options['port'] && 'webdavs' == $storage_options['webdav'])) { - $storage_options['port'] = ''; - } - - if ('/' == substr($storage_options['path'], 0, 1)) { - $slash = ""; - } - - if (false === strpos($storage_options['host'], "@")) { - $host = "@"; - } - - if ('' != $storage_options['user'] && '' != $storage_options['pass']) { - $colon = ":"; - } - - if ('' != $storage_options['host'] && '' != $storage_options['port']) { - $port_colon = ":"; - } - - if (!empty($storage_options['url']) && 'http' == strtolower(substr($storage_options['url'], 0, 4))) { - $storage_options['url'] = 'webdav'.substr($storage_options['url'], 4); - } elseif ('' != $storage_options['user'] && '' != $storage_options['pass']) { - $storage_options['url'] = $storage_options['webdav'].urlencode($storage_options['user']).$colon.urlencode($storage_options['pass']).$host.urlencode($storage_options['host']).$port_colon.$storage_options['port'].$slash.$storage_options['path']; - } else { - $storage_options['url'] = $storage_options['webdav'].urlencode($storage_options['host']).$port_colon.$storage_options['port'].$slash.$storage_options['path']; - } - - $opts['settings'][$instance_id]['url'] = $storage_options['url']; - - if (!isset($storage_options['enable_chunk'])) $storage_options['enable_chunk'] = 1; // force old instance settings from the old versions which don't have "enable_chunk" field to now use enable_chunk by default? - - // Now we have constructed the URL we should loop over the options and save any extras, but we should ignore the options used to create the URL as they are no longer needed. - $skip_keys = array("url", "webdav", "user", "pass", "host", "port", "path"); - - foreach ($storage_options as $key => $value) { - if (!in_array($key, $skip_keys)) { - $opts['settings'][$instance_id][$key] = $storage_options[$key]; - } - } - } - } - } - - return $opts; - } - - /** - * Get the pre configuration template (directly output) - * - * @return String - the template - */ - public function get_pre_configuration_template() { - ?> - - -

{{method_display_name}}

- - - get_configuration_middlesection_template(); - } - - /** - * Get configuration template of middle section - * - * @return String - the partial template, ready for substitutions to be carried out - */ - public function get_configuration_middlesection_template() { - ob_start(); - ?> - - {{input_url_label}}: - - - -

- {{input_url_title}} -

- - - - {{input_protocol_label}}: - - - - - - {{input_username_label}}: - - - - - - {{input_password_label}}: - - - - - - {{input_host_label}}: - - -
- - - - - {{input_port_label}}: - - -
- {{input_port_title}} - - - - - {{input_path_title}}: - - - - - - - {{input_chunk_label}}: - - - - - - - {{{get_template_test_button_html "WebDav"}}} - __('WebDAV URL', 'updraftplus'), - 'input_url_title' => __('This WebDAV URL is generated by filling in the options below. If you do not know the details, then you will need to ask your WebDAV provider.', 'updraftplus'), - 'input_protocol_label' => __('Protocol (SSL or not)', 'updraftplus'), - 'input_username_label' => __('Username', 'updraftplus'), - 'input_password_label' => __('Password', 'updraftplus'), - 'input_password_type' => apply_filters('updraftplus_admin_secret_field_type', 'password'), - 'input_host_label' => __('Host', 'updraftplus'), - 'hostname_error_label' => __('Error:', 'updraftplus').' '.__('A host name cannot contain a slash.', 'updraftplus').' '.__('Enter any path in the field below.', 'updraftplus'), - 'input_port_label' => __('Port', 'updraftplus'), - 'input_port_title' => __('Leave this blank to use the default (80 for webdav, 443 for webdavs)', 'updraftplus'), - 'input_path_title' => __('Path', 'updraftplus'), - 'input_chunk_label' => __('Split uploads into chunks', 'updraftplus'), - 'input_chunk_title' => __('Yes', '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()); - } - - /** - * Modifies handerbar template options - * - * @param array $opts - * - * @return array - Modified handerbar template options - */ - public function transform_options_for_template($opts) { - $url = empty($opts['url']) ? '' : $opts['url']; - $parse_url = @parse_url($url);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - if (false === $parse_url) $url = ''; - $opts['url'] = $url; - $url_scheme = @parse_url($url, PHP_URL_SCHEME);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - if ('webdav' == $url_scheme) { - $opts['is_webdav_protocol'] = true; - } elseif ('webdavs' == $url_scheme) { - $opts['is_webdavs_protocol'] = true; - } - $opts['user'] = urldecode((string) @parse_url($url, PHP_URL_USER));// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - $opts['pass'] = urldecode((string) @parse_url($url, PHP_URL_PASS));// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - $opts['host'] = urldecode((string) @parse_url($url, PHP_URL_HOST));// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - $opts['port'] = @parse_url($url, PHP_URL_PORT);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - $opts['path'] = @parse_url($url, PHP_URL_PATH);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - if (!isset($opts['enable_chunk'])) $opts['enable_chunk'] = 1; // force old instance settings from the old versions which don't have "enable_chunk" field to now use enable_chunk by default? - return $opts; - } - - /** - * This method will take the passed in credentials and try and connect and write data to the remote storage option - * - * @param Array $posted_settings - an array of settings - * - * @return Void - result is echoed to page - */ - public function credentials_test($posted_settings) { - - if (empty($posted_settings['url'])) { - printf(__("Failure: No %s was given.", 'updraftplus'), 'URL'); - return; - } - - $url = preg_replace('/^http/i', 'webdav', untrailingslashit($posted_settings['url'])); - - $this->mkdir($url); - - $testfile = $url.'/'.md5(time().rand()); - $this->_parse_url($testfile); - $msg = __("Success", 'updraftplus'); - $res = true; - try { - $res = $this->write(self::CREDENTIALS_TEST_DATA, $posted_settings['enable_chunk']); - } catch (Exception $e) { - $msg = $e->getMessage(); - } - if (!$res) $msg = __("Failed: We were not able to place a file in that directory - please check your credentials.", 'updraftplus'); - $this->unlink($testfile); - echo $msg; - } - - /** - * Delete a single file from the service - * - * @param Boolean $ret - value to return - * @param Array|String $files - array of file names to delete - * @param Array $storage_arr - service details - * - * @return Boolean|String - either a boolean true or an error code string - */ - public function delete_files($ret, $files, $storage_arr = false) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - - if (is_string($files)) $files = array($files); - - if ($storage_arr) { - $url = $storage_arr['url']; - } else { - $options = $this->get_options(); - if (!array($options) || !isset($options['url'])) { - $this->log('No '.$this->desc.' settings were found'); - $this->log(sprintf(__('No %s settings were found', 'updraftplus'), $this->desc), 'error'); - return 'authentication_fail'; - } - $url = untrailingslashit($options['url']); - } - - $logurl = preg_replace('/:([^\@:]*)\@/', ':(password)@', $url); - - $ret = true; - - foreach ($files as $file) { - $this->log("Delete remote: $logurl/$file"); - if (!$this->unlink("$url/$file")) { - $this->log("Delete failed"); - $ret = 'file_delete_error'; - } - } - return $ret; - } - - /** - * Uploads a single file in chunks to the service - * - * @param String $file - the file to upload - * @param String $url - the upload destination - * - * @return Boolean - returns true on success or false on failure - */ - public function upload($file, $url) { - - global $updraftplus; - - $orig_file_size = filesize($file); - - $start_offset = 0; - $this->error_404_should_be_logged = false; - $url_size = $this->filesize($url); - - if ($url_size) { - if ($url_size == $orig_file_size) { - $this->log("This file has already been successfully uploaded"); - return true; - } elseif ($url_size > $orig_file_size) { - $this->log("A larger file than expected ($url_size > $orig_file_size) already exists"); - return false; - } - $this->log("$url_size bytes already uploaded; resuming"); - $start_offset = $url_size; - } - - $this->error_404_should_be_logged = true; - - if (!$rh = fopen($file, 'rb')) { - $this->log('Failed to open local file'); - return false; - } - - $this->log("Enable chunked upload: ".($this->use_chunk() ? 'yes' : 'no')); - - $upload_chunk_size = $this->upload_chunk_size; - - $chunks = floor($orig_file_size / $upload_chunk_size); - // There will be a remnant unless the file size was exactly on a 5MB boundary - if ($orig_file_size % $upload_chunk_size > 0) $chunks++; - - - $read_buffer_size = 131072; - - if ($this->use_chunk()) { - $read_buffer_size = min($upload_chunk_size, 1048576); - $this->log(sprintf("Upload chunk size: successfully changed to %d bytes", $upload_chunk_size)); - } - - if (!$this->use_chunk()) { - $chunks = 1; - $upload_chunk_size = $orig_file_size+1; - $read_buffer_size = $orig_file_size; - } - - $res = true; - $last_time = time(); - for ($i = 1; $i <= $chunks; $i++) { - - $chunk_start = ($i-1)*$upload_chunk_size; - $chunk_end = min($i*$upload_chunk_size-1, $orig_file_size); - - if ($start_offset > $chunk_end) { - $this->log("Chunk $i: Already uploaded"); - } else { - - $this->seek($chunk_start, SEEK_SET); - fseek($rh, $chunk_start); - - $bytes_left = $chunk_end - $chunk_start; - while ($bytes_left > 0) { - if ($buf = fread($rh, $read_buffer_size)) { - $bytes_written = $this->write($buf, $this->use_chunk()); // first attempt to upload file - if ($bytes_written > 0) { - $bytes_left = $bytes_left - strlen($buf); - if (time()-$last_time > 15) { - $last_time = time(); - touch($file); - } - } elseif (-1 === $bytes_written && $this->use_chunk()) { // "-1" for handling recoverable error especially when the error status/code is 400 or 501. It will be recovered only when failed uploading in chunks which means the chunk setting is enabled - $this->position = 0; - $res = $this->write(file_get_contents($file), false); // all-at-once upload (the second attempt), the chunk setting is enabled for the corresponding instance but since it's a recovery so we force to not use chunk - if (false === $res || -1 === $res) { - $res = false; - $this->log('WebDAV: All-in-one write failed'); - // The return result is ignored; so, we throw an exception instead - throw new Exception('WebDAV: All-in-one write failed'); - } else { - $this->log('WebDAV: All-in-one write succeeded'); - $updraftplus->record_uploaded_chunk(100, "$i", $file); - } - break 2; - } else { - $this->log("Chunk $i: A write error occurred"); - $res = false; - break 2; - } - } else { - $this->log("Chunk $i: A read error occurred"); - $res = false; - break 2; - } - } - } - - if ($this->use_chunk()) $updraftplus->record_uploaded_chunk(round(100*$i/$chunks, 1), "$i", $file); - } - - try { - if (!$this->connection_close()) { - $this->log('Upload failed (connection_close error)'); - $res = false; - } - } catch (Exception $e) { - $this->log('Upload failed (connection_close exception; class='.get_class($e).'): '.$e->getMessage()); - $res = false; - } - if (!fclose($rh)) $this->log('Upload failed (fclose error)'); - - return $res; - - } - - /** - * Lists files found at the service which match the passed in string - * - * @param String $match - the string we want to match when searching for files - * - * @return Array|WP_Error - an array of files found - */ - public function listfiles($match = 'backup_') { - - $options = $this->get_options(); - if (!array($options) || empty($options['url'])) return new WP_Error('no_settings', sprintf(__('No %s settings were found', 'updraftplus'), $this->desc)); - - $url = trailingslashit($options['url']); - - // A change to how WebDAV settings are saved resulted in near-empty URLs being saved, like webdav:/// . Detect 'empty URLs'. - if (preg_match('/^[a-z]+:$/', untrailingslashit($url))) { - return new WP_Error('no_settings', sprintf(__('No %s settings were found', 'updraftplus'), $this->desc)); - } - - if (false == ($handle = $this->opendir($url))) return new WP_Error('no_access', sprintf('Failed to gain %s access', $this->desc)); - - $results = array(); - - while (false !== ($entry = $this->readdir())) { - if ($this->filesize($url.$entry) && 0 === strpos($entry, $match)) { - $results[] = array('name' => $entry, 'size' => $this->filesize($url.$entry)); - } - } - - return $results; - - } - - /** - * Uploads a list of files to the service - * - * @param Boolean $ret a boolean - * @param Array $backup_array an array of files to upload - * - * @return Array|Boolean - returns an array on success or boolean false on failure - */ - public function upload_files($ret, $backup_array) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found - - global $updraftplus; - - $options = $this->get_options(); - if (!array($options) || !isset($options['url'])) { - $this->log('No '.$this->desc.' settings were found'); - $this->log(sprintf(__('No %s settings were found', 'updraftplus'), $this->desc), 'error'); - return false; - } - - $any_failures = false; - - $updraft_dir = untrailingslashit($updraftplus->backups_dir_location()); - $url = untrailingslashit($options['url']); - - foreach ($backup_array as $file) { - $this->log("upload: attempt: $file"); - if ($this->upload($updraft_dir.'/'.$file, $url.'/'.$file)) { - $updraftplus->uploaded_file($file); - } else { - $any_failures = true; - $this->log('ERROR: '.$this->desc.': Failed to upload file: '.$file); - $this->log(__('Error', 'updraftplus').': '.$this->desc.': '.sprintf(__('Failed to upload to %s', 'updraftplus'), $file), 'error'); - } - } - - return ($any_failures) ? null : array('url' => $url); - - } - - /** - * Downloads a list of files from the service - * - * @param Boolean $ret - a boolean - * @param Array $files - an array of files to download - * - * @return Boolean - returns false on failure and true on success - */ - public function download_file($ret, $files) { - - global $updraftplus; - - if (is_string($files)) $files = array($files); - - $options = $this->get_options(); - - if (!array($options) || !isset($options['url'])) { - $this->log('No '.$this->desc.' settings were found'); - $this->log(sprintf(__('No %s settings were found', 'updraftplus'), $this->desc), 'error'); - return false; - } - - $ret = true; - foreach ($files as $file) { - - $fullpath = $updraftplus->backups_dir_location().'/'.$file; - $url = untrailingslashit($options['url']).'/'.$file; - - $start_offset = (file_exists($fullpath)) ? filesize($fullpath) : 0; - $url_size = $this->filesize($url); - if ($url_size == $start_offset) {// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - $ret = false; - continue; - } - - if (!$fh = fopen($fullpath, 'a')) { - $this->log("Error opening local file: Failed to download: $file"); - $this->log("$file: ".sprintf(__("%s Error", 'updraftplus'), $this->desc).": ".__('Error opening local file: Failed to download', 'updraftplus'), 'error'); - $ret = false; - continue; - } - - if (!$this->connection_open($url, 'rb')) { - $this->log("Error opening remote file: Failed to download: $file"); - $this->log("$file: ".sprintf(__("%s Error", 'updraftplus'), $this->desc).": ".__('Error opening remote file: Failed to download', 'updraftplus'), 'error'); - $ret = false; - continue; - } - - $read_buffer_size = 262144; - - if (isset($this->download_chunk_size)) { - $read_buffer_size = $this->download_chunk_size; - $this->log(sprintf("Download chunk size successfully changed to %d", 'updraftplus'), $this->download_chunk_size); - } - - if ($start_offset) { - fseek($fh, $start_offset); - $this->seek($start_offset, SEEK_SET); - } - - while (!$this->eof() && $buf = $this->read($read_buffer_size)) { - if (!fwrite($fh, $buf, strlen($buf))) { - $this->log("Error: Local write failed: Failed to download: $file"); - $this->log("$file: ".sprintf(__("%s Error", 'updraftplus'), $this->desc).": ".__('Local write failed: Failed to download', 'updraftplus'), 'error'); - $ret = false; - continue; - } - } - - $this->connection_close(); - } - - return $ret; - - } - - /** - * Method for open connection - * - * @param string $path resource URL - * @param string $mode flag - * - * @return bool true on success - */ - private function connection_open($path, $mode) { - $this->stat = array(); - // rewrite the request URL - if (!$this->_parse_url($path)) return false; - - $writing = preg_match('|[aw\+]|', $mode); - - // query server for WebDAV options - if (!$this->_check_options()) { - if ($writing) { - // Retry on the directory instead of on the file itself - $old_url = $this->url; - $this->url = dirname($this->url); - if (!$this->_check_options()) { - $this->url = $old_url; - $this->log('Failed to check WebDAV server options'); - return false; - } - $this->url = $old_url; - } else { - $this->log('Failed to check WebDAV server options'); - return false; - } - } - - try { - // now get the file metadata - // we only need type, size, creation and modification date - $req = $this->_startRequest('PROPFIND'); - if (is_string($this->user)) { - $req->setAuth($this->user, $this->pass); - } - $req->setHeader('Depth', "0"); - $req->setHeader('Content-type', 'text/xml'); - $req->setBody(' - - - - - - - - - '); - $result = $req->send(); - } catch (Exception $e) { - if (preg_match("/Malformed response: /i", $e->getMessage(), $matches)) { - return $this->_check_options(); - } - throw $e; - } - - // check the response code, anything but 207 indicates a problem - switch ($result->getStatus()) { - case 207: - // OK - // now we have to parse the result to get the status info items - $propinfo = new HTTP_WebDAV_Client_parse_propfind_response($result->getBody()); - $this->stat = $propinfo->stat(); - unset($propinfo); - break; - - case 404: - // not found is ok in write modes - if (preg_match('|[aw\+]|', $mode)) { - break; // write - } - $this->eof = true; - // else fallthru - - /* - case 405: // method disabled. In write mode, try to carry on. - if (preg_match('|[aw\+]|', $mode)) { - break; // write - } - $this->eof = true; - */ - // N.B. Some 404s drop also through to here - default: - // Log only if the condition was not expected - if ($this->error_404_should_be_logged) { - $msg = UpdraftPlus_HTTP_Error_Descriptions::get_http_status_code_description(404); - $this->log(sprintf("File not found (404): %s", $msg)); - } - return false; - } - - // 'w' -> open for writing, truncate existing files - if (false !== strpos($mode, 'w')) { - try { - $req = $this->_startRequest('PUT'); - - $req->setHeader('Content-length', 0); - - if (is_string($this->user)) { - $req->setAuth($this->user, $this->pass); - } - - $req->send(); - } catch (Exception $e) { - if (preg_match("/Malformed response: /i", $e->getMessage(), $matches)) { - return $this->_check_options(); - } - throw $e; - } - } - - // we are done :) - return true; - } - - - /** - * Method for close connection - */ - public function connection_close() { - // closing is simple as HTTP is stateless - $this->url = $this->eof = false; - $this->position = 0; - $this->stat = array(); - - // unlock? - if ($this->locktoken) { - return $this->lock(LOCK_UN); - } - - return true; - } - - /** - * Method for retrieving information about a file resource - * - * @return array stat entries - */ - public function stat() { - return $this->stat; - } - - /** - * Method for reading a file resource - * - * @param int $count requested byte count - * - * @return string read data - */ - public function read($count) { - - $start = $this->position; - $end = $start + $count - 1; - - try { - // create a GET request with a range - $req = $this->_startRequest('GET'); - if (is_string($this->user)) { - $req->setAuth($this->user, $this->pass); - } - $req->setHeader("Range", "bytes=$start-$end"); - - $result = $req->send(); - } catch (Exception $e) { - if (preg_match("/Malformed response: /i", $e->getMessage(), $matches)) { - return $this->_check_options(); - } - throw $e; - } - $data = $result->getBody(); - $len = strlen($data); - - // lets see what happened - switch ($result->getStatus()) { - case 200: - // server doesn't support range requests - // TODO we should add some sort of cacheing here - inherited from initial commit - $data = substr($data, $start, $count); - break; - - case 206: - // server supports range requests - break; - - case 416: - // reading beyond end of file is not an error - $data = ""; - $len = 0; - break; - - default: - return false; - } - - // no data indicates end of file - if (!$len) { - $this->eof = true; - } - - // update position - $this->position += $len; - - // thats it! - return $data; - } - - /** - * Method for writing a file resource - * - * @param string $buffer data to write - * @param bool $use_chunk whether to set Content-Range header for uploading file in chunks - * - * @return int number of bytes actually written - */ - public function write($buffer, $use_chunk) { - // do some math - $start = $this->position; - $end = $this->position + strlen($buffer) - 1; - - $method = ($use_chunk && defined('UPDRAFTPLUS_WEBDAV_USE_SABRE_APPEND') && UPDRAFTPLUS_WEBDAV_USE_SABRE_APPEND) ? 'PATCH' : 'PUT'; - - try { - // create a partial PUT request - $req = $this->_startRequest($method); - if (is_string($this->user)) { - $req->setAuth($this->user, $this->pass); - } - - if (defined('UPDRAFTPLUS_WEBDAV_USE_SABRE_APPEND') && UPDRAFTPLUS_WEBDAV_USE_SABRE_APPEND) { - if ($use_chunk) { - $req->setHeader('Content-Type', 'application/x-sabredav-partialupdate'); // this will replace the existing content-type that has been set via _startRequest() before - $req->setHeader("X-Update-Range", "append"); - } - } else { - if ($use_chunk) $req->setHeader("Content-Range", "bytes $start-$end/*"); - } - if ($this->locktoken) { - $req->setHeader("If", "(<{$this->locktoken}>)"); - } - $req->setBody($buffer); - - // go! go! go! - $result = $req->send(); - } catch (Exception $e) { - if (preg_match("/Malformed response: /i", $e->getMessage(), $matches)) { - return $this->_check_options(); - } - throw $e; - } - - $status = apply_filters('updraftplus_webdav_uploading_status', $result->getStatus(), $use_chunk); - // check result - switch ($status) { - case 200: - case 201: - case 204: - $this->position += strlen($buffer); - return 1 + $end - $start; - - // New in UD 1.11.13 for ownCloud 8.1.? (strictly, the version of SabreDav in it) - - /* - - - Sabre\DAV\Exception\BadRequest - Content-Range on PUT requests are forbidden. - - */ - break; - case 400: - if (false !== strpos($result->getBody(), 'Content-Range')) { - $this->log('WebDAV server returned 400 due to Content-Range issue; will try all-at-once method'); - if (self::CREDENTIALS_TEST_DATA === $buffer) throw new Exception(__('WebDAV server returned 400; probably does not support Content-Range (chunks)', 'updraftplus')); - return ($use_chunk) ? -1 : false; // "-1" recoverable error, false if chunks is in use - } else { - $msg = UpdraftPlus_HTTP_Error_Descriptions::get_http_status_code_description($result->getStatus()); - $this->log(sprintf("Unexpected HTTP response code (%s): %s", $result->getStatus(), $msg)); - if (self::CREDENTIALS_TEST_DATA === $buffer) throw new Exception(sprintf(__("Unexpected HTTP response code (%s): %s", "updraftplus"), $result->getStatus(), $msg)); - return false; - } - break; - case 501: - $this->log('WebDAV server returned 501; probably does not support Content-Range; will try all-at-once method'); - if (self::CREDENTIALS_TEST_DATA === $buffer) throw new Exception(__('WebDAV server returned 501; probably does not support Content-Range (chunks)', 'updraftplus')); - return ($use_chunk) ? -1 : false; // "-1" recoverable error, false if chunks is in use - break; - default: - $msg = UpdraftPlus_HTTP_Error_Descriptions::get_http_status_code_description($result->getStatus()); - $this->log(sprintf("Unexpected HTTP response code (%s): %s", $result->getStatus(), $msg)); - if (self::CREDENTIALS_TEST_DATA === $buffer) throw new Exception(sprintf(__("Unexpected HTTP response code (%s): %s", "updraftplus"), $result->getStatus(), $msg)); - return false; - } - - /* - We do not cope with servers that do not support partial PUTs! - And we do assume that a server does conform to the following - rule from RFC 2616 Section 9.6: - - "The recipient of the entity MUST NOT ignore any Content-* - (e.g. Content-Range) headers that it does not understand or - implement and MUST return a 501 (Not Implemented) response - in such cases." - - So the worst case scenario with a compliant server not - implementing partial PUTs should be a failed request. A - server simply ignoring "Content-Range" would replace - file contents with the request body instead of putting - the data at the requested place but we can blame it - for not being compliant in this case ;) - - (TODO: maybe we should do a HTTP version check first?) - inherited from initial commit - - we *could* emulate partial PUT support by adding local - cacheing but for now we don't want to as it adds a lot - of complexity and storage overhead to the client ... - */ - - return true; - } - - /** - * Method for returning an end-of-file on a file pointer - * - * @return bool true if end of file was reached - */ - public function eof() { - return $this->eof; - } - - /** - * Method for seeking to specific location of file resource - * - * @param int $pos position to seek to - * @param int $whence seek mode - * - * @return bool true on success - */ - public function seek($pos, $whence) { - switch ($whence) { - case SEEK_SET: - // absolute position - $this->position = $pos; - break; - case SEEK_CUR: - // relative position - $this->position += $pos; - break; - case SEEK_END: - // relative position form end - $this->position = $this->stat['size'] + $pos; - break; - default: - return false; - } - - // TODO: this is rather naive (check how libc handles this) - inherited from initial commit - $this->eof = false; - - return true; - } - - /** - * Method for reading directory - * - * @param string $path directory resource URL - * - * @return bool true on success - */ - public function opendir($path) { - // rewrite the request URL - if (!$this->_parse_url($path)) return false; - - // query server for WebDAV options - if (!$this->_check_options()) return false; - - if (!isset($this->dav_allow['PROPFIND'])) { - return false; - } - - try { - // now read the directory - $req = $this->_startRequest('PROPFIND'); - if (is_string($this->user)) { - $req->setAuth($this->user, $this->pass); - } - $req->setHeader("Depth", "1"); - $req->setHeader("Content-Type", "text/xml"); - $req->setBody(' - - - - - - - - - '); - $result = $req->send(); - } catch (Exception $e) { - if (preg_match("/Malformed response: /i", $e->getMessage(), $matches)) { - return $this->_check_options(); - } - throw $e; - } - - switch ($result->getStatus()) { - case 207: - // multistatus content - $this->dirfiles = array(); - $this->dirpos = 0; - - // for all returned resource entries - foreach (explode("\n", $result->getBody()) as $line) { - // Preg_match_all if the whole response is one line! - if (preg_match_all("/href>([^<]*)/", $line, $matches)) { - // skip the directory itself - foreach ($matches[1] as $match) { - // Compare to $this->url too - if ("" == $match || $match == $this->path || $match == $this->url) { - continue; - } - // just remember the basenames to return them later with readdir() - $this->dirfiles[] = basename($match); - } - } - } - return true; - default: - // any other response state indicates an error - if ($this->error_404_should_be_logged) { - $msg = UpdraftPlus_HTTP_Error_Descriptions::get_http_status_code_description($result->getStatus()); - $this->log(sprintf("File not found (404): %s", $msg)); - } - return false; - } - } - - - /** - * Method for reading directory - * - * @return string filename - */ - public function readdir() { - // bailout if directory is empty - if (!is_array($this->dirfiles)) { - return false; - } - - // bailout if we already reached end of dir - if ($this->dirpos >= count($this->dirfiles)) { - return false; - } - - // return an entry and move on - return $this->dirfiles[$this->dirpos++]; - } - - /** - * Method for creating a new directory - * - * @param string $path collection URL to be created - * - * @return bool true on access - */ - public function mkdir($path) { - // rewrite the request URL - if (!$this->_parse_url($path)) return false; - - // query server for WebDAV options - if (!$this->_check_options()) return false; - - try { - $req = $this->_startRequest('MKCOL'); - if (is_string($this->user)) { - $req->setAuth($this->user, $this->pass); - } - if ($this->locktoken) { - $req->setHeader("If", "(<{$this->locktoken}>)"); - } - $result = $req->send(); - } catch (Exception $e) { - if (preg_match("/Malformed response: /i", $e->getMessage(), $matches)) { - return $this->_check_options(); - } - throw $e; - } - - // check the response code, anything but 201 indicates a problem - $stat = $result->getStatus(); - switch ($stat) { - case 201: - case 405: // directory already created - return true; - default: - $this->log(sprintf("mkdir failed - %s", $stat)); - return false; - } - } - - /** - * Method for removing a file - * - * @param string $path resource URL to be removed - * @return bool true on success - */ - public function unlink($path) { - // rewrite the request URL - if (!$this->_parse_url($path)) return false; - - // query server for WebDAV options - if (!$this->_check_options()) return false; - - // is DELETE supported? - if (!isset($this->dav_allow['DELETE'])) { - return false; - } - - try { - $req = $this->_startRequest('DELETE'); - if (is_string($this->user)) { - $req->setAuth($this->user, $this->pass); - } - if ($this->locktoken) { - $req->setHeader("If", "(<{$this->locktoken}>)"); - } - $result = $req->send(); - } catch (Exception $e) { - if (preg_match("/Malformed response: /i", $e->getMessage(), $matches)) { - return $this->_check_options(); - } - throw $e; - } - - switch ($result->getStatus()) { - case 204: - // ok - return true; - default: - return false; - } - } - - - - /** - * Helper function for URL analysis - * - * @param string $path original request URL - * - * @return bool true on success else false - */ - private function _parse_url($path) { - // rewrite the WebDAV url as a plain HTTP url - $url = parse_url($path); - - // detect whether plain or SSL-encrypted transfer is requested - $scheme = $url['scheme']; - switch ($scheme) { - case "webdav": - $url['scheme'] = "http"; - break; - case "webdavs": - $url['scheme'] = "https"; - break; - default: - $this->log(sprintf("only 'webdav:' and 'webdavs:' are supported, not '%s'", $url['scheme'].':')); - return false; - } - - // if a TCP port is specified we have to add it after the host - if (isset($url['port'])) { - $url['host'] .= ":$url[port]"; - } - - if (!isset($url['path'])) { - $url['path'] = ''; - } - - // store the plain path for possible later use - $this->path = $url["path"]; - - // now we can put together the new URL - $this->url = "$url[scheme]://$url[host]$url[path]"; - - // extract authentication information - if (isset($url['user'])) { - $this->user = urldecode($url['user']); - } - if (isset($url['pass'])) { - $this->pass = urldecode($url['pass']); - } - - return true; - } - - /** - * Helper function for WebDAV OPTIONS detection - * - * @return bool true on success else false - */ - private function _check_options() { - $this->load_libraries(); - - try { - // now check OPTIONS reply for WebDAV response headers - $req = $this->_startRequest(HTTP_Request2::METHOD_OPTIONS); - if (is_string($this->user)) { - $req->setAuth($this->user, $this->pass); - } - $result = $req->send(); - } catch (Exception $e) { - if (preg_match("/Malformed response: /i", $e->getMessage(), $matches)) { - return $this->_check_options(); - } - throw $e; - } - - if ($result->getStatus() != 200) { - // If the status is 301 we want to return false so the calling code can deal with it but not trigger any errors on the front end - if ($result->getStatus() != 301) { - $msg = UpdraftPlus_HTTP_Error_Descriptions::get_http_status_code_description($result->getStatus()); - $this->log(sprintf('%s returned when checking WebDAV server options using URL: %s response: %s', $msg." (".$result->getStatus().")", $this->url, json_encode($result->getBody()))); - } - return false; - } - - // get the supported DAV levels and extensions - $dav = $result->getHeader("DAV"); - $this->dav_level = array(); - foreach (explode(",", $dav) as $level) { - $this->dav_level[trim($level)] = true; - } - if (!isset($this->dav_level["1"])) { - // we need at least DAV Level 1 conformance - $this->log('WebDAV server must be at least DAV level 1 conformance'); - return false; - } - - // get the supported HTTP methods - // TODO these are not checked for WebDAV compliance yet - inherited from initial commit - $allow = $result->getHeader("Allow"); - $this->dav_allow = array(); - foreach (explode(",", $allow) as $method) { - $this->dav_allow[trim($method)] = true; - } - - // TODO check for required WebDAV methods - inherited from initial commit - - return true; - } - - - /** - * Method for locking a file resource - * - * @param string $mode lock mode - * - * @return bool true on success else false - */ - private function lock($mode) { - /* TODO: - - think over how to refresh locks - inherited from initial commit - */ - - $ret = false; - - // LOCK is only supported by DAV Level 2 - if (!isset($this->dav_level["2"])) { - return false; - } - - switch ($mode & ~LOCK_NB) { - case LOCK_UN: - if ($this->locktoken) { - try { - $req = $this->_startRequest('UNLOCK'); - if (is_string($this->user)) { - $req->setAuth($this->user, $this->pass); - } - $req->setHeader("Lock-Token", "<{$this->locktoken}>"); - $result = $req->send(); - } catch (Exception $e) { - if (preg_match("/Malformed response: /i", $e->getMessage(), $matches)) { - return $this->_check_options(); - } - throw $e; - } - - $ret = $result->getStatus() == 204; - } - break; - - case LOCK_SH: - case LOCK_EX: - $body = sprintf(' - - - -%s -', ($mode & LOCK_SH) ? "shared" : "exclusive", get_class($this)); // TODO better owner string - inherited from initial commit - try { - $req = $this->_startRequest('LOCK'); - if (is_string($this->user)) { - $req->setAuth($this->user, $this->pass); - } - if ($this->locktoken) { // needed for refreshing a lock - $req->setHeader("Lock-Token", "<{$this->locktoken}>"); - } - $req->setHeader("Timeout", "Infinite, Second-4100000000"); - $req->setHeader("Content-Type", 'text/xml; charset="utf-8"'); - $req->setBody($body); - $result = $req->send(); - } catch (Exception $e) { - if (preg_match("/Malformed response: /i", $e->getMessage(), $matches)) { - return $this->_check_options(); - } - throw $e; - } - - $ret = $result->getStatus() == 200; - - if ($ret) { - $propinfo = new HTTP_WebDAV_Client_parse_lock_response($result->getBody()); - $this->locktoken = $propinfo->locktoken; - // TODO deal with timeout - inherited from initial commit - } - break; - - default: - break; - } - - return $ret; - } - - private function _startRequest($method) { - $req = new HTTP_Request2($this->url); - - // We need to set this to fix a bug in an old nginx as it sends a response body for HEAD requests which is a violation of RFC 2616 and fixed in newer nginx versions (https://pear.php.net/bugs/bug.php?id=20227) - $req->setHeader('Accept-Encoding', 'identity'); - $req->setHeader('User-agent', $this->user_agent); - $req->setHeader('Content-type', $this->content_type); - - $req->setMethod($method); - - return $req; - } - - private function filesize($url) { - $this->connection_open($url, 'r'); - - if (isset($this->stat['size'])) { - return intval($this->stat['size']); - } - } -} - -// Do *not* instantiate here; it is a storage module, so is instantiated on-demand -// $updraftplus_addons_webdav = new UpdraftPlus_Addons_RemoteStorage_webdav; diff --git a/wp-content/plugins/updraftplus/addons/wp-cli.php b/wp-content/plugins/updraftplus/addons/wp-cli.php deleted file mode 100644 index 701fcb0a..00000000 --- a/wp-content/plugins/updraftplus/addons/wp-cli.php +++ /dev/null @@ -1,1231 +0,0 @@ -] - * : File entities which will be backed up. Multiple file entities names should separate by comma (,). - * - * [--include-tables=] - * : Tables which will be backed up. You should backup all tables unless you are an expert in the internals of the WordPress database. Multiple table names seperated by comma (,). If include-tables is not added in command, All tables will be backed up - * --- - * default: all - * --- - * - * [--send-to-cloud] - * : Whether or not send backup to remote cloud storage - * - * [--always-keep] - * : Only allow this backup to be deleted manually (i.e. keep it even if retention limits are hit) - * - * [--label=