PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.2.0
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.2.0
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 +348 -616 4.8.24.2.0 View file →
@@ -1,20 +1,15 @@
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 -use WPDeveloper\BetterDocs\Admin\NoticePointers;
12 7 use WPDeveloper\BetterDocs\Utils\Base;
13 8 use PriyoMukul\WPNotice\Utils\CacheBank;
14 9 use WPDeveloper\BetterDocs\Utils\Helper;
15 10 use WPDeveloper\BetterDocs\Utils\Enqueue;
16 -use WPDeveloper\BetterDocs\Insights\Insights;
11 +use WPDeveloper\BetterDocs\Utils\Insights;
17 12 use PriyoMukul\WPNotice\Utils\NoticeRemover;
18 13 use WPDeveloper\BetterDocs\Core\PluginInstaller;
19 14 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
20 15
@@ -24,9 +19,8 @@
24 19 */
25 20 private static $cache_bank;
26 21 /**
27 22 * Admin Root Menu Slug
28 - *
29 23 * @var string
30 24 */
31 25 private $slug = 'betterdocs-dashboard';
32 26 /**
@@ -58,19 +52,17 @@
58 52 private $kbmigration;
59 53
60 54 /**
61 55 * Enqueue
62 - *
63 56 * @var Enqueue
64 57 */
65 58 private $assets;
66 59
67 60 // modules
68 - protected $installer;
61 + protected $installer;
69 62
70 63 /**
71 64 * FAQBuilder
72 - *
73 65 * @var FAQBuilder
74 66 */
75 67 private $faq_builder;
76 68 private $glossaries;
@@ -81,10 +73,10 @@
81 73 $this->settings = $settings;
82 74 $this->kbmigration = $kbmigration;
83 75 $this->slug = 'betterdocs-dashboard';
84 76
85 - add_action( 'init', array( $type, 'register' ), 9 );
86 - add_action( 'rest_api_init', array( $this, 'order_terms_in_wp_terms_admin_table' ) );
77 + add_action( 'init', [ $type, 'register' ], 9 );
78 + add_action('rest_api_init', [$this, 'order_terms_in_wp_terms_admin_table']);
87 79
88 80 $type->init();
89 81 $type->admin_init();
90 82
@@ -90,18 +82,8 @@
90 82
91 83 $this->faq_builder = $this->container->get( FAQBuilder::class );
92 84 $this->glossaries = $this->container->get( Glossaries::class );
93 85
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 86 if ( ! is_admin() ) {
105 87 return;
106 88 }
107 89
@@ -106,57 +88,47 @@
106 88 }
107 89
108 90 $this->installer = new PluginInstaller();
109 91
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 );
92 + $this->plugin_insights();
93 + add_action( 'admin_notices', [ $this, 'compatibility_notices' ] );
121 94 // add_action( 'admin_init', [$this, 'notices'], 9 );
122 - add_filter( 'admin_init', array( $this, 'save_admin_page' ), 99 );
95 + add_filter( 'admin_init', [ $this, 'save_admin_page' ], 99 );
123 96
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' ) );
97 + add_action( 'admin_menu', [ $this, 'menus' ] );
98 + add_action( 'admin_menu', [ $this, 'reset_submenu' ] );
99 + add_action( 'admin_head', [ $this, 'add_custom_classes_to_menu_items' ] );
100 + add_filter( 'plugin_action_links_' . BETTERDOCS_PLUGIN_BASENAME, [ $this, 'insert_plugin_links' ] );
128 101
129 102 // $this->container->get( SetupWizard::class )->init();
130 103
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 );
104 + add_action( 'admin_enqueue_scripts', [ $this, 'styles' ] );
105 + add_action( 'admin_enqueue_scripts', [ $this, 'scripts' ] );
106 + //add_action( 'betterdocs_listing_header', [ $this, 'header' ], 10, 1 );
107 + add_action( 'admin_bar_menu', [ $this, 'toolbar_menu' ], 32 );
135 108
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 );
109 + add_filter( 'admin_body_class', [ $this, 'body_classes' ] );
110 + add_filter( 'parent_file', [ $type, 'highlight_admin_menu' ] );
111 + add_filter( 'submenu_file', [ $type, 'highlight_admin_submenu' ], 10, 2 );
112 + add_filter( 'betterdocs_admin_menu', [ $this, 'quick_setup_menu' ], 10, 1 );
140 113
141 114 /**
142 115 * Remove Comments Column from List Table.
143 116 */
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 );
117 + add_filter( 'manage_docs_posts_columns', [ $this, 'set_custom_edit_action_columns' ] );
118 + add_filter( 'manage_docs_posts_custom_column', [ $this, 'manage_custom_columns' ], 10, 2 );
146 119
147 120 /**
148 121 * Add New Column
149 122 */
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 );
123 + add_filter( 'manage_users_columns', [ $this, 'add_users_total_docs_column' ], 10, 1 );
124 + add_filter( 'manage_users_custom_column', [ $this, 'popular_users_docs_data' ], 10, 3 );
152 125 if ( is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ) ) {
153 126 add_action( 'admin_footer-plugins.php', array( $this, 'disable_deactivation' ) );
154 127 }
155 128
156 129 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' ) );
130 + add_action( 'add_meta_boxes', [ $this, 'reading_meta_box_' ], 10 );
159 131 }
160 132
161 133 self::$cache_bank = CacheBank::get_instance();
162 134
@@ -167,28 +139,17 @@
167 139 $this->notices();
168 140 } catch ( Exception $e ) {
169 141 unset( $e );
170 142 }
171 -
172 - // Initialize Black Friday Pointer
173 - $this->init_black_friday_pointer();
174 -
175 - // Register AJAX handler for pointer dismissal
176 - add_action( 'wp_ajax_betterdocs_dismiss_black_friday_pointer', array( $this, 'ajax_dismiss_black_friday_pointer' ) );
177 143 }
178 144
179 145 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 - );
146 + //order the terms correctly to be shown on the admin panel categories menu with betterdocs order
147 + add_action('rest_insert_doc_category', function( $term, $request, $bool ) {
148 + $max_order = Helper::get_max_doc_category_order_from_term_meta() ?? 0;
149 + $next_order = $max_order + 1;
150 + update_term_meta( $term->term_id, 'doc_category_order', $next_order );
151 + }, 10, 3);
191 152 }
192 153
193 154 public function disable_deactivation() {
194 155 $tooltip_text = esc_html__( 'Deactivate BetterDocs Pro First', 'betterdocs' );
@@ -235,17 +196,17 @@
235 196 <?php
236 197 }
237 198
238 199 public function add_users_total_docs_column( $columns ) {
239 - $new_column = array(
240 - 'docs' => __( 'Docs', 'betterdocs' ),
241 - );
200 + $new_column = [
201 + 'docs' => __( 'Docs', 'betterdocs' )
202 + ];
242 203 $columns = array_merge( $columns, $new_column );
243 204 return $columns;
244 205 }
245 206
246 207 public function popular_users_docs_data( $output, $column_name, $user_id ) {
247 - if ( 'docs' == $column_name ) {
208 + if ( $column_name == 'docs' ) {
248 209 $total_count = count_user_posts( $user_id, 'docs', true );
249 210 return '<a href="edit.php?post_type=docs&author=' . $user_id . '" class="edit"><span aria-hidden="true">' . $total_count . '</span></a>';
250 211 }
251 212 return $output;
@@ -250,8 +211,20 @@
250 211 }
251 212 return $output;
252 213 }
253 214
215 + public function reading_meta_box_() {
216 + add_meta_box(
217 + 'betterdocs_estimated_time_metabox',
218 + __( 'Estimated Reading Time', 'betterdocs' ),
219 + [
220 + $this,
221 + 'render_estimated_time_markup'
222 + ],
223 + 'docs'
224 + );
225 + }
226 +
254 227 public function render_estimated_time_markup() {
255 228 betterdocs()->views->get( 'admin/metabox/estimated-reading-box' );
256 229 }
257 230
@@ -259,15 +232,13 @@
259 232 if ( betterdocs()->is_pro_active() ) {
260 233 $plugins = Helper::get_plugins();
261 234 $plugin_data = $plugins['betterdocs-pro/betterdocs-pro.php'];
262 235
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', '>=' ) ) {
236 + if ( isset( $plugin_data['Version'] ) && version_compare( $plugin_data['Version'], '2.5.0', '>=' ) ) {
266 237 return;
267 238 }
268 239
269 - betterdocs()->views->get( 'admin/notices/compatibility', array( 'version' => $plugin_data['Version'] ) );
240 + betterdocs()->views->get( 'admin/notices/compatibility', [ 'version' => $plugin_data['Version'] ] );
270 241 }
271 242 }
272 243
273 244 public function plugin_insights( $prevent_init = false ) {
@@ -272,20 +243,20 @@
272 243
273 244 public function plugin_insights( $prevent_init = false ) {
274 245 $this->insights = Insights::get_instance(
275 246 BETTERDOCS_PLUGIN_FILE,
276 - array(
247 + [
277 248 'opt_in' => true,
278 249 'goodbye_form' => true,
279 - 'item_id' => 'c7b16777b4f1b83f6083',
280 - )
250 + 'item_id' => 'c7b16777b4f1b83f6083'
251 + ]
281 252 );
282 253
283 254 $this->insights->set_notice_options(
284 - array(
255 + [
285 256 '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 - )
257 + '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' )
258 + ]
288 259 );
289 260
290 261 if ( ! $prevent_init ) {
291 262 $this->insights->init();
@@ -301,77 +272,76 @@
301 272 * @throws Exception
302 273 */
303 274 public function notices() {
304 275 $notices = new Notices(
305 - array(
276 + [
306 277 'id' => 'betterdocs',
307 278 'storage_key' => 'notices',
308 279 'lifetime' => 3,
309 280 'stylesheet_url' => $this->assets->asset_url( 'admin/css/notices.css' ),
310 281 'styles' => $this->assets->asset_url( 'admin/css/notices.css' ),
311 - 'priority' => 4,
312 - )
282 + 'priority' => 4
283 + ]
313 284 );
314 285
315 286 /**
316 - * Review Notice
317 - *
318 - * @var mixed $message
319 - */
287 + * Review Notice
288 + * @var mixed $message
289 + */
320 290
321 291 $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 292
323 - $_review_notice = array(
293 + $_review_notice = [
324 294 'thumbnail' => $this->assets->icon( 'betterdocs-logo.svg', true ),
325 295 'html' => '<p>' . $message . '</p>',
326 - 'links' => array(
327 - 'later' => array(
296 + 'links' => [
297 + 'later' => [
328 298 'link' => 'https://wordpress.org/plugins/betterdocs/#reviews',
329 299 'target' => '_blank',
330 300 'label' => __( 'Sure, you deserve it!', 'betterdocs' ),
331 - 'icon_class' => 'dashicons dashicons-external',
332 - ),
333 - 'allready' => array(
301 + 'icon_class' => 'dashicons dashicons-external'
302 + ],
303 + 'allready' => [
334 304 'label' => __( 'I already did', 'betterdocs' ),
335 305 'icon_class' => 'dashicons dashicons-smiley',
336 - 'attributes' => array(
337 - 'data-dismiss' => true,
338 - ),
339 - ),
340 - 'maybe_later' => array(
306 + 'attributes' => [
307 + 'data-dismiss' => true
308 + ]
309 + ],
310 + 'maybe_later' => [
341 311 'label' => __( 'Maybe Later', 'betterdocs' ),
342 312 'icon_class' => 'dashicons dashicons-calendar-alt',
343 - 'attributes' => array(
313 + 'attributes' => [
344 314 'data-later' => true,
345 - 'class' => 'dismiss-btn',
346 - ),
347 - ),
348 - 'support' => array(
315 + 'class' => 'dismiss-btn'
316 + ]
317 + ],
318 + 'support' => [
349 319 'link' => 'https://wpdeveloper.com/support',
350 - 'attributes' => array(
351 - 'target' => '_blank',
352 - ),
320 + 'attributes' => [
321 + 'target' => '_blank'
322 + ],
353 323 'label' => __( 'I need help', 'betterdocs' ),
354 - 'icon_class' => 'dashicons dashicons-sos',
355 - ),
356 - 'never_show_again' => array(
324 + 'icon_class' => 'dashicons dashicons-sos'
325 + ],
326 + 'never_show_again' => [
357 327 'label' => __( 'Never show again', 'betterdocs' ),
358 328 'icon_class' => 'dashicons dashicons-dismiss',
359 - 'attributes' => array(
360 - 'data-dismiss' => true,
361 - ),
362 - ),
363 - ),
364 - );
329 + 'attributes' => [
330 + 'data-dismiss' => true
331 + ]
332 + ]
333 + ]
334 + ];
365 335
366 336 $notices->add(
367 337 'review',
368 338 $_review_notice,
369 - array(
339 + [
370 340 'start' => $notices->strtotime( '+10 days' ),
371 341 'recurrence' => 30,
372 - 'dismissible' => true,
373 - )
342 + 'dismissible' => true
343 + ]
374 344 );
375 345
376 346 if ( $this->kbmigration->existing_plugins && ! in_array( $this->kbmigration->existing_plugins[0][0], $this->kbmigration->migrated_plugins ) ) {
377 347 $plugin_name = '<strong>' . esc_html( $this->kbmigration->existing_plugins[0][1] ) . '</strong>';
@@ -376,9 +346,9 @@
376 346 if ( $this->kbmigration->existing_plugins && ! in_array( $this->kbmigration->existing_plugins[0][0], $this->kbmigration->migrated_plugins ) ) {
377 347 $plugin_name = '<strong>' . esc_html( $this->kbmigration->existing_plugins[0][1] ) . '</strong>';
378 348
379 349 $message = sprintf(
380 - /* translators: %s is the name of the existing knowledge base plugin. */
350 + /* translators: %s is the name of the existing knowledge base plugin. */
381 351 __( 'Already using %s? Power up your Knowledge Base by migrating all your docs and settings to BetterDocs with just 1 click.', 'betterdocs' ),
382 352 esc_html( $plugin_name )
383 353 );
384 354
@@ -388,51 +358,50 @@
388 358 esc_url( admin_url( 'admin.php?page=betterdocs-settings&tab=tab-migration' ) ),
389 359 esc_html__( 'Start Migration', 'betterdocs' )
390 360 );
391 361
392 - $_migration_notice = array(
362 + $_migration_notice = [
393 363 'thumbnail' => '',
394 364 'html' => $migration_message,
395 - 'links' => array(
396 - 'maybe_later' => array(
365 + 'links' => [
366 + 'maybe_later' => [
397 367 'label' => __( 'Maybe Later', 'betterdocs' ),
398 368 'icon_class' => 'dashicons dashicons-calendar-alt',
399 - 'attributes' => array(
369 + 'attributes' => [
400 370 'data-later' => true,
401 - 'class' => 'dismiss-btn',
402 - ),
403 - ),
404 - 'never_show_again' => array(
371 + 'class' => 'dismiss-btn'
372 + ]
373 + ],
374 + 'never_show_again' => [
405 375 'label' => __( 'Never show again', 'betterdocs' ),
406 376 'icon_class' => 'dashicons dashicons-dismiss',
407 - 'attributes' => array(
408 - 'data-dismiss' => true,
409 - ),
410 - ),
411 - ),
412 - );
377 + 'attributes' => [
378 + 'data-dismiss' => true
379 + ]
380 + ]
381 + ]
382 + ];
413 383
414 384 $notices->add(
415 385 'migration',
416 386 $_migration_notice,
417 - array(
387 + [
418 388 'start' => $notices->time(),
419 389 'recurrence' => false,
420 - 'dismissible' => true,
421 - )
390 + 'dismissible' => true
391 + ]
422 392 );
423 393 }
424 394
425 395 /**
426 - *
427 396 * Opt-In Notice
428 397 */
429 398 $allow_tracking = get_option( 'wpins_allow_tracking' );
430 - if ( null != $this->insights && ! isset( $allow_tracking['betterdocs'] ) ) {
399 + if ( $this->insights != null && ! isset( $allow_tracking['betterdocs'] ) ) {
431 400 $notices->add(
432 401 'opt_in',
433 - array( $this->insights, 'notice' ),
434 - array(
402 + [ $this->insights, 'notice' ],
403 + [
435 404 'classes' => 'updated put-dismiss-notice',
436 405 'start' => $notices->time(),
437 406 'refresh' => BETTERDOCS_VERSION,
438 407 'dismissible' => true,
@@ -437,48 +406,55 @@
437 406 'refresh' => BETTERDOCS_VERSION,
438 407 'dismissible' => true,
439 408 'do_action' => 'wpdeveloper_notice_clicked_for_betterdocs',
440 409 'display_if' => ! function_exists( 'betterdocs_pro' ),
441 - 'screens' => array( 'dashboard' ),
442 - )
410 + 'screens' => [ 'dashboard' ]
411 + ]
443 412 );
444 413 }
445 414
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(
415 + // $blackfriday_message = '<div class="betterdocs-notice-body"><p style="margin-top: 0; margin-bottom: 0;"><strong>🛍️ Black Friday Specials:</strong> Save up to 35% OFF on BetterDocs PRO plans & manage docs/FAQs seamlessly.</p><a class="button button-primary" href="https://betterdocs.co/bfcm24-pricing" target="_blank"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
416 + // <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"/>
417 + // <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"/>
418 + // </svg> Upgrade To PRO</a></div>';
419 + // $_blackfriday_notice = [
420 + // 'thumbnail' => $this->assets->icon( 'betterdocs-logo.svg', true ),
421 + // 'html' => $blackfriday_message
422 + // ];
423 +
424 + // $notices->add(
425 + // 'blackfriday24',
426 + // $_blackfriday_notice,
427 + // [
428 + // 'start' => $notices->time(),
429 + // 'recurrence' => false,
430 + // 'dismissible' => true,
431 + // 'refresh' => BETTERDOCS_VERSION,
432 + // 'expire' => strtotime( '11:59:59pm December 5, 2024' ),
433 + // 'display_if' => ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' )
434 + // ]
435 + // );
436 +
437 + $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">
438 + <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"/>
439 + <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"/>
440 + </svg>Upgrade To PRO</a></div></div>';
441 + $_offer_notice = [
448 442 '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(
454 - 'target' => '_blank',
455 - 'class' => 'offer-button',
456 - ),
457 - 'label' => __( 'Upgrade To PRO Now', 'betterdocs' ),
458 - ),
459 - 'maybe_later' => array(
460 - 'label' => __( 'I’ll Grab It Later', 'betterdocs' ),
461 - 'attributes' => array(
462 - 'target' => '_blank',
463 - 'data-later' => true,
464 - 'class' => 'dismiss-btn',
465 - ),
466 - ),
467 - ),
468 - );
443 + 'html' => $offer_message
444 + ];
469 445
470 446 $notices->add(
471 - 'summer-campaign-26',
472 - $_summer_campaign_notice,
473 - array(
447 + '40k_offer',
448 + $_offer_notice,
449 + [
474 450 'start' => $notices->time(),
475 451 'recurrence' => false,
476 452 'dismissible' => true,
477 453 'refresh' => BETTERDOCS_VERSION,
478 - 'expire' => strtotime( '11:59:59pm June 25, 2026' ),
479 - 'display_if' => ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ),
480 - )
454 + 'expire' => strtotime( '11:59:59pm April 30, 2025' ),
455 + 'display_if' => ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' )
456 + ]
481 457 );
482 458
483 459 self::$cache_bank->create_account( $notices );
484 460 self::$cache_bank->calculate_deposits( $notices );
@@ -483,9 +459,9 @@
483 459 self::$cache_bank->create_account( $notices );
484 460 self::$cache_bank->calculate_deposits( $notices );
485 461 if ( method_exists( self::$cache_bank, 'clear_notices_in_' ) ) {
486 462 self::$cache_bank->clear_notices_in_(
487 - array(
463 + [
488 464 'toplevel_page_betterdocs-dashboard',
489 465 'admin_page_betterdocs-admin',
490 466 'betterdocs_page_betterdocs-admin',
491 467 'betterdocs_page_betterdocs-settings',
@@ -492,14 +468,11 @@
492 468 'betterdocs_page_betterdocs-faq',
493 469 'betterdocs_page_betterdocs-analytics',
494 470 'betterdocs_page_betterdocs-glossaries',
495 471 'betterdocs_page_betterdocs-ai-chatbot',
496 - 'betterdocs_page_betterdocs-api-docs',
497 - 'betterdocs_page_betterdocs-doc-categories',
498 - 'betterdocs_page_betterdocs-doc-tags',
499 472 'edit-doc_category',
500 - 'edit-doc_tag',
501 - ),
473 + 'edit-doc_tag'
474 + ],
502 475 $notices,
503 476 true
504 477 );
505 478 }
@@ -504,86 +477,31 @@
504 477 );
505 478 }
506 479 }
507 480
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 481 public function body_classes( $classes ) {
553 - $dark_mode = $this->is_dark_mode();
482 + $saved_settings = get_option( 'betterdocs_settings', false );
483 + $dark_mode = isset( $saved_settings['dark_mode'] ) ? $saved_settings['dark_mode'] : false;
484 + $dark_mode = ! empty( $dark_mode ) ? boolval( $dark_mode ) : false;
554 485 $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 - ) );
486 + $registered_screens = [
487 + 'betterdocs-settings',
488 + 'betterdocs-admin',
489 + 'betterdocs-dashboard',
490 + 'betterdocs-analytics',
491 + 'betterdocs-glossaries',
492 + 'betterdocs-faq',
493 + 'edit-doc_category',
494 + 'edit-doc_tag',
495 + 'edit-knowledge_base',
496 + 'betterdocs-ai-chatbot'
497 + ];
577 498
578 - if ( in_array( $current_screen_id, $registered_screens ) ) {
579 - $classes .= ' betterdocs-admin ';
580 - }
499 + if( in_array( $current_screen_id, $registered_screens ) ) {
500 + $classes .= ' betterdocs-admin ';
501 + }
581 502
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 ) ) {
503 + if ( $dark_mode === true && in_array( $current_screen_id, $registered_screens ) ) {
586 504 $classes .= ' betterdocs-dark-mode ';
587 505 }
588 506
589 507 return $classes;
@@ -598,11 +516,11 @@
598 516 * @since 1.0.0
599 517 */
600 518 public function set_custom_edit_action_columns( $columns ) {
601 519 unset( $columns['comments'] );
602 - $new_columns = array();
520 + $new_columns = [];
603 521 foreach ( $columns as $key => $value ) {
604 - if ( 'date' == $key ) {
522 + if ( $key == 'date' ) {
605 523 $new_columns['betterdocs_word_count'] = __( 'Word Count', 'betterdocs' ); // put the tags column before it
606 524 $new_columns['betterdocs_reaction'] = __( 'Reactions', 'betterdocs' );
607 525 }
608 526 $new_columns[ $key ] = $value;
@@ -614,29 +532,26 @@
614 532 public function manage_custom_columns( $column, $post_id ) {
615 533 global $wpdb;
616 534 switch ( $column ) {
617 535 case 'betterdocs_word_count':
618 - $content_without_html_tags = trim( wp_strip_all_tags( get_post_field( 'post_content', $post_id ) ) );
536 + $content_without_html_tags = trim( strip_tags( get_post_field( 'post_content', $post_id ) ) );
619 537 preg_match_all( '/<[^>]*>|[\p{L}\p{M}]+/u', $content_without_html_tags, $matches );
620 - $total_words = ! empty( $matches[0] ) ? count( $matches[0] ) : count( array() );
538 + $total_words = ! empty( $matches[0] ) ? count( $matches[0] ) : count( [] );
621 539 $word_count = $total_words;
622 540 echo '<span>' . esc_html( intval( $word_count ) ) . '</span>';
623 541 break;
624 542 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.
543 + $where = "WHERE post_id='" . esc_sql( $post_id ) . "'";
626 544 $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 - )
545 + "SELECT
546 + sum(impressions) as totalViews,
547 + sum(unique_visit) as totalUniqueViews,
548 + sum(happy + sad + normal) as totalReactions,
549 + sum(happy) as totalHappy,
550 + sum(normal) as totalNormal,
551 + sum(sad) as totalSad
552 + FROM {$wpdb->prefix}betterdocs_analytics
553 + $where"
639 554 );
640 555
641 556 echo '<ul class="reactions-count">
642 557 <li>
@@ -692,22 +607,22 @@
692 607 * @return void
693 608 * @since 1.0.0
694 609 */
695 610 public function styles( $hook ) {
696 - $this->assets->enqueue( 'betterdocs-global', 'admin/css/global.css', array(), 'all' );
611 + $this->assets->enqueue( 'betterdocs-global', 'admin/css/global.css', [], 'all' );
697 612
698 613 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
699 614 return;
700 615 }
701 616
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' );
617 + $this->assets->enqueue( 'betterdocs-select2', 'vendor/css/select2.min.css', [], 'all' );
618 + $this->assets->enqueue( 'betterdocs-daterangepicker', 'vendor/css/daterangepicker.css', [], 'all' );
619 + $this->assets->enqueue( 'betterdocs-old', 'admin/css/betterdocs.css', [], 'all' );
705 620
706 621 /**
707 - * This scripts enqueued for Dashboard App.
708 - */
709 - $this->assets->enqueue( 'betterdocs', 'admin/css/dashboard.css', array( 'betterdocs-old' ), '', BETTERDOCS_VERSION );
622 + * This scripts enqueued for Dashboard App.
623 + */
624 + $this->assets->enqueue( 'betterdocs', 'admin/css/dashboard.css', [ 'betterdocs-old' ], '', BETTERDOCS_VERSION );
710 625 $this->assets->enqueue( 'betterdocs-icons', 'admin/btd-icon/style.css' );
711 626 }
712 627
713 628 /**
@@ -718,70 +633,26 @@
718 633 * @return void
719 634 * @since 1.0.0
720 635 */
721 636 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 ) {
637 + if ( ( $hook === 'edit.php' ) && get_post_type() == 'docs' ) {
762 638 $this->assets->enqueue(
763 639 'betterdocs-switcher',
764 640 'admin/js/switcher.js',
765 - array(
766 - 'jquery',
767 - )
641 + [
642 + 'jquery'
643 + ]
768 644 );
769 645
770 646 $this->assets->localize(
771 647 'betterdocs-switcher',
772 648 'betterdocsSwitcher',
773 - array(
649 + [
774 650 '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 651 'site_address' => get_bloginfo( 'url' ),
781 652 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(),
782 - 'betterdocs_pro_version' => betterdocs()->pro_version(),
783 - )
653 + 'betterdocs_pro_version' => betterdocs()->pro_version()
654 + ]
784 655 );
785 656
786 657 return;
787 658 }
@@ -791,107 +662,82 @@
791 662 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
792 663 return;
793 664 }
794 665
795 - wp_enqueue_media(); // load early to fix problems with media upload issues on settings for WordPress 6.0.9
666 + wp_enqueue_media(); // load early to fix problems with media upload issues on settings for wordpress 6.0.9
796 667 $this->assets->register( 'betterdocs-admin', 'admin/js/dashboard.js' );
797 668
798 669 $saved_settings = get_option( 'betterdocs_settings', false );
799 - $dark_mode = $this->is_dark_mode();
670 + $dark_mode = $saved_settings['dark_mode'] ?? false;
671 + $dark_mode = ! empty( $dark_mode ) && boolval( $dark_mode );
800 672 $this->assets->localize(
801 673 'betterdocs-admin',
802 674 'betterdocs_admin',
803 - array(
675 + [
804 676 'ajaxurl' => admin_url( 'admin-ajax.php' ),
805 677 'doc_cat_order_nonce' => wp_create_nonce( 'doc_cat_order_nonce' ),
806 678 '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 - )
679 + 'paged' => isset( $_GET['paged'] ) ? absint( wp_unslash( $_GET['paged'] ) ) : 0, // phpcs:ignore WordPress.Security.NonceVerification.Missing
680 + 'per_page_id' => 'edit_doc_category_per_page',
681 + 'menu_title' => __( 'Switch to BetterDocs UI', 'betterdocs' ),
682 + 'dark_mode' => $dark_mode,
683 + 'text' => __( 'Copied!', 'betterdocs' ),
684 + 'test_report' => __( 'Test Report!', 'betterdocs' ),
685 + 'sending' => __( 'Sending...', 'betterdocs' ),
686 + 'dir_url' => BETTERDOCS_ABSURL,
687 + 'rest_url' => esc_url_raw( rest_url() ),
688 + 'free_version' => betterdocs()->version,
689 + 'generate_data_url' => get_rest_url( null, '/betterdocs/v1/create-sample-docs' ),
690 + 'nonce' => wp_create_nonce( 'wp_rest' ),
691 + 'sync_nonce' => wp_create_nonce( 'ai_chatbot_embed' ),
692 + 'count_all_docs' => array_sum((array) wp_count_posts('docs')),
693 + 'count_all_faq' => array_sum((array) wp_count_posts('betterdocs_faq')),
694 + 'count_new_docs' => count(get_option('saved_docs_post_ids', [])) + count(get_option('betterdocs_ai_chatbot_error_posts', [])),
695 + 'admin_url' => admin_url(),
696 + 'ia_preview' => betterdocs()->settings->get( 'ia_enable_preview', false ),
697 + 'multiple_kb' => betterdocs()->settings->get( 'multiple_kb' ),
698 + 'previewMode' => betterdocs()->settings->get( 'ia_enable_preview', false ),
699 + 'dashboard_mode' => get_option( 'dashboard_mode' ),
700 + 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(),
701 + 'betterdocs_pro_version' => betterdocs()->pro_version(),
702 + 'analytics_older' => version_compare( betterdocs()->pro_version(), '3.3.4', '<=' ),
703 + 'disabled_embed_model_option' => get_option('disabled_embed_model_option'),
704 + 'betterdocs_ChatBot_plugin' => is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' ),
705 + 'total_doc_category_terms' => wp_count_terms( 'doc_category')
706 + ]
845 707 );
846 708
847 709 // If wp-date (which includes moment.js) is not registered, enqueue your custom moment.js
848 710 if ( ! wp_script_is( 'wp-date', 'registered' ) ) {
849 - $this->assets->enqueue( 'moment', 'vendor/js/moment.min.js', array() );
711 + $this->assets->enqueue( 'moment', 'vendor/js/moment.min.js', [] );
850 712 }
851 713 wp_enqueue_script( 'betterdocs-admin' );
852 714
853 715 /**
854 - * Duplicate Codes Need to Be Removed From Here Onwards
855 - */
716 + * Duplicate Codes Need to Be Removed From Here Onwards
717 + */
856 718
857 - // FAQ Builder Related Localization
719 + //FAQ Builder Related Localization
858 720 betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/css/faq.css' );
859 721 betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/js/faq.js' );
860 722
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 723 // removing emoji support
870 724 remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
871 725 remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
872 726
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 727 betterdocs()->assets->localize(
882 728 'betterdocs-admin-faq',
883 729 'betterdocsFaq',
884 - array(
730 + [
885 731 'dir_url' => BETTERDOCS_ABSURL,
886 732 'rest_url' => esc_url_raw( rest_url() ),
887 733 'free_version' => betterdocs()->version,
888 734 'nonce' => wp_create_nonce( 'wp_rest' ),
889 - 'betterdocs_settings' => $betterdocs_settings,
890 - )
735 + 'betterdocs_settings' => get_option( 'betterdocs_settings', false )
736 + ]
891 737 );
892 738
893 - // Glossaries Related Localization
739 + //Glossaries Related Localization
894 740 betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/css/faq.css' );
895 741
896 742 betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/js/glossaries.js' );
897 743
@@ -897,15 +743,15 @@
897 743
898 744 betterdocs()->assets->localize(
899 745 'betterdocs-admin-glossaries',
900 746 'betterdocsGlossary',
901 - array(
747 + [
902 748 'dir_url' => BETTERDOCS_ABSURL,
903 749 'rest_url' => esc_url_raw( rest_url() ),
904 750 'free_version' => betterdocs()->version,
905 751 'nonce' => wp_create_nonce( 'wp_rest' ),
906 - 'betterdocs_settings' => $betterdocs_settings,
907 - )
752 + 'betterdocs_settings' => get_option( 'betterdocs_settings', false )
753 + ]
908 754 );
909 755 }
910 756
911 757 /**
@@ -914,31 +760,31 @@
914 760 * @return void
915 761 * @since 1.0.0
916 762 */
917 763 public function header( $admin_tab_name ) {
918 - $quick_links = array(
764 + $quick_links = [
919 765 'switch_view' => sprintf(
920 766 '<a href="%s" class="betterdocs-button betterdocs-button-secondary">%s</a>',
921 767 add_query_arg(
922 - array(
768 + [
923 769 'post_type' => 'docs',
924 - 'bdocs_view' => 'classic',
925 - ),
770 + 'bdocs_view' => 'classic'
771 + ],
926 772 'edit.php'
927 773 ),
928 774 __( 'Switch to Classic UI', 'betterdocs' )
929 775 ),
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 - );
776 + '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' ) )
777 + ];
932 778
933 779 $quick_links = apply_filters( 'betterdocs_quick_links', $quick_links );
934 780
935 781 betterdocs()->views->get(
936 782 'admin/header',
937 - array(
783 + [
938 784 'quick_links' => $quick_links,
939 - 'active_tab' => $admin_tab_name,
940 - )
785 + 'active_tab' => $admin_tab_name
786 + ]
941 787 );
942 788 }
943 789
944 790 /**
@@ -947,17 +793,17 @@
947 793 * @return void
948 794 * @since 1.0.0
949 795 */
950 796 public function menus() {
951 - $default_args = array(
797 + $default_args = [
952 798 'page_title' => 'BetterDocs',
953 799 'menu_title' => 'BetterDocs',
954 - 'capability' => 'edit_docs', // Unified capability
800 + 'capability' => 'edit_docs', // Unified capability
955 801 'menu_slug' => $this->slug,
956 - 'callback' => array( $this, 'output' ),
802 + 'callback' => [ $this, 'output' ],
957 803 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ),
958 - 'position' => 5,
959 - );
804 + 'position' => 5
805 + ];
960 806
961 807 $_menu_position = 5;
962 808 global $submenu;
963 809
@@ -964,23 +810,23 @@
964 810 // Always register both UI endpoints
965 811 $this->register_modern_ui_fallback();
966 812
967 813 foreach ( $this->menu_list() as $key => $value ) {
968 - if ( 'betterdocs' === $key ) {
814 + if ( $key === 'betterdocs' ) {
969 815 $callable = 'add_menu_page';
970 816 $value = wp_parse_args( $value, $default_args );
971 817 call_user_func_array( $callable, $value );
972 818 } else {
973 - $is_core_page = strpos( $value['menu_slug'], '?' ) !== false;
819 + $is_core_page = strpos($value['menu_slug'], '?') !== false;
974 820
975 - if ( $is_core_page ) {
821 + if ($is_core_page) {
976 822 // Add classic UI directly
977 - $submenu[ $this->slug ][] = array(
823 + $submenu[$this->slug][] = [
978 824 $value['menu_title'],
979 825 $value['capability'],
980 826 $value['menu_slug'],
981 - $value['page_title'],
982 - );
827 + $value['page_title']
828 + ];
983 829 } else {
984 830 // Add modern UI through WordPress API
985 831 add_submenu_page(
986 832 $this->slug,
@@ -990,9 +836,9 @@
990 836 $value['menu_slug'],
991 837 $value['callback']
992 838 );
993 839 }
994 - ++$_menu_position;
840 + $_menu_position++;
995 841 }
996 842 }
997 843 }
998 844
@@ -999,21 +845,21 @@
999 845 private function register_modern_ui_fallback() {
1000 846 // Add the submenu with valid parent slug
1001 847 add_submenu_page(
1002 848 'betterdocs', // Valid parent slug
1003 - __( 'All Docs', 'betterdocs' ),
849 + __('All Docs', 'betterdocs'),
1004 850 '', // Empty menu title hides it
1005 851 'edit_docs',
1006 852 'betterdocs-admin',
1007 - array( $this, 'output' )
853 + [ $this, 'output' ]
1008 854 );
1009 855
1010 856 // Hide the menu item from appearing in the admin sidebar
1011 857 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 ] );
858 + if (isset($submenu['betterdocs'])) {
859 + foreach ($submenu['betterdocs'] as $key => $item) {
860 + if ($item[2] === 'betterdocs-admin') {
861 + unset($submenu['betterdocs'][$key]);
1016 862 break;
1017 863 }
1018 864 }
1019 865 }
@@ -1026,17 +872,17 @@
1026 872 * @since 1.0.0
1027 873 */
1028 874 public function output() {
1029 875 if ( betterdocs()->is_pro_active()
1030 - && version_compare( betterdocs()->pro_version(), '3.3.4', '<=' )
1031 - && get_current_screen()->id == 'betterdocs_page_betterdocs-analytics' ) {
876 + && version_compare( betterdocs()->pro_version(), '3.3.4', '<=' )
877 + && get_current_screen()->id == 'betterdocs_page_betterdocs-analytics' ) {
1032 878 betterdocs_pro()->views->get( 'admin/analytics-pro' );
1033 879 } else {
1034 880 betterdocs()->views->get(
1035 881 'admin/main',
1036 - array(
1037 - 'admin_ui' => 'dnd',
1038 - )
882 + [
883 + 'admin_ui' => 'dnd'
884 + ]
1039 885 );
1040 886 }
1041 887 }
1042 888
@@ -1049,10 +895,11 @@
1049 895 * @param array $callback
1050 896 *
1051 897 * @return array
1052 898 * @since 2.5.0
899 + *
1053 900 */
1054 - private function normalize_menu( $title, $slug, $cap = 'edit_docs', $callback = null, $optional = array() ) {
901 + private function normalize_menu( $title, $slug, $cap = 'edit_docs', $callback = null, $optional = [] ) {
1055 902 return Helper::normalize_menu( $title, $slug, $cap, $callback, $optional );
1056 903 }
1057 904
1058 905 /**
@@ -1061,34 +908,34 @@
1061 908 * @return array
1062 909 * @since 1.0.0
1063 910 */
1064 911 private function menu_list() {
1065 - $parent_slug = array();
912 + $parent_slug = [];
1066 913
1067 - $betterdocs_admin_pages = array(
1068 - 'betterdocs' => array(
914 + $betterdocs_admin_pages = [
915 + 'betterdocs' => [
1069 916 'menu_slug' => $this->slug,
1070 917 'page_title' => 'BetterDocs',
1071 918 'menu_title' => 'BetterDocs',
1072 919 'capability' => 'edit_docs',
1073 - 'callback' => array( $this, 'output' ),
920 + 'callback' => [ $this, 'output' ],
1074 921 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ),
1075 - 'position' => 5,
1076 - ),
1077 - 'dashboard' => $this->normalize_menu(
922 + 'position' => 5
923 + ],
924 + 'dashboard' => $this->normalize_menu(
1078 925 __( 'Dashboard', 'betterdocs' ),
1079 926 'betterdocs-dashboard',
1080 927 'edit_docs',
1081 - array(
928 + [
1082 929 $this,
1083 - 'output',
1084 - )
930 + 'output'
931 + ]
1085 932 ),
1086 933 'all_docs' => $this->normalize_menu(
1087 934 __( 'All Docs', 'betterdocs' ),
1088 935 $this->ui_slug(),
1089 936 'edit_docs',
1090 - array( $this, 'output' ),
937 + [ $this, 'output' ],
1091 938 $parent_slug
1092 939 ),
1093 940 'add_new' => $this->normalize_menu(
1094 941 __( 'Add New', 'betterdocs' ),
@@ -1095,28 +942,24 @@
1095 942 'post-new.php?post_type=docs'
1096 943 ),
1097 944 'categories' => $this->normalize_menu(
1098 945 __( 'Categories', 'betterdocs' ),
1099 - 'betterdocs-doc-categories',
1100 - 'manage_doc_terms',
1101 - array( $this, 'output' ),
1102 - $parent_slug
946 + 'edit-tags.php?taxonomy=doc_category&post_type=docs',
947 + 'manage_doc_terms'
1103 948 ),
1104 949 'tags' => $this->normalize_menu(
1105 950 __( 'Tags', 'betterdocs' ),
1106 - 'betterdocs-doc-tags',
1107 - 'manage_doc_terms',
1108 - array( $this, 'output' ),
1109 - $parent_slug
951 + 'edit-tags.php?taxonomy=doc_tag&post_type=docs',
952 + 'manage_doc_terms'
1110 953 ),
1111 954 'settings' => $this->normalize_menu(
1112 955 __( 'Settings', 'betterdocs' ),
1113 956 'betterdocs-settings',
1114 957 'edit_docs_settings',
1115 - array(
958 + [
1116 959 $this,
1117 - 'output',
1118 - ),
960 + 'output'
961 + ],
1119 962 $parent_slug
1120 963 ),
1121 964 'analytics' => $this->normalize_menu(
1122 965 __( 'Analytics', 'betterdocs' ),
@@ -1121,12 +964,12 @@
1121 964 'analytics' => $this->normalize_menu(
1122 965 __( 'Analytics', 'betterdocs' ),
1123 966 'betterdocs-analytics',
1124 967 'read_docs_analytics',
1125 - array(
968 + [
1126 969 $this,
1127 - 'output',
1128 - ),
970 + 'output'
971 + ],
1129 972 $parent_slug
1130 973 ),
1131 974 'faq' => $this->normalize_menu(
1132 975 __( 'FAQ Builder', 'betterdocs' ),
@@ -1131,15 +974,15 @@
1131 974 'faq' => $this->normalize_menu(
1132 975 __( 'FAQ Builder', 'betterdocs' ),
1133 976 'betterdocs-faq',
1134 977 'read_faq_builder',
1135 - array(
978 + [
1136 979 $this,
1137 - 'output',
1138 - ),
980 + 'output'
981 + ],
1139 982 $parent_slug
1140 - ),
1141 - );
983 + )
984 + ];
1142 985
1143 986 if ( betterdocs()->is_pro_active() && betterdocs()->settings->get( 'enable_glossaries' ) == true ) {
1144 987 $betterdocs_admin_pages['glossaries'] = $this->normalize_menu(
1145 988 __( 'Glossaries', 'betterdocs' ),
@@ -1144,65 +987,50 @@
1144 987 $betterdocs_admin_pages['glossaries'] = $this->normalize_menu(
1145 988 __( 'Glossaries', 'betterdocs' ),
1146 989 'betterdocs-glossaries',
1147 990 'read_docs_analytics',
1148 - array(
991 + [
1149 992 $this,
1150 - 'output',
1151 - ),
993 + 'output'
994 + ],
1152 995 $parent_slug
1153 996 );
1154 997 }
1155 998
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(
999 +
1000 + if (!betterdocs()->is_chatbot_active()) {
1001 + $betterdocs_admin_pages['ai_chatbot'] = $this->normalize_menu(
1002 + __('AI Chatbot', 'betterdocs'),
1003 + 'betterdocs-ai-chatbot',
1004 + 'edit_docs_settings',
1005 + [
1165 1006 $this,
1166 - 'output',
1167 - ),
1168 - $parent_slug
1169 - );
1170 - }
1007 + 'output'
1008 + ],
1009 + $parent_slug
1010 + );
1011 + }
1171 1012
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 - }
1013 + return apply_filters( 'betterdocs_admin_menu', $betterdocs_admin_pages, [$this, 'output'], $parent_slug);
1184 1014
1185 - return apply_filters( 'betterdocs_admin_menu', $betterdocs_admin_pages, array( $this, 'output' ), $parent_slug );
1186 - }
1015 + }
1187 1016
1188 1017 public function add_custom_classes_to_menu_items() {
1189 1018 global $menu, $submenu;
1190 1019
1191 - $menu_items = array(
1020 + $menu_items = [
1192 1021 'betterdocs' => 'betterdocs',
1193 1022 'betterdocs_page_all_docs' => 'betterdocs-all-docs',
1194 1023 'betterdocs_page_add_new' => 'betterdocs-add-new',
1195 - 'betterdocs-doc-categories' => 'betterdocs-categories',
1196 - 'betterdocs-doc-tags' => 'betterdocs-tags',
1024 + 'edit-tags.php?taxonomy=doc_category&post_type=docs' => 'betterdocs-categories',
1025 + 'edit-tags.php?taxonomy=doc_tag&post_type=docs' => 'betterdocs-tags',
1197 1026 'betterdocs-settings' => 'betterdocs-settings',
1198 1027 'betterdocs-analytics' => 'betterdocs-analytics',
1199 1028 'betterdocs-faq' => 'betterdocs-faq',
1200 1029 'betterdocs-glossaries' => 'betterdocs-glossaries',
1201 1030 '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 - );
1031 + 'edit-tags.php?taxonomy=knowledge_base&post_type=docs' => 'betterdocs-multiplekb'
1032 + ];
1205 1033
1206 1034 foreach ( $menu as &$item ) {
1207 1035 if ( isset( $menu_items[ $item[2] ] ) ) {
1208 1036 if ( ! isset( $item[4] ) ) {
@@ -1232,12 +1060,12 @@
1232 1060 $menus['quick_setup'] = $this->normalize_menu(
1233 1061 __( 'Quick Setup', 'betterdocs' ),
1234 1062 'betterdocs-setup',
1235 1063 'delete_users',
1236 - array(
1064 + [
1237 1065 $this->container->get( SetupWizard::class ),
1238 - 'views',
1239 - )
1066 + 'views'
1067 + ]
1240 1068 );
1241 1069 }
1242 1070
1243 1071 return $menus;
@@ -1245,12 +1073,8 @@
1245 1073
1246 1074 public function insert_plugin_links( $links ) {
1247 1075 $links[] = '<a href="admin.php?page=betterdocs-settings">' . __( 'Settings', 'betterdocs' ) . '</a>';
1248 1076
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>';
1251 - }
1252 -
1253 1077 return $links;
1254 1078 }
1255 1079
1256 1080 public function toolbar_menu( $admin_bar ) {
@@ -1277,22 +1101,17 @@
1277 1101 }
1278 1102
1279 1103 $slug = $this->settings->get( 'encyclopedia_root_slug' );
1280 1104
1281 - global $wp_rewrite;
1282 - if ( $wp_rewrite->using_index_permalinks() ) {
1283 - $slug = $wp_rewrite->index . '/' . $slug;
1284 - }
1285 -
1286 1105 $encyclopedia_url = home_url( $slug );
1287 1106
1288 1107 $admin_bar->add_node(
1289 - array(
1108 + [
1290 1109 'parent' => 'site-name',
1291 1110 'id' => 'view-docs',
1292 1111 'title' => __( 'Visit Documentation', 'betterdocs' ),
1293 - 'href' => $docs_url,
1294 - )
1112 + 'href' => $docs_url
1113 + ]
1295 1114 );
1296 1115
1297 1116 $is_enable_encyclopedia = betterdocs()->settings->get( 'enable_encyclopedia' );
1298 1117
@@ -1297,14 +1116,14 @@
1297 1116 $is_enable_encyclopedia = betterdocs()->settings->get( 'enable_encyclopedia' );
1298 1117
1299 1118 if ( $is_enable_encyclopedia && betterdocs()->is_pro_active() ) {
1300 1119 $admin_bar->add_node(
1301 - array(
1120 + [
1302 1121 'parent' => 'site-name',
1303 1122 'id' => 'view-encyclopedia',
1304 1123 'title' => __( 'Visit Encyclopedia', 'betterdocs' ),
1305 - 'href' => $encyclopedia_url,
1306 - )
1124 + 'href' => $encyclopedia_url
1125 + ]
1307 1126 );
1308 1127 }
1309 1128 }
1310 1129
@@ -1311,20 +1130,14 @@
1311 1130 /**
1312 1131 * Save last visited admin ui
1313 1132 *
1314 1133 * @since 3.0.1
1134 + *
1315 1135 */
1316 1136 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 ) {
1137 + if ( isset( $_GET['post_type'] ) && $_GET['post_type'] === 'docs' && isset( $_GET['bdocs_view'] ) && $_GET['bdocs_view'] === 'classic' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1325 1138 update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'classic_ui' );
1326 - } elseif ( 'betterdocs-admin' === $page ) {
1139 + } elseif ( isset( $_GET['page'] ) && $_GET['page'] === 'betterdocs-admin' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1327 1140 update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'modern_ui' );
1328 1141 }
1329 1142 }
1330 1143
@@ -1334,20 +1147,18 @@
1334 1147 * @return string
1335 1148 * @since 3.0.1
1336 1149 */
1337 1150 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 - );
1151 + $last_visited = get_user_meta(get_current_user_id(), 'last_visited_docs_admin_page', true);
1152 + $docs_exist = get_posts([
1153 + 'post_type' => 'docs',
1154 + 'post_status' => 'any',
1155 + 'numberposts' => 1
1156 + ]);
1346 1157
1347 - return ( 'modern_ui' === $last_visited || empty( $docs_exist ) )
1348 - ? 'betterdocs-admin'
1349 - : 'edit.php?post_type=docs&bdocs_view=classic';
1158 + return ($last_visited === 'modern_ui' || empty($docs_exist))
1159 + ? 'betterdocs-admin'
1160 + : 'edit.php?post_type=docs&bdocs_view=classic';
1350 1161 }
1351 1162
1352 1163 /**
1353 1164 * Resets a duplicate submenu in WordPress if the parent main menu and the first submenu permalink are not the same.
@@ -1357,9 +1168,9 @@
1357 1168 */
1358 1169 public function reset_submenu() {
1359 1170 global $submenu;
1360 1171
1361 - $docs = get_posts( array( 'post_type' => 'docs' ) );
1172 + $docs = get_posts( [ 'post_type' => 'docs' ] );
1362 1173 if ( count( $docs ) == 0 ) {
1363 1174 return;
1364 1175 }
1365 1176
@@ -1364,89 +1175,10 @@
1364 1175 }
1365 1176
1366 1177 $last_visited = get_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', true );
1367 1178
1368 - if ( 'classic_ui' === $last_visited && isset( $submenu['betterdocs-admin'] ) && in_array( 'betterdocs-admin', $submenu['betterdocs-admin'][0] ) ) {
1179 + if ( $last_visited === 'classic_ui' && isset( $submenu['betterdocs-admin'] ) && in_array( 'betterdocs-admin', $submenu['betterdocs-admin'][0] ) ) {
1369 1180 unset( $submenu['betterdocs-admin'][0] );
1370 1181 $submenu['betterdocs-admin'] = array_values( $submenu['betterdocs-admin'] );
1371 1182 }
1372 - }
1373 -
1374 - /**
1375 - * Initialize Black Friday Pointer
1376 - *
1377 - * @return void
1378 - * @since 3.7.0
1379 - */
1380 - private function init_black_friday_pointer() {
1381 - // Only initialize if conditions are met
1382 - if ( NoticePointers::should_display_notice() ) {
1383 - new NoticePointers();
1384 - }
1385 - }
1386 -
1387 - /**
1388 - * AJAX handler for dismissing Black Friday pointer
1389 - *
1390 - * @return void
1391 - * @since 3.7.0
1392 - */
1393 - public function ajax_dismiss_black_friday_pointer() {
1394 - // 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' ) ) );
1398 - }
1399 -
1400 - // Check if user has permission
1401 - if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'edit_docs' ) ) {
1402 - wp_send_json_error( array( 'message' => __( 'Permission denied', 'betterdocs' ) ) );
1403 - }
1404 -
1405 - // Get the introduction key
1406 - $introduction_key = isset( $_POST['introduction_key'] ) ? sanitize_text_field( wp_unslash( $_POST['introduction_key'] ) ) : '';
1407 -
1408 - if ( empty( $introduction_key ) ) {
1409 - wp_send_json_error( array( 'message' => __( 'Invalid introduction key', 'betterdocs' ) ) );
1410 - }
1411 -
1412 - // Set the introduction as viewed
1413 - NoticePointers::set_introduction_viewed( $introduction_key );
1414 -
1415 - // Clear the priority option so other plugins can set their priority
1416 - delete_option( '_wpdeveloper_plugin_pointer_priority' );
1417 -
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 ) ) );
1451 1183 }
1452 1184 }