| @@ -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 | |
| @@ -19,41 +14,13 @@ | ||
| 19 | 14 | use WPDeveloper\BetterDocs\Dependencies\DI\Container; |
| 20 | 15 | |
| 21 | 16 | class Admin extends Base { |
| 22 | 17 | /** |
| 23 | - * Per-user flag recording that this administrator has opened the MCP screen. | |
| 24 | - * | |
| 25 | - * Stores the timestamp of the first visit, but only its **presence** is read: | |
| 26 | - * absent means "this user has not seen MCP yet", which is what puts the | |
| 27 | - * one-time discovery badge on the menu (ADR-063). Per user on purpose — two | |
| 28 | - * administrators each get their own first look, and neither clears the other's. | |
| 29 | - * | |
| 30 | - * @var string | |
| 31 | - * @since 4.9.0 | |
| 32 | - */ | |
| 33 | - const MCP_SEEN_META = 'betterdocs_mcp_seen'; | |
| 34 | - | |
| 35 | - /** | |
| 36 | - * Whether this request painted the MCP discovery badge onto the menu. | |
| 37 | - * | |
| 38 | - * Decided once in `menus()` (on `admin_menu`) and read again in | |
| 39 | - * `mcp_badge_styles()` (on `admin_head`), rather than re-deciding, because the | |
| 40 | - * two must agree: on the request that opens the MCP screen the badge is still | |
| 41 | - * painted while the meta is already written, and re-deciding at `admin_head` | |
| 42 | - * would leave that one painted pill unstyled. | |
| 43 | - * | |
| 44 | - * @var bool | |
| 45 | - * @since 4.9.0 | |
| 46 | - */ | |
| 47 | - private $mcp_badge = false; | |
| 48 | - | |
| 49 | - /** | |
| 50 | 18 | * @var CacheBank |
| 51 | 19 | */ |
| 52 | 20 | private static $cache_bank; |
| 53 | 21 | /** |
| 54 | 22 | * Admin Root Menu Slug |
| 55 | - * | |
| 56 | 23 | * @var string |
| 57 | 24 | */ |
| 58 | 25 | private $slug = 'betterdocs-dashboard'; |
| 59 | 26 | /** |
| @@ -85,19 +52,17 @@ | ||
| 85 | 52 | private $kbmigration; |
| 86 | 53 | |
| 87 | 54 | /** |
| 88 | 55 | * Enqueue |
| 89 | - * | |
| 90 | 56 | * @var Enqueue |
| 91 | 57 | */ |
| 92 | 58 | private $assets; |
| 93 | 59 | |
| 94 | 60 | // modules |
| 95 | - protected $installer; | |
| 61 | + protected $installer; | |
| 96 | 62 | |
| 97 | 63 | /** |
| 98 | 64 | * FAQBuilder |
| 99 | - * | |
| 100 | 65 | * @var FAQBuilder |
| 101 | 66 | */ |
| 102 | 67 | private $faq_builder; |
| 103 | 68 | private $glossaries; |
| @@ -108,10 +73,10 @@ | ||
| 108 | 73 | $this->settings = $settings; |
| 109 | 74 | $this->kbmigration = $kbmigration; |
| 110 | 75 | $this->slug = 'betterdocs-dashboard'; |
| 111 | 76 | |
| 112 | - add_action( 'init', array( $type, 'register' ), 9 ); | |
| 113 | - 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']); | |
| 114 | 79 | |
| 115 | 80 | $type->init(); |
| 116 | 81 | $type->admin_init(); |
| 117 | 82 | |
| @@ -117,18 +82,8 @@ | ||
| 117 | 82 | |
| 118 | 83 | $this->faq_builder = $this->container->get( FAQBuilder::class ); |
| 119 | 84 | $this->glossaries = $this->container->get( Glossaries::class ); |
| 120 | 85 | |
| 121 | - /** | |
| 122 | - * Register usage tracking (including the daily `put_do_weekly_action` cron | |
| 123 | - * handler) on every request — WP-Cron runs with is_admin() === false, so | |
| 124 | - * this MUST sit above the admin guard or the cron send never fires. The | |
| 125 | - * admin-only UI hooks inside Insights::init() (deactivation form, footer | |
| 126 | - * scripts, plugin_action_links) are context-specific and simply never run | |
| 127 | - * outside wp-admin. | |
| 128 | - */ | |
| 129 | - $this->plugin_insights(); | |
| 130 | - | |
| 131 | 86 | if ( ! is_admin() ) { |
| 132 | 87 | return; |
| 133 | 88 | } |
| 134 | 89 | |
| @@ -133,64 +88,47 @@ | ||
| 133 | 88 | } |
| 134 | 89 | |
| 135 | 90 | $this->installer = new PluginInstaller(); |
| 136 | 91 | |
| 137 | - add_action( 'admin_notices', array( $this, 'compatibility_notices' ) ); | |
| 138 | - // The WPNotice CacheBank wipes all admin_notices at priority 10 on BetterDocs | |
| 139 | - // screens, so the hook above never renders inside the BetterDocs panels. | |
| 140 | - // Re-add the compatibility notice after that wipe (in_admin_header, priority | |
| 141 | - // 999) so it shows on the panels like the review / license notices. | |
| 142 | - add_action( 'in_admin_header', function () { | |
| 143 | - $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; | |
| 144 | - if ( $screen && betterdocs()->is_betterdocs_screen( $screen->id ) ) { | |
| 145 | - add_action( 'admin_notices', array( $this, 'compatibility_notices' ) ); | |
| 146 | - } | |
| 147 | - }, 999 ); | |
| 92 | + $this->plugin_insights(); | |
| 93 | + add_action( 'admin_notices', [ $this, 'compatibility_notices' ] ); | |
| 148 | 94 | // add_action( 'admin_init', [$this, 'notices'], 9 ); |
| 149 | - add_filter( 'admin_init', array( $this, 'save_admin_page' ), 99 ); | |
| 95 | + add_filter( 'admin_init', [ $this, 'save_admin_page' ], 99 ); | |
| 150 | 96 | |
| 151 | - add_action( 'admin_menu', array( $this, 'menus' ) ); | |
| 152 | - // The badge's clear runs on `admin_init` — a hook that fires for every | |
| 153 | - // admin request — and identifies the screen by its page slug, rather | |
| 154 | - // than on `load-{$hook_suffix}` (ADR-065). `admin_init` fires *after* | |
| 155 | - // `admin_menu`, measured on the rig, so the badge is still painted on | |
| 156 | - // the request that opens the screen exactly as before. | |
| 157 | - add_action( 'admin_init', array( $this, 'mark_mcp_seen' ) ); | |
| 158 | - add_action( 'admin_menu', array( $this, 'reset_submenu' ) ); | |
| 159 | - add_action( 'admin_head', array( $this, 'add_custom_classes_to_menu_items' ) ); | |
| 160 | - add_action( 'admin_head', array( $this, 'mcp_badge_styles' ) ); | |
| 161 | - 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' ] ); | |
| 162 | 101 | |
| 163 | 102 | // $this->container->get( SetupWizard::class )->init(); |
| 164 | 103 | |
| 165 | - add_action( 'admin_enqueue_scripts', array( $this, 'styles' ) ); | |
| 166 | - add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) ); | |
| 167 | - // add_action( 'betterdocs_listing_header', [ $this, 'header' ], 10, 1 ); | |
| 168 | - 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 ); | |
| 169 | 108 | |
| 170 | - add_filter( 'admin_body_class', array( $this, 'body_classes' ) ); | |
| 171 | - add_filter( 'parent_file', array( $type, 'highlight_admin_menu' ) ); | |
| 172 | - add_filter( 'submenu_file', array( $type, 'highlight_admin_submenu' ), 10, 2 ); | |
| 173 | - 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 ); | |
| 174 | 113 | |
| 175 | 114 | /** |
| 176 | 115 | * Remove Comments Column from List Table. |
| 177 | 116 | */ |
| 178 | - add_filter( 'manage_docs_posts_columns', array( $this, 'set_custom_edit_action_columns' ) ); | |
| 179 | - 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 ); | |
| 180 | 119 | |
| 181 | 120 | /** |
| 182 | 121 | * Add New Column |
| 183 | 122 | */ |
| 184 | - add_filter( 'manage_users_columns', array( $this, 'add_users_total_docs_column' ), 10, 1 ); | |
| 185 | - 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 ); | |
| 186 | 125 | if ( is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ) ) { |
| 187 | 126 | add_action( 'admin_footer-plugins.php', array( $this, 'disable_deactivation' ) ); |
| 188 | 127 | } |
| 189 | 128 | |
| 190 | 129 | if ( $this->settings->get( 'enable_estimated_reading_time' ) ) { |
| 191 | - // Hook into unified metabox instead of creating separate metabox | |
| 192 | - add_action( 'betterdocs_reading_time_tab_content', array( $this, 'render_estimated_time_markup' ) ); | |
| 130 | + add_action( 'add_meta_boxes', [ $this, 'reading_meta_box_' ], 10 ); | |
| 193 | 131 | } |
| 194 | 132 | |
| 195 | 133 | self::$cache_bank = CacheBank::get_instance(); |
| 196 | 134 | |
| @@ -201,28 +139,17 @@ | ||
| 201 | 139 | $this->notices(); |
| 202 | 140 | } catch ( Exception $e ) { |
| 203 | 141 | unset( $e ); |
| 204 | 142 | } |
| 205 | - | |
| 206 | - // Initialize Black Friday Pointer | |
| 207 | - $this->init_black_friday_pointer(); | |
| 208 | - | |
| 209 | - // Register AJAX handler for pointer dismissal | |
| 210 | - add_action( 'wp_ajax_betterdocs_dismiss_black_friday_pointer', array( $this, 'ajax_dismiss_black_friday_pointer' ) ); | |
| 211 | 143 | } |
| 212 | 144 | |
| 213 | 145 | public function order_terms_in_wp_terms_admin_table() { |
| 214 | - // order the terms correctly to be shown on the admin panel categories menu with betterdocs order | |
| 215 | - add_action( | |
| 216 | - 'rest_insert_doc_category', | |
| 217 | - function ( $term, $request, $bool ) { | |
| 218 | - $max_order = Helper::get_max_doc_category_order_from_term_meta() ?? 0; | |
| 219 | - $next_order = $max_order + 1; | |
| 220 | - update_term_meta( $term->term_id, 'doc_category_order', $next_order ); | |
| 221 | - }, | |
| 222 | - 10, | |
| 223 | - 3 | |
| 224 | - ); | |
| 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); | |
| 225 | 152 | } |
| 226 | 153 | |
| 227 | 154 | public function disable_deactivation() { |
| 228 | 155 | $tooltip_text = esc_html__( 'Deactivate BetterDocs Pro First', 'betterdocs' ); |
| @@ -269,17 +196,17 @@ | ||
| 269 | 196 | <?php |
| 270 | 197 | } |
| 271 | 198 | |
| 272 | 199 | public function add_users_total_docs_column( $columns ) { |
| 273 | - $new_column = array( | |
| 274 | - 'docs' => __( 'Docs', 'betterdocs' ), | |
| 275 | - ); | |
| 200 | + $new_column = [ | |
| 201 | + 'docs' => __( 'Docs', 'betterdocs' ) | |
| 202 | + ]; | |
| 276 | 203 | $columns = array_merge( $columns, $new_column ); |
| 277 | 204 | return $columns; |
| 278 | 205 | } |
| 279 | 206 | |
| 280 | 207 | public function popular_users_docs_data( $output, $column_name, $user_id ) { |
| 281 | - if ( 'docs' == $column_name ) { | |
| 208 | + if ( $column_name == 'docs' ) { | |
| 282 | 209 | $total_count = count_user_posts( $user_id, 'docs', true ); |
| 283 | 210 | return '<a href="edit.php?post_type=docs&author=' . $user_id . '" class="edit"><span aria-hidden="true">' . $total_count . '</span></a>'; |
| 284 | 211 | } |
| 285 | 212 | return $output; |
| @@ -284,8 +211,20 @@ | ||
| 284 | 211 | } |
| 285 | 212 | return $output; |
| 286 | 213 | } |
| 287 | 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 | + | |
| 288 | 227 | public function render_estimated_time_markup() { |
| 289 | 228 | betterdocs()->views->get( 'admin/metabox/estimated-reading-box' ); |
| 290 | 229 | } |
| 291 | 230 | |
| @@ -293,15 +232,13 @@ | ||
| 293 | 232 | if ( betterdocs()->is_pro_active() ) { |
| 294 | 233 | $plugins = Helper::get_plugins(); |
| 295 | 234 | $plugin_data = $plugins['betterdocs-pro/betterdocs-pro.php']; |
| 296 | 235 | |
| 297 | - // Require the paired Pro release: the Analytics UI is version-coupled to | |
| 298 | - // Pro's advanced modules, so an older Pro renders a broken/partial panel. | |
| 299 | - 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', '>=' ) ) { | |
| 300 | 237 | return; |
| 301 | 238 | } |
| 302 | 239 | |
| 303 | - betterdocs()->views->get( 'admin/notices/compatibility', array( 'version' => $plugin_data['Version'] ) ); | |
| 240 | + betterdocs()->views->get( 'admin/notices/compatibility', [ 'version' => $plugin_data['Version'] ] ); | |
| 304 | 241 | } |
| 305 | 242 | } |
| 306 | 243 | |
| 307 | 244 | public function plugin_insights( $prevent_init = false ) { |
| @@ -306,20 +243,20 @@ | ||
| 306 | 243 | |
| 307 | 244 | public function plugin_insights( $prevent_init = false ) { |
| 308 | 245 | $this->insights = Insights::get_instance( |
| 309 | 246 | BETTERDOCS_PLUGIN_FILE, |
| 310 | - array( | |
| 247 | + [ | |
| 311 | 248 | 'opt_in' => true, |
| 312 | 249 | 'goodbye_form' => true, |
| 313 | - 'item_id' => 'c7b16777b4f1b83f6083', | |
| 314 | - ) | |
| 250 | + 'item_id' => 'c7b16777b4f1b83f6083' | |
| 251 | + ] | |
| 315 | 252 | ); |
| 316 | 253 | |
| 317 | 254 | $this->insights->set_notice_options( |
| 318 | - array( | |
| 255 | + [ | |
| 319 | 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' ), |
| 320 | - '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' ), | |
| 321 | - ) | |
| 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 | + ] | |
| 322 | 259 | ); |
| 323 | 260 | |
| 324 | 261 | if ( ! $prevent_init ) { |
| 325 | 262 | $this->insights->init(); |
| @@ -335,77 +272,76 @@ | ||
| 335 | 272 | * @throws Exception |
| 336 | 273 | */ |
| 337 | 274 | public function notices() { |
| 338 | 275 | $notices = new Notices( |
| 339 | - array( | |
| 276 | + [ | |
| 340 | 277 | 'id' => 'betterdocs', |
| 341 | 278 | 'storage_key' => 'notices', |
| 342 | 279 | 'lifetime' => 3, |
| 343 | 280 | 'stylesheet_url' => $this->assets->asset_url( 'admin/css/notices.css' ), |
| 344 | 281 | 'styles' => $this->assets->asset_url( 'admin/css/notices.css' ), |
| 345 | - 'priority' => 4, | |
| 346 | - ) | |
| 282 | + 'priority' => 4 | |
| 283 | + ] | |
| 347 | 284 | ); |
| 348 | 285 | |
| 349 | 286 | /** |
| 350 | - * Review Notice | |
| 351 | - * | |
| 352 | - * @var mixed $message | |
| 353 | - */ | |
| 287 | + * Review Notice | |
| 288 | + * @var mixed $message | |
| 289 | + */ | |
| 354 | 290 | |
| 355 | 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' ); |
| 356 | 292 | |
| 357 | - $_review_notice = array( | |
| 293 | + $_review_notice = [ | |
| 358 | 294 | 'thumbnail' => $this->assets->icon( 'betterdocs-logo.svg', true ), |
| 359 | 295 | 'html' => '<p>' . $message . '</p>', |
| 360 | - 'links' => array( | |
| 361 | - 'later' => array( | |
| 296 | + 'links' => [ | |
| 297 | + 'later' => [ | |
| 362 | 298 | 'link' => 'https://wordpress.org/plugins/betterdocs/#reviews', |
| 363 | 299 | 'target' => '_blank', |
| 364 | 300 | 'label' => __( 'Sure, you deserve it!', 'betterdocs' ), |
| 365 | - 'icon_class' => 'dashicons dashicons-external', | |
| 366 | - ), | |
| 367 | - 'allready' => array( | |
| 301 | + 'icon_class' => 'dashicons dashicons-external' | |
| 302 | + ], | |
| 303 | + 'allready' => [ | |
| 368 | 304 | 'label' => __( 'I already did', 'betterdocs' ), |
| 369 | 305 | 'icon_class' => 'dashicons dashicons-smiley', |
| 370 | - 'attributes' => array( | |
| 371 | - 'data-dismiss' => true, | |
| 372 | - ), | |
| 373 | - ), | |
| 374 | - 'maybe_later' => array( | |
| 306 | + 'attributes' => [ | |
| 307 | + 'data-dismiss' => true | |
| 308 | + ] | |
| 309 | + ], | |
| 310 | + 'maybe_later' => [ | |
| 375 | 311 | 'label' => __( 'Maybe Later', 'betterdocs' ), |
| 376 | 312 | 'icon_class' => 'dashicons dashicons-calendar-alt', |
| 377 | - 'attributes' => array( | |
| 313 | + 'attributes' => [ | |
| 378 | 314 | 'data-later' => true, |
| 379 | - 'class' => 'dismiss-btn', | |
| 380 | - ), | |
| 381 | - ), | |
| 382 | - 'support' => array( | |
| 315 | + 'class' => 'dismiss-btn' | |
| 316 | + ] | |
| 317 | + ], | |
| 318 | + 'support' => [ | |
| 383 | 319 | 'link' => 'https://wpdeveloper.com/support', |
| 384 | - 'attributes' => array( | |
| 385 | - 'target' => '_blank', | |
| 386 | - ), | |
| 320 | + 'attributes' => [ | |
| 321 | + 'target' => '_blank' | |
| 322 | + ], | |
| 387 | 323 | 'label' => __( 'I need help', 'betterdocs' ), |
| 388 | - 'icon_class' => 'dashicons dashicons-sos', | |
| 389 | - ), | |
| 390 | - 'never_show_again' => array( | |
| 324 | + 'icon_class' => 'dashicons dashicons-sos' | |
| 325 | + ], | |
| 326 | + 'never_show_again' => [ | |
| 391 | 327 | 'label' => __( 'Never show again', 'betterdocs' ), |
| 392 | 328 | 'icon_class' => 'dashicons dashicons-dismiss', |
| 393 | - 'attributes' => array( | |
| 394 | - 'data-dismiss' => true, | |
| 395 | - ), | |
| 396 | - ), | |
| 397 | - ), | |
| 398 | - ); | |
| 329 | + 'attributes' => [ | |
| 330 | + 'data-dismiss' => true | |
| 331 | + ] | |
| 332 | + ] | |
| 333 | + ] | |
| 334 | + ]; | |
| 399 | 335 | |
| 400 | 336 | $notices->add( |
| 401 | 337 | 'review', |
| 402 | 338 | $_review_notice, |
| 403 | - array( | |
| 339 | + [ | |
| 404 | 340 | 'start' => $notices->strtotime( '+10 days' ), |
| 405 | 341 | 'recurrence' => 30, |
| 406 | - 'dismissible' => true, | |
| 407 | - ) | |
| 342 | + 'dismissible' => true | |
| 343 | + ] | |
| 408 | 344 | ); |
| 409 | 345 | |
| 410 | 346 | if ( $this->kbmigration->existing_plugins && ! in_array( $this->kbmigration->existing_plugins[0][0], $this->kbmigration->migrated_plugins ) ) { |
| 411 | 347 | $plugin_name = '<strong>' . esc_html( $this->kbmigration->existing_plugins[0][1] ) . '</strong>'; |
| @@ -410,9 +346,9 @@ | ||
| 410 | 346 | if ( $this->kbmigration->existing_plugins && ! in_array( $this->kbmigration->existing_plugins[0][0], $this->kbmigration->migrated_plugins ) ) { |
| 411 | 347 | $plugin_name = '<strong>' . esc_html( $this->kbmigration->existing_plugins[0][1] ) . '</strong>'; |
| 412 | 348 | |
| 413 | 349 | $message = sprintf( |
| 414 | - /* translators: %s is the name of the existing knowledge base plugin. */ | |
| 350 | + /* translators: %s is the name of the existing knowledge base plugin. */ | |
| 415 | 351 | __( 'Already using %s? Power up your Knowledge Base by migrating all your docs and settings to BetterDocs with just 1 click.', 'betterdocs' ), |
| 416 | 352 | esc_html( $plugin_name ) |
| 417 | 353 | ); |
| 418 | 354 | |
| @@ -422,51 +358,50 @@ | ||
| 422 | 358 | esc_url( admin_url( 'admin.php?page=betterdocs-settings&tab=tab-migration' ) ), |
| 423 | 359 | esc_html__( 'Start Migration', 'betterdocs' ) |
| 424 | 360 | ); |
| 425 | 361 | |
| 426 | - $_migration_notice = array( | |
| 362 | + $_migration_notice = [ | |
| 427 | 363 | 'thumbnail' => '', |
| 428 | 364 | 'html' => $migration_message, |
| 429 | - 'links' => array( | |
| 430 | - 'maybe_later' => array( | |
| 365 | + 'links' => [ | |
| 366 | + 'maybe_later' => [ | |
| 431 | 367 | 'label' => __( 'Maybe Later', 'betterdocs' ), |
| 432 | 368 | 'icon_class' => 'dashicons dashicons-calendar-alt', |
| 433 | - 'attributes' => array( | |
| 369 | + 'attributes' => [ | |
| 434 | 370 | 'data-later' => true, |
| 435 | - 'class' => 'dismiss-btn', | |
| 436 | - ), | |
| 437 | - ), | |
| 438 | - 'never_show_again' => array( | |
| 371 | + 'class' => 'dismiss-btn' | |
| 372 | + ] | |
| 373 | + ], | |
| 374 | + 'never_show_again' => [ | |
| 439 | 375 | 'label' => __( 'Never show again', 'betterdocs' ), |
| 440 | 376 | 'icon_class' => 'dashicons dashicons-dismiss', |
| 441 | - 'attributes' => array( | |
| 442 | - 'data-dismiss' => true, | |
| 443 | - ), | |
| 444 | - ), | |
| 445 | - ), | |
| 446 | - ); | |
| 377 | + 'attributes' => [ | |
| 378 | + 'data-dismiss' => true | |
| 379 | + ] | |
| 380 | + ] | |
| 381 | + ] | |
| 382 | + ]; | |
| 447 | 383 | |
| 448 | 384 | $notices->add( |
| 449 | 385 | 'migration', |
| 450 | 386 | $_migration_notice, |
| 451 | - array( | |
| 387 | + [ | |
| 452 | 388 | 'start' => $notices->time(), |
| 453 | 389 | 'recurrence' => false, |
| 454 | - 'dismissible' => true, | |
| 455 | - ) | |
| 390 | + 'dismissible' => true | |
| 391 | + ] | |
| 456 | 392 | ); |
| 457 | 393 | } |
| 458 | 394 | |
| 459 | 395 | /** |
| 460 | - * | |
| 461 | 396 | * Opt-In Notice |
| 462 | 397 | */ |
| 463 | 398 | $allow_tracking = get_option( 'wpins_allow_tracking' ); |
| 464 | - if ( null != $this->insights && ! isset( $allow_tracking['betterdocs'] ) ) { | |
| 399 | + if ( $this->insights != null && ! isset( $allow_tracking['betterdocs'] ) ) { | |
| 465 | 400 | $notices->add( |
| 466 | 401 | 'opt_in', |
| 467 | - array( $this->insights, 'notice' ), | |
| 468 | - array( | |
| 402 | + [ $this->insights, 'notice' ], | |
| 403 | + [ | |
| 469 | 404 | 'classes' => 'updated put-dismiss-notice', |
| 470 | 405 | 'start' => $notices->time(), |
| 471 | 406 | 'refresh' => BETTERDOCS_VERSION, |
| 472 | 407 | 'dismissible' => true, |
| @@ -471,48 +406,55 @@ | ||
| 471 | 406 | 'refresh' => BETTERDOCS_VERSION, |
| 472 | 407 | 'dismissible' => true, |
| 473 | 408 | 'do_action' => 'wpdeveloper_notice_clicked_for_betterdocs', |
| 474 | 409 | 'display_if' => ! function_exists( 'betterdocs_pro' ), |
| 475 | - 'screens' => array( 'dashboard' ), | |
| 476 | - ) | |
| 410 | + 'screens' => [ 'dashboard' ] | |
| 411 | + ] | |
| 477 | 412 | ); |
| 478 | 413 | } |
| 479 | 414 | |
| 480 | - $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>'; | |
| 481 | - $_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 = [ | |
| 482 | 442 | 'thumbnail' => $this->assets->icon( 'betterdocs-logo.svg', true ), |
| 483 | - 'html' => $summer_campaign_message, | |
| 484 | - 'links' => array( | |
| 485 | - 'support' => array( | |
| 486 | - 'link' => 'https://betterdocs.co/summer2026-admin-notice', | |
| 487 | - 'attributes' => array( | |
| 488 | - 'target' => '_blank', | |
| 489 | - 'class' => 'offer-button', | |
| 490 | - ), | |
| 491 | - 'label' => __( 'Upgrade To PRO Now', 'betterdocs' ), | |
| 492 | - ), | |
| 493 | - 'maybe_later' => array( | |
| 494 | - 'label' => __( 'I’ll Grab It Later', 'betterdocs' ), | |
| 495 | - 'attributes' => array( | |
| 496 | - 'target' => '_blank', | |
| 497 | - 'data-later' => true, | |
| 498 | - 'class' => 'dismiss-btn', | |
| 499 | - ), | |
| 500 | - ), | |
| 501 | - ), | |
| 502 | - ); | |
| 443 | + 'html' => $offer_message | |
| 444 | + ]; | |
| 503 | 445 | |
| 504 | 446 | $notices->add( |
| 505 | - 'summer-campaign-26', | |
| 506 | - $_summer_campaign_notice, | |
| 507 | - array( | |
| 447 | + '40k_offer', | |
| 448 | + $_offer_notice, | |
| 449 | + [ | |
| 508 | 450 | 'start' => $notices->time(), |
| 509 | 451 | 'recurrence' => false, |
| 510 | 452 | 'dismissible' => true, |
| 511 | 453 | 'refresh' => BETTERDOCS_VERSION, |
| 512 | - 'expire' => strtotime( '11:59:59pm June 25, 2026' ), | |
| 513 | - 'display_if' => ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ), | |
| 514 | - ) | |
| 454 | + 'expire' => strtotime( '11:59:59pm April 30, 2025' ), | |
| 455 | + 'display_if' => ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ) | |
| 456 | + ] | |
| 515 | 457 | ); |
| 516 | 458 | |
| 517 | 459 | self::$cache_bank->create_account( $notices ); |
| 518 | 460 | self::$cache_bank->calculate_deposits( $notices ); |
| @@ -517,9 +459,9 @@ | ||
| 517 | 459 | self::$cache_bank->create_account( $notices ); |
| 518 | 460 | self::$cache_bank->calculate_deposits( $notices ); |
| 519 | 461 | if ( method_exists( self::$cache_bank, 'clear_notices_in_' ) ) { |
| 520 | 462 | self::$cache_bank->clear_notices_in_( |
| 521 | - array( | |
| 463 | + [ | |
| 522 | 464 | 'toplevel_page_betterdocs-dashboard', |
| 523 | 465 | 'admin_page_betterdocs-admin', |
| 524 | 466 | 'betterdocs_page_betterdocs-admin', |
| 525 | 467 | 'betterdocs_page_betterdocs-settings', |
| @@ -526,14 +468,11 @@ | ||
| 526 | 468 | 'betterdocs_page_betterdocs-faq', |
| 527 | 469 | 'betterdocs_page_betterdocs-analytics', |
| 528 | 470 | 'betterdocs_page_betterdocs-glossaries', |
| 529 | 471 | 'betterdocs_page_betterdocs-ai-chatbot', |
| 530 | - 'betterdocs_page_betterdocs-api-docs', | |
| 531 | - 'betterdocs_page_betterdocs-doc-categories', | |
| 532 | - 'betterdocs_page_betterdocs-doc-tags', | |
| 533 | 472 | 'edit-doc_category', |
| 534 | - 'edit-doc_tag', | |
| 535 | - ), | |
| 473 | + 'edit-doc_tag' | |
| 474 | + ], | |
| 536 | 475 | $notices, |
| 537 | 476 | true |
| 538 | 477 | ); |
| 539 | 478 | } |
| @@ -538,92 +477,31 @@ | ||
| 538 | 477 | ); |
| 539 | 478 | } |
| 540 | 479 | } |
| 541 | 480 | |
| 542 | - /** | |
| 543 | - * Resolve the admin dark-mode preference. | |
| 544 | - * | |
| 545 | - * The mode switcher stores the choice in a client cookie (no DB write, shared | |
| 546 | - * across every admin screen). Fall back to the legacy | |
| 547 | - * `betterdocs_settings['dark_mode']` value for installs that set it before this | |
| 548 | - * change and haven't toggled since. | |
| 549 | - * | |
| 550 | - * @return bool | |
| 551 | - */ | |
| 552 | - public function is_dark_mode() { | |
| 553 | - if ( isset( $_COOKIE['betterdocs_admin_dark_mode'] ) ) { | |
| 554 | - return '1' === $_COOKIE['betterdocs_admin_dark_mode']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 555 | - } | |
| 556 | - | |
| 557 | - $saved = get_option( 'betterdocs_settings', array() ); | |
| 558 | - return ! empty( $saved['dark_mode'] ); | |
| 559 | - } | |
| 560 | - | |
| 561 | - /** | |
| 562 | - * Whether the knowledge base is genuinely empty (no doc categories and no | |
| 563 | - * non-trash docs). Localized to the admin so the All Docs panel can render a | |
| 564 | - * skeleton shaped like the "No Category Found" empty card on first paint — | |
| 565 | - * instead of a category/docs skeleton it would immediately replace — without | |
| 566 | - * waiting for the REST fetch to reveal the count. | |
| 567 | - * | |
| 568 | - * @return bool | |
| 569 | - */ | |
| 570 | - public function kb_is_empty() { | |
| 571 | - $cats = wp_count_terms( array( 'taxonomy' => 'doc_category', 'hide_empty' => false ) ); | |
| 572 | - $cats = is_wp_error( $cats ) ? 0 : (int) $cats; | |
| 573 | - if ( $cats > 0 ) { | |
| 574 | - return false; | |
| 575 | - } | |
| 576 | - | |
| 577 | - $counts = (array) wp_count_posts( 'docs' ); | |
| 578 | - $total = 0; | |
| 579 | - foreach ( array( 'publish', 'future', 'draft', 'pending', 'private' ) as $status ) { | |
| 580 | - $total += isset( $counts[ $status ] ) ? (int) $counts[ $status ] : 0; | |
| 581 | - } | |
| 582 | - | |
| 583 | - return 0 === $total; | |
| 584 | - } | |
| 585 | - | |
| 586 | 481 | public function body_classes( $classes ) { |
| 587 | - $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; | |
| 588 | 485 | $current_screen_id = get_current_screen() != null ? str_replace( 'betterdocs_page_', '', str_replace( 'toplevel_page_', '', str_replace( 'admin_page_', '', get_current_screen()->id ) ) ) : ''; |
| 589 | - /** | |
| 590 | - * Filter the list of (prefix-stripped) screen ids that receive the | |
| 591 | - * `betterdocs-admin` body class (and dark-mode class). Pro/add-ons can | |
| 592 | - * register their own React admin pages, e.g. the Knowledge Base page. | |
| 593 | - * | |
| 594 | - * @param string[] $registered_screens Screen ids with the page prefix removed. | |
| 595 | - */ | |
| 596 | - $registered_screens = apply_filters( 'betterdocs_admin_screen_slugs', array( | |
| 597 | - 'betterdocs-settings', | |
| 598 | - 'betterdocs-admin', | |
| 599 | - 'betterdocs-dashboard', | |
| 600 | - 'betterdocs-analytics', | |
| 601 | - 'betterdocs-glossaries', | |
| 602 | - 'betterdocs-faq', | |
| 603 | - 'betterdocs-doc-categories', | |
| 604 | - 'betterdocs-doc-tags', | |
| 605 | - 'edit-doc_category', | |
| 606 | - 'edit-doc_tag', | |
| 607 | - 'edit-knowledge_base', | |
| 608 | - 'betterdocs-ai-chatbot', | |
| 609 | - 'betterdocs-api-docs', | |
| 610 | - // Without this the MCP screen never receives `betterdocs-admin`, and | |
| 611 | - // the design tokens' dark-mode overrides — which are declared on | |
| 612 | - // `.betterdocs-admin.betterdocs-dark-mode` — can never apply there: | |
| 613 | - // the switcher in the header flips the cookie and the page stays | |
| 614 | - // light. @since 4.9.0 | |
| 615 | - 'betterdocs-mcp', | |
| 616 | - ) ); | |
| 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 | + ]; | |
| 617 | 498 | |
| 618 | - if ( in_array( $current_screen_id, $registered_screens ) ) { | |
| 619 | - $classes .= ' betterdocs-admin '; | |
| 620 | - } | |
| 499 | + if( in_array( $current_screen_id, $registered_screens ) ) { | |
| 500 | + $classes .= ' betterdocs-admin '; | |
| 501 | + } | |
| 621 | 502 | |
| 622 | - // Dark mode also applies on the Quick Setup wizard, whose self-scoped chrome | |
| 623 | - // keys off `.betterdocs_page_betterdocs-setup.betterdocs-dark-mode`. | |
| 624 | - $dark_screens = array_merge( $registered_screens, array( 'betterdocs-setup' ) ); | |
| 625 | - if ( $dark_mode && in_array( $current_screen_id, $dark_screens, true ) ) { | |
| 503 | + if ( $dark_mode === true && in_array( $current_screen_id, $registered_screens ) ) { | |
| 626 | 504 | $classes .= ' betterdocs-dark-mode '; |
| 627 | 505 | } |
| 628 | 506 | |
| 629 | 507 | return $classes; |
| @@ -638,11 +516,11 @@ | ||
| 638 | 516 | * @since 1.0.0 |
| 639 | 517 | */ |
| 640 | 518 | public function set_custom_edit_action_columns( $columns ) { |
| 641 | 519 | unset( $columns['comments'] ); |
| 642 | - $new_columns = array(); | |
| 520 | + $new_columns = []; | |
| 643 | 521 | foreach ( $columns as $key => $value ) { |
| 644 | - if ( 'date' == $key ) { | |
| 522 | + if ( $key == 'date' ) { | |
| 645 | 523 | $new_columns['betterdocs_word_count'] = __( 'Word Count', 'betterdocs' ); // put the tags column before it |
| 646 | 524 | $new_columns['betterdocs_reaction'] = __( 'Reactions', 'betterdocs' ); |
| 647 | 525 | } |
| 648 | 526 | $new_columns[ $key ] = $value; |
| @@ -654,29 +532,26 @@ | ||
| 654 | 532 | public function manage_custom_columns( $column, $post_id ) { |
| 655 | 533 | global $wpdb; |
| 656 | 534 | switch ( $column ) { |
| 657 | 535 | case 'betterdocs_word_count': |
| 658 | - $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 ) ) ); | |
| 659 | 537 | preg_match_all( '/<[^>]*>|[\p{L}\p{M}]+/u', $content_without_html_tags, $matches ); |
| 660 | - $total_words = ! empty( $matches[0] ) ? count( $matches[0] ) : count( array() ); | |
| 538 | + $total_words = ! empty( $matches[0] ) ? count( $matches[0] ) : count( [] ); | |
| 661 | 539 | $word_count = $total_words; |
| 662 | 540 | echo '<span>' . esc_html( intval( $word_count ) ) . '</span>'; |
| 663 | 541 | break; |
| 664 | 542 | case 'betterdocs_reaction': |
| 665 | - // 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 ) . "'"; | |
| 666 | 544 | $analytics = $wpdb->get_results( |
| 667 | - $wpdb->prepare( | |
| 668 | - "SELECT | |
| 669 | - sum(impressions) as totalViews, | |
| 670 | - sum(unique_visit) as totalUniqueViews, | |
| 671 | - sum(happy + sad + normal) as totalReactions, | |
| 672 | - sum(happy) as totalHappy, | |
| 673 | - sum(normal) as totalNormal, | |
| 674 | - sum(sad) as totalSad | |
| 675 | - FROM {$wpdb->prefix}betterdocs_analytics | |
| 676 | - WHERE post_id = %d", | |
| 677 | - $post_id | |
| 678 | - ) | |
| 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" | |
| 679 | 554 | ); |
| 680 | 555 | |
| 681 | 556 | echo '<ul class="reactions-count"> |
| 682 | 557 | <li> |
| @@ -732,22 +607,22 @@ | ||
| 732 | 607 | * @return void |
| 733 | 608 | * @since 1.0.0 |
| 734 | 609 | */ |
| 735 | 610 | public function styles( $hook ) { |
| 736 | - $this->assets->enqueue( 'betterdocs-global', 'admin/css/global.css', array(), 'all' ); | |
| 611 | + $this->assets->enqueue( 'betterdocs-global', 'admin/css/global.css', [], 'all' ); | |
| 737 | 612 | |
| 738 | 613 | if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) { |
| 739 | 614 | return; |
| 740 | 615 | } |
| 741 | 616 | |
| 742 | - $this->assets->enqueue( 'betterdocs-select2', 'vendor/css/select2.min.css', array(), 'all' ); | |
| 743 | - $this->assets->enqueue( 'betterdocs-daterangepicker', 'vendor/css/daterangepicker.css', array(), 'all' ); | |
| 744 | - $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' ); | |
| 745 | 620 | |
| 746 | 621 | /** |
| 747 | - * This scripts enqueued for Dashboard App. | |
| 748 | - */ | |
| 749 | - $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 ); | |
| 750 | 625 | $this->assets->enqueue( 'betterdocs-icons', 'admin/btd-icon/style.css' ); |
| 751 | 626 | } |
| 752 | 627 | |
| 753 | 628 | /** |
| @@ -758,70 +633,26 @@ | ||
| 758 | 633 | * @return void |
| 759 | 634 | * @since 1.0.0 |
| 760 | 635 | */ |
| 761 | 636 | public function scripts( $hook ) { |
| 762 | - // Classic-UI screens that should offer a "Switch to BetterDocs UI" | |
| 763 | - // button: All Docs, FAQ list, FAQ groups, Product FAQ groups, | |
| 764 | - // Doc Categories, Doc Tags. Maps each to the React admin page to | |
| 765 | - // return to; $switch_args carries extra query args (e.g. the FAQ | |
| 766 | - // Builder tab) appended to the React page URL. | |
| 767 | - $switch_page = ''; | |
| 768 | - $switch_args = array(); | |
| 769 | - if ( 'edit.php' === $hook && 'docs' === get_post_type() ) { | |
| 770 | - $switch_page = 'betterdocs-admin'; | |
| 771 | - } elseif ( 'edit.php' === $hook && 'betterdocs_faq' === get_post_type() ) { | |
| 772 | - $switch_page = 'betterdocs-faq'; | |
| 773 | - } elseif ( 'edit-tags.php' === $hook ) { | |
| 774 | - $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; | |
| 775 | - $taxonomy = $screen && ! empty( $screen->taxonomy ) | |
| 776 | - ? $screen->taxonomy | |
| 777 | - : ( isset( $_GET['taxonomy'] ) ? sanitize_key( wp_unslash( $_GET['taxonomy'] ) ) : '' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 778 | - if ( 'betterdocs_faq_category' === $taxonomy ) { | |
| 779 | - $switch_page = 'betterdocs-faq'; | |
| 780 | - } elseif ( 'betterdocs_product_faq_category' === $taxonomy ) { | |
| 781 | - // Product FAQ groups live on the FAQ Builder's WooCommerce tab. | |
| 782 | - $switch_page = 'betterdocs-faq'; | |
| 783 | - $switch_args = array( 'faq_tab' => 'woocommerce' ); | |
| 784 | - } elseif ( 'doc_category' === $taxonomy ) { | |
| 785 | - $switch_page = 'betterdocs-doc-categories'; | |
| 786 | - } elseif ( 'doc_tag' === $taxonomy ) { | |
| 787 | - $switch_page = 'betterdocs-doc-tags'; | |
| 788 | - } | |
| 789 | - } | |
| 790 | - | |
| 791 | - /** | |
| 792 | - * Allow Pro/add-ons to map their own classic-UI taxonomy screens to a | |
| 793 | - * React admin page for the "Switch to BetterDocs UI" button — e.g. the | |
| 794 | - * Knowledge Base taxonomy, which only exists when Pro is active. | |
| 795 | - * | |
| 796 | - * @param string $switch_page React page slug, or '' for no switcher. | |
| 797 | - * @param string $hook Current admin page hook. | |
| 798 | - */ | |
| 799 | - $switch_page = apply_filters( 'betterdocs_classic_switch_page', $switch_page, $hook ); | |
| 800 | - | |
| 801 | - if ( $switch_page ) { | |
| 637 | + if ( ( $hook === 'edit.php' ) && get_post_type() == 'docs' ) { | |
| 802 | 638 | $this->assets->enqueue( |
| 803 | 639 | 'betterdocs-switcher', |
| 804 | 640 | 'admin/js/switcher.js', |
| 805 | - array( | |
| 806 | - 'jquery', | |
| 807 | - ) | |
| 641 | + [ | |
| 642 | + 'jquery' | |
| 643 | + ] | |
| 808 | 644 | ); |
| 809 | 645 | |
| 810 | 646 | $this->assets->localize( |
| 811 | 647 | 'betterdocs-switcher', |
| 812 | 648 | 'betterdocsSwitcher', |
| 813 | - array( | |
| 649 | + [ | |
| 814 | 650 | 'menu_title' => __( 'Switch to BetterDocs UI', 'betterdocs' ), |
| 815 | - 'page' => $switch_page, | |
| 816 | - 'url' => add_query_arg( | |
| 817 | - array_merge( array( 'page' => $switch_page ), $switch_args ), | |
| 818 | - admin_url( 'admin.php' ) | |
| 819 | - ), | |
| 820 | 651 | 'site_address' => get_bloginfo( 'url' ), |
| 821 | 652 | 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(), |
| 822 | - 'betterdocs_pro_version' => betterdocs()->pro_version(), | |
| 823 | - ) | |
| 653 | + 'betterdocs_pro_version' => betterdocs()->pro_version() | |
| 654 | + ] | |
| 824 | 655 | ); |
| 825 | 656 | |
| 826 | 657 | return; |
| 827 | 658 | } |
| @@ -831,121 +662,82 @@ | ||
| 831 | 662 | if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) { |
| 832 | 663 | return; |
| 833 | 664 | } |
| 834 | 665 | |
| 835 | - 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 | |
| 836 | 667 | $this->assets->register( 'betterdocs-admin', 'admin/js/dashboard.js' ); |
| 837 | 668 | |
| 838 | 669 | $saved_settings = get_option( 'betterdocs_settings', false ); |
| 839 | - $dark_mode = $this->is_dark_mode(); | |
| 670 | + $dark_mode = $saved_settings['dark_mode'] ?? false; | |
| 671 | + $dark_mode = ! empty( $dark_mode ) && boolval( $dark_mode ); | |
| 840 | 672 | $this->assets->localize( |
| 841 | 673 | 'betterdocs-admin', |
| 842 | 674 | 'betterdocs_admin', |
| 843 | - array( | |
| 675 | + [ | |
| 844 | 676 | 'ajaxurl' => admin_url( 'admin-ajax.php' ), |
| 845 | 677 | 'doc_cat_order_nonce' => wp_create_nonce( 'doc_cat_order_nonce' ), |
| 846 | 678 | 'knowledge_base_order_nonce' => wp_create_nonce( 'knowledge_base_order_nonce' ), |
| 847 | - 'paged' => isset( $_GET['paged'] ) ? absint( wp_unslash( $_GET['paged'] ) ) : 0, // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- pagination read from URL. | |
| 848 | - 'per_page_id' => 'edit_doc_category_per_page', | |
| 849 | - 'menu_title' => __( 'Switch to BetterDocs UI', 'betterdocs' ), | |
| 850 | - 'dark_mode' => $dark_mode, | |
| 851 | - 'kb_is_empty' => $this->kb_is_empty(), | |
| 852 | - 'text' => __( 'Copied!', 'betterdocs' ), | |
| 853 | - 'test_report' => __( 'Test Report!', 'betterdocs' ), | |
| 854 | - 'sending' => __( 'Sending...', 'betterdocs' ), | |
| 855 | - 'dir_url' => BETTERDOCS_ABSURL, | |
| 856 | - 'rest_url' => esc_url_raw( rest_url() ), | |
| 857 | - 'free_version' => betterdocs()->version, | |
| 858 | - 'generate_data_url' => get_rest_url( null, '/betterdocs/v1/create-sample-docs' ), | |
| 859 | - 'ai_sample_docs' => array( | |
| 860 | - 'enabled' => (bool) betterdocs()->settings->get( 'enable_ai_sample_docs', true ), | |
| 861 | - 'rest_base' => esc_url_raw( get_rest_url( null, '/betterdocs/v1/sample-docs' ) ), | |
| 862 | - ), | |
| 863 | - 'nonce' => wp_create_nonce( 'wp_rest' ), | |
| 864 | - 'sync_nonce' => wp_create_nonce( 'ai_chatbot_embed' ), | |
| 865 | - 'count_all_docs' => array_sum( (array) wp_count_posts( 'docs' ) ), | |
| 866 | - 'count_all_faq' => array_sum( (array) wp_count_posts( 'betterdocs_faq' ) ), | |
| 867 | - 'faq_order' => get_option( 'betterdocs_faq_order', 'default' ), | |
| 868 | - 'count_new_docs' => $this->get_not_synced_docs_count(), | |
| 869 | - 'admin_url' => admin_url(), | |
| 870 | - 'ia_preview' => betterdocs()->settings->get( 'ia_enable_preview', false ), | |
| 871 | - 'multiple_kb' => betterdocs()->settings->get( 'multiple_kb' ), | |
| 872 | - 'previewMode' => betterdocs()->settings->get( 'ia_enable_preview', false ), | |
| 873 | - 'dashboard_mode' => get_option( 'dashboard_mode' ), | |
| 874 | - 'betterdocs_pro_plugin' => betterdocs()->is_pro_active(), | |
| 875 | - 'betterdocs_pro_version' => betterdocs()->pro_version(), | |
| 876 | - 'analytics_older' => version_compare( betterdocs()->pro_version(), '3.3.4', '<=' ), | |
| 877 | - 'betterdocs_ChatBot_plugin' => is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' ), | |
| 878 | - 'api_docs_teaser' => betterdocs()->show_api_docs_teaser(), | |
| 879 | - 'is_woocommerce_active' => class_exists( 'WooCommerce' ), | |
| 880 | - 'total_doc_category_terms' => wp_count_terms( 'doc_category' ), | |
| 881 | - 'current_admin_language' => Helper::get_current_admin_language(), | |
| 882 | - 'is_multilingual' => Helper::is_multilingual_active(), | |
| 883 | - 'languages' => Helper::get_admin_languages(), | |
| 884 | - /** | |
| 885 | - * MCP page bootstrap. `abilities_api_available` decides whether the | |
| 886 | - * page offers a connection at all: without the Abilities API there | |
| 887 | - * is no tool catalog, so an AI client would connect and find | |
| 888 | - * nothing. `enabled` is only the initial paint — the toggle owns | |
| 889 | - * the value from then on. | |
| 890 | - * | |
| 891 | - * @since 4.9.0 | |
| 892 | - */ | |
| 893 | - 'mcp' => array( | |
| 894 | - 'abilities_api_available' => function_exists( 'wp_register_ability' ), | |
| 895 | - 'enabled' => (bool) betterdocs()->settings->get( 'enable_mcp', false ), | |
| 896 | - 'rest' => 'betterdocs/v1', | |
| 897 | - ), | |
| 898 | - ) | |
| 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 | + ] | |
| 899 | 707 | ); |
| 900 | 708 | |
| 901 | 709 | // If wp-date (which includes moment.js) is not registered, enqueue your custom moment.js |
| 902 | 710 | if ( ! wp_script_is( 'wp-date', 'registered' ) ) { |
| 903 | - $this->assets->enqueue( 'moment', 'vendor/js/moment.min.js', array() ); | |
| 711 | + $this->assets->enqueue( 'moment', 'vendor/js/moment.min.js', [] ); | |
| 904 | 712 | } |
| 905 | 713 | wp_enqueue_script( 'betterdocs-admin' ); |
| 906 | 714 | |
| 907 | 715 | /** |
| 908 | - * Duplicate Codes Need to Be Removed From Here Onwards | |
| 909 | - */ | |
| 716 | + * Duplicate Codes Need to Be Removed From Here Onwards | |
| 717 | + */ | |
| 910 | 718 | |
| 911 | - // FAQ Builder Related Localization | |
| 719 | + //FAQ Builder Related Localization | |
| 912 | 720 | betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/css/faq.css' ); |
| 913 | 721 | betterdocs()->assets->enqueue( 'betterdocs-admin-faq', 'admin/js/faq.js' ); |
| 914 | 722 | |
| 915 | - // Load the classic editor (TinyMCE + QuickTags) so the FAQ rich-text editor can mount via wp.editor.initialize(). | |
| 916 | - if ( function_exists( 'wp_enqueue_editor' ) ) { | |
| 917 | - wp_enqueue_editor(); | |
| 918 | - } | |
| 919 | - if ( function_exists( 'wp_enqueue_media' ) ) { | |
| 920 | - wp_enqueue_media(); | |
| 921 | - } | |
| 922 | - | |
| 923 | 723 | // removing emoji support |
| 924 | 724 | remove_action( 'wp_head', 'print_emoji_detection_script', 7 ); |
| 925 | 725 | remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); |
| 926 | 726 | |
| 927 | - // Get settings and remove unnecessary keys | |
| 928 | - $betterdocs_settings = get_option( 'betterdocs_settings', false ); | |
| 929 | - if ( is_array( $betterdocs_settings ) && ! current_user_can( 'edit_docs_settings' ) ) { | |
| 930 | - foreach ( Settings::sensitive_api_key_fields() as $sensitive_key ) { | |
| 931 | - unset( $betterdocs_settings[ $sensitive_key ] ); | |
| 932 | - } | |
| 933 | - } | |
| 934 | - | |
| 935 | 727 | betterdocs()->assets->localize( |
| 936 | 728 | 'betterdocs-admin-faq', |
| 937 | 729 | 'betterdocsFaq', |
| 938 | - array( | |
| 730 | + [ | |
| 939 | 731 | 'dir_url' => BETTERDOCS_ABSURL, |
| 940 | 732 | 'rest_url' => esc_url_raw( rest_url() ), |
| 941 | 733 | 'free_version' => betterdocs()->version, |
| 942 | 734 | 'nonce' => wp_create_nonce( 'wp_rest' ), |
| 943 | - 'betterdocs_settings' => $betterdocs_settings, | |
| 944 | - ) | |
| 735 | + 'betterdocs_settings' => get_option( 'betterdocs_settings', false ) | |
| 736 | + ] | |
| 945 | 737 | ); |
| 946 | 738 | |
| 947 | - // Glossaries Related Localization | |
| 739 | + //Glossaries Related Localization | |
| 948 | 740 | betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/css/faq.css' ); |
| 949 | 741 | |
| 950 | 742 | betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/js/glossaries.js' ); |
| 951 | 743 | |
| @@ -951,15 +743,15 @@ | ||
| 951 | 743 | |
| 952 | 744 | betterdocs()->assets->localize( |
| 953 | 745 | 'betterdocs-admin-glossaries', |
| 954 | 746 | 'betterdocsGlossary', |
| 955 | - array( | |
| 747 | + [ | |
| 956 | 748 | 'dir_url' => BETTERDOCS_ABSURL, |
| 957 | 749 | 'rest_url' => esc_url_raw( rest_url() ), |
| 958 | 750 | 'free_version' => betterdocs()->version, |
| 959 | 751 | 'nonce' => wp_create_nonce( 'wp_rest' ), |
| 960 | - 'betterdocs_settings' => $betterdocs_settings, | |
| 961 | - ) | |
| 752 | + 'betterdocs_settings' => get_option( 'betterdocs_settings', false ) | |
| 753 | + ] | |
| 962 | 754 | ); |
| 963 | 755 | } |
| 964 | 756 | |
| 965 | 757 | /** |
| @@ -968,31 +760,31 @@ | ||
| 968 | 760 | * @return void |
| 969 | 761 | * @since 1.0.0 |
| 970 | 762 | */ |
| 971 | 763 | public function header( $admin_tab_name ) { |
| 972 | - $quick_links = array( | |
| 764 | + $quick_links = [ | |
| 973 | 765 | 'switch_view' => sprintf( |
| 974 | 766 | '<a href="%s" class="betterdocs-button betterdocs-button-secondary">%s</a>', |
| 975 | 767 | add_query_arg( |
| 976 | - array( | |
| 768 | + [ | |
| 977 | 769 | 'post_type' => 'docs', |
| 978 | - 'bdocs_view' => 'classic', | |
| 979 | - ), | |
| 770 | + 'bdocs_view' => 'classic' | |
| 771 | + ], | |
| 980 | 772 | 'edit.php' |
| 981 | 773 | ), |
| 982 | 774 | __( 'Switch to Classic UI', 'betterdocs' ) |
| 983 | 775 | ), |
| 984 | - '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' ) ), | |
| 985 | - ); | |
| 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 | + ]; | |
| 986 | 778 | |
| 987 | 779 | $quick_links = apply_filters( 'betterdocs_quick_links', $quick_links ); |
| 988 | 780 | |
| 989 | 781 | betterdocs()->views->get( |
| 990 | 782 | 'admin/header', |
| 991 | - array( | |
| 783 | + [ | |
| 992 | 784 | 'quick_links' => $quick_links, |
| 993 | - 'active_tab' => $admin_tab_name, | |
| 994 | - ) | |
| 785 | + 'active_tab' => $admin_tab_name | |
| 786 | + ] | |
| 995 | 787 | ); |
| 996 | 788 | } |
| 997 | 789 | |
| 998 | 790 | /** |
| @@ -1001,17 +793,17 @@ | ||
| 1001 | 793 | * @return void |
| 1002 | 794 | * @since 1.0.0 |
| 1003 | 795 | */ |
| 1004 | 796 | public function menus() { |
| 1005 | - $default_args = array( | |
| 797 | + $default_args = [ | |
| 1006 | 798 | 'page_title' => 'BetterDocs', |
| 1007 | 799 | 'menu_title' => 'BetterDocs', |
| 1008 | - 'capability' => 'edit_docs', // Unified capability | |
| 800 | + 'capability' => 'edit_docs', // Unified capability | |
| 1009 | 801 | 'menu_slug' => $this->slug, |
| 1010 | - 'callback' => array( $this, 'output' ), | |
| 802 | + 'callback' => [ $this, 'output' ], | |
| 1011 | 803 | 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ), |
| 1012 | - 'position' => 5, | |
| 1013 | - ); | |
| 804 | + 'position' => 5 | |
| 805 | + ]; | |
| 1014 | 806 | |
| 1015 | 807 | $_menu_position = 5; |
| 1016 | 808 | global $submenu; |
| 1017 | 809 | |
| @@ -1017,29 +809,24 @@ | ||
| 1017 | 809 | |
| 1018 | 810 | // Always register both UI endpoints |
| 1019 | 811 | $this->register_modern_ui_fallback(); |
| 1020 | 812 | |
| 1021 | - // The one-time MCP discovery badge (ADR-063). Decided once, here, and | |
| 1022 | - // remembered for `mcp_badge_styles()`: the pill's markup and the pill's | |
| 1023 | - // stylesheet have to be printed on the same requests as each other. | |
| 1024 | - $this->mcp_badge = self::should_flag_mcp(); | |
| 1025 | - | |
| 1026 | 813 | foreach ( $this->menu_list() as $key => $value ) { |
| 1027 | - if ( 'betterdocs' === $key ) { | |
| 814 | + if ( $key === 'betterdocs' ) { | |
| 1028 | 815 | $callable = 'add_menu_page'; |
| 1029 | 816 | $value = wp_parse_args( $value, $default_args ); |
| 1030 | 817 | call_user_func_array( $callable, $value ); |
| 1031 | 818 | } else { |
| 1032 | - $is_core_page = strpos( $value['menu_slug'], '?' ) !== false; | |
| 819 | + $is_core_page = strpos($value['menu_slug'], '?') !== false; | |
| 1033 | 820 | |
| 1034 | - if ( $is_core_page ) { | |
| 821 | + if ($is_core_page) { | |
| 1035 | 822 | // Add classic UI directly |
| 1036 | - $submenu[ $this->slug ][] = array( | |
| 823 | + $submenu[$this->slug][] = [ | |
| 1037 | 824 | $value['menu_title'], |
| 1038 | 825 | $value['capability'], |
| 1039 | 826 | $value['menu_slug'], |
| 1040 | - $value['page_title'], | |
| 1041 | - ); | |
| 827 | + $value['page_title'] | |
| 828 | + ]; | |
| 1042 | 829 | } else { |
| 1043 | 830 | // Add modern UI through WordPress API |
| 1044 | 831 | add_submenu_page( |
| 1045 | 832 | $this->slug, |
| @@ -1049,223 +836,30 @@ | ||
| 1049 | 836 | $value['menu_slug'], |
| 1050 | 837 | $value['callback'] |
| 1051 | 838 | ); |
| 1052 | 839 | } |
| 1053 | - ++$_menu_position; | |
| 840 | + $_menu_position++; | |
| 1054 | 841 | } |
| 1055 | 842 | } |
| 1056 | - | |
| 1057 | - $this->paint_mcp_badge(); | |
| 1058 | 843 | } |
| 1059 | 844 | |
| 1060 | - /** | |
| 1061 | - * Append the discovery badge to the registered menu titles. | |
| 1062 | - * | |
| 1063 | - * **After** registration, editing `$menu` / `$submenu` in place — the same | |
| 1064 | - * shape `add_custom_classes_to_menu_items()` uses — and never by passing a | |
| 1065 | - * decorated title to `add_menu_page()`. That distinction is not cosmetic: | |
| 1066 | - * core stores `sanitize_title( $menu_title )` as `$admin_page_hooks[ $slug ]` | |
| 1067 | - * (`wp-admin/includes/plugin.php:1397`) and builds every child page's hook | |
| 1068 | - * suffix from it (`get_plugin_page_hookname()`), so markup in the parent's | |
| 1069 | - * title renames `betterdocs_page_betterdocs-mcp` — and the MCP screen, whose | |
| 1070 | - * asset enqueue is keyed on that exact suffix, silently loads no React | |
| 1071 | - * bundle at all. Measured: the first visit came back 102,513 bytes with no | |
| 1072 | - * `dashboard.js`, against 489,272 bytes once the badge had cleared. | |
| 1073 | - * | |
| 1074 | - * Titles are only ever appended to, never rebuilt: the menu list is filtered | |
| 1075 | - * (`betterdocs_admin_menu`), so whatever a filter put in a title survives. | |
| 1076 | - * | |
| 1077 | - * @return void | |
| 1078 | - * @since 4.9.0 | |
| 1079 | - */ | |
| 1080 | - private function paint_mcp_badge() { | |
| 1081 | - if ( ! $this->mcp_badge ) { | |
| 1082 | - return; | |
| 1083 | - } | |
| 1084 | - | |
| 1085 | - global $menu, $submenu; | |
| 1086 | - | |
| 1087 | - if ( is_array( $menu ) ) { | |
| 1088 | - foreach ( $menu as &$item ) { | |
| 1089 | - if ( isset( $item[2] ) && $this->slug === $item[2] ) { | |
| 1090 | - $item[0] .= self::mcp_parent_bubble(); | |
| 1091 | - break; | |
| 1092 | - } | |
| 1093 | - } | |
| 1094 | - unset( $item ); | |
| 1095 | - } | |
| 1096 | - | |
| 1097 | - if ( isset( $submenu[ $this->slug ] ) && is_array( $submenu[ $this->slug ] ) ) { | |
| 1098 | - foreach ( $submenu[ $this->slug ] as &$sub_item ) { | |
| 1099 | - if ( isset( $sub_item[2] ) && 'betterdocs-mcp' === $sub_item[2] ) { | |
| 1100 | - $sub_item[0] .= self::mcp_submenu_pill(); | |
| 1101 | - break; | |
| 1102 | - } | |
| 1103 | - } | |
| 1104 | - unset( $sub_item ); | |
| 1105 | - } | |
| 1106 | - } | |
| 1107 | - | |
| 1108 | - /** | |
| 1109 | - * Whether the current user should see the one-time MCP discovery badge. | |
| 1110 | - * | |
| 1111 | - * True only for a user who can actually reach the screen and has never | |
| 1112 | - * opened it. The capability is the one the MCP menu item is already | |
| 1113 | - * registered with (`manage_options`) rather than a second, re-derived rule — | |
| 1114 | - * so the badge can never advertise a page its reader cannot open. | |
| 1115 | - * | |
| 1116 | - * @return bool | |
| 1117 | - * @since 4.9.0 | |
| 1118 | - */ | |
| 1119 | - public static function should_flag_mcp() { | |
| 1120 | - if ( ! current_user_can( 'manage_options' ) ) { | |
| 1121 | - return false; | |
| 1122 | - } | |
| 1123 | - | |
| 1124 | - $user_id = get_current_user_id(); | |
| 1125 | - | |
| 1126 | - if ( ! $user_id ) { | |
| 1127 | - return false; | |
| 1128 | - } | |
| 1129 | - | |
| 1130 | - // `get_user_meta( …, true )` answers '' for a key that is not there, and | |
| 1131 | - // the value written is always `time()` — so an empty string is the only | |
| 1132 | - // shape "never opened" takes. | |
| 1133 | - return '' === get_user_meta( $user_id, self::MCP_SEEN_META, true ); | |
| 1134 | - } | |
| 1135 | - | |
| 1136 | - /** | |
| 1137 | - * WordPress' own update-count bubble, for the BetterDocs parent menu item. | |
| 1138 | - * | |
| 1139 | - * Core's markup on purpose: the red bubble, its position and its dark-mode | |
| 1140 | - * colours are already in `wp-admin`'s stylesheet, so this needs no CSS of | |
| 1141 | - * ours and cannot drift from the Plugins/Updates bubbles beside it. | |
| 1142 | - * | |
| 1143 | - * @return string | |
| 1144 | - * @since 4.9.0 | |
| 1145 | - */ | |
| 1146 | - private static function mcp_parent_bubble() { | |
| 1147 | - return ' <span class="update-plugins count-1"><span class="update-count">1</span></span>'; | |
| 1148 | - } | |
| 1149 | - | |
| 1150 | - /** | |
| 1151 | - * The green "New" pill for the MCP submenu item. | |
| 1152 | - * | |
| 1153 | - * Core has no submenu-badge markup, so this one is ours — styled by | |
| 1154 | - * `mcp_badge_styles()`. | |
| 1155 | - * | |
| 1156 | - * @return string | |
| 1157 | - * @since 4.9.0 | |
| 1158 | - */ | |
| 1159 | - private static function mcp_submenu_pill() { | |
| 1160 | - return ' <span class="bd-menu-pill">' . esc_html__( 'New', 'betterdocs' ) . '</span>'; | |
| 1161 | - } | |
| 1162 | - | |
| 1163 | - /** | |
| 1164 | - * Whether this request is the MCP screen being opened by someone who can | |
| 1165 | - * open it. | |
| 1166 | - * | |
| 1167 | - * The whole of the clearing decision, in one static so it can be pinned by | |
| 1168 | - * a test. It reads the **page slug** rather than an admin hook suffix | |
| 1169 | - * because the suffix is derived state: core builds it from | |
| 1170 | - * `sanitize_title()` of the *parent* menu title (`get_plugin_page_hookname()`), | |
| 1171 | - * that title is filtered (`betterdocs_admin_menu`), and the parent slug is | |
| 1172 | - * spelled two ways in this class already. `?page=betterdocs-mcp` is the one | |
| 1173 | - * thing that identifies this screen on every install (ADR-065). | |
| 1174 | - * | |
| 1175 | - * The capability is `manage_options`, the same one the MCP menu item is | |
| 1176 | - * registered with and the same one {@see self::should_flag_mcp()} gates on: | |
| 1177 | - * nothing may be written for a user who cannot reach the page. | |
| 1178 | - * | |
| 1179 | - * @return bool | |
| 1180 | - * @since 4.9.0 | |
| 1181 | - */ | |
| 1182 | - public static function is_mcp_screen_request() { | |
| 1183 | - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection; see mark_mcp_seen(). | |
| 1184 | - $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; | |
| 1185 | - | |
| 1186 | - if ( 'betterdocs-mcp' !== $page ) { | |
| 1187 | - return false; | |
| 1188 | - } | |
| 1189 | - | |
| 1190 | - return current_user_can( 'manage_options' ); | |
| 1191 | - } | |
| 1192 | - | |
| 1193 | - /** | |
| 1194 | - * Record that this user has now seen the MCP screen. | |
| 1195 | - * | |
| 1196 | - * Bound to `admin_init` — which fires for every admin request — and gated on | |
| 1197 | - * the page slug, rather than to `load-{$hook_suffix}` for the one suffix | |
| 1198 | - * `add_submenu_page()` happened to return. `admin_menu` has already run by | |
| 1199 | - * the time `admin_init` fires (measured), so the badge is still painted on | |
| 1200 | - * *this* request and is gone from the next admin page — that is expected and | |
| 1201 | - * correct. Do not add JavaScript to strip it mid-request. | |
| 1202 | - * | |
| 1203 | - * **No nonce, on purpose.** A nonce protects a state change an attacker | |
| 1204 | - * could make a logged-in administrator perform unknowingly. The only state | |
| 1205 | - * here is "this administrator has now been shown the MCP screen once", it is | |
| 1206 | - * written for the current user alone, it holds no attacker-chosen value, and | |
| 1207 | - * the worst a forged request can achieve is hiding a discovery badge from | |
| 1208 | - * the person it was drawn for. A nonce on a plain page view would also have | |
| 1209 | - * to survive the menu link, which carries none. | |
| 1210 | - * | |
| 1211 | - * @return void | |
| 1212 | - * @since 4.9.0 | |
| 1213 | - */ | |
| 1214 | - public function mark_mcp_seen() { | |
| 1215 | - if ( ! self::is_mcp_screen_request() ) { | |
| 1216 | - return; | |
| 1217 | - } | |
| 1218 | - | |
| 1219 | - $user_id = get_current_user_id(); | |
| 1220 | - | |
| 1221 | - if ( ! $user_id ) { | |
| 1222 | - return; | |
| 1223 | - } | |
| 1224 | - | |
| 1225 | - update_user_meta( $user_id, self::MCP_SEEN_META, time() ); | |
| 1226 | - } | |
| 1227 | - | |
| 1228 | - /** | |
| 1229 | - * The handful of declarations the "New" pill needs, inline, and only while | |
| 1230 | - * it is being shown. | |
| 1231 | - * | |
| 1232 | - * The menu is read from the WordPress Dashboard, and `styles()` above | |
| 1233 | - * early-returns on non-BetterDocs screens — so `admin/css/dashboard.css` is | |
| 1234 | - * not loaded where this pill is seen. Loading the whole BetterDocs admin | |
| 1235 | - * stylesheet globally, or shipping a stylesheet file for nine declarations, | |
| 1236 | - * both cost far more than printing them here. The accent is written out | |
| 1237 | - * rather than taken from `--base-color-700`: that token lives in | |
| 1238 | - * `dashboard.css`, which is exactly the file that is not loaded here. | |
| 1239 | - * | |
| 1240 | - * @return void | |
| 1241 | - * @since 4.9.0 | |
| 1242 | - */ | |
| 1243 | - public function mcp_badge_styles() { | |
| 1244 | - if ( ! $this->mcp_badge ) { | |
| 1245 | - return; | |
| 1246 | - } | |
| 1247 | - | |
| 1248 | - echo '<style id="betterdocs-menu-pill">#adminmenu .bd-menu-pill{display:inline-block;background:#00b884;color:#fff;font-size:10px;text-transform:uppercase;line-height:1.6;padding:1px 6px;margin-left:6px;border-radius:9px;}</style>' . "\n"; | |
| 1249 | - } | |
| 1250 | - | |
| 1251 | 845 | private function register_modern_ui_fallback() { |
| 1252 | 846 | // Add the submenu with valid parent slug |
| 1253 | 847 | add_submenu_page( |
| 1254 | 848 | 'betterdocs', // Valid parent slug |
| 1255 | - __( 'All Docs', 'betterdocs' ), | |
| 849 | + __('All Docs', 'betterdocs'), | |
| 1256 | 850 | '', // Empty menu title hides it |
| 1257 | 851 | 'edit_docs', |
| 1258 | 852 | 'betterdocs-admin', |
| 1259 | - array( $this, 'output' ) | |
| 853 | + [ $this, 'output' ] | |
| 1260 | 854 | ); |
| 1261 | 855 | |
| 1262 | 856 | // Hide the menu item from appearing in the admin sidebar |
| 1263 | 857 | global $submenu; |
| 1264 | - if ( isset( $submenu['betterdocs'] ) ) { | |
| 1265 | - foreach ( $submenu['betterdocs'] as $key => $item ) { | |
| 1266 | - if ( 'betterdocs-admin' === $item[2] ) { | |
| 1267 | - 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]); | |
| 1268 | 862 | break; |
| 1269 | 863 | } |
| 1270 | 864 | } |
| 1271 | 865 | } |
| @@ -1278,17 +872,17 @@ | ||
| 1278 | 872 | * @since 1.0.0 |
| 1279 | 873 | */ |
| 1280 | 874 | public function output() { |
| 1281 | 875 | if ( betterdocs()->is_pro_active() |
| 1282 | - && version_compare( betterdocs()->pro_version(), '3.3.4', '<=' ) | |
| 1283 | - && 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' ) { | |
| 1284 | 878 | betterdocs_pro()->views->get( 'admin/analytics-pro' ); |
| 1285 | 879 | } else { |
| 1286 | 880 | betterdocs()->views->get( |
| 1287 | 881 | 'admin/main', |
| 1288 | - array( | |
| 1289 | - 'admin_ui' => 'dnd', | |
| 1290 | - ) | |
| 882 | + [ | |
| 883 | + 'admin_ui' => 'dnd' | |
| 884 | + ] | |
| 1291 | 885 | ); |
| 1292 | 886 | } |
| 1293 | 887 | } |
| 1294 | 888 | |
| @@ -1301,10 +895,11 @@ | ||
| 1301 | 895 | * @param array $callback |
| 1302 | 896 | * |
| 1303 | 897 | * @return array |
| 1304 | 898 | * @since 2.5.0 |
| 899 | + * | |
| 1305 | 900 | */ |
| 1306 | - 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 = [] ) { | |
| 1307 | 902 | return Helper::normalize_menu( $title, $slug, $cap, $callback, $optional ); |
| 1308 | 903 | } |
| 1309 | 904 | |
| 1310 | 905 | /** |
| @@ -1313,34 +908,34 @@ | ||
| 1313 | 908 | * @return array |
| 1314 | 909 | * @since 1.0.0 |
| 1315 | 910 | */ |
| 1316 | 911 | private function menu_list() { |
| 1317 | - $parent_slug = array(); | |
| 912 | + $parent_slug = []; | |
| 1318 | 913 | |
| 1319 | - $betterdocs_admin_pages = array( | |
| 1320 | - 'betterdocs' => array( | |
| 914 | + $betterdocs_admin_pages = [ | |
| 915 | + 'betterdocs' => [ | |
| 1321 | 916 | 'menu_slug' => $this->slug, |
| 1322 | 917 | 'page_title' => 'BetterDocs', |
| 1323 | 918 | 'menu_title' => 'BetterDocs', |
| 1324 | 919 | 'capability' => 'edit_docs', |
| 1325 | - 'callback' => array( $this, 'output' ), | |
| 920 | + 'callback' => [ $this, 'output' ], | |
| 1326 | 921 | 'icon_url' => betterdocs()->assets->icon( 'betterdocs-icon-white.svg', true ), |
| 1327 | - 'position' => 5, | |
| 1328 | - ), | |
| 1329 | - 'dashboard' => $this->normalize_menu( | |
| 922 | + 'position' => 5 | |
| 923 | + ], | |
| 924 | + 'dashboard' => $this->normalize_menu( | |
| 1330 | 925 | __( 'Dashboard', 'betterdocs' ), |
| 1331 | 926 | 'betterdocs-dashboard', |
| 1332 | 927 | 'edit_docs', |
| 1333 | - array( | |
| 928 | + [ | |
| 1334 | 929 | $this, |
| 1335 | - 'output', | |
| 1336 | - ) | |
| 930 | + 'output' | |
| 931 | + ] | |
| 1337 | 932 | ), |
| 1338 | 933 | 'all_docs' => $this->normalize_menu( |
| 1339 | 934 | __( 'All Docs', 'betterdocs' ), |
| 1340 | 935 | $this->ui_slug(), |
| 1341 | 936 | 'edit_docs', |
| 1342 | - array( $this, 'output' ), | |
| 937 | + [ $this, 'output' ], | |
| 1343 | 938 | $parent_slug |
| 1344 | 939 | ), |
| 1345 | 940 | 'add_new' => $this->normalize_menu( |
| 1346 | 941 | __( 'Add New', 'betterdocs' ), |
| @@ -1347,48 +942,34 @@ | ||
| 1347 | 942 | 'post-new.php?post_type=docs' |
| 1348 | 943 | ), |
| 1349 | 944 | 'categories' => $this->normalize_menu( |
| 1350 | 945 | __( 'Categories', 'betterdocs' ), |
| 1351 | - 'betterdocs-doc-categories', | |
| 1352 | - 'manage_doc_terms', | |
| 1353 | - array( $this, 'output' ), | |
| 1354 | - $parent_slug | |
| 946 | + 'edit-tags.php?taxonomy=doc_category&post_type=docs', | |
| 947 | + 'manage_doc_terms' | |
| 1355 | 948 | ), |
| 1356 | 949 | 'tags' => $this->normalize_menu( |
| 1357 | 950 | __( 'Tags', 'betterdocs' ), |
| 1358 | - 'betterdocs-doc-tags', | |
| 1359 | - 'manage_doc_terms', | |
| 1360 | - array( $this, 'output' ), | |
| 1361 | - $parent_slug | |
| 951 | + 'edit-tags.php?taxonomy=doc_tag&post_type=docs', | |
| 952 | + 'manage_doc_terms' | |
| 1362 | 953 | ), |
| 1363 | 954 | 'settings' => $this->normalize_menu( |
| 1364 | 955 | __( 'Settings', 'betterdocs' ), |
| 1365 | 956 | 'betterdocs-settings', |
| 1366 | 957 | 'edit_docs_settings', |
| 1367 | - array( | |
| 958 | + [ | |
| 1368 | 959 | $this, |
| 1369 | - 'output', | |
| 1370 | - ), | |
| 960 | + 'output' | |
| 961 | + ], | |
| 1371 | 962 | $parent_slug |
| 1372 | 963 | ), |
| 1373 | - 'mcp' => $this->normalize_menu( | |
| 1374 | - __( 'MCP', 'betterdocs' ), | |
| 1375 | - 'betterdocs-mcp', | |
| 1376 | - 'manage_options', | |
| 1377 | - array( | |
| 1378 | - $this, | |
| 1379 | - 'output', | |
| 1380 | - ), | |
| 1381 | - $parent_slug | |
| 1382 | - ), | |
| 1383 | 964 | 'analytics' => $this->normalize_menu( |
| 1384 | 965 | __( 'Analytics', 'betterdocs' ), |
| 1385 | 966 | 'betterdocs-analytics', |
| 1386 | 967 | 'read_docs_analytics', |
| 1387 | - array( | |
| 968 | + [ | |
| 1388 | 969 | $this, |
| 1389 | - 'output', | |
| 1390 | - ), | |
| 970 | + 'output' | |
| 971 | + ], | |
| 1391 | 972 | $parent_slug |
| 1392 | 973 | ), |
| 1393 | 974 | 'faq' => $this->normalize_menu( |
| 1394 | 975 | __( 'FAQ Builder', 'betterdocs' ), |
| @@ -1393,15 +974,15 @@ | ||
| 1393 | 974 | 'faq' => $this->normalize_menu( |
| 1394 | 975 | __( 'FAQ Builder', 'betterdocs' ), |
| 1395 | 976 | 'betterdocs-faq', |
| 1396 | 977 | 'read_faq_builder', |
| 1397 | - array( | |
| 978 | + [ | |
| 1398 | 979 | $this, |
| 1399 | - 'output', | |
| 1400 | - ), | |
| 980 | + 'output' | |
| 981 | + ], | |
| 1401 | 982 | $parent_slug |
| 1402 | - ), | |
| 1403 | - ); | |
| 983 | + ) | |
| 984 | + ]; | |
| 1404 | 985 | |
| 1405 | 986 | if ( betterdocs()->is_pro_active() && betterdocs()->settings->get( 'enable_glossaries' ) == true ) { |
| 1406 | 987 | $betterdocs_admin_pages['glossaries'] = $this->normalize_menu( |
| 1407 | 988 | __( 'Glossaries', 'betterdocs' ), |
| @@ -1406,66 +987,50 @@ | ||
| 1406 | 987 | $betterdocs_admin_pages['glossaries'] = $this->normalize_menu( |
| 1407 | 988 | __( 'Glossaries', 'betterdocs' ), |
| 1408 | 989 | 'betterdocs-glossaries', |
| 1409 | 990 | 'read_docs_analytics', |
| 1410 | - array( | |
| 991 | + [ | |
| 1411 | 992 | $this, |
| 1412 | - 'output', | |
| 1413 | - ), | |
| 993 | + 'output' | |
| 994 | + ], | |
| 1414 | 995 | $parent_slug |
| 1415 | 996 | ); |
| 1416 | 997 | } |
| 1417 | 998 | |
| 1418 | - // API Docs ships in Pro, which overwrites this same key in place — declaring | |
| 1419 | - // the slot here is what keeps the item in this position. Without Pro it | |
| 1420 | - // holds Free's locked teaser instead. | |
| 1421 | - if ( betterdocs()->show_api_docs_teaser() || betterdocs()->has_api_docs() ) { | |
| 1422 | - $betterdocs_admin_pages['api_docs'] = $this->normalize_menu( | |
| 1423 | - __( 'API Docs', 'betterdocs' ), | |
| 1424 | - 'betterdocs-api-docs', | |
| 1425 | - apply_filters( 'betterdocs_api_ref_capability', 'manage_options' ), | |
| 1426 | - 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 | + [ | |
| 1427 | 1006 | $this, |
| 1428 | - 'output', | |
| 1429 | - ), | |
| 1430 | - $parent_slug | |
| 1431 | - ); | |
| 1432 | - } | |
| 1007 | + 'output' | |
| 1008 | + ], | |
| 1009 | + $parent_slug | |
| 1010 | + ); | |
| 1011 | + } | |
| 1433 | 1012 | |
| 1434 | - if ( ! betterdocs()->is_chatbot_active() ) { | |
| 1435 | - $betterdocs_admin_pages['ai_chatbot'] = $this->normalize_menu( | |
| 1436 | - __( 'AI Chatbot', 'betterdocs' ), | |
| 1437 | - 'betterdocs-ai-chatbot', | |
| 1438 | - 'edit_docs_settings', | |
| 1439 | - array( | |
| 1440 | - $this, | |
| 1441 | - 'output', | |
| 1442 | - ), | |
| 1443 | - $parent_slug | |
| 1444 | - ); | |
| 1445 | - } | |
| 1013 | + return apply_filters( 'betterdocs_admin_menu', $betterdocs_admin_pages, [$this, 'output'], $parent_slug); | |
| 1446 | 1014 | |
| 1447 | - return apply_filters( 'betterdocs_admin_menu', $betterdocs_admin_pages, array( $this, 'output' ), $parent_slug ); | |
| 1448 | - } | |
| 1015 | + } | |
| 1449 | 1016 | |
| 1450 | 1017 | public function add_custom_classes_to_menu_items() { |
| 1451 | 1018 | global $menu, $submenu; |
| 1452 | 1019 | |
| 1453 | - $menu_items = array( | |
| 1020 | + $menu_items = [ | |
| 1454 | 1021 | 'betterdocs' => 'betterdocs', |
| 1455 | 1022 | 'betterdocs_page_all_docs' => 'betterdocs-all-docs', |
| 1456 | 1023 | 'betterdocs_page_add_new' => 'betterdocs-add-new', |
| 1457 | - 'betterdocs-doc-categories' => 'betterdocs-categories', | |
| 1458 | - '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', | |
| 1459 | 1026 | 'betterdocs-settings' => 'betterdocs-settings', |
| 1460 | - 'betterdocs-mcp' => 'betterdocs-mcp', | |
| 1461 | 1027 | 'betterdocs-analytics' => 'betterdocs-analytics', |
| 1462 | 1028 | 'betterdocs-faq' => 'betterdocs-faq', |
| 1463 | 1029 | 'betterdocs-glossaries' => 'betterdocs-glossaries', |
| 1464 | 1030 | 'betterdocs-ai-chatbot' => 'betterdocs-ai-chatbot', |
| 1465 | - 'betterdocs-api-docs' => 'betterdocs-api-docs', | |
| 1466 | - 'edit-tags.php?taxonomy=knowledge_base&post_type=docs' => 'betterdocs-multiplekb', | |
| 1467 | - ); | |
| 1031 | + 'edit-tags.php?taxonomy=knowledge_base&post_type=docs' => 'betterdocs-multiplekb' | |
| 1032 | + ]; | |
| 1468 | 1033 | |
| 1469 | 1034 | foreach ( $menu as &$item ) { |
| 1470 | 1035 | if ( isset( $menu_items[ $item[2] ] ) ) { |
| 1471 | 1036 | if ( ! isset( $item[4] ) ) { |
| @@ -1495,12 +1060,12 @@ | ||
| 1495 | 1060 | $menus['quick_setup'] = $this->normalize_menu( |
| 1496 | 1061 | __( 'Quick Setup', 'betterdocs' ), |
| 1497 | 1062 | 'betterdocs-setup', |
| 1498 | 1063 | 'delete_users', |
| 1499 | - array( | |
| 1064 | + [ | |
| 1500 | 1065 | $this->container->get( SetupWizard::class ), |
| 1501 | - 'views', | |
| 1502 | - ) | |
| 1066 | + 'views' | |
| 1067 | + ] | |
| 1503 | 1068 | ); |
| 1504 | 1069 | } |
| 1505 | 1070 | |
| 1506 | 1071 | return $menus; |
| @@ -1508,12 +1073,8 @@ | ||
| 1508 | 1073 | |
| 1509 | 1074 | public function insert_plugin_links( $links ) { |
| 1510 | 1075 | $links[] = '<a href="admin.php?page=betterdocs-settings">' . __( 'Settings', 'betterdocs' ) . '</a>'; |
| 1511 | 1076 | |
| 1512 | - if ( ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ) ) { | |
| 1513 | - $links[] = '<a href="https://betterdocs.co/upgrade-to-pro-plugins-wp" target="_blank" style="color: #000; font-weight: bold;">' . __( 'Upgrade to Pro', 'betterdocs' ) . '</a>'; | |
| 1514 | - } | |
| 1515 | - | |
| 1516 | 1077 | return $links; |
| 1517 | 1078 | } |
| 1518 | 1079 | |
| 1519 | 1080 | public function toolbar_menu( $admin_bar ) { |
| @@ -1540,22 +1101,17 @@ | ||
| 1540 | 1101 | } |
| 1541 | 1102 | |
| 1542 | 1103 | $slug = $this->settings->get( 'encyclopedia_root_slug' ); |
| 1543 | 1104 | |
| 1544 | - global $wp_rewrite; | |
| 1545 | - if ( $wp_rewrite->using_index_permalinks() ) { | |
| 1546 | - $slug = $wp_rewrite->index . '/' . $slug; | |
| 1547 | - } | |
| 1548 | - | |
| 1549 | 1105 | $encyclopedia_url = home_url( $slug ); |
| 1550 | 1106 | |
| 1551 | 1107 | $admin_bar->add_node( |
| 1552 | - array( | |
| 1108 | + [ | |
| 1553 | 1109 | 'parent' => 'site-name', |
| 1554 | 1110 | 'id' => 'view-docs', |
| 1555 | 1111 | 'title' => __( 'Visit Documentation', 'betterdocs' ), |
| 1556 | - 'href' => $docs_url, | |
| 1557 | - ) | |
| 1112 | + 'href' => $docs_url | |
| 1113 | + ] | |
| 1558 | 1114 | ); |
| 1559 | 1115 | |
| 1560 | 1116 | $is_enable_encyclopedia = betterdocs()->settings->get( 'enable_encyclopedia' ); |
| 1561 | 1117 | |
| @@ -1560,14 +1116,14 @@ | ||
| 1560 | 1116 | $is_enable_encyclopedia = betterdocs()->settings->get( 'enable_encyclopedia' ); |
| 1561 | 1117 | |
| 1562 | 1118 | if ( $is_enable_encyclopedia && betterdocs()->is_pro_active() ) { |
| 1563 | 1119 | $admin_bar->add_node( |
| 1564 | - array( | |
| 1120 | + [ | |
| 1565 | 1121 | 'parent' => 'site-name', |
| 1566 | 1122 | 'id' => 'view-encyclopedia', |
| 1567 | 1123 | 'title' => __( 'Visit Encyclopedia', 'betterdocs' ), |
| 1568 | - 'href' => $encyclopedia_url, | |
| 1569 | - ) | |
| 1124 | + 'href' => $encyclopedia_url | |
| 1125 | + ] | |
| 1570 | 1126 | ); |
| 1571 | 1127 | } |
| 1572 | 1128 | } |
| 1573 | 1129 | |
| @@ -1574,20 +1130,14 @@ | ||
| 1574 | 1130 | /** |
| 1575 | 1131 | * Save last visited admin ui |
| 1576 | 1132 | * |
| 1577 | 1133 | * @since 3.0.1 |
| 1134 | + * | |
| 1578 | 1135 | */ |
| 1579 | 1136 | public function save_admin_page() { |
| 1580 | - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection. | |
| 1581 | - $post_type = isset( $_GET['post_type'] ) ? sanitize_text_field( wp_unslash( $_GET['post_type'] ) ) : ''; | |
| 1582 | - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection. | |
| 1583 | - $bdocs_view = isset( $_GET['bdocs_view'] ) ? sanitize_text_field( wp_unslash( $_GET['bdocs_view'] ) ) : ''; | |
| 1584 | - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection. | |
| 1585 | - $page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; | |
| 1586 | - | |
| 1587 | - 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 | |
| 1588 | 1138 | update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'classic_ui' ); |
| 1589 | - } elseif ( 'betterdocs-admin' === $page ) { | |
| 1139 | + } elseif ( isset( $_GET['page'] ) && $_GET['page'] === 'betterdocs-admin' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing | |
| 1590 | 1140 | update_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', 'modern_ui' ); |
| 1591 | 1141 | } |
| 1592 | 1142 | } |
| 1593 | 1143 | |
| @@ -1597,20 +1147,18 @@ | ||
| 1597 | 1147 | * @return string |
| 1598 | 1148 | * @since 3.0.1 |
| 1599 | 1149 | */ |
| 1600 | 1150 | public function ui_slug() { |
| 1601 | - $last_visited = get_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', true ); | |
| 1602 | - $docs_exist = get_posts( | |
| 1603 | - array( | |
| 1604 | - 'post_type' => 'docs', | |
| 1605 | - 'post_status' => 'any', | |
| 1606 | - 'numberposts' => 1, | |
| 1607 | - ) | |
| 1608 | - ); | |
| 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 | + ]); | |
| 1609 | 1157 | |
| 1610 | - return ( 'modern_ui' === $last_visited || empty( $docs_exist ) ) | |
| 1611 | - ? 'betterdocs-admin' | |
| 1612 | - : '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'; | |
| 1613 | 1161 | } |
| 1614 | 1162 | |
| 1615 | 1163 | /** |
| 1616 | 1164 | * Resets a duplicate submenu in WordPress if the parent main menu and the first submenu permalink are not the same. |
| @@ -1620,9 +1168,9 @@ | ||
| 1620 | 1168 | */ |
| 1621 | 1169 | public function reset_submenu() { |
| 1622 | 1170 | global $submenu; |
| 1623 | 1171 | |
| 1624 | - $docs = get_posts( array( 'post_type' => 'docs' ) ); | |
| 1172 | + $docs = get_posts( [ 'post_type' => 'docs' ] ); | |
| 1625 | 1173 | if ( count( $docs ) == 0 ) { |
| 1626 | 1174 | return; |
| 1627 | 1175 | } |
| 1628 | 1176 | |
| @@ -1627,89 +1175,10 @@ | ||
| 1627 | 1175 | } |
| 1628 | 1176 | |
| 1629 | 1177 | $last_visited = get_user_meta( get_current_user_id(), 'last_visited_docs_admin_page', true ); |
| 1630 | 1178 | |
| 1631 | - 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] ) ) { | |
| 1632 | 1180 | unset( $submenu['betterdocs-admin'][0] ); |
| 1633 | 1181 | $submenu['betterdocs-admin'] = array_values( $submenu['betterdocs-admin'] ); |
| 1634 | 1182 | } |
| 1635 | - } | |
| 1636 | - | |
| 1637 | - /** | |
| 1638 | - * Initialize Black Friday Pointer | |
| 1639 | - * | |
| 1640 | - * @return void | |
| 1641 | - * @since 3.7.0 | |
| 1642 | - */ | |
| 1643 | - private function init_black_friday_pointer() { | |
| 1644 | - // Only initialize if conditions are met | |
| 1645 | - if ( NoticePointers::should_display_notice() ) { | |
| 1646 | - new NoticePointers(); | |
| 1647 | - } | |
| 1648 | - } | |
| 1649 | - | |
| 1650 | - /** | |
| 1651 | - * AJAX handler for dismissing Black Friday pointer | |
| 1652 | - * | |
| 1653 | - * @return void | |
| 1654 | - * @since 3.7.0 | |
| 1655 | - */ | |
| 1656 | - public function ajax_dismiss_black_friday_pointer() { | |
| 1657 | - // Verify nonce | |
| 1658 | - $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : ''; | |
| 1659 | - if ( ! wp_verify_nonce( $nonce, 'betterdocs_dismiss_pointer' ) ) { | |
| 1660 | - wp_send_json_error( array( 'message' => __( 'Invalid nonce', 'betterdocs' ) ) ); | |
| 1661 | - } | |
| 1662 | - | |
| 1663 | - // Check if user has permission | |
| 1664 | - if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'edit_docs' ) ) { | |
| 1665 | - wp_send_json_error( array( 'message' => __( 'Permission denied', 'betterdocs' ) ) ); | |
| 1666 | - } | |
| 1667 | - | |
| 1668 | - // Get the introduction key | |
| 1669 | - $introduction_key = isset( $_POST['introduction_key'] ) ? sanitize_text_field( wp_unslash( $_POST['introduction_key'] ) ) : ''; | |
| 1670 | - | |
| 1671 | - if ( empty( $introduction_key ) ) { | |
| 1672 | - wp_send_json_error( array( 'message' => __( 'Invalid introduction key', 'betterdocs' ) ) ); | |
| 1673 | - } | |
| 1674 | - | |
| 1675 | - // Set the introduction as viewed | |
| 1676 | - NoticePointers::set_introduction_viewed( $introduction_key ); | |
| 1677 | - | |
| 1678 | - // Clear the priority option so other plugins can set their priority | |
| 1679 | - delete_option( '_wpdeveloper_plugin_pointer_priority' ); | |
| 1680 | - | |
| 1681 | - wp_send_json_success( array( 'message' => __( 'Pointer dismissed successfully', 'betterdocs' ) ) ); | |
| 1682 | - } | |
| 1683 | - | |
| 1684 | - /** | |
| 1685 | - * Get count of docs that are not yet synced | |
| 1686 | - * Similar to Helper::get_not_synced_post_ids() in betterdocs-ai-chatbot plugin | |
| 1687 | - * | |
| 1688 | - * @return int | |
| 1689 | - * @since 3.7.0 | |
| 1690 | - */ | |
| 1691 | - private function get_not_synced_docs_count() { | |
| 1692 | - $new_post_ids = get_option( 'saved_docs_post_ids', array() ); | |
| 1693 | - $error_posts_data = get_option( 'betterdocs_ai_chatbot_error_posts', array() ); | |
| 1694 | - | |
| 1695 | - // Extract post IDs from error posts (handle both old and new formats) | |
| 1696 | - $error_post_ids = array(); | |
| 1697 | - if ( is_array( $error_posts_data ) && ! empty( $error_posts_data ) ) { | |
| 1698 | - foreach ( $error_posts_data as $key => $value ) { | |
| 1699 | - if ( is_numeric( $key ) && is_numeric( $value ) ) { | |
| 1700 | - // Old format: numeric key with post ID as value | |
| 1701 | - $error_post_ids[] = $value; | |
| 1702 | - } elseif ( is_numeric( $key ) && is_array( $value ) && isset( $value['post_id'] ) ) { | |
| 1703 | - // New format: post ID as key with structured data | |
| 1704 | - $error_post_ids[] = $key; | |
| 1705 | - } elseif ( is_numeric( $key ) ) { | |
| 1706 | - // New format: post ID as key | |
| 1707 | - $error_post_ids[] = $key; | |
| 1708 | - } | |
| 1709 | - } | |
| 1710 | - } | |
| 1711 | - | |
| 1712 | - // Merge both arrays and remove duplicates, then count | |
| 1713 | - return count( array_unique( array_merge( $new_post_ids, $error_post_ids ) ) ); | |
| 1714 | 1183 | } |
| 1715 | 1184 | } |