PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.8.9
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.8.9
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 / Core / Admin.php

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

1,045 lines 34.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\Core;
4
5 use Exception;
6 use PriyoMukul\WPNotice\Notices;
7 use WPDeveloper\BetterDocs\Utils\Base;
8 use PriyoMukul\WPNotice\Utils\CacheBank;
9 use WPDeveloper\BetterDocs\Utils\Helper;
10 use WPDeveloper\BetterDocs\Utils\Enqueue;
11 use WPDeveloper\BetterDocs\Utils\Insights;
12 use PriyoMukul\WPNotice\Utils\NoticeRemover;
13 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
14
15 class Admin extends Base {
16 /**
17 * @var CacheBank
18 */
19 private static $cache_bank;
20 /**
21 * Admin Root Menu Slug
22 * @var string
23 */
24 private $slug = 'betterdocs-admin';
25 /**
26 * Insights
27 *
28 * @var Insights
29 */
30 private $insights = null;
31
32 /**
33 * DI\Container
34 *
35 * @var Container
36 */
37 private $container;
38
39 /**
40 * Database Wrapper
41 *
42 * @var Settings
43 */
44 private $settings;
45
46 /**
47 * KBMigration
48 *
49 * @var KBMigration
50 */
51 private $kbmigration;
52
53 /**
54 * Enqueue
55 * @var Enqueue
56 */
57 private $assets;
58
59 /**
60 * FAQBuilder
61 * @var FAQBuilder
62 */
63 private $faq_builder;
64 private $glossaries;
65
66 public function __construct( Container $container, PostType $type, Enqueue $assets, Settings $settings, KBMigration $kbmigration ) {
67 $this->container = $container;
68 $this->assets = $assets;
69 $this->settings = $settings;
70 $this->kbmigration = $kbmigration;
71 $this->slug = 'betterdocs-admin';
72
73 add_action( 'init', [ $type, 'register' ], 9 );
74
75 $type->init();
76 $type->admin_init();
77
78 $this->faq_builder = $this->container->get( FAQBuilder::class );
79 $this->glossaries = $this->container->get( Glossaries::class );
80
81 if ( ! is_admin() ) {
82 return;
83 }
84
85 $this->plugin_insights();
86 add_action( 'admin_notices', [ $this, 'compatibility_notices' ] );
87 // add_action( 'admin_init', [$this, 'notices'], 9 );
88 add_filter( 'admin_init', [ $this, 'save_admin_page' ], 99 );
89
90 add_action( 'admin_menu', [ $this, 'menus' ] );
91 add_action( 'admin_menu', [ $this, 'reset_submenu' ] );
92 add_action( 'admin_head', [ $this, 'add_custom_classes_to_menu_items' ] );
93 add_filter( 'plugin_action_links_' . BETTERDOCS_PLUGIN_BASENAME, [ $this, 'insert_plugin_links' ] );
94
95 // $this->container->get( SetupWizard::class )->init();
96
97 add_action( 'admin_enqueue_scripts', [ $this, 'styles' ] );
98 add_action( 'admin_enqueue_scripts', [ $this, 'scripts' ] );
99 //add_action( 'betterdocs_listing_header', [ $this, 'header' ], 10, 1 );
100 add_action( 'admin_bar_menu', [ $this, 'toolbar_menu' ], 32 );
101
102 add_filter( 'admin_body_class', [ $this, 'body_classes' ] );
103 add_filter( 'parent_file', [ $type, 'highlight_admin_menu' ] );
104 add_filter( 'submenu_file', [ $type, 'highlight_admin_submenu' ], 10, 2 );
105 add_filter( 'betterdocs_admin_menu', [ $this, 'quick_setup_menu' ], 10, 1 );
106
107 /**
108 * Remove Comments Column from List Table.
109 */
110 add_filter( 'manage_docs_posts_columns', [ $this, 'set_custom_edit_action_columns' ] );
111 add_filter( 'manage_docs_posts_custom_column', [ $this, 'manage_custom_columns' ], 10, 2 );
112
113 /**
114 * Add New Column
115 */
116 add_filter( 'manage_users_columns', [ $this, 'add_users_total_docs_column' ], 10, 1 );
117 add_filter( 'manage_users_custom_column', [ $this, 'popular_users_docs_data' ], 10, 3 );
118 if ( is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ) ) {
119 add_action( 'admin_footer-plugins.php', array( $this, 'disable_deactivation' ) );
120 }
121
122 if ( $this->settings->get( 'enable_estimated_reading_time' ) ) {
123 add_action( 'add_meta_boxes', [ $this, 'reading_meta_box_' ], 10 );
124 }
125
126 self::$cache_bank = CacheBank::get_instance();
127
128 // Remove OLD notice from 1.0.0 (if other WPDeveloper plugin has notice)
129 NoticeRemover::get_instance( '1.0.0' );
130
131 try {
132 $this->notices();
133 } catch ( Exception $e ) {
134 unset( $e );
135 }
136 }
137
138 public function disable_deactivation() {
139 $tooltip_text = esc_html__( 'Deactivate BetterDocs Pro First', 'betterdocs' );
140 ?>
141 <style type="text/css">
142 #deactivate-betterdocs {
143 color: #cccccc;
144 position: relative;
145 }
146
147 /* Tooltip styling */
148 #deactivate-betterdocs[title]:hover::after {
149 content: attr(title);
150 position: absolute;
151 bottom: 100%;
152 left: 50%;
153 transform: translateX(-50%);
154 background-color: #333;
155 color: #fff;
156 padding: 5px 10px;
157 border-radius: 4px;
158 font-size: 12px;
159 white-space: nowrap;
160 box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2);
161 z-index: 10;
162 }
163 #deactivate-betterdocs:focus {
164 box-shadow: none;
165 outline: none;
166 }
167 </style>
168 <script type="text/javascript">
169 jQuery(document).ready(function($) {
170 // Disable the default action and add class with tooltip by default
171 const tooltipText = "<?php echo esc_attr( $tooltip_text ); ?>";
172 $("#deactivate-betterdocs")
173 .addClass("disabled-tooltip")
174 .attr("title", tooltipText)
175 .on("click", function(e) {
176 e.preventDefault(); // Prevent any action on click
177 });
178 });
179 </script>
180 <?php
181 }
182
183 public function add_users_total_docs_column( $columns ) {
184 $new_column = [
185 'docs' => __( 'Docs', 'betterdocs' )
186 ];
187 $columns = array_merge( $columns, $new_column );
188 return $columns;
189 }
190
191 public function popular_users_docs_data( $output, $column_name, $user_id ) {
192 if ( $column_name == 'docs' ) {
193 $total_count = count_user_posts( $user_id, 'docs', true );
194 return '<a href="edit.php?post_type=docs&author=' . $user_id . '" class="edit"><span aria-hidden="true">' . $total_count . '</span></a>';
195 }
196 return $output;
197 }
198
199 public function reading_meta_box_() {
200 add_meta_box(
201 'betterdocs_estimated_time_metabox',
202 __( 'Estimated Reading Time', 'betterdocs' ),
203 [
204 $this,
205 'render_estimated_time_markup'
206 ],
207 'docs'
208 );
209 }
210
211 public function render_estimated_time_markup() {
212 betterdocs()->views->get( 'admin/metabox/estimated-reading-box' );
213 }
214
215 public function compatibility_notices() {
216 if ( betterdocs()->is_pro_active() ) {
217 $plugins = Helper::get_plugins();
218 $plugin_data = $plugins['betterdocs-pro/betterdocs-pro.php'];
219
220 if ( isset( $plugin_data['Version'] ) && version_compare( $plugin_data['Version'], '2.5.0', '>=' ) ) {
221 return;
222 }
223
224 betterdocs()->views->get( 'admin/notices/compatibility', [ 'version' => $plugin_data['Version'] ] );
225 }
226 }
227
228 public function plugin_insights( $prevent_init = false ) {
229 $this->insights = Insights::get_instance(
230 BETTERDOCS_PLUGIN_FILE,
231 [
232 'opt_in' => true,
233 'goodbye_form' => true,
234 'item_id' => 'c7b16777b4f1b83f6083'
235 ]
236 );
237
238 $this->insights->set_notice_options(
239 [
240 'notice' => __( 'Want to help make <strong>BetterDocs</strong> even more awesome? You can get a <strong>10% discount coupon</strong> for Premium extensions if you allow us to track the usage.', 'betterdocs' ),
241 'extra_notice' => __( 'We collect non-sensitive diagnostic data and plugin usage information. Your site URL, WordPress & PHP version, plugins & themes and email address to send you the discount coupon. This data lets us make sure this plugin always stays compatible with the most popular plugins and themes. No spam, I promise.', 'betterdocs' )
242 ]
243 );
244
245 if ( ! $prevent_init ) {
246 $this->insights->init();
247 }
248
249 return $this->insights;
250 }
251
252 /**
253 * Admin notices for Review and others.
254 *
255 * @return void
256 * @throws Exception
257 */
258 public function notices() {
259 $notices = new Notices(
260 [
261 'id' => 'betterdocs',
262 'storage_key' => 'notices',
263 'lifetime' => 3,
264 'stylesheet_url' => $this->assets->asset_url( 'admin/css/notices.css' ),
265 'styles' => $this->assets->asset_url( 'admin/css/notices.css' ),
266 'priority' => 4
267 ]
268 );
269
270 /**
271 * Review Notice
272 * @var mixed $message
273 */
274
275 $message = __( 'We hope you\'re enjoying BetterDocs! Could you please do us a BIG favor and give it a 5-star rating on WordPress to help us spread the word and boost our motivation?', 'betterdocs' );
276
277 $_review_notice = [
278 'thumbnail' => $this->assets->icon( 'betterdocs-logo.svg', true ),
279 'html' => '<p>' . $message . '</p>',
280 'links' => [
281 'later' => [
282 'link' => 'https://wordpress.org/plugins/betterdocs/#reviews',
283 'target' => '_blank',
284 'label' => __( 'Sure, you deserve it!', 'betterdocs' ),
285 'icon_class' => 'dashicons dashicons-external'
286 ],
287 'allready' => [
288 'label' => __( 'I already did', 'betterdocs' ),
289 'icon_class' => 'dashicons dashicons-smiley',
290 'attributes' => [
291 'data-dismiss' => true
292 ]
293 ],
294 'maybe_later' => [
295 'label' => __( 'Maybe Later', 'betterdocs' ),
296 'icon_class' => 'dashicons dashicons-calendar-alt',
297 'attributes' => [
298 'data-later' => true,
299 'class' => 'dismiss-btn'
300 ]
301 ],
302 'support' => [
303 'link' => 'https://wpdeveloper.com/support',
304 'attributes' => [
305 'target' => '_blank'
306 ],
307 'label' => __( 'I need help', 'betterdocs' ),
308 'icon_class' => 'dashicons dashicons-sos'
309 ],
310 'never_show_again' => [
311 'label' => __( 'Never show again', 'betterdocs' ),
312 'icon_class' => 'dashicons dashicons-dismiss',
313 'attributes' => [
314 'data-dismiss' => true
315 ]
316 ]
317 ]
318 ];
319
320 $notices->add(
321 'review',
322 $_review_notice,
323 [
324 'start' => $notices->strtotime( '+10 days' ),
325 'recurrence' => 30,
326 'dismissible' => true
327 ]
328 );
329
330 if ( $this->kbmigration->existing_plugins && ! in_array( $this->kbmigration->existing_plugins[0][0], $this->kbmigration->migrated_plugins ) ) {
331 $plugin_name = '<strong>' . esc_html( $this->kbmigration->existing_plugins[0][1] ) . '</strong>';
332
333 $message = sprintf(
334 /* translators: %s is the name of the existing knowledge base plugin. */
335 __( 'Already using %s? Power up your Knowledge Base by migrating all your docs and settings to BetterDocs with just 1 click.', 'betterdocs' ),
336 esc_html( $plugin_name )
337 );
338
339 $migration_message = sprintf(
340 '<p class="migration-message">%s</p><a class="button button-primary betterdocs-migration-notice" href="%s">%s</a>',
341 $message,
342 esc_url( admin_url( 'admin.php?page=betterdocs-settings&tab=tab-migration' ) ),
343 esc_html__( 'Start Migration', 'betterdocs' )
344 );
345
346 $_migration_notice = [
347 'thumbnail' => '',
348 'html' => $migration_message,
349 'links' => [
350 'maybe_later' => [
351 'label' => __( 'Maybe Later', 'betterdocs' ),
352 'icon_class' => 'dashicons dashicons-calendar-alt',
353 'attributes' => [
354 'data-later' => true,
355 'class' => 'dismiss-btn'
356 ]
357 ],
358 'never_show_again' => [
359 'label' => __( 'Never show again', 'betterdocs' ),
360 'icon_class' => 'dashicons dashicons-dismiss',
361 'attributes' => [
362 'data-dismiss' => true
363 ]
364 ]
365 ]
366 ];
367
368 $notices->add(
369 'migration',
370 $_migration_notice,
371 [
372 'start' => $notices->time(),
373 'recurrence' => false,
374 'dismissible' => true
375 ]
376 );
377 }
378
379 /**
380 * Opt-In Notice
381 */
382 $allow_tracking = get_option( 'wpins_allow_tracking' );
383 if ( $this->insights != null && ! isset( $allow_tracking['betterdocs'] ) ) {
384 $notices->add(
385 'opt_in',
386 [ $this->insights, 'notice' ],
387 [
388 'classes' => 'updated put-dismiss-notice',
389 'start' => $notices->time(),
390 'refresh' => BETTERDOCS_VERSION,
391 'dismissible' => true,
392 'do_action' => 'wpdeveloper_notice_clicked_for_betterdocs',
393 'display_if' => ! function_exists( 'betterdocs_pro' ),
394 'screens' => [ 'dashboard' ]
395 ]
396 );
397 }
398
399 self::$cache_bank->create_account( $notices );
400 self::$cache_bank->calculate_deposits( $notices );
401 if ( method_exists( self::$cache_bank, 'clear_notices_in_' ) ) {
402 self::$cache_bank->clear_notices_in_(
403 [
404 'toplevel_page_betterdocs-admin',
405 'betterdocs_page_betterdocs-admin',
406 'betterdocs_page_betterdocs-settings',
407 'betterdocs_page_betterdocs-faq',
408 'betterdocs_page_betterdocs-analytics',
409 'betterdocs_page_betterdocs-glossaries',
410 'betterdocs_page_betterdocs-ai-chatbot',
411 'edit-doc_category',
412 'edit-doc_tag'
413 ],
414 $notices,
415 true
416 );
417 }
418 }
419
420 public function body_classes( $classes ) {
421 $saved_settings = get_option( 'betterdocs_settings', false );
422 $dark_mode = isset( $saved_settings['dark_mode'] ) ? $saved_settings['dark_mode'] : false;
423 $dark_mode = ! empty( $dark_mode ) ? boolval( $dark_mode ) : false;
424 $current_screen_id = get_current_screen() != null ? str_replace( 'betterdocs_page_', '', str_replace( 'toplevel_page_', '', get_current_screen()->id ) ) : '';
425 $registered_screens = [
426 'betterdocs-settings',
427 'betterdocs-admin',
428 'betterdocs-analytics',
429 'betterdocs-glossaries',
430 'betterdocs-faq',
431 'edit-doc_category',
432 'edit-doc_tag',
433 'edit-knowledge_base',
434 'betterdocs-ai-chatbot'
435 ];
436
437 if( in_array( $current_screen_id, $registered_screens ) ) {
438 $classes .= ' betterdocs-admin ';
439 }
440
441 if ( $dark_mode === true && in_array( $current_screen_id, $registered_screens ) ) {
442 $classes .= ' betterdocs-dark-mode ';
443 }
444
445 return $classes;
446 }
447
448 /**
449 * Remove Comments Column From List Table
450 *
451 * @param array $columns
452 *
453 * @return array
454 * @since 1.0.0
455 */
456 public function set_custom_edit_action_columns( $columns ) {
457 unset( $columns['comments'] );
458 $new_columns = [];
459 foreach ( $columns as $key => $value ) {
460 if ( $key == 'date' ) {
461 $new_columns['betterdocs_word_count'] = __( 'Word Count', 'betterdocs' ); // put the tags column before it
462 $new_columns['betterdocs_reaction'] = __( 'Reactions', 'betterdocs' );
463 }
464 $new_columns[ $key ] = $value;
465 }
466
467 return $new_columns;
468 }
469
470 public function manage_custom_columns( $column, $post_id ) {
471 global $wpdb;
472 switch ( $column ) {
473 case 'betterdocs_word_count':
474 $content_without_html_tags = trim( strip_tags( get_post_field( 'post_content', $post_id ) ) );
475 preg_match_all( '/<[^>]*>|[\p{L}\p{M}]+/u', $content_without_html_tags, $matches );
476 $total_words = ! empty( $matches[0] ) ? count( $matches[0] ) : count( [] );
477 $word_count = $total_words;
478 echo '<span>' . esc_html( intval( $word_count ) ) . '</span>';
479 break;
480 case 'betterdocs_reaction':
481 $where = "WHERE post_id='" . esc_sql( $post_id ) . "'";
482 $analytics = $wpdb->get_results(
483 "SELECT
484 sum(impressions) as totalViews,
485 sum(unique_visit) as totalUniqueViews,
486 sum(happy + sad + normal) as totalReactions,
487 sum(happy) as totalHappy,
488 sum(normal) as totalNormal,
489 sum(sad) as totalSad
490 FROM {$wpdb->prefix}betterdocs_analytics
491 $where"
492 );
493
494 echo '<ul class="reactions-count">
495 <li>
496 <a title="happy" class="betterdocs-feelings happy" data-feelings="happy" href="#">
497 <svg width="15" height="15" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
498 <path class="st0" d="M10,0.1c-5.4,0-9.9,4.4-9.9,9.8c0,5.4,4.4,9.9,9.8,9.9c5.4,0,9.9-4.4,9.9-9.8C19.9,4.5,15.4,0.1,10,0.1z
499 M13.3,6.4c0.8,0,1.5,0.7,1.5,1.5c0,0.8-0.7,1.5-1.5,1.5c-0.8,0-1.5-0.7-1.5-1.5C11.8,7.1,12.5,6.4,13.3,6.4z M6.7,6.4
500 c0.8,0,1.5,0.7,1.5,1.5c0,0.8-0.7,1.5-1.5,1.5c-0.8,0-1.5-0.7-1.5-1.5C5.2,7.1,5.9,6.4,6.7,6.4z M10,16.1c-2.6,0-4.9-1.6-5.8-4
501 l1.2-0.4c0.7,1.9,2.5,3.2,4.6,3.2s3.9-1.3,4.6-3.2l1.2,0.4C14.9,14.5,12.6,16.1,10,16.1z" />
502 <path class="st1" d="M-6.6-119.7c-7.1,0-12.9,5.8-12.9,12.9s5.8,12.9,12.9,12.9s12.9-5.8,12.9-12.9S0.6-119.7-6.6-119.7z
503 M-2.3-111.4c1.1,0,2,0.9,2,2c0,1.1-0.9,2-2,2c-1.1,0-2-0.9-2-2C-4.3-110.5-3.4-111.4-2.3-111.4z M-10.9-111.4c1.1,0,2,0.9,2,2
504 c0,1.1-0.9,2-2,2c-1.1,0-2-0.9-2-2C-12.9-110.5-12-111.4-10.9-111.4z M-6.6-98.7c-3.4,0-6.4-2.1-7.6-5.3l1.6-0.6
505 c0.9,2.5,3.3,4.2,6,4.2s5.1-1.7,6-4.2L1-104C-0.1-100.8-3.2-98.7-6.6-98.7z" />
506 </svg>
507 <span>' . esc_html( ( intval( $analytics[0]->totalHappy ) !== null ? intval( $analytics[0]->totalHappy ) : 0 ) ) . '</span>
508 </a>
509 </li>
510 <li>
511 <a title="normal" class="betterdocs-feelings normal" data-feelings="normal" href="#">
512 <svg width="15" height="15" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
513 <path class="st0" d="M10,0.2c-5.4,0-9.8,4.4-9.8,9.8s4.4,9.8,9.8,9.8s9.8-4.4,9.8-9.8S15.4,0.2,10,0.2z M6.7,6.5
514 c0.8,0,1.5,0.7,1.5,1.5c0,0.8-0.7,1.5-1.5,1.5C5.9,9.5,5.2,8.9,5.2,8C5.2,7.2,5.9,6.5,6.7,6.5z M14.2,14.3H5.9
515 c-0.3,0-0.6-0.3-0.6-0.6c0-0.3,0.3-0.6,0.6-0.6h8.3c0.3,0,0.6,0.3,0.6,0.6C14.8,14,14.5,14.3,14.2,14.3z M13.3,9.5
516 c-0.8,0-1.5-0.7-1.5-1.5c0-0.8,0.7-1.5,1.5-1.5c0.8,0,1.5,0.7,1.5,1.5C14.8,8.9,14.1,9.5,13.3,9.5z" />
517 </svg>
518 <span>' . esc_html( ( intval( $analytics[0]->totalNormal ) !== null ? intval( $analytics[0]->totalNormal ) : 0 ) ) . '</span>
519 </a>
520 </li>
521 <li>
522 <a title="sad" class="betterdocs-feelings sad" data-feelings="sad" href="#">
523 <svg width="15" height="15" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
524 <circle class="st0" cx="27.5" cy="0.6" r="1.9" />
525 <circle class="st0" cx="36" cy="0.6" r="1.9" />
526 <path class="st1" d="M10,0.3c-5.4,0-9.8,4.4-9.8,9.8s4.4,9.8,9.8,9.8s9.8-4.4,9.8-9.8S15.4,0.3,10,0.3z M13.3,6.6
527 c0.8,0,1.5,0.7,1.5,1.5c0,0.8-0.7,1.5-1.5,1.5c-0.8,0-1.5-0.7-1.5-1.5C11.8,7.3,12.4,6.6,13.3,6.6z M6.7,6.6c0.8,0,1.5,0.7,1.5,1.5
528 c0,0.8-0.7,1.5-1.5,1.5C5.9,9.6,5.2,9,5.2,8.1C5.2,7.3,5.9,6.6,6.7,6.6z M14.1,15L14.1,15c-0.2,0-0.4-0.1-0.5-0.2
529 c-0.9-1-2.2-1.7-3.7-1.7s-2.8,0.6-3.7,1.7C6.2,14.9,6,15,5.9,15h0c-0.6,0-0.8-0.6-0.5-1.1c1.1-1.3,2.8-2.1,4.6-2.1
530 c1.8,0,3.5,0.8,4.6,2.1C15,14.3,14.7,15,14.1,15z" />
531 </svg>
532 <span>' . esc_html( ( intval( $analytics[0]->totalNormal ) !== null ? intval( $analytics[0]->totalNormal ) : 0 ) ) . '</span>
533 </a>
534 </li>
535 </ul>';
536 break;
537 }
538 }
539
540 /**
541 * Enqueue Assets for Admin ( Styles )
542 *
543 * @param string $hook
544 *
545 * @return void
546 * @since 1.0.0
547 */
548 public function styles( $hook ) {
549 $this->assets->enqueue( 'betterdocs-global', 'admin/css/global.css', [], 'all' );
550
551 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
552 return;
553 }
554
555 $this->assets->enqueue( 'betterdocs-select2', 'vendor/css/select2.min.css', [], 'all' );
556 $this->assets->enqueue( 'betterdocs-daterangepicker', 'vendor/css/daterangepicker.css', [], 'all' );
557 $this->assets->enqueue( 'betterdocs-old', 'admin/css/betterdocs.css', [], 'all' );
558
559 /**
560 * This scripts enqueued for Dashboard App.
561 */
562 $this->assets->enqueue( 'betterdocs', 'admin/css/dashboard.css', [ 'betterdocs-old' ] );
563 $this->assets->enqueue( 'betterdocs-icons', 'admin/btd-icon/style.css' );
564 }
565
566 /**
567 * Enqueue Assets for Admin ( Scripts )
568 *
569 * @param string $hook
570 *
571 * @return void
572 * @since 1.0.0
573 */
574 public function scripts( $hook ) {
575 if ( ( $hook === 'edit.php' ) && get_post_type() == 'docs' ) {
576 $this->assets->enqueue(
577 'betterdocs-switcher',
578 'admin/js/switcher.js',
579 [
580 'jquery'
581 ]
582 );
583
584 $this->assets->localize(
585 'betterdocs-switcher',
586 'betterdocsSwitcher',
587 [
588 'menu_title' => __( 'Switch to BetterDocs UI', 'betterdocs' ),
589 'site_address' => get_bloginfo( 'url' ),
590 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(),
591 'betterdocs_pro_version' => betterdocs()->pro_version()
592 ]
593 );
594
595 return;
596 }
597
598 wp_enqueue_script( 'wp-editor' ); // enqueue this for yoast related issue
599
600 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
601 return;
602 }
603
604 $this->assets->register( 'betterdocs-admin', 'admin/js/dashboard.js' );
605
606 $saved_settings = get_option( 'betterdocs_settings', false );
607 $dark_mode = $saved_settings['dark_mode'] ?? false;
608 $dark_mode = ! empty( $dark_mode ) && boolval( $dark_mode );
609 $this->assets->localize(
610 'betterdocs-admin',
611 'betterdocs_admin',
612 [
613 'ajaxurl' => admin_url( 'admin-ajax.php' ),
614 'doc_cat_order_nonce' => wp_create_nonce( 'doc_cat_order_nonce' ),
615 'knowledge_base_order_nonce' => wp_create_nonce( 'knowledge_base_order_nonce' ),
616 'paged' => isset( $_GET['paged'] ) ? absint( wp_unslash( $_GET['paged'] ) ) : 0, // phpcs:ignore WordPress.Security.NonceVerification.Missing
617 'per_page_id' => 'edit_doc_category_per_page',
618 'menu_title' => __( 'Switch to BetterDocs UI', 'betterdocs' ),
619 'dark_mode' => $dark_mode,
620 'text' => __( 'Copied!', 'betterdocs' ),
621 'test_report' => __( 'Test Report!', 'betterdocs' ),
622 'sending' => __( 'Sending...', 'betterdocs' ),
623 'dir_url' => BETTERDOCS_ABSURL,
624 'rest_url' => esc_url_raw( rest_url() ),
625 'free_version' => betterdocs()->version,
626 'generate_data_url' => get_rest_url( null, '/betterdocs/v1/create-sample-docs' ),
627 'nonce' => wp_create_nonce( 'wp_rest' ),
628 'sync_nonce' => wp_create_nonce( 'ai_chatbot_embed' ),
629 'count_all_docs' => wp_count_posts('docs')->publish,
630 'count_new_docs' => count(get_option('saved_docs_post_ids', [])),
631 'admin_url' => admin_url(),
632 'ia_preview' => betterdocs()->settings->get( 'ia_enable_preview', false ),
633 'multiple_kb' => betterdocs()->settings->get( 'multiple_kb' ),
634 'previewMode' => betterdocs()->settings->get( 'ia_enable_preview', false ),
635 'dashboard_mode' => get_option( 'dashboard_mode' ),
636 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(),
637 'betterdocs_pro_version' => betterdocs()->pro_version(),
638 'analytics_older' => version_compare( betterdocs()->pro_version(), '3.3.4', '<=' ),
639 'disabled_embed_model_option'=> get_option('disabled_embed_model_option'),
640 ]
641 );
642
643 // If wp-date (which includes moment.js) is not registered, enqueue your custom moment.js
644 if ( ! wp_script_is( 'wp-date', 'registered' ) ) {
645 $this->assets->enqueue( 'moment', 'vendor/js/moment.min.js', [] );
646 }
647 wp_enqueue_script( 'betterdocs-admin' );
648
649 /**
650 * Duplicate Codes Need to Be Removed From Here Onwards
651 */
652
653 //FAQ Builder Related Localization
654 betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/css/faq.css' );
655 betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/js/faq.js' );
656
657 // removing emoji support
658 remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
659 remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
660
661 betterdocs()->assets->localize(
662 'betterdocs-admin-faq',
663 'betterdocsFaq',
664 [
665 'dir_url' => BETTERDOCS_ABSURL,
666 'rest_url' => esc_url_raw( rest_url() ),
667 'free_version' => betterdocs()->version,
668 'nonce' => wp_create_nonce( 'wp_rest' ),
669 'betterdocs_settings' => get_option( 'betterdocs_settings', false )
670 ]
671 );
672
673 //Glossaries Related Localization
674 betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/css/faq.css' );
675
676 betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/js/glossaries.js' );
677
678 betterdocs()->assets->localize(
679 'betterdocs-admin-glossaries',
680 'betterdocsGlossary',
681 [
682 'dir_url' => BETTERDOCS_ABSURL,
683 'rest_url' => esc_url_raw( rest_url() ),
684 'free_version' => betterdocs()->version,
685 'nonce' => wp_create_nonce( 'wp_rest' ),
686 'betterdocs_settings' => get_option( 'betterdocs_settings', false )
687 ]
688 );
689 }
690
691 /**
692 * All admin pages header
693 *
694 * @return void
695 * @since 1.0.0
696 */
697 public function header( $admin_tab_name ) {
698 $quick_links = [
699 'switch_view' => sprintf(
700 '<a href="%s" class="betterdocs-button betterdocs-button-secondary">%s</a>',
701 add_query_arg(
702 [
703 'post_type' => 'docs',
704 'bdocs_view' => 'classic'
705 ],
706 'edit.php'
707 ),
708 __( 'Switch to Classic UI', 'betterdocs' )
709 ),
710 'add_new_doc' => sprintf( '<a href="%s" class="betterdocs-button betterdocs-button-primary">%s</a>', add_query_arg( [ 'post_type' => 'docs' ], 'post-new.php' ), __( 'Add New Doc', 'betterdocs' ) )
711 ];
712
713 $quick_links = apply_filters( 'betterdocs_quick_links', $quick_links );
714
715 betterdocs()->views->get(
716 'admin/header',
717 [
718 'quick_links' => $quick_links,
719 'active_tab' => $admin_tab_name
720 ]
721 );
722 }
723
724 /**
725 * Register all the menus for BetterDocs
726 *
727 * @return void
728 * @since 1.0.0
729 */
730
731 public function menus() {
732 $default_args = [
733 'page_title' => 'BetterDocs',
734 'menu_title' => 'BetterDocs',
735 'capability' => 'edit_posts',
736 'menu_slug' => $this->slug,
737 'callback' => [ $this, 'output' ],
738 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ),
739 'position' => 5
740 ];
741
742 $_menu_position = 5;
743
744 foreach ( $this->menu_list() as $key => $value ) {
745 if ( $key === 'betterdocs' ) {
746 $callable = 'add_menu_page';
747 $value = wp_parse_args( $value, $default_args );
748 } else {
749 $callable = 'add_submenu_page';
750 $default_args['callback'] = '';
751 $default_args['position'] = $_menu_position;
752 unset( $default_args['icon_url'] );
753 $value = wp_parse_args( $value, array_merge( [ 'parent_slug' => $this->slug ], $default_args ) );
754 }
755
756 ++$_menu_position;
757
758 call_user_func_array( $callable, $value );
759 }
760 }
761
762 /**
763 * BetterDocs Admin Page Output
764 *
765 * @return void
766 * @since 1.0.0
767 */
768 public function output() {
769 if ( betterdocs()->is_pro_active()
770 && version_compare( betterdocs()->pro_version(), '3.3.4', '<=' )
771 && get_current_screen()->id == 'betterdocs_page_betterdocs-analytics' ) {
772 betterdocs_pro()->views->get( 'admin/analytics-pro' );
773 } else {
774 betterdocs()->views->get(
775 'admin/main',
776 [
777 'admin_ui' => 'dnd'
778 ]
779 );
780 }
781 }
782
783 /**
784 * Menu creator helper
785 *
786 * @param string $title
787 * @param string $slug
788 * @param string $cap
789 * @param array $callback
790 *
791 * @return array
792 * @since 2.5.0
793 *
794 */
795 private function normalize_menu( $title, $slug, $cap = 'edit_docs', $callback = null, $optional = [] ) {
796 return Helper::normalize_menu( $title, $slug, $cap, $callback, $optional );
797 }
798
799 /**
800 * BetterDocs Menu List
801 *
802 * @return array
803 * @since 1.0.0
804 */
805 private function menu_list() {
806 $parent_slug = [];
807
808 $betterdocs_admin_pages = [
809 'betterdocs' => [
810 'menu_slug' => $this->slug,
811 'page_title' => 'BetterDocs',
812 'menu_title' => 'BetterDocs',
813 'capability' => 'edit_docs',
814 'callback' => [ $this, 'output' ],
815 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ),
816 'position' => 5
817 ],
818 'all_docs' => $this->normalize_menu( __( 'All Docs', 'betterdocs' ), $this->ui_slug() ),
819 'add_new' => $this->normalize_menu( __( 'Add New', 'betterdocs' ), 'post-new.php?post_type=docs' ),
820 'categories' => $this->normalize_menu( __( 'Categories', 'betterdocs' ), 'edit-tags.php?taxonomy=doc_category&post_type=docs', 'manage_doc_terms' ),
821 'tags' => $this->normalize_menu( __( 'Tags', 'betterdocs' ), 'edit-tags.php?taxonomy=doc_tag&post_type=docs', 'manage_doc_terms' ),
822 'settings' => $this->normalize_menu(
823 __( 'Settings', 'betterdocs' ),
824 'betterdocs-settings',
825 'edit_docs_settings',
826 [
827 $this,
828 'output'
829 ],
830 $parent_slug
831 ),
832 'analytics' => $this->normalize_menu(
833 __( 'Analytics', 'betterdocs' ),
834 'betterdocs-analytics',
835 'read_docs_analytics',
836 [
837 $this,
838 'output'
839 ],
840 $parent_slug
841 ),
842 'faq' => $this->normalize_menu(
843 __( 'FAQ Builder', 'betterdocs' ),
844 'betterdocs-faq',
845 'read_docs_analytics',
846 [
847 $this,
848 'output'
849 ],
850 $parent_slug
851 )
852 ];
853
854 if ( betterdocs()->is_pro_active() && betterdocs()->settings->get( 'enable_glossaries' ) == true ) {
855 $betterdocs_admin_pages['glossaries'] = $this->normalize_menu(
856 __( 'Glossaries', 'betterdocs' ),
857 'betterdocs-glossaries',
858 'read_docs_analytics',
859 [
860 $this,
861 'output'
862 ],
863 $parent_slug
864 );
865 }
866
867 return apply_filters( 'betterdocs_admin_menu', $betterdocs_admin_pages, [$this, 'output'], $parent_slug);
868
869 }
870
871 public function add_custom_classes_to_menu_items() {
872 global $menu, $submenu;
873
874 $menu_items = [
875 'betterdocs' => 'betterdocs',
876 'betterdocs_page_all_docs' => 'betterdocs-all-docs',
877 'betterdocs_page_add_new' => 'betterdocs-add-new',
878 'edit-tags.php?taxonomy=doc_category&post_type=docs' => 'betterdocs-categories',
879 'edit-tags.php?taxonomy=doc_tag&post_type=docs' => 'betterdocs-tags',
880 'betterdocs-settings' => 'betterdocs-settings',
881 'betterdocs-analytics' => 'betterdocs-analytics',
882 'betterdocs-faq' => 'betterdocs-faq',
883 'betterdocs-glossaries' => 'betterdocs-glossaries',
884 'betterdocs-ai-chatbot' => 'betterdocs-ai-chatbot',
885 'edit-tags.php?taxonomy=knowledge_base&post_type=docs' => 'betterdocs-multiplekb'
886 ];
887
888 foreach ( $menu as &$item ) {
889 if ( isset( $menu_items[ $item[2] ] ) ) {
890 if ( ! isset( $item[4] ) ) {
891 $item[4] = '';
892 }
893 $item[4] .= '' . $menu_items[ $item[2] ];
894 }
895 }
896
897 foreach ( $submenu as &$submenu_items ) {
898 foreach ( $submenu_items as &$sub_item ) {
899 if ( isset( $menu_items[ $sub_item[2] ] ) ) {
900 if ( ! isset( $sub_item[4] ) ) {
901 $sub_item[4] = '';
902 }
903 $sub_item[4] .= '' . $menu_items[ $sub_item[2] ];
904 }
905 }
906 }
907 }
908
909 public function quick_setup_menu( $menus ) {
910 $betterdocs_settings = get_option( 'betterdocs_settings' );
911 if ( $betterdocs_settings ) {
912 return $menus;
913 } else {
914 $menus['quick_setup'] = $this->normalize_menu(
915 __( 'Quick Setup', 'betterdocs' ),
916 'betterdocs-setup',
917 'delete_users',
918 [
919 $this->container->get( SetupWizard::class ),
920 'views'
921 ]
922 );
923 }
924
925 return $menus;
926 }
927
928 public function insert_plugin_links( $links ) {
929 $links[] = '<a href="admin.php?page=betterdocs-settings">' . __( 'Settings', 'betterdocs' ) . '</a>';
930
931 return $links;
932 }
933
934 public function toolbar_menu( $admin_bar ) {
935 if ( ! is_admin() || ! is_admin_bar_showing() ) {
936 return;
937 }
938
939 // Show only when the user is a member of this site, or they're a super admin.
940 if ( ! is_user_member_of_blog() && ! is_super_admin() ) {
941 return;
942 }
943
944 $docs_url = '';
945 $encyclopedia_url = '';
946
947 if ( $this->settings->get( 'builtin_doc_page' ) ) {
948 $docs_url = get_post_type_archive_link( 'docs' );
949 } elseif ( intval( $docs_page = $this->settings->get( 'docs_page' ) ) ) {
950 $docs_url = ! empty( $docs_page ) ? get_page_link( $docs_page ) : false;
951 }
952
953 if ( ! $docs_url ) {
954 return;
955 }
956
957 $slug = $this->settings->get( 'encyclopedia_root_slug' );
958
959 $encyclopedia_url = home_url( $slug );
960
961 $admin_bar->add_node(
962 [
963 'parent' => 'site-name',
964 'id' => 'view-docs',
965 'title' => __( 'Visit Documentation', 'betterdocs' ),
966 'href' => $docs_url
967 ]
968 );
969
970 $is_enable_encyclopedia = betterdocs()->settings->get( 'enable_encyclopedia' );
971
972 if ( $is_enable_encyclopedia && betterdocs()->is_pro_active() ) {
973 $admin_bar->add_node(
974 [
975 'parent' => 'site-name',
976 'id' => 'view-encyclopedia',
977 'title' => __( 'Visit Encyclopedia', 'betterdocs' ),
978 'href' => $encyclopedia_url
979 ]
980 );
981 }
982 }
983
984 /**
985 * Save last visited admin ui
986 *
987 * @since 3.0.1
988 *
989 */
990 public function save_admin_page() {
991 if ( isset( $_GET['post_type'] ) && $_GET['post_type'] === 'docs' && isset( $_GET['bdocs_view'] ) && $_GET['bdocs_view'] === 'classic' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
992 update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'classic_ui' );
993 } elseif ( isset( $_GET['page'] ) && $_GET['page'] === 'betterdocs-admin' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
994 update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'modern_ui' );
995 }
996 }
997
998 /**
999 * Return last visited admin ui slug
1000 *
1001 * @return string
1002 * @since 3.0.1
1003 */
1004 public function ui_slug() {
1005 $last_visited = get_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', true );
1006 $docs = get_posts(
1007 [
1008 'post_type' => 'docs',
1009 'post_status' => 'any',
1010 'numberposts' => 2
1011 ]
1012 );
1013
1014 if ( $last_visited === 'modern_ui' || count( $docs ) == 0 ) {
1015 $slug = 'betterdocs-admin';
1016 } else {
1017 $slug = admin_url( 'edit.php?post_type=docs&bdocs_view=classic' );
1018 }
1019
1020 return $slug;
1021 }
1022
1023 /**
1024 * Resets a duplicate submenu in WordPress if the parent main menu and the first submenu permalink are not the same.
1025 *
1026 * @return string
1027 * @since 3.0.1
1028 */
1029 public function reset_submenu() {
1030 global $submenu;
1031
1032 $docs = get_posts( [ 'post_type' => 'docs' ] );
1033 if ( count( $docs ) == 0 ) {
1034 return;
1035 }
1036
1037 $last_visited = get_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', true );
1038
1039 if ( $last_visited === 'classic_ui' && isset( $submenu['betterdocs-admin'] ) && in_array( 'betterdocs-admin', $submenu['betterdocs-admin'][0] ) ) {
1040 unset( $submenu['betterdocs-admin'][0] );
1041 $submenu['betterdocs-admin'] = array_values( $submenu['betterdocs-admin'] );
1042 }
1043 }
1044 }
1045