PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.10.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.10.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 1.0.0 1.0.1 All 51 releases
← All changes | includes/core/class-settings-manager.php +54 -6 2.0.0 → 2.10.0 View file →
@@ -83,12 +83,21 @@
83 83 'gemini_api_key',
84 84 'gemini_model',
85 85 'openrouter_api_key',
86 86 'openrouter_model',
87 + 'openai_compatible_base_url',
88 + 'openai_compatible_api_key',
89 + 'openai_compatible_model',
90 + 'openai_compatible_timeout',
91 + 'openai_compatible_supports_images',
92 + 'openai_compatible_json_mode',
93 + 'openai_compatible_price_per_million',
87 94 'max_tokens',
88 95 'temperature',
89 96 'cache_duration',
90 97 'max_requests_per_minute',
98 + 'ai_daily_request_limit',
99 + 'ai_paused',
91 100 'enable_logging',
92 101 'debug_mode',
93 102 'api_timeout',
94 103 'retry_attempts',
@@ -257,12 +266,8 @@
257 266 'seo_analytics_google_analytics_property_id',
258 267 'ga_analytics_account_id',
259 268 'ga_analytics_data_stream_id',
260 269
261 - // GA4 Tracking Code Injection (Pro)
262 - 'ga4_auto_inject',
263 - 'ga4_measurement_id',
264 -
265 270 // Search Console configuration
266 271 'search_console_property',
267 272
268 273 // AI features
@@ -454,8 +459,23 @@
454 459 * @since 1.0.0
455 460 *
456 461 * @return array Categories information
457 462 */
463 + /**
464 + * The setting keys a category defines.
465 + *
466 + * Exposed so callers can reject keys a category does not define instead of
467 + * persisting whatever they are handed (#395).
468 + *
469 + * @since 2.0.1
470 + *
471 + * @param string $category Category name.
472 + * @return string[] Setting keys, or [] when the category is unknown here.
473 + */
474 + public function get_category_keys(string $category): array {
475 + return $this->settings_categories[$category]['keys'] ?? [];
476 + }
477 +
458 478 public function get_categories(): array {
459 479 $categories = [];
460 480
461 481 foreach ($this->settings_categories as $key => $config) {
@@ -629,8 +649,16 @@
629 649 * @return array Core settings for category
630 650 */
631 651 private function get_core_settings_by_category(string $category): array {
632 652 $category_config = $this->settings_categories[$category];
653 +
654 + // Prime the option cache in one query before the loop. Every
655 + // thinkrank_* option is autoload=off, so WordPress cannot serve them
656 + // from `alloptions` and each Settings->get() below was its own
657 + // round-trip — 16 of them on every anonymous front-end request, on
658 + // pages that use none of the values (#393).
659 + $this->core_settings->prime($category_config['keys']);
660 +
633 661 $settings = [];
634 662
635 663 foreach ($category_config['keys'] as $key) {
636 664 $settings[$key] = $this->core_settings->get($key);
@@ -786,8 +814,10 @@
786 814 switch ($key) {
787 815 case 'openai_api_key':
788 816 case 'claude_api_key':
789 817 case 'openrouter_api_key':
818 + case 'openai_compatible_api_key':
819 + case 'openai_compatible_model':
790 820 if (!empty($value) && !is_string($value)) {
791 821 $validation['valid'] = false;
792 822 $validation['errors'][] = "{$key} must be a string";
793 823 }
@@ -792,15 +822,29 @@
792 822 $validation['errors'][] = "{$key} must be a string";
793 823 }
794 824 break;
795 825
826 + case 'openai_compatible_base_url':
827 + // An unreachable or dangerous URL is refused with a reason
828 + // rather than quietly stored (see Endpoint_URL_Validator).
829 + if (!empty($value)) {
830 + $validated = \ThinkRank\AI\Endpoint_URL_Validator::validate((string) $value);
831 + if (is_wp_error($validated)) {
832 + $validation['valid'] = false;
833 + $validation['errors'][] = $validated->get_error_message();
834 + }
835 + }
836 + break;
837 +
796 838 case 'max_tokens':
797 839 case 'cache_duration':
798 840 case 'max_requests_per_minute':
841 + case 'ai_daily_request_limit':
799 842 case 'seo_score_threshold':
800 843 case 'api_timeout':
801 844 case 'retry_attempts':
802 845 case 'data_retention_days':
846 + case 'openai_compatible_timeout':
803 847 if (!is_numeric($value) || $value < 0) {
804 848 $validation['valid'] = false;
805 849 $validation['errors'][] = "{$key} must be a positive number";
806 850 }
@@ -813,11 +857,15 @@
813 857 }
814 858 break;
815 859
816 860 case 'ai_provider':
817 - if (!in_array($value, ['openai', 'claude', 'gemini', 'openrouter'], true)) {
861 + // '' is legal: it is Settings::AI_PROVIDER_NONE, the state a
862 + // fresh install starts in and the one a user returns to by
863 + // deselecting their provider (#572).
864 + if (!in_array($value, \ThinkRank\Core\Settings::selectable_ai_providers(), true)) {
818 865 $validation['valid'] = false;
819 - $validation['errors'][] = "ai_provider must be 'openai', 'claude', 'gemini', or 'openrouter'";
866 + $validation['errors'][] = "ai_provider must be empty (no provider) or one of: "
867 + . implode(', ', \ThinkRank\Core\Settings::SUPPORTED_AI_PROVIDERS);
820 868 }
821 869 break;
822 870
823 871 case 'dashboard_widgets':