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/admin/class-manager.php +73 -16 2.1.02.7.0 View file →
@@ -20,8 +20,10 @@
20 20 use ThinkRank\Admin\Metabox_Manager;
21 21 use ThinkRank\Admin\Elementor_Metabox;
22 22 use ThinkRank\Admin\Oxygen_Metabox;
23 23 use ThinkRank\Admin\Divi_Metabox;
24 +use ThinkRank\Admin\Bricks_Metabox;
25 +use ThinkRank\Admin\Beaver_Metabox;
24 26 use ThinkRank\Admin\Bulk_Action_Manager;
25 27 use ThinkRank\Admin\Post_List_Filters;
26 28
27 29 // Prevent direct access
@@ -80,8 +82,22 @@
80 82 */
81 83 private Divi_Metabox $divi_metabox;
82 84
83 85 /**
86 + * Bricks builder metabox integration instance
87 + *
88 + * @var Bricks_Metabox
89 + */
90 + private Bricks_Metabox $bricks_metabox;
91 +
92 + /**
93 + * Beaver Builder metabox integration
94 + *
95 + * @var Beaver_Metabox
96 + */
97 + private Beaver_Metabox $beaver_metabox;
98 +
99 + /**
84 100 * Post list columns instance
85 101 *
86 102 * @var Post_List_Columns
87 103 */
@@ -134,8 +150,10 @@
134 150 $this->metabox_manager = new Metabox_Manager($this->settings);
135 151 $this->elementor_metabox = new Elementor_Metabox($this->metabox_manager);
136 152 $this->oxygen_metabox = new Oxygen_Metabox($this->metabox_manager);
137 153 $this->divi_metabox = new Divi_Metabox($this->metabox_manager);
154 + $this->bricks_metabox = new Bricks_Metabox($this->metabox_manager);
155 + $this->beaver_metabox = new Beaver_Metabox($this->metabox_manager);
138 156 $this->post_list_columns = new Post_List_Columns();
139 157 $this->focus_keyword_ajax = new Focus_Keyword_Ajax();
140 158 $this->seo_quick_edit_ajax = new Seo_Quick_Edit_Ajax();
141 159 $this->bulk_action_manager = new Bulk_Action_Manager();
@@ -171,8 +189,16 @@
171 189 // Initialize Divi Visual Builder integration (hooks gate on the VB
172 190 // request, so they no-op without Divi)
173 191 $this->divi_metabox->init();
174 192
193 + // Initialize Bricks builder integration (hooks gate on Bricks' own
194 + // builder detector, so they no-op without Bricks)
195 + $this->bricks_metabox->init();
196 +
197 + // Initialize Beaver Builder integration (hooks gate on Beaver Builder's
198 + // own builder detector, so they no-op without Beaver Builder)
199 + $this->beaver_metabox->init();
200 +
175 201 // Initialize post list columns
176 202 $this->post_list_columns->init();
177 203
178 204 // Initialize focus keyword AJAX handler
@@ -260,13 +286,33 @@
260 286 'thinkrank-settings',
261 287 [$this, 'render_settings_page']
262 288 );
263 289
264 - // Migration page — re-run SEO data imports from other plugins after
265 - // setup. Hidden by default; shown only when the "Enable Migration Tools"
266 - // advanced setting is on. Capability matches the import REST endpoints
267 - // (`manage_options`) so the UI and API stay in agreement.
268 - if (Settings::instance()->get('enable_migration_tools', false)) {
290 + // Two separate screens, one per setting, so each menu item appears
291 + // exactly when its own feature is on. They shared a page (and therefore
292 + // a menu item) until #228: with one route, turning Import / Export off
293 + // still left "Import / Export" in the sidebar whenever Migration Tools
294 + // happened to be on, which is the opposite of what the switch promises.
295 + //
296 + // Capability matches the import/export REST endpoints (`manage_options`)
297 + // so the UI and API stay in agreement.
298 +
299 + // ThinkRank's own data, out to a file and back. Off by default.
300 + if ((bool) Settings::instance()->get('enable_import_export', false)) {
301 + $this->pages['import-export'] = add_submenu_page(
302 + 'thinkrank',
303 + __('Import / Export', 'thinkrank'),
304 + __('Import / Export', 'thinkrank'),
305 + 'manage_options',
306 + 'thinkrank-import-export',
307 + [$this, 'render_import_export_page']
308 + );
309 + }
310 +
311 + // Importing FROM another SEO plugin. Off by default. Slug kept as
312 + // thinkrank-migration so existing links, bookmarks and the docs keep
313 + // resolving to the migration screen they always meant.
314 + if ((bool) Settings::instance()->get('enable_migration_tools', false)) {
269 315 $this->pages['migration'] = add_submenu_page(
270 316 'thinkrank',
271 317 __('Migration', 'thinkrank'),
272 318 __('Migration', 'thinkrank'),
@@ -381,8 +427,12 @@
381 427 'capabilities' => $this->get_user_capabilities(),
382 428 'settings' => $this->get_admin_settings(),
383 429 'i18n' => $this->get_i18n_strings(),
384 430 'isAdmin' => current_user_can('manage_options'),
431 + // One flag per half of the Import / Export page: the "import from
432 + // another SEO plugin" section, and ThinkRank's own export/restore.
433 + 'migrationToolsEnabled' => (bool) $this->settings->get('enable_migration_tools', false),
434 + 'importExportEnabled' => (bool) $this->settings->get('enable_import_export', false),
385 435 // Whether any AI provider API key is configured — used to gate
386 436 // "Generate with AI" buttons in the UI
387 437 'aiConfigured' => $this->is_ai_configured(),
388 438 // Plugin version - directly available without API call
@@ -402,13 +452,10 @@
402 452 'sectionCaps' => Capability_Manager::section_map(),
403 453 'canManageRoles' => Capability_Manager::current_user_can(Capability_Manager::MANAGE_ROLES),
404 454 // Pro detection flag
405 455 'isPro' => Plan_Config::is_pro(),
406 - // Data update frequency (Pro: daily, Free: every 3 days)
407 - 'dataUpdateFrequency' => Plan_Config::is_pro() ? 'daily' : '3days',
408 - // NB: no per-feature capability maps here. Email Reporting reads
409 - // its capabilities from GET /thinkrank/v1/email-report/config;
410 - // localizing a second copy only invited the two to drift.
456 + // NB: no per-feature capability maps here; each screen reads its
457 + // own state over REST, so a localized copy cannot drift from it.
411 458 // MCP (Model Context Protocol) connection details for the MCP page.
412 459 'mcp' => $this->get_mcp_globals(),
413 460 // Google OAuth: JS only ever gets a nonce-signed admin-post URL.
414 461 // The consent URL, client ID, and scopes are assembled by the proxy,
@@ -540,13 +587,22 @@
540 587 ]);
541 588 }
542 589
543 590 /**
544 - * Render import/export page
591 + * Render the Import / Export page (ThinkRank's own data, out and back).
545 592 *
593 + * Defense in depth: the submenu is only registered while the setting is on,
594 + * but re-check here so a direct hit on the page URL cannot bypass the gate.
595 + * The export REST routes carry their own `manage_options` check, so nothing
596 + * is protected by the page alone.
597 + *
546 598 * @return void
547 599 */
548 600 public function render_import_export_page(): void {
601 + if (!(bool) Settings::instance()->get('enable_import_export', false)) {
602 + wp_die(esc_html__('Import / Export is not enabled.', 'thinkrank'));
603 + }
604 +
549 605 $this->render_admin_page('import-export', [
550 606 'page_title' => __('Import / Export', 'thinkrank'),
551 607 ]);
552 608 }
@@ -551,19 +607,19 @@
551 607 ]);
552 608 }
553 609
554 610 /**
555 - * Render the Migration page (re-run SEO data imports).
611 + * Render the Migration page (import SEO data from another plugin).
556 612 *
557 - * Defense in depth: the submenu is only registered when the setting is on,
558 - * but re-check here so a direct hit on the page URL can't bypass the gate.
613 + * Same defense in depth as above, against its own setting.
559 614 *
560 615 * @return void
561 616 */
562 617 public function render_migration_page(): void {
563 - if (!Settings::instance()->get('enable_migration_tools', false)) {
618 + if (!(bool) Settings::instance()->get('enable_migration_tools', false)) {
564 619 wp_die(esc_html__('The Migration tools are not enabled.', 'thinkrank'));
565 620 }
621 +
566 622 $this->render_admin_page('migration', [
567 623 'page_title' => __('Migration', 'thinkrank'),
568 624 ]);
569 625 }
@@ -815,9 +871,9 @@
815 871 */
816 872 private function get_admin_settings(): array {
817 873 return [
818 874
819 - 'ai_provider' => $this->settings->get('ai_provider', 'openai'),
875 + 'ai_provider' => $this->settings->get('ai_provider', Settings::AI_PROVIDER_NONE),
820 876 'cache_duration' => $this->settings->get('cache_duration', 3600),
821 877 ];
822 878 }
823 879
@@ -949,8 +1005,9 @@
949 1005 'thinkrank_page_thinkrank-ai-tools',
950 1006 'thinkrank_page_thinkrank-settings',
951 1007 'thinkrank_page_thinkrank-usages',
952 1008 'thinkrank_page_thinkrank-license',
1009 + 'thinkrank_page_thinkrank-import-export',
953 1010 'thinkrank_page_thinkrank-migration'
954 1011 ] , true) ) {
955 1012
956 1013 remove_all_actions( 'user_admin_notices' );