PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.11.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.11.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 1.0.0 All 52 releases
← All changes | includes/admin/class-manager.php +36 -22 2.4.0 → 2.11.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
@@ -414,8 +428,11 @@
414 428 'capabilities' => $this->get_user_capabilities(),
415 429 'settings' => $this->get_admin_settings(),
416 430 'i18n' => $this->get_i18n_strings(),
417 431 'isAdmin' => current_user_can('manage_options'),
432 + // Simple / Advanced navigation for this user (#730). Read once
433 + // when the page is built; the header switch updates it in place.
434 + 'uiMode' => UI_Mode::get(),
418 435 // One flag per half of the Import / Export page: the "import from
419 436 // another SEO plugin" section, and ThinkRank's own export/restore.
420 437 'migrationToolsEnabled' => (bool) $this->settings->get('enable_migration_tools', false),
421 438 'importExportEnabled' => (bool) $this->settings->get('enable_import_export', false),
@@ -431,8 +448,13 @@
431 448 'faviconUrl' => get_site_icon_url(64) ?: '',
432 449 'adminEmail' => get_option('admin_email'),
433 450 // Post types for Global SEO navigation
434 451 'postTypes' => $this->get_public_post_types(),
452 + // schema.org LocalBusiness subtypes for the Local SEO control.
453 + // Localized rather than mirrored in a JS config: the list runs to
454 + // ~150 entries and is also the MCP ability's enum, so a second copy
455 + // would be a second thing to keep in step (#623).
456 + 'localBusinessTypes' => Local_Business_Types_Config::get_options(),
435 457 // Role Manager: capabilities the current user holds + the
436 458 // section → capability map, so the SPA can hide sections a role
437 459 // cannot access. Administrators receive every capability.
438 460 'caps' => Capability_Manager::user_capabilities(),
@@ -439,13 +461,10 @@
439 461 'sectionCaps' => Capability_Manager::section_map(),
440 462 'canManageRoles' => Capability_Manager::current_user_can(Capability_Manager::MANAGE_ROLES),
441 463 // Pro detection flag
442 464 '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.
465 + // NB: no per-feature capability maps here; each screen reads its
466 + // own state over REST, so a localized copy cannot drift from it.
448 467 // MCP (Model Context Protocol) connection details for the MCP page.
449 468 'mcp' => $this->get_mcp_globals(),
450 469 // Google OAuth: JS only ever gets a nonce-signed admin-post URL.
451 470 // The consent URL, client ID, and scopes are assembled by the proxy,
@@ -479,8 +498,13 @@
479 498 }
480 499
481 500 // Check for plugin updates
482 501 $this->check_plugin_updates();
502 +
503 + // One-time: a Key Features value saved while commas were delimiters
504 + // becomes one feature per line, so the next regeneration publishes the
505 + // bullets the site already had rather than one merged line.
506 + \ThinkRank\SEO\LLMs_Txt_Manager::maybe_migrate_legacy_key_features();
483 507 }
484 508
485 509 /**
486 510 * Load admin page
@@ -792,19 +816,14 @@
792 816 wp_die();
793 817 }
794 818
795 819 /**
796 - * Check if API key is configured
820 + * Check if the selected AI provider is configured
797 821 *
798 - * @return bool True if at least one API key is configured
822 + * @return bool True when the chosen provider has what it needs to run
799 823 */
800 824 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'));
825 + return \ThinkRank\Core\Settings::instance()->has_ai_provider_configured();
807 826 }
808 827
809 828 /**
810 829 * Get menu icon
@@ -867,22 +886,17 @@
867 886 ];
868 887 }
869 888
870 889 /**
871 - * Whether any AI provider API key is configured
890 + * Whether the selected AI provider is configured
872 891 *
873 - * Mirrors the check used by ThinkRank\AI\Manager.
892 + * Same answer as ThinkRank\AI\Manager — both read
893 + * Settings::has_ai_provider_configured().
874 894 *
875 895 * @return bool
876 896 */
877 897 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;
898 + return $this->settings->has_ai_provider_configured();
885 899 }
886 900
887 901 /**
888 902 * Get internationalization strings