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

521 lines 16.1 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.7.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 $this->define( 'BETTERDOCS_BLOCKS_DIRECTORY', BETTERDOCS_ABSPATH . 'assets/blocks/' );
196 $this->define( 'BETTERDOCS_ROOT_DIR_PATH', plugin_dir_path( BETTERDOCS_PLUGIN_FILE ) );
197 $this->define( 'BETTERDOCS_FSE_TEMPLATES_PATH', BETTERDOCS_ROOT_DIR_PATH . 'views/templates/fse' );
198
199 /**
200 * Third Party Constants
201 * @since 2.5.0
202 *
203 * WPML compatibility with Polylang
204 */
205 if ( Helper::is_plugin_active( 'polylang/polylang.php' ) ) {
206 // Polylang's documented integration constant — must use the upstream-defined name.
207 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
208 define( 'PLL_WPML_COMPAT', false );
209 }
210 }
211
212 /**
213 * Define constant if not already set.
214 *
215 * @param string $name Constant name.
216 * @param string|bool $value Constant value.
217 */
218 private function define( $name, $value ) {
219 if ( ! defined( $name ) ) {
220 // Caller passes plugin-prefixed names (BETTERDOCS_*); $name comes from a controlled internal allowlist.
221 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.VariableConstantNameFound
222 define( $name, $value );
223 }
224 }
225
226 public function setup_container() {
227 $config_array = require_once BETTERDOCS_ABSPATH . 'includes/config.php';
228 $config = apply_filters( 'betterdocs_container_config', $config_array );
229 $builder = new ContainerBuilder();
230
231 $builder->addDefinitions( $config );
232 $this->container = $builder->build();
233 }
234
235 public function initialize() {
236
237 /**
238 * Setup localization.
239 */
240 $this->load_plugin_textdomain();
241
242 $this->container->get( Scripts::class );
243 $this->rewrite = $this->container->get( Rewrite::class );
244 $this->request = $this->container->get( Request::class );
245 $this->query = $this->container->get( Query::class );
246
247 // Initialize background process
248 $this->backgroundProccessor = $this->container->get( HelpScoutMigration::class );
249
250 $this->rewrite->init();
251 $this->request->init();
252
253 $this->assets = $this->container->get( Enqueue::class );
254 $this->views = $this->container->get( Views::class );
255 $this->helper = $this->container->get( Helper::class );
256 $this->kbmigration = $this->container->get( KBMigration::class );
257 $this->admin = $this->container->get( Admin::class );
258 $this->database = $this->container->get( Database::class );
259 $this->settings = $this->container->get( Settings::class );
260 $this->analytics = $this->container->get( Analytics::class );
261 // Free analytics collectors: the frontend view/scroll tracker and the
262 // daily retention purge. Each self-registers its hooks on construct.
263 $this->container->get( AnalyticsTracker::class );
264 $this->container->get( AnalyticsRetention::class );
265
266 $this->template_helper = $this->container->get( TemplateTags::class );
267 $this->customizer = $this->container->get( Customizer::class );
268 $this->editor = $this->container->get( Editor::class );
269 $this->ai_autowrtie = $this->container->get( WriteWithAI::class );
270 $this->article_summary = $this->container->get( ArticleSummary::class );
271
272 // Initialize unified metabox before individual features
273 $this->container->get( UnifiedMetabox::class );
274 $this->article_quality_score = $this->container->get( ArticleQualityScore::class );
275
276 // Editor "Suggest Categories / Tags / Glossaries" AI actions on the docs
277 // post type (gated by enable_docs_ai_suite + an OpenAI key inside the class).
278 $this->container->get( DocsAISuite::class );
279
280 $this->container->get( Admin::class );
281 $this->container->get( Roles::class );
282 $this->container->get( ReportEmail::class );
283 // Usage-analytics collector. Registered here (runs on every request, incl.
284 // WP-Cron) rather than in Admin so its `betterdocs_insights_data` filter
285 // callback is present whenever the tracking payload is built.
286 $this->container->get( \WPDeveloper\BetterDocs\Insights\Collector::class );
287 /**
288 * Initialize Shortcode
289 * Make sure you have listed out all shortcode in shortcode factory.
290 */
291 $this->container->get( ShortcodeFactory::class )->init();
292
293 $this->container->get( FrontEnd::class );
294 $this->container->get( SearchExtender::class );
295
296 /**
297 * Single-product FAQ rendering (WooCommerce). The class itself bails when
298 * WooCommerce is inactive or the feature is disabled.
299 */
300 $this->container->get( WooProductFAQ::class );
301
302 do_action( 'betterdocs_init' );
303
304 $this->editor->init();
305 }
306
307 /**
308 * Load plugins textdomain `betterdocs` into actions.
309 * @return void
310 */
311 public function load_plugin_textdomain( $textdomain = 'betterdocs', $plugin_file = BETTERDOCS_PLUGIN_FILE ) {
312 $locale = determine_locale();
313
314 /**
315 * Filter to adjust the BetterDocs locale to use for translations.
316 */
317 // 'plugin_locale' is a WP-core filter; using its documented name is required.
318 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
319 $locale = apply_filters( 'plugin_locale', $locale, $textdomain );
320
321 if ( file_exists( WP_LANG_DIR . "/$textdomain-" . $locale . '.mo' ) ) {
322 unload_textdomain( $textdomain );
323 load_textdomain( $textdomain, WP_LANG_DIR . "/$textdomain-" . $locale . '.mo' );
324 }
325 }
326
327 /**
328 * For AJAX Only
329 * @return void
330 */
331 public function ajax() {
332 }
333
334 /**
335 * Create a plugin instance.
336 *
337 * @param mixed ...$args
338 *
339 * @return static
340 *
341 * @suppress PHP0441
342 * @since 2.5.0
343 */
344 public static function get_instance() {
345 if ( null == self::$_instance ) {
346 self::$_instance = new self();
347
348 do_action( 'betterdocs_loaded' );
349 }
350
351 return self::$_instance;
352 }
353
354 /**
355 * Hooked with `admin_init` action.
356 * @return void
357 */
358 public function admin_init() {
359 /**
360 * Maybe Redirect
361 * for setup related settings.
362 */
363 $this->maybe_redirect();
364 }
365
366 /**
367 * Summary of maybe_redirect
368 * @return void
369 */
370 public function maybe_redirect() {
371 // Bail if no activation transient is set.
372 if ( ! $this->database->get_transient( 'betterdocs_maybe_redirect' ) ) {
373 return;
374 }
375
376 // Delete the activation transient.
377 $this->database->delete_transient( 'betterdocs_maybe_redirect' );
378
379 if ( ! is_multisite() ) {
380 $betterdocs_settings = get_option( 'betterdocs_settings' );
381 if ( $betterdocs_settings ) {
382 wp_safe_redirect( add_query_arg( array( 'page' => 'betterdocs-settings' ), admin_url( 'admin.php' ) ) );
383 } else {
384 wp_safe_redirect( add_query_arg( array( 'page' => 'betterdocs-setup' ), admin_url( 'admin.php' ) ) );
385 }
386 }
387 }
388
389 /**
390 * Is Pro Plugin Is Installed?
391 * @return bool
392 */
393 public function is_pro_installed() {
394 return $this->helper->get_plugins( 'betterdocs-pro/betterdocs-pro.php' );
395 }
396
397 /**
398 * Is Pro Plugin Is Active?
399 * @return bool
400 */
401 public function pro_file() {
402 return WP_PLUGIN_DIR . '/betterdocs-pro/betterdocs-pro.php';
403 }
404
405 public function is_pro_active() {
406 if ( file_exists( $this->pro_file() ) ) {
407 return $this->helper->is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' );
408 }
409
410 return false;
411 }
412
413 public function chatbot_file() {
414 return WP_PLUGIN_DIR . '/betterdocs-ai-chatbot/betterdocs-ai-chatbot.php';
415 }
416
417 public function is_chatbot_active() {
418 if ( file_exists( $this->chatbot_file() ) ) {
419 return $this->helper->is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' );
420 }
421
422 return false;
423 }
424
425 public function pro_version() {
426 if ( ! $this->is_pro_active() ) {
427 return false;
428 }
429
430 if ( ! function_exists( 'get_plugin_data' ) ) {
431 require_once ABSPATH . 'wp-admin/includes/plugin.php';
432 }
433
434 $plugin_data = get_plugin_data( $this->pro_file() );
435
436 return $plugin_data[ 'Version' ];
437 }
438
439 /**
440 * Get all the API initialized.
441 * @return void
442 */
443 public function api_initialization() {
444 $_api_classes = scandir( __DIR__ . DIRECTORY_SEPARATOR . 'REST' );
445
446 if ( ! empty( $_api_classes ) && is_array( $_api_classes ) ) {
447 foreach ( $_api_classes as $class ) {
448 if ( '.' == $class || '..' == $class || strpos( $class, '.' ) === 0 ) {
449 continue;
450 }
451
452 $classname = basename( $class, '.php' );
453 $classname = '\\' . __NAMESPACE__ . "\\REST\\$classname";
454 $_api_class = $this->container->get( $classname );
455
456 if ( $_api_class instanceof BaseAPI ) {
457 $_api_class->register();
458 }
459 }
460 }
461 }
462
463 public function is_betterdocs_screen( $hook, $admin_check = true ): bool {
464 /**
465 * Filter the list of admin screen hook suffixes treated as BetterDocs
466 * screens (controls whether the React admin bundle + styles load).
467 * Pro/add-ons can register their own React pages here, e.g. the
468 * Knowledge Base admin page.
469 *
470 * @param string[] $screens Full hook suffixes (e.g. betterdocs_page_betterdocs-foo).
471 */
472 $screens = apply_filters( 'betterdocs_admin_screens', array(
473 'toplevel_page_betterdocs-dashboard',
474 'toplevel_page_betterdocs-admin',
475 'admin_page_betterdocs-admin',
476 'betterdocs_page_betterdocs-admin',
477 'betterdocs_page_betterdocs-analytics',
478 'betterdocs_page_betterdocs-settings',
479 'betterdocs_page_betterdocs-faq',
480 'betterdocs_page_betterdocs-glossaries',
481 'betterdocs_page_betterdocs-ai-chatbot',
482 'betterdocs_page_betterdocs-doc-categories',
483 'betterdocs_page_betterdocs-doc-tags',
484 ) );
485
486 if ( $admin_check ) {
487 if ( in_array( $hook, $screens ) ) {
488 return true;
489 }
490
491 return false;
492 }
493
494 return false;
495 }
496
497 public function get_betterdocs_screen() {
498 $registered_screens = array(
499 'toplevel_page_betterdocs-dashboard',
500 'admin_page_betterdocs-admin',
501 'betterdocs_page_betterdocs-admin',
502 'betterdocs_page_betterdocs-settings',
503 'betterdocs_page_betterdocs-analytics',
504 'betterdocs_page_betterdocs-faq',
505 'betterdocs_page_betterdocs-glossaries',
506 'betterdocs_page_betterdocs-ai-chatbot',
507 'betterdocs_page_betterdocs-doc-categories',
508 'betterdocs_page_betterdocs-doc-tags',
509 'edit-docs'
510 );
511
512 $current_screen_id = get_current_screen() != null ? get_current_screen()->id : '';
513
514 if ( in_array( $current_screen_id, $registered_screens ) ) {
515 return $current_screen_id;
516 }
517
518 return false;
519 }
520 }
521