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

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