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/admin/class-manager.php +33 -22 2.4.0 → 2.10.0 View file →
@@ -16,13 +16,15 @@
16 16 use ThinkRank\Core\Settings;
17 17 use ThinkRank\Core\Database;
18 18 use ThinkRank\Core\Plan_Config;
19 19 use ThinkRank\Core\Capability_Manager;
20 +use ThinkRank\Config\Local_Business_Types_Config;
20 21 use ThinkRank\Admin\Metabox_Manager;
21 22 use ThinkRank\Admin\Elementor_Metabox;
22 23 use ThinkRank\Admin\Oxygen_Metabox;
23 24 use ThinkRank\Admin\Divi_Metabox;
24 25 use ThinkRank\Admin\Bricks_Metabox;
26 +use ThinkRank\Admin\Beaver_Metabox;
25 27 use ThinkRank\Admin\Bulk_Action_Manager;
26 28 use ThinkRank\Admin\Post_List_Filters;
27 29
28 30 // Prevent direct access
@@ -88,8 +90,15 @@
88 90 */
89 91 private Bricks_Metabox $bricks_metabox;
90 92
91 93 /**
94 + * Beaver Builder metabox integration
95 + *
96 + * @var Beaver_Metabox
97 + */
98 + private Beaver_Metabox $beaver_metabox;
99 +
100 + /**
92 101 * Post list columns instance
93 102 *
94 103 * @var Post_List_Columns
95 104 */
@@ -143,8 +152,9 @@
143 152 $this->elementor_metabox = new Elementor_Metabox($this->metabox_manager);
144 153 $this->oxygen_metabox = new Oxygen_Metabox($this->metabox_manager);
145 154 $this->divi_metabox = new Divi_Metabox($this->metabox_manager);
146 155 $this->bricks_metabox = new Bricks_Metabox($this->metabox_manager);
156 + $this->beaver_metabox = new Beaver_Metabox($this->metabox_manager);
147 157 $this->post_list_columns = new Post_List_Columns();
148 158 $this->focus_keyword_ajax = new Focus_Keyword_Ajax();
149 159 $this->seo_quick_edit_ajax = new Seo_Quick_Edit_Ajax();
150 160 $this->bulk_action_manager = new Bulk_Action_Manager();
@@ -184,8 +194,12 @@
184 194 // Initialize Bricks builder integration (hooks gate on Bricks' own
185 195 // builder detector, so they no-op without Bricks)
186 196 $this->bricks_metabox->init();
187 197
198 + // Initialize Beaver Builder integration (hooks gate on Beaver Builder's
199 + // own builder detector, so they no-op without Beaver Builder)
200 + $this->beaver_metabox->init();
201 +
188 202 // Initialize post list columns
189 203 $this->post_list_columns->init();
190 204
191 205 // Initialize focus keyword AJAX handler
@@ -431,8 +445,13 @@
431 445 'faviconUrl' => get_site_icon_url(64) ?: '',
432 446 'adminEmail' => get_option('admin_email'),
433 447 // Post types for Global SEO navigation
434 448 'postTypes' => $this->get_public_post_types(),
449 + // schema.org LocalBusiness subtypes for the Local SEO control.
450 + // Localized rather than mirrored in a JS config: the list runs to
451 + // ~150 entries and is also the MCP ability's enum, so a second copy
452 + // would be a second thing to keep in step (#623).
453 + 'localBusinessTypes' => Local_Business_Types_Config::get_options(),
435 454 // Role Manager: capabilities the current user holds + the
436 455 // section → capability map, so the SPA can hide sections a role
437 456 // cannot access. Administrators receive every capability.
438 457 'caps' => Capability_Manager::user_capabilities(),
@@ -439,13 +458,10 @@
439 458 'sectionCaps' => Capability_Manager::section_map(),
440 459 'canManageRoles' => Capability_Manager::current_user_can(Capability_Manager::MANAGE_ROLES),
441 460 // Pro detection flag
442 461 'isPro' => Plan_Config::is_pro(),
443 - // Data update frequency (Pro: daily, Free: every 3 days)
444 - 'dataUpdateFrequency' => Plan_Config::is_pro() ? 'daily' : '3days',
445 - // NB: no per-feature capability maps here. Email Reporting reads
446 - // its capabilities from GET /thinkrank/v1/email-report/config;
447 - // localizing a second copy only invited the two to drift.
462 + // NB: no per-feature capability maps here; each screen reads its
463 + // own state over REST, so a localized copy cannot drift from it.
448 464 // MCP (Model Context Protocol) connection details for the MCP page.
449 465 'mcp' => $this->get_mcp_globals(),
450 466 // Google OAuth: JS only ever gets a nonce-signed admin-post URL.
451 467 // The consent URL, client ID, and scopes are assembled by the proxy,
@@ -479,8 +495,13 @@
479 495 }
480 496
481 497 // Check for plugin updates
482 498 $this->check_plugin_updates();
499 +
500 + // One-time: a Key Features value saved while commas were delimiters
501 + // becomes one feature per line, so the next regeneration publishes the
502 + // bullets the site already had rather than one merged line.
503 + \ThinkRank\SEO\LLMs_Txt_Manager::maybe_migrate_legacy_key_features();
483 504 }
484 505
485 506 /**
486 507 * Load admin page
@@ -792,19 +813,14 @@
792 813 wp_die();
793 814 }
794 815
795 816 /**
796 - * Check if API key is configured
817 + * Check if the selected AI provider is configured
797 818 *
798 - * @return bool True if at least one API key is configured
819 + * @return bool True when the chosen provider has what it needs to run
799 820 */
800 821 private function has_api_key_configured(): bool {
801 - $settings = \ThinkRank\Core\Settings::instance();
802 -
803 - return !empty($settings->get('openai_api_key'))
804 - || !empty($settings->get('claude_api_key'))
805 - || !empty($settings->get('gemini_api_key'))
806 - || !empty($settings->get('openrouter_api_key'));
822 + return \ThinkRank\Core\Settings::instance()->has_ai_provider_configured();
807 823 }
808 824
809 825 /**
810 826 * Get menu icon
@@ -867,22 +883,17 @@
867 883 ];
868 884 }
869 885
870 886 /**
871 - * Whether any AI provider API key is configured
887 + * Whether the selected AI provider is configured
872 888 *
873 - * Mirrors the check used by ThinkRank\AI\Manager.
889 + * Same answer as ThinkRank\AI\Manager — both read
890 + * Settings::has_ai_provider_configured().
874 891 *
875 892 * @return bool
876 893 */
877 894 private function is_ai_configured(): bool {
878 - foreach (['openai_api_key', 'claude_api_key', 'gemini_api_key', 'openrouter_api_key'] as $key) {
879 - if (!empty($this->settings->get($key, ''))) {
880 - return true;
881 - }
882 - }
883 -
884 - return false;
895 + return $this->settings->has_ai_provider_configured();
885 896 }
886 897
887 898 /**
888 899 * Get internationalization strings