PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.3
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.3
4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 All 201 releases
← All changes | includes/Plugin.php +158 -3 4.7.0 → 4.9.3 View file →
@@ -31,11 +31,14 @@
31 31 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
32 32 use WPDeveloper\BetterDocs\Dependencies\DI\ContainerBuilder;
33 33 use WPDeveloper\BetterDocs\Editors\Editor;
34 34 use WPDeveloper\BetterDocs\FrontEnd\FrontEnd;
35 +use WPDeveloper\BetterDocs\FrontEnd\PrintTemplate;
35 36 use WPDeveloper\BetterDocs\FrontEnd\SearchExtender;
36 37 use WPDeveloper\BetterDocs\FrontEnd\TemplateTags;
37 38 use WPDeveloper\BetterDocs\FrontEnd\WooProductFAQ;
39 +use WPDeveloper\BetterDocs\Abilities\AbilitiesRegistrar;
40 +use WPDeveloper\BetterDocs\Mcp\MCPManager;
38 41 use WPDeveloper\BetterDocs\Modules\StyleHandler as ModulesStyleHandler;
39 42 use WPDeveloper\BetterDocs\Utils\Database;
40 43 use WPDeveloper\BetterDocs\Utils\Enqueue;
41 44 use WPDeveloper\BetterDocs\Utils\Helper;
@@ -129,9 +132,9 @@
129 132 /**
130 133 * Plugin Version
131 134 * @var string
132 135 */
133 - public $version = '4.7.0';
136 + public $version = '4.9.3';
134 137
135 138 /**
136 139 * WriteWithAI Class
137 140 * @var string
@@ -148,9 +151,9 @@
148 151 /**
149 152 * Plugin DB Version
150 153 * @var string
151 154 */
152 - public $db_version = '1.0.2';
155 + public $db_version = '1.0.3';
153 156
154 157 public function __construct() {
155 158 $this->define_constants();
156 159
@@ -162,8 +165,21 @@
162 165 * and version updates check
163 166 */
164 167 $this->container->get( Install::class );
165 168
169 + /**
170 + * Abilities API registrar.
171 + *
172 + * Resolved here, at plugin load, and not from initialize(): both the
173 + * bundled Abilities API and WordPress core's build their registry as a
174 + * lazy singleton and fire `wp_abilities_api_init` from it, at or after
175 + * `init`, the first time anything reads the registry. Our listeners
176 + * therefore have to be attached before that first read, whenever it
177 + * happens — and a container resolve on `init` would already be too late
178 + * for a plugin that reads the registry earlier in the same hook.
179 + */
180 + $this->container->get( AbilitiesRegistrar::class );
181 +
166 182 add_action( 'init', array( $this, 'initialize' ), 0 );
167 183
168 184 /**
169 185 * Initialize API
@@ -191,9 +207,11 @@
191 207 $this->define( 'BETTERDOCS_DB_VERSION', $this->db_version );
192 208 $this->define( 'BETTERDOCS_ABSPATH', dirname( BETTERDOCS_PLUGIN_FILE ) . '/' );
193 209 $this->define( 'BETTERDOCS_ABSURL', plugin_dir_url( BETTERDOCS_PLUGIN_FILE ) );
194 210 $this->define( 'BETTERDOCS_PLUGIN_BASENAME', plugin_basename( BETTERDOCS_PLUGIN_FILE ) );
195 - $this->define( 'BETTERDOCS_BLOCKS_DIRECTORY', BETTERDOCS_ABSPATH . 'assets/blocks/' );
211 + // Compiled block metadata lives under assets/build/ since the Node 24 asset
212 + // restructure; register_block_type() fails silently if this path is wrong.
213 + $this->define( 'BETTERDOCS_BLOCKS_DIRECTORY', BETTERDOCS_ABSPATH . 'assets/build/blocks/' );
196 214 $this->define( 'BETTERDOCS_ROOT_DIR_PATH', plugin_dir_path( BETTERDOCS_PLUGIN_FILE ) );
197 215 $this->define( 'BETTERDOCS_FSE_TEMPLATES_PATH', BETTERDOCS_ROOT_DIR_PATH . 'views/templates/fse' );
198 216
199 217 /**
@@ -278,8 +296,17 @@
278 296 $this->container->get( DocsAISuite::class );
279 297
280 298 $this->container->get( Admin::class );
281 299 $this->container->get( Roles::class );
300 +
301 + /**
302 + * MCP transport: rewrite rules, the pretty endpoint, OAuth discovery
303 + * and every REST route. Resolved here rather than at plugin load
304 + * (where the abilities registrar has to be) because its earliest hook
305 + * is `init`, which is what this method already runs on.
306 + */
307 + $this->container->get( MCPManager::class );
308 +
282 309 $this->container->get( ReportEmail::class );
283 310 // Usage-analytics collector. Registered here (runs on every request, incl.
284 311 // WP-Cron) rather than in Admin so its `betterdocs_insights_data` filter
285 312 // callback is present whenever the tracking payload is built.
@@ -292,8 +319,13 @@
292 319
293 320 $this->container->get( FrontEnd::class );
294 321 $this->container->get( SearchExtender::class );
295 322
323 + // Running logo header / page footer for the browser's own Print command.
324 + // Registered unconditionally: it covers every front-end page, including
325 + // the ones that carry no BetterDocs print button.
326 + $this->container->get( PrintTemplate::class );
327 +
296 328 /**
297 329 * Single-product FAQ rendering (WooCommerce). The class itself bails when
298 330 * WooCommerce is inactive or the feature is disabled.
299 331 */
@@ -382,8 +414,12 @@
382 414 wp_safe_redirect( add_query_arg( array( 'page' => 'betterdocs-settings' ), admin_url( 'admin.php' ) ) );
383 415 } else {
384 416 wp_safe_redirect( add_query_arg( array( 'page' => 'betterdocs-setup' ), admin_url( 'admin.php' ) ) );
385 417 }
418 + // This runs at `admin_init` priority 0. Without exiting, the rest of
419 + // admin_init still executes and can mutate the very state the redirect
420 + // target depends on before the browser ever follows the Location header.
421 + exit;
386 422 }
387 423 }
388 424
389 425 /**
@@ -421,8 +457,122 @@
421 457
422 458 return false;
423 459 }
424 460
461 + /**
462 + * Whether Pro provides the real API Documentation screen.
463 + *
464 + * Pro flips this via `betterdocs_pro_has_api_docs`; the class_exists() default
465 + * keeps the gate correct against a Pro build that predates that filter.
466 + *
467 + * @return bool
468 + */
469 + public function has_api_docs() {
470 + return (bool) apply_filters(
471 + 'betterdocs_pro_has_api_docs',
472 + class_exists( '\\WPDeveloper\\BetterDocsPro\\Core\\ApiReferences' )
473 + );
474 + }
475 +
476 + /**
477 + * Whether Free should show its locked API Docs teaser.
478 + *
479 + * Only without Pro. An older Pro has already paid, so they get nothing here —
480 + * they need a plugin update, not an upsell.
481 + *
482 + * @return bool
483 + */
484 + public function show_api_docs_teaser() {
485 + return ! $this->is_pro_active() && ! $this->has_api_docs();
486 + }
487 +
488 + /**
489 + * Whether the real Glossaries screen is available (i.e. Pro is providing it).
490 + *
491 + * Glossaries now lives in Pro (moved out of Free), so — like Content
492 + * Intelligence and API Docs — there is a concrete Pro class to detect. The
493 + * class_exists() default keeps the gate correct against a Pro build that
494 + * predates the move (that Pro is active but does NOT ship the manager, so
495 + * this must be false, not simply `is_pro_active()`); the filter lets Pro/add-ons
496 + * flip it explicitly.
497 + *
498 + * @return bool
499 + */
500 + public function has_glossaries() {
501 + return (bool) apply_filters(
502 + 'betterdocs_pro_has_glossaries',
503 + class_exists( '\\WPDeveloper\\BetterDocsPro\\Core\\GlossaryTaxonomy' )
504 + );
505 + }
506 +
507 + /**
508 + * Whether Free should render its locked Glossaries screen instead of the real
509 + * manager.
510 + *
511 + * True whenever the Pro manager is not available — i.e. Pro is off (a genuine
512 + * upsell) OR an older Pro that predates the glossaries move is active (it can no
513 + * longer provide the manager, so the slot must not be left blank). Only a Pro new
514 + * enough to ship GlossaryTaxonomy hides this and takes over the route.
515 + *
516 + * @return bool
517 + */
518 + public function show_glossary_teaser() {
519 + return ! $this->has_glossaries();
520 + }
521 +
522 + /**
523 + * Whether the locked Glossaries screen is showing because an *outdated* Pro
524 + * is active (as opposed to no Pro at all).
525 + *
526 + * Glossaries moved into Pro, so a Pro that predates the move is active but no
527 + * longer provides the manager. That user already owns Pro, so the locked
528 + * screen must ask them to UPDATE Pro rather than upsell "get Pro". Mirrors the
529 + * has_glossaries() capability check.
530 + *
531 + * @return bool
532 + */
533 + public function glossaries_needs_pro_update() {
534 + return $this->is_pro_active() && ! $this->has_glossaries();
535 + }
536 +
537 + /**
538 + * The BetterDocs Pro version that first ships the Glossaries manager, shown in
539 + * the "please update Pro" copy. Filterable so the target can move with Pro.
540 + *
541 + * @return string
542 + */
543 + public function glossaries_min_pro_version() {
544 + return (string) apply_filters( 'betterdocs_glossaries_min_pro_version', '4.3.1' );
545 + }
546 +
547 + /**
548 + * Whether Pro provides the real Content Intelligence screen.
549 + *
550 + * Pro flips this via `betterdocs_pro_has_content_intelligence`; the
551 + * class_exists() default keeps the gate correct against a Pro build that
552 + * predates that filter.
553 + *
554 + * @return bool
555 + */
556 + public function has_content_intelligence() {
557 + return (bool) apply_filters(
558 + 'betterdocs_pro_has_content_intelligence',
559 + class_exists( '\\WPDeveloper\\BetterDocsPro\\Core\\ContentIntelligenceService' )
560 + );
561 + }
562 +
563 + /**
564 + * Whether Free should show its locked Content Intelligence teaser.
565 + *
566 + * Only without Pro. An older Pro has already paid, so they get nothing here —
567 + * they need a plugin update, not an upsell.
568 + *
569 + * @return bool
570 + */
571 + public function show_content_intelligence_teaser() {
572 + return ! $this->is_pro_active() && ! $this->has_content_intelligence();
573 + }
574 +
425 575 public function pro_version() {
426 576 if ( ! $this->is_pro_active() ) {
427 577 return false;
428 578 }
@@ -474,12 +624,15 @@
474 624 'toplevel_page_betterdocs-admin',
475 625 'admin_page_betterdocs-admin',
476 626 'betterdocs_page_betterdocs-admin',
477 627 'betterdocs_page_betterdocs-analytics',
628 + 'betterdocs_page_betterdocs-content-iq',
478 629 'betterdocs_page_betterdocs-settings',
630 + 'betterdocs_page_betterdocs-mcp',
479 631 'betterdocs_page_betterdocs-faq',
480 632 'betterdocs_page_betterdocs-glossaries',
481 633 'betterdocs_page_betterdocs-ai-chatbot',
634 + 'betterdocs_page_betterdocs-api-docs',
482 635 'betterdocs_page_betterdocs-doc-categories',
483 636 'betterdocs_page_betterdocs-doc-tags',
484 637 ) );
485 638
@@ -499,12 +652,14 @@
499 652 'toplevel_page_betterdocs-dashboard',
500 653 'admin_page_betterdocs-admin',
501 654 'betterdocs_page_betterdocs-admin',
502 655 'betterdocs_page_betterdocs-settings',
656 + 'betterdocs_page_betterdocs-mcp',
503 657 'betterdocs_page_betterdocs-analytics',
504 658 'betterdocs_page_betterdocs-faq',
505 659 'betterdocs_page_betterdocs-glossaries',
506 660 'betterdocs_page_betterdocs-ai-chatbot',
661 + 'betterdocs_page_betterdocs-api-docs',
507 662 'betterdocs_page_betterdocs-doc-categories',
508 663 'betterdocs_page_betterdocs-doc-tags',
509 664 'edit-docs'
510 665 );