api_url, [ 'timeout' => 15, 'sslverify' => false // For local dev/flexibility, though true is better for prod ]); if (is_wp_error($response)) { return; } $code = wp_remote_retrieve_response_code($response); if ($code === 200) { $body = wp_remote_retrieve_body($response); $notices = json_decode($body, true); if (is_array($notices)) { update_option('advaipbl_remote_notices', $notices, false); // false = not autoloaded unless needed } } } public function display_notices() { if (!current_user_can('manage_options')) { return; } $notices = get_option('advaipbl_remote_notices', []); $dismissed = get_option('advaipbl_dismissed_notices', []); if (empty($notices) || !is_array($notices)) { return; } $current_version = defined('ADVAIPBL_VERSION') ? ADVAIPBL_VERSION : '8.13.21'; foreach ($notices as $notice) { if (in_array($notice['id'], $dismissed)) { continue; } // Version check if (!empty($notice['target_version_min']) && version_compare($current_version, $notice['target_version_min'], '<')) { continue; } if (!empty($notice['target_version_max']) && version_compare($current_version, $notice['target_version_max'], '>')) { continue; } if (!$this->matches_environment_filters($notice)) { continue; } $type = isset($notice['type']) ? sanitize_text_field($notice['type']) : 'info'; // Classes: notice-info, notice-success, notice-warning, notice-error $class = 'notice notice-' . $type . ' is-dismissible advaipbl-remote-notice'; $button_html = ''; if (!empty($notice['button_text']) && !empty($notice['button_link'])) { $button_html = '

' . esc_html($notice['button_text']) . '

'; } echo '
'; echo '

' . wp_kses_post($notice['message']) . '

'; echo wp_kses_post($button_html); echo '
'; } } private function matches_environment_filters($notice) { // 1. PHP branch version check (e.g. "8.2.35, 8.3.36, 8.4.27, 8.5.12") if (!empty($notice['target_php_safe'])) { $current_php = preg_replace('/[^0-9\.].*$/', '', PHP_VERSION); $parts = explode('.', $current_php); $current_branch = isset($parts[0], $parts[1]) ? $parts[0] . '.' . $parts[1] : $current_php; $safe_versions = array_filter(array_map('trim', explode(',', $notice['target_php_safe']))); $safe_branches = []; $lowest_branch = null; foreach ($safe_versions as $sv) { $sv_clean = preg_replace('/[^0-9\.]/', '', $sv); if (empty($sv_clean)) { continue; } $sv_parts = explode('.', $sv_clean); if (count($sv_parts) >= 2) { $branch = $sv_parts[0] . '.' . $sv_parts[1]; $safe_branches[$branch] = $sv_clean; if ($lowest_branch === null || version_compare($branch, $lowest_branch, '<')) { $lowest_branch = $branch; } } } if (!empty($safe_branches)) { $is_php_vulnerable = false; if (isset($safe_branches[$current_branch])) { if (version_compare($current_php, $safe_branches[$current_branch], '<')) { $is_php_vulnerable = true; } } elseif ($lowest_branch !== null && version_compare($current_branch, $lowest_branch, '<')) { // Older EOL branch below the minimum supported branch $is_php_vulnerable = true; } if (!$is_php_vulnerable) { return false; } } } // 2. Installed Plugin / Theme check (e.g. "duplicator:1.5.17, duplicator-pro:4.6.10") $item_type = isset($notice['target_item_type']) ? $notice['target_item_type'] : 'none'; $item_rules_raw = isset($notice['target_item_rules']) ? trim($notice['target_item_rules']) : ''; $item_status = isset($notice['target_item_status']) ? $notice['target_item_status'] : 'installed'; if (($item_type === 'plugin' || $item_type === 'theme') && !empty($item_rules_raw)) { $rules = array_filter(array_map('trim', explode(',', $item_rules_raw))); $matched_item = false; foreach ($rules as $rule) { if (!preg_match('/^([a-zA-Z0-9_\-]+)\s*(?:(<=|<|=|:)\s*([0-9a-zA-Z\.\-_]+))?$/', $rule, $matches)) { continue; } $target_slug = strtolower($matches[1]); $operator = !empty($matches[2]) ? ($matches[2] === ':' ? '<=' : $matches[2]) : ''; $target_ver = isset($matches[3]) ? $matches[3] : ''; if ($item_type === 'plugin') { if (!function_exists('get_plugins')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $all_plugins = get_plugins(); foreach ($all_plugins as $plugin_file => $plugin_data) { $dir_slug = strpos($plugin_file, '/') !== false ? dirname($plugin_file) : basename($plugin_file, '.php'); if (strtolower($dir_slug) !== $target_slug) { continue; } if ($item_status === 'active' && !is_plugin_active($plugin_file) && (!function_exists('is_plugin_active_for_network') || !is_plugin_active_for_network($plugin_file))) { continue; } $installed_ver = isset($plugin_data['Version']) ? $plugin_data['Version'] : ''; if (empty($operator) || empty($target_ver) || (!empty($installed_ver) && version_compare($installed_ver, $target_ver, $operator))) { $matched_item = true; break 2; } } } elseif ($item_type === 'theme') { $theme = wp_get_theme($target_slug); if ($theme->exists()) { if ($item_status === 'active') { $active_theme = wp_get_theme(); $active_slug = strtolower($active_theme->get_stylesheet()); $parent_slug = strtolower($active_theme->get_template()); if ($active_slug !== $target_slug && $parent_slug !== $target_slug) { continue; } } $installed_ver = $theme->get('Version'); if (empty($operator) || empty($target_ver) || (!empty($installed_ver) && version_compare($installed_ver, $target_ver, $operator))) { $matched_item = true; break; } } } } if (!$matched_item) { return false; } } return true; } public function ajax_dismiss_notice() { check_ajax_referer('advaipbl_dismiss_notice_nonce', 'security'); if (!current_user_can('manage_options')) { wp_die(-1); } $notice_id = isset($_POST['notice_id']) ? sanitize_text_field(wp_unslash($_POST['notice_id'])) : ''; if ($notice_id) { $dismissed = get_option('advaipbl_dismissed_notices', []); if (!in_array($notice_id, $dismissed)) { $dismissed[] = $notice_id; update_option('advaipbl_dismissed_notices', $dismissed, false); } } wp_send_json_success(); } public function enqueue_scripts($hook) { // Enqueue small inline JS for the dismiss action $version = defined('ADVAIPBL_VERSION') ? ADVAIPBL_VERSION : '1.0.0'; wp_register_script('advaipbl-remote-notices-js', false, [], $version, true); wp_enqueue_script('advaipbl-remote-notices-js'); $nonce = wp_create_nonce('advaipbl_dismiss_notice_nonce'); $js = " jQuery(document).ready(function($) { $(document).on('click', '.advaipbl-remote-notice .notice-dismiss', function() { var notice_id = $(this).parent('.advaipbl-remote-notice').data('notice-id'); if (notice_id) { $.post(ajaxurl, { action: 'advaipbl_dismiss_notice', security: '{$nonce}', notice_id: notice_id }); } }); }); "; wp_add_inline_script('advaipbl-remote-notices-js', $js); } }