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