PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.7.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.7.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 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
← All changes | includes/core/class-settings-manager.php +29 -6 1.32.02.7.0 View file →
@@ -257,12 +257,8 @@
257 257 'seo_analytics_google_analytics_property_id',
258 258 'ga_analytics_account_id',
259 259 'ga_analytics_data_stream_id',
260 260
261 - // GA4 Tracking Code Injection (Pro)
262 - 'ga4_auto_inject',
263 - 'ga4_measurement_id',
264 -
265 261 // Search Console configuration
266 262 'search_console_property',
267 263
268 264 // AI features
@@ -454,8 +450,23 @@
454 450 * @since 1.0.0
455 451 *
456 452 * @return array Categories information
457 453 */
454 + /**
455 + * The setting keys a category defines.
456 + *
457 + * Exposed so callers can reject keys a category does not define instead of
458 + * persisting whatever they are handed (#395).
459 + *
460 + * @since 2.0.1
461 + *
462 + * @param string $category Category name.
463 + * @return string[] Setting keys, or [] when the category is unknown here.
464 + */
465 + public function get_category_keys(string $category): array {
466 + return $this->settings_categories[$category]['keys'] ?? [];
467 + }
468 +
458 469 public function get_categories(): array {
459 470 $categories = [];
460 471
461 472 foreach ($this->settings_categories as $key => $config) {
@@ -629,8 +640,16 @@
629 640 * @return array Core settings for category
630 641 */
631 642 private function get_core_settings_by_category(string $category): array {
632 643 $category_config = $this->settings_categories[$category];
644 +
645 + // Prime the option cache in one query before the loop. Every
646 + // thinkrank_* option is autoload=off, so WordPress cannot serve them
647 + // from `alloptions` and each Settings->get() below was its own
648 + // round-trip — 16 of them on every anonymous front-end request, on
649 + // pages that use none of the values (#393).
650 + $this->core_settings->prime($category_config['keys']);
651 +
633 652 $settings = [];
634 653
635 654 foreach ($category_config['keys'] as $key) {
636 655 $settings[$key] = $this->core_settings->get($key);
@@ -813,11 +832,15 @@
813 832 }
814 833 break;
815 834
816 835 case 'ai_provider':
817 - if (!in_array($value, ['openai', 'claude', 'gemini', 'openrouter'], true)) {
836 + // '' is legal: it is Settings::AI_PROVIDER_NONE, the state a
837 + // fresh install starts in and the one a user returns to by
838 + // deselecting their provider (#572).
839 + if (!in_array($value, \ThinkRank\Core\Settings::selectable_ai_providers(), true)) {
818 840 $validation['valid'] = false;
819 - $validation['errors'][] = "ai_provider must be 'openai', 'claude', 'gemini', or 'openrouter'";
841 + $validation['errors'][] = "ai_provider must be empty (no provider) or one of: "
842 + . implode(', ', \ThinkRank\Core\Settings::SUPPORTED_AI_PROVIDERS);
820 843 }
821 844 break;
822 845
823 846 case 'dashboard_widgets':