css
22 hours ago
docs
22 hours ago
img
22 hours ago
js
22 hours ago
tabs
22 hours ago
api.php
22 hours ago
cleanup.php
22 hours ago
colors.php
22 hours ago
deprecated.php
22 hours ago
header.php
22 hours ago
helpers.php
22 hours ago
index.php
22 hours ago
menu.php
22 hours ago
plugins-page.php
22 hours ago
post-type-help-doc-imports.php
22 hours ago
post-type-help-docs.php
22 hours ago
shortcodes.php
22 hours ago
taxonomy-folders.php
22 hours ago
post-type-help-docs.php
1223 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Help Docs post type (Manage Tab) |
| 4 | */ |
| 5 | |
| 6 | namespace PluginRx\AdminHelpDocs; |
| 7 | |
| 8 | if ( ! defined( 'ABSPATH' ) ) exit; |
| 9 | |
| 10 | class HelpDocs { |
| 11 | |
| 12 | /** |
| 13 | * Post type slug |
| 14 | * |
| 15 | * @var string |
| 16 | */ |
| 17 | public static $post_type = 'help-docs'; |
| 18 | |
| 19 | |
| 20 | /** |
| 21 | * Get available site locations for displaying help docs |
| 22 | * |
| 23 | * @return array |
| 24 | */ |
| 25 | public static function site_locations() { |
| 26 | $cache_key = 'helpdocs_loc_' . get_current_user_id(); |
| 27 | $cached = get_transient( $cache_key ); |
| 28 | |
| 29 | if ( false !== $cached && is_array( $cached ) ) { |
| 30 | return apply_filters( 'helpdocs_site_locations', $cached ); |
| 31 | } |
| 32 | |
| 33 | $locations = [ |
| 34 | 'main' => [ |
| 35 | 'label' => __( 'Main Documentation Page', 'admin-help-docs' ), |
| 36 | 'fields' => [ 'order', 'toc' ] |
| 37 | ], |
| 38 | 'admin_bar' => [ |
| 39 | 'label' => __( 'Admin Bar Menu (Must Be Enabled in Settings)', 'admin-help-docs' ), |
| 40 | 'fields' => [ 'order', 'admin_bar_tips' ] |
| 41 | ], |
| 42 | 'update-core.php' => [ |
| 43 | 'label' => __( 'Updates Page', 'admin-help-docs' ), |
| 44 | 'fields' => [ 'page_location', 'page_location:element:css_selector' ] |
| 45 | ], |
| 46 | 'replace_dashboard' => [ |
| 47 | 'label' => __( 'WordPress Dashboard (Replaces Dashboard Entirely)', 'admin-help-docs' ), |
| 48 | 'fields' => [ 'dashboard_warning' ] |
| 49 | ], |
| 50 | 'index.php' => [ |
| 51 | 'label' => __( 'WordPress Dashboard Widget', 'admin-help-docs' ), |
| 52 | 'fields' => [] |
| 53 | ], |
| 54 | 'function' => [ |
| 55 | 'label' => __( 'Function: admin_help_doc( id )', 'admin-help-docs' ), |
| 56 | 'fields' => [ 'function_example' ] |
| 57 | ], |
| 58 | 'post.php' => [ |
| 59 | 'label' => __( 'Post/Page Edit Screen', 'admin-help-docs' ), |
| 60 | 'fields' => [ 'page_location', 'post_types', 'page_location:element:css_selector' ] |
| 61 | ], |
| 62 | 'edit.php' => [ |
| 63 | 'label' => __( 'Post/Page Admin List Screen', 'admin-help-docs' ), |
| 64 | 'fields' => [ 'page_location', 'post_types', 'page_location:element:css_selector' ] |
| 65 | ] |
| 66 | ]; |
| 67 | |
| 68 | global $menu, $submenu; |
| 69 | $fetching_all_locations = false; |
| 70 | |
| 71 | if ( ! empty( $menu ) && is_array( $menu ) ) { |
| 72 | $fetching_all_locations = true; |
| 73 | |
| 74 | $site_location_names = apply_filters( 'helpdocs_location_names', [ |
| 75 | 'edit-comments.php' => __( 'Comments', 'admin-help-docs' ), |
| 76 | 'site-editor.php' => __( 'Editor', 'admin-help-docs' ), |
| 77 | 'plugins.php' => __( 'Plugins', 'admin-help-docs' ), |
| 78 | 'dev-debug-tools' => 'Dev Debug Tools', |
| 79 | 'admin.php?page=dev-debug-tools&tool=logs' => __( 'Logs', 'admin-help-docs' ) |
| 80 | ] ); |
| 81 | |
| 82 | $textdomain = Bootstrap::textdomain(); |
| 83 | $admin_url = Bootstrap::admin_url(); |
| 84 | |
| 85 | foreach ( $menu as $m ) { |
| 86 | |
| 87 | // Skip separators |
| 88 | if ( str_starts_with( $m[2], 'separator' ) || $m[2] == 'hp_separator' || ( isset( $m[4] ) && strpos( $m[4], 'wp-menu-separator' ) !== false ) ) { |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | // Skip dashboard and help topics |
| 93 | if ( $m[2] == 'index.php' || $m[2] == $textdomain ) { |
| 94 | continue; |
| 95 | } |
| 96 | |
| 97 | // Skip post types |
| 98 | if ( str_starts_with( $m[2], 'edit.php' ) ) { |
| 99 | continue; |
| 100 | } |
| 101 | |
| 102 | // Change option names |
| 103 | if ( array_key_exists( $m[2], $site_location_names ) ) { |
| 104 | $site_location_name = $site_location_names[ $m[2] ]; |
| 105 | } else { |
| 106 | $site_location_name = $m[0]; |
| 107 | } |
| 108 | |
| 109 | // Strip html |
| 110 | $site_location_name = self::strip_admin_menu_counters( $site_location_name ); |
| 111 | |
| 112 | // Add the parent location |
| 113 | if ( ! array_key_exists( $m[2], $submenu ) ) { |
| 114 | |
| 115 | $url = self::get_admin_menu_item_url( $m[2] ); |
| 116 | if ( is_null( $url ) ) { |
| 117 | $url = 'not found'; |
| 118 | } |
| 119 | |
| 120 | $url = str_replace( $admin_url, '', $url ); |
| 121 | |
| 122 | $locations[ $url ] = [ |
| 123 | 'label' => $site_location_name, |
| 124 | 'fields' => [ 'page_location', 'page_location:element:css_selector' ] |
| 125 | ]; |
| 126 | } |
| 127 | |
| 128 | // Check for sub menu items |
| 129 | foreach ( $submenu as $k => $sub ) { |
| 130 | |
| 131 | if ( $k == $m[2] ) { |
| 132 | foreach ( $sub as $s ) { |
| 133 | |
| 134 | if ( array_key_exists( $s[2], $site_location_names ) ) { |
| 135 | $sublocation_name = $site_location_names[ $s[2] ]; |
| 136 | } else { |
| 137 | $sublocation_name = $s[0]; |
| 138 | } |
| 139 | |
| 140 | $sublocation_name = self::strip_admin_menu_counters( $sublocation_name ); |
| 141 | |
| 142 | $url = self::get_admin_menu_item_url( $s[2] ); |
| 143 | if ( is_null( $url ) ) { |
| 144 | $url = 'not found'; |
| 145 | } |
| 146 | |
| 147 | $url = str_replace( $admin_url, '', $url ); |
| 148 | |
| 149 | $locations[ $url ] = [ |
| 150 | 'label' => $site_location_name . ' > ' . $sublocation_name, |
| 151 | 'fields' => [ 'page_location', 'page_location:element:css_selector' ] |
| 152 | ]; |
| 153 | } |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | // Add a custom link option to the bottom |
| 160 | $locations[ 'custom' ] = [ |
| 161 | 'label' => __( 'Other/Custom Page', 'admin-help-docs' ), |
| 162 | 'fields' => [ 'custom', 'addt_params', 'page_location', 'page_location:element:css_selector' ] |
| 163 | ]; |
| 164 | |
| 165 | // Cache the result for 1 hour |
| 166 | if ( $fetching_all_locations ) { |
| 167 | set_transient( $cache_key, $locations, 12 * HOUR_IN_SECONDS ); |
| 168 | } |
| 169 | |
| 170 | return apply_filters( 'helpdocs_site_locations', $locations ); |
| 171 | } // End site_locations() |
| 172 | |
| 173 | |
| 174 | /** |
| 175 | * Get available page locations for displaying help docs |
| 176 | * |
| 177 | * @return array |
| 178 | */ |
| 179 | public static function page_locations() { |
| 180 | $locations = [ |
| 181 | 'contextual' => __( 'Contextual Help Tab (At Top of Screen)', 'admin-help-docs' ), |
| 182 | 'top' => __( 'Top', 'admin-help-docs' ), |
| 183 | 'bottom' => __( 'Bottom', 'admin-help-docs' ), |
| 184 | 'side' => __( 'Side', 'admin-help-docs' ), |
| 185 | 'element' => __( 'Next to Specific Element (Beta)', 'admin-help-docs' ), |
| 186 | ]; |
| 187 | |
| 188 | return apply_filters( 'helpdocs_page_locations', $locations ); |
| 189 | } // End page_locations() |
| 190 | |
| 191 | |
| 192 | /** |
| 193 | * Get available post types for displaying help docs |
| 194 | * |
| 195 | * @return array |
| 196 | */ |
| 197 | public static function post_types() { |
| 198 | return array_merge( [ 'post' => 'post', 'page' => 'page' ], get_post_types( [ '_builtin' => false ] ) ); |
| 199 | } // End post_types() |
| 200 | |
| 201 | |
| 202 | /** |
| 203 | * The single instance of the class |
| 204 | * |
| 205 | * @var self|null |
| 206 | */ |
| 207 | private static ?HelpDocs $instance = null; |
| 208 | |
| 209 | |
| 210 | /** |
| 211 | * Get the singleton instance |
| 212 | * |
| 213 | * @return self |
| 214 | */ |
| 215 | public static function instance() : self { |
| 216 | return self::$instance ??= new self(); |
| 217 | } // End instance() |
| 218 | |
| 219 | |
| 220 | /** |
| 221 | * HelpDocs constructor. |
| 222 | * |
| 223 | * Private to enforce singleton pattern. |
| 224 | */ |
| 225 | private function __construct() { |
| 226 | |
| 227 | // Register the post type |
| 228 | add_action( 'init', [ $this, 'register_post_type' ] ); |
| 229 | |
| 230 | // Add the header to the top of the admin list page |
| 231 | add_action( 'load-edit.php', [ $this, 'add_header' ] ); |
| 232 | |
| 233 | // Move the search box to the subheader |
| 234 | add_action( 'helpdocs_subheader_right', [ $this, 'render_search_box' ] ); |
| 235 | |
| 236 | // Add the meta box |
| 237 | add_action( 'add_meta_boxes', [ $this, 'meta_boxes' ], 999 ); |
| 238 | |
| 239 | // Rename excerpt meta box |
| 240 | add_filter( 'gettext', [ $this, 'excerpt_meta_box' ], 10, 2 ); |
| 241 | |
| 242 | // Save the post data |
| 243 | add_action( 'save_post', [ $this, 'save_post' ] ); |
| 244 | add_action( 'before_delete_post', [ $this, 'delete_post' ] ); |
| 245 | |
| 246 | // Add admin columns |
| 247 | add_filter( 'manage_' . self::$post_type . '_posts_columns', [ $this, 'admin_columns' ] ); |
| 248 | add_action( 'manage_' . self::$post_type . '_posts_custom_column', [ $this, 'admin_column_content' ], 10, 2 ); |
| 249 | |
| 250 | // Either enqueue block editor styles or disable block editor |
| 251 | if ( ! get_option( 'helpdocs_gutenberg_editor' ) ) { |
| 252 | add_filter( 'use_block_editor_for_post_type', [ $this, 'disable_gutenberg' ], 10, 2 ); |
| 253 | } |
| 254 | |
| 255 | // Enqueue back-end styles |
| 256 | add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_styles' ] ); |
| 257 | |
| 258 | } // End __construct() |
| 259 | |
| 260 | |
| 261 | /** |
| 262 | * Register the post type |
| 263 | */ |
| 264 | public function register_post_type() { |
| 265 | // Set the labels |
| 266 | $labels = [ |
| 267 | 'name' => _x( 'Help Documents', 'Post Type General Name', 'admin-help-docs' ), |
| 268 | 'singular_name' => _x( 'Help Document', 'Post Type Singular Name', 'admin-help-docs' ), |
| 269 | 'menu_name' => __( 'Help Documents', 'admin-help-docs' ), |
| 270 | 'name_admin_bar' => __( 'Help Documents', 'admin-help-docs' ), |
| 271 | 'archives' => __( 'Help Document Archives', 'admin-help-docs' ), |
| 272 | 'attributes' => __( 'Help Document Attributes', 'admin-help-docs' ), |
| 273 | 'parent_item_colon' => __( 'Parent Help Document:', 'admin-help-docs' ), |
| 274 | 'all_items' => __( 'All Help Documents', 'admin-help-docs' ), |
| 275 | 'add_new_item' => __( 'Add New Help Document', 'admin-help-docs' ), |
| 276 | 'add_new' => __( 'Add New', 'admin-help-docs' ), |
| 277 | 'new_item' => __( 'New Help Document', 'admin-help-docs' ), |
| 278 | 'edit_item' => __( 'Edit Help Document', 'admin-help-docs' ), |
| 279 | 'update_item' => __( 'Update Help Document', 'admin-help-docs' ), |
| 280 | 'view_item' => __( 'View Help Document', 'admin-help-docs' ), |
| 281 | 'view_items' => __( 'View Help Documents', 'admin-help-docs' ), |
| 282 | 'search_items' => __( 'Search Help Documents', 'admin-help-docs' ), |
| 283 | 'not_found' => __( 'Not found', 'admin-help-docs' ), |
| 284 | 'not_found_in_trash' => __( 'Not found in Trash', 'admin-help-docs' ), |
| 285 | 'insert_into_item' => __( 'Insert into help document', 'admin-help-docs' ), |
| 286 | 'uploaded_to_this_item' => __( 'Uploaded to this help document', 'admin-help-docs' ), |
| 287 | 'items_list' => __( 'Help Document list', 'admin-help-docs' ), |
| 288 | 'items_list_navigation' => __( 'Help Document list navigation', 'admin-help-docs' ), |
| 289 | 'filter_items_list' => __( 'Filter help document list', 'admin-help-docs' ), |
| 290 | ]; |
| 291 | |
| 292 | // Allow filter for supports and taxonomies |
| 293 | $supports = apply_filters( 'helpdocs_post_type_supports', [ 'title', 'editor' ] ); |
| 294 | $taxonomies = apply_filters( 'helpdocs_post_type_taxonomies', [] ); |
| 295 | |
| 296 | // Set the CPT args |
| 297 | $args = [ |
| 298 | 'label' => __( 'Help Documents', 'admin-help-docs' ), |
| 299 | 'description' => __( 'Help Documents', 'admin-help-docs' ), |
| 300 | 'labels' => $labels, |
| 301 | 'supports' => $supports, |
| 302 | 'taxonomies' => $taxonomies, |
| 303 | 'public' => true, |
| 304 | 'show_ui' => true, |
| 305 | 'show_in_menu' => false, |
| 306 | 'show_in_admin_bar' => false, |
| 307 | 'show_in_nav_menus' => false, |
| 308 | 'can_export' => true, |
| 309 | 'has_archive' => false, |
| 310 | 'exclude_from_search' => true, |
| 311 | 'publicly_queryable' => false, |
| 312 | 'query_var' => self::$post_type, |
| 313 | 'capability_type' => 'post', |
| 314 | 'show_in_rest' => true, |
| 315 | ]; |
| 316 | |
| 317 | // Register the CPT |
| 318 | register_post_type( self::$post_type, $args ); |
| 319 | } // End register_post_type() |
| 320 | |
| 321 | |
| 322 | /** |
| 323 | * Add the header to the top of the admin list page |
| 324 | * |
| 325 | * @return void |
| 326 | */ |
| 327 | public function add_header() { |
| 328 | $screen = get_current_screen(); |
| 329 | if ( 'edit-' . self::$post_type === $screen->id ) { |
| 330 | add_action( 'in_admin_header', function() { |
| 331 | include Bootstrap::path( 'inc/header.php' ); |
| 332 | } ); |
| 333 | } |
| 334 | } // End add_header() |
| 335 | |
| 336 | |
| 337 | /** |
| 338 | * Add a search box to the subheader on the folders page |
| 339 | * |
| 340 | * @param string $current_tab The current admin tab |
| 341 | * @return void |
| 342 | */ |
| 343 | public function render_search_box( string $current_tab ) { |
| 344 | if ( $current_tab !== 'manage' ) { |
| 345 | return; |
| 346 | } |
| 347 | |
| 348 | $search_value = sanitize_text_field( wp_unslash( $_GET[ 's' ] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 349 | ?> |
| 350 | <form method="get" class="helpdocs-posttype-search"> |
| 351 | <?php foreach ( $_GET as $key => $value ) : // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 352 | if ( in_array( $key, [ 's', 'action', 'paged' ], true ) ) continue; |
| 353 | ?> |
| 354 | <input type="hidden" name="<?php echo esc_attr( $key ); ?>" value="<?php echo esc_attr( $value ); ?>" /> |
| 355 | <?php endforeach; ?> |
| 356 | |
| 357 | <input type="search" |
| 358 | name="s" |
| 359 | value="<?php echo esc_attr( $search_value ); ?>" |
| 360 | placeholder="<?php echo esc_attr__( 'Search Docs', 'admin-help-docs' ); ?>" |
| 361 | class="helpdocs-search-input" /> |
| 362 | |
| 363 | <input type="submit" |
| 364 | class="helpdocs-button" |
| 365 | value="<?php echo esc_attr__( 'Search', 'admin-help-docs' ); ?>" /> |
| 366 | |
| 367 | <a href="<?php |
| 368 | $clear_url = remove_query_arg( 's' ); |
| 369 | echo esc_url( $clear_url ); |
| 370 | ?>" class="helpdocs-button"> |
| 371 | <?php echo esc_html__( 'Clear', 'admin-help-docs' ); ?> |
| 372 | </a> |
| 373 | </form> |
| 374 | <?php |
| 375 | } // End render_search_box() |
| 376 | |
| 377 | |
| 378 | /** |
| 379 | * Meta box |
| 380 | * |
| 381 | * @return void |
| 382 | */ |
| 383 | public function meta_boxes() { |
| 384 | global $wp_meta_boxes; |
| 385 | |
| 386 | $screen = get_current_screen(); |
| 387 | if ( $screen && $screen->post_type === self::$post_type ) { |
| 388 | |
| 389 | // Define the IDs we want to KEEP |
| 390 | $whitelist = [ |
| 391 | 'submitdiv', // The Publish box |
| 392 | 'formatdiv', // Post Formats |
| 393 | 'categorydiv', // Standard Category box |
| 394 | 'tagsdiv-post_tag', // Standard Tags box |
| 395 | 'pageparentdiv', // Page Attributes |
| 396 | 'postimagediv', // Featured Image |
| 397 | 'tagsdiv-' . Folders::$taxonomy // Folders taxonomy box |
| 398 | ]; |
| 399 | |
| 400 | // Loop through all contexts (normal, advanced, side) |
| 401 | foreach ( [ 'normal', 'advanced', 'side' ] as $context ) { |
| 402 | foreach ( [ 'high', 'core', 'default', 'low' ] as $priority ) { |
| 403 | |
| 404 | if ( ! isset( $wp_meta_boxes[ self::$post_type ][ $context ][ $priority ] ) ) { |
| 405 | continue; |
| 406 | } |
| 407 | |
| 408 | foreach ( $wp_meta_boxes[ self::$post_type ][ $context ][ $priority ] as $box_id => $box_data ) { |
| 409 | // If the box ID is NOT in our whitelist, remove it |
| 410 | if ( ! in_array( $box_id, $whitelist ) ) { |
| 411 | remove_meta_box( $box_id, self::$post_type, $context ); |
| 412 | } |
| 413 | } |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | // 2. Now that the deck is clear, add ONLY your meta box |
| 418 | add_meta_box( |
| 419 | 'helpdocs_locations', |
| 420 | __( 'Location', 'admin-help-docs' ), |
| 421 | [ $this, 'meta_box_content' ], |
| 422 | self::$post_type, |
| 423 | 'advanced', |
| 424 | 'high' |
| 425 | ); |
| 426 | } |
| 427 | } // End meta_boxes() |
| 428 | |
| 429 | |
| 430 | /** |
| 431 | * Rename excerpt meta box as description |
| 432 | * |
| 433 | * @param string $translation |
| 434 | * @param string $original |
| 435 | * @return string |
| 436 | */ |
| 437 | public function excerpt_meta_box( $translation, $original ) { |
| 438 | // Make sure we are only looking at our post type |
| 439 | global $post_type; |
| 440 | if ( $post_type != self::$post_type ) { |
| 441 | return $translation; |
| 442 | } |
| 443 | |
| 444 | // Update the box |
| 445 | if ( 'Excerpt' == $original ) { |
| 446 | return __( 'Description', 'admin-help-docs' ); |
| 447 | } elseif ( false !== strpos( $original, 'Excerpts are optional hand-crafted summaries of your' ) ) { |
| 448 | return __( 'This is only used to summarize the document for the manage documents list table. If left blank, it will show first couple of lines of the content.', 'admin-help-docs' ); |
| 449 | } |
| 450 | return $translation; |
| 451 | } // End excerpt_meta_box() |
| 452 | |
| 453 | |
| 454 | /** |
| 455 | * Meta box content |
| 456 | * |
| 457 | * @param object $post |
| 458 | * @return void |
| 459 | */ |
| 460 | public function meta_box_content( $post ) { |
| 461 | wp_nonce_field( 'help_location_nonce', 'help_location_nonce' ); |
| 462 | |
| 463 | $locations = get_post_meta( $post->ID, 'helpdocs_locations', true ); |
| 464 | |
| 465 | if ( 'auto-draft' === $post->post_status && isset( $_GET[ 'site_location' ] ) ) { // phpcs:ignore |
| 466 | $locations = [ |
| 467 | [ |
| 468 | 'site_location' => sanitize_text_field( wp_unslash( $_GET[ 'site_location' ] ) ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 469 | 'page_location' => 'top', // Default to top |
| 470 | 'custom' => isset( $_GET[ 'custom_url' ] ) ? esc_url_raw( wp_unslash( $_GET[ 'custom_url' ] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 471 | 'post_types' => isset( $_GET[ 'slpt' ] ) ? [ sanitize_text_field( wp_unslash( $_GET[ 'slpt' ] ) ) ] : [], // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 472 | 'order' => 0, |
| 473 | 'toc' => false, |
| 474 | 'css_selector' => '', |
| 475 | 'addt_params' => true, |
| 476 | ] |
| 477 | ]; |
| 478 | } |
| 479 | |
| 480 | if ( ! is_array( $locations ) || empty( $locations ) ) { |
| 481 | // Backward compatibility: use legacy single-location fields |
| 482 | $meta = get_post_meta( $post->ID ); |
| 483 | |
| 484 | $locations = [ |
| 485 | [ |
| 486 | 'site_location' => $meta[ 'helpdocs_site_location' ][ 0 ] ?? '', |
| 487 | 'page_location' => $meta[ 'helpdocs_page_location' ][ 0 ] ?? '', |
| 488 | 'custom' => $meta[ 'helpdocs_custom' ][ 0 ] ?? '', |
| 489 | 'addt_params' => ! empty( $meta[ 'helpdocs_addt_params' ][ 0 ] ), |
| 490 | 'post_types' => isset( $meta[ 'helpdocs_post_types' ][ 0 ] ) ? Helpers::normalize_meta_array( $meta[ 'helpdocs_post_types' ][ 0 ] ) : [], |
| 491 | 'order' => isset( $meta[ 'helpdocs_order' ][ 0 ] ) ? intval( $meta[ 'helpdocs_order' ][ 0 ] ) : 0, |
| 492 | 'toc' => isset( $meta[ 'helpdocs_toc' ][ 0 ] ) ? filter_var( $meta[ 'helpdocs_toc' ][ 0 ], FILTER_VALIDATE_BOOLEAN ) : false, |
| 493 | 'css_selector' => $meta[ 'helpdocs_css_selector' ][ 0 ] ?? '', |
| 494 | ] |
| 495 | ]; |
| 496 | } |
| 497 | |
| 498 | // Global fields |
| 499 | $meta = get_post_meta( $post->ID ); |
| 500 | $api = $meta[ 'helpdocs_api' ][ 0 ] ?? ''; |
| 501 | $view_roles = isset( $meta[ 'helpdocs_view_roles' ][ 0 ] ) ? Helpers::normalize_meta_array( $meta[ 'helpdocs_view_roles' ][ 0 ] ) : []; |
| 502 | $roles = Helpers::get_role_options(); |
| 503 | |
| 504 | $all_site_locations = self::site_locations(); |
| 505 | $all_page_locations = self::page_locations(); |
| 506 | $all_post_types = self::post_types(); |
| 507 | ?> |
| 508 | <!-- Repeater --> |
| 509 | <div id="helpdocs-location-repeater"> |
| 510 | <?php foreach ( $locations as $i => $location ) : |
| 511 | $site_location = $location[ 'site_location' ] ?? ''; |
| 512 | $site_location_decoded = base64_decode( $site_location ); |
| 513 | $page_location = $location[ 'page_location' ] ?? ''; |
| 514 | $custom = $location[ 'custom' ] ?? ''; |
| 515 | $addt_params = ! empty( $location[ 'addt_params' ] ); |
| 516 | $post_types = $location[ 'post_types' ] ?? []; |
| 517 | $order = $location[ 'order' ] ?? 0; |
| 518 | $toc = ! empty( $location[ 'toc' ] ); |
| 519 | $css_selector = $location[ 'css_selector' ] ?? ''; |
| 520 | |
| 521 | // Determine which fields to show |
| 522 | $fields_to_show = []; |
| 523 | if ( isset( $all_site_locations[ $site_location_decoded ][ 'fields' ] ) ) { |
| 524 | foreach ( $all_site_locations[ $site_location_decoded ][ 'fields' ] as $f ) { |
| 525 | if ( strpos( $f, ':' ) !== false ) { |
| 526 | list( $controller, $controller_value, $dependent ) = explode( ':', $f ); |
| 527 | if ( ( $location[ $controller ] ?? '' ) === $controller_value ) { |
| 528 | $fields_to_show[] = $dependent; |
| 529 | } |
| 530 | } else { |
| 531 | $fields_to_show[] = $f; |
| 532 | } |
| 533 | } |
| 534 | } |
| 535 | ?> |
| 536 | <div class="helpdocs-location-row" style="margin-bottom:15px; padding:10px; border:1px solid #ddd;"> |
| 537 | <div class="helpdocs-row-main"> |
| 538 | |
| 539 | <!-- Site Location --> |
| 540 | <?php $id_site = 'helpdocs_locations_' . $i . '_site_location'; ?> |
| 541 | <label for="<?php echo esc_attr( $id_site ); ?>"><?php esc_html_e( 'Site Location:', 'admin-help-docs' ); ?></label> |
| 542 | <select name="helpdocs_locations[ <?php echo esc_attr( $i ); ?> ][site_location]" id="<?php echo esc_attr( $id_site ); ?>"> |
| 543 | <option value=""><?php esc_html_e( '-- None --', 'admin-help-docs' ); ?></option> |
| 544 | <?php foreach ( $all_site_locations as $key_l => $info ) : |
| 545 | $encoded = base64_encode( $key_l ); |
| 546 | ?> |
| 547 | <option value="<?php echo esc_attr( $encoded ); ?>" <?php selected( $encoded, $site_location ); ?>> |
| 548 | <?php echo esc_html( $info[ 'label' ] ); ?> |
| 549 | </option> |
| 550 | <?php endforeach; ?> |
| 551 | </select> |
| 552 | |
| 553 | <?php if ( $i > 0 ) : ?> |
| 554 | <button type="button" class="remove-location button" title="Remove Location"><span class="dashicons dashicons-trash"></span></button> |
| 555 | <?php endif; ?> |
| 556 | |
| 557 | <!-- Page Location --> |
| 558 | <?php $id_page = 'helpdocs_locations_' . $i . '_page_location'; ?> |
| 559 | <label for="<?php echo esc_attr( $id_page ); ?>" style="display:<?php echo in_array( 'page_location', $fields_to_show ) ? 'block' : 'none'; ?>;"> |
| 560 | <?php esc_html_e( 'Page Location:', 'admin-help-docs' ); ?> |
| 561 | </label> |
| 562 | <select name="helpdocs_locations[ <?php echo esc_attr( $i ); ?> ][page_location]" id="<?php echo esc_attr( $id_page ); ?>" style="display:<?php echo in_array( 'page_location', $fields_to_show ) ? 'block' : 'none'; ?>;"> |
| 563 | <?php foreach ( $all_page_locations as $key_lop => $lop ) : ?> |
| 564 | <option value="<?php echo esc_attr( $key_lop ); ?>" <?php selected( $key_lop, $page_location ); ?>> |
| 565 | <?php echo esc_html( $lop ); ?> |
| 566 | </option> |
| 567 | <?php endforeach; ?> |
| 568 | </select> |
| 569 | |
| 570 | <!-- Element CSS Selector --> |
| 571 | <?php $id_css_selector = 'helpdocs_locations_' . $i . '_css_selector'; ?> |
| 572 | <label for="<?php echo esc_attr( $id_css_selector ); ?>" style="display:<?php echo in_array( 'css_selector', $fields_to_show ) ? 'block' : 'none'; ?>;"> |
| 573 | <?php esc_html_e( 'Element CSS Selector:', 'admin-help-docs' ); ?> |
| 574 | </label> |
| 575 | <input type="text" name="helpdocs_locations[ <?php echo esc_attr( $i ); ?> ][css_selector]" id="<?php echo esc_attr( $id_css_selector ); ?>" value="<?php echo esc_attr( $css_selector ); ?>" style="display:<?php echo in_array( 'css_selector', $fields_to_show ) ? 'inline-block' : 'none'; ?>;"> |
| 576 | |
| 577 | <!-- Order --> |
| 578 | <?php $id_order = 'helpdocs_locations_' . $i . '_order'; ?> |
| 579 | <label for="<?php echo esc_attr( $id_order ); ?>" style="display:<?php echo in_array( 'order', $fields_to_show ) ? 'block' : 'none'; ?>;"> |
| 580 | <?php esc_html_e( 'Menu Order:', 'admin-help-docs' ); ?> |
| 581 | </label> |
| 582 | <input type="number" name="helpdocs_locations[ <?php echo esc_attr( $i ); ?> ][order]" id="<?php echo esc_attr( $id_order ); ?>" value="<?php echo esc_attr( $order ); ?>" style="display:<?php echo in_array( 'order', $fields_to_show ) ? 'inline-block' : 'none'; ?>;"> |
| 583 | |
| 584 | <!-- TOC --> |
| 585 | <?php $id_toc = 'helpdocs_locations_' . $i . '_toc'; ?> |
| 586 | <label style="display:<?php echo in_array( 'toc', $fields_to_show ) ? 'block' : 'none'; ?>;"> |
| 587 | <input type="checkbox" name="helpdocs_locations[ <?php echo esc_attr( $i ); ?> ][toc]" id="<?php echo esc_attr( $id_toc ); ?>" value="1" <?php checked( $toc ); ?>> |
| 588 | <?php esc_html_e( 'Add to Dashboard Table of Contents', 'admin-help-docs' ); ?> |
| 589 | </label> |
| 590 | |
| 591 | </div> |
| 592 | |
| 593 | <!-- Function Example --> |
| 594 | <?php $id_function = 'helpdocs_locations_' . $i . '_function_example'; ?> |
| 595 | <span class="helpdocs-function-example info" id="<?php echo esc_attr( $id_function ); ?>" style="display:<?php echo in_array( 'function_example', $fields_to_show ) ? 'block' : 'none'; ?>;"> |
| 596 | <?php esc_html_e( 'Developers can use this function to display a specific help document by its ID:', 'admin-help-docs' ); ?> <code>admin_help_doc(<?php echo esc_attr( $post->ID ); ?>);</code> |
| 597 | </span> |
| 598 | |
| 599 | <!-- Admin Bar Tips --> |
| 600 | <?php $id_function = 'helpdocs_locations_' . $i . '_admin_bar_tips'; ?> |
| 601 | <span class="helpdocs-admin-bar-tips info" id="<?php echo esc_attr( $id_function ); ?>" style="display:<?php echo in_array( 'admin_bar_tips', $fields_to_show ) ? 'block' : 'none'; ?>;"> |
| 602 | <?php esc_html_e( ' To add a quick link to the admin bar, set the title of this help doc to the label and add the link by itself to the content.', 'admin-help-docs' ); ?> |
| 603 | </span> |
| 604 | |
| 605 | <!-- Dashboard Warning --> |
| 606 | <?php $id_dashboard_warning = 'helpdocs_locations_' . $i . '_dashboard_warning'; ?> |
| 607 | <span class="helpdocs-dashboard-warning warning" id="<?php echo esc_attr( $id_dashboard_warning ); ?>" style="display:<?php echo in_array( 'dashboard_warning', $fields_to_show ) ? 'block' : 'none'; ?>;"> |
| 608 | <?php esc_html_e( 'You must enable "Replace WordPress Dashboard with a Help Doc" in Settings for this to show up. Warning: Replacing the WordPress dashboard entirely may affect other plugins and functionality. No other widgets or dashboard elements will be displayed.', 'admin-help-docs' ); ?> |
| 609 | </span> |
| 610 | |
| 611 | <!-- Custom URL --> |
| 612 | <div class="helpdocs-custom-url" style="display:<?php echo in_array( 'custom', $fields_to_show ) ? 'block' : 'none'; ?>;"> |
| 613 | <?php $id_custom = 'helpdocs_locations_' . $i . '_custom'; ?> |
| 614 | <label class="helpdocs-custom-label" for="<?php echo esc_attr( $id_custom ); ?>"> |
| 615 | <?php esc_html_e( 'Custom URL:', 'admin-help-docs' ); ?> |
| 616 | </label> |
| 617 | <input class="helpdocs-custom" type="text" name="helpdocs_locations[ <?php echo esc_attr( $i ); ?> ][custom]" id="<?php echo esc_attr( $id_custom ); ?>" value="<?php echo esc_attr( $custom ); ?>" style="width:60%; display:<?php echo in_array( 'custom', $fields_to_show ) ? 'inline-block' : 'none'; ?>;"> |
| 618 | |
| 619 | <!-- Ignore Params --> |
| 620 | <?php $id_addt = 'helpdocs_locations_' . $i . '_addt_params'; ?> |
| 621 | <label style="display:<?php echo in_array( 'addt_params', $fields_to_show ) ? 'block' : 'none'; ?>;"> |
| 622 | <input type="checkbox" name="helpdocs_locations[ <?php echo esc_attr( $i ); ?> ][addt_params]" id="<?php echo esc_attr( $id_addt ); ?>" value="1" <?php checked( $addt_params ); ?>> |
| 623 | <?php esc_html_e( 'Ignore Additional Query String Parameters', 'admin-help-docs' ); ?> |
| 624 | </label> |
| 625 | </div> |
| 626 | |
| 627 | <!-- Post Types --> |
| 628 | <?php $id_post_types = 'helpdocs_locations_' . $i . '_post_types'; ?> |
| 629 | <div id="<?php echo esc_attr( $id_post_types ); ?>" class="helpdocs-post-types" style="display:<?php echo in_array( 'post_types', $fields_to_show ) ? 'block' : 'none'; ?>;"> |
| 630 | <div class="helpdocs-checkboxes"> |
| 631 | <?php foreach ( $all_post_types as $pt ) : |
| 632 | $pt_obj = get_post_type_object( $pt ); |
| 633 | if ( ! $pt_obj || ! $pt_obj->show_ui ) continue; |
| 634 | ?> |
| 635 | <label> |
| 636 | <input type="checkbox" name="helpdocs_locations[ <?php echo esc_attr( $i ); ?> ][post_types][]" value="<?php echo esc_attr( $pt ); ?>" <?php checked( in_array( $pt, $post_types ) ); ?>> |
| 637 | <?php echo esc_html( $pt_obj->labels->name ); ?> |
| 638 | </label> |
| 639 | <?php endforeach; ?> |
| 640 | </div> |
| 641 | </div> |
| 642 | |
| 643 | </div> |
| 644 | <?php endforeach; ?> |
| 645 | </div> |
| 646 | |
| 647 | <button type="button" id="add-location" class="button button-primary"><?php esc_html_e( 'Add Location', 'admin-help-docs' ); ?></button> |
| 648 | |
| 649 | <!-- ----------------------------- |
| 650 | Global Fields |
| 651 | ----------------------------- --> |
| 652 | |
| 653 | <div class="helpdocs-global-settings"> |
| 654 | |
| 655 | <!-- API --> |
| 656 | <div class="helpdocs-api"> |
| 657 | <?php $id_api = 'helpdocs_api'; ?> |
| 658 | <label for="<?php echo esc_attr( $id_api ); ?>"><?php esc_html_e( 'Allow Public:', 'admin-help-docs' ); ?></label> |
| 659 | <select name="helpdocs_api" id="<?php echo esc_attr( $id_api ); ?>"> |
| 660 | <?php |
| 661 | $get_default_api = get_option( 'helpdocs_api' ); |
| 662 | if ( $get_default_api === 'yes' || $get_default_api === true ) { |
| 663 | $default_api_choice = 'yes'; |
| 664 | } else { |
| 665 | $default_api_choice = 'no'; |
| 666 | } |
| 667 | $api_choices = [ |
| 668 | 'default' => sprintf( __( 'Default ( %s )', 'admin-help-docs' ), ucwords( $default_api_choice ) ), |
| 669 | 'no' => __( 'No', 'admin-help-docs' ), |
| 670 | 'yes' => __( 'Yes', 'admin-help-docs' ) |
| 671 | ]; |
| 672 | foreach ( $api_choices as $key_api => $a ) : |
| 673 | ?> |
| 674 | <option value="<?php echo esc_attr( $key_api ); ?>" <?php selected( $key_api, $api ? $api : 'default' ); ?>> |
| 675 | <?php echo esc_html( $a ); ?> |
| 676 | </option> |
| 677 | <?php endforeach; ?> |
| 678 | </select> |
| 679 | </div> |
| 680 | |
| 681 | <!-- View Roles --> |
| 682 | <div class="helpdocs-view-roles"> |
| 683 | <label><?php esc_html_e( 'Viewable By Roles:', 'admin-help-docs' ); ?></label> |
| 684 | <div class="helpdocs-checkboxes"> |
| 685 | <?php foreach ( $roles as $role ) : ?> |
| 686 | <label> |
| 687 | <input type="checkbox" name="helpdocs_view_roles[]" value="<?php echo esc_attr( $role[ 'value' ] ); ?>" <?php checked( in_array( $role[ 'value' ], $view_roles ) ); ?>> |
| 688 | <?php echo esc_html( $role[ 'label' ] ); ?> |
| 689 | </label> |
| 690 | <?php endforeach; ?> |
| 691 | </div> |
| 692 | </div> |
| 693 | |
| 694 | </div> |
| 695 | <?php |
| 696 | } // End meta_box_content() |
| 697 | |
| 698 | |
| 699 | /** |
| 700 | * Get menu item url |
| 701 | * Snippet courtesy of Mighty Minnow |
| 702 | * https://www.mightyminnow.com/2013/12/how-to-get-urls-for-wordpress-admin-menu-items/ |
| 703 | * |
| 704 | * @param string $menu_item_file |
| 705 | * @param bool $submenu_as_parent |
| 706 | * @return string |
| 707 | */ |
| 708 | public static function get_admin_menu_item_url( $menu_item_file, $submenu_as_parent = true ) { |
| 709 | global $menu, $submenu, $self, $typenow; |
| 710 | |
| 711 | $admin_is_parent = false; |
| 712 | $item = ''; |
| 713 | $submenu_item = ''; |
| 714 | $url = ''; |
| 715 | |
| 716 | // 1. Check if top-level menu item |
| 717 | foreach( $menu as $key => $menu_item ) { |
| 718 | if ( array_keys( $menu_item, $menu_item_file, true ) ) { |
| 719 | $item = $menu[ $key ]; |
| 720 | } |
| 721 | |
| 722 | if ( $submenu_as_parent && ! empty( $submenu_item ) ) { |
| 723 | $menu_hook = get_plugin_page_hook( $submenu_item[2], $item[2] ); |
| 724 | $menu_file = $submenu_item[2]; |
| 725 | |
| 726 | if ( false !== ( $pos = strpos( $menu_file, '?' ) ) ) |
| 727 | $menu_file = substr( $menu_file, 0, $pos ); |
| 728 | if ( ! empty( $menu_hook ) || ( ( 'index.php' != $submenu_item[2] ) && file_exists( WP_PLUGIN_DIR . "/$menu_file" ) && ! file_exists( ABSPATH . "/wp-admin/$menu_file" ) ) ) { |
| 729 | $admin_is_parent = true; |
| 730 | $url = 'admin.php?page=' . $submenu_item[2]; |
| 731 | } else { |
| 732 | $url = $submenu_item[2]; |
| 733 | } |
| 734 | } |
| 735 | |
| 736 | elseif ( ! empty( $item[2] ) && current_user_can( $item[1] ) ) { |
| 737 | $menu_hook = get_plugin_page_hook( $item[2], 'admin.php' ); |
| 738 | $menu_file = $item[2]; |
| 739 | |
| 740 | if ( false !== ( $pos = strpos( $menu_file, '?' ) ) ) |
| 741 | $menu_file = substr( $menu_file, 0, $pos ); |
| 742 | if ( ! empty( $menu_hook ) || ( ( 'index.php' != $item[2] ) && file_exists( WP_PLUGIN_DIR . "/$menu_file" ) && ! file_exists( ABSPATH . "/wp-admin/$menu_file" ) ) ) { |
| 743 | $admin_is_parent = true; |
| 744 | $url = 'admin.php?page=' . $item[2]; |
| 745 | } else { |
| 746 | $url = $item[2]; |
| 747 | } |
| 748 | } |
| 749 | } |
| 750 | |
| 751 | // 2. Check if sub-level menu item |
| 752 | if ( ! $item ) { |
| 753 | $sub_item = ''; |
| 754 | foreach( $submenu as $top_file => $submenu_items ) { |
| 755 | |
| 756 | $submenu_items = array_values( $submenu_items ); |
| 757 | foreach( $submenu_items as $key => $submenu_item ) { |
| 758 | if ( array_keys( $submenu_item, $menu_item_file ) ) { |
| 759 | $sub_item = $submenu_items[ $key ]; |
| 760 | break; |
| 761 | } |
| 762 | } |
| 763 | |
| 764 | if ( ! empty( $sub_item ) ) { |
| 765 | break; |
| 766 | } |
| 767 | } |
| 768 | |
| 769 | foreach( $menu as $key => $menu_item ) { |
| 770 | if ( array_keys( $menu_item, $top_file, true ) ) { |
| 771 | $item = $menu[ $key ]; |
| 772 | break; |
| 773 | } |
| 774 | } |
| 775 | |
| 776 | if ( ! $sub_item ) { |
| 777 | return false; |
| 778 | } |
| 779 | |
| 780 | if ( is_array( $sub_item ) && isset( $sub_item[2] ) && is_array( $item ) && isset( $item[2] ) ) { |
| 781 | |
| 782 | $menu_file = $item[2]; |
| 783 | if ( false !== ( $pos = strpos( $menu_file, '?' ) ) ) { |
| 784 | $menu_file = substr( $menu_file, 0, $pos ); |
| 785 | } |
| 786 | |
| 787 | $menu_hook = get_plugin_page_hook( $sub_item[2], $item[2] ); |
| 788 | |
| 789 | $sub_file = $sub_item[2]; |
| 790 | if ( false !== ( $pos = strpos( $sub_file, '?' ) ) ) { |
| 791 | $sub_file = substr( $sub_file, 0, $pos ); |
| 792 | } |
| 793 | |
| 794 | if ( ! empty( $menu_hook ) || ( ( 'index.php' != $sub_item[2] ) && file_exists( WP_PLUGIN_DIR . "/$sub_file" ) && ! file_exists( ABSPATH . "/wp-admin/$sub_file" ) ) ) { |
| 795 | if ( ( ! $admin_is_parent && file_exists( WP_PLUGIN_DIR . "/$menu_file" ) && ! is_dir( WP_PLUGIN_DIR . "/{$item[2]}" ) ) || file_exists( $menu_file ) ) |
| 796 | $url = add_query_arg( array( 'page' => $sub_item[2] ), $item[2] ); |
| 797 | else |
| 798 | $url = add_query_arg( array( 'page' => $sub_item[2] ), 'admin.php' ); |
| 799 | } else { |
| 800 | $url = $sub_item[2]; |
| 801 | } |
| 802 | |
| 803 | } else { |
| 804 | return false; |
| 805 | } |
| 806 | } |
| 807 | |
| 808 | return esc_url( $url ); |
| 809 | } // End get_admin_menu_item_url() |
| 810 | |
| 811 | |
| 812 | /** |
| 813 | * Save the post data |
| 814 | * |
| 815 | * @param int $post_id |
| 816 | * @return void |
| 817 | */ |
| 818 | public function save_post( $post_id ) { |
| 819 | // Check nonce and permissions |
| 820 | if ( ! Helpers::can_save_post( self::$post_type, 'help_location_nonce', 'help_location_nonce' ) ) { |
| 821 | return; |
| 822 | } |
| 823 | |
| 824 | $all_site_locations = self::site_locations(); |
| 825 | $locations_input = isset( $_POST[ 'helpdocs_locations' ] ) ? wp_unslash( $_POST[ 'helpdocs_locations' ] ) : []; // phpcs:ignore |
| 826 | $locations = []; |
| 827 | |
| 828 | if ( is_array( $locations_input ) ) { |
| 829 | foreach ( $locations_input as $loc ) { |
| 830 | if ( empty( $loc[ 'site_location' ] ) ) { |
| 831 | continue; |
| 832 | } |
| 833 | |
| 834 | $site_key_encoded = sanitize_text_field( $loc[ 'site_location' ] ); |
| 835 | $site_key_decoded = base64_decode( $site_key_encoded ); |
| 836 | |
| 837 | if ( ! isset( $all_site_locations[ $site_key_decoded ] ) ) { |
| 838 | continue; |
| 839 | } |
| 840 | |
| 841 | $clean = [ 'site_location' => $site_key_encoded ]; |
| 842 | $allowed_fields = $all_site_locations[ $site_key_decoded ][ 'fields' ] ?? []; |
| 843 | |
| 844 | foreach ( $allowed_fields as $field_rule ) { |
| 845 | $field_name = $field_rule; |
| 846 | $should_save = true; |
| 847 | |
| 848 | // Handle complex rules (controller:value:dependent) |
| 849 | if ( strpos( $field_rule, ':' ) !== false ) { |
| 850 | $parts = explode( ':', $field_rule ); |
| 851 | if ( count( $parts ) === 3 ) { |
| 852 | list( $controller, $controller_value, $dependent ) = $parts; |
| 853 | $field_name = $dependent; |
| 854 | |
| 855 | // Only save if the controller field matches the required value |
| 856 | $current_val = $loc[ $controller ] ?? ''; |
| 857 | if ( $current_val !== $controller_value ) { |
| 858 | $should_save = false; |
| 859 | } |
| 860 | } else { |
| 861 | $field_name = end( $parts ); |
| 862 | } |
| 863 | } |
| 864 | |
| 865 | if ( ! $should_save ) { |
| 866 | continue; |
| 867 | } |
| 868 | |
| 869 | // If the field isn't in POST, skip it (except post_types which might be empty) |
| 870 | if ( ! isset( $loc[ $field_name ] ) && $field_name !== 'post_types' ) { |
| 871 | continue; |
| 872 | } |
| 873 | |
| 874 | switch ( $field_name ) { |
| 875 | case 'order': |
| 876 | $clean[ $field_name ] = intval( $loc[ $field_name ] ); |
| 877 | break; |
| 878 | |
| 879 | case 'toc': |
| 880 | case 'addt_params': |
| 881 | $clean[ $field_name ] = ! empty( $loc[ $field_name ] ); |
| 882 | break; |
| 883 | |
| 884 | case 'post_types': |
| 885 | $pts = isset( $loc[ 'post_types' ] ) ? (array) $loc[ 'post_types' ] : []; |
| 886 | $clean[ $field_name ] = array_map( 'sanitize_key', $pts ); |
| 887 | break; |
| 888 | |
| 889 | case 'custom': |
| 890 | $clean[ $field_name ] = esc_url_raw( $loc[ $field_name ] ); |
| 891 | break; |
| 892 | |
| 893 | case 'page_location': |
| 894 | $clean[ $field_name ] = sanitize_key( $loc[ $field_name ] ); |
| 895 | break; |
| 896 | |
| 897 | case 'css_selector': |
| 898 | $clean[ $field_name ] = sanitize_text_field( $loc[ $field_name ] ); |
| 899 | break; |
| 900 | |
| 901 | default: |
| 902 | $clean[ $field_name ] = sanitize_text_field( $loc[ $field_name ] ); |
| 903 | break; |
| 904 | } |
| 905 | } |
| 906 | $locations[] = $clean; |
| 907 | } |
| 908 | } |
| 909 | |
| 910 | update_post_meta( $post_id, 'helpdocs_locations', $locations ); |
| 911 | |
| 912 | // Global fields |
| 913 | $view_roles = isset( $_POST[ 'helpdocs_view_roles' ] ) ? array_map( 'sanitize_key', wp_unslash( $_POST[ 'helpdocs_view_roles' ] ) ) : []; // phpcs:ignore |
| 914 | update_post_meta( $post_id, 'helpdocs_view_roles', $view_roles ); |
| 915 | |
| 916 | $api = isset( $_POST[ 'helpdocs_api' ] ) ? sanitize_text_field( wp_unslash( $_POST[ 'helpdocs_api' ] ) ) : 'default'; // phpcs:ignore |
| 917 | update_post_meta( $post_id, 'helpdocs_api', $api ); |
| 918 | |
| 919 | // Cleanup legacy meta fields: these are now handled within the 'helpdocs_locations' repeater array. |
| 920 | $legacy_keys = [ |
| 921 | 'site_location', |
| 922 | 'page_location', |
| 923 | 'custom', |
| 924 | 'addt_params', |
| 925 | 'post_types', |
| 926 | 'order', |
| 927 | 'toc', |
| 928 | 'css_selector' |
| 929 | ]; |
| 930 | |
| 931 | foreach ( $legacy_keys as $key ) { |
| 932 | delete_post_meta( $post_id, 'helpdocs_' . $key ); |
| 933 | } |
| 934 | |
| 935 | // Flush the cache so changes show immediately |
| 936 | Helpers::flush_location_cache(); |
| 937 | } // End save_post() |
| 938 | |
| 939 | |
| 940 | /** |
| 941 | * Delete post callback to flush cache |
| 942 | * |
| 943 | * @param int $post_id |
| 944 | * @return void |
| 945 | */ |
| 946 | public function delete_post( $post_id ) { |
| 947 | if ( get_post_type( $post_id ) !== self::$post_type ) { |
| 948 | return; |
| 949 | } |
| 950 | |
| 951 | // Flush the cache so changes show immediately |
| 952 | Helpers::flush_location_cache(); |
| 953 | } // End delete_post() |
| 954 | |
| 955 | |
| 956 | /** |
| 957 | * Admin columns |
| 958 | * |
| 959 | * @param array $columns |
| 960 | * @return array |
| 961 | */ |
| 962 | public function admin_columns( $columns ) { |
| 963 | $columns[ 'helpdocs_desc' ] = __( 'Description', 'admin-help-docs' ); |
| 964 | $columns[ 'helpdocs_site_locations' ] = __( 'Site Locations', 'admin-help-docs' ); |
| 965 | $columns[ 'helpdocs_allow_public' ] = __( 'Public', 'admin-help-docs' ); |
| 966 | return $columns; |
| 967 | } // End admin_columns() |
| 968 | |
| 969 | |
| 970 | /** |
| 971 | * Admin column content |
| 972 | * |
| 973 | * @param string $column |
| 974 | * @param int $post_id |
| 975 | * @return void |
| 976 | */ |
| 977 | public function admin_column_content( $column, $post_id ) { |
| 978 | // Description |
| 979 | if ( 'helpdocs_desc' === $column ) { |
| 980 | echo esc_html( $this->truncate( get_the_excerpt( $post_id ), 140 ) ); |
| 981 | } |
| 982 | |
| 983 | // Consolidated Locations Column |
| 984 | if ( 'helpdocs_site_locations' === $column ) { |
| 985 | $locations = get_post_meta( $post_id, 'helpdocs_locations', true ); |
| 986 | |
| 987 | // Back-compat check: if repeater is empty, try to build a single location from legacy meta |
| 988 | if ( ! is_array( $locations ) || empty( $locations ) ) { |
| 989 | $site_val = get_post_meta( $post_id, 'helpdocs_site_location', true ); |
| 990 | if ( ! empty( $site_val ) ) { |
| 991 | $locations = [ |
| 992 | [ |
| 993 | 'site_location' => $site_val, |
| 994 | 'page_location' => get_post_meta( $post_id, 'helpdocs_page_location', true ), |
| 995 | 'custom' => get_post_meta( $post_id, 'helpdocs_custom', true ), |
| 996 | 'post_types' => Helpers::normalize_meta_array( get_post_meta( $post_id, 'helpdocs_post_types', true ) ), |
| 997 | 'order' => get_post_meta( $post_id, 'helpdocs_order', true ), |
| 998 | 'css_selector' => get_post_meta( $post_id, 'helpdocs_css_selector', true ), |
| 999 | ] |
| 1000 | ]; |
| 1001 | } |
| 1002 | } |
| 1003 | |
| 1004 | if ( ! is_array( $locations ) || empty( $locations ) ) { |
| 1005 | echo '<span class="description">' . esc_html__( 'No locations set.', 'admin-help-docs' ) . '</span>'; |
| 1006 | return; |
| 1007 | } |
| 1008 | |
| 1009 | echo '<ul style="margin:0; padding:0; list-style:none;">'; |
| 1010 | foreach ( $locations as $loc ) { |
| 1011 | $site_location_encoded = $loc[ 'site_location' ] ?? ''; |
| 1012 | if ( empty( $site_location_encoded ) ) { |
| 1013 | continue; |
| 1014 | } |
| 1015 | |
| 1016 | $label = Helpers::get_admin_page_title_from_url( $site_location_encoded ); |
| 1017 | $url = Helpers::get_link_from_site_location( $post_id, $loc ); |
| 1018 | |
| 1019 | echo '<li style="margin-bottom: 8px; border-bottom: 1px solid #eee; padding-bottom: 4px;">'; |
| 1020 | if ( $url ) { |
| 1021 | echo '<a href="' . esc_url( $url ) . '" target="_blank"><strong>' . wp_kses_post( $label ) . '</strong></a>'; |
| 1022 | } else { |
| 1023 | echo '<strong>' . wp_kses_post( $label ) . '</strong>'; |
| 1024 | } |
| 1025 | |
| 1026 | // Meta detail string |
| 1027 | $details = []; |
| 1028 | |
| 1029 | // 1. Page Location |
| 1030 | if ( ! empty( $loc[ 'page_location' ] ) ) { |
| 1031 | $page_loc = ucwords( str_replace( '_', ' ', $loc[ 'page_location' ] ) ); |
| 1032 | if ( $page_loc === 'Contextual' ) { |
| 1033 | $page_loc .= ' Help Tab'; |
| 1034 | } |
| 1035 | $details[] = '<em>' . esc_html( $page_loc ) . '</em>'; |
| 1036 | } |
| 1037 | |
| 1038 | // 2. Custom URL |
| 1039 | if ( ! empty( $loc[ 'custom' ] ) ) { |
| 1040 | $details[] = '<code style="font-size:10px;">' . esc_html( $loc[ 'custom' ] ) . '</code>'; |
| 1041 | } |
| 1042 | |
| 1043 | // 3. Post Types |
| 1044 | if ( ! empty( $loc[ 'post_types' ] ) && is_array( $loc[ 'post_types' ] ) ) { |
| 1045 | $pt_labels = []; |
| 1046 | foreach ( $loc[ 'post_types' ] as $pt ) { |
| 1047 | $pt_obj = get_post_type_object( $pt ); |
| 1048 | $pt_labels[] = $pt_obj ? $pt_obj->labels->name : $pt; |
| 1049 | } |
| 1050 | $details[] = 'Types: ' . esc_html( implode( ', ', $pt_labels ) ); |
| 1051 | } |
| 1052 | |
| 1053 | // 4. CSS Selector |
| 1054 | if ( ! empty( $loc[ 'css_selector' ] ) ) { |
| 1055 | $details[] = 'Selector: <code>' . esc_html( $loc[ 'css_selector' ] ) . '</code>'; |
| 1056 | } |
| 1057 | |
| 1058 | // 5. Order |
| 1059 | if ( isset( $loc[ 'order' ] ) && $loc[ 'order' ] !== '' ) { |
| 1060 | $details[] = 'Order: ' . intval( $loc[ 'order' ] ); |
| 1061 | } |
| 1062 | |
| 1063 | if ( ! empty( $details ) ) { |
| 1064 | echo '<br><span class="description" style="font-size:11px;">' . wp_kses_post( implode( ' | ', $details ) ) . '</span>'; |
| 1065 | } |
| 1066 | |
| 1067 | echo '</li>'; |
| 1068 | } |
| 1069 | echo '</ul>'; |
| 1070 | } |
| 1071 | |
| 1072 | if ( 'helpdocs_allow_public' === $column ) { |
| 1073 | $api = sanitize_key( get_post_meta( $post_id, 'helpdocs_api', true ) ); |
| 1074 | if ( empty( $api ) || 'default' === $api ) { |
| 1075 | $default_api = get_option( 'helpdocs_api' ); |
| 1076 | if ( $default_api === 'yes' || $default_api === true ) { |
| 1077 | $default_api = true; |
| 1078 | } else { |
| 1079 | $default_api = false; |
| 1080 | } |
| 1081 | $api = $default_api ? __( 'Default (Yes)', 'admin-help-docs' ) : __( 'Default (No)', 'admin-help-docs' ); |
| 1082 | } else { |
| 1083 | $api = ucwords( $api ); |
| 1084 | } |
| 1085 | echo esc_html( $api ); |
| 1086 | } |
| 1087 | } // End admin_column_content() |
| 1088 | |
| 1089 | |
| 1090 | /** |
| 1091 | * Strip admin menu counters from a label |
| 1092 | * |
| 1093 | * @param string $label |
| 1094 | * @return string |
| 1095 | */ |
| 1096 | private static function strip_admin_menu_counters( $label ) { |
| 1097 | if ( !is_string( $label ) ) { |
| 1098 | return $label; |
| 1099 | } |
| 1100 | |
| 1101 | // Remove update badges (nested spans included) |
| 1102 | $label = preg_replace( '/<span[^>]*class="[^"]*\b(update-plugins|awaiting-mod|count-[0-9]+|bubble)\b[^"]*"[^>]*>.*?<\/span>/i', '', $label ); |
| 1103 | |
| 1104 | // Remove any remaining HTML |
| 1105 | $label = wp_strip_all_tags( $label ); |
| 1106 | |
| 1107 | // Decode entities then trim |
| 1108 | $label = trim( html_entity_decode( $label, ENT_QUOTES, 'UTF-8' ) ); |
| 1109 | |
| 1110 | // Remove trailing counts like "85", "(85)", "[85]", "{85}" |
| 1111 | $label = preg_replace( '/(?:\s*[\(\[\{]\s*\d+\s*[\)\]\}])|\s*\d+\s*$/', '', $label ); |
| 1112 | |
| 1113 | return trim( $label ); |
| 1114 | } // End strip_admin_menu_counters() |
| 1115 | |
| 1116 | |
| 1117 | /** |
| 1118 | * Trancate string with ellipses |
| 1119 | * |
| 1120 | * @param string $string |
| 1121 | * @param int $length |
| 1122 | * @return string |
| 1123 | */ |
| 1124 | public function truncate( $string, $length ) { |
| 1125 | if ( strlen( $string ) > $length ) { |
| 1126 | return substr( $string, 0, $length - 3 ).'...'; |
| 1127 | } |
| 1128 | return $string; |
| 1129 | } // End truncate() |
| 1130 | |
| 1131 | |
| 1132 | /** |
| 1133 | * Disable Gutenberg while allowing rest |
| 1134 | * |
| 1135 | * @param bool $current_status |
| 1136 | * @param string $post_type |
| 1137 | * @return bool |
| 1138 | */ |
| 1139 | public function disable_gutenberg( $current_status, $post_type ) { |
| 1140 | $disabled_post_types = [ self::$post_type ]; |
| 1141 | if ( in_array( $post_type, $disabled_post_types, true ) ) { |
| 1142 | $current_status = false; |
| 1143 | } |
| 1144 | return $current_status; |
| 1145 | } // End disable_gutenberg() |
| 1146 | |
| 1147 | |
| 1148 | /** |
| 1149 | * Get the location rules |
| 1150 | * |
| 1151 | * @param array $all_site_locations |
| 1152 | * @return array |
| 1153 | */ |
| 1154 | public static function location_rules( $all_site_locations ) { |
| 1155 | $rules = []; |
| 1156 | |
| 1157 | foreach ( $all_site_locations as $loc_key => $loc_info ) { |
| 1158 | $fields = []; |
| 1159 | |
| 1160 | if ( isset( $loc_info[ 'fields' ] ) ) { |
| 1161 | foreach ( $loc_info[ 'fields' ] as $f ) { |
| 1162 | if ( strpos( $f, ':' ) !== false ) { |
| 1163 | $parts = explode( ':', $f ); |
| 1164 | // Handle controller:value:dependent (and more) |
| 1165 | $fields[] = [ |
| 1166 | 'controller' => $parts[ 0 ], |
| 1167 | 'controller_value' => $parts[ 1 ], |
| 1168 | 'dependent' => end( $parts ), // Always the last part |
| 1169 | ]; |
| 1170 | } else { |
| 1171 | $fields[] = [ |
| 1172 | 'field' => $f, |
| 1173 | ]; |
| 1174 | } |
| 1175 | } |
| 1176 | } |
| 1177 | |
| 1178 | $rules[ base64_encode( $loc_key ) ] = $fields; |
| 1179 | } |
| 1180 | |
| 1181 | return $rules; |
| 1182 | } // End location_rules() |
| 1183 | |
| 1184 | |
| 1185 | /** |
| 1186 | * Enqueue admin styles. |
| 1187 | */ |
| 1188 | public function enqueue_admin_styles() { |
| 1189 | // Only load on our post type edit screen |
| 1190 | global $current_screen; |
| 1191 | if ( ! is_admin() || ! isset( $current_screen ) || $current_screen->id !== self::$post_type || $current_screen->base !== 'post' ) { |
| 1192 | return; |
| 1193 | } |
| 1194 | |
| 1195 | $text_domain = Bootstrap::textdomain(); |
| 1196 | $version = Bootstrap::script_version(); |
| 1197 | |
| 1198 | // CSS |
| 1199 | wp_enqueue_style( |
| 1200 | $text_domain . '-post-type-' . self::$post_type, |
| 1201 | Bootstrap::url( 'inc/css/post-type-' . self::$post_type . '.css' ), |
| 1202 | [], |
| 1203 | $version |
| 1204 | ); |
| 1205 | |
| 1206 | // JS |
| 1207 | wp_enqueue_script( |
| 1208 | $text_domain . '-post-type-' . self::$post_type, |
| 1209 | Bootstrap::url( 'inc/js/post-type-' . self::$post_type . '.js' ), |
| 1210 | [ 'jquery' ], |
| 1211 | $version, |
| 1212 | true |
| 1213 | ); |
| 1214 | |
| 1215 | wp_localize_script( $text_domain . '-post-type-' . self::$post_type, 'helpdocs_' . str_replace( '-', '_', self::$post_type ), [ |
| 1216 | 'location_rules' => self::location_rules( self::site_locations() ), |
| 1217 | ] ); |
| 1218 | } // End enqueue_admin_styles() |
| 1219 | |
| 1220 | } |
| 1221 | |
| 1222 | |
| 1223 | HelpDocs::instance(); |