function_exists('rocket_init'), 'defined WP_ROCKET_VERSION' => defined('WP_ROCKET_VERSION'), 'defined WP_ROCKET_FILE' => defined('WP_ROCKET_FILE'), 'function_exists get_rocket_option' => function_exists('get_rocket_option'), 'class_exists WP_Rocket\Engine\Optimization\GoogleFonts\Combine' => class_exists('WP_Rocket\Engine\Optimization\GoogleFonts\Combine'), 'plugin active wp-rocket/wp-rocket.php' => in_array('wp-rocket/wp-rocket.php', (array) get_option('active_plugins', array()), true) || (is_multisite() && isset(get_site_option('active_sitewide_plugins', array())['wp-rocket/wp-rocket.php'])) ); foreach ($checks as $check_name => $result) { self::debug_log("WP Rocket check - {$check_name}: " . ($result ? 'YES' : 'NO')); if ($result) { return true; } } return false; } /** * Enable WP Rocket integration by adding filter */ public static function enable_wp_rocket_integration() { self::debug_log('Checking WP Rocket integration...'); if (self::is_wp_rocket_active()) { self::debug_log('WP Rocket is active - adding rocket_cache_reject_ua filter'); add_filter('rocket_cache_reject_ua', array(__CLASS__, 'add_ai_bots_to_wp_rocket_cache_reject_ua')); } else { self::debug_log('WP Rocket is not active'); } self::debug_log('WP Rocket cache reject UA filter added'); } /** * Disable WP Rocket integration by removing filter */ public static function disable_wp_rocket_integration() { if (self::is_wp_rocket_active()) { remove_filter('rocket_cache_reject_ua', array(__CLASS__, 'add_ai_bots_to_wp_rocket_cache_reject_ua')); } } /** * Filter WP Rocket's cache reject UA to include AI bot patterns * * @param array $reject_ua Current array of user agents to reject from cache * @return array Modified array with AI bot patterns added */ public static function add_ai_bots_to_wp_rocket_cache_reject_ua($reject_ua) { self::debug_log('WP Rocket rocket_cache_reject_ua filter called'); self::debug_log('Original reject_ua: ' . wp_json_encode($reject_ua)); // Ensure we have an array if (!is_array($reject_ua)) { $reject_ua = array(); } // Get AI bot user agent patterns $ai_bot_patterns = self::get_ai_bot_user_agent_patterns(); self::debug_log('AI bot patterns to add: ' . wp_json_encode($ai_bot_patterns)); // Add our AI bot patterns with wildcards for partial matching foreach ($ai_bot_patterns as $pattern) { // Add pattern with wildcards for partial matching $wildcard_pattern = '(.*)' . preg_quote($pattern, '/') . '(.*)'; // Only add if not already present if (!in_array($wildcard_pattern, $reject_ua)) { $reject_ua[] = $wildcard_pattern; } } self::debug_log('Final reject_ua: ' . wp_json_encode($reject_ua)); return $reject_ua; } /** * Get AI bot user agent patterns (centralized source of truth) * * @return array */ public static function get_ai_bot_user_agent_patterns() { return array( // AI Agents 'NovaAct', 'Operator', 'Anthropic-AI', 'Claude-Web', 'Claude-User', 'Cohere-AI', 'Cline', // AI Assistants 'ChatGPT-User', 'DuckAssistBot', 'Meta-ExternalFetcher', 'Perplexity-User', // AI Data Scrapers 'AI2Bot', 'AppleBot-Extended', 'ByteSpider', 'CCBot', 'ClaudeBot', 'Cohere-Training-Data-Crawler', 'DiffBot', 'FacebookBot', 'Google-Extended', 'GPTBot', 'Meta-ExternalAgent', 'Omgili', 'PanguBot', 'TimpiBot', 'Webzio-Extended', // AI Search Crawlers 'AmazonBot', 'AppleBot', 'OAI-SearchBot', 'PerplexityBot', 'YouBot', 'Claude-SearchBot', // Generic AI patterns 'OpenAI', 'Claude', 'Anthropic', 'Bard', 'Gemini', 'Copilot', 'Bing-Chat', 'Perplexity', 'AI-Assistant', 'AI-Bot', 'LLM-Bot', 'Artificial-Intelligence', 'GPT-User' ); } /** * Debug flag */ private static $debug_enabled = false; // Set to true for debugging /** * Debug log function */ public static function debug_log($message) { if (self::$debug_enabled && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) { if (function_exists('error_log')) { error_log('Airefs Tracking Debug: ' . $message); } } } /** * Initialize tracking functionality */ public static function init() { self::debug_log('Init called'); // Enable WP Rocket integration regardless of tracking settings self::enable_wp_rocket_integration(); // Only initialize if tracking is enabled $options = get_option(AIREFS_TRACKING_OPTIONS_NAME, array()); if (empty($options['tracking_enabled'])) { self::debug_log('Tracking not enabled in options'); return; } if (empty($options['access_token'])) { self::debug_log('Access token is empty'); return; } self::debug_log('Tracking initialized successfully'); // Hook into WordPress to track pageviews (server-side) add_action('init', array(__CLASS__, 'prevent_bot_caching'), 0); add_action('wp', array(__CLASS__, 'track_pageview')); // Hook to enqueue client-side tracking script add_action('wp_enqueue_scripts', array(__CLASS__, 'enqueue_tracking_script'), 10); } /** * Enqueue client-side tracking script */ public static function enqueue_tracking_script() { // Skip for admin, AJAX, cron, and CLI requests if (is_admin()) { return; } if (wp_doing_ajax()) { return; } if (wp_doing_cron()) { return; } if (defined('WP_CLI') && WP_CLI) { return; } // Skip for 404 pages unless configured otherwise if (is_404()) { return; } // Enqueue the tracking script wp_enqueue_script( 'airefs-tracking', AIREFS_TRACKING_PLUGIN_URL . 'assets/js/airefs-tracking.js', array(), AIREFS_TRACKING_VERSION, true // Load in footer ); // Localize script with WordPress API settings wp_localize_script( 'airefs-tracking', 'airefsApiSettings', array( 'root' => esc_url_raw(rest_url()), 'apiEndpoint' => esc_url_raw(rest_url('airefs/v1/events')), 'apiEndpoints' => array_values(array_unique(array( esc_url_raw(rest_url('airefs/v1/events')), esc_url_raw(add_query_arg('rest_route', '/airefs/v1/events', home_url('/'))), ))), ) ); self::debug_log('Client-side tracking script enqueued'); } /** * Check if the current request is from an AI bot * * @return bool */ private static function is_ai_bot() { // Check UTM source parameter $utm_source = isset($_GET['utm_source']) ? sanitize_text_field(wp_unslash($_GET['utm_source'])) : ''; // Check user agent $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : ''; // AI bot patterns to detect $ai_bot_patterns = array( // UTM source patterns 'utm_sources' => array( 'chatgpt', 'gpt', 'openai', 'claude', 'anthropic', 'bard', 'gemini', 'copilot', 'bing-chat', 'perplexity', 'ai-assistant', 'ai-bot', 'llm', 'ai-search' ), // User agent patterns (using centralized function) 'user_agents' => self::get_ai_bot_user_agent_patterns() ); // Check UTM source if (!empty($utm_source)) { foreach ($ai_bot_patterns['utm_sources'] as $pattern) { if (stripos($utm_source, $pattern) !== false) { return true; } } } // Check user agent if (!empty($user_agent)) { foreach ($ai_bot_patterns['user_agents'] as $pattern) { if (stripos($user_agent, $pattern) !== false) { return true; } } } return false; } public static function prevent_bot_caching() { if (is_admin() || wp_doing_ajax() || wp_doing_cron() || (defined('WP_CLI') && WP_CLI)) { return; } // Check if request is from an AI bot if (self::is_ai_bot()) { self::debug_log('AI bot detected - setting no-cache headers'); // Set WordPress constant to prevent caching if (!defined('DONOTCACHEPAGE')) { define('DONOTCACHEPAGE', true); } // Get options to check if cache vary headers are enabled $options = get_option(AIREFS_TRACKING_OPTIONS_NAME, array()); $enable_cache_vary = isset($options['enable_cache_vary']) ? $options['enable_cache_vary'] : false; // LiteSpeed Cache specific handling - only if enabled if (defined('LSCWP_V') && $enable_cache_vary) { // Use LiteSpeed hook to disable caching do_action('litespeed_control_set_nocache', 'AI bot detected'); // Add User-Agent to vary for LiteSpeed add_filter('litespeed_vary', function ($varies) { if (!in_array('user-agent', $varies)) { $varies[] = 'user-agent'; } return $varies; }); } // Set cache control headers if (!headers_sent()) { header('Cache-Control: no-cache, must-revalidate, max-age=0'); header('Pragma: no-cache'); header('Expires: 0'); // Tell caches that response varies based on User-Agent - only if enabled if ($enable_cache_vary) { header('Vary: User-Agent'); } // Additional CDN headers to prevent caching header('X-Accel-Expires: 0'); // Nginx header('X-Cache-Control: no-cache'); // Some CDNs } } } /** * Track pageview on wp hook */ public static function track_pageview() { self::debug_log('track_pageview called'); // Skip for admin, AJAX, cron, and CLI requests if (is_admin()) { self::debug_log('Skipping: is_admin()'); return; } if (wp_doing_ajax()) { self::debug_log('Skipping: wp_doing_ajax()'); return; } if (wp_doing_cron()) { self::debug_log('Skipping: wp_doing_cron()'); return; } if (defined('WP_CLI') && WP_CLI) { self::debug_log('Skipping: WP_CLI'); return; } // Skip for 404 pages unless configured otherwise if (is_404()) { self::debug_log('Skipping: is_404()'); return; } // Skip REST API requests if (defined('REST_REQUEST') && REST_REQUEST) { self::debug_log('Skipping: REST API request'); return; } // Get current page URL for additional checks $current_url = self::get_current_url(); // Additional check for REST API endpoints in URL if (strpos($current_url, '/wp-json/') !== false) { self::debug_log('Skipping: wp-json endpoint request'); return; } // Skip WordPress-specific pages if (self::is_wordpress_page($current_url)) { self::debug_log('Skipping: WordPress-specific page'); return; } self::debug_log('Current URL: ' . $current_url); // Skip asset requests (favicon, images, css, js, etc.) if (self::is_asset_request($current_url)) { self::debug_log('Skipping: asset request'); return; } // Capture metadata now; queue after final response status is available. self::trigger_tracking_event($current_url); } /** * Get current page URL * * @return string */ private static function get_current_url() { $scheme = is_ssl() ? 'https' : 'http'; $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : ''; $request_uri = isset($_SERVER['REQUEST_URI']) && is_string($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : ''; return $scheme . '://' . $host . $request_uri; } /** * Check if the current request is for a WordPress-specific page * * @param string $url * @return bool */ private static function is_wordpress_page($url) { // Parse URL to get the path $parsed_url = wp_parse_url($url); $path = isset($parsed_url['path']) ? $parsed_url['path'] : ''; // WordPress-specific paths to exclude from tracking $wordpress_paths = array( '/wp-admin', '/wp-login.php', '/wp-register.php', '/wp-signup.php', '/wp-activate.php', '/wp-cron.php', '/wp-mail.php', '/wp-trackback.php', '/wp-comments-post.php', '/wp-links-opml.php', '/wp-app.php', '/wp-atom.php', '/wp-commentsrss2.php', '/wp-feed.php', '/wp-rdf.php', '/wp-rss.php', '/wp-rss2.php', '/xmlrpc.php', '/wp-includes/', '/wp-content/uploads/', ); // Check if path starts with any WordPress-specific path foreach ($wordpress_paths as $wp_path) { if (strpos($path, $wp_path) === 0) { return true; } } return false; } /** * Check if the current request is for an asset file * * @param string $url * @return bool */ private static function is_asset_request($url) { // Parse URL to get the path $parsed_url = wp_parse_url($url); $path = isset($parsed_url['path']) ? $parsed_url['path'] : ''; // Get file extension $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION)); // List of asset file extensions to exclude from tracking $asset_extensions = array( 'ico', 'png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'bmp', 'tiff', 'css', 'js', 'map', 'woff', 'woff2', 'ttf', 'eot', 'otf', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'zip', 'rar', '7z', 'tar', 'gz', 'mp3', 'wav', 'ogg', 'flac', 'mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'txt', 'log', 'xml', 'json' ); return in_array($extension, $asset_extensions, true); } /** * Capture an event for the bounded background queue * * @param string $url */ private static function trigger_tracking_event($url) { $payload = array( 'name' => 'pageview', 'source' => 'site', 'url' => $url, 'headers' => self::get_request_headers(), 'session_id' => self::get_or_create_session_id(), 'timestamp' => current_time('c', true), 'event_client' => AIREFS_TRACKING_UA, ); $blog_id = get_current_blog_id(); add_action('shutdown', function () use ($payload, $blog_id) { if (get_current_blog_id() !== $blog_id) { return; } $status = http_response_code(); if ($status === 404) { return; } if (is_int($status) && $status >= 100 && $status <= 599) { $payload['headers']['status'] = (string) $status; } AirefsTrackingQueue::enqueue($payload); }, PHP_INT_MAX); } /** * Get request headers for tracking * * @return array */ private static function get_request_headers() { $headers = array(); if (isset($_SERVER['HTTP_USER_AGENT'])) { $headers['user-agent'] = sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])); } if (isset($_SERVER['HTTP_REFERER'])) { $referrer = airefs_tracking_safe_referrer(wp_unslash($_SERVER['HTTP_REFERER'])); if ($referrer !== null) { $headers['referer'] = $referrer; } } // Use the server's peer address; arbitrary proxy headers are neither trusted nor retained. if (isset($_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP)) { $headers['x-real-ip'] = $_SERVER['REMOTE_ADDR']; } return $headers; } /** * Get or create session ID * * @return string */ public static function get_or_create_session_id() { // Check if we already have a session ID in cookie $session_id = isset($_COOKIE['airefs_session_id']) ? sanitize_text_field(wp_unslash($_COOKIE['airefs_session_id'])) : ''; if (empty($session_id) || !self::is_valid_uuid($session_id)) { // Generate new UUID v4 $session_id = self::generate_uuid_v4(); // Set cookie for 30 days if (!headers_sent()) { setcookie('airefs_session_id', $session_id, array( 'expires' => time() + 30 * DAY_IN_SECONDS, 'path' => '/', 'secure' => is_ssl(), 'httponly' => true, 'samesite' => 'Lax', )); } $_COOKIE['airefs_session_id'] = $session_id; } return $session_id; } /** * Generate UUID v4 * * @return string */ private static function generate_uuid_v4() { // Generate 16 random bytes $data = random_bytes(16); // Set version to 0100 $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // Set bits 6-7 to 10 $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // Output the 36 character UUID return sprintf( '%08s-%04s-%04s-%04s-%12s', bin2hex(substr($data, 0, 4)), bin2hex(substr($data, 4, 2)), bin2hex(substr($data, 6, 2)), bin2hex(substr($data, 8, 2)), bin2hex(substr($data, 10, 6)) ); } /** * Validate UUID format * * @param string $uuid * @return bool */ private static function is_valid_uuid($uuid) { return preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $uuid) === 1; } /** * Queue a server tracking event * * @param array $tracking_data */ public static function send_tracking_event($tracking_data) { return AirefsTrackingQueue::enqueue(array( 'name' => 'pageview', 'source' => 'site', 'url' => $tracking_data['url'], 'headers' => isset($tracking_data['headers']) ? $tracking_data['headers'] : array(), 'session_id' => isset($tracking_data['session_id']) ? $tracking_data['session_id'] : '', 'timestamp' => isset($tracking_data['timestamp']) ? $tracking_data['timestamp'] : current_time('c', true), 'event_client' => AIREFS_TRACKING_UA, )); } /** * Queue a browser tracking event * * @param array $tracking_data */ public static function send_client_analytics_event($tracking_data) { $payload = $tracking_data['client_data']; $payload['name'] = 'pageview'; $payload['source'] = 'client'; $payload['url'] = $tracking_data['url']; $payload['headers'] = $tracking_data['headers']; $payload['session_id'] = $tracking_data['session_id']; $payload['timestamp'] = $tracking_data['timestamp']; $payload['event_client'] = AIREFS_TRACKING_UA; return AirefsTrackingQueue::enqueue($payload); } /** * Manual tracking trigger (for testing) * * @param string $url * @return bool */ public static function track_url_manually($url) { $options = get_option(AIREFS_TRACKING_OPTIONS_NAME, array()); if (empty($options['access_token'])) { return false; } $tracking_data = array( 'url' => $url, 'headers' => self::get_request_headers(), 'session_id' => self::get_or_create_session_id(), 'timestamp' => current_time('c', true), ); return self::send_tracking_event($tracking_data); } } /** * AJAX handler for manual tracking test */ function airefs_manual_tracking_test() { // Verify nonce if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'airefs_manual_tracking_test')) { wp_die('Security check failed'); } // Check user capabilities if (!current_user_can('manage_options')) { wp_die('Insufficient permissions'); } $test_url = isset($_POST['test_url']) && is_string($_POST['test_url']) ? wp_unslash($_POST['test_url']) : home_url(); $result = AirefsTrackingCore::track_url_manually($test_url); if ($result === true) { wp_send_json_success(array( // translators: %s is the URL queued for tracking 'message' => sprintf(__('Test event queued for URL: %s. Delivery has not yet been confirmed.', 'airefs-ai-search-analytics-tracking'), $test_url) )); } else { wp_send_json_error(array( 'message' => is_wp_error($result) ? $result->get_error_message() : __('Test tracking could not be queued. Please check your settings.', 'airefs-ai-search-analytics-tracking') )); } } // Initialize tracking functionality AirefsTrackingCore::init(); // Register AJAX handlers add_action('wp_ajax_airefs_manual_tracking_test', 'airefs_manual_tracking_test'); /** * Register REST API endpoints */ function airefs_register_rest_endpoints() { $route = array( 'methods' => 'POST', 'callback' => 'airefs_handle_analytics_event', 'permission_callback' => '__return_true', 'args' => array( 'name' => array( 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'enum' => array('pageview'), ), 'source' => array( 'required' => false, 'type' => 'string', 'default' => 'client', 'enum' => array('client'), 'sanitize_callback' => 'sanitize_text_field', ), 'url' => array( 'required' => true, 'type' => 'string', ), 'page_title' => array( 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'referrer' => array( 'required' => false, 'type' => array('string', 'null'), 'sanitize_callback' => function ($value) { return airefs_tracking_safe_referrer($value); }, ), 'screen_width' => array( 'required' => true, 'type' => 'integer', 'minimum' => 0, 'maximum' => 10000, 'sanitize_callback' => 'absint', ), 'screen_height' => array( 'required' => true, 'type' => 'integer', 'minimum' => 0, 'maximum' => 10000, 'sanitize_callback' => 'absint', ), 'user_agent' => array( 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'language' => array( 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'timezone_offset' => array( 'required' => true, 'type' => 'integer', ), 'session_id' => array( 'required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'timestamp' => array( 'required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'device_type' => array( 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'os' => array( 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'browser' => array( 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'browser_version' => array( 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'event_client' => array( 'required' => false, 'type' => 'string', 'default' => AIREFS_TRACKING_UA, 'sanitize_callback' => 'sanitize_text_field', ), ), ); foreach ($route['args'] as &$argument) { $argument['validate_callback'] = 'rest_validate_request_arg'; } unset($argument); register_rest_route('airefs/v1', '/events', $route); } /** * Handle analytics event from REST API */ function airefs_handle_analytics_event($request) { // Check if tracking is enabled $options = get_option(AIREFS_TRACKING_OPTIONS_NAME, array()); if (empty($options['tracking_enabled'])) { return new WP_Error('tracking_disabled', 'Tracking is disabled', array('status' => 403)); } if (empty($options['access_token'])) { return new WP_Error('no_access_token', 'No access token configured', array('status' => 500)); } // Get parameters $params = $request->get_params(); // Validate required parameters if (empty($params['name']) || empty($params['url'])) { return new WP_Error('missing_required_params', 'Missing required parameters: name and url are required', array('status' => 400)); } // Validate URL format if (!filter_var($params['url'], FILTER_VALIDATE_URL)) { return new WP_Error('invalid_url', 'Invalid URL format', array('status' => 400)); } if (airefs_tracking_origin($params['url']) !== airefs_tracking_origin(home_url('/'))) { return new WP_Error('invalid_site_url', 'Event URL must belong to this site.', array('status' => 403)); } if ($params['name'] !== 'pageview' || $params['source'] !== 'client') { return new WP_Error('invalid_event_type', 'Only client pageviews are accepted.', array('status' => 400)); } // Validate session ID if provided if (!empty($params['session_id']) && !preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $params['session_id'])) { return new WP_Error('invalid_session_id', 'Invalid session ID format', array('status' => 400)); } // Validate numeric fields if (isset($params['screen_width']) && (!is_numeric($params['screen_width']) || $params['screen_width'] < 0 || $params['screen_width'] > 10000)) { return new WP_Error('invalid_screen_width', 'Invalid screen width value', array('status' => 400)); } if (isset($params['screen_height']) && (!is_numeric($params['screen_height']) || $params['screen_height'] < 0 || $params['screen_height'] > 10000)) { return new WP_Error('invalid_screen_height', 'Invalid screen height value', array('status' => 400)); } if (isset($params['timezone_offset']) && (!is_numeric($params['timezone_offset']) || $params['timezone_offset'] < -840 || $params['timezone_offset'] > 720)) { return new WP_Error('invalid_timezone_offset', 'Invalid timezone offset value', array('status' => 400)); } // Validate referrer URL if provided if (!empty($params['referrer']) && !filter_var($params['referrer'], FILTER_VALIDATE_URL)) { return new WP_Error('invalid_referrer', 'Invalid referrer URL format', array('status' => 400)); } if (isset($params['timestamp']) && !airefs_tracking_valid_timestamp($params['timestamp'])) { return new WP_Error('invalid_timestamp', 'Timestamp must be a valid ISO 8601 date and time.', array('status' => 400)); } $client_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; if (!airefs_tracking_allow_request($client_ip)) { return new WP_Error('rate_limit_exceeded', 'Too many requests', array('status' => 429)); } // Prepare tracking data for Airefs API $tracking_data = array( 'url' => $params['url'], 'headers' => array( 'user-agent' => isset($params['user_agent']) ? $params['user_agent'] : '', 'referer' => isset($params['referrer']) ? $params['referrer'] : '', ), 'session_id' => AirefsTrackingCore::get_or_create_session_id(), 'timestamp' => isset($params['timestamp']) ? $params['timestamp'] : current_time('c', true), 'client_data' => array( 'name' => 'pageview', 'source' => 'client', 'page_title' => isset($params['page_title']) ? $params['page_title'] : '', 'screen_width' => isset($params['screen_width']) ? (int) $params['screen_width'] : null, 'screen_height' => isset($params['screen_height']) ? (int) $params['screen_height'] : null, 'language' => isset($params['language']) ? $params['language'] : '', 'timezone_offset' => isset($params['timezone_offset']) ? (int) $params['timezone_offset'] : null, 'device_type' => isset($params['device_type']) ? $params['device_type'] : '', 'os' => isset($params['os']) ? $params['os'] : '', 'browser' => isset($params['browser']) ? $params['browser'] : '', 'browser_version' => isset($params['browser_version']) ? $params['browser_version'] : '', 'user_agent' => isset($params['user_agent']) ? $params['user_agent'] : '', 'referrer' => isset($params['referrer']) ? $params['referrer'] : null, 'event_client' => AIREFS_TRACKING_UA, ), ); // Queue delivery; this request never contacts the analytics service. $result = AirefsTrackingCore::send_client_analytics_event($tracking_data); if (is_wp_error($result)) { return $result; } return new WP_REST_Response(array( 'success' => true, 'queued' => true, 'message' => 'Event queued; delivery is pending.', ), 202); } // Register REST API endpoint add_action('rest_api_init', 'airefs_register_rest_endpoints');