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 +357 -402 4.5.64.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',
@@ -481,10 +491,10 @@
481 491 'betterdocs_page_betterdocs-analytics',
482 492 'betterdocs_page_betterdocs-glossaries',
483 493 'betterdocs_page_betterdocs-ai-chatbot',
484 494 'edit-doc_category',
485 - 'edit-doc_tag',
486 - ),
495 + 'edit-doc_tag'
496 + ],
487 497 $notices,
488 498 true
489 499 );
490 500 }
@@ -494,26 +504,26 @@
494 504 $saved_settings = get_option( 'betterdocs_settings', false );
495 505 $dark_mode = isset( $saved_settings['dark_mode'] ) ? $saved_settings['dark_mode'] : false;
496 506 $dark_mode = ! empty( $dark_mode ) ? boolval( $dark_mode ) : false;
497 507 $current_screen_id = get_current_screen() != null ? str_replace( 'betterdocs_page_', '', str_replace( 'toplevel_page_', '', str_replace( 'admin_page_', '', get_current_screen()->id ) ) ) : '';
498 - $registered_screens = array(
499 - 'betterdocs-settings',
500 - 'betterdocs-admin',
501 - 'betterdocs-dashboard',
502 - 'betterdocs-analytics',
503 - 'betterdocs-glossaries',
504 - 'betterdocs-faq',
505 - 'edit-doc_category',
506 - 'edit-doc_tag',
507 - 'edit-knowledge_base',
508 - 'betterdocs-ai-chatbot',
509 - );
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 + ];
510 520
511 - if ( in_array( $current_screen_id, $registered_screens ) ) {
512 - $classes .= ' betterdocs-admin ';
513 - }
521 + if( in_array( $current_screen_id, $registered_screens ) ) {
522 + $classes .= ' betterdocs-admin ';
523 + }
514 524
515 - if ( true === $dark_mode && in_array( $current_screen_id, $registered_screens ) ) {
525 + if ( $dark_mode === true && in_array( $current_screen_id, $registered_screens ) ) {
516 526 $classes .= ' betterdocs-dark-mode ';
517 527 }
518 528
519 529 return $classes;
@@ -528,11 +538,11 @@
528 538 * @since 1.0.0
529 539 */
530 540 public function set_custom_edit_action_columns( $columns ) {
531 541 unset( $columns['comments'] );
532 - $new_columns = array();
542 + $new_columns = [];
533 543 foreach ( $columns as $key => $value ) {
534 - if ( 'date' == $key ) {
544 + if ( $key == 'date' ) {
535 545 $new_columns['betterdocs_word_count'] = __( 'Word Count', 'betterdocs' ); // put the tags column before it
536 546 $new_columns['betterdocs_reaction'] = __( 'Reactions', 'betterdocs' );
537 547 }
538 548 $new_columns[ $key ] = $value;
@@ -544,29 +554,26 @@
544 554 public function manage_custom_columns( $column, $post_id ) {
545 555 global $wpdb;
546 556 switch ( $column ) {
547 557 case 'betterdocs_word_count':
548 - $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 ) ) );
549 559 preg_match_all( '/<[^>]*>|[\p{L}\p{M}]+/u', $content_without_html_tags, $matches );
550 - $total_words = ! empty( $matches[0] ) ? count( $matches[0] ) : count( array() );
560 + $total_words = ! empty( $matches[0] ) ? count( $matches[0] ) : count( [] );
551 561 $word_count = $total_words;
552 562 echo '<span>' . esc_html( intval( $word_count ) ) . '</span>';
553 563 break;
554 564 case 'betterdocs_reaction':
555 - // 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 ) . "'";
556 566 $analytics = $wpdb->get_results(
557 - $wpdb->prepare(
558 - "SELECT
559 - sum(impressions) as totalViews,
560 - sum(unique_visit) as totalUniqueViews,
561 - sum(happy + sad + normal) as totalReactions,
562 - sum(happy) as totalHappy,
563 - sum(normal) as totalNormal,
564 - sum(sad) as totalSad
565 - FROM {$wpdb->prefix}betterdocs_analytics
566 - WHERE post_id = %d",
567 - $post_id
568 - )
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"
569 576 );
570 577
571 578 echo '<ul class="reactions-count">
572 579 <li>
@@ -622,22 +629,22 @@
622 629 * @return void
623 630 * @since 1.0.0
624 631 */
625 632 public function styles( $hook ) {
626 - $this->assets->enqueue( 'betterdocs-global', 'admin/css/global.css', array(), 'all' );
633 + $this->assets->enqueue( 'betterdocs-global', 'admin/css/global.css', [], 'all' );
627 634
628 635 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
629 636 return;
630 637 }
631 638
632 - $this->assets->enqueue( 'betterdocs-select2', 'vendor/css/select2.min.css', array(), 'all' );
633 - $this->assets->enqueue( 'betterdocs-daterangepicker', 'vendor/css/daterangepicker.css', array(), 'all' );
634 - $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' );
635 642
636 643 /**
637 - * This scripts enqueued for Dashboard App.
638 - */
639 - $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 );
640 647 $this->assets->enqueue( 'betterdocs-icons', 'admin/btd-icon/style.css' );
641 648 }
642 649
643 650 /**
@@ -648,26 +655,26 @@
648 655 * @return void
649 656 * @since 1.0.0
650 657 */
651 658 public function scripts( $hook ) {
652 - if ( ( 'edit.php' === $hook ) && get_post_type() == 'docs' ) {
659 + if ( ( $hook === 'edit.php' ) && get_post_type() == 'docs' ) {
653 660 $this->assets->enqueue(
654 661 'betterdocs-switcher',
655 662 'admin/js/switcher.js',
656 - array(
657 - 'jquery',
658 - )
663 + [
664 + 'jquery'
665 + ]
659 666 );
660 667
661 668 $this->assets->localize(
662 669 'betterdocs-switcher',
663 670 'betterdocsSwitcher',
664 - array(
671 + [
665 672 'menu_title' => __( 'Switch to BetterDocs UI', 'betterdocs' ),
666 673 'site_address' => get_bloginfo( 'url' ),
667 674 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(),
668 - 'betterdocs_pro_version' => betterdocs()->pro_version(),
669 - )
675 + 'betterdocs_pro_version' => betterdocs()->pro_version()
676 + ]
670 677 );
671 678
672 679 return;
673 680 }
@@ -677,9 +684,9 @@
677 684 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
678 685 return;
679 686 }
680 687
681 - 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
682 689 $this->assets->register( 'betterdocs-admin', 'admin/js/dashboard.js' );
683 690
684 691 $saved_settings = get_option( 'betterdocs_settings', false );
685 692 $dark_mode = $saved_settings['dark_mode'] ?? false;
@@ -686,55 +693,53 @@
686 693 $dark_mode = ! empty( $dark_mode ) && boolval( $dark_mode );
687 694 $this->assets->localize(
688 695 'betterdocs-admin',
689 696 'betterdocs_admin',
690 - array(
697 + [
691 698 'ajaxurl' => admin_url( 'admin-ajax.php' ),
692 699 'doc_cat_order_nonce' => wp_create_nonce( 'doc_cat_order_nonce' ),
693 700 'knowledge_base_order_nonce' => wp_create_nonce( 'knowledge_base_order_nonce' ),
694 - 'paged' => isset( $_GET['paged'] ) ? absint( wp_unslash( $_GET['paged'] ) ) : 0, // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- pagination read from URL.
695 - 'per_page_id' => 'edit_doc_category_per_page',
696 - 'menu_title' => __( 'Switch to BetterDocs UI', 'betterdocs' ),
697 - 'dark_mode' => $dark_mode,
698 - 'text' => __( 'Copied!', 'betterdocs' ),
699 - 'test_report' => __( 'Test Report!', 'betterdocs' ),
700 - 'sending' => __( 'Sending...', 'betterdocs' ),
701 - 'dir_url' => BETTERDOCS_ABSURL,
702 - 'rest_url' => esc_url_raw( rest_url() ),
703 - 'free_version' => betterdocs()->version,
704 - 'generate_data_url' => get_rest_url( null, '/betterdocs/v1/create-sample-docs' ),
705 - 'nonce' => wp_create_nonce( 'wp_rest' ),
706 - 'sync_nonce' => wp_create_nonce( 'ai_chatbot_embed' ),
707 - 'count_all_docs' => array_sum( (array) wp_count_posts( 'docs' ) ),
708 - 'count_all_faq' => array_sum( (array) wp_count_posts( 'betterdocs_faq' ) ),
709 - 'count_new_docs' => $this->get_not_synced_docs_count(),
710 - 'admin_url' => admin_url(),
711 - 'ia_preview' => betterdocs()->settings->get( 'ia_enable_preview', false ),
712 - 'multiple_kb' => betterdocs()->settings->get( 'multiple_kb' ),
713 - 'previewMode' => betterdocs()->settings->get( 'ia_enable_preview', false ),
714 - 'dashboard_mode' => get_option( 'dashboard_mode' ),
715 - 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(),
716 - 'betterdocs_pro_version' => betterdocs()->pro_version(),
717 - 'analytics_older' => version_compare( betterdocs()->pro_version(), '3.3.4', '<=' ),
718 - 'disabled_embed_model_option' => get_option( 'disabled_embed_model_option' ),
719 - 'betterdocs_ChatBot_plugin' => is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' ),
720 - 'total_doc_category_terms' => wp_count_terms( 'doc_category' ),
721 - 'current_admin_language' => Helper::get_current_admin_language(),
722 - 'is_multilingual' => Helper::is_multilingual_active(),
723 - )
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 + ]
724 729 );
725 730
726 731 // If wp-date (which includes moment.js) is not registered, enqueue your custom moment.js
727 732 if ( ! wp_script_is( 'wp-date', 'registered' ) ) {
728 - $this->assets->enqueue( 'moment', 'vendor/js/moment.min.js', array() );
733 + $this->assets->enqueue( 'moment', 'vendor/js/moment.min.js', [] );
729 734 }
730 735 wp_enqueue_script( 'betterdocs-admin' );
731 736
732 737 /**
733 - * Duplicate Codes Need to Be Removed From Here Onwards
734 - */
738 + * Duplicate Codes Need to Be Removed From Here Onwards
739 + */
735 740
736 - // FAQ Builder Related Localization
741 + //FAQ Builder Related Localization
737 742 betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/css/faq.css' );
738 743 betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/js/faq.js' );
739 744
740 745 // removing emoji support
@@ -740,28 +745,21 @@
740 745 // removing emoji support
741 746 remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
742 747 remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
743 748
744 - // Get settings and remove unnecessary keys
745 - $betterdocs_settings = get_option( 'betterdocs_settings', false );
746 - if ( is_array( $betterdocs_settings ) && ! current_user_can( 'edit_docs_settings' ) ) {
747 - unset( $betterdocs_settings['ai_autowrite_api_key'] );
748 - unset( $betterdocs_settings['ai_chatbot_api_key'] );
749 - }
750 -
751 749 betterdocs()->assets->localize(
752 750 'betterdocs-admin-faq',
753 751 'betterdocsFaq',
754 - array(
752 + [
755 753 'dir_url' => BETTERDOCS_ABSURL,
756 754 'rest_url' => esc_url_raw( rest_url() ),
757 755 'free_version' => betterdocs()->version,
758 756 'nonce' => wp_create_nonce( 'wp_rest' ),
759 - 'betterdocs_settings' => $betterdocs_settings,
760 - )
757 + 'betterdocs_settings' => get_option( 'betterdocs_settings', false )
758 + ]
761 759 );
762 760
763 - // Glossaries Related Localization
761 + //Glossaries Related Localization
764 762 betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/css/faq.css' );
765 763
766 764 betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/js/glossaries.js' );
767 765
@@ -767,15 +765,15 @@
767 765
768 766 betterdocs()->assets->localize(
769 767 'betterdocs-admin-glossaries',
770 768 'betterdocsGlossary',
771 - array(
769 + [
772 770 'dir_url' => BETTERDOCS_ABSURL,
773 771 'rest_url' => esc_url_raw( rest_url() ),
774 772 'free_version' => betterdocs()->version,
775 773 'nonce' => wp_create_nonce( 'wp_rest' ),
776 - 'betterdocs_settings' => $betterdocs_settings,
777 - )
774 + 'betterdocs_settings' => get_option( 'betterdocs_settings', false )
775 + ]
778 776 );
779 777 }
780 778
781 779 /**
@@ -784,31 +782,31 @@
784 782 * @return void
785 783 * @since 1.0.0
786 784 */
787 785 public function header( $admin_tab_name ) {
788 - $quick_links = array(
786 + $quick_links = [
789 787 'switch_view' => sprintf(
790 788 '<a href="%s" class="betterdocs-button betterdocs-button-secondary">%s</a>',
791 789 add_query_arg(
792 - array(
790 + [
793 791 'post_type' => 'docs',
794 - 'bdocs_view' => 'classic',
795 - ),
792 + 'bdocs_view' => 'classic'
793 + ],
796 794 'edit.php'
797 795 ),
798 796 __( 'Switch to Classic UI', 'betterdocs' )
799 797 ),
800 - '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' ) ),
801 - );
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 + ];
802 800
803 801 $quick_links = apply_filters( 'betterdocs_quick_links', $quick_links );
804 802
805 803 betterdocs()->views->get(
806 804 'admin/header',
807 - array(
805 + [
808 806 'quick_links' => $quick_links,
809 - 'active_tab' => $admin_tab_name,
810 - )
807 + 'active_tab' => $admin_tab_name
808 + ]
811 809 );
812 810 }
813 811
814 812 /**
@@ -817,17 +815,17 @@
817 815 * @return void
818 816 * @since 1.0.0
819 817 */
820 818 public function menus() {
821 - $default_args = array(
819 + $default_args = [
822 820 'page_title' => 'BetterDocs',
823 821 'menu_title' => 'BetterDocs',
824 - 'capability' => 'edit_docs', // Unified capability
822 + 'capability' => 'edit_docs', // Unified capability
825 823 'menu_slug' => $this->slug,
826 - 'callback' => array( $this, 'output' ),
824 + 'callback' => [ $this, 'output' ],
827 825 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ),
828 - 'position' => 5,
829 - );
826 + 'position' => 5
827 + ];
830 828
831 829 $_menu_position = 5;
832 830 global $submenu;
833 831
@@ -834,23 +832,23 @@
834 832 // Always register both UI endpoints
835 833 $this->register_modern_ui_fallback();
836 834
837 835 foreach ( $this->menu_list() as $key => $value ) {
838 - if ( 'betterdocs' === $key ) {
836 + if ( $key === 'betterdocs' ) {
839 837 $callable = 'add_menu_page';
840 838 $value = wp_parse_args( $value, $default_args );
841 839 call_user_func_array( $callable, $value );
842 840 } else {
843 - $is_core_page = strpos( $value['menu_slug'], '?' ) !== false;
841 + $is_core_page = strpos($value['menu_slug'], '?') !== false;
844 842
845 - if ( $is_core_page ) {
843 + if ($is_core_page) {
846 844 // Add classic UI directly
847 - $submenu[ $this->slug ][] = array(
845 + $submenu[$this->slug][] = [
848 846 $value['menu_title'],
849 847 $value['capability'],
850 848 $value['menu_slug'],
851 - $value['page_title'],
852 - );
849 + $value['page_title']
850 + ];
853 851 } else {
854 852 // Add modern UI through WordPress API
855 853 add_submenu_page(
856 854 $this->slug,
@@ -860,9 +858,9 @@
860 858 $value['menu_slug'],
861 859 $value['callback']
862 860 );
863 861 }
864 - ++$_menu_position;
862 + $_menu_position++;
865 863 }
866 864 }
867 865 }
868 866
@@ -869,21 +867,21 @@
869 867 private function register_modern_ui_fallback() {
870 868 // Add the submenu with valid parent slug
871 869 add_submenu_page(
872 870 'betterdocs', // Valid parent slug
873 - __( 'All Docs', 'betterdocs' ),
871 + __('All Docs', 'betterdocs'),
874 872 '', // Empty menu title hides it
875 873 'edit_docs',
876 874 'betterdocs-admin',
877 - array( $this, 'output' )
875 + [ $this, 'output' ]
878 876 );
879 877
880 878 // Hide the menu item from appearing in the admin sidebar
881 879 global $submenu;
882 - if ( isset( $submenu['betterdocs'] ) ) {
883 - foreach ( $submenu['betterdocs'] as $key => $item ) {
884 - if ( 'betterdocs-admin' === $item[2] ) {
885 - 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]);
886 884 break;
887 885 }
888 886 }
889 887 }
@@ -896,17 +894,17 @@
896 894 * @since 1.0.0
897 895 */
898 896 public function output() {
899 897 if ( betterdocs()->is_pro_active()
900 - && version_compare( betterdocs()->pro_version(), '3.3.4', '<=' )
901 - && 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' ) {
902 900 betterdocs_pro()->views->get( 'admin/analytics-pro' );
903 901 } else {
904 902 betterdocs()->views->get(
905 903 'admin/main',
906 - array(
907 - 'admin_ui' => 'dnd',
908 - )
904 + [
905 + 'admin_ui' => 'dnd'
906 + ]
909 907 );
910 908 }
911 909 }
912 910
@@ -919,10 +917,11 @@
919 917 * @param array $callback
920 918 *
921 919 * @return array
922 920 * @since 2.5.0
921 + *
923 922 */
924 - 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 = [] ) {
925 924 return Helper::normalize_menu( $title, $slug, $cap, $callback, $optional );
926 925 }
927 926
928 927 /**
@@ -931,34 +930,34 @@
931 930 * @return array
932 931 * @since 1.0.0
933 932 */
934 933 private function menu_list() {
935 - $parent_slug = array();
934 + $parent_slug = [];
936 935
937 - $betterdocs_admin_pages = array(
938 - 'betterdocs' => array(
936 + $betterdocs_admin_pages = [
937 + 'betterdocs' => [
939 938 'menu_slug' => $this->slug,
940 939 'page_title' => 'BetterDocs',
941 940 'menu_title' => 'BetterDocs',
942 941 'capability' => 'edit_docs',
943 - 'callback' => array( $this, 'output' ),
942 + 'callback' => [ $this, 'output' ],
944 943 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ),
945 - 'position' => 5,
946 - ),
947 - 'dashboard' => $this->normalize_menu(
944 + 'position' => 5
945 + ],
946 + 'dashboard' => $this->normalize_menu(
948 947 __( 'Dashboard', 'betterdocs' ),
949 948 'betterdocs-dashboard',
950 949 'edit_docs',
951 - array(
950 + [
952 951 $this,
953 - 'output',
954 - )
952 + 'output'
953 + ]
955 954 ),
956 955 'all_docs' => $this->normalize_menu(
957 956 __( 'All Docs', 'betterdocs' ),
958 957 $this->ui_slug(),
959 958 'edit_docs',
960 - array( $this, 'output' ),
959 + [ $this, 'output' ],
961 960 $parent_slug
962 961 ),
963 962 'add_new' => $this->normalize_menu(
964 963 __( 'Add New', 'betterdocs' ),
@@ -977,12 +976,12 @@
977 976 'settings' => $this->normalize_menu(
978 977 __( 'Settings', 'betterdocs' ),
979 978 'betterdocs-settings',
980 979 'edit_docs_settings',
981 - array(
980 + [
982 981 $this,
983 - 'output',
984 - ),
982 + 'output'
983 + ],
985 984 $parent_slug
986 985 ),
987 986 'analytics' => $this->normalize_menu(
988 987 __( 'Analytics', 'betterdocs' ),
@@ -987,12 +986,12 @@
987 986 'analytics' => $this->normalize_menu(
988 987 __( 'Analytics', 'betterdocs' ),
989 988 'betterdocs-analytics',
990 989 'read_docs_analytics',
991 - array(
990 + [
992 991 $this,
993 - 'output',
994 - ),
992 + 'output'
993 + ],
995 994 $parent_slug
996 995 ),
997 996 'faq' => $this->normalize_menu(
998 997 __( 'FAQ Builder', 'betterdocs' ),
@@ -997,15 +996,15 @@
997 996 'faq' => $this->normalize_menu(
998 997 __( 'FAQ Builder', 'betterdocs' ),
999 998 'betterdocs-faq',
1000 999 'read_faq_builder',
1001 - array(
1000 + [
1002 1001 $this,
1003 - 'output',
1004 - ),
1002 + 'output'
1003 + ],
1005 1004 $parent_slug
1006 - ),
1007 - );
1005 + )
1006 + ];
1008 1007
1009 1008 if ( betterdocs()->is_pro_active() && betterdocs()->settings->get( 'enable_glossaries' ) == true ) {
1010 1009 $betterdocs_admin_pages['glossaries'] = $this->normalize_menu(
1011 1010 __( 'Glossaries', 'betterdocs' ),
@@ -1010,36 +1009,38 @@
1010 1009 $betterdocs_admin_pages['glossaries'] = $this->normalize_menu(
1011 1010 __( 'Glossaries', 'betterdocs' ),
1012 1011 'betterdocs-glossaries',
1013 1012 'read_docs_analytics',
1014 - array(
1013 + [
1015 1014 $this,
1016 - 'output',
1017 - ),
1015 + 'output'
1016 + ],
1018 1017 $parent_slug
1019 1018 );
1020 1019 }
1021 1020
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 - 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 + [
1028 1028 $this,
1029 - 'output',
1030 - ),
1031 - $parent_slug
1032 - );
1033 - }
1029 + 'output'
1030 + ],
1031 + $parent_slug
1032 + );
1033 + }
1034 1034
1035 - return apply_filters( 'betterdocs_admin_menu', $betterdocs_admin_pages, array( $this, 'output' ), $parent_slug );
1036 - }
1035 + return apply_filters( 'betterdocs_admin_menu', $betterdocs_admin_pages, [$this, 'output'], $parent_slug);
1037 1036
1037 + }
1038 +
1038 1039 public function add_custom_classes_to_menu_items() {
1039 1040 global $menu, $submenu;
1040 1041
1041 - $menu_items = array(
1042 + $menu_items = [
1042 1043 'betterdocs' => 'betterdocs',
1043 1044 'betterdocs_page_all_docs' => 'betterdocs-all-docs',
1044 1045 'betterdocs_page_add_new' => 'betterdocs-add-new',
1045 1046 'edit-tags.php?taxonomy=doc_category&post_type=docs' => 'betterdocs-categories',
@@ -1048,10 +1049,10 @@
1048 1049 'betterdocs-analytics' => 'betterdocs-analytics',
1049 1050 'betterdocs-faq' => 'betterdocs-faq',
1050 1051 'betterdocs-glossaries' => 'betterdocs-glossaries',
1051 1052 'betterdocs-ai-chatbot' => 'betterdocs-ai-chatbot',
1052 - 'edit-tags.php?taxonomy=knowledge_base&post_type=docs' => 'betterdocs-multiplekb',
1053 - );
1053 + 'edit-tags.php?taxonomy=knowledge_base&post_type=docs' => 'betterdocs-multiplekb'
1054 + ];
1054 1055
1055 1056 foreach ( $menu as &$item ) {
1056 1057 if ( isset( $menu_items[ $item[2] ] ) ) {
1057 1058 if ( ! isset( $item[4] ) ) {
@@ -1081,12 +1082,12 @@
1081 1082 $menus['quick_setup'] = $this->normalize_menu(
1082 1083 __( 'Quick Setup', 'betterdocs' ),
1083 1084 'betterdocs-setup',
1084 1085 'delete_users',
1085 - array(
1086 + [
1086 1087 $this->container->get( SetupWizard::class ),
1087 - 'views',
1088 - )
1088 + 'views'
1089 + ]
1089 1090 );
1090 1091 }
1091 1092
1092 1093 return $menus;
@@ -1094,10 +1095,10 @@
1094 1095
1095 1096 public function insert_plugin_links( $links ) {
1096 1097 $links[] = '<a href="admin.php?page=betterdocs-settings">' . __( 'Settings', 'betterdocs' ) . '</a>';
1097 1098
1098 - if ( ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ) ) {
1099 - $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>';
1100 1101 }
1101 1102
1102 1103 return $links;
1103 1104 }
@@ -1126,22 +1127,17 @@
1126 1127 }
1127 1128
1128 1129 $slug = $this->settings->get( 'encyclopedia_root_slug' );
1129 1130
1130 - global $wp_rewrite;
1131 - if ( $wp_rewrite->using_index_permalinks() ) {
1132 - $slug = $wp_rewrite->index . '/' . $slug;
1133 - }
1134 -
1135 1131 $encyclopedia_url = home_url( $slug );
1136 1132
1137 1133 $admin_bar->add_node(
1138 - array(
1134 + [
1139 1135 'parent' => 'site-name',
1140 1136 'id' => 'view-docs',
1141 1137 'title' => __( 'Visit Documentation', 'betterdocs' ),
1142 - 'href' => $docs_url,
1143 - )
1138 + 'href' => $docs_url
1139 + ]
1144 1140 );
1145 1141
1146 1142 $is_enable_encyclopedia = betterdocs()->settings->get( 'enable_encyclopedia' );
1147 1143
@@ -1146,14 +1142,14 @@
1146 1142 $is_enable_encyclopedia = betterdocs()->settings->get( 'enable_encyclopedia' );
1147 1143
1148 1144 if ( $is_enable_encyclopedia && betterdocs()->is_pro_active() ) {
1149 1145 $admin_bar->add_node(
1150 - array(
1146 + [
1151 1147 'parent' => 'site-name',
1152 1148 'id' => 'view-encyclopedia',
1153 1149 'title' => __( 'Visit Encyclopedia', 'betterdocs' ),
1154 - 'href' => $encyclopedia_url,
1155 - )
1150 + 'href' => $encyclopedia_url
1151 + ]
1156 1152 );
1157 1153 }
1158 1154 }
1159 1155
@@ -1160,20 +1156,14 @@
1160 1156 /**
1161 1157 * Save last visited admin ui
1162 1158 *
1163 1159 * @since 3.0.1
1160 + *
1164 1161 */
1165 1162 public function save_admin_page() {
1166 - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection.
1167 - $post_type = isset( $_GET['post_type'] ) ? sanitize_text_field( wp_unslash( $_GET['post_type'] ) ) : '';
1168 - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection.
1169 - $bdocs_view = isset( $_GET['bdocs_view'] ) ? sanitize_text_field( wp_unslash( $_GET['bdocs_view'] ) ) : '';
1170 - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection.
1171 - $page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
1172 -
1173 - 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
1174 1164 update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'classic_ui' );
1175 - } elseif ( 'betterdocs-admin' === $page ) {
1165 + } elseif ( isset( $_GET['page'] ) && $_GET['page'] === 'betterdocs-admin' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1176 1166 update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'modern_ui' );
1177 1167 }
1178 1168 }
1179 1169
@@ -1183,20 +1173,18 @@
1183 1173 * @return string
1184 1174 * @since 3.0.1
1185 1175 */
1186 1176 public function ui_slug() {
1187 - $last_visited = get_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', true );
1188 - $docs_exist = get_posts(
1189 - array(
1190 - 'post_type' => 'docs',
1191 - 'post_status' => 'any',
1192 - 'numberposts' => 1,
1193 - )
1194 - );
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 + ]);
1195 1183
1196 - return ( 'modern_ui' === $last_visited || empty( $docs_exist ) )
1197 - ? 'betterdocs-admin'
1198 - : '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';
1199 1187 }
1200 1188
1201 1189 /**
1202 1190 * Resets a duplicate submenu in WordPress if the parent main menu and the first submenu permalink are not the same.
@@ -1206,9 +1194,9 @@
1206 1194 */
1207 1195 public function reset_submenu() {
1208 1196 global $submenu;
1209 1197
1210 - $docs = get_posts( array( 'post_type' => 'docs' ) );
1198 + $docs = get_posts( [ 'post_type' => 'docs' ] );
1211 1199 if ( count( $docs ) == 0 ) {
1212 1200 return;
1213 1201 }
1214 1202
@@ -1213,9 +1201,9 @@
1213 1201 }
1214 1202
1215 1203 $last_visited = get_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', true );
1216 1204
1217 - 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] ) ) {
1218 1206 unset( $submenu['betterdocs-admin'][0] );
1219 1207 $submenu['betterdocs-admin'] = array_values( $submenu['betterdocs-admin'] );
1220 1208 }
1221 1209 }
@@ -1240,23 +1228,22 @@
1240 1228 * @since 3.7.0
1241 1229 */
1242 1230 public function ajax_dismiss_black_friday_pointer() {
1243 1231 // Verify nonce
1244 - $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
1245 - if ( ! wp_verify_nonce( $nonce, 'betterdocs_dismiss_pointer' ) ) {
1246 - 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' ) ] );
1247 1234 }
1248 1235
1249 1236 // Check if user has permission
1250 1237 if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'edit_docs' ) ) {
1251 - wp_send_json_error( array( 'message' => __( 'Permission denied', 'betterdocs' ) ) );
1238 + wp_send_json_error( [ 'message' => __( 'Permission denied', 'betterdocs' ) ] );
1252 1239 }
1253 1240
1254 1241 // Get the introduction key
1255 - $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'] ) : '';
1256 1243
1257 1244 if ( empty( $introduction_key ) ) {
1258 - wp_send_json_error( array( 'message' => __( 'Invalid introduction key', 'betterdocs' ) ) );
1245 + wp_send_json_error( [ 'message' => __( 'Invalid introduction key', 'betterdocs' ) ] );
1259 1246 }
1260 1247
1261 1248 // Set the introduction as viewed
1262 1249 NoticePointers::set_introduction_viewed( $introduction_key );
@@ -1263,39 +1250,7 @@
1263 1250
1264 1251 // Clear the priority option so other plugins can set their priority
1265 1252 delete_option( '_wpdeveloper_plugin_pointer_priority' );
1266 1253
1267 - wp_send_json_success( array( 'message' => __( 'Pointer dismissed successfully', 'betterdocs' ) ) );
1268 - }
1269 -
1270 - /**
1271 - * Get count of docs that are not yet synced
1272 - * Similar to Helper::get_not_synced_post_ids() in betterdocs-ai-chatbot plugin
1273 - *
1274 - * @return int
1275 - * @since 3.7.0
1276 - */
1277 - private function get_not_synced_docs_count() {
1278 - $new_post_ids = get_option( 'saved_docs_post_ids', array() );
1279 - $error_posts_data = get_option( 'betterdocs_ai_chatbot_error_posts', array() );
1280 -
1281 - // Extract post IDs from error posts (handle both old and new formats)
1282 - $error_post_ids = array();
1283 - if ( is_array( $error_posts_data ) && ! empty( $error_posts_data ) ) {
1284 - foreach ( $error_posts_data as $key => $value ) {
1285 - if ( is_numeric( $key ) && is_numeric( $value ) ) {
1286 - // Old format: numeric key with post ID as value
1287 - $error_post_ids[] = $value;
1288 - } elseif ( is_numeric( $key ) && is_array( $value ) && isset( $value['post_id'] ) ) {
1289 - // New format: post ID as key with structured data
1290 - $error_post_ids[] = $key;
1291 - } elseif ( is_numeric( $key ) ) {
1292 - // New format: post ID as key
1293 - $error_post_ids[] = $key;
1294 - }
1295 - }
1296 - }
1297 -
1298 - // Merge both arrays and remove duplicates, then count
1299 - return count( array_unique( array_merge( $new_post_ids, $error_post_ids ) ) );
1254 + wp_send_json_success( [ 'message' => __( 'Pointer dismissed successfully', 'betterdocs' ) ] );
1300 1255 }
1301 1256 }