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

1,257 lines 42.6 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\Admin\NoticePointers;
8 use WPDeveloper\BetterDocs\Utils\Base;
9 use PriyoMukul\WPNotice\Utils\CacheBank;
10 use WPDeveloper\BetterDocs\Utils\Helper;
11 use WPDeveloper\BetterDocs\Utils\Enqueue;
12 use WPDeveloper\BetterDocs\Utils\Insights;
13 use PriyoMukul\WPNotice\Utils\NoticeRemover;
14 use WPDeveloper\BetterDocs\Core\PluginInstaller;
15 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
16
17 class Admin extends Base {
18 /**
19 * @var CacheBank
20 */
21 private static $cache_bank;
22 /**
23 * Admin Root Menu Slug
24 * @var string
25 */
26 private $slug = 'betterdocs-dashboard';
27 /**
28 * Insights
29 *
30 * @var Insights
31 */
32 private $insights = null;
33
34 /**
35 * DI\Container
36 *
37 * @var Container
38 */
39 private $container;
40
41 /**
42 * Database Wrapper
43 *
44 * @var Settings
45 */
46 private $settings;
47
48 /**
49 * KBMigration
50 *
51 * @var KBMigration
52 */
53 private $kbmigration;
54
55 /**
56 * Enqueue
57 * @var Enqueue
58 */
59 private $assets;
60
61 // modules
62 protected $installer;
63
64 /**
65 * FAQBuilder
66 * @var FAQBuilder
67 */
68 private $faq_builder;
69 private $glossaries;
70
71 public function __construct( Container $container, PostType $type, Enqueue $assets, Settings $settings, KBMigration $kbmigration ) {
72 $this->container = $container;
73 $this->assets = $assets;
74 $this->settings = $settings;
75 $this->kbmigration = $kbmigration;
76 $this->slug = 'betterdocs-dashboard';
77
78 add_action( 'init', [ $type, 'register' ], 9 );
79 add_action('rest_api_init', [$this, 'order_terms_in_wp_terms_admin_table']);
80
81 $type->init();
82 $type->admin_init();
83
84 $this->faq_builder = $this->container->get( FAQBuilder::class );
85 $this->glossaries = $this->container->get( Glossaries::class );
86
87 if ( ! is_admin() ) {
88 return;
89 }
90
91 $this->installer = new PluginInstaller();
92
93 $this->plugin_insights();
94 add_action( 'admin_notices', [ $this, 'compatibility_notices' ] );
95 // add_action( 'admin_init', [$this, 'notices'], 9 );
96 add_filter( 'admin_init', [ $this, 'save_admin_page' ], 99 );
97
98 add_action( 'admin_menu', [ $this, 'menus' ] );
99 add_action( 'admin_menu', [ $this, 'reset_submenu' ] );
100 add_action( 'admin_head', [ $this, 'add_custom_classes_to_menu_items' ] );
101 add_filter( 'plugin_action_links_' . BETTERDOCS_PLUGIN_BASENAME, [ $this, 'insert_plugin_links' ] );
102
103 // $this->container->get( SetupWizard::class )->init();
104
105 add_action( 'admin_enqueue_scripts', [ $this, 'styles' ] );
106 add_action( 'admin_enqueue_scripts', [ $this, 'scripts' ] );
107 //add_action( 'betterdocs_listing_header', [ $this, 'header' ], 10, 1 );
108 add_action( 'admin_bar_menu', [ $this, 'toolbar_menu' ], 32 );
109
110 add_filter( 'admin_body_class', [ $this, 'body_classes' ] );
111 add_filter( 'parent_file', [ $type, 'highlight_admin_menu' ] );
112 add_filter( 'submenu_file', [ $type, 'highlight_admin_submenu' ], 10, 2 );
113 add_filter( 'betterdocs_admin_menu', [ $this, 'quick_setup_menu' ], 10, 1 );
114
115 /**
116 * Remove Comments Column from List Table.
117 */
118 add_filter( 'manage_docs_posts_columns', [ $this, 'set_custom_edit_action_columns' ] );
119 add_filter( 'manage_docs_posts_custom_column', [ $this, 'manage_custom_columns' ], 10, 2 );
120
121 /**
122 * Add New Column
123 */
124 add_filter( 'manage_users_columns', [ $this, 'add_users_total_docs_column' ], 10, 1 );
125 add_filter( 'manage_users_custom_column', [ $this, 'popular_users_docs_data' ], 10, 3 );
126 if ( is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ) ) {
127 add_action( 'admin_footer-plugins.php', array( $this, 'disable_deactivation' ) );
128 }
129
130 if ( $this->settings->get( 'enable_estimated_reading_time' ) ) {
131 add_action( 'add_meta_boxes', [ $this, 'reading_meta_box_' ], 10 );
132 }
133
134 self::$cache_bank = CacheBank::get_instance();
135
136 // Remove OLD notice from 1.0.0 (if other WPDeveloper plugin has notice)
137 NoticeRemover::get_instance( '1.0.0' );
138
139 try {
140 $this->notices();
141 } catch ( Exception $e ) {
142 unset( $e );
143 }
144
145 // Initialize Black Friday Pointer
146 $this->init_black_friday_pointer();
147
148 // Register AJAX handler for pointer dismissal
149 add_action( 'wp_ajax_betterdocs_dismiss_black_friday_pointer', [ $this, 'ajax_dismiss_black_friday_pointer' ] );
150 }
151
152 public function order_terms_in_wp_terms_admin_table() {
153 //order the terms correctly to be shown on the admin panel categories menu with betterdocs order
154 add_action('rest_insert_doc_category', function( $term, $request, $bool ) {
155 $max_order = Helper::get_max_doc_category_order_from_term_meta() ?? 0;
156 $next_order = $max_order + 1;
157 update_term_meta( $term->term_id, 'doc_category_order', $next_order );
158 }, 10, 3);
159 }
160
161 public function disable_deactivation() {
162 $tooltip_text = esc_html__( 'Deactivate BetterDocs Pro First', 'betterdocs' );
163 ?>
164 <style type="text/css">
165 #deactivate-betterdocs {
166 color: #cccccc;
167 position: relative;
168 }
169
170 /* Tooltip styling */
171 #deactivate-betterdocs[title]:hover::after {
172 content: attr(title);
173 position: absolute;
174 bottom: 100%;
175 left: 50%;
176 transform: translateX(-50%);
177 background-color: #333;
178 color: #fff;
179 padding: 5px 10px;
180 border-radius: 4px;
181 font-size: 12px;
182 white-space: nowrap;
183 box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2);
184 z-index: 10;
185 }
186 #deactivate-betterdocs:focus {
187 box-shadow: none;
188 outline: none;
189 }
190 </style>
191 <script type="text/javascript">
192 jQuery(document).ready(function($) {
193 // Disable the default action and add class with tooltip by default
194 const tooltipText = "<?php echo esc_attr( $tooltip_text ); ?>";
195 $("#deactivate-betterdocs")
196 .addClass("disabled-tooltip")
197 .attr("title", tooltipText)
198 .on("click", function(e) {
199 e.preventDefault(); // Prevent any action on click
200 });
201 });
202 </script>
203 <?php
204 }
205
206 public function add_users_total_docs_column( $columns ) {
207 $new_column = [
208 'docs' => __( 'Docs', 'betterdocs' )
209 ];
210 $columns = array_merge( $columns, $new_column );
211 return $columns;
212 }
213
214 public function popular_users_docs_data( $output, $column_name, $user_id ) {
215 if ( $column_name == 'docs' ) {
216 $total_count = count_user_posts( $user_id, 'docs', true );
217 return '<a href="edit.php?post_type=docs&author=' . $user_id . '" class="edit"><span aria-hidden="true">' . $total_count . '</span></a>';
218 }
219 return $output;
220 }
221
222 public function reading_meta_box_() {
223 add_meta_box(
224 'betterdocs_estimated_time_metabox',
225 __( 'Estimated Reading Time', 'betterdocs' ),
226 [
227 $this,
228 'render_estimated_time_markup'
229 ],
230 'docs'
231 );
232 }
233
234 public function render_estimated_time_markup() {
235 betterdocs()->views->get( 'admin/metabox/estimated-reading-box' );
236 }
237
238 public function compatibility_notices() {
239 if ( betterdocs()->is_pro_active() ) {
240 $plugins = Helper::get_plugins();
241 $plugin_data = $plugins['betterdocs-pro/betterdocs-pro.php'];
242
243 if ( isset( $plugin_data['Version'] ) && version_compare( $plugin_data['Version'], '2.5.0', '>=' ) ) {
244 return;
245 }
246
247 betterdocs()->views->get( 'admin/notices/compatibility', [ 'version' => $plugin_data['Version'] ] );
248 }
249 }
250
251 public function plugin_insights( $prevent_init = false ) {
252 $this->insights = Insights::get_instance(
253 BETTERDOCS_PLUGIN_FILE,
254 [
255 'opt_in' => true,
256 'goodbye_form' => true,
257 'item_id' => 'c7b16777b4f1b83f6083'
258 ]
259 );
260
261 $this->insights->set_notice_options(
262 [
263 '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' ),
264 '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' )
265 ]
266 );
267
268 if ( ! $prevent_init ) {
269 $this->insights->init();
270 }
271
272 return $this->insights;
273 }
274
275 /**
276 * Admin notices for Review and others.
277 *
278 * @return void
279 * @throws Exception
280 */
281 public function notices() {
282 $notices = new Notices(
283 [
284 'id' => 'betterdocs',
285 'storage_key' => 'notices',
286 'lifetime' => 3,
287 'stylesheet_url' => $this->assets->asset_url( 'admin/css/notices.css' ),
288 'styles' => $this->assets->asset_url( 'admin/css/notices.css' ),
289 'priority' => 4
290 ]
291 );
292
293 /**
294 * Review Notice
295 * @var mixed $message
296 */
297
298 $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' );
299
300 $_review_notice = [
301 'thumbnail' => $this->assets->icon( 'betterdocs-logo.svg', true ),
302 'html' => '<p>' . $message . '</p>',
303 'links' => [
304 'later' => [
305 'link' => 'https://wordpress.org/plugins/betterdocs/#reviews',
306 'target' => '_blank',
307 'label' => __( 'Sure, you deserve it!', 'betterdocs' ),
308 'icon_class' => 'dashicons dashicons-external'
309 ],
310 'allready' => [
311 'label' => __( 'I already did', 'betterdocs' ),
312 'icon_class' => 'dashicons dashicons-smiley',
313 'attributes' => [
314 'data-dismiss' => true
315 ]
316 ],
317 'maybe_later' => [
318 'label' => __( 'Maybe Later', 'betterdocs' ),
319 'icon_class' => 'dashicons dashicons-calendar-alt',
320 'attributes' => [
321 'data-later' => true,
322 'class' => 'dismiss-btn'
323 ]
324 ],
325 'support' => [
326 'link' => 'https://wpdeveloper.com/support',
327 'attributes' => [
328 'target' => '_blank'
329 ],
330 'label' => __( 'I need help', 'betterdocs' ),
331 'icon_class' => 'dashicons dashicons-sos'
332 ],
333 'never_show_again' => [
334 'label' => __( 'Never show again', 'betterdocs' ),
335 'icon_class' => 'dashicons dashicons-dismiss',
336 'attributes' => [
337 'data-dismiss' => true
338 ]
339 ]
340 ]
341 ];
342
343 $notices->add(
344 'review',
345 $_review_notice,
346 [
347 'start' => $notices->strtotime( '+10 days' ),
348 'recurrence' => 30,
349 'dismissible' => true
350 ]
351 );
352
353 if ( $this->kbmigration->existing_plugins && ! in_array( $this->kbmigration->existing_plugins[0][0], $this->kbmigration->migrated_plugins ) ) {
354 $plugin_name = '<strong>' . esc_html( $this->kbmigration->existing_plugins[0][1] ) . '</strong>';
355
356 $message = sprintf(
357 /* translators: %s is the name of the existing knowledge base plugin. */
358 __( 'Already using %s? Power up your Knowledge Base by migrating all your docs and settings to BetterDocs with just 1 click.', 'betterdocs' ),
359 esc_html( $plugin_name )
360 );
361
362 $migration_message = sprintf(
363 '<p class="migration-message">%s</p><a class="button button-primary betterdocs-migration-notice" href="%s">%s</a>',
364 $message,
365 esc_url( admin_url( 'admin.php?page=betterdocs-settings&tab=tab-migration' ) ),
366 esc_html__( 'Start Migration', 'betterdocs' )
367 );
368
369 $_migration_notice = [
370 'thumbnail' => '',
371 'html' => $migration_message,
372 'links' => [
373 'maybe_later' => [
374 'label' => __( 'Maybe Later', 'betterdocs' ),
375 'icon_class' => 'dashicons dashicons-calendar-alt',
376 'attributes' => [
377 'data-later' => true,
378 'class' => 'dismiss-btn'
379 ]
380 ],
381 'never_show_again' => [
382 'label' => __( 'Never show again', 'betterdocs' ),
383 'icon_class' => 'dashicons dashicons-dismiss',
384 'attributes' => [
385 'data-dismiss' => true
386 ]
387 ]
388 ]
389 ];
390
391 $notices->add(
392 'migration',
393 $_migration_notice,
394 [
395 'start' => $notices->time(),
396 'recurrence' => false,
397 'dismissible' => true
398 ]
399 );
400 }
401
402 /**
403 * Opt-In Notice
404 */
405 $allow_tracking = get_option( 'wpins_allow_tracking' );
406 if ( $this->insights != null && ! isset( $allow_tracking['betterdocs'] ) ) {
407 $notices->add(
408 'opt_in',
409 [ $this->insights, 'notice' ],
410 [
411 'classes' => 'updated put-dismiss-notice',
412 'start' => $notices->time(),
413 'refresh' => BETTERDOCS_VERSION,
414 'dismissible' => true,
415 'do_action' => 'wpdeveloper_notice_clicked_for_betterdocs',
416 'display_if' => ! function_exists( 'betterdocs_pro' ),
417 'screens' => [ 'dashboard' ]
418 ]
419 );
420 }
421
422 $blackfriday_message = '<div class="betterdocs-blackfriday-notice-body"><p style="margin-top: 0; margin-bottom: 0;"><strong>Black Friday Mega Sale:</strong> Build <strong>AI-Powered Knowledge Base</strong> & FAQs to empower support and improve user experience – now <strong>up to $120 OFF!</strong> 🎁</p></div>';
423 $_blackfriday_notice = [
424 'thumbnail' => $this->assets->icon( 'betterdocs-logo.svg', true ),
425 'html' => $blackfriday_message,
426 'links' => [
427 'support' => [
428 'link' => 'https://betterdocs.co/bfcm2025-admin-notice',
429 'attributes' => [
430 'target' => '_blank',
431 'class' => 'offer-button'
432 ],
433 'label' => __( 'Upgrade To PRO', 'betterdocs' )
434 ],
435 'maybe_later' => [
436 'label' => __( 'I’ll Grab It Later', 'betterdocs' ),
437 'attributes' => [
438 'target' => '_blank',
439 'data-later' => true,
440 'class' => 'dismiss-btn'
441 ]
442 ],
443 ]
444 ];
445
446 $notices->add(
447 'blackfriday25',
448 $_blackfriday_notice,
449 [
450 'start' => $notices->time(),
451 'recurrence' => false,
452 'dismissible' => true,
453 'refresh' => BETTERDOCS_VERSION,
454 'expire' => strtotime( '11:59:59pm December 4, 2025' ),
455 'display_if' => ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' )
456 ]
457 );
458
459 $offer_message = '<div class="betterdocs-notice-body betterdocs-holiday-notice-body"><p style="margin-top: 0; margin-bottom: 0;"><strong>🎉 BetterDocs Just Hit 40,000+ Users!</strong> Join the celebration with 20% OFF & unlock even more power with premium features today!</p><div class="betterdocs-notice-actions"><a class="button button-primary" href="https://betterdocs.co/40K-admin-notice" target="_blank"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
460 <path d="M15.7431 10.9381L15.904 9.35966C15.9898 8.5175 16.0464 7.9614 16.002 7.61102L16.0175 7.61112C16.7442 7.61112 17.3333 6.98929 17.3333 6.22223C17.3333 5.45517 16.7442 4.83334 16.0175 4.83334C15.2908 4.83334 14.7017 5.45517 14.7017 6.22223C14.7017 6.56914 14.8222 6.88634 15.0214 7.12975C14.7354 7.31608 14.3615 7.70926 13.7987 8.30106L13.7986 8.30107L13.7986 8.30108C13.365 8.75699 13.1482 8.98495 12.9064 9.02025C12.7724 9.03981 12.6358 9.01971 12.5121 8.96219C12.2887 8.85838 12.1398 8.57656 11.842 8.01293L10.2723 5.04204C10.0886 4.69433 9.9348 4.40331 9.79616 4.16913C10.3649 3.86285 10.7543 3.23869 10.7543 2.51852C10.7543 1.49577 9.96888 0.666672 8.99996 0.666672C8.03104 0.666672 7.24557 1.49577 7.24557 2.51852C7.24557 3.23869 7.63503 3.86285 8.20376 4.16913C8.06511 4.40333 7.91137 4.6943 7.72763 5.04204L6.1579 8.01293C5.8601 8.57656 5.71119 8.85838 5.48786 8.96219C5.36411 9.01971 5.22757 9.03981 5.09355 9.02025C4.85169 8.98495 4.63488 8.75699 4.20127 8.30107C3.63844 7.70928 3.26449 7.31608 2.97849 7.12975C3.17771 6.88634 3.29821 6.56914 3.29821 6.22223C3.29821 5.45517 2.70911 4.83334 1.98242 4.83334C1.25572 4.83334 0.666626 5.45517 0.666626 6.22223C0.666626 6.98929 1.25572 7.61112 1.98242 7.61112L1.99795 7.61102C1.95348 7.96139 2.01015 8.51749 2.09596 9.35965L2.2568 10.938C2.34608 11.8142 2.42032 12.6478 2.51125 13.3982H15.4887C15.5796 12.6478 15.6538 11.8142 15.7431 10.9381Z" fill="white"/>
461 <path d="M8.04563 17.3333H9.95429C12.4419 17.3333 13.6858 17.3333 14.5157 16.5492C14.8779 16.207 15.1073 15.59 15.2728 14.787H2.72711C2.89263 15.59 3.12201 16.207 3.48424 16.5492C4.31414 17.3333 5.55797 17.3333 8.04563 17.3333Z" fill="white"/>
462 </svg>Upgrade To PRO</a></div></div>';
463 $_offer_notice = [
464 'thumbnail' => $this->assets->icon( 'betterdocs-logo.svg', true ),
465 'html' => $offer_message
466 ];
467
468 $notices->add(
469 '40k_offer',
470 $_offer_notice,
471 [
472 'start' => $notices->time(),
473 'recurrence' => false,
474 'dismissible' => true,
475 'refresh' => BETTERDOCS_VERSION,
476 'expire' => strtotime( '11:59:59pm April 30, 2025' ),
477 'display_if' => ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' )
478 ]
479 );
480
481 self::$cache_bank->create_account( $notices );
482 self::$cache_bank->calculate_deposits( $notices );
483 if ( method_exists( self::$cache_bank, 'clear_notices_in_' ) ) {
484 self::$cache_bank->clear_notices_in_(
485 [
486 'toplevel_page_betterdocs-dashboard',
487 'admin_page_betterdocs-admin',
488 'betterdocs_page_betterdocs-admin',
489 'betterdocs_page_betterdocs-settings',
490 'betterdocs_page_betterdocs-faq',
491 'betterdocs_page_betterdocs-analytics',
492 'betterdocs_page_betterdocs-glossaries',
493 'betterdocs_page_betterdocs-ai-chatbot',
494 'edit-doc_category',
495 'edit-doc_tag'
496 ],
497 $notices,
498 true
499 );
500 }
501 }
502
503 public function body_classes( $classes ) {
504 $saved_settings = get_option( 'betterdocs_settings', false );
505 $dark_mode = isset( $saved_settings['dark_mode'] ) ? $saved_settings['dark_mode'] : false;
506 $dark_mode = ! empty( $dark_mode ) ? boolval( $dark_mode ) : false;
507 $current_screen_id = get_current_screen() != null ? str_replace( 'betterdocs_page_', '', str_replace( 'toplevel_page_', '', str_replace( 'admin_page_', '', get_current_screen()->id ) ) ) : '';
508 $registered_screens = [
509 'betterdocs-settings',
510 'betterdocs-admin',
511 'betterdocs-dashboard',
512 'betterdocs-analytics',
513 'betterdocs-glossaries',
514 'betterdocs-faq',
515 'edit-doc_category',
516 'edit-doc_tag',
517 'edit-knowledge_base',
518 'betterdocs-ai-chatbot'
519 ];
520
521 if( in_array( $current_screen_id, $registered_screens ) ) {
522 $classes .= ' betterdocs-admin ';
523 }
524
525 if ( $dark_mode === true && in_array( $current_screen_id, $registered_screens ) ) {
526 $classes .= ' betterdocs-dark-mode ';
527 }
528
529 return $classes;
530 }
531
532 /**
533 * Remove Comments Column From List Table
534 *
535 * @param array $columns
536 *
537 * @return array
538 * @since 1.0.0
539 */
540 public function set_custom_edit_action_columns( $columns ) {
541 unset( $columns['comments'] );
542 $new_columns = [];
543 foreach ( $columns as $key => $value ) {
544 if ( $key == 'date' ) {
545 $new_columns['betterdocs_word_count'] = __( 'Word Count', 'betterdocs' ); // put the tags column before it
546 $new_columns['betterdocs_reaction'] = __( 'Reactions', 'betterdocs' );
547 }
548 $new_columns[ $key ] = $value;
549 }
550
551 return $new_columns;
552 }
553
554 public function manage_custom_columns( $column, $post_id ) {
555 global $wpdb;
556 switch ( $column ) {
557 case 'betterdocs_word_count':
558 $content_without_html_tags = trim( strip_tags( get_post_field( 'post_content', $post_id ) ) );
559 preg_match_all( '/<[^>]*>|[\p{L}\p{M}]+/u', $content_without_html_tags, $matches );
560 $total_words = ! empty( $matches[0] ) ? count( $matches[0] ) : count( [] );
561 $word_count = $total_words;
562 echo '<span>' . esc_html( intval( $word_count ) ) . '</span>';
563 break;
564 case 'betterdocs_reaction':
565 $where = "WHERE post_id='" . esc_sql( $post_id ) . "'";
566 $analytics = $wpdb->get_results(
567 "SELECT
568 sum(impressions) as totalViews,
569 sum(unique_visit) as totalUniqueViews,
570 sum(happy + sad + normal) as totalReactions,
571 sum(happy) as totalHappy,
572 sum(normal) as totalNormal,
573 sum(sad) as totalSad
574 FROM {$wpdb->prefix}betterdocs_analytics
575 $where"
576 );
577
578 echo '<ul class="reactions-count">
579 <li>
580 <a title="happy" class="betterdocs-feelings happy" data-feelings="happy" href="#">
581 <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">
582 <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
583 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
584 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
585 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" />
586 <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
587 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
588 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
589 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" />
590 </svg>
591 <span>' . esc_html( ( intval( $analytics[0]->totalHappy ) !== null ? intval( $analytics[0]->totalHappy ) : 0 ) ) . '</span>
592 </a>
593 </li>
594 <li>
595 <a title="normal" class="betterdocs-feelings normal" data-feelings="normal" href="#">
596 <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">
597 <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
598 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
599 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
600 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" />
601 </svg>
602 <span>' . esc_html( ( intval( $analytics[0]->totalNormal ) !== null ? intval( $analytics[0]->totalNormal ) : 0 ) ) . '</span>
603 </a>
604 </li>
605 <li>
606 <a title="sad" class="betterdocs-feelings sad" data-feelings="sad" href="#">
607 <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">
608 <circle class="st0" cx="27.5" cy="0.6" r="1.9" />
609 <circle class="st0" cx="36" cy="0.6" r="1.9" />
610 <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
611 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
612 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
613 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
614 c1.8,0,3.5,0.8,4.6,2.1C15,14.3,14.7,15,14.1,15z" />
615 </svg>
616 <span>' . esc_html( ( intval( $analytics[0]->totalNormal ) !== null ? intval( $analytics[0]->totalNormal ) : 0 ) ) . '</span>
617 </a>
618 </li>
619 </ul>';
620 break;
621 }
622 }
623
624 /**
625 * Enqueue Assets for Admin ( Styles )
626 *
627 * @param string $hook
628 *
629 * @return void
630 * @since 1.0.0
631 */
632 public function styles( $hook ) {
633 $this->assets->enqueue( 'betterdocs-global', 'admin/css/global.css', [], 'all' );
634
635 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
636 return;
637 }
638
639 $this->assets->enqueue( 'betterdocs-select2', 'vendor/css/select2.min.css', [], 'all' );
640 $this->assets->enqueue( 'betterdocs-daterangepicker', 'vendor/css/daterangepicker.css', [], 'all' );
641 $this->assets->enqueue( 'betterdocs-old', 'admin/css/betterdocs.css', [], 'all' );
642
643 /**
644 * This scripts enqueued for Dashboard App.
645 */
646 $this->assets->enqueue( 'betterdocs', 'admin/css/dashboard.css', [ 'betterdocs-old' ], '', BETTERDOCS_VERSION );
647 $this->assets->enqueue( 'betterdocs-icons', 'admin/btd-icon/style.css' );
648 }
649
650 /**
651 * Enqueue Assets for Admin ( Scripts )
652 *
653 * @param string $hook
654 *
655 * @return void
656 * @since 1.0.0
657 */
658 public function scripts( $hook ) {
659 if ( ( $hook === 'edit.php' ) && get_post_type() == 'docs' ) {
660 $this->assets->enqueue(
661 'betterdocs-switcher',
662 'admin/js/switcher.js',
663 [
664 'jquery'
665 ]
666 );
667
668 $this->assets->localize(
669 'betterdocs-switcher',
670 'betterdocsSwitcher',
671 [
672 'menu_title' => __( 'Switch to BetterDocs UI', 'betterdocs' ),
673 'site_address' => get_bloginfo( 'url' ),
674 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(),
675 'betterdocs_pro_version' => betterdocs()->pro_version()
676 ]
677 );
678
679 return;
680 }
681
682 wp_enqueue_script( 'wp-editor' ); // enqueue this for yoast related issue
683
684 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
685 return;
686 }
687
688 wp_enqueue_media(); // load early to fix problems with media upload issues on settings for wordpress 6.0.9
689 $this->assets->register( 'betterdocs-admin', 'admin/js/dashboard.js' );
690
691 $saved_settings = get_option( 'betterdocs_settings', false );
692 $dark_mode = $saved_settings['dark_mode'] ?? false;
693 $dark_mode = ! empty( $dark_mode ) && boolval( $dark_mode );
694 $this->assets->localize(
695 'betterdocs-admin',
696 'betterdocs_admin',
697 [
698 'ajaxurl' => admin_url( 'admin-ajax.php' ),
699 'doc_cat_order_nonce' => wp_create_nonce( 'doc_cat_order_nonce' ),
700 'knowledge_base_order_nonce' => wp_create_nonce( 'knowledge_base_order_nonce' ),
701 'paged' => isset( $_GET['paged'] ) ? absint( wp_unslash( $_GET['paged'] ) ) : 0, // phpcs:ignore WordPress.Security.NonceVerification.Missing
702 'per_page_id' => 'edit_doc_category_per_page',
703 'menu_title' => __( 'Switch to BetterDocs UI', 'betterdocs' ),
704 'dark_mode' => $dark_mode,
705 'text' => __( 'Copied!', 'betterdocs' ),
706 'test_report' => __( 'Test Report!', 'betterdocs' ),
707 'sending' => __( 'Sending...', 'betterdocs' ),
708 'dir_url' => BETTERDOCS_ABSURL,
709 'rest_url' => esc_url_raw( rest_url() ),
710 'free_version' => betterdocs()->version,
711 'generate_data_url' => get_rest_url( null, '/betterdocs/v1/create-sample-docs' ),
712 'nonce' => wp_create_nonce( 'wp_rest' ),
713 'sync_nonce' => wp_create_nonce( 'ai_chatbot_embed' ),
714 'count_all_docs' => array_sum((array) wp_count_posts('docs')),
715 'count_all_faq' => array_sum((array) wp_count_posts('betterdocs_faq')),
716 'count_new_docs' => count(get_option('saved_docs_post_ids', [])) + count(get_option('betterdocs_ai_chatbot_error_posts', [])),
717 'admin_url' => admin_url(),
718 'ia_preview' => betterdocs()->settings->get( 'ia_enable_preview', false ),
719 'multiple_kb' => betterdocs()->settings->get( 'multiple_kb' ),
720 'previewMode' => betterdocs()->settings->get( 'ia_enable_preview', false ),
721 'dashboard_mode' => get_option( 'dashboard_mode' ),
722 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(),
723 'betterdocs_pro_version' => betterdocs()->pro_version(),
724 'analytics_older' => version_compare( betterdocs()->pro_version(), '3.3.4', '<=' ),
725 'disabled_embed_model_option' => get_option('disabled_embed_model_option'),
726 'betterdocs_ChatBot_plugin' => is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' ),
727 'total_doc_category_terms' => wp_count_terms( 'doc_category')
728 ]
729 );
730
731 // If wp-date (which includes moment.js) is not registered, enqueue your custom moment.js
732 if ( ! wp_script_is( 'wp-date', 'registered' ) ) {
733 $this->assets->enqueue( 'moment', 'vendor/js/moment.min.js', [] );
734 }
735 wp_enqueue_script( 'betterdocs-admin' );
736
737 /**
738 * Duplicate Codes Need to Be Removed From Here Onwards
739 */
740
741 //FAQ Builder Related Localization
742 betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/css/faq.css' );
743 betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/js/faq.js' );
744
745 // removing emoji support
746 remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
747 remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
748
749 betterdocs()->assets->localize(
750 'betterdocs-admin-faq',
751 'betterdocsFaq',
752 [
753 'dir_url' => BETTERDOCS_ABSURL,
754 'rest_url' => esc_url_raw( rest_url() ),
755 'free_version' => betterdocs()->version,
756 'nonce' => wp_create_nonce( 'wp_rest' ),
757 'betterdocs_settings' => get_option( 'betterdocs_settings', false )
758 ]
759 );
760
761 //Glossaries Related Localization
762 betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/css/faq.css' );
763
764 betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/js/glossaries.js' );
765
766 betterdocs()->assets->localize(
767 'betterdocs-admin-glossaries',
768 'betterdocsGlossary',
769 [
770 'dir_url' => BETTERDOCS_ABSURL,
771 'rest_url' => esc_url_raw( rest_url() ),
772 'free_version' => betterdocs()->version,
773 'nonce' => wp_create_nonce( 'wp_rest' ),
774 'betterdocs_settings' => get_option( 'betterdocs_settings', false )
775 ]
776 );
777 }
778
779 /**
780 * All admin pages header
781 *
782 * @return void
783 * @since 1.0.0
784 */
785 public function header( $admin_tab_name ) {
786 $quick_links = [
787 'switch_view' => sprintf(
788 '<a href="%s" class="betterdocs-button betterdocs-button-secondary">%s</a>',
789 add_query_arg(
790 [
791 'post_type' => 'docs',
792 'bdocs_view' => 'classic'
793 ],
794 'edit.php'
795 ),
796 __( 'Switch to Classic UI', 'betterdocs' )
797 ),
798 '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' ) )
799 ];
800
801 $quick_links = apply_filters( 'betterdocs_quick_links', $quick_links );
802
803 betterdocs()->views->get(
804 'admin/header',
805 [
806 'quick_links' => $quick_links,
807 'active_tab' => $admin_tab_name
808 ]
809 );
810 }
811
812 /**
813 * Register all the menus for BetterDocs
814 *
815 * @return void
816 * @since 1.0.0
817 */
818 public function menus() {
819 $default_args = [
820 'page_title' => 'BetterDocs',
821 'menu_title' => 'BetterDocs',
822 'capability' => 'edit_docs', // Unified capability
823 'menu_slug' => $this->slug,
824 'callback' => [ $this, 'output' ],
825 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ),
826 'position' => 5
827 ];
828
829 $_menu_position = 5;
830 global $submenu;
831
832 // Always register both UI endpoints
833 $this->register_modern_ui_fallback();
834
835 foreach ( $this->menu_list() as $key => $value ) {
836 if ( $key === 'betterdocs' ) {
837 $callable = 'add_menu_page';
838 $value = wp_parse_args( $value, $default_args );
839 call_user_func_array( $callable, $value );
840 } else {
841 $is_core_page = strpos($value['menu_slug'], '?') !== false;
842
843 if ($is_core_page) {
844 // Add classic UI directly
845 $submenu[$this->slug][] = [
846 $value['menu_title'],
847 $value['capability'],
848 $value['menu_slug'],
849 $value['page_title']
850 ];
851 } else {
852 // Add modern UI through WordPress API
853 add_submenu_page(
854 $this->slug,
855 $value['page_title'],
856 $value['menu_title'],
857 $value['capability'],
858 $value['menu_slug'],
859 $value['callback']
860 );
861 }
862 $_menu_position++;
863 }
864 }
865 }
866
867 private function register_modern_ui_fallback() {
868 // Add the submenu with valid parent slug
869 add_submenu_page(
870 'betterdocs', // Valid parent slug
871 __('All Docs', 'betterdocs'),
872 '', // Empty menu title hides it
873 'edit_docs',
874 'betterdocs-admin',
875 [ $this, 'output' ]
876 );
877
878 // Hide the menu item from appearing in the admin sidebar
879 global $submenu;
880 if (isset($submenu['betterdocs'])) {
881 foreach ($submenu['betterdocs'] as $key => $item) {
882 if ($item[2] === 'betterdocs-admin') {
883 unset($submenu['betterdocs'][$key]);
884 break;
885 }
886 }
887 }
888 }
889
890 /**
891 * BetterDocs Admin Page Output
892 *
893 * @return void
894 * @since 1.0.0
895 */
896 public function output() {
897 if ( betterdocs()->is_pro_active()
898 && version_compare( betterdocs()->pro_version(), '3.3.4', '<=' )
899 && get_current_screen()->id == 'betterdocs_page_betterdocs-analytics' ) {
900 betterdocs_pro()->views->get( 'admin/analytics-pro' );
901 } else {
902 betterdocs()->views->get(
903 'admin/main',
904 [
905 'admin_ui' => 'dnd'
906 ]
907 );
908 }
909 }
910
911 /**
912 * Menu creator helper
913 *
914 * @param string $title
915 * @param string $slug
916 * @param string $cap
917 * @param array $callback
918 *
919 * @return array
920 * @since 2.5.0
921 *
922 */
923 private function normalize_menu( $title, $slug, $cap = 'edit_docs', $callback = null, $optional = [] ) {
924 return Helper::normalize_menu( $title, $slug, $cap, $callback, $optional );
925 }
926
927 /**
928 * BetterDocs Menu List
929 *
930 * @return array
931 * @since 1.0.0
932 */
933 private function menu_list() {
934 $parent_slug = [];
935
936 $betterdocs_admin_pages = [
937 'betterdocs' => [
938 'menu_slug' => $this->slug,
939 'page_title' => 'BetterDocs',
940 'menu_title' => 'BetterDocs',
941 'capability' => 'edit_docs',
942 'callback' => [ $this, 'output' ],
943 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ),
944 'position' => 5
945 ],
946 'dashboard' => $this->normalize_menu(
947 __( 'Dashboard', 'betterdocs' ),
948 'betterdocs-dashboard',
949 'edit_docs',
950 [
951 $this,
952 'output'
953 ]
954 ),
955 'all_docs' => $this->normalize_menu(
956 __( 'All Docs', 'betterdocs' ),
957 $this->ui_slug(),
958 'edit_docs',
959 [ $this, 'output' ],
960 $parent_slug
961 ),
962 'add_new' => $this->normalize_menu(
963 __( 'Add New', 'betterdocs' ),
964 'post-new.php?post_type=docs'
965 ),
966 'categories' => $this->normalize_menu(
967 __( 'Categories', 'betterdocs' ),
968 'edit-tags.php?taxonomy=doc_category&post_type=docs',
969 'manage_doc_terms'
970 ),
971 'tags' => $this->normalize_menu(
972 __( 'Tags', 'betterdocs' ),
973 'edit-tags.php?taxonomy=doc_tag&post_type=docs',
974 'manage_doc_terms'
975 ),
976 'settings' => $this->normalize_menu(
977 __( 'Settings', 'betterdocs' ),
978 'betterdocs-settings',
979 'edit_docs_settings',
980 [
981 $this,
982 'output'
983 ],
984 $parent_slug
985 ),
986 'analytics' => $this->normalize_menu(
987 __( 'Analytics', 'betterdocs' ),
988 'betterdocs-analytics',
989 'read_docs_analytics',
990 [
991 $this,
992 'output'
993 ],
994 $parent_slug
995 ),
996 'faq' => $this->normalize_menu(
997 __( 'FAQ Builder', 'betterdocs' ),
998 'betterdocs-faq',
999 'read_faq_builder',
1000 [
1001 $this,
1002 'output'
1003 ],
1004 $parent_slug
1005 )
1006 ];
1007
1008 if ( betterdocs()->is_pro_active() && betterdocs()->settings->get( 'enable_glossaries' ) == true ) {
1009 $betterdocs_admin_pages['glossaries'] = $this->normalize_menu(
1010 __( 'Glossaries', 'betterdocs' ),
1011 'betterdocs-glossaries',
1012 'read_docs_analytics',
1013 [
1014 $this,
1015 'output'
1016 ],
1017 $parent_slug
1018 );
1019 }
1020
1021
1022 if (!betterdocs()->is_chatbot_active()) {
1023 $betterdocs_admin_pages['ai_chatbot'] = $this->normalize_menu(
1024 __('AI Chatbot', 'betterdocs'),
1025 'betterdocs-ai-chatbot',
1026 'edit_docs_settings',
1027 [
1028 $this,
1029 'output'
1030 ],
1031 $parent_slug
1032 );
1033 }
1034
1035 return apply_filters( 'betterdocs_admin_menu', $betterdocs_admin_pages, [$this, 'output'], $parent_slug);
1036
1037 }
1038
1039 public function add_custom_classes_to_menu_items() {
1040 global $menu, $submenu;
1041
1042 $menu_items = [
1043 'betterdocs' => 'betterdocs',
1044 'betterdocs_page_all_docs' => 'betterdocs-all-docs',
1045 'betterdocs_page_add_new' => 'betterdocs-add-new',
1046 'edit-tags.php?taxonomy=doc_category&post_type=docs' => 'betterdocs-categories',
1047 'edit-tags.php?taxonomy=doc_tag&post_type=docs' => 'betterdocs-tags',
1048 'betterdocs-settings' => 'betterdocs-settings',
1049 'betterdocs-analytics' => 'betterdocs-analytics',
1050 'betterdocs-faq' => 'betterdocs-faq',
1051 'betterdocs-glossaries' => 'betterdocs-glossaries',
1052 'betterdocs-ai-chatbot' => 'betterdocs-ai-chatbot',
1053 'edit-tags.php?taxonomy=knowledge_base&post_type=docs' => 'betterdocs-multiplekb'
1054 ];
1055
1056 foreach ( $menu as &$item ) {
1057 if ( isset( $menu_items[ $item[2] ] ) ) {
1058 if ( ! isset( $item[4] ) ) {
1059 $item[4] = '';
1060 }
1061 $item[4] .= '' . $menu_items[ $item[2] ];
1062 }
1063 }
1064
1065 foreach ( $submenu as &$submenu_items ) {
1066 foreach ( $submenu_items as &$sub_item ) {
1067 if ( isset( $menu_items[ $sub_item[2] ] ) ) {
1068 if ( ! isset( $sub_item[4] ) ) {
1069 $sub_item[4] = '';
1070 }
1071 $sub_item[4] .= '' . $menu_items[ $sub_item[2] ];
1072 }
1073 }
1074 }
1075 }
1076
1077 public function quick_setup_menu( $menus ) {
1078 $betterdocs_settings = get_option( 'betterdocs_settings' );
1079 if ( $betterdocs_settings ) {
1080 return $menus;
1081 } else {
1082 $menus['quick_setup'] = $this->normalize_menu(
1083 __( 'Quick Setup', 'betterdocs' ),
1084 'betterdocs-setup',
1085 'delete_users',
1086 [
1087 $this->container->get( SetupWizard::class ),
1088 'views'
1089 ]
1090 );
1091 }
1092
1093 return $menus;
1094 }
1095
1096 public function insert_plugin_links( $links ) {
1097 $links[] = '<a href="admin.php?page=betterdocs-settings">' . __( 'Settings', 'betterdocs' ) . '</a>';
1098
1099 if (! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ) ) {
1100 $links[] = '<a href="https://betterdocs.co/bfcm-2025-plugins-wp" target="_blank" style="color: #000; font-weight: bold;">' . __( 'Save 40% Today', 'betterdocs' ) . '</a>';
1101 }
1102
1103 return $links;
1104 }
1105
1106 public function toolbar_menu( $admin_bar ) {
1107 if ( ! is_admin() || ! is_admin_bar_showing() ) {
1108 return;
1109 }
1110
1111 // Show only when the user is a member of this site, or they're a super admin.
1112 if ( ! is_user_member_of_blog() && ! is_super_admin() ) {
1113 return;
1114 }
1115
1116 $docs_url = '';
1117 $encyclopedia_url = '';
1118
1119 if ( $this->settings->get( 'builtin_doc_page' ) ) {
1120 $docs_url = get_post_type_archive_link( 'docs' );
1121 } elseif ( intval( $docs_page = $this->settings->get( 'docs_page' ) ) ) {
1122 $docs_url = ! empty( $docs_page ) ? get_page_link( $docs_page ) : false;
1123 }
1124
1125 if ( ! $docs_url ) {
1126 return;
1127 }
1128
1129 $slug = $this->settings->get( 'encyclopedia_root_slug' );
1130
1131 $encyclopedia_url = home_url( $slug );
1132
1133 $admin_bar->add_node(
1134 [
1135 'parent' => 'site-name',
1136 'id' => 'view-docs',
1137 'title' => __( 'Visit Documentation', 'betterdocs' ),
1138 'href' => $docs_url
1139 ]
1140 );
1141
1142 $is_enable_encyclopedia = betterdocs()->settings->get( 'enable_encyclopedia' );
1143
1144 if ( $is_enable_encyclopedia && betterdocs()->is_pro_active() ) {
1145 $admin_bar->add_node(
1146 [
1147 'parent' => 'site-name',
1148 'id' => 'view-encyclopedia',
1149 'title' => __( 'Visit Encyclopedia', 'betterdocs' ),
1150 'href' => $encyclopedia_url
1151 ]
1152 );
1153 }
1154 }
1155
1156 /**
1157 * Save last visited admin ui
1158 *
1159 * @since 3.0.1
1160 *
1161 */
1162 public function save_admin_page() {
1163 if ( isset( $_GET['post_type'] ) && $_GET['post_type'] === 'docs' && isset( $_GET['bdocs_view'] ) && $_GET['bdocs_view'] === 'classic' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1164 update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'classic_ui' );
1165 } elseif ( isset( $_GET['page'] ) && $_GET['page'] === 'betterdocs-admin' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1166 update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'modern_ui' );
1167 }
1168 }
1169
1170 /**
1171 * Return last visited admin ui slug
1172 *
1173 * @return string
1174 * @since 3.0.1
1175 */
1176 public function ui_slug() {
1177 $last_visited = get_user_meta(get_current_user_id(), 'last_visited_docs_admin_page', true);
1178 $docs_exist = get_posts([
1179 'post_type' => 'docs',
1180 'post_status' => 'any',
1181 'numberposts' => 1
1182 ]);
1183
1184 return ($last_visited === 'modern_ui' || empty($docs_exist))
1185 ? 'betterdocs-admin'
1186 : 'edit.php?post_type=docs&bdocs_view=classic';
1187 }
1188
1189 /**
1190 * Resets a duplicate submenu in WordPress if the parent main menu and the first submenu permalink are not the same.
1191 *
1192 * @return string
1193 * @since 3.0.1
1194 */
1195 public function reset_submenu() {
1196 global $submenu;
1197
1198 $docs = get_posts( [ 'post_type' => 'docs' ] );
1199 if ( count( $docs ) == 0 ) {
1200 return;
1201 }
1202
1203 $last_visited = get_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', true );
1204
1205 if ( $last_visited === 'classic_ui' && isset( $submenu['betterdocs-admin'] ) && in_array( 'betterdocs-admin', $submenu['betterdocs-admin'][0] ) ) {
1206 unset( $submenu['betterdocs-admin'][0] );
1207 $submenu['betterdocs-admin'] = array_values( $submenu['betterdocs-admin'] );
1208 }
1209 }
1210
1211 /**
1212 * Initialize Black Friday Pointer
1213 *
1214 * @return void
1215 * @since 3.7.0
1216 */
1217 private function init_black_friday_pointer() {
1218 // Only initialize if conditions are met
1219 if ( NoticePointers::should_display_notice() ) {
1220 new NoticePointers();
1221 }
1222 }
1223
1224 /**
1225 * AJAX handler for dismissing Black Friday pointer
1226 *
1227 * @return void
1228 * @since 3.7.0
1229 */
1230 public function ajax_dismiss_black_friday_pointer() {
1231 // Verify nonce
1232 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'betterdocs_dismiss_pointer' ) ) {
1233 wp_send_json_error( [ 'message' => __( 'Invalid nonce', 'betterdocs' ) ] );
1234 }
1235
1236 // Check if user has permission
1237 if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'edit_docs' ) ) {
1238 wp_send_json_error( [ 'message' => __( 'Permission denied', 'betterdocs' ) ] );
1239 }
1240
1241 // Get the introduction key
1242 $introduction_key = isset( $_POST['introduction_key'] ) ? sanitize_text_field( $_POST['introduction_key'] ) : '';
1243
1244 if ( empty( $introduction_key ) ) {
1245 wp_send_json_error( [ 'message' => __( 'Invalid introduction key', 'betterdocs' ) ] );
1246 }
1247
1248 // Set the introduction as viewed
1249 NoticePointers::set_introduction_viewed( $introduction_key );
1250
1251 // Clear the priority option so other plugins can set their priority
1252 delete_option( '_wpdeveloper_plugin_pointer_priority' );
1253
1254 wp_send_json_success( [ 'message' => __( 'Pointer dismissed successfully', 'betterdocs' ) ] );
1255 }
1256 }
1257