PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.8.0
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.8.0
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.0, at includes/Plugin.php

523 lines 16.2 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.0';
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 }
389 }
390
391 /**
392 * Is Pro Plugin Is Installed?
393 * @return bool
394 */
395 public function is_pro_installed() {
396 return $this->helper->get_plugins( 'betterdocs-pro/betterdocs-pro.php' );
397 }
398
399 /**
400 * Is Pro Plugin Is Active?
401 * @return bool
402 */
403 public function pro_file() {
404 return WP_PLUGIN_DIR . '/betterdocs-pro/betterdocs-pro.php';
405 }
406
407 public function is_pro_active() {
408 if ( file_exists( $this->pro_file() ) ) {
409 return $this->helper->is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' );
410 }
411
412 return false;
413 }
414
415 public function chatbot_file() {
416 return WP_PLUGIN_DIR . '/betterdocs-ai-chatbot/betterdocs-ai-chatbot.php';
417 }
418
419 public function is_chatbot_active() {
420 if ( file_exists( $this->chatbot_file() ) ) {
421 return $this->helper->is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' );
422 }
423
424 return false;
425 }
426
427 public function pro_version() {
428 if ( ! $this->is_pro_active() ) {
429 return false;
430 }
431
432 if ( ! function_exists( 'get_plugin_data' ) ) {
433 require_once ABSPATH . 'wp-admin/includes/plugin.php';
434 }
435
436 $plugin_data = get_plugin_data( $this->pro_file() );
437
438 return $plugin_data[ 'Version' ];
439 }
440
441 /**
442 * Get all the API initialized.
443 * @return void
444 */
445 public function api_initialization() {
446 $_api_classes = scandir( __DIR__ . DIRECTORY_SEPARATOR . 'REST' );
447
448 if ( ! empty( $_api_classes ) && is_array( $_api_classes ) ) {
449 foreach ( $_api_classes as $class ) {
450 if ( '.' == $class || '..' == $class || strpos( $class, '.' ) === 0 ) {
451 continue;
452 }
453
454 $classname = basename( $class, '.php' );
455 $classname = '\\' . __NAMESPACE__ . "\\REST\\$classname";
456 $_api_class = $this->container->get( $classname );
457
458 if ( $_api_class instanceof BaseAPI ) {
459 $_api_class->register();
460 }
461 }
462 }
463 }
464
465 public function is_betterdocs_screen( $hook, $admin_check = true ): bool {
466 /**
467 * Filter the list of admin screen hook suffixes treated as BetterDocs
468 * screens (controls whether the React admin bundle + styles load).
469 * Pro/add-ons can register their own React pages here, e.g. the
470 * Knowledge Base admin page.
471 *
472 * @param string[] $screens Full hook suffixes (e.g. betterdocs_page_betterdocs-foo).
473 */
474 $screens = apply_filters( 'betterdocs_admin_screens', array(
475 'toplevel_page_betterdocs-dashboard',
476 'toplevel_page_betterdocs-admin',
477 'admin_page_betterdocs-admin',
478 'betterdocs_page_betterdocs-admin',
479 'betterdocs_page_betterdocs-analytics',
480 'betterdocs_page_betterdocs-settings',
481 'betterdocs_page_betterdocs-faq',
482 'betterdocs_page_betterdocs-glossaries',
483 'betterdocs_page_betterdocs-ai-chatbot',
484 'betterdocs_page_betterdocs-doc-categories',
485 'betterdocs_page_betterdocs-doc-tags',
486 ) );
487
488 if ( $admin_check ) {
489 if ( in_array( $hook, $screens ) ) {
490 return true;
491 }
492
493 return false;
494 }
495
496 return false;
497 }
498
499 public function get_betterdocs_screen() {
500 $registered_screens = array(
501 'toplevel_page_betterdocs-dashboard',
502 'admin_page_betterdocs-admin',
503 'betterdocs_page_betterdocs-admin',
504 'betterdocs_page_betterdocs-settings',
505 'betterdocs_page_betterdocs-analytics',
506 'betterdocs_page_betterdocs-faq',
507 'betterdocs_page_betterdocs-glossaries',
508 'betterdocs_page_betterdocs-ai-chatbot',
509 'betterdocs_page_betterdocs-doc-categories',
510 'betterdocs_page_betterdocs-doc-tags',
511 'edit-docs'
512 );
513
514 $current_screen_id = get_current_screen() != null ? get_current_screen()->id : '';
515
516 if ( in_array( $current_screen_id, $registered_screens ) ) {
517 return $current_screen_id;
518 }
519
520 return false;
521 }
522 }
523