PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.8.1
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.8.1
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 3.5.1 3.5.2 All 199 releases
betterdocs / includes / Plugin.php

Plugin.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.8.1, at includes/Plugin.php

527 lines 16.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit; // Exit if accessed directly
7 }
8
9 use WPDeveloper\BetterDocs\Admin\Analytics;
10 use WPDeveloper\BetterDocs\Admin\Customizer\Customizer;
11 use WPDeveloper\BetterDocs\Admin\HelpScoutMigration;
12 use WPDeveloper\BetterDocs\Admin\ReportEmail;
13 use WPDeveloper\BetterDocs\Core\Admin;
14 use WPDeveloper\BetterDocs\Core\AnalyticsTracker;
15 use WPDeveloper\BetterDocs\Core\AnalyticsRetention;
16 use WPDeveloper\BetterDocs\Core\BaseAPI;
17 use WPDeveloper\BetterDocs\Core\Install;
18 use WPDeveloper\BetterDocs\Core\KBMigration;
19 use WPDeveloper\BetterDocs\Core\Query;
20 use WPDeveloper\BetterDocs\Core\Request;
21 use WPDeveloper\BetterDocs\Core\Rewrite;
22 use WPDeveloper\BetterDocs\Core\Roles;
23 use WPDeveloper\BetterDocs\Core\Scripts;
24 use WPDeveloper\BetterDocs\Core\Settings;
25 use WPDeveloper\BetterDocs\Core\ShortcodeFactory;
26 use WPDeveloper\BetterDocs\Core\WriteWithAI;
27 use WPDeveloper\BetterDocs\Core\ArticleSummary;
28 use WPDeveloper\BetterDocs\Core\ArticleQualityScore;
29 use WPDeveloper\BetterDocs\Core\UnifiedMetabox;
30 use WPDeveloper\BetterDocs\Core\DocsAISuite;
31 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
32 use WPDeveloper\BetterDocs\Dependencies\DI\ContainerBuilder;
33 use WPDeveloper\BetterDocs\Editors\Editor;
34 use WPDeveloper\BetterDocs\FrontEnd\FrontEnd;
35 use WPDeveloper\BetterDocs\FrontEnd\SearchExtender;
36 use WPDeveloper\BetterDocs\FrontEnd\TemplateTags;
37 use WPDeveloper\BetterDocs\FrontEnd\WooProductFAQ;
38 use WPDeveloper\BetterDocs\Modules\StyleHandler as ModulesStyleHandler;
39 use WPDeveloper\BetterDocs\Utils\Database;
40 use WPDeveloper\BetterDocs\Utils\Enqueue;
41 use WPDeveloper\BetterDocs\Utils\Helper;
42 use WPDeveloper\BetterDocs\Utils\Views;
43
44 final class Plugin {
45 private static $_instance = null;
46 /**
47 * Assets manager
48 *
49 * @var Enqueue
50 */
51 public $assets;
52 /**
53 * View manager
54 *
55 * @var Views
56 */
57 public $views;
58 /**
59 * Container Manager
60 *
61 * @var Container
62 */
63 public $container;
64 /**
65 * Editor Manager
66 * @var Editor
67 */
68 public $editor;
69 /**
70 * Helper class
71 * @var Helper
72 */
73 public $helper;
74 /**
75 * KBMigration class
76 * @var KBMigration
77 */
78 public $kbmigration;
79 /**
80 * KBMigration class
81 * @var Admin
82 */
83 public $admin;
84 /**
85 * Helper class
86 * @var Database
87 */
88 public $database;
89 /**
90 * Helper class
91 * @var Settings
92 */
93 public $settings;
94 /**
95 * Helper class
96 * @var TemplateTags
97 */
98 public $template_helper;
99 /**
100 * Article Summary class
101 * @var ArticleSummary
102 */
103 public $article_summary;
104 /**
105 * Customizer class
106 * @var Customizer
107 */
108 public $customizer;
109 /**
110 * Query class
111 * @var Query
112 */
113 public $query;
114 /**
115 * Rewrite Class
116 * @var Rewrite
117 */
118 public $rewrite;
119 /**
120 * Request Class
121 * @var Request
122 */
123 public $request;
124 /**
125 * Analytics Class
126 * @var Analytics
127 */
128 public $analytics;
129 /**
130 * Plugin Version
131 * @var string
132 */
133 public $version = '4.8.1';
134
135 /**
136 * WriteWithAI Class
137 * @var string
138 */
139 public $ai_autowrtie;
140 public $backgroundProccessor;
141
142 /**
143 * ArticleQualityScore Class
144 * @var ArticleQualityScore
145 */
146 public $article_quality_score;
147
148 /**
149 * Plugin DB Version
150 * @var string
151 */
152 public $db_version = '1.0.2';
153
154 public function __construct() {
155 $this->define_constants();
156
157 do_action( 'betterdocs_init_before' );
158
159 $this->setup_container();
160 /**
161 * Register activation and deactivation hooks
162 * and version updates check
163 */
164 $this->container->get( Install::class );
165
166 add_action( 'init', array( $this, 'initialize' ), 0 );
167
168 /**
169 * Initialize API
170 */
171 add_action( 'rest_api_init', array( $this, 'api_initialization' ) );
172
173 /**
174 * For admin only
175 */
176 add_action( 'admin_init', array( $this, 'admin_init' ), 0 );
177
178 /**
179 * For AJAX only
180 */
181 $this->ajax();
182
183 /**
184 * Style Handler For Parsing and Saving Styles as file.
185 */
186 ModulesStyleHandler::init();
187 }
188
189 private function define_constants() {
190 $this->define( 'BETTERDOCS_VERSION', $this->version );
191 $this->define( 'BETTERDOCS_DB_VERSION', $this->db_version );
192 $this->define( 'BETTERDOCS_ABSPATH', dirname( BETTERDOCS_PLUGIN_FILE ) . '/' );
193 $this->define( 'BETTERDOCS_ABSURL', plugin_dir_url( BETTERDOCS_PLUGIN_FILE ) );
194 $this->define( 'BETTERDOCS_PLUGIN_BASENAME', plugin_basename( BETTERDOCS_PLUGIN_FILE ) );
195 // Compiled block metadata lives under assets/build/ since the Node 24 asset
196 // restructure; register_block_type() fails silently if this path is wrong.
197 $this->define( 'BETTERDOCS_BLOCKS_DIRECTORY', BETTERDOCS_ABSPATH . 'assets/build/blocks/' );
198 $this->define( 'BETTERDOCS_ROOT_DIR_PATH', plugin_dir_path( BETTERDOCS_PLUGIN_FILE ) );
199 $this->define( 'BETTERDOCS_FSE_TEMPLATES_PATH', BETTERDOCS_ROOT_DIR_PATH . 'views/templates/fse' );
200
201 /**
202 * Third Party Constants
203 * @since 2.5.0
204 *
205 * WPML compatibility with Polylang
206 */
207 if ( Helper::is_plugin_active( 'polylang/polylang.php' ) ) {
208 // Polylang's documented integration constant — must use the upstream-defined name.
209 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
210 define( 'PLL_WPML_COMPAT', false );
211 }
212 }
213
214 /**
215 * Define constant if not already set.
216 *
217 * @param string $name Constant name.
218 * @param string|bool $value Constant value.
219 */
220 private function define( $name, $value ) {
221 if ( ! defined( $name ) ) {
222 // Caller passes plugin-prefixed names (BETTERDOCS_*); $name comes from a controlled internal allowlist.
223 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.VariableConstantNameFound
224 define( $name, $value );
225 }
226 }
227
228 public function setup_container() {
229 $config_array = require_once BETTERDOCS_ABSPATH . 'includes/config.php';
230 $config = apply_filters( 'betterdocs_container_config', $config_array );
231 $builder = new ContainerBuilder();
232
233 $builder->addDefinitions( $config );
234 $this->container = $builder->build();
235 }
236
237 public function initialize() {
238
239 /**
240 * Setup localization.
241 */
242 $this->load_plugin_textdomain();
243
244 $this->container->get( Scripts::class );
245 $this->rewrite = $this->container->get( Rewrite::class );
246 $this->request = $this->container->get( Request::class );
247 $this->query = $this->container->get( Query::class );
248
249 // Initialize background process
250 $this->backgroundProccessor = $this->container->get( HelpScoutMigration::class );
251
252 $this->rewrite->init();
253 $this->request->init();
254
255 $this->assets = $this->container->get( Enqueue::class );
256 $this->views = $this->container->get( Views::class );
257 $this->helper = $this->container->get( Helper::class );
258 $this->kbmigration = $this->container->get( KBMigration::class );
259 $this->admin = $this->container->get( Admin::class );
260 $this->database = $this->container->get( Database::class );
261 $this->settings = $this->container->get( Settings::class );
262 $this->analytics = $this->container->get( Analytics::class );
263 // Free analytics collectors: the frontend view/scroll tracker and the
264 // daily retention purge. Each self-registers its hooks on construct.
265 $this->container->get( AnalyticsTracker::class );
266 $this->container->get( AnalyticsRetention::class );
267
268 $this->template_helper = $this->container->get( TemplateTags::class );
269 $this->customizer = $this->container->get( Customizer::class );
270 $this->editor = $this->container->get( Editor::class );
271 $this->ai_autowrtie = $this->container->get( WriteWithAI::class );
272 $this->article_summary = $this->container->get( ArticleSummary::class );
273
274 // Initialize unified metabox before individual features
275 $this->container->get( UnifiedMetabox::class );
276 $this->article_quality_score = $this->container->get( ArticleQualityScore::class );
277
278 // Editor "Suggest Categories / Tags / Glossaries" AI actions on the docs
279 // post type (gated by enable_docs_ai_suite + an OpenAI key inside the class).
280 $this->container->get( DocsAISuite::class );
281
282 $this->container->get( Admin::class );
283 $this->container->get( Roles::class );
284 $this->container->get( ReportEmail::class );
285 // Usage-analytics collector. Registered here (runs on every request, incl.
286 // WP-Cron) rather than in Admin so its `betterdocs_insights_data` filter
287 // callback is present whenever the tracking payload is built.
288 $this->container->get( \WPDeveloper\BetterDocs\Insights\Collector::class );
289 /**
290 * Initialize Shortcode
291 * Make sure you have listed out all shortcode in shortcode factory.
292 */
293 $this->container->get( ShortcodeFactory::class )->init();
294
295 $this->container->get( FrontEnd::class );
296 $this->container->get( SearchExtender::class );
297
298 /**
299 * Single-product FAQ rendering (WooCommerce). The class itself bails when
300 * WooCommerce is inactive or the feature is disabled.
301 */
302 $this->container->get( WooProductFAQ::class );
303
304 do_action( 'betterdocs_init' );
305
306 $this->editor->init();
307 }
308
309 /**
310 * Load plugins textdomain `betterdocs` into actions.
311 * @return void
312 */
313 public function load_plugin_textdomain( $textdomain = 'betterdocs', $plugin_file = BETTERDOCS_PLUGIN_FILE ) {
314 $locale = determine_locale();
315
316 /**
317 * Filter to adjust the BetterDocs locale to use for translations.
318 */
319 // 'plugin_locale' is a WP-core filter; using its documented name is required.
320 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
321 $locale = apply_filters( 'plugin_locale', $locale, $textdomain );
322
323 if ( file_exists( WP_LANG_DIR . "/$textdomain-" . $locale . '.mo' ) ) {
324 unload_textdomain( $textdomain );
325 load_textdomain( $textdomain, WP_LANG_DIR . "/$textdomain-" . $locale . '.mo' );
326 }
327 }
328
329 /**
330 * For AJAX Only
331 * @return void
332 */
333 public function ajax() {
334 }
335
336 /**
337 * Create a plugin instance.
338 *
339 * @param mixed ...$args
340 *
341 * @return static
342 *
343 * @suppress PHP0441
344 * @since 2.5.0
345 */
346 public static function get_instance() {
347 if ( null == self::$_instance ) {
348 self::$_instance = new self();
349
350 do_action( 'betterdocs_loaded' );
351 }
352
353 return self::$_instance;
354 }
355
356 /**
357 * Hooked with `admin_init` action.
358 * @return void
359 */
360 public function admin_init() {
361 /**
362 * Maybe Redirect
363 * for setup related settings.
364 */
365 $this->maybe_redirect();
366 }
367
368 /**
369 * Summary of maybe_redirect
370 * @return void
371 */
372 public function maybe_redirect() {
373 // Bail if no activation transient is set.
374 if ( ! $this->database->get_transient( 'betterdocs_maybe_redirect' ) ) {
375 return;
376 }
377
378 // Delete the activation transient.
379 $this->database->delete_transient( 'betterdocs_maybe_redirect' );
380
381 if ( ! is_multisite() ) {
382 $betterdocs_settings = get_option( 'betterdocs_settings' );
383 if ( $betterdocs_settings ) {
384 wp_safe_redirect( add_query_arg( array( 'page' => 'betterdocs-settings' ), admin_url( 'admin.php' ) ) );
385 } else {
386 wp_safe_redirect( add_query_arg( array( 'page' => 'betterdocs-setup' ), admin_url( 'admin.php' ) ) );
387 }
388 // This runs at `admin_init` priority 0. Without exiting, the rest of
389 // admin_init still executes and can mutate the very state the redirect
390 // target depends on before the browser ever follows the Location header.
391 exit;
392 }
393 }
394
395 /**
396 * Is Pro Plugin Is Installed?
397 * @return bool
398 */
399 public function is_pro_installed() {
400 return $this->helper->get_plugins( 'betterdocs-pro/betterdocs-pro.php' );
401 }
402
403 /**
404 * Is Pro Plugin Is Active?
405 * @return bool
406 */
407 public function pro_file() {
408 return WP_PLUGIN_DIR . '/betterdocs-pro/betterdocs-pro.php';
409 }
410
411 public function is_pro_active() {
412 if ( file_exists( $this->pro_file() ) ) {
413 return $this->helper->is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' );
414 }
415
416 return false;
417 }
418
419 public function chatbot_file() {
420 return WP_PLUGIN_DIR . '/betterdocs-ai-chatbot/betterdocs-ai-chatbot.php';
421 }
422
423 public function is_chatbot_active() {
424 if ( file_exists( $this->chatbot_file() ) ) {
425 return $this->helper->is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' );
426 }
427
428 return false;
429 }
430
431 public function pro_version() {
432 if ( ! $this->is_pro_active() ) {
433 return false;
434 }
435
436 if ( ! function_exists( 'get_plugin_data' ) ) {
437 require_once ABSPATH . 'wp-admin/includes/plugin.php';
438 }
439
440 $plugin_data = get_plugin_data( $this->pro_file() );
441
442 return $plugin_data[ 'Version' ];
443 }
444
445 /**
446 * Get all the API initialized.
447 * @return void
448 */
449 public function api_initialization() {
450 $_api_classes = scandir( __DIR__ . DIRECTORY_SEPARATOR . 'REST' );
451
452 if ( ! empty( $_api_classes ) && is_array( $_api_classes ) ) {
453 foreach ( $_api_classes as $class ) {
454 if ( '.' == $class || '..' == $class || strpos( $class, '.' ) === 0 ) {
455 continue;
456 }
457
458 $classname = basename( $class, '.php' );
459 $classname = '\\' . __NAMESPACE__ . "\\REST\\$classname";
460 $_api_class = $this->container->get( $classname );
461
462 if ( $_api_class instanceof BaseAPI ) {
463 $_api_class->register();
464 }
465 }
466 }
467 }
468
469 public function is_betterdocs_screen( $hook, $admin_check = true ): bool {
470 /**
471 * Filter the list of admin screen hook suffixes treated as BetterDocs
472 * screens (controls whether the React admin bundle + styles load).
473 * Pro/add-ons can register their own React pages here, e.g. the
474 * Knowledge Base admin page.
475 *
476 * @param string[] $screens Full hook suffixes (e.g. betterdocs_page_betterdocs-foo).
477 */
478 $screens = apply_filters( 'betterdocs_admin_screens', array(
479 'toplevel_page_betterdocs-dashboard',
480 'toplevel_page_betterdocs-admin',
481 'admin_page_betterdocs-admin',
482 'betterdocs_page_betterdocs-admin',
483 'betterdocs_page_betterdocs-analytics',
484 'betterdocs_page_betterdocs-settings',
485 'betterdocs_page_betterdocs-faq',
486 'betterdocs_page_betterdocs-glossaries',
487 'betterdocs_page_betterdocs-ai-chatbot',
488 'betterdocs_page_betterdocs-doc-categories',
489 'betterdocs_page_betterdocs-doc-tags',
490 ) );
491
492 if ( $admin_check ) {
493 if ( in_array( $hook, $screens ) ) {
494 return true;
495 }
496
497 return false;
498 }
499
500 return false;
501 }
502
503 public function get_betterdocs_screen() {
504 $registered_screens = array(
505 'toplevel_page_betterdocs-dashboard',
506 'admin_page_betterdocs-admin',
507 'betterdocs_page_betterdocs-admin',
508 'betterdocs_page_betterdocs-settings',
509 'betterdocs_page_betterdocs-analytics',
510 'betterdocs_page_betterdocs-faq',
511 'betterdocs_page_betterdocs-glossaries',
512 'betterdocs_page_betterdocs-ai-chatbot',
513 'betterdocs_page_betterdocs-doc-categories',
514 'betterdocs_page_betterdocs-doc-tags',
515 'edit-docs'
516 );
517
518 $current_screen_id = get_current_screen() != null ? get_current_screen()->id : '';
519
520 if ( in_array( $current_screen_id, $registered_screens ) ) {
521 return $current_screen_id;
522 }
523
524 return false;
525 }
526 }
527