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
← All changes | includes/Core/Admin.php +368 -532 4.6.24.2.6 View file →
@@ -1,12 +1,8 @@
1 1 <?php
2 +
2 3 namespace WPDeveloper\BetterDocs\Core;
3 4
4 -if ( ! defined( 'ABSPATH' ) ) {
5 - exit;
6 -}
7 -
8 -
9 5 use Exception;
10 6 use PriyoMukul\WPNotice\Notices;
11 7 use WPDeveloper\BetterDocs\Admin\NoticePointers;
12 8 use WPDeveloper\BetterDocs\Utils\Base;
@@ -12,9 +8,9 @@
12 8 use WPDeveloper\BetterDocs\Utils\Base;
13 9 use PriyoMukul\WPNotice\Utils\CacheBank;
14 10 use WPDeveloper\BetterDocs\Utils\Helper;
15 11 use WPDeveloper\BetterDocs\Utils\Enqueue;
16 -use WPDeveloper\BetterDocs\Insights\Insights;
12 +use WPDeveloper\BetterDocs\Utils\Insights;
17 13 use PriyoMukul\WPNotice\Utils\NoticeRemover;
18 14 use WPDeveloper\BetterDocs\Core\PluginInstaller;
19 15 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
20 16
@@ -24,9 +20,8 @@
24 20 */
25 21 private static $cache_bank;
26 22 /**
27 23 * Admin Root Menu Slug
28 - *
29 24 * @var string
30 25 */
31 26 private $slug = 'betterdocs-dashboard';
32 27 /**
@@ -58,19 +53,17 @@
58 53 private $kbmigration;
59 54
60 55 /**
61 56 * Enqueue
62 - *
63 57 * @var Enqueue
64 58 */
65 59 private $assets;
66 60
67 61 // modules
68 - protected $installer;
62 + protected $installer;
69 63
70 64 /**
71 65 * FAQBuilder
72 - *
73 66 * @var FAQBuilder
74 67 */
75 68 private $faq_builder;
76 69 private $glossaries;
@@ -81,10 +74,10 @@
81 74 $this->settings = $settings;
82 75 $this->kbmigration = $kbmigration;
83 76 $this->slug = 'betterdocs-dashboard';
84 77
85 - add_action( 'init', array( $type, 'register' ), 9 );
86 - add_action( 'rest_api_init', array( $this, 'order_terms_in_wp_terms_admin_table' ) );
78 + add_action( 'init', [ $type, 'register' ], 9 );
79 + add_action('rest_api_init', [$this, 'order_terms_in_wp_terms_admin_table']);
87 80
88 81 $type->init();
89 82 $type->admin_init();
90 83
@@ -90,18 +83,8 @@
90 83
91 84 $this->faq_builder = $this->container->get( FAQBuilder::class );
92 85 $this->glossaries = $this->container->get( Glossaries::class );
93 86
94 - /**
95 - * Register usage tracking (including the daily `put_do_weekly_action` cron
96 - * handler) on every request — WP-Cron runs with is_admin() === false, so
97 - * this MUST sit above the admin guard or the cron send never fires. The
98 - * admin-only UI hooks inside Insights::init() (deactivation form, footer
99 - * scripts, plugin_action_links) are context-specific and simply never run
100 - * outside wp-admin.
101 - */
102 - $this->plugin_insights();
103 -
104 87 if ( ! is_admin() ) {
105 88 return;
106 89 }
107 90
@@ -106,47 +89,47 @@
106 89 }
107 90
108 91 $this->installer = new PluginInstaller();
109 92
110 - add_action( 'admin_notices', array( $this, 'compatibility_notices' ) );
93 + $this->plugin_insights();
94 + add_action( 'admin_notices', [ $this, 'compatibility_notices' ] );
111 95 // add_action( 'admin_init', [$this, 'notices'], 9 );
112 - add_filter( 'admin_init', array( $this, 'save_admin_page' ), 99 );
96 + add_filter( 'admin_init', [ $this, 'save_admin_page' ], 99 );
113 97
114 - add_action( 'admin_menu', array( $this, 'menus' ) );
115 - add_action( 'admin_menu', array( $this, 'reset_submenu' ) );
116 - add_action( 'admin_head', array( $this, 'add_custom_classes_to_menu_items' ) );
117 - add_filter( 'plugin_action_links_' . BETTERDOCS_PLUGIN_BASENAME, array( $this, 'insert_plugin_links' ) );
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' ] );
118 102
119 103 // $this->container->get( SetupWizard::class )->init();
120 104
121 - add_action( 'admin_enqueue_scripts', array( $this, 'styles' ) );
122 - add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );
123 - // add_action( 'betterdocs_listing_header', [ $this, 'header' ], 10, 1 );
124 - add_action( 'admin_bar_menu', array( $this, 'toolbar_menu' ), 32 );
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 );
125 109
126 - add_filter( 'admin_body_class', array( $this, 'body_classes' ) );
127 - add_filter( 'parent_file', array( $type, 'highlight_admin_menu' ) );
128 - add_filter( 'submenu_file', array( $type, 'highlight_admin_submenu' ), 10, 2 );
129 - add_filter( 'betterdocs_admin_menu', array( $this, 'quick_setup_menu' ), 10, 1 );
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 );
130 114
131 115 /**
132 116 * Remove Comments Column from List Table.
133 117 */
134 - add_filter( 'manage_docs_posts_columns', array( $this, 'set_custom_edit_action_columns' ) );
135 - add_filter( 'manage_docs_posts_custom_column', array( $this, 'manage_custom_columns' ), 10, 2 );
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 );
136 120
137 121 /**
138 122 * Add New Column
139 123 */
140 - add_filter( 'manage_users_columns', array( $this, 'add_users_total_docs_column' ), 10, 1 );
141 - add_filter( 'manage_users_custom_column', array( $this, 'popular_users_docs_data' ), 10, 3 );
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 );
142 126 if ( is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ) ) {
143 127 add_action( 'admin_footer-plugins.php', array( $this, 'disable_deactivation' ) );
144 128 }
145 129
146 130 if ( $this->settings->get( 'enable_estimated_reading_time' ) ) {
147 - // Hook into unified metabox instead of creating separate metabox
148 - add_action( 'betterdocs_reading_time_tab_content', array( $this, 'render_estimated_time_markup' ) );
131 + add_action( 'add_meta_boxes', [ $this, 'reading_meta_box_' ], 10 );
149 132 }
150 133
151 134 self::$cache_bank = CacheBank::get_instance();
152 135
@@ -162,23 +145,18 @@
162 145 // Initialize Black Friday Pointer
163 146 $this->init_black_friday_pointer();
164 147
165 148 // Register AJAX handler for pointer dismissal
166 - add_action( 'wp_ajax_betterdocs_dismiss_black_friday_pointer', array( $this, 'ajax_dismiss_black_friday_pointer' ) );
149 + add_action( 'wp_ajax_betterdocs_dismiss_black_friday_pointer', [ $this, 'ajax_dismiss_black_friday_pointer' ] );
167 150 }
168 151
169 152 public function order_terms_in_wp_terms_admin_table() {
170 - // order the terms correctly to be shown on the admin panel categories menu with betterdocs order
171 - add_action(
172 - 'rest_insert_doc_category',
173 - function ( $term, $request, $bool ) {
174 - $max_order = Helper::get_max_doc_category_order_from_term_meta() ?? 0;
175 - $next_order = $max_order + 1;
176 - update_term_meta( $term->term_id, 'doc_category_order', $next_order );
177 - },
178 - 10,
179 - 3
180 - );
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);
181 159 }
182 160
183 161 public function disable_deactivation() {
184 162 $tooltip_text = esc_html__( 'Deactivate BetterDocs Pro First', 'betterdocs' );
@@ -225,17 +203,17 @@
225 203 <?php
226 204 }
227 205
228 206 public function add_users_total_docs_column( $columns ) {
229 - $new_column = array(
230 - 'docs' => __( 'Docs', 'betterdocs' ),
231 - );
207 + $new_column = [
208 + 'docs' => __( 'Docs', 'betterdocs' )
209 + ];
232 210 $columns = array_merge( $columns, $new_column );
233 211 return $columns;
234 212 }
235 213
236 214 public function popular_users_docs_data( $output, $column_name, $user_id ) {
237 - if ( 'docs' == $column_name ) {
215 + if ( $column_name == 'docs' ) {
238 216 $total_count = count_user_posts( $user_id, 'docs', true );
239 217 return '<a href="edit.php?post_type=docs&author=' . $user_id . '" class="edit"><span aria-hidden="true">' . $total_count . '</span></a>';
240 218 }
241 219 return $output;
@@ -240,8 +218,20 @@
240 218 }
241 219 return $output;
242 220 }
243 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 +
244 234 public function render_estimated_time_markup() {
245 235 betterdocs()->views->get( 'admin/metabox/estimated-reading-box' );
246 236 }
247 237
@@ -253,9 +243,9 @@
253 243 if ( isset( $plugin_data['Version'] ) && version_compare( $plugin_data['Version'], '2.5.0', '>=' ) ) {
254 244 return;
255 245 }
256 246
257 - betterdocs()->views->get( 'admin/notices/compatibility', array( 'version' => $plugin_data['Version'] ) );
247 + betterdocs()->views->get( 'admin/notices/compatibility', [ 'version' => $plugin_data['Version'] ] );
258 248 }
259 249 }
260 250
261 251 public function plugin_insights( $prevent_init = false ) {
@@ -260,20 +250,20 @@
260 250
261 251 public function plugin_insights( $prevent_init = false ) {
262 252 $this->insights = Insights::get_instance(
263 253 BETTERDOCS_PLUGIN_FILE,
264 - array(
254 + [
265 255 'opt_in' => true,
266 256 'goodbye_form' => true,
267 - 'item_id' => 'c7b16777b4f1b83f6083',
268 - )
257 + 'item_id' => 'c7b16777b4f1b83f6083'
258 + ]
269 259 );
270 260
271 261 $this->insights->set_notice_options(
272 - array(
262 + [
273 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' ),
274 - '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' ),
275 - )
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 + ]
276 266 );
277 267
278 268 if ( ! $prevent_init ) {
279 269 $this->insights->init();
@@ -289,77 +279,76 @@
289 279 * @throws Exception
290 280 */
291 281 public function notices() {
292 282 $notices = new Notices(
293 - array(
283 + [
294 284 'id' => 'betterdocs',
295 285 'storage_key' => 'notices',
296 286 'lifetime' => 3,
297 287 'stylesheet_url' => $this->assets->asset_url( 'admin/css/notices.css' ),
298 288 'styles' => $this->assets->asset_url( 'admin/css/notices.css' ),
299 - 'priority' => 4,
300 - )
289 + 'priority' => 4
290 + ]
301 291 );
302 292
303 293 /**
304 - * Review Notice
305 - *
306 - * @var mixed $message
307 - */
294 + * Review Notice
295 + * @var mixed $message
296 + */
308 297
309 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' );
310 299
311 - $_review_notice = array(
300 + $_review_notice = [
312 301 'thumbnail' => $this->assets->icon( 'betterdocs-logo.svg', true ),
313 302 'html' => '<p>' . $message . '</p>',
314 - 'links' => array(
315 - 'later' => array(
303 + 'links' => [
304 + 'later' => [
316 305 'link' => 'https://wordpress.org/plugins/betterdocs/#reviews',
317 306 'target' => '_blank',
318 307 'label' => __( 'Sure, you deserve it!', 'betterdocs' ),
319 - 'icon_class' => 'dashicons dashicons-external',
320 - ),
321 - 'allready' => array(
308 + 'icon_class' => 'dashicons dashicons-external'
309 + ],
310 + 'allready' => [
322 311 'label' => __( 'I already did', 'betterdocs' ),
323 312 'icon_class' => 'dashicons dashicons-smiley',
324 - 'attributes' => array(
325 - 'data-dismiss' => true,
326 - ),
327 - ),
328 - 'maybe_later' => array(
313 + 'attributes' => [
314 + 'data-dismiss' => true
315 + ]
316 + ],
317 + 'maybe_later' => [
329 318 'label' => __( 'Maybe Later', 'betterdocs' ),
330 319 'icon_class' => 'dashicons dashicons-calendar-alt',
331 - 'attributes' => array(
320 + 'attributes' => [
332 321 'data-later' => true,
333 - 'class' => 'dismiss-btn',
334 - ),
335 - ),
336 - 'support' => array(
322 + 'class' => 'dismiss-btn'
323 + ]
324 + ],
325 + 'support' => [
337 326 'link' => 'https://wpdeveloper.com/support',
338 - 'attributes' => array(
339 - 'target' => '_blank',
340 - ),
327 + 'attributes' => [
328 + 'target' => '_blank'
329 + ],
341 330 'label' => __( 'I need help', 'betterdocs' ),
342 - 'icon_class' => 'dashicons dashicons-sos',
343 - ),
344 - 'never_show_again' => array(
331 + 'icon_class' => 'dashicons dashicons-sos'
332 + ],
333 + 'never_show_again' => [
345 334 'label' => __( 'Never show again', 'betterdocs' ),
346 335 'icon_class' => 'dashicons dashicons-dismiss',
347 - 'attributes' => array(
348 - 'data-dismiss' => true,
349 - ),
350 - ),
351 - ),
352 - );
336 + 'attributes' => [
337 + 'data-dismiss' => true
338 + ]
339 + ]
340 + ]
341 + ];
353 342
354 343 $notices->add(
355 344 'review',
356 345 $_review_notice,
357 - array(
346 + [
358 347 'start' => $notices->strtotime( '+10 days' ),
359 348 'recurrence' => 30,
360 - 'dismissible' => true,
361 - )
349 + 'dismissible' => true
350 + ]
362 351 );
363 352
364 353 if ( $this->kbmigration->existing_plugins && ! in_array( $this->kbmigration->existing_plugins[0][0], $this->kbmigration->migrated_plugins ) ) {
365 354 $plugin_name = '<strong>' . esc_html( $this->kbmigration->existing_plugins[0][1] ) . '</strong>';
@@ -364,9 +353,9 @@
364 353 if ( $this->kbmigration->existing_plugins && ! in_array( $this->kbmigration->existing_plugins[0][0], $this->kbmigration->migrated_plugins ) ) {
365 354 $plugin_name = '<strong>' . esc_html( $this->kbmigration->existing_plugins[0][1] ) . '</strong>';
366 355
367 356 $message = sprintf(
368 - /* translators: %s is the name of the existing knowledge base plugin. */
357 + /* translators: %s is the name of the existing knowledge base plugin. */
369 358 __( 'Already using %s? Power up your Knowledge Base by migrating all your docs and settings to BetterDocs with just 1 click.', 'betterdocs' ),
370 359 esc_html( $plugin_name )
371 360 );
372 361
@@ -376,51 +365,50 @@
376 365 esc_url( admin_url( 'admin.php?page=betterdocs-settings&tab=tab-migration' ) ),
377 366 esc_html__( 'Start Migration', 'betterdocs' )
378 367 );
379 368
380 - $_migration_notice = array(
369 + $_migration_notice = [
381 370 'thumbnail' => '',
382 371 'html' => $migration_message,
383 - 'links' => array(
384 - 'maybe_later' => array(
372 + 'links' => [
373 + 'maybe_later' => [
385 374 'label' => __( 'Maybe Later', 'betterdocs' ),
386 375 'icon_class' => 'dashicons dashicons-calendar-alt',
387 - 'attributes' => array(
376 + 'attributes' => [
388 377 'data-later' => true,
389 - 'class' => 'dismiss-btn',
390 - ),
391 - ),
392 - 'never_show_again' => array(
378 + 'class' => 'dismiss-btn'
379 + ]
380 + ],
381 + 'never_show_again' => [
393 382 'label' => __( 'Never show again', 'betterdocs' ),
394 383 'icon_class' => 'dashicons dashicons-dismiss',
395 - 'attributes' => array(
396 - 'data-dismiss' => true,
397 - ),
398 - ),
399 - ),
400 - );
384 + 'attributes' => [
385 + 'data-dismiss' => true
386 + ]
387 + ]
388 + ]
389 + ];
401 390
402 391 $notices->add(
403 392 'migration',
404 393 $_migration_notice,
405 - array(
394 + [
406 395 'start' => $notices->time(),
407 396 'recurrence' => false,
408 - 'dismissible' => true,
409 - )
397 + 'dismissible' => true
398 + ]
410 399 );
411 400 }
412 401
413 402 /**
414 - *
415 403 * Opt-In Notice
416 404 */
417 405 $allow_tracking = get_option( 'wpins_allow_tracking' );
418 - if ( null != $this->insights && ! isset( $allow_tracking['betterdocs'] ) ) {
406 + if ( $this->insights != null && ! isset( $allow_tracking['betterdocs'] ) ) {
419 407 $notices->add(
420 408 'opt_in',
421 - array( $this->insights, 'notice' ),
422 - array(
409 + [ $this->insights, 'notice' ],
410 + [
423 411 'classes' => 'updated put-dismiss-notice',
424 412 'start' => $notices->time(),
425 413 'refresh' => BETTERDOCS_VERSION,
426 414 'dismissible' => true,
@@ -425,48 +413,70 @@
425 413 'refresh' => BETTERDOCS_VERSION,
426 414 'dismissible' => true,
427 415 'do_action' => 'wpdeveloper_notice_clicked_for_betterdocs',
428 416 'display_if' => ! function_exists( 'betterdocs_pro' ),
429 - 'screens' => array( 'dashboard' ),
430 - )
417 + 'screens' => [ 'dashboard' ]
418 + ]
431 419 );
432 420 }
433 421
434 - $summer_campaign_message = '<div class="betterdocs-summer-notice-body"><p style="margin-top: 0; margin-bottom: 0;">🏖️ <strong>Summer Savings:</strong> Build AI-powered Knowledge Bases & FAQs to cut support tickets and improve user experience – now <strong>up to $100 OFF!</strong></p></div>';
435 - $_summer_campaign_notice = array(
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 = [
436 424 'thumbnail' => $this->assets->icon( 'betterdocs-logo.svg', true ),
437 - 'html' => $summer_campaign_message,
438 - 'links' => array(
439 - 'support' => array(
440 - 'link' => 'https://betterdocs.co/summer2026-admin-notice',
441 - 'attributes' => array(
425 + 'html' => $blackfriday_message,
426 + 'links' => [
427 + 'support' => [
428 + 'link' => 'https://betterdocs.co/bfcm2025-admin-notice',
429 + 'attributes' => [
442 430 'target' => '_blank',
443 - 'class' => 'offer-button',
444 - ),
445 - 'label' => __( 'Upgrade To PRO Now', 'betterdocs' ),
446 - ),
447 - 'maybe_later' => array(
431 + 'class' => 'offer-button'
432 + ],
433 + 'label' => __( 'Upgrade To PRO', 'betterdocs' )
434 + ],
435 + 'maybe_later' => [
448 436 'label' => __( 'I’ll Grab It Later', 'betterdocs' ),
449 - 'attributes' => array(
450 - 'target' => '_blank',
437 + 'attributes' => [
438 + 'target' => '_blank',
451 439 'data-later' => true,
452 - 'class' => 'dismiss-btn',
453 - ),
454 - ),
455 - ),
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 + ]
456 457 );
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 +
458 468 $notices->add(
459 - 'summer-campaign-26',
460 - $_summer_campaign_notice,
461 - array(
469 + '40k_offer',
470 + $_offer_notice,
471 + [
462 472 'start' => $notices->time(),
463 473 'recurrence' => false,
464 474 'dismissible' => true,
465 475 'refresh' => BETTERDOCS_VERSION,
466 - 'expire' => strtotime( '11:59:59pm June 25, 2026' ),
467 - 'display_if' => ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ),
468 - )
476 + 'expire' => strtotime( '11:59:59pm April 30, 2025' ),
477 + 'display_if' => ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' )
478 + ]
469 479 );
470 480
471 481 self::$cache_bank->create_account( $notices );
472 482 self::$cache_bank->calculate_deposits( $notices );
@@ -471,9 +481,9 @@
471 481 self::$cache_bank->create_account( $notices );
472 482 self::$cache_bank->calculate_deposits( $notices );
473 483 if ( method_exists( self::$cache_bank, 'clear_notices_in_' ) ) {
474 484 self::$cache_bank->clear_notices_in_(
475 - array(
485 + [
476 486 'toplevel_page_betterdocs-dashboard',
477 487 'admin_page_betterdocs-admin',
478 488 'betterdocs_page_betterdocs-admin',
479 489 'betterdocs_page_betterdocs-settings',
@@ -480,13 +490,11 @@
480 490 'betterdocs_page_betterdocs-faq',
481 491 'betterdocs_page_betterdocs-analytics',
482 492 'betterdocs_page_betterdocs-glossaries',
483 493 'betterdocs_page_betterdocs-ai-chatbot',
484 - 'betterdocs_page_betterdocs-doc-categories',
485 - 'betterdocs_page_betterdocs-doc-tags',
486 494 'edit-doc_category',
487 - 'edit-doc_tag',
488 - ),
495 + 'edit-doc_tag'
496 + ],
489 497 $notices,
490 498 true
491 499 );
492 500 }
@@ -491,85 +499,31 @@
491 499 );
492 500 }
493 501 }
494 502
495 - /**
496 - * Resolve the admin dark-mode preference.
497 - *
498 - * The mode switcher stores the choice in a client cookie (no DB write, shared
499 - * across every admin screen). Fall back to the legacy
500 - * `betterdocs_settings['dark_mode']` value for installs that set it before this
501 - * change and haven't toggled since.
502 - *
503 - * @return bool
504 - */
505 - public function is_dark_mode() {
506 - if ( isset( $_COOKIE['betterdocs_admin_dark_mode'] ) ) {
507 - return '1' === $_COOKIE['betterdocs_admin_dark_mode']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
508 - }
509 -
510 - $saved = get_option( 'betterdocs_settings', array() );
511 - return ! empty( $saved['dark_mode'] );
512 - }
513 -
514 - /**
515 - * Whether the knowledge base is genuinely empty (no doc categories and no
516 - * non-trash docs). Localized to the admin so the All Docs panel can render a
517 - * skeleton shaped like the "No Category Found" empty card on first paint —
518 - * instead of a category/docs skeleton it would immediately replace — without
519 - * waiting for the REST fetch to reveal the count.
520 - *
521 - * @return bool
522 - */
523 - public function kb_is_empty() {
524 - $cats = wp_count_terms( array( 'taxonomy' => 'doc_category', 'hide_empty' => false ) );
525 - $cats = is_wp_error( $cats ) ? 0 : (int) $cats;
526 - if ( $cats > 0 ) {
527 - return false;
528 - }
529 -
530 - $counts = (array) wp_count_posts( 'docs' );
531 - $total = 0;
532 - foreach ( array( 'publish', 'future', 'draft', 'pending', 'private' ) as $status ) {
533 - $total += isset( $counts[ $status ] ) ? (int) $counts[ $status ] : 0;
534 - }
535 -
536 - return 0 === $total;
537 - }
538 -
539 503 public function body_classes( $classes ) {
540 - $dark_mode = $this->is_dark_mode();
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;
541 507 $current_screen_id = get_current_screen() != null ? str_replace( 'betterdocs_page_', '', str_replace( 'toplevel_page_', '', str_replace( 'admin_page_', '', get_current_screen()->id ) ) ) : '';
542 - /**
543 - * Filter the list of (prefix-stripped) screen ids that receive the
544 - * `betterdocs-admin` body class (and dark-mode class). Pro/add-ons can
545 - * register their own React admin pages, e.g. the Knowledge Base page.
546 - *
547 - * @param string[] $registered_screens Screen ids with the page prefix removed.
548 - */
549 - $registered_screens = apply_filters( 'betterdocs_admin_screen_slugs', array(
550 - 'betterdocs-settings',
551 - 'betterdocs-admin',
552 - 'betterdocs-dashboard',
553 - 'betterdocs-analytics',
554 - 'betterdocs-glossaries',
555 - 'betterdocs-faq',
556 - 'betterdocs-doc-categories',
557 - 'betterdocs-doc-tags',
558 - 'edit-doc_category',
559 - 'edit-doc_tag',
560 - 'edit-knowledge_base',
561 - 'betterdocs-ai-chatbot',
562 - ) );
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 + ];
563 520
564 - if ( in_array( $current_screen_id, $registered_screens ) ) {
565 - $classes .= ' betterdocs-admin ';
566 - }
521 + if( in_array( $current_screen_id, $registered_screens ) ) {
522 + $classes .= ' betterdocs-admin ';
523 + }
567 524
568 - // Dark mode also applies on the Quick Setup wizard, whose self-scoped chrome
569 - // keys off `.betterdocs_page_betterdocs-setup.betterdocs-dark-mode`.
570 - $dark_screens = array_merge( $registered_screens, array( 'betterdocs-setup' ) );
571 - if ( $dark_mode && in_array( $current_screen_id, $dark_screens, true ) ) {
525 + if ( $dark_mode === true && in_array( $current_screen_id, $registered_screens ) ) {
572 526 $classes .= ' betterdocs-dark-mode ';
573 527 }
574 528
575 529 return $classes;
@@ -584,11 +538,11 @@
584 538 * @since 1.0.0
585 539 */
586 540 public function set_custom_edit_action_columns( $columns ) {
587 541 unset( $columns['comments'] );
588 - $new_columns = array();
542 + $new_columns = [];
589 543 foreach ( $columns as $key => $value ) {
590 - if ( 'date' == $key ) {
544 + if ( $key == 'date' ) {
591 545 $new_columns['betterdocs_word_count'] = __( 'Word Count', 'betterdocs' ); // put the tags column before it
592 546 $new_columns['betterdocs_reaction'] = __( 'Reactions', 'betterdocs' );
593 547 }
594 548 $new_columns[ $key ] = $value;
@@ -600,29 +554,26 @@
600 554 public function manage_custom_columns( $column, $post_id ) {
601 555 global $wpdb;
602 556 switch ( $column ) {
603 557 case 'betterdocs_word_count':
604 - $content_without_html_tags = trim( wp_strip_all_tags( get_post_field( 'post_content', $post_id ) ) );
558 + $content_without_html_tags = trim( strip_tags( get_post_field( 'post_content', $post_id ) ) );
605 559 preg_match_all( '/<[^>]*>|[\p{L}\p{M}]+/u', $content_without_html_tags, $matches );
606 - $total_words = ! empty( $matches[0] ) ? count( $matches[0] ) : count( array() );
560 + $total_words = ! empty( $matches[0] ) ? count( $matches[0] ) : count( [] );
607 561 $word_count = $total_words;
608 562 echo '<span>' . esc_html( intval( $word_count ) ) . '</span>';
609 563 break;
610 564 case 'betterdocs_reaction':
611 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- per-post analytics aggregation rendered in admin list table; cache would mask live reactions.
565 + $where = "WHERE post_id='" . esc_sql( $post_id ) . "'";
612 566 $analytics = $wpdb->get_results(
613 - $wpdb->prepare(
614 - "SELECT
615 - sum(impressions) as totalViews,
616 - sum(unique_visit) as totalUniqueViews,
617 - sum(happy + sad + normal) as totalReactions,
618 - sum(happy) as totalHappy,
619 - sum(normal) as totalNormal,
620 - sum(sad) as totalSad
621 - FROM {$wpdb->prefix}betterdocs_analytics
622 - WHERE post_id = %d",
623 - $post_id
624 - )
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"
625 576 );
626 577
627 578 echo '<ul class="reactions-count">
628 579 <li>
@@ -678,22 +629,22 @@
678 629 * @return void
679 630 * @since 1.0.0
680 631 */
681 632 public function styles( $hook ) {
682 - $this->assets->enqueue( 'betterdocs-global', 'admin/css/global.css', array(), 'all' );
633 + $this->assets->enqueue( 'betterdocs-global', 'admin/css/global.css', [], 'all' );
683 634
684 635 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
685 636 return;
686 637 }
687 638
688 - $this->assets->enqueue( 'betterdocs-select2', 'vendor/css/select2.min.css', array(), 'all' );
689 - $this->assets->enqueue( 'betterdocs-daterangepicker', 'vendor/css/daterangepicker.css', array(), 'all' );
690 - $this->assets->enqueue( 'betterdocs-old', 'admin/css/betterdocs.css', array(), 'all' );
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' );
691 642
692 643 /**
693 - * This scripts enqueued for Dashboard App.
694 - */
695 - $this->assets->enqueue( 'betterdocs', 'admin/css/dashboard.css', array( 'betterdocs-old' ), '', BETTERDOCS_VERSION );
644 + * This scripts enqueued for Dashboard App.
645 + */
646 + $this->assets->enqueue( 'betterdocs', 'admin/css/dashboard.css', [ 'betterdocs-old' ], '', BETTERDOCS_VERSION );
696 647 $this->assets->enqueue( 'betterdocs-icons', 'admin/btd-icon/style.css' );
697 648 }
698 649
699 650 /**
@@ -704,70 +655,26 @@
704 655 * @return void
705 656 * @since 1.0.0
706 657 */
707 658 public function scripts( $hook ) {
708 - // Classic-UI screens that should offer a "Switch to BetterDocs UI"
709 - // button: All Docs, FAQ list, FAQ groups, Product FAQ groups,
710 - // Doc Categories, Doc Tags. Maps each to the React admin page to
711 - // return to; $switch_args carries extra query args (e.g. the FAQ
712 - // Builder tab) appended to the React page URL.
713 - $switch_page = '';
714 - $switch_args = array();
715 - if ( 'edit.php' === $hook && 'docs' === get_post_type() ) {
716 - $switch_page = 'betterdocs-admin';
717 - } elseif ( 'edit.php' === $hook && 'betterdocs_faq' === get_post_type() ) {
718 - $switch_page = 'betterdocs-faq';
719 - } elseif ( 'edit-tags.php' === $hook ) {
720 - $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
721 - $taxonomy = $screen && ! empty( $screen->taxonomy )
722 - ? $screen->taxonomy
723 - : ( isset( $_GET['taxonomy'] ) ? sanitize_key( wp_unslash( $_GET['taxonomy'] ) ) : '' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
724 - if ( 'betterdocs_faq_category' === $taxonomy ) {
725 - $switch_page = 'betterdocs-faq';
726 - } elseif ( 'betterdocs_product_faq_category' === $taxonomy ) {
727 - // Product FAQ groups live on the FAQ Builder's WooCommerce tab.
728 - $switch_page = 'betterdocs-faq';
729 - $switch_args = array( 'faq_tab' => 'woocommerce' );
730 - } elseif ( 'doc_category' === $taxonomy ) {
731 - $switch_page = 'betterdocs-doc-categories';
732 - } elseif ( 'doc_tag' === $taxonomy ) {
733 - $switch_page = 'betterdocs-doc-tags';
734 - }
735 - }
736 -
737 - /**
738 - * Allow Pro/add-ons to map their own classic-UI taxonomy screens to a
739 - * React admin page for the "Switch to BetterDocs UI" button — e.g. the
740 - * Knowledge Base taxonomy, which only exists when Pro is active.
741 - *
742 - * @param string $switch_page React page slug, or '' for no switcher.
743 - * @param string $hook Current admin page hook.
744 - */
745 - $switch_page = apply_filters( 'betterdocs_classic_switch_page', $switch_page, $hook );
746 -
747 - if ( $switch_page ) {
659 + if ( ( $hook === 'edit.php' ) && get_post_type() == 'docs' ) {
748 660 $this->assets->enqueue(
749 661 'betterdocs-switcher',
750 662 'admin/js/switcher.js',
751 - array(
752 - 'jquery',
753 - )
663 + [
664 + 'jquery'
665 + ]
754 666 );
755 667
756 668 $this->assets->localize(
757 669 'betterdocs-switcher',
758 670 'betterdocsSwitcher',
759 - array(
671 + [
760 672 'menu_title' => __( 'Switch to BetterDocs UI', 'betterdocs' ),
761 - 'page' => $switch_page,
762 - 'url' => add_query_arg(
763 - array_merge( array( 'page' => $switch_page ), $switch_args ),
764 - admin_url( 'admin.php' )
765 - ),
766 673 'site_address' => get_bloginfo( 'url' ),
767 674 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(),
768 - 'betterdocs_pro_version' => betterdocs()->pro_version(),
769 - )
675 + 'betterdocs_pro_version' => betterdocs()->pro_version()
676 + ]
770 677 );
771 678
772 679 return;
773 680 }
@@ -777,106 +684,82 @@
777 684 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
778 685 return;
779 686 }
780 687
781 - wp_enqueue_media(); // load early to fix problems with media upload issues on settings for WordPress 6.0.9
688 + wp_enqueue_media(); // load early to fix problems with media upload issues on settings for wordpress 6.0.9
782 689 $this->assets->register( 'betterdocs-admin', 'admin/js/dashboard.js' );
783 690
784 691 $saved_settings = get_option( 'betterdocs_settings', false );
785 - $dark_mode = $this->is_dark_mode();
692 + $dark_mode = $saved_settings['dark_mode'] ?? false;
693 + $dark_mode = ! empty( $dark_mode ) && boolval( $dark_mode );
786 694 $this->assets->localize(
787 695 'betterdocs-admin',
788 696 'betterdocs_admin',
789 - array(
697 + [
790 698 'ajaxurl' => admin_url( 'admin-ajax.php' ),
791 699 'doc_cat_order_nonce' => wp_create_nonce( 'doc_cat_order_nonce' ),
792 700 'knowledge_base_order_nonce' => wp_create_nonce( 'knowledge_base_order_nonce' ),
793 - 'paged' => isset( $_GET['paged'] ) ? absint( wp_unslash( $_GET['paged'] ) ) : 0, // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- pagination read from URL.
794 - 'per_page_id' => 'edit_doc_category_per_page',
795 - 'menu_title' => __( 'Switch to BetterDocs UI', 'betterdocs' ),
796 - 'dark_mode' => $dark_mode,
797 - 'kb_is_empty' => $this->kb_is_empty(),
798 - 'text' => __( 'Copied!', 'betterdocs' ),
799 - 'test_report' => __( 'Test Report!', 'betterdocs' ),
800 - 'sending' => __( 'Sending...', 'betterdocs' ),
801 - 'dir_url' => BETTERDOCS_ABSURL,
802 - 'rest_url' => esc_url_raw( rest_url() ),
803 - 'free_version' => betterdocs()->version,
804 - 'generate_data_url' => get_rest_url( null, '/betterdocs/v1/create-sample-docs' ),
805 - 'ai_sample_docs' => array(
806 - 'enabled' => (bool) betterdocs()->settings->get( 'enable_ai_sample_docs', true ),
807 - 'rest_base' => esc_url_raw( get_rest_url( null, '/betterdocs/v1/sample-docs' ) ),
808 - ),
809 - 'nonce' => wp_create_nonce( 'wp_rest' ),
810 - 'sync_nonce' => wp_create_nonce( 'ai_chatbot_embed' ),
811 - 'count_all_docs' => array_sum( (array) wp_count_posts( 'docs' ) ),
812 - 'count_all_faq' => array_sum( (array) wp_count_posts( 'betterdocs_faq' ) ),
813 - 'faq_order' => get_option( 'betterdocs_faq_order', 'default' ),
814 - 'count_new_docs' => $this->get_not_synced_docs_count(),
815 - 'admin_url' => admin_url(),
816 - 'ia_preview' => betterdocs()->settings->get( 'ia_enable_preview', false ),
817 - 'multiple_kb' => betterdocs()->settings->get( 'multiple_kb' ),
818 - 'previewMode' => betterdocs()->settings->get( 'ia_enable_preview', false ),
819 - 'dashboard_mode' => get_option( 'dashboard_mode' ),
820 - 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(),
821 - 'betterdocs_pro_version' => betterdocs()->pro_version(),
822 - 'analytics_older' => version_compare( betterdocs()->pro_version(), '3.3.4', '<=' ),
823 - 'disabled_embed_model_option' => get_option( 'disabled_embed_model_option' ),
824 - 'betterdocs_ChatBot_plugin' => is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' ),
825 - 'is_woocommerce_active' => class_exists( 'WooCommerce' ),
826 - 'total_doc_category_terms' => wp_count_terms( 'doc_category' ),
827 - 'current_admin_language' => Helper::get_current_admin_language(),
828 - 'is_multilingual' => Helper::is_multilingual_active(),
829 - 'languages' => Helper::get_admin_languages(),
830 - )
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 + ]
831 729 );
832 730
833 731 // If wp-date (which includes moment.js) is not registered, enqueue your custom moment.js
834 732 if ( ! wp_script_is( 'wp-date', 'registered' ) ) {
835 - $this->assets->enqueue( 'moment', 'vendor/js/moment.min.js', array() );
733 + $this->assets->enqueue( 'moment', 'vendor/js/moment.min.js', [] );
836 734 }
837 735 wp_enqueue_script( 'betterdocs-admin' );
838 736
839 737 /**
840 - * Duplicate Codes Need to Be Removed From Here Onwards
841 - */
738 + * Duplicate Codes Need to Be Removed From Here Onwards
739 + */
842 740
843 - // FAQ Builder Related Localization
741 + //FAQ Builder Related Localization
844 742 betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/css/faq.css' );
845 743 betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/js/faq.js' );
846 744
847 - // Load the classic editor (TinyMCE + QuickTags) so the FAQ rich-text editor can mount via wp.editor.initialize().
848 - if ( function_exists( 'wp_enqueue_editor' ) ) {
849 - wp_enqueue_editor();
850 - }
851 - if ( function_exists( 'wp_enqueue_media' ) ) {
852 - wp_enqueue_media();
853 - }
854 -
855 745 // removing emoji support
856 746 remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
857 747 remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
858 748
859 - // Get settings and remove unnecessary keys
860 - $betterdocs_settings = get_option( 'betterdocs_settings', false );
861 - if ( is_array( $betterdocs_settings ) && ! current_user_can( 'edit_docs_settings' ) ) {
862 - unset( $betterdocs_settings['ai_autowrite_api_key'] );
863 - unset( $betterdocs_settings['ai_chatbot_api_key'] );
864 - }
865 -
866 749 betterdocs()->assets->localize(
867 750 'betterdocs-admin-faq',
868 751 'betterdocsFaq',
869 - array(
752 + [
870 753 'dir_url' => BETTERDOCS_ABSURL,
871 754 'rest_url' => esc_url_raw( rest_url() ),
872 755 'free_version' => betterdocs()->version,
873 756 'nonce' => wp_create_nonce( 'wp_rest' ),
874 - 'betterdocs_settings' => $betterdocs_settings,
875 - )
757 + 'betterdocs_settings' => get_option( 'betterdocs_settings', false )
758 + ]
876 759 );
877 760
878 - // Glossaries Related Localization
761 + //Glossaries Related Localization
879 762 betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/css/faq.css' );
880 763
881 764 betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/js/glossaries.js' );
882 765
@@ -882,15 +765,15 @@
882 765
883 766 betterdocs()->assets->localize(
884 767 'betterdocs-admin-glossaries',
885 768 'betterdocsGlossary',
886 - array(
769 + [
887 770 'dir_url' => BETTERDOCS_ABSURL,
888 771 'rest_url' => esc_url_raw( rest_url() ),
889 772 'free_version' => betterdocs()->version,
890 773 'nonce' => wp_create_nonce( 'wp_rest' ),
891 - 'betterdocs_settings' => $betterdocs_settings,
892 - )
774 + 'betterdocs_settings' => get_option( 'betterdocs_settings', false )
775 + ]
893 776 );
894 777 }
895 778
896 779 /**
@@ -899,31 +782,31 @@
899 782 * @return void
900 783 * @since 1.0.0
901 784 */
902 785 public function header( $admin_tab_name ) {
903 - $quick_links = array(
786 + $quick_links = [
904 787 'switch_view' => sprintf(
905 788 '<a href="%s" class="betterdocs-button betterdocs-button-secondary">%s</a>',
906 789 add_query_arg(
907 - array(
790 + [
908 791 'post_type' => 'docs',
909 - 'bdocs_view' => 'classic',
910 - ),
792 + 'bdocs_view' => 'classic'
793 + ],
911 794 'edit.php'
912 795 ),
913 796 __( 'Switch to Classic UI', 'betterdocs' )
914 797 ),
915 - 'add_new_doc' => sprintf( '<a href="%s" class="betterdocs-button betterdocs-button-primary">%s</a>', add_query_arg( array( 'post_type' => 'docs' ), 'post-new.php' ), __( 'Add New Doc', 'betterdocs' ) ),
916 - );
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 + ];
917 800
918 801 $quick_links = apply_filters( 'betterdocs_quick_links', $quick_links );
919 802
920 803 betterdocs()->views->get(
921 804 'admin/header',
922 - array(
805 + [
923 806 'quick_links' => $quick_links,
924 - 'active_tab' => $admin_tab_name,
925 - )
807 + 'active_tab' => $admin_tab_name
808 + ]
926 809 );
927 810 }
928 811
929 812 /**
@@ -932,17 +815,17 @@
932 815 * @return void
933 816 * @since 1.0.0
934 817 */
935 818 public function menus() {
936 - $default_args = array(
819 + $default_args = [
937 820 'page_title' => 'BetterDocs',
938 821 'menu_title' => 'BetterDocs',
939 - 'capability' => 'edit_docs', // Unified capability
822 + 'capability' => 'edit_docs', // Unified capability
940 823 'menu_slug' => $this->slug,
941 - 'callback' => array( $this, 'output' ),
824 + 'callback' => [ $this, 'output' ],
942 825 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ),
943 - 'position' => 5,
944 - );
826 + 'position' => 5
827 + ];
945 828
946 829 $_menu_position = 5;
947 830 global $submenu;
948 831
@@ -949,23 +832,23 @@
949 832 // Always register both UI endpoints
950 833 $this->register_modern_ui_fallback();
951 834
952 835 foreach ( $this->menu_list() as $key => $value ) {
953 - if ( 'betterdocs' === $key ) {
836 + if ( $key === 'betterdocs' ) {
954 837 $callable = 'add_menu_page';
955 838 $value = wp_parse_args( $value, $default_args );
956 839 call_user_func_array( $callable, $value );
957 840 } else {
958 - $is_core_page = strpos( $value['menu_slug'], '?' ) !== false;
841 + $is_core_page = strpos($value['menu_slug'], '?') !== false;
959 842
960 - if ( $is_core_page ) {
843 + if ($is_core_page) {
961 844 // Add classic UI directly
962 - $submenu[ $this->slug ][] = array(
845 + $submenu[$this->slug][] = [
963 846 $value['menu_title'],
964 847 $value['capability'],
965 848 $value['menu_slug'],
966 - $value['page_title'],
967 - );
849 + $value['page_title']
850 + ];
968 851 } else {
969 852 // Add modern UI through WordPress API
970 853 add_submenu_page(
971 854 $this->slug,
@@ -975,9 +858,9 @@
975 858 $value['menu_slug'],
976 859 $value['callback']
977 860 );
978 861 }
979 - ++$_menu_position;
862 + $_menu_position++;
980 863 }
981 864 }
982 865 }
983 866
@@ -984,21 +867,21 @@
984 867 private function register_modern_ui_fallback() {
985 868 // Add the submenu with valid parent slug
986 869 add_submenu_page(
987 870 'betterdocs', // Valid parent slug
988 - __( 'All Docs', 'betterdocs' ),
871 + __('All Docs', 'betterdocs'),
989 872 '', // Empty menu title hides it
990 873 'edit_docs',
991 874 'betterdocs-admin',
992 - array( $this, 'output' )
875 + [ $this, 'output' ]
993 876 );
994 877
995 878 // Hide the menu item from appearing in the admin sidebar
996 879 global $submenu;
997 - if ( isset( $submenu['betterdocs'] ) ) {
998 - foreach ( $submenu['betterdocs'] as $key => $item ) {
999 - if ( 'betterdocs-admin' === $item[2] ) {
1000 - unset( $submenu['betterdocs'][ $key ] );
880 + if (isset($submenu['betterdocs'])) {
881 + foreach ($submenu['betterdocs'] as $key => $item) {
882 + if ($item[2] === 'betterdocs-admin') {
883 + unset($submenu['betterdocs'][$key]);
1001 884 break;
1002 885 }
1003 886 }
1004 887 }
@@ -1011,17 +894,17 @@
1011 894 * @since 1.0.0
1012 895 */
1013 896 public function output() {
1014 897 if ( betterdocs()->is_pro_active()
1015 - && version_compare( betterdocs()->pro_version(), '3.3.4', '<=' )
1016 - && get_current_screen()->id == 'betterdocs_page_betterdocs-analytics' ) {
898 + && version_compare( betterdocs()->pro_version(), '3.3.4', '<=' )
899 + && get_current_screen()->id == 'betterdocs_page_betterdocs-analytics' ) {
1017 900 betterdocs_pro()->views->get( 'admin/analytics-pro' );
1018 901 } else {
1019 902 betterdocs()->views->get(
1020 903 'admin/main',
1021 - array(
1022 - 'admin_ui' => 'dnd',
1023 - )
904 + [
905 + 'admin_ui' => 'dnd'
906 + ]
1024 907 );
1025 908 }
1026 909 }
1027 910
@@ -1034,10 +917,11 @@
1034 917 * @param array $callback
1035 918 *
1036 919 * @return array
1037 920 * @since 2.5.0
921 + *
1038 922 */
1039 - private function normalize_menu( $title, $slug, $cap = 'edit_docs', $callback = null, $optional = array() ) {
923 + private function normalize_menu( $title, $slug, $cap = 'edit_docs', $callback = null, $optional = [] ) {
1040 924 return Helper::normalize_menu( $title, $slug, $cap, $callback, $optional );
1041 925 }
1042 926
1043 927 /**
@@ -1046,34 +930,34 @@
1046 930 * @return array
1047 931 * @since 1.0.0
1048 932 */
1049 933 private function menu_list() {
1050 - $parent_slug = array();
934 + $parent_slug = [];
1051 935
1052 - $betterdocs_admin_pages = array(
1053 - 'betterdocs' => array(
936 + $betterdocs_admin_pages = [
937 + 'betterdocs' => [
1054 938 'menu_slug' => $this->slug,
1055 939 'page_title' => 'BetterDocs',
1056 940 'menu_title' => 'BetterDocs',
1057 941 'capability' => 'edit_docs',
1058 - 'callback' => array( $this, 'output' ),
942 + 'callback' => [ $this, 'output' ],
1059 943 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ),
1060 - 'position' => 5,
1061 - ),
1062 - 'dashboard' => $this->normalize_menu(
944 + 'position' => 5
945 + ],
946 + 'dashboard' => $this->normalize_menu(
1063 947 __( 'Dashboard', 'betterdocs' ),
1064 948 'betterdocs-dashboard',
1065 949 'edit_docs',
1066 - array(
950 + [
1067 951 $this,
1068 - 'output',
1069 - )
952 + 'output'
953 + ]
1070 954 ),
1071 955 'all_docs' => $this->normalize_menu(
1072 956 __( 'All Docs', 'betterdocs' ),
1073 957 $this->ui_slug(),
1074 958 'edit_docs',
1075 - array( $this, 'output' ),
959 + [ $this, 'output' ],
1076 960 $parent_slug
1077 961 ),
1078 962 'add_new' => $this->normalize_menu(
1079 963 __( 'Add New', 'betterdocs' ),
@@ -1080,28 +964,24 @@
1080 964 'post-new.php?post_type=docs'
1081 965 ),
1082 966 'categories' => $this->normalize_menu(
1083 967 __( 'Categories', 'betterdocs' ),
1084 - 'betterdocs-doc-categories',
1085 - 'manage_doc_terms',
1086 - array( $this, 'output' ),
1087 - $parent_slug
968 + 'edit-tags.php?taxonomy=doc_category&post_type=docs',
969 + 'manage_doc_terms'
1088 970 ),
1089 971 'tags' => $this->normalize_menu(
1090 972 __( 'Tags', 'betterdocs' ),
1091 - 'betterdocs-doc-tags',
1092 - 'manage_doc_terms',
1093 - array( $this, 'output' ),
1094 - $parent_slug
973 + 'edit-tags.php?taxonomy=doc_tag&post_type=docs',
974 + 'manage_doc_terms'
1095 975 ),
1096 976 'settings' => $this->normalize_menu(
1097 977 __( 'Settings', 'betterdocs' ),
1098 978 'betterdocs-settings',
1099 979 'edit_docs_settings',
1100 - array(
980 + [
1101 981 $this,
1102 - 'output',
1103 - ),
982 + 'output'
983 + ],
1104 984 $parent_slug
1105 985 ),
1106 986 'analytics' => $this->normalize_menu(
1107 987 __( 'Analytics', 'betterdocs' ),
@@ -1106,12 +986,12 @@
1106 986 'analytics' => $this->normalize_menu(
1107 987 __( 'Analytics', 'betterdocs' ),
1108 988 'betterdocs-analytics',
1109 989 'read_docs_analytics',
1110 - array(
990 + [
1111 991 $this,
1112 - 'output',
1113 - ),
992 + 'output'
993 + ],
1114 994 $parent_slug
1115 995 ),
1116 996 'faq' => $this->normalize_menu(
1117 997 __( 'FAQ Builder', 'betterdocs' ),
@@ -1116,15 +996,15 @@
1116 996 'faq' => $this->normalize_menu(
1117 997 __( 'FAQ Builder', 'betterdocs' ),
1118 998 'betterdocs-faq',
1119 999 'read_faq_builder',
1120 - array(
1000 + [
1121 1001 $this,
1122 - 'output',
1123 - ),
1002 + 'output'
1003 + ],
1124 1004 $parent_slug
1125 - ),
1126 - );
1005 + )
1006 + ];
1127 1007
1128 1008 if ( betterdocs()->is_pro_active() && betterdocs()->settings->get( 'enable_glossaries' ) == true ) {
1129 1009 $betterdocs_admin_pages['glossaries'] = $this->normalize_menu(
1130 1010 __( 'Glossaries', 'betterdocs' ),
@@ -1129,48 +1009,50 @@
1129 1009 $betterdocs_admin_pages['glossaries'] = $this->normalize_menu(
1130 1010 __( 'Glossaries', 'betterdocs' ),
1131 1011 'betterdocs-glossaries',
1132 1012 'read_docs_analytics',
1133 - array(
1013 + [
1134 1014 $this,
1135 - 'output',
1136 - ),
1015 + 'output'
1016 + ],
1137 1017 $parent_slug
1138 1018 );
1139 1019 }
1140 1020
1141 - if ( ! betterdocs()->is_chatbot_active() ) {
1142 - $betterdocs_admin_pages['ai_chatbot'] = $this->normalize_menu(
1143 - __( 'AI Chatbot', 'betterdocs' ),
1144 - 'betterdocs-ai-chatbot',
1145 - 'edit_docs_settings',
1146 - array(
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 + [
1147 1028 $this,
1148 - 'output',
1149 - ),
1150 - $parent_slug
1151 - );
1152 - }
1029 + 'output'
1030 + ],
1031 + $parent_slug
1032 + );
1033 + }
1153 1034
1154 - return apply_filters( 'betterdocs_admin_menu', $betterdocs_admin_pages, array( $this, 'output' ), $parent_slug );
1155 - }
1035 + return apply_filters( 'betterdocs_admin_menu', $betterdocs_admin_pages, [$this, 'output'], $parent_slug);
1156 1036
1037 + }
1038 +
1157 1039 public function add_custom_classes_to_menu_items() {
1158 1040 global $menu, $submenu;
1159 1041
1160 - $menu_items = array(
1042 + $menu_items = [
1161 1043 'betterdocs' => 'betterdocs',
1162 1044 'betterdocs_page_all_docs' => 'betterdocs-all-docs',
1163 1045 'betterdocs_page_add_new' => 'betterdocs-add-new',
1164 - 'betterdocs-doc-categories' => 'betterdocs-categories',
1165 - 'betterdocs-doc-tags' => 'betterdocs-tags',
1046 + 'edit-tags.php?taxonomy=doc_category&post_type=docs' => 'betterdocs-categories',
1047 + 'edit-tags.php?taxonomy=doc_tag&post_type=docs' => 'betterdocs-tags',
1166 1048 'betterdocs-settings' => 'betterdocs-settings',
1167 1049 'betterdocs-analytics' => 'betterdocs-analytics',
1168 1050 'betterdocs-faq' => 'betterdocs-faq',
1169 1051 'betterdocs-glossaries' => 'betterdocs-glossaries',
1170 1052 'betterdocs-ai-chatbot' => 'betterdocs-ai-chatbot',
1171 - 'edit-tags.php?taxonomy=knowledge_base&post_type=docs' => 'betterdocs-multiplekb',
1172 - );
1053 + 'edit-tags.php?taxonomy=knowledge_base&post_type=docs' => 'betterdocs-multiplekb'
1054 + ];
1173 1055
1174 1056 foreach ( $menu as &$item ) {
1175 1057 if ( isset( $menu_items[ $item[2] ] ) ) {
1176 1058 if ( ! isset( $item[4] ) ) {
@@ -1200,12 +1082,12 @@
1200 1082 $menus['quick_setup'] = $this->normalize_menu(
1201 1083 __( 'Quick Setup', 'betterdocs' ),
1202 1084 'betterdocs-setup',
1203 1085 'delete_users',
1204 - array(
1086 + [
1205 1087 $this->container->get( SetupWizard::class ),
1206 - 'views',
1207 - )
1088 + 'views'
1089 + ]
1208 1090 );
1209 1091 }
1210 1092
1211 1093 return $menus;
@@ -1213,10 +1095,10 @@
1213 1095
1214 1096 public function insert_plugin_links( $links ) {
1215 1097 $links[] = '<a href="admin.php?page=betterdocs-settings">' . __( 'Settings', 'betterdocs' ) . '</a>';
1216 1098
1217 - if ( ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ) ) {
1218 - $links[] = '<a href="https://betterdocs.co/upgrade-to-pro-plugins-wp" target="_blank" style="color: #000; font-weight: bold;">' . __( 'Upgrade to Pro', 'betterdocs' ) . '</a>';
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>';
1219 1101 }
1220 1102
1221 1103 return $links;
1222 1104 }
@@ -1245,22 +1127,17 @@
1245 1127 }
1246 1128
1247 1129 $slug = $this->settings->get( 'encyclopedia_root_slug' );
1248 1130
1249 - global $wp_rewrite;
1250 - if ( $wp_rewrite->using_index_permalinks() ) {
1251 - $slug = $wp_rewrite->index . '/' . $slug;
1252 - }
1253 -
1254 1131 $encyclopedia_url = home_url( $slug );
1255 1132
1256 1133 $admin_bar->add_node(
1257 - array(
1134 + [
1258 1135 'parent' => 'site-name',
1259 1136 'id' => 'view-docs',
1260 1137 'title' => __( 'Visit Documentation', 'betterdocs' ),
1261 - 'href' => $docs_url,
1262 - )
1138 + 'href' => $docs_url
1139 + ]
1263 1140 );
1264 1141
1265 1142 $is_enable_encyclopedia = betterdocs()->settings->get( 'enable_encyclopedia' );
1266 1143
@@ -1265,14 +1142,14 @@
1265 1142 $is_enable_encyclopedia = betterdocs()->settings->get( 'enable_encyclopedia' );
1266 1143
1267 1144 if ( $is_enable_encyclopedia && betterdocs()->is_pro_active() ) {
1268 1145 $admin_bar->add_node(
1269 - array(
1146 + [
1270 1147 'parent' => 'site-name',
1271 1148 'id' => 'view-encyclopedia',
1272 1149 'title' => __( 'Visit Encyclopedia', 'betterdocs' ),
1273 - 'href' => $encyclopedia_url,
1274 - )
1150 + 'href' => $encyclopedia_url
1151 + ]
1275 1152 );
1276 1153 }
1277 1154 }
1278 1155
@@ -1279,20 +1156,14 @@
1279 1156 /**
1280 1157 * Save last visited admin ui
1281 1158 *
1282 1159 * @since 3.0.1
1160 + *
1283 1161 */
1284 1162 public function save_admin_page() {
1285 - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection.
1286 - $post_type = isset( $_GET['post_type'] ) ? sanitize_text_field( wp_unslash( $_GET['post_type'] ) ) : '';
1287 - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection.
1288 - $bdocs_view = isset( $_GET['bdocs_view'] ) ? sanitize_text_field( wp_unslash( $_GET['bdocs_view'] ) ) : '';
1289 - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection.
1290 - $page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
1291 -
1292 - if ( 'docs' === $post_type && 'classic' === $bdocs_view ) {
1163 + if ( isset( $_GET['post_type'] ) && $_GET['post_type'] === 'docs' && isset( $_GET['bdocs_view'] ) && $_GET['bdocs_view'] === 'classic' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1293 1164 update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'classic_ui' );
1294 - } elseif ( 'betterdocs-admin' === $page ) {
1165 + } elseif ( isset( $_GET['page'] ) && $_GET['page'] === 'betterdocs-admin' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1295 1166 update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'modern_ui' );
1296 1167 }
1297 1168 }
1298 1169
@@ -1302,20 +1173,18 @@
1302 1173 * @return string
1303 1174 * @since 3.0.1
1304 1175 */
1305 1176 public function ui_slug() {
1306 - $last_visited = get_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', true );
1307 - $docs_exist = get_posts(
1308 - array(
1309 - 'post_type' => 'docs',
1310 - 'post_status' => 'any',
1311 - 'numberposts' => 1,
1312 - )
1313 - );
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 + ]);
1314 1183
1315 - return ( 'modern_ui' === $last_visited || empty( $docs_exist ) )
1316 - ? 'betterdocs-admin'
1317 - : 'edit.php?post_type=docs&bdocs_view=classic';
1184 + return ($last_visited === 'modern_ui' || empty($docs_exist))
1185 + ? 'betterdocs-admin'
1186 + : 'edit.php?post_type=docs&bdocs_view=classic';
1318 1187 }
1319 1188
1320 1189 /**
1321 1190 * Resets a duplicate submenu in WordPress if the parent main menu and the first submenu permalink are not the same.
@@ -1325,9 +1194,9 @@
1325 1194 */
1326 1195 public function reset_submenu() {
1327 1196 global $submenu;
1328 1197
1329 - $docs = get_posts( array( 'post_type' => 'docs' ) );
1198 + $docs = get_posts( [ 'post_type' => 'docs' ] );
1330 1199 if ( count( $docs ) == 0 ) {
1331 1200 return;
1332 1201 }
1333 1202
@@ -1332,9 +1201,9 @@
1332 1201 }
1333 1202
1334 1203 $last_visited = get_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', true );
1335 1204
1336 - if ( 'classic_ui' === $last_visited && isset( $submenu['betterdocs-admin'] ) && in_array( 'betterdocs-admin', $submenu['betterdocs-admin'][0] ) ) {
1205 + if ( $last_visited === 'classic_ui' && isset( $submenu['betterdocs-admin'] ) && in_array( 'betterdocs-admin', $submenu['betterdocs-admin'][0] ) ) {
1337 1206 unset( $submenu['betterdocs-admin'][0] );
1338 1207 $submenu['betterdocs-admin'] = array_values( $submenu['betterdocs-admin'] );
1339 1208 }
1340 1209 }
@@ -1359,23 +1228,22 @@
1359 1228 * @since 3.7.0
1360 1229 */
1361 1230 public function ajax_dismiss_black_friday_pointer() {
1362 1231 // Verify nonce
1363 - $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
1364 - if ( ! wp_verify_nonce( $nonce, 'betterdocs_dismiss_pointer' ) ) {
1365 - wp_send_json_error( array( 'message' => __( 'Invalid nonce', 'betterdocs' ) ) );
1232 + if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'betterdocs_dismiss_pointer' ) ) {
1233 + wp_send_json_error( [ 'message' => __( 'Invalid nonce', 'betterdocs' ) ] );
1366 1234 }
1367 1235
1368 1236 // Check if user has permission
1369 1237 if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'edit_docs' ) ) {
1370 - wp_send_json_error( array( 'message' => __( 'Permission denied', 'betterdocs' ) ) );
1238 + wp_send_json_error( [ 'message' => __( 'Permission denied', 'betterdocs' ) ] );
1371 1239 }
1372 1240
1373 1241 // Get the introduction key
1374 - $introduction_key = isset( $_POST['introduction_key'] ) ? sanitize_text_field( wp_unslash( $_POST['introduction_key'] ) ) : '';
1242 + $introduction_key = isset( $_POST['introduction_key'] ) ? sanitize_text_field( $_POST['introduction_key'] ) : '';
1375 1243
1376 1244 if ( empty( $introduction_key ) ) {
1377 - wp_send_json_error( array( 'message' => __( 'Invalid introduction key', 'betterdocs' ) ) );
1245 + wp_send_json_error( [ 'message' => __( 'Invalid introduction key', 'betterdocs' ) ] );
1378 1246 }
1379 1247
1380 1248 // Set the introduction as viewed
1381 1249 NoticePointers::set_introduction_viewed( $introduction_key );
@@ -1382,39 +1250,7 @@
1382 1250
1383 1251 // Clear the priority option so other plugins can set their priority
1384 1252 delete_option( '_wpdeveloper_plugin_pointer_priority' );
1385 1253
1386 - wp_send_json_success( array( 'message' => __( 'Pointer dismissed successfully', 'betterdocs' ) ) );
1387 - }
1388 -
1389 - /**
1390 - * Get count of docs that are not yet synced
1391 - * Similar to Helper::get_not_synced_post_ids() in betterdocs-ai-chatbot plugin
1392 - *
1393 - * @return int
1394 - * @since 3.7.0
1395 - */
1396 - private function get_not_synced_docs_count() {
1397 - $new_post_ids = get_option( 'saved_docs_post_ids', array() );
1398 - $error_posts_data = get_option( 'betterdocs_ai_chatbot_error_posts', array() );
1399 -
1400 - // Extract post IDs from error posts (handle both old and new formats)
1401 - $error_post_ids = array();
1402 - if ( is_array( $error_posts_data ) && ! empty( $error_posts_data ) ) {
1403 - foreach ( $error_posts_data as $key => $value ) {
1404 - if ( is_numeric( $key ) && is_numeric( $value ) ) {
1405 - // Old format: numeric key with post ID as value
1406 - $error_post_ids[] = $value;
1407 - } elseif ( is_numeric( $key ) && is_array( $value ) && isset( $value['post_id'] ) ) {
1408 - // New format: post ID as key with structured data
1409 - $error_post_ids[] = $key;
1410 - } elseif ( is_numeric( $key ) ) {
1411 - // New format: post ID as key
1412 - $error_post_ids[] = $key;
1413 - }
1414 - }
1415 - }
1416 -
1417 - // Merge both arrays and remove duplicates, then count
1418 - return count( array_unique( array_merge( $new_post_ids, $error_post_ids ) ) );
1254 + wp_send_json_success( [ 'message' => __( 'Pointer dismissed successfully', 'betterdocs' ) ] );
1419 1255 }
1420 1256 }