'', 'subscription' => 'basic', 'threshold_exceeded' => false, 'activation_datetime' => 0 ]; private $x_api_key = 'hudft60djisdusdjwek'; private $app_host_url = 'https://app.hu-manity.co'; private $app_login_url = 'https://app.hu-manity.co/#/login'; private $app_dashboard_url = 'https://app.hu-manity.co/#/'; private $account_api_url = 'https://account-api.hu-manity.co'; private $designer_api_url = 'https://designer-api.hu-manity.co'; private $transactional_api_url = 'https://transactional-api.hu-manity.co'; private $app_widget_url = '//cdn.hu-manity.co/hu-banner.min.js'; private $deactivaion_url = ''; private $network_admin = false; private $plugin_network_active = false; private static $_instance; private $notices = []; public $options = []; public $network_options = []; public $bot_detect; public $dashboard; public $frontend; public $settings; public $consent_logs; public $privacy_consent; public $privacy_consent_logs; public $welcome; public $welcome_api; public $welcome_frontend; public $db_version; /** * @var $defaults */ public $defaults = [ 'general' => [ 'global_override' => false, 'global_cookie' => false, 'app_id' => '', 'app_key' => '', 'app_blocking' => true, 'conditional_active' => false, 'conditional_display' => 'hide', 'conditional_rules' => [], 'amp_support' => false, 'bot_detection' => true, 'caching_compatibility' => true, 'debug_mode' => false, 'excluded_handles' => [], 'position' => 'bottom', 'message_text' => '', 'css_class' => '', 'accept_text' => '', 'refuse_text' => '', 'refuse_opt' => false, 'refuse_code' => '', 'refuse_code_head' => '', 'revoke_cookies' => false, 'revoke_cookies_opt' => 'automatic', 'revoke_message_text' => '', 'revoke_text' => '', 'redirection' => false, 'see_more' => false, 'link_target' => '_blank', 'link_position' => 'banner', 'time' => 'month', 'time_rejected' => 'month', 'hide_effect' => 'fade', 'on_scroll' => false, 'on_scroll_offset' => 100, 'on_click' => false, 'colors' => [ 'text' => '#fff', 'button' => '#00a99d', 'bar' => '#32323a', 'bar_opacity' => 100 ], 'see_more_opt' => [ 'text' => '', 'link_type' => 'page', 'id' => 0, 'link' => '', 'sync' => false ], 'script_placement' => 'header', 'translate' => true, 'deactivation_delete' => false, 'review_notice' => true, 'review_notice_delay' => 0, 'update_version' => 8, 'update_notice' => true, 'update_notice_diss' => false, 'update_delay_date' => 0, 'update_threshold_date' => 0, 'csp_notice' => false, 'ui_mode' => 'legacy', // applied_template removed — now computed on the fly in React via matchTemplate(). 'displayType' => 'floating', ], 'privacy_consent' => [ 'wordpress_active' => true, 'wordpress_active_type' => 'all', 'contactform7_active' => false, 'contactform7_active_type' => 'all', 'mailchimp_active' => false, 'mailchimp_active_type' => 'all', 'wpforms_active' => false, 'wpforms_active_type' => 'all', 'woocommerce_active' => false, 'woocommerce_active_type' => 'all', 'formidableforms_active' => false, 'formidableforms_active_type' => 'all', 'easydigitaldownloads_active' => false, 'easydigitaldownloads_active_type' => 'all' ], 'data' => [ 'status' => '', 'subscription' => 'basic', 'threshold_exceeded' => false, 'activation_datetime' => 0 ], 'version' => '3.0.2' ]; /** * Authoritative field-ownership partition (#2264). * * Only these keys may be written to cookie_notice_options by plugin code paths * (save_options in react-admin-ajax.php, validate_options in settings.php). * * API-owned fields (position, displayType, bannerColor, primaryColor) are * NEVER written here — cookie_notice_app_design is their exclusive store. * The Designer API config-pull populates that option via get_app_config(). * * @var string[] */ public static $plugin_owned_fields = [ 'message_text', 'accept_text', 'refuse_text', 'revoke_text', 'revoke_message_text', 'css_class', 'refuse_opt', 'revoke_cookies', 'revoke_cookies_opt', 'on_scroll', 'on_scroll_offset', 'on_click', 'redirection', 'see_more', 'see_more_opt', 'link_target', 'link_position', 'time', 'time_rejected', 'hide_effect', 'script_placement', 'bot_detection', 'amp_support', 'caching_compatibility', 'debug_mode', 'conditional_active', 'conditional_display', 'conditional_rules', 'deactivation_delete', 'app_blocking', 'excluded_handles', 'refuse_code', 'refuse_code_head', 'app_id', 'app_key', 'ui_mode', 'global_override', 'global_cookie', 'colors', 'redirect_delay', 'review_notice', 'review_notice_delay', 'update_version', 'update_notice', 'update_notice_diss', 'update_delay_date', 'update_threshold_date', 'csp_notice', 'translate', ]; /** * Disable object cloning. * * @return void */ public function __clone() {} /** * Disable unserializing of the class. * * @return void */ public function __wakeup() {} /** * Main plugin instance. * * @return object */ public static function instance() { if ( self::$_instance === null ) { self::$_instance = new self(); add_action( 'init', [ self::$_instance, 'load_textdomain' ] ); self::$_instance->includes(); self::$_instance->bot_detect = new Cookie_Notice_Bot_Detect(); self::$_instance->dashboard = new Cookie_Notice_Dashboard(); self::$_instance->frontend = new Cookie_Notice_Frontend(); self::$_instance->settings = new Cookie_Notice_Settings(); new Cookie_Notice_React_Admin_Ajax(); self::$_instance->consent_logs = new Cookie_Notice_Consent_Logs(); self::$_instance->privacy_consent = new Cookie_Notice_Privacy_Consent(); self::$_instance->privacy_consent_logs = new Cookie_Notice_Privacy_Consent_Logs(); self::$_instance->welcome = new Cookie_Notice_Welcome(); self::$_instance->welcome_api = new Cookie_Notice_Welcome_API(); self::$_instance->welcome_frontend = new Cookie_Notice_Welcome_Frontend(); } return self::$_instance; } /** * Class constructor. * * @return void */ public function __construct() { // Allow wp-config.php overrides for staging/prod switching. // Usage: define( 'CN_ACCOUNT_API_URL', 'https://stage-api.hu-manity.co' ); if ( defined( 'CN_ACCOUNT_API_URL' ) ) $this->account_api_url = CN_ACCOUNT_API_URL; if ( defined( 'CN_DESIGNER_API_URL' ) ) $this->designer_api_url = CN_DESIGNER_API_URL; if ( defined( 'CN_TRANSACTIONAL_API_URL' ) ) $this->transactional_api_url = CN_TRANSACTIONAL_API_URL; if ( defined( 'CN_X_API_KEY' ) ) $this->x_api_key = CN_X_API_KEY; if ( defined( 'CN_APP_WIDGET_URL' ) ) $this->app_widget_url = CN_APP_WIDGET_URL; if ( defined( 'CN_APP_HOST_URL' ) ) $this->app_host_url = CN_APP_HOST_URL; // define plugin constants $this->define_constants(); // activation hooks register_activation_hook( __FILE__, [ $this, 'activation' ] ); register_deactivation_hook( __FILE__, [ $this, 'deactivation' ] ); // set network data $this->set_network_data(); $this->check_legacy_options(); // get options if ( is_multisite() ) { // get network options $this->network_options['general'] = get_site_option( 'cookie_notice_options', $this->defaults['general'] ); $this->network_options['privacy_consent'] = get_site_option( 'cookie_notice_privacy_consent', $this->defaults['privacy_consent'] ); if ( $this->is_network_admin() ) { $general_options = $this->network_options['general']; $privacy_consent_options = $this->network_options['privacy_consent']; } else { $page = isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : ''; // settings page? if ( is_admin() && $page === 'cookie-notice' ) { // get current url path $url_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ); if ( is_string( $url_path ) && basename( $url_path ) === 'admin.php' ) { // get site options $general_options = get_option( 'cookie_notice_options', $this->defaults['general'] ); $privacy_consent_options = get_option( 'cookie_notice_privacy_consent', $this->defaults['privacy_consent'] ); } } else { if ( $this->is_plugin_network_active() && $this->network_options['general']['global_override'] ) { $general_options = $this->network_options['general']; $privacy_consent_options = $this->network_options['privacy_consent']; } else { $general_options = get_option( 'cookie_notice_options', $this->defaults['general'] ); $privacy_consent_options = get_option( 'cookie_notice_privacy_consent', $this->defaults['privacy_consent'] ); } } } } else { $general_options = get_option( 'cookie_notice_options', $this->defaults['general'] ); $privacy_consent_options = get_option( 'cookie_notice_privacy_consent', $this->defaults['privacy_consent'] ); } // merge old options with new ones $this->options['general'] = $this->multi_array_merge( $this->defaults['general'], $general_options ); $this->options['privacy_consent'] = $this->multi_array_merge( $this->defaults['privacy_consent'], $privacy_consent_options ); if ( ! isset( $this->options['general']['see_more_opt']['sync'] ) ) $this->options['general']['see_more_opt']['sync'] = $this->defaults['general']['see_more_opt']['sync']; // actions add_action( 'plugins_loaded', [ $this, 'set_database_version' ], 0 ); add_action( 'plugins_loaded', [ $this, 'set_status_data' ], 0 ); add_action( 'init', [ $this, 'register_shortcodes' ] ); add_action( 'init', [ $this, 'wpsc_add_cookie' ] ); add_action( 'init', [ $this, 'maybe_apply_dev_tier_override' ] ); add_action( 'init', [ $this, 'set_plugin_links' ] ); add_action( 'admin_init', [ $this, 'update_notice' ] ); add_action( 'admin_init', [ $this, 'maybe_redirect_after_activation' ] ); add_action( 'admin_init', [ $this, 'maybe_show_license_assigned_notice' ] ); add_action( 'admin_init', [ $this, 'maybe_switch_ui_mode' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] ); add_action( 'admin_footer', [ $this, 'deactivate_plugin_template' ] ); add_action( 'wp_ajax_cn_dismiss_notice', [ $this, 'ajax_dismiss_admin_notice' ] ); add_action( 'wp_ajax_cn_review_notice', [ $this, 'ajax_review_notice' ] ); add_action( 'wp_ajax_cn-deactivate-plugin', [ $this, 'deactivate_plugin' ] ); } /** * Set current plugin version from database. * * @return void */ public function set_database_version() { // get current version if ( $this->is_network_admin() ) $this->db_version = get_site_option( 'cookie_notice_version', '1.0.0' ); else $this->db_version = get_option( 'cookie_notice_version', '1.0.0' ); } /** * Check legacy options. * * @return void */ public function check_legacy_options() { // multisite? if ( is_multisite() ) { // get network options $site_options = get_site_option( 'cookie_notice_options', $this->defaults['general'] ); // update legacy options $site_options = $this->update_legacy_options( $site_options ); // any changes? if ( $site_options !== false ) update_site_option( 'cookie_notice_options', $site_options ); } // get options $options = get_option( 'cookie_notice_options', $this->defaults['general'] ); // update legacy options $options = $this->update_legacy_options( $options ); // any changes? if ( $options !== false ) update_option( 'cookie_notice_options', $options ); } /** * Maybe change legacy options. * * @param array $options * @return false|array */ public function update_legacy_options( $options ) { // bail out if options are missing or invalid to avoid PHP 8 fatal on non-array values if ( ! is_array( $options ) ) return $this->defaults['general']; $options_changed = false; // check legacy parameters that were yes/no strings foreach ( [ 'refuse_opt', 'on_scroll', 'on_click', 'deactivation_delete', 'see_more' ] as $param ) { if ( array_key_exists( $param, $options ) && ! is_bool( $options[$param] ) ) { $options[$param] = $options[$param] === 'yes'; $options_changed = true; } } // migrate banner_size → displayType (#2269) if ( array_key_exists( 'banner_size', $options ) ) { $options['displayType'] = $options['banner_size']; unset( $options['banner_size'] ); $options_changed = true; } elseif ( ! array_key_exists( 'displayType', $options ) ) { $options['displayType'] = 'floating'; $options_changed = true; } // check hide banner if ( isset( $options['hide_banner'] ) ) { if ( $options['hide_banner'] && ! isset( $options['conditional_active'] ) ) { $options['conditional_active'] = true; $options['conditional_display'] = 'hide'; $options['conditional_rules'] = [ 1 => [ 1 => [ 'param' => 'user_type', 'operator' => 'equal', 'value' => 'logged_in' ] ] ]; } unset( $options['hide_banner'] ); $options_changed = true; } if ( $options_changed ) return $options; else return false; } /** * Setup plugin constants. * * @return void */ private function define_constants() { define( 'COOKIE_NOTICE_URL', plugins_url( '', __FILE__ ) ); define( 'COOKIE_NOTICE_PATH', plugin_dir_path( __FILE__ ) ); define( 'COOKIE_NOTICE_BASENAME', plugin_basename( __FILE__ ) ); define( 'COOKIE_NOTICE_REL_PATH', dirname( COOKIE_NOTICE_BASENAME ) ); } /** * Set cookie compliance status data. * * @return void */ public function set_status_data() { $default_data = $this->defaults['data']; if ( is_multisite() ) { if ( $this->is_plugin_network_active() ) { // network if ( $this->is_network_admin() ) { if ( $this->network_options['general']['global_override'] ) $status_data = get_site_option( 'cookie_notice_status', $default_data ); else $status_data = $default_data; // site } else { if ( $this->network_options['general']['global_override'] ) $status_data = get_site_option( 'cookie_notice_status', $default_data ); else $status_data = get_option( 'cookie_notice_status', $default_data ); } } else { // network if ( $this->is_network_admin() ) $status_data = $default_data; // site else $status_data = get_option( 'cookie_notice_status', $default_data ); } } else $status_data = get_option( 'cookie_notice_status', $default_data ); // old status format? if ( ! is_array( $status_data ) ) { // update config data $status_data = $this->welcome_api->get_app_config( '', true ); } else { // merge database data with default data $status_data = array_merge( $default_data, $status_data ); } if ( $status_data['threshold_exceeded'] ) $this->options['general']['app_blocking'] = false; // check status $status = $this->check_status( $status_data['status'] ); // no activation timestamp? if ( empty( $status_data['activation_datetime'] ) ) { if ( $status === 'active' ) $activation = time(); else $activation = 0; } else $activation = (int) $status_data['activation_datetime']; // set status data $this->status_data = [ 'status' => $status, 'subscription' => $this->check_subscription( $status_data['subscription'] ), 'threshold_exceeded' => (bool) $status_data['threshold_exceeded'], 'activation_datetime' => $activation ]; } /** * Get cookie compliance status data. * * @return string */ public function get_status_data() { return $this->status_data; } /** * Get cookie compliance status. * * @return string */ public function get_status() { return $this->status_data['status']; } /** * Check cookie compliance status. * * @param string $status * @return string */ public function check_status( $status ) { $status = sanitize_key( $status ); return ! empty( $status ) && in_array( $status, [ 'active', 'pending' ], true ) ? $status : $this->defaults['data']['status']; } /** * Get cookie compliance subscription. * * @return string */ /** * CN_DEV_MODE: Apply ?cn_tier override. * * Hooked on 'init' so current_user_can() is available (it is NOT at plugins_loaded:0). * Overrides status_data + app_id in-memory for the current request. */ public function maybe_apply_dev_tier_override() { if ( ! defined( 'CN_DEV_MODE' ) || ! CN_DEV_MODE ) return; if ( ! current_user_can( 'manage_options' ) ) return; $cn_tier = isset( $_GET['cn_tier'] ) ? sanitize_key( $_GET['cn_tier'] ) : ''; if ( $cn_tier === 'basic' ) { $this->status_data['subscription'] = 'basic'; $this->options['general']['app_id'] = ''; } elseif ( $cn_tier === 'free' ) { $this->status_data['subscription'] = 'basic'; if ( empty( $this->options['general']['app_id'] ) ) $this->options['general']['app_id'] = 'cn-dev-free-plan'; } elseif ( $cn_tier === 'pro' ) { $this->status_data['subscription'] = 'pro'; if ( empty( $this->options['general']['app_id'] ) ) $this->options['general']['app_id'] = 'cn-dev-pro-plan'; } } public function get_subscription() { return $this->status_data['subscription']; } /** * Check cookie compliance subscription. * * @param string $subscription * @return string */ public function check_subscription( $subscription ) { $subscription = sanitize_key( $subscription ); return ! empty( $subscription ) && in_array( $subscription, [ 'basic', 'pro' ], true ) ? $subscription : $this->defaults['data']['subscription']; } /** * Check whether the current threshold is exceeded. * * @return bool */ public function threshold_exceeded() { return $this->status_data['threshold_exceeded']; } /** * Get cookie compliance activation timestamp. * * @return int */ public function get_cc_activation_datetime() { return (int) $this->status_data['activation_datetime']; } /** * Get endpoint URL. * * @param string $type * @param string $query * @return string */ public function get_url( $type, $query = '' ) { if ( $type === 'login' ) $url = $this->app_login_url; elseif ( $type === 'dashboard' ) $url = $this->app_dashboard_url; elseif ( $type === 'widget' ) $url = $this->app_widget_url; elseif ( $type === 'host' ) $url = $this->app_host_url; elseif ( $type === 'account_api' ) $url = $this->account_api_url; elseif ( $type === 'designer_api' ) $url = $this->designer_api_url; elseif ( $type === 'transactional_api' ) $url = $this->transactional_api_url; return $url . ( $query !== '' ? $query : '' ); } /** * Get API key. * * @return string */ public function get_api_key() { return $this->x_api_key; } /** * Check whether the current request is for the network administrative interface. * * @return bool */ public function is_network_admin() { return $this->network_admin; } /** * Check whether the plugin is active for the entire network. * * @return bool */ public function is_plugin_network_active() { return $this->plugin_network_active; } /** * Check whether network-wide options should be used. * * Returns true when the plugin is network-active with global_override enabled, * meaning all sites share the network-level configuration. * * @return bool */ public function is_network_options() { return is_multisite() && $this->is_plugin_network_active() && $this->network_options['general']['global_override']; } /** * Set network data. * * @return void */ private function set_network_data() { // load plugin.php file if ( ! function_exists( 'is_plugin_active_for_network' ) ) require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); $cn_network = isset( $_POST['cn_network'] ) ? (int) $_POST['cn_network'] : false; // bypass is_network_admin() to handle AJAX requests properly. $this->network_admin = is_multisite() && ( is_network_admin() || ( wp_doing_ajax() && $cn_network === 1 ) ); // check whether the plugin is active for the entire network. $this->plugin_network_active = is_plugin_active_for_network( COOKIE_NOTICE_BASENAME ); } /** * Include required files. * * @return void */ private function includes() { include_once( COOKIE_NOTICE_PATH . 'includes/bot-detect.php' ); include_once( COOKIE_NOTICE_PATH . 'includes/dashboard.php' ); include_once( COOKIE_NOTICE_PATH . 'includes/frontend.php' ); include_once( COOKIE_NOTICE_PATH . 'includes/functions.php' ); include_once( COOKIE_NOTICE_PATH . 'includes/settings.php' ); include_once( COOKIE_NOTICE_PATH . 'includes/react-admin-ajax.php' ); include_once( COOKIE_NOTICE_PATH . 'includes/consent-logs.php' ); include_once( COOKIE_NOTICE_PATH . 'includes/privacy-consent.php' ); include_once( COOKIE_NOTICE_PATH . 'includes/privacy-consent-logs.php' ); include_once( COOKIE_NOTICE_PATH . 'includes/welcome.php' ); include_once( COOKIE_NOTICE_PATH . 'includes/welcome-api.php' ); include_once( COOKIE_NOTICE_PATH . 'includes/welcome-frontend.php' ); } /** * Load textdomain. * * @return void */ public function load_textdomain() { load_plugin_textdomain( 'cookie-notice', false, dirname( COOKIE_NOTICE_BASENAME ) . '/languages/' ); } /** * Plugin activation. * * @global object $wpdb * * @param bool $network * @return void */ public function activation( $network ) { // New installs start with React UI. We keep the compile-time default as // 'legacy' so existing sites (which may lack ui_mode in the DB) continue // to see the legacy interface through the multi_array_merge() fallback. $activation_defaults = $this->defaults['general']; $activation_defaults['ui_mode'] = 'react'; // network activation? if ( is_multisite() && $network ) { // add network options add_site_option( 'cookie_notice_options', $activation_defaults ); add_site_option( 'cookie_notice_privacy_consent', $this->defaults['privacy_consent'] ); add_site_option( 'cookie_notice_status', $this->defaults['data'] ); add_site_option( 'cookie_notice_version', $this->defaults['version'] ); // Reactivations: switch network-level option to React UI. $net_options = get_site_option( 'cookie_notice_options', [] ); if ( is_array( $net_options ) && ( ! isset( $net_options['ui_mode'] ) || $net_options['ui_mode'] !== 'react' ) ) { $net_options['ui_mode'] = 'react'; update_site_option( 'cookie_notice_options', $net_options ); } global $wpdb; // get all available sites $blogs_ids = $wpdb->get_col( 'SELECT blog_id FROM ' . $wpdb->blogs ); foreach ( $blogs_ids as $blog_id ) { // change to another site switch_to_blog( (int) $blog_id ); // run current site activation process $this->activate_site(); restore_current_blog(); } } else { $this->activate_site(); // Set transient so maybe_redirect_after_activation() fires on the next admin_init. // Single-site only — network activation handled above (no per-site redirect). set_transient( 'cn_activation_redirect', 1, 30 ); } } /** * Single site activation. * * @return void */ public function activate_site() { // New installs start with React UI via activation_defaults. $activation_defaults = $this->defaults['general']; $activation_defaults['ui_mode'] = 'react'; add_option( 'cookie_notice_options', $activation_defaults, null, false ); add_option( 'cookie_notice_privacy_consent', $this->defaults['privacy_consent'], null, false ); add_option( 'cookie_notice_status', $this->defaults['data'], null, false ); add_option( 'cookie_notice_version', $this->defaults['version'], null, false ); // Reactivations: add_option above is a no-op when the key exists, // so explicitly switch existing sites to React UI on activation. $options = get_option( 'cookie_notice_options', [] ); if ( is_array( $options ) && ( ! isset( $options['ui_mode'] ) || $options['ui_mode'] !== 'react' ) ) { $options['ui_mode'] = 'react'; update_option( 'cookie_notice_options', $options ); } } /** * Redirect to the React admin welcome screen after single-site activation. * * Fires on admin_init. Reads a short-lived transient set by activation(). * Guards against: network admin, bulk activation, and insufficient caps. * * ⚠️ Uses cn_react_welcome=1 (NOT welcome=1) — admin-welcome.js intercepts * the ?welcome=1 param and opens the old PHP modal simultaneously if used. * * @return void */ public function maybe_redirect_after_activation() { if ( ! get_transient( 'cn_activation_redirect' ) ) { return; } // Never redirect inside the network admin screen. if ( is_network_admin() ) { return; } // Bulk-activate (wp-admin/plugins.php?activate-multi=true) — skip redirect. if ( isset( $_GET['activate-multi'] ) ) { return; } // Only admins should be redirected. if ( ! current_user_can( 'manage_options' ) ) { return; } delete_transient( 'cn_activation_redirect' ); wp_safe_redirect( admin_url( 'admin.php?page=cookie-notice&cn_react_welcome=1' ) ); exit; } /** * Show a one-time success notice after a React modal license assignment. * * Triggered by ?license_assigned=1 (set by LicenseSelectStep on success). * Reads optional ?slots_remaining=N for copy personalisation. * The param disappears on the next page load automatically — no transient needed. * * @return void */ public function maybe_show_license_assigned_notice() { if ( ! is_admin() ) return; if ( empty( $_GET['license_assigned'] ) || $_GET['license_assigned'] !== '1' ) return; if ( ! current_user_can( 'manage_options' ) ) return; $slots = isset( $_GET['slots_remaining'] ) ? (int) $_GET['slots_remaining'] : null; if ( $slots !== null && $slots > 0 ) { /* translators: %d: number of remaining domains on the plan */ $slots_text = ' ' . sprintf( _n( '%d domain remaining on your plan.', '%d domains remaining on your plan.', $slots, 'cookie-notice' ), $slots ); } elseif ( $slots === 0 ) { $slots_text = ' ' . esc_html__( 'No domains remaining on this plan.', 'cookie-notice' ); } else { $slots_text = ''; } $message = esc_html__( 'Compliance by Hu-manity.co — Pro is now active on this site.', 'cookie-notice' ) . $slots_text; $this->add_notice( '
' . $message . '
', 'notice-success is-dismissible' ); } /** * Switch UI mode via ?ui_mode=react|legacy query param. * * Persists the choice to the DB so it sticks across page loads. * Admin-only (manage_options). Works in production — no CN_DEV_MODE required. * * @return void */ public function maybe_switch_ui_mode() { if ( ! isset( $_GET['ui_mode'] ) ) return; // Only process ui_mode switches on the plugin's own admin page. if ( ! isset( $_GET['page'] ) || $_GET['page'] !== 'cookie-notice' ) return; if ( ! current_user_can( 'manage_options' ) ) return; $requested = sanitize_key( $_GET['ui_mode'] ); if ( ! in_array( $requested, [ 'react', 'legacy' ], true ) ) return; $current = $this->options['general']['ui_mode']; // Update DB only if the value actually changed. if ( $current !== $requested ) { $this->options['general']['ui_mode'] = $requested; if ( $this->is_network_admin() ) { $db_options = get_site_option( 'cookie_notice_options', [] ); } else { $db_options = get_option( 'cookie_notice_options', [] ); } $db_options['ui_mode'] = $requested; if ( $this->is_network_admin() ) { update_site_option( 'cookie_notice_options', $db_options ); } else { update_option( 'cookie_notice_options', $db_options ); } } // Always set in-memory so the current request renders the correct view. $this->options['general']['ui_mode'] = $requested; } /** * Plugin deactivation. * * @global object $wpdb * * @param bool $network * @return void */ public function deactivation( $network ) { // network deactivation? if ( is_multisite() && $network ) { $delete = $this->options['general']['global_override'] && $this->options['general']['deactivation_delete']; // delete network options? if ( $delete ) { delete_site_option( 'cookie_notice_options' ); delete_site_option( 'cookie_notice_privacy_consent' ); delete_site_option( 'cookie_notice_status' ); delete_site_option( 'cookie_notice_app_analytics' ); delete_site_option( 'cookie_notice_app_blocking' ); delete_site_option( 'cookie_notice_version' ); } global $wpdb; // get all available sites $blogs_ids = $wpdb->get_col( 'SELECT blog_id FROM ' . $wpdb->blogs ); foreach ( $blogs_ids as $blog_id ) { // change to another site switch_to_blog( (int) $blog_id ); // run current site deactivation process $this->deactivate_site( $delete ); restore_current_blog(); } } else $this->deactivate_site(); } /** * Single site deactivation. * * @param bool $force_deletion * @return void */ public function deactivate_site( $force_deletion = false ) { // delete settings? if ( $force_deletion || $this->options['general']['deactivation_delete'] ) { // delete options delete_option( 'cookie_notice_options' ); delete_option( 'cookie_notice_privacy_consent' ); delete_option( 'cookie_notice_status' ); delete_option( 'cookie_notice_app_analytics' ); delete_option( 'cookie_notice_app_blocking' ); delete_option( 'cookie_notice_version' ); // delete transients if any delete_transient( 'cookie_notice_app_token' ); delete_transient( 'cookie_notice_app_quick_config' ); delete_transient( 'cookie_notice_app_subscriptions' ); } // remove wp super cache cookie $this->wpsc_delete_cookie(); } /** * Update notice. * * @return void */ public function update_notice() { if ( ! current_user_can( 'install_plugins' ) ) return; // bail an ajax if ( wp_doing_ajax() ) return; $network = $this->is_network_admin(); // get cookie compliance status $status = $this->get_status(); // get subscription $subscription = $this->get_subscription(); // update number $current_update = 14; // new version? if ( version_compare( $this->db_version, $this->defaults['version'], '<' ) ) { if ( $this->options['general']['update_version'] < $current_update ) { // check version, if update version is lower than plugin version, set update notice to true $this->options['general']['update_version'] = $current_update; $this->options['general']['update_notice'] = true; // update options if ( $network ) { $this->options['general']['update_notice_diss'] = false; update_site_option( 'cookie_notice_options', $this->options['general'] ); } else update_option( 'cookie_notice_options', $this->options['general'] ); } // update 2.4.17+ if ( version_compare( $this->db_version, '2.4.17', '<' ) ) { // get cookie compliance activation timestamp $activation_date = $this->get_cc_activation_datetime(); // get status data $data = $this->status_data; // no activation timestamp? if ( empty( $activation_date ) ) { if ( $status === 'active' ) $activation = time(); else $activation = 0; } else $activation = (int) $data['activation_datetime']; // update activation timestamp $data['activation_datetime'] = $activation; if ( $network ) update_site_option( 'cookie_notice_status', $data ); else update_option( 'cookie_notice_status', $data, false ); } // update plugin version if ( $network ) update_site_option( 'cookie_notice_version', $this->defaults['version'] ); else update_option( 'cookie_notice_version', $this->defaults['version'], false ); } // check page $page = isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : ''; // if visiting settings, mark notice as read if ( $page === 'cookie-notice' && ! empty( $_GET['welcome'] ) ) { $this->options['general']['update_notice'] = false; if ( $network ) { $this->options['general']['update_notice_diss'] = true; update_site_option( 'cookie_notice_options', $this->options['general'] ); } else update_option( 'cookie_notice_options', $this->options['general'] ); } if ( is_multisite() && ( ( $this->is_plugin_network_active() && ! $network && $this->network_options['general']['global_override'] ) || ( $network && ! $this->is_plugin_network_active() ) ) ) $this->options['general']['update_notice'] = false; // compliance only if ( $status === 'active' ) { // get analytics data options if ( $network ) $analytics = get_site_option( 'cookie_notice_app_analytics', [] ); else $analytics = get_option( 'cookie_notice_app_analytics', [] ); if ( is_multisite() && ( ( $network && ! $this->is_plugin_network_active() && ! $this->network_options['general']['global_override'] ) || ( ! $network && $this->is_plugin_network_active() && $this->network_options['general']['global_override'] ) ) ) $allow_notice = false; else $allow_notice = true; // show threshold limit warning if ( ! empty( $analytics ) && $allow_notice ) { // cycle usage data $cycle_usage = [ 'threshold' => ! empty( $analytics['cycleUsage']->threshold ) ? (int) $analytics['cycleUsage']->threshold : 0, 'visits' => ! empty( $analytics['cycleUsage']->visits ) ? (int) $analytics['cycleUsage']->visits : 0, 'end_date' => ! empty( $analytics['cycleUsage']->endDate ) ? date_create_from_format( '!Y-m-d', $analytics['cycleUsage']->endDate ) : date_create_from_format( 'Y-m-d H:i:s', current_time( 'mysql', true ) ), 'last_updated' => ! empty( $analytics['lastUpdated'] ) ? date_create_from_format( 'Y-m-d H:i:s', $analytics['lastUpdated'] ) : date_create_from_format( 'Y-m-d H:i:s', current_time( 'mysql', true ) ) ]; // if threshold in use if ( $cycle_usage['threshold'] ) { // if threshold exceeded and there was no notice before if ( $cycle_usage['visits'] >= $cycle_usage['threshold'] && $cycle_usage['last_updated']->getTimestamp() < $cycle_usage['end_date']->getTimestamp() && $this->options['general']['update_threshold_date'] < $cycle_usage['end_date']->getTimestamp() ) { $date_format = get_option( 'date_format' ); $upgrade_link = $this->get_url( 'dashboard', '?app-id=' . $this->options['general']['app_id'] . '&open-modal=payment' ); $threshold = $cycle_usage['threshold']; $cycle_date = date_i18n( $date_format, $cycle_usage['end_date']->getTimestamp() ); $this->add_notice( '' . sprintf( __( 'Your website has reached the %1$s visits usage limit for the Compliance by Hu-manity.co Free Plan. Compliance services such as Consent Record Storage, Autoblocking, and Consent Analytics have been deactivated until current usage cycle ends on %2$s.', 'cookie-notice' ), $threshold, $cycle_date ) . '
' . sprintf( __( 'To reactivate compliance services now, upgrade your domain to a Pro plan.', 'cookie-notice' ) . '
' . sprintf( __( "Hi, you've been using Compliance by Hu-manity.co for more than %s. We hope it has been a valuable addition to your WordPress site. We would be grateful if you could take a few minutes to share your thoughts by leaving a review.", 'cookie-notice' ), human_time_diff( $activation_date, $current_time ) ) . '
' . esc_html__( 'Thank you for helping us improve and grow!', 'cookie-notice' ) . '
' . esc_html__( 'Review', 'cookie-notice' ) . '' . esc_html__( 'Delay', 'cookie-notice' ) . '' . esc_html__( 'Dismiss', 'cookie-notice' ) . '