PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.12.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.12.0
2.12.0 2.11.0 2.10.0 2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk All 53 releases
← All changes | includes/seo/class-analytics-manager.php +55 -498 2.5.0 → 2.12.0 View file →
@@ -16,9 +16,8 @@
16 16
17 17 namespace ThinkRank\SEO;
18 18
19 19 use ThinkRank\Core\Settings_Manager;
20 -use ThinkRank\Core\Plan_Config;
21 20 use ThinkRank\Integrations\Google_Analytics_Client;
22 21 use ThinkRank\Integrations\Google_Search_Console_Client;
23 22 use ThinkRank\Integrations\Google_PageSpeed_Client;
24 23 use ThinkRank\Integrations\Google_Search_Analytics_Client;
@@ -602,66 +601,72 @@
602 601 *
603 602 * @return array Connection test results
604 603 */
605 604 public function test_connections(): array {
605 + // Every entry carries a `message`, configured or not: the result is
606 + // handed to the Integrations screen as JSON, and a key that exists on
607 + // some services and not others reads as `undefined` there.
606 608 $results = [
607 - 'google_analytics' => ['status' => 'not_configured'],
608 - 'search_console' => ['status' => 'not_configured'],
609 - 'pagespeed' => ['status' => 'not_configured']
609 + 'google_analytics' => ['status' => 'not_configured', 'message' => ''],
610 + 'search_console' => ['status' => 'not_configured', 'message' => ''],
611 + 'pagespeed' => ['status' => 'not_configured', 'message' => '']
610 612 ];
611 613
612 - // Test Google Analytics connection
613 - if ($this->analytics_client) {
614 - try {
615 - $test_result = $this->analytics_client->test_connection();
616 - $results['google_analytics'] = [
617 - 'status' => $test_result['success'] ? 'connected' : 'error',
618 - 'message' => $test_result['message'],
619 - 'details' => $test_result
620 - ];
621 - } catch (\Exception $e) {
622 - $results['google_analytics'] = [
623 - 'status' => 'error',
624 - 'message' => $e->getMessage()
625 - ];
614 + // One shape for all three. They were three copies of the same block
615 + // differing only in the client, which is how the same unguarded read
616 + // came to exist in triplicate (#852).
617 + $clients = [
618 + 'google_analytics' => $this->analytics_client,
619 + 'search_console' => $this->search_console_client,
620 + 'pagespeed' => $this->pagespeed_client,
621 + ];
622 +
623 + foreach ($clients as $service => $client) {
624 + if (!$client) {
625 + continue;
626 626 }
627 +
628 + $results[$service] = $this->describe_connection_test($client);
627 629 }
628 630
629 - // Test Search Console connection
630 - if ($this->search_console_client) {
631 - try {
632 - $test_result = $this->search_console_client->test_connection();
633 - $results['search_console'] = [
634 - 'status' => $test_result['success'] ? 'connected' : 'error',
635 - 'message' => $test_result['message'],
636 - 'details' => $test_result
637 - ];
638 - } catch (\Exception $e) {
639 - $results['search_console'] = [
640 - 'status' => 'error',
641 - 'message' => $e->getMessage()
642 - ];
631 + return $results;
632 + }
633 +
634 + /**
635 + * Run one client's connection test and report it in a fixed shape.
636 + *
637 + * The clients answer success with `message` and failure with `error`, and
638 + * this read `$test_result['message']` unconditionally — so a failed test
639 + * raised "Undefined array key" and handed the admin an error with
640 + * `message => null`. The reason the client had in hand was the one thing
641 + * the screen needed. The clients now set `message` on both branches; the
642 + * fallbacks here cover a client that does not, including one that answers
643 + * with neither key.
644 + *
645 + * @since 2.12.0
646 + *
647 + * @param object $client A client exposing test_connection(): array.
648 + * @return array{status:string, message:string, details?:array}
649 + */
650 + private function describe_connection_test($client): array {
651 + try {
652 + $test_result = $client->test_connection();
653 +
654 + if (!is_array($test_result)) {
655 + return ['status' => 'error', 'message' => ''];
643 656 }
644 - }
645 657
646 - // Test PageSpeed connection
647 - if ($this->pagespeed_client) {
648 - try {
649 - $test_result = $this->pagespeed_client->test_connection();
650 - $results['pagespeed'] = [
651 - 'status' => $test_result['success'] ? 'connected' : 'error',
652 - 'message' => $test_result['message'],
653 - 'details' => $test_result
654 - ];
655 - } catch (\Exception $e) {
656 - $results['pagespeed'] = [
657 - 'status' => 'error',
658 - 'message' => $e->getMessage()
659 - ];
660 - }
658 + return [
659 + 'status' => !empty($test_result['success']) ? 'connected' : 'error',
660 + 'message' => (string) ($test_result['message'] ?? $test_result['error'] ?? ''),
661 + 'details' => $test_result,
662 + ];
663 + } catch (\Exception $e) {
664 + return [
665 + 'status' => 'error',
666 + 'message' => $e->getMessage(),
667 + ];
661 668 }
662 -
663 - return $results;
664 669 }
665 670
666 671 /**
667 672 * Get analytics dashboard data
@@ -711,10 +716,8 @@
711 716 // 401s are re-thrown so the token-refresh retry below runs.
712 717 if ($this->analytics_client) {
713 718 try {
714 719 $dashboard_data['traffic'] = $this->analytics_client->get_traffic_data($date_range);
715 - $dashboard_data['organic_traffic'] = $this->analytics_client->get_organic_traffic($date_range);
716 - $dashboard_data['top_pages'] = $this->analytics_client->get_top_pages(10, $date_range);
717 720 } catch (\Exception $ga_error) {
718 721 if ($ga_error->getCode() === 401) {
719 722 throw $ga_error;
720 723 }
@@ -772,10 +775,8 @@
772 775 $dashboard_data['search_performance'] = array_merge($search_performance, [
773 776 'totals' => $totals,
774 777 'position_distribution' => $position_distribution
775 778 ]);
776 -
777 - $dashboard_data['page_performance'] = $this->search_console_client->get_page_performance($site_url, $date_range, 10);
778 779 } // Closing Search Console block
779 780
780 781 // If successful, break loop
781 782 break;
@@ -973,44 +974,8 @@
973 974 }
974 975 }
975 976
976 977 /**
977 - * Get site indexing status
978 - *
979 - * @return array Indexing status data
980 - */
981 - public function get_indexing_status(): array {
982 - $cache_key = 'indexing_status';
983 - $cached_data = get_transient($cache_key);
984 -
985 - if ($cached_data !== false) {
986 - return $cached_data;
987 - }
988 -
989 - $indexing_data = [
990 - 'status' => 'unknown',
991 - 'last_updated' => current_time('mysql')
992 - ];
993 -
994 - try {
995 - if ($this->search_console_client) {
996 - $site_url = $this->get_setting('search_console_property', get_site_url());
997 - $indexing_data = $this->search_console_client->get_indexing_status($site_url);
998 - }
999 - } catch (\Exception $e) {
1000 - $indexing_data['error'] = $e->getMessage();
1001 - }
1002 -
1003 - // Cache successful results for 1 hour. Errors are never cached, so a
1004 - // transient Google failure isn't served as "no data" for a full hour.
1005 - if (empty($indexing_data['error'])) {
1006 - set_transient($cache_key, $indexing_data, 3600);
1007 - }
1008 -
1009 - return $indexing_data;
1010 - }
1011 -
1012 - /**
1013 978 * Force refresh of all cached data
1014 979 *
1015 980 * @return array Refresh results
1016 981 */
@@ -1027,11 +992,8 @@
1027 992 'analytics_dashboard_v5_180d',
1028 993 'seo_opportunities_7d',
1029 994 'seo_opportunities_30d',
1030 995 'seo_opportunities_90d',
1031 - 'seo_insights_7d',
1032 - 'seo_insights_30d',
1033 - 'seo_insights_90d',
1034 996 'indexing_status',
1035 997 'thinkrank_dashboard_cwv'
1036 998 ];
1037 999
@@ -1099,411 +1061,6 @@
1099 1061 */
1100 1062 public function cleanup_cache(): void {
1101 1063 // WordPress handles transient cleanup automatically
1102 1064 // This method is for future custom cache cleanup if needed
1103 - }
1104 -
1105 - // ========================================
1106 - // SEO Intelligence Enhancement Methods
1107 - // ========================================
1108 -
1109 - /**
1110 - * Get intelligent dashboard data with trends and insights
1111 - *
1112 - * @param string $date_range Date range for analysis
1113 - * @return array Enhanced dashboard data with intelligence
1114 - */
1115 - public function get_intelligent_dashboard_data(string $date_range = '30d'): array {
1116 - // Get base dashboard data
1117 - $dashboard_data = $this->get_dashboard_data($date_range);
1118 -
1119 - // Check if there's an error in the data
1120 - if (isset($dashboard_data['error'])) {
1121 - return [
1122 - 'success' => false,
1123 - 'data' => null,
1124 - 'message' => 'Failed to retrieve dashboard data: ' . $dashboard_data['error'],
1125 - 'timestamp' => current_time('mysql')
1126 - ];
1127 - }
1128 -
1129 - // Check if we have real data available
1130 - if (!$this->has_real_data($dashboard_data)) {
1131 - return [
1132 - 'success' => false,
1133 - 'data' => null,
1134 - 'message' => 'No analytics data available yet. Please ensure your Google Analytics and Search Console are properly configured and have collected data.',
1135 - 'timestamp' => current_time('mysql')
1136 - ];
1137 - }
1138 -
1139 - // Intelligence engine is a Pro-only feature. The four SEO_* classes ship
1140 - // in Free too (the PSR-4 autoloader would resolve them), so class_exists()
1141 - // can't gate this — check the real Pro signal instead.
1142 - if (!Plan_Config::is_pro()) {
1143 - return [
1144 - 'success' => false,
1145 - 'data' => null,
1146 - 'message' => 'Intelligent dashboard requires ThinkRank Pro.',
1147 - 'timestamp' => current_time('mysql')
1148 - ];
1149 - }
1150 -
1151 - $trend_analyzer = new SEO_Trend_Analyzer();
1152 - $scoring_engine = new SEO_Scoring_Engine();
1153 - $insight_generator = new SEO_Insight_Generator();
1154 -
1155 - $data = $dashboard_data;
1156 -
1157 - // Generate trend analysis
1158 - $current_data = $data;
1159 - $historical_data = $this->get_historical_data($date_range);
1160 -
1161 - $trends = [
1162 - 'traffic_trends' => $trend_analyzer->analyze_traffic_trends($current_data, $historical_data),
1163 - 'keyword_trends' => $trend_analyzer->analyze_keyword_trends($data['search_performance'] ?? [], $date_range),
1164 - 'content_trends' => $trend_analyzer->analyze_content_trends($data, $data['search_performance'] ?? [])
1165 - ];
1166 -
1167 - // Calculate SEO health score
1168 - $seo_health = $scoring_engine->calculate_seo_health_score($data, $data['search_performance'] ?? []);
1169 -
1170 - // Generate insights
1171 - $insights = [
1172 - 'traffic_insights' => $insight_generator->generate_traffic_insights($trends['traffic_trends']),
1173 - 'keyword_insights' => $insight_generator->generate_keyword_insights($trends['keyword_trends']),
1174 - 'content_insights' => $insight_generator->generate_content_insights($trends['content_trends'])
1175 - ];
1176 -
1177 - // Combine all intelligence data
1178 - $enhanced_data = array_merge($data, [
1179 - 'intelligence' => [
1180 - 'trends' => $trends,
1181 - 'seo_health_score' => $seo_health,
1182 - 'insights' => $insights,
1183 - 'last_analyzed' => current_time('mysql')
1184 - ]
1185 - ]);
1186 -
1187 - return [
1188 - 'success' => true,
1189 - 'data' => $enhanced_data,
1190 - 'message' => 'Intelligent dashboard data retrieved successfully'
1191 - ];
1192 - }
1193 -
1194 - /**
1195 - * Get intelligent SEO opportunities with prioritization
1196 - *
1197 - * @param string $date_range Date range for analysis
1198 - * @return array Enhanced opportunities with intelligence
1199 - */
1200 - public function get_intelligent_seo_opportunities(string $date_range = '30d'): array {
1201 - // Get base opportunities data
1202 - $opportunities_data = $this->get_seo_opportunities($date_range);
1203 -
1204 - // Check if there's an error in the data
1205 - if (isset($opportunities_data['error'])) {
1206 - return [
1207 - 'success' => false,
1208 - 'data' => null,
1209 - 'message' => 'Failed to retrieve opportunities data: ' . $opportunities_data['error'],
1210 - 'timestamp' => current_time('mysql')
1211 - ];
1212 - }
1213 -
1214 - // The opportunities payload itself has no search_performance key — that
1215 - // data lives in the (cached) dashboard payload. Pull it from there both
1216 - // for the availability check and as input for the opportunity detectors;
1217 - // checking $opportunities_data['search_performance'] here used to make
1218 - // this method always bail with "No Search Console data available".
1219 - $dashboard_data = $this->get_dashboard_data($date_range);
1220 - $search_performance = $dashboard_data['search_performance'] ?? [];
1221 - $opportunities_data['search_performance'] = $search_performance;
1222 -
1223 - $has_search_data = !empty($search_performance['rows']) ||
1224 - ($search_performance['total_clicks'] ?? 0) > 0 ||
1225 - ($search_performance['total_impressions'] ?? 0) > 0;
1226 -
1227 - if (!$has_search_data) {
1228 - return [
1229 - 'success' => false,
1230 - 'data' => null,
1231 - 'message' => 'No Search Console data available yet. Please ensure your Search Console is properly configured and has collected data.',
1232 - 'timestamp' => current_time('mysql')
1233 - ];
1234 - }
1235 -
1236 - // Intelligence engine is a Pro-only feature — gate on the real Pro signal,
1237 - // not class_exists() (the classes ship in Free and would autoload).
1238 - if (!Plan_Config::is_pro()) {
1239 - return [
1240 - 'success' => false,
1241 - 'data' => null,
1242 - 'message' => 'Intelligent opportunities require ThinkRank Pro.',
1243 - 'timestamp' => current_time('mysql')
1244 - ];
1245 - }
1246 -
1247 - $opportunity_detector = new SEO_Opportunity_Detector();
1248 - $scoring_engine = new SEO_Scoring_Engine();
1249 -
1250 - $data = $opportunities_data;
1251 -
1252 - // Detect intelligent opportunities
1253 - $search_console_data = $data['search_performance'] ?? [];
1254 - $analytics_data = $data;
1255 -
1256 - $intelligent_opportunities = [
1257 - 'quick_wins' => $opportunity_detector->detect_quick_wins($search_console_data, $analytics_data),
1258 - 'content_opportunities' => $opportunity_detector->identify_content_opportunities($search_console_data, $analytics_data),
1259 - 'keyword_opportunities' => $scoring_engine->score_keyword_opportunities($search_console_data)
1260 - ];
1261 -
1262 - // Prioritize all opportunities. prioritize_opportunities() expects
1263 - // category => [opportunities]; calculate_impact_effort_matrix() expects a flat list.
1264 - $opportunities_by_category = [
1265 - 'quick_wins' => $intelligent_opportunities['quick_wins']['opportunities'] ?? [],
1266 - 'content' => $intelligent_opportunities['content_opportunities']['opportunities'] ?? [],
1267 - 'keywords' => $intelligent_opportunities['keyword_opportunities']['opportunities'] ?? [],
1268 - ];
1269 - $all_opportunities = array_merge(...array_values($opportunities_by_category));
1270 -
1271 - $prioritized = $opportunity_detector->prioritize_opportunities($opportunities_by_category);
1272 - $impact_matrix = $opportunity_detector->calculate_impact_effort_matrix($all_opportunities);
1273 -
1274 - // Enhance original data with intelligence
1275 - $enhanced_data = array_merge($data, [
1276 - 'intelligent_opportunities' => $intelligent_opportunities,
1277 - 'prioritized_opportunities' => $prioritized,
1278 - 'impact_effort_matrix' => $impact_matrix,
1279 - 'opportunity_summary' => $this->generate_opportunity_summary($intelligent_opportunities),
1280 - 'last_analyzed' => current_time('mysql')
1281 - ]);
1282 -
1283 - return [
1284 - 'success' => true,
1285 - 'data' => $enhanced_data,
1286 - 'message' => 'Intelligent SEO opportunities retrieved successfully'
1287 - ];
1288 - }
1289 -
1290 - /**
1291 - * Get SEO performance insights
1292 - *
1293 - * @param string $date_range Date range for analysis
1294 - * @return array SEO insights data
1295 - */
1296 - public function get_seo_insights(string $date_range = '30d'): array {
1297 - $cache_key = "seo_insights_{$date_range}";
1298 - $cached_data = get_transient($cache_key);
1299 -
1300 - if ($cached_data !== false) {
1301 - return [
1302 - 'success' => true,
1303 - 'data' => $cached_data,
1304 - 'cached' => true,
1305 - 'message' => 'SEO insights retrieved from cache'
1306 - ];
1307 - }
1308 -
1309 - try {
1310 - // Get dashboard data for analysis
1311 - $dashboard_result = $this->get_intelligent_dashboard_data($date_range);
1312 -
1313 - if (!$dashboard_result['success']) {
1314 - return $dashboard_result;
1315 - }
1316 -
1317 - $dashboard_data = $dashboard_result['data'];
1318 - $intelligence = $dashboard_data['intelligence'] ?? [];
1319 -
1320 - // Insights are a Pro-only feature — gate on the real Pro signal,
1321 - // not class_exists() (SEO_Insight_Generator ships in Free too).
1322 - if (!Plan_Config::is_pro()) {
1323 - return [
1324 - 'success' => false,
1325 - 'data' => null,
1326 - 'message' => 'SEO insights require ThinkRank Pro.',
1327 - 'timestamp' => current_time('mysql')
1328 - ];
1329 - }
1330 -
1331 - $insight_generator = new SEO_Insight_Generator();
1332 -
1333 - // Collect all insights
1334 - $all_insights = [];
1335 -
1336 - if (!empty($intelligence['insights']['traffic_insights']['insights'])) {
1337 - $all_insights = array_merge($all_insights, $intelligence['insights']['traffic_insights']['insights']);
1338 - }
1339 -
1340 - if (!empty($intelligence['insights']['keyword_insights']['insights'])) {
1341 - $all_insights = array_merge($all_insights, $intelligence['insights']['keyword_insights']['insights']);
1342 - }
1343 -
1344 - if (!empty($intelligence['insights']['content_insights']['insights'])) {
1345 - $all_insights = array_merge($all_insights, $intelligence['insights']['content_insights']['insights']);
1346 - }
1347 -
1348 - // Format and prioritize insights
1349 - $formatted_insights = $insight_generator->format_insights_for_display($all_insights);
1350 - $prioritized_insights = $insight_generator->prioritize_insights_by_impact($formatted_insights);
1351 -
1352 - $insights_data = [
1353 - 'insights' => $prioritized_insights['prioritized_insights'],
1354 - 'summary' => [
1355 - 'total_insights' => count($formatted_insights),
1356 - 'high_impact_count' => $prioritized_insights['high_impact_count'],
1357 - 'action_required_count' => $prioritized_insights['action_required_count']
1358 - ],
1359 - 'seo_health_score' => $intelligence['seo_health_score'] ?? null,
1360 - 'generated_at' => current_time('mysql')
1361 - ];
1362 -
1363 - // Cache the results
1364 - set_transient($cache_key, $insights_data, $this->cache_duration);
1365 -
1366 - return [
1367 - 'success' => true,
1368 - 'data' => $insights_data,
1369 - 'cached' => false,
1370 - 'message' => 'SEO insights generated successfully'
1371 - ];
1372 - } catch (\Exception $e) {
1373 - return [
1374 - 'success' => false,
1375 - 'error' => 'Failed to generate SEO insights: ' . $e->getMessage(),
1376 - 'data' => null
1377 - ];
1378 - }
1379 - }
1380 -
1381 - /**
1382 - * Check if real analytics data is available
1383 - *
1384 - * @param array $dashboard_data Dashboard data to check
1385 - * @return bool True if real data is available
1386 - */
1387 - private function has_real_data(array $dashboard_data): bool {
1388 - // Check if we have meaningful traffic data
1389 - $traffic = $dashboard_data['traffic'] ?? [];
1390 - $search_performance = $dashboard_data['search_performance'] ?? [];
1391 -
1392 - $has_traffic = !empty($traffic) && (
1393 - ($traffic['sessions'] ?? 0) > 0 ||
1394 - ($traffic['pageviews'] ?? 0) > 0 ||
1395 - ($traffic['active_users'] ?? 0) > 0
1396 - );
1397 -
1398 - $has_search_data = !empty($search_performance) && (
1399 - !empty($search_performance['rows']) ||
1400 - ($search_performance['total_clicks'] ?? 0) > 0 ||
1401 - ($search_performance['total_impressions'] ?? 0) > 0
1402 - );
1403 -
1404 - return $has_traffic || $has_search_data;
1405 - }
1406 -
1407 - /**
1408 - * Get historical data for trend comparison
1409 - *
1410 - * @param string $current_range Current date range
1411 - * @return array Historical data
1412 - */
1413 - private function get_historical_data(string $current_range): array {
1414 - // Calculate previous period based on current range
1415 - $previous_range = $this->calculate_previous_period($current_range);
1416 -
1417 - // Try to get actual historical data from previous period
1418 - $historical_data = $this->get_dashboard_data($previous_range);
1419 -
1420 - // Return the actual historical data (may be empty if no real data available)
1421 - return [
1422 - 'sessions' => $historical_data['traffic']['sessions'] ?? 0,
1423 - 'pageviews' => $historical_data['traffic']['pageviews'] ?? 0,
1424 - 'organic_traffic' => $historical_data['organic_traffic'] ?? ['organic_traffic' => ['sessions' => 0]],
1425 - 'bounce_rate' => $historical_data['traffic']['bounce_rate'] ?? 0,
1426 - 'avg_session_duration' => $historical_data['traffic']['avg_session_duration'] ?? 0
1427 - ];
1428 - }
1429 -
1430 - /**
1431 - * Calculate previous period for comparison
1432 - *
1433 - * @param string $current_range Current range
1434 - * @return string Previous period range
1435 - */
1436 - private function calculate_previous_period(string $current_range): string {
1437 - // Simple mapping for now - could be enhanced with actual date calculations
1438 - $period_mapping = [
1439 - '7d' => '14d',
1440 - '30d' => '60d',
1441 - '90d' => '180d'
1442 - ];
1443 -
1444 - return $period_mapping[$current_range] ?? '60d';
1445 - }
1446 -
1447 - /**
1448 - * Generate opportunity summary
1449 - *
1450 - * @param array $opportunities All opportunities
1451 - * @return array Opportunity summary
1452 - */
1453 - private function generate_opportunity_summary(array $opportunities): array {
1454 - $quick_wins_count = count($opportunities['quick_wins']['opportunities'] ?? []);
1455 - $content_opportunities_count = count($opportunities['content_opportunities']['opportunities'] ?? []);
1456 - $keyword_opportunities_count = count($opportunities['keyword_opportunities']['opportunities'] ?? []);
1457 -
1458 - $total_opportunities = $quick_wins_count + $content_opportunities_count + $keyword_opportunities_count;
1459 -
1460 - $potential_clicks = 0;
1461 - if (!empty($opportunities['quick_wins']['potential_additional_clicks'])) {
1462 - $potential_clicks = $opportunities['quick_wins']['potential_additional_clicks'];
1463 - }
1464 -
1465 - return [
1466 - 'total_opportunities' => $total_opportunities,
1467 - 'quick_wins_count' => $quick_wins_count,
1468 - 'content_opportunities_count' => $content_opportunities_count,
1469 - 'keyword_opportunities_count' => $keyword_opportunities_count,
1470 - 'potential_additional_clicks' => $potential_clicks,
1471 - 'priority_recommendation' => $quick_wins_count > 0 ?
1472 - 'Focus on quick wins first for immediate impact' :
1473 - 'Focus on content optimization for long-term growth'
1474 - ];
1475 - }
1476 -
1477 - /**
1478 - * Clear intelligence cache
1479 - *
1480 - * @return array Clear result
1481 - */
1482 - public function clear_intelligence_cache(): array {
1483 - $intelligence_cache_keys = [
1484 - 'seo_insights_7d',
1485 - 'seo_insights_30d',
1486 - 'seo_insights_90d',
1487 - 'intelligent_dashboard_7d',
1488 - 'intelligent_dashboard_30d',
1489 - 'intelligent_dashboard_90d',
1490 - 'intelligent_opportunities_7d',
1491 - 'intelligent_opportunities_30d',
1492 - 'intelligent_opportunities_90d'
1493 - ];
1494 -
1495 - $cleared = 0;
1496 - foreach ($intelligence_cache_keys as $key) {
1497 - if (delete_transient($key)) {
1498 - $cleared++;
1499 - }
1500 - }
1501 -
1502 - return [
1503 - 'success' => true,
1504 - 'message' => "Cleared {$cleared} intelligence cache entries",
1505 - 'cleared_count' => $cleared,
1506 - 'timestamp' => current_time('mysql')
1507 - ];
1508 1065 }
1509 1066 }