db_version() && version_compare($wpdb->db_version(), '5.5.3', '<')) { $errors[] = __('Maple GDPR Cookies requires MySQL 5.5.3 or higher for utf8mb4 support.', 'maple-gdpr-cookies'); } return $errors; } /** * Create database tables on activation */ function mgc_create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $table_name = $wpdb->prefix . 'mgc_consent_logs'; $sql = "CREATE TABLE IF NOT EXISTS $table_name ( id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, user_id bigint(20) UNSIGNED NULL, ip_address varchar(45) NOT NULL, user_agent varchar(255) NOT NULL, consent_type varchar(20) NOT NULL, categories text NULL, consent_given tinyint(1) NOT NULL, consent_date datetime NOT NULL, PRIMARY KEY (id), KEY user_id (user_id), KEY ip_address (ip_address), KEY consent_date (consent_date) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); // Store database version update_option('mgc_db_version', MGC_DB_VERSION); } /** * Set default options on activation */ function mgc_set_defaults() { $default_options = array( 'enabled' => true, 'notice_text' => __('We use cookies to make this site work properly and to understand how you use it. This includes essential cookies (required), analytics cookies (to understand site usage), and marketing cookies (for personalized content). You can accept all, reject optional cookies, or customize your preferences.', 'maple-gdpr-cookies'), 'accept_button_text' => __('Accept All', 'maple-gdpr-cookies'), 'reject_button_text' => __('Reject Optional', 'maple-gdpr-cookies'), 'settings_button_text' => __('Cookie Settings', 'maple-gdpr-cookies'), 'privacy_policy_url' => get_privacy_policy_url(), 'privacy_policy_text' => __('Privacy Policy', 'maple-gdpr-cookies'), 'cookie_expiry' => 365, 'position' => 'bottom', 'theme' => 'light', 'animation' => 'slide', 'button_color' => 'blue', 'custom_button_color' => '', 'custom_button_hover_color' => '', 'show_reject_button' => true, 'show_settings_button' => true, 'preference_display_type' => 'icon', 'custom_css' => '', 'enable_analytics' => true, 'enable_marketing' => true, 'enable_functional' => true, 'enable_logging' => true ); foreach ($default_options as $key => $value) { if (get_option('mgc_' . $key) === false) { add_option('mgc_' . $key, $value); } } } /** * Clear all plugin caches - FIXED VERSION */ function mgc_clear_all_caches() { // Method 1: Clear WordPress object cache (if available) if (function_exists('wp_cache_flush')) { wp_cache_flush(); } // Method 2: Clear plugin-specific transients global $wpdb; // Get all mgc transients $transients = $wpdb->get_col( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '_transient_mgc_%' OR option_name LIKE '_transient_timeout_mgc_%'" ); // Delete each transient foreach ($transients as $transient) { if (strpos($transient, '_transient_timeout_') === 0) { continue; // Skip timeout entries, they'll be deleted with the transient } $transient_key = str_replace('_transient_', '', $transient); delete_transient($transient_key); } // Method 3: Clear specific plugin caches $cache_keys = array( 'mgc_settings', 'mgc_stats', 'mgc_consent_logs' ); foreach ($cache_keys as $key) { wp_cache_delete($key, 'maple-gdpr-cookies'); delete_transient($key); } // Method 4: Clear WooCommerce cache if present if (function_exists('wc_delete_shop_order_transients')) { wc_delete_shop_order_transients(); } // Method 5: Trigger cache clear hooks for other plugins do_action('mgc_clear_caches'); } /** * Plugin activation */ function mgc_activate() { // Check requirements $errors = mgc_check_requirements(); if (!empty($errors)) { deactivate_plugins(plugin_basename(__FILE__)); wp_die( implode('
', $errors), __('Plugin Activation Error', 'maple-gdpr-cookies'), array('back_link' => true) ); } // Create database tables mgc_create_tables(); // Set default options mgc_set_defaults(); // Clear all caches - now using fixed function mgc_clear_all_caches(); // Set activation flag set_transient('mgc_activation_redirect', true, 30); } register_activation_hook(__FILE__, 'mgc_activate'); /** * Plugin deactivation */ function mgc_deactivate() { // Clear all caches mgc_clear_all_caches(); // Clear scheduled events wp_clear_scheduled_hook('mgc_cleanup_logs'); wp_clear_scheduled_hook('mgc_optimize_database'); } register_deactivation_hook(__FILE__, 'mgc_deactivate'); /** * Plugin uninstall - only if user chooses to delete data */ function mgc_uninstall() { global $wpdb; // Always clear scheduled tasks even if keeping data wp_clear_scheduled_hook('mgc_cleanup_logs'); wp_clear_scheduled_hook('mgc_optimize_database'); // Check if user wants to keep data if (get_option('mgc_keep_data_on_uninstall', false)) { return; } // Drop database tables $table_name = $wpdb->prefix . 'mgc_consent_logs'; $wpdb->query("DROP TABLE IF EXISTS $table_name"); // Delete all plugin options $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE 'mgc_%'"); // Clear all caches mgc_clear_all_caches(); } register_uninstall_hook(__FILE__, 'mgc_uninstall'); /** * Load plugin textdomain for translations */ function mgc_load_textdomain() { load_plugin_textdomain( 'maple-gdpr-cookies', false, dirname(plugin_basename(__FILE__)) . '/languages' ); } add_action('plugins_loaded', 'mgc_load_textdomain'); /** * Enqueue frontend scripts and styles */ function mgc_enqueue_frontend_assets() { // Only load if enabled if (!get_option('mgc_enabled', true)) { return; } // Check if user already has consent cookie $has_consent = isset($_COOKIE['mgc_consent']); // Enqueue CSS wp_enqueue_style( 'mgc-frontend', MGC_PLUGIN_URL . 'public/css/frontend.css', array(), MGC_PLUGIN_VERSION ); // Add custom CSS if provided $custom_css = get_option('mgc_custom_css'); if (!empty($custom_css)) { wp_add_inline_style('mgc-frontend', $custom_css); } // Add custom button colors if provided $custom_button_color = get_option('mgc_custom_button_color'); $custom_button_hover_color = get_option('mgc_custom_button_hover_color'); if (!empty($custom_button_color) || !empty($custom_button_hover_color)) { $color_css = ''; if (!empty($custom_button_color)) { $color_css .= '.mgc-button { background: ' . esc_attr($custom_button_color) . ' !important; }'; $color_css .= '.mgc-floating-button { background: ' . esc_attr($custom_button_color) . ' !important; }'; } if (!empty($custom_button_hover_color)) { $color_css .= '.mgc-button:hover { background: ' . esc_attr($custom_button_hover_color) . ' !important; }'; $color_css .= '.mgc-floating-button:hover { background: ' . esc_attr($custom_button_hover_color) . ' !important; }'; } wp_add_inline_style('mgc-frontend', $color_css); } // Enqueue the compliant JS (blocks scripts before consent) wp_enqueue_script( 'mgc-frontend-compliant', MGC_PLUGIN_URL . 'public/js/frontend-compliant.js', array(), MGC_PLUGIN_VERSION, false // Load in head for early script blocking ); // Localize script with settings wp_localize_script('mgc-frontend-compliant', 'mgcSettings', array( 'ajaxUrl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('mgc_consent_nonce'), 'noticeText' => get_option('mgc_notice_text'), 'acceptButtonText' => get_option('mgc_accept_button_text'), 'rejectButtonText' => get_option('mgc_reject_button_text'), 'settingsButtonText' => get_option('mgc_settings_button_text'), 'privacyPolicyUrl' => get_option('mgc_privacy_policy_url'), 'privacyPolicyText' => get_option('mgc_privacy_policy_text'), 'cookieExpiry' => intval(get_option('mgc_cookie_expiry', 365)), 'position' => get_option('mgc_position', 'bottom'), 'theme' => get_option('mgc_theme', 'light'), 'animation' => get_option('mgc_animation', 'slide'), 'buttonColor' => get_option('mgc_button_color', 'blue'), 'showRejectButton' => (bool) get_option('mgc_show_reject_button', true), 'showSettingsButton' => (bool) get_option('mgc_show_settings_button', true), 'preferenceDisplayType' => get_option('mgc_preference_display_type', 'icon') )); } add_action('wp_enqueue_scripts', 'mgc_enqueue_frontend_assets'); /** * Enqueue admin scripts and styles */ function mgc_enqueue_admin_assets($hook) { // Only load on plugin settings pages if (strpos($hook, 'maple-gdpr-cookies') === false) { return; } // Enqueue WordPress color picker wp_enqueue_style('wp-color-picker'); // Enqueue admin CSS wp_enqueue_style( 'mgc-admin', MGC_PLUGIN_URL . 'admin/css/admin.css', array(), MGC_PLUGIN_VERSION ); // Enqueue admin JS wp_enqueue_script( 'mgc-admin', MGC_PLUGIN_URL . 'admin/js/admin.js', array('jquery', 'wp-color-picker'), MGC_PLUGIN_VERSION, true ); } add_action('admin_enqueue_scripts', 'mgc_enqueue_admin_assets'); /** * Add admin menu */ function mgc_add_admin_menu() { add_menu_page( __('Maple GDPR Cookies', 'maple-gdpr-cookies'), __('Cookie Settings', 'maple-gdpr-cookies'), 'manage_options', 'maple-gdpr-cookies', 'mgc_admin_page', 'dashicons-shield', 100 ); add_submenu_page( 'maple-gdpr-cookies', __('Settings', 'maple-gdpr-cookies'), __('Settings', 'maple-gdpr-cookies'), 'manage_options', 'maple-gdpr-cookies', 'mgc_admin_page' ); add_submenu_page( 'maple-gdpr-cookies', __('Consent Logs', 'maple-gdpr-cookies'), __('Consent Logs', 'maple-gdpr-cookies'), 'manage_options', 'maple-gdpr-cookies-logs', 'mgc_logs_page' ); } add_action('admin_menu', 'mgc_add_admin_menu'); /** * Admin page callback */ function mgc_admin_page() { if (!current_user_can('manage_options')) { return; } // Handle form submission if (isset($_POST['mgc_save_settings']) && check_admin_referer('mgc_settings_nonce')) { mgc_save_settings(); } include MGC_PLUGIN_PATH . 'admin/views/settings.php'; } /** * Logs page callback */ function mgc_logs_page() { if (!current_user_can('manage_options')) { return; } include MGC_PLUGIN_PATH . 'admin/views/logs.php'; } /** * Save settings */ function mgc_save_settings() { $settings = array( 'enabled', 'notice_text', 'accept_button_text', 'reject_button_text', 'settings_button_text', 'privacy_policy_url', 'privacy_policy_text', 'cookie_expiry', 'position', 'theme', 'animation', 'button_color', 'custom_button_color', 'custom_button_hover_color', 'show_reject_button', 'show_settings_button', 'preference_display_type', 'custom_css', 'enable_analytics', 'enable_marketing', 'enable_functional', 'enable_logging' ); // List of checkbox fields (need special handling since unchecked = no POST data) $checkbox_fields = array( 'enabled', 'show_reject_button', 'show_settings_button', 'enable_analytics', 'enable_marketing', 'enable_functional', 'enable_logging' ); foreach ($settings as $setting) { // Special handling for checkboxes if (in_array($setting, $checkbox_fields)) { // Checkbox: if it's in POST and = '1', it's checked. Otherwise it's unchecked. $value = (isset($_POST['mgc_' . $setting]) && $_POST['mgc_' . $setting] == '1') ? true : false; } else { $value = isset($_POST['mgc_' . $setting]) ? $_POST['mgc_' . $setting] : ''; // Sanitize based on type if ($setting === 'cookie_expiry') { $value = absint($value); // Enforce minimum and maximum values for GDPR compliance if ($value < 1) $value = 1; if ($value > 365) $value = 365; } elseif ($setting === 'custom_css') { $value = wp_strip_all_tags($value); // Remove any javascript: or data: URLs to prevent CSS injection attacks $value = preg_replace('/url\s*\(\s*[\'"]?\s*(?:javascript|data):/i', 'url(blocked:', $value); // Limit length to prevent abuse (10KB should be more than enough for custom CSS) $value = substr($value, 0, 10000); } elseif (in_array($setting, array('custom_button_color', 'custom_button_hover_color'))) { // Sanitize hex color $value = sanitize_text_field($value); // Validate hex color format if (!empty($value) && !preg_match('/^#[a-fA-F0-9]{6}$/', $value)) { $value = ''; // Clear invalid hex colors } } elseif ($setting === 'preference_display_type') { // Validate preference display type $value = sanitize_text_field($value); if (!in_array($value, array('icon', 'footer', 'neither'))) { $value = 'icon'; // Default to icon if invalid } } else { $value = sanitize_text_field($value); } } update_option('mgc_' . $setting, $value); } // Clear caches after saving mgc_clear_all_caches(); add_settings_error( 'mgc_messages', 'mgc_message', __('Settings saved successfully.', 'maple-gdpr-cookies'), 'updated' ); } /** * AJAX handler for saving consent */ function mgc_save_consent() { check_ajax_referer('mgc_consent_nonce', 'nonce'); // Rate limiting - max 10 consent saves per hour per IP to prevent abuse $ip = mgc_get_ip_address(); $rate_key = 'mgc_consent_rate_' . md5($ip); $attempts = get_transient($rate_key); if ($attempts && $attempts >= 10) { wp_send_json_error(array( 'message' => __('Too many requests. Please try again later.', 'maple-gdpr-cookies') ), 429); return; } // Increment rate limit counter set_transient($rate_key, ($attempts ? $attempts + 1 : 1), HOUR_IN_SECONDS); $consent_type = sanitize_text_field($_POST['consent_type']); $categories = isset($_POST['categories']) ? array_map('sanitize_text_field', $_POST['categories']) : array(); // Log consent if enabled if (get_option('mgc_enable_logging', true)) { mgc_log_consent($consent_type, $categories); } wp_send_json_success(array( 'message' => __('Consent saved successfully', 'maple-gdpr-cookies') )); } add_action('wp_ajax_mgc_save_consent', 'mgc_save_consent'); add_action('wp_ajax_nopriv_mgc_save_consent', 'mgc_save_consent'); /** * Log user consent */ function mgc_log_consent($consent_type, $categories = array()) { global $wpdb; $table_name = $wpdb->prefix . 'mgc_consent_logs'; // Get and anonymize IP address (GDPR compliance) $ip_address = mgc_anonymize_ip(mgc_get_ip_address()); $wpdb->insert( $table_name, array( 'user_id' => get_current_user_id(), 'ip_address' => $ip_address, 'user_agent' => sanitize_text_field(substr($_SERVER['HTTP_USER_AGENT'], 0, 255)), 'consent_type' => $consent_type, 'categories' => json_encode($categories), 'consent_given' => ($consent_type === 'accept'), 'consent_date' => current_time('mysql') ), array('%d', '%s', '%s', '%s', '%s', '%d', '%s') ); } /** * Get user IP address */ function mgc_get_ip_address() { $ip = ''; // Check if behind a proxy and validate if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { // X-Forwarded-For can contain multiple IPs, get the first one (original client) $ip_list = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $ip = trim($ip_list[0]); } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } else { $ip = $_SERVER['REMOTE_ADDR']; } // Validate it's a real IP address if (!filter_var($ip, FILTER_VALIDATE_IP)) { // If invalid, fall back to direct connection IP $ip = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0'; } return sanitize_text_field($ip); } /** * Anonymize IP address (GDPR compliance) * Removes last octet for IPv4, last segment for IPv6 */ function mgc_anonymize_ip($ip) { // Validate and anonymize IPv4 if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { return preg_replace('/\.\d+$/', '.0', $ip); } // Validate and anonymize IPv6 if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { return preg_replace('/:[^:]+$/', ':0', $ip); } // If invalid IP, return masked version return '0.0.0.0'; } /** * Schedule cleanup tasks */ function mgc_schedule_cleanup() { if (!wp_next_scheduled('mgc_cleanup_logs')) { wp_schedule_event(time(), 'daily', 'mgc_cleanup_logs'); } if (!wp_next_scheduled('mgc_optimize_database')) { wp_schedule_event(time(), 'weekly', 'mgc_optimize_database'); } } add_action('wp', 'mgc_schedule_cleanup'); /** * Cleanup old consent logs */ function mgc_cleanup_old_logs() { global $wpdb; $table_name = $wpdb->prefix . 'mgc_consent_logs'; $retention_days = apply_filters('mgc_log_retention_days', 365); $wpdb->query( $wpdb->prepare( "DELETE FROM $table_name WHERE consent_date < DATE_SUB(NOW(), INTERVAL %d DAY)", $retention_days ) ); } add_action('mgc_cleanup_logs', 'mgc_cleanup_old_logs'); /** * Optimize database tables */ function mgc_optimize_database_tables() { global $wpdb; $table_name = $wpdb->prefix . 'mgc_consent_logs'; $wpdb->query("OPTIMIZE TABLE $table_name"); } add_action('mgc_optimize_database', 'mgc_optimize_database_tables'); /** * Add settings link on plugins page */ function mgc_add_settings_link($links) { $settings_link = '' . __('Settings', 'maple-gdpr-cookies') . ''; array_unshift($links, $settings_link); return $links; } add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'mgc_add_settings_link'); /** * Redirect to settings page on activation */ function mgc_activation_redirect() { if (get_transient('mgc_activation_redirect')) { delete_transient('mgc_activation_redirect'); if (!isset($_GET['activate-multi'])) { wp_safe_redirect(admin_url('admin.php?page=maple-gdpr-cookies')); exit; } } } add_action('admin_init', 'mgc_activation_redirect'); /** * Cookie Preferences Shortcode * Usage: [mgc_cookie_preferences] or [mgc_cookie_preferences text="Manage Cookies"] */ function mgc_cookie_preferences_shortcode($atts) { // Only show if plugin is enabled if (!get_option('mgc_enabled', true)) { return ''; } // Parse attributes $atts = shortcode_atts(array( 'text' => __('Cookie Preferences', 'maple-gdpr-cookies'), 'class' => 'mgc-preferences-link' ), $atts); // Return link with data attribute that JavaScript will handle return sprintf( '%s', esc_attr($atts['class']), esc_attr($atts['text']), esc_html($atts['text']) ); } add_shortcode('mgc_cookie_preferences', 'mgc_cookie_preferences_shortcode');