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