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