settings_manager = $settings_manager ?? new Settings_Manager(); $this->cache_duration = (int) $this->get_setting('cache_duration', 3600); // Clear any existing cached insights to ensure new logic takes effect $this->clear_insights_cache(); } /** * Initialize Analytics Manager * Following ThinkRank init patterns * * @return void */ public function init(): void { // Initialize Google API clients add_action('init', [$this, 'initialize_clients']); // Schedule cache cleanup add_action('thinkrank_daily_cleanup', [$this, 'cleanup_cache']); } /** * Initialize Google API clients * Following AI_Manager client initialization pattern * * @return void */ public function initialize_clients(): void { try { // Initialize Google Analytics client $ga_api_key = $this->get_setting('google_analytics_api_key'); $ga_property_id = $this->get_setting('google_analytics_property_id'); if (!empty($ga_api_key) && !empty($ga_property_id)) { $timeout = (int) $this->get_setting('api_timeout', 30); $this->analytics_client = new Google_Analytics_Client($ga_api_key, $ga_property_id, $timeout); } // Initialize Search Console client $gsc_api_key = $this->get_setting('google_search_console_api_key'); if (!empty($gsc_api_key)) { $timeout = (int) $this->get_setting('api_timeout', 30); $this->search_console_client = new Google_Search_Console_Client($gsc_api_key, $timeout); } // Initialize PageSpeed client $ps_api_key = $this->get_setting('google_pagespeed_api_key'); if (!empty($ps_api_key)) { $timeout = (int) $this->get_setting('api_timeout', 30); $this->pagespeed_client = new Google_PageSpeed_Client($ps_api_key, $timeout); } } catch (\Exception $e) { // Client initialization failed, will be handled later } } /** * Test all Google API connections * Following ThinkRank test_connection patterns * * @return array Connection test results */ public function test_connections(): array { $results = [ 'google_analytics' => ['status' => 'not_configured'], 'search_console' => ['status' => 'not_configured'], 'pagespeed' => ['status' => 'not_configured'] ]; // Test Google Analytics connection if ($this->analytics_client) { try { $test_result = $this->analytics_client->test_connection(); $results['google_analytics'] = [ 'status' => $test_result['success'] ? 'connected' : 'error', 'message' => $test_result['message'], 'details' => $test_result ]; } catch (\Exception $e) { $results['google_analytics'] = [ 'status' => 'error', 'message' => $e->getMessage() ]; } } // Test Search Console connection if ($this->search_console_client) { try { $test_result = $this->search_console_client->test_connection(); $results['search_console'] = [ 'status' => $test_result['success'] ? 'connected' : 'error', 'message' => $test_result['message'], 'details' => $test_result ]; } catch (\Exception $e) { $results['search_console'] = [ 'status' => 'error', 'message' => $e->getMessage() ]; } } // Test PageSpeed connection if ($this->pagespeed_client) { try { $test_result = $this->pagespeed_client->test_connection(); $results['pagespeed'] = [ 'status' => $test_result['success'] ? 'connected' : 'error', 'message' => $test_result['message'], 'details' => $test_result ]; } catch (\Exception $e) { $results['pagespeed'] = [ 'status' => 'error', 'message' => $e->getMessage() ]; } } return $results; } /** * Get analytics dashboard data * Combines data from all Google APIs with caching * * @param string $date_range Date range for data * @return array Dashboard data */ public function get_dashboard_data(string $date_range = '30d'): array { $cache_key = "analytics_dashboard_{$date_range}"; $cached_data = get_transient($cache_key); if ($cached_data !== false) { return $cached_data; } $dashboard_data = [ 'traffic' => [], 'search_performance' => [], 'core_web_vitals' => [], 'last_updated' => current_time('mysql'), 'date_range' => $date_range ]; try { // Get Google Analytics traffic data if ($this->analytics_client) { $dashboard_data['traffic'] = $this->analytics_client->get_traffic_data($date_range); $dashboard_data['organic_traffic'] = $this->analytics_client->get_organic_traffic($date_range); $dashboard_data['top_pages'] = $this->analytics_client->get_top_pages(10, $date_range); } // Get Search Console data if ($this->search_console_client) { $site_url = $this->get_setting('search_console_property', get_site_url()); $dashboard_data['search_performance'] = $this->search_console_client->get_search_performance($site_url, $date_range); $dashboard_data['top_queries'] = $this->search_console_client->get_top_queries($site_url, 10); $dashboard_data['page_performance'] = $this->search_console_client->get_page_performance($site_url, $date_range, 10); } // Get Core Web Vitals data if ($this->pagespeed_client) { $site_url = get_site_url(); $dashboard_data['core_web_vitals'] = $this->pagespeed_client->get_core_web_vitals($site_url); } } catch (\Exception $e) { $dashboard_data['error'] = $e->getMessage(); } // Cache the results set_transient($cache_key, $dashboard_data, $this->cache_duration); return $dashboard_data; } /** * Get SEO opportunities using Search Console data * * @param string $date_range Date range for analysis * @return array SEO opportunities */ public function get_seo_opportunities(string $date_range = '30d'): array { $cache_key = "seo_opportunities_{$date_range}"; $cached_data = get_transient($cache_key); if ($cached_data !== false) { return $cached_data; } $opportunities = [ 'keyword_opportunities' => [], 'page_opportunities' => [], 'device_insights' => [], 'last_updated' => current_time('mysql') ]; try { if ($this->search_console_client) { $site_url = $this->get_setting('search_console_property', get_site_url()); // Get keyword opportunities $opportunities['keyword_opportunities'] = $this->search_console_client->get_keyword_opportunities($site_url, $date_range); // Get device performance insights $opportunities['device_insights'] = $this->search_console_client->get_device_performance($site_url, $date_range); // Get search appearance data $opportunities['search_appearance'] = $this->search_console_client->get_search_appearance($site_url, $date_range); } } catch (\Exception $e) { $opportunities['error'] = $e->getMessage(); } // Cache the results set_transient($cache_key, $opportunities, $this->cache_duration); return $opportunities; } /** * Get setting value from integrations category * Following ThinkRank settings patterns * * @param string $key Setting key * @param mixed $default Default value * @return mixed Setting value */ private function get_setting(string $key, $default = '') { $integrations_settings = $this->settings_manager->get_settings('integrations'); return $integrations_settings[$key] ?? $default; } /** * One-click setup for Google Search Console verification * Following ThinkRank setup patterns * * @param string $site_url Site URL to verify * @return array Setup results */ public function setup_search_console_verification(string $site_url): array { try { if (!$this->search_console_client) { return [ 'success' => false, 'message' => 'Search Console API key not configured' ]; } $verification_result = $this->search_console_client->verify_site($site_url); if ($verification_result['success']) { // Update settings with verified site URL $this->settings_manager->update_setting('integrations', 'search_console_property', $site_url); } return $verification_result; } catch (\Exception $e) { return [ 'success' => false, 'message' => $e->getMessage() ]; } } /** * Get site indexing status * * @return array Indexing status data */ public function get_indexing_status(): array { $cache_key = 'indexing_status'; $cached_data = get_transient($cache_key); if ($cached_data !== false) { return $cached_data; } $indexing_data = [ 'status' => 'unknown', 'last_updated' => current_time('mysql') ]; try { if ($this->search_console_client) { $site_url = $this->get_setting('search_console_property', get_site_url()); $indexing_data = $this->search_console_client->get_indexing_status($site_url); } } catch (\Exception $e) { $indexing_data['error'] = $e->getMessage(); } // Cache for 1 hour set_transient($cache_key, $indexing_data, 3600); return $indexing_data; } /** * Force refresh of all cached data * * @return array Refresh results */ public function refresh_data(): array { // Clear all analytics-related transients $cache_keys = [ 'analytics_dashboard_7d', 'analytics_dashboard_30d', 'analytics_dashboard_90d', 'seo_opportunities_7d', 'seo_opportunities_30d', 'seo_opportunities_90d', 'seo_insights_7d', 'seo_insights_30d', 'seo_insights_90d', 'indexing_status' ]; $cleared = 0; foreach ($cache_keys as $key) { if (delete_transient($key)) { $cleared++; } } return [ 'success' => true, 'message' => "Cleared {$cleared} cached data entries", 'cleared_count' => $cleared, 'timestamp' => current_time('mysql') ]; } /** * Clear insights cache specifically * * @return void */ private function clear_insights_cache(): void { $insight_cache_keys = [ 'seo_insights_7d', 'seo_insights_30d', 'seo_insights_90d' ]; foreach ($insight_cache_keys as $key) { delete_transient($key); } } /** * Get client status for debugging * * @return array Client status information */ public function get_client_status(): array { return [ 'google_analytics' => [ 'initialized' => !is_null($this->analytics_client), 'api_key_configured' => !empty($this->get_setting('google_analytics_api_key')), 'property_id_configured' => !empty($this->get_setting('google_analytics_property_id')) ], 'search_console' => [ 'initialized' => !is_null($this->search_console_client), 'api_key_configured' => !empty($this->get_setting('google_search_console_api_key')), 'site_url_configured' => !empty($this->get_setting('search_console_property')) ], 'pagespeed' => [ 'initialized' => !is_null($this->pagespeed_client), 'api_key_configured' => !empty($this->get_setting('google_pagespeed_api_key')) ], 'cache_duration' => $this->cache_duration, 'last_checked' => current_time('mysql') ]; } /** * Cleanup expired cache data * Following ThinkRank cache cleanup patterns * * @return void */ public function cleanup_cache(): void { // WordPress handles transient cleanup automatically // This method is for future custom cache cleanup if needed } // ======================================== // SEO Intelligence Enhancement Methods // ======================================== /** * Get intelligent dashboard data with trends and insights * * @param string $date_range Date range for analysis * @return array Enhanced dashboard data with intelligence */ public function get_intelligent_dashboard_data(string $date_range = '30d'): array { // Get base dashboard data $dashboard_data = $this->get_dashboard_data($date_range); // Check if there's an error in the data if (isset($dashboard_data['error'])) { return [ 'success' => false, 'data' => null, 'message' => 'Failed to retrieve dashboard data: ' . $dashboard_data['error'], 'timestamp' => current_time('mysql') ]; } // Check if we have real data available if (!$this->has_real_data($dashboard_data)) { return [ 'success' => false, 'data' => null, 'message' => 'No analytics data available yet. Please ensure your Google Analytics and Search Console are properly configured and have collected data.', 'timestamp' => current_time('mysql') ]; } // Initialize intelligence classes $trend_analyzer = new SEO_Trend_Analyzer(); $scoring_engine = new SEO_Scoring_Engine(); $insight_generator = new SEO_Insight_Generator(); $data = $dashboard_data; // Generate trend analysis $current_data = $data; $historical_data = $this->get_historical_data($date_range); $trends = [ 'traffic_trends' => $trend_analyzer->analyze_traffic_trends($current_data, $historical_data), 'keyword_trends' => $trend_analyzer->analyze_keyword_trends($data['search_performance'] ?? [], $date_range), 'content_trends' => $trend_analyzer->analyze_content_trends($data, $data['search_performance'] ?? []) ]; // Calculate SEO health score $seo_health = $scoring_engine->calculate_seo_health_score($data, $data['search_performance'] ?? []); // Generate insights $insights = [ 'traffic_insights' => $insight_generator->generate_traffic_insights($trends['traffic_trends']), 'keyword_insights' => $insight_generator->generate_keyword_insights($trends['keyword_trends']), 'content_insights' => $insight_generator->generate_content_insights($trends['content_trends']) ]; // Combine all intelligence data $enhanced_data = array_merge($data, [ 'intelligence' => [ 'trends' => $trends, 'seo_health_score' => $seo_health, 'insights' => $insights, 'last_analyzed' => current_time('mysql') ] ]); return [ 'success' => true, 'data' => $enhanced_data, 'message' => 'Intelligent dashboard data retrieved successfully' ]; } /** * Get intelligent SEO opportunities with prioritization * * @param string $date_range Date range for analysis * @return array Enhanced opportunities with intelligence */ public function get_intelligent_seo_opportunities(string $date_range = '30d'): array { // Get base opportunities data $opportunities_data = $this->get_seo_opportunities($date_range); // Check if there's an error in the data if (isset($opportunities_data['error'])) { return [ 'success' => false, 'data' => null, 'message' => 'Failed to retrieve opportunities data: ' . $opportunities_data['error'], 'timestamp' => current_time('mysql') ]; } // Check if we have real search console data for opportunities $search_performance = $opportunities_data['search_performance'] ?? []; $has_search_data = !empty($search_performance['rows']) || ($search_performance['total_clicks'] ?? 0) > 0 || ($search_performance['total_impressions'] ?? 0) > 0; if (!$has_search_data) { return [ 'success' => false, 'data' => null, 'message' => 'No Search Console data available yet. Please ensure your Search Console is properly configured and has collected data.', 'timestamp' => current_time('mysql') ]; } // Initialize intelligence classes $opportunity_detector = new SEO_Opportunity_Detector(); $scoring_engine = new SEO_Scoring_Engine(); $data = $opportunities_data; // Detect intelligent opportunities $search_console_data = $data['search_performance'] ?? []; $analytics_data = $data; $intelligent_opportunities = [ 'quick_wins' => $opportunity_detector->detect_quick_wins($search_console_data, $analytics_data), 'content_opportunities' => $opportunity_detector->identify_content_opportunities($search_console_data, $analytics_data), 'keyword_opportunities' => $scoring_engine->score_keyword_opportunities($search_console_data) ]; // Prioritize all opportunities $all_opportunities = array_merge( $intelligent_opportunities['quick_wins']['opportunities'] ?? [], $intelligent_opportunities['content_opportunities']['opportunities'] ?? [], $intelligent_opportunities['keyword_opportunities']['opportunities'] ?? [] ); $prioritized = $opportunity_detector->prioritize_opportunities($all_opportunities); $impact_matrix = $opportunity_detector->calculate_impact_effort_matrix($all_opportunities); // Enhance original data with intelligence $enhanced_data = array_merge($data, [ 'intelligent_opportunities' => $intelligent_opportunities, 'prioritized_opportunities' => $prioritized, 'impact_effort_matrix' => $impact_matrix, 'opportunity_summary' => $this->generate_opportunity_summary($intelligent_opportunities), 'last_analyzed' => current_time('mysql') ]); return [ 'success' => true, 'data' => $enhanced_data, 'message' => 'Intelligent SEO opportunities retrieved successfully' ]; } /** * Get SEO performance insights * * @param string $date_range Date range for analysis * @return array SEO insights data */ public function get_seo_insights(string $date_range = '30d'): array { $cache_key = "seo_insights_{$date_range}"; $cached_data = get_transient($cache_key); if ($cached_data !== false) { return [ 'success' => true, 'data' => $cached_data, 'cached' => true, 'message' => 'SEO insights retrieved from cache' ]; } try { // Get dashboard data for analysis $dashboard_result = $this->get_intelligent_dashboard_data($date_range); if (!$dashboard_result['success']) { return $dashboard_result; } $dashboard_data = $dashboard_result['data']; $intelligence = $dashboard_data['intelligence'] ?? []; // Initialize insight generator $insight_generator = new SEO_Insight_Generator(); // Collect all insights $all_insights = []; if (!empty($intelligence['insights']['traffic_insights']['insights'])) { $all_insights = array_merge($all_insights, $intelligence['insights']['traffic_insights']['insights']); } if (!empty($intelligence['insights']['keyword_insights']['insights'])) { $all_insights = array_merge($all_insights, $intelligence['insights']['keyword_insights']['insights']); } if (!empty($intelligence['insights']['content_insights']['insights'])) { $all_insights = array_merge($all_insights, $intelligence['insights']['content_insights']['insights']); } // Format and prioritize insights $formatted_insights = $insight_generator->format_insights_for_display($all_insights); $prioritized_insights = $insight_generator->prioritize_insights_by_impact($formatted_insights); $insights_data = [ 'insights' => $prioritized_insights['prioritized_insights'], 'summary' => [ 'total_insights' => count($formatted_insights), 'high_impact_count' => $prioritized_insights['high_impact_count'], 'action_required_count' => $prioritized_insights['action_required_count'] ], 'seo_health_score' => $intelligence['seo_health_score'] ?? null, 'generated_at' => current_time('mysql') ]; // Cache the results set_transient($cache_key, $insights_data, $this->cache_duration); return [ 'success' => true, 'data' => $insights_data, 'cached' => false, 'message' => 'SEO insights generated successfully' ]; } catch (Exception $e) { return [ 'success' => false, 'error' => 'Failed to generate SEO insights: ' . $e->getMessage(), 'data' => null ]; } } /** * Check if real analytics data is available * * @param array $dashboard_data Dashboard data to check * @return bool True if real data is available */ private function has_real_data(array $dashboard_data): bool { // Check if we have meaningful traffic data $traffic = $dashboard_data['traffic'] ?? []; $search_performance = $dashboard_data['search_performance'] ?? []; $has_traffic = !empty($traffic) && ( ($traffic['sessions'] ?? 0) > 0 || ($traffic['pageviews'] ?? 0) > 0 || ($traffic['active_users'] ?? 0) > 0 ); $has_search_data = !empty($search_performance) && ( !empty($search_performance['rows']) || ($search_performance['total_clicks'] ?? 0) > 0 || ($search_performance['total_impressions'] ?? 0) > 0 ); return $has_traffic || $has_search_data; } /** * Get historical data for trend comparison * * @param string $current_range Current date range * @return array Historical data */ private function get_historical_data(string $current_range): array { // Calculate previous period based on current range $previous_range = $this->calculate_previous_period($current_range); // Try to get actual historical data from previous period $historical_data = $this->get_dashboard_data($previous_range); // Return the actual historical data (may be empty if no real data available) return [ 'sessions' => $historical_data['traffic']['sessions'] ?? 0, 'pageviews' => $historical_data['traffic']['pageviews'] ?? 0, 'organic_traffic' => $historical_data['organic_traffic'] ?? ['organic_traffic' => ['sessions' => 0]], 'bounce_rate' => $historical_data['traffic']['bounce_rate'] ?? 0, 'avg_session_duration' => $historical_data['traffic']['avg_session_duration'] ?? 0 ]; } /** * Calculate previous period for comparison * * @param string $current_range Current range * @return string Previous period range */ private function calculate_previous_period(string $current_range): string { // Simple mapping for now - could be enhanced with actual date calculations $period_mapping = [ '7d' => '14d', '30d' => '60d', '90d' => '180d' ]; return $period_mapping[$current_range] ?? '60d'; } /** * Generate opportunity summary * * @param array $opportunities All opportunities * @return array Opportunity summary */ private function generate_opportunity_summary(array $opportunities): array { $quick_wins_count = count($opportunities['quick_wins']['opportunities'] ?? []); $content_opportunities_count = count($opportunities['content_opportunities']['opportunities'] ?? []); $keyword_opportunities_count = count($opportunities['keyword_opportunities']['opportunities'] ?? []); $total_opportunities = $quick_wins_count + $content_opportunities_count + $keyword_opportunities_count; $potential_clicks = 0; if (!empty($opportunities['quick_wins']['potential_additional_clicks'])) { $potential_clicks = $opportunities['quick_wins']['potential_additional_clicks']; } return [ 'total_opportunities' => $total_opportunities, 'quick_wins_count' => $quick_wins_count, 'content_opportunities_count' => $content_opportunities_count, 'keyword_opportunities_count' => $keyword_opportunities_count, 'potential_additional_clicks' => $potential_clicks, 'priority_recommendation' => $quick_wins_count > 0 ? 'Focus on quick wins first for immediate impact' : 'Focus on content optimization for long-term growth' ]; } /** * Clear intelligence cache * * @return array Clear result */ public function clear_intelligence_cache(): array { $intelligence_cache_keys = [ 'seo_insights_7d', 'seo_insights_30d', 'seo_insights_90d', 'intelligent_dashboard_7d', 'intelligent_dashboard_30d', 'intelligent_dashboard_90d', 'intelligent_opportunities_7d', 'intelligent_opportunities_30d', 'intelligent_opportunities_90d' ]; $cleared = 0; foreach ($intelligence_cache_keys as $key) { if (delete_transient($key)) { $cleared++; } } return [ 'success' => true, 'message' => "Cleared {$cleared} intelligence cache entries", 'cleared_count' => $cleared, 'timestamp' => current_time('mysql') ]; } }