[ 'label' => __( 'Main Documentation Page', 'admin-help-docs' ), 'fields' => [ 'order', 'toc' ] ], 'admin_bar' => [ 'label' => __( 'Admin Bar Menu (Must Be Enabled in Settings)', 'admin-help-docs' ), 'fields' => [ 'order', 'admin_bar_tips' ] ], 'update-core.php' => [ 'label' => __( 'Updates Page', 'admin-help-docs' ), 'fields' => [ 'page_location', 'page_location:element:css_selector' ] ], 'replace_dashboard' => [ 'label' => __( 'WordPress Dashboard (Replaces Dashboard Entirely)', 'admin-help-docs' ), 'fields' => [ 'dashboard_warning' ] ], 'index.php' => [ 'label' => __( 'WordPress Dashboard Widget', 'admin-help-docs' ), 'fields' => [] ], 'function' => [ 'label' => __( 'Function: admin_help_doc( id )', 'admin-help-docs' ), 'fields' => [ 'function_example' ] ], 'post.php' => [ 'label' => __( 'Post/Page Edit Screen', 'admin-help-docs' ), 'fields' => [ 'page_location', 'post_types', 'page_location:element:css_selector' ] ], 'edit.php' => [ 'label' => __( 'Post/Page Admin List Screen', 'admin-help-docs' ), 'fields' => [ 'page_location', 'post_types', 'page_location:element:css_selector' ] ] ]; global $menu, $submenu; $fetching_all_locations = false; if ( ! empty( $menu ) && is_array( $menu ) ) { $fetching_all_locations = true; $site_location_names = apply_filters( 'helpdocs_location_names', [ 'edit-comments.php' => __( 'Comments', 'admin-help-docs' ), 'site-editor.php' => __( 'Editor', 'admin-help-docs' ), 'plugins.php' => __( 'Plugins', 'admin-help-docs' ), 'dev-debug-tools' => 'Dev Debug Tools', 'admin.php?page=dev-debug-tools&tool=logs' => __( 'Logs', 'admin-help-docs' ) ] ); $textdomain = Bootstrap::textdomain(); $admin_url = Bootstrap::admin_url(); foreach ( $menu as $m ) { // Skip separators if ( str_starts_with( $m[2], 'separator' ) || $m[2] == 'hp_separator' || ( isset( $m[4] ) && strpos( $m[4], 'wp-menu-separator' ) !== false ) ) { continue; } // Skip dashboard and help topics if ( $m[2] == 'index.php' || $m[2] == $textdomain ) { continue; } // Skip post types if ( str_starts_with( $m[2], 'edit.php' ) ) { continue; } // Change option names if ( array_key_exists( $m[2], $site_location_names ) ) { $site_location_name = $site_location_names[ $m[2] ]; } else { $site_location_name = $m[0]; } // Strip html $site_location_name = self::strip_admin_menu_counters( $site_location_name ); // Add the parent location if ( ! array_key_exists( $m[2], $submenu ) ) { $url = self::get_admin_menu_item_url( $m[2] ); if ( is_null( $url ) ) { $url = 'not found'; } $url = str_replace( $admin_url, '', $url ); $locations[ $url ] = [ 'label' => $site_location_name, 'fields' => [ 'page_location', 'page_location:element:css_selector' ] ]; } // Check for sub menu items foreach ( $submenu as $k => $sub ) { if ( $k == $m[2] ) { foreach ( $sub as $s ) { if ( array_key_exists( $s[2], $site_location_names ) ) { $sublocation_name = $site_location_names[ $s[2] ]; } else { $sublocation_name = $s[0]; } $sublocation_name = self::strip_admin_menu_counters( $sublocation_name ); $url = self::get_admin_menu_item_url( $s[2] ); if ( is_null( $url ) ) { $url = 'not found'; } $url = str_replace( $admin_url, '', $url ); $locations[ $url ] = [ 'label' => $site_location_name . ' > ' . $sublocation_name, 'fields' => [ 'page_location', 'page_location:element:css_selector' ] ]; } } } } } // Add a custom link option to the bottom $locations[ 'custom' ] = [ 'label' => __( 'Other/Custom Page', 'admin-help-docs' ), 'fields' => [ 'custom', 'addt_params', 'page_location', 'page_location:element:css_selector' ] ]; // Cache the result for 1 hour if ( $fetching_all_locations ) { set_transient( $cache_key, $locations, 12 * HOUR_IN_SECONDS ); } return apply_filters( 'helpdocs_site_locations', $locations ); } // End site_locations() /** * Get available page locations for displaying help docs * * @return array */ public static function page_locations() { $locations = [ 'contextual' => __( 'Contextual Help Tab (At Top of Screen)', 'admin-help-docs' ), 'top' => __( 'Top', 'admin-help-docs' ), 'bottom' => __( 'Bottom', 'admin-help-docs' ), 'side' => __( 'Side', 'admin-help-docs' ), 'element' => __( 'Next to Specific Element (Beta)', 'admin-help-docs' ), ]; return apply_filters( 'helpdocs_page_locations', $locations ); } // End page_locations() /** * Get available post types for displaying help docs * * @return array */ public static function post_types() { return array_merge( [ 'post' => 'post', 'page' => 'page' ], get_post_types( [ '_builtin' => false ] ) ); } // End post_types() /** * The single instance of the class * * @var self|null */ private static ?HelpDocs $instance = null; /** * Get the singleton instance * * @return self */ public static function instance() : self { return self::$instance ??= new self(); } // End instance() /** * HelpDocs constructor. * * Private to enforce singleton pattern. */ private function __construct() { // Register the post type add_action( 'init', [ $this, 'register_post_type' ] ); // Add the header to the top of the admin list page add_action( 'load-edit.php', [ $this, 'add_header' ] ); // Move the search box to the subheader add_action( 'helpdocs_subheader_right', [ $this, 'render_search_box' ] ); // Add the meta box add_action( 'add_meta_boxes', [ $this, 'meta_boxes' ], 999 ); // Rename excerpt meta box add_filter( 'gettext', [ $this, 'excerpt_meta_box' ], 10, 2 ); // Save the post data add_action( 'save_post', [ $this, 'save_post' ] ); add_action( 'before_delete_post', [ $this, 'delete_post' ] ); // Add admin columns add_filter( 'manage_' . self::$post_type . '_posts_columns', [ $this, 'admin_columns' ] ); add_action( 'manage_' . self::$post_type . '_posts_custom_column', [ $this, 'admin_column_content' ], 10, 2 ); // Either enqueue block editor styles or disable block editor if ( ! get_option( 'helpdocs_gutenberg_editor' ) ) { add_filter( 'use_block_editor_for_post_type', [ $this, 'disable_gutenberg' ], 10, 2 ); } // Enqueue back-end styles add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_styles' ] ); } // End __construct() /** * Register the post type */ public function register_post_type() { // Set the labels $labels = [ 'name' => _x( 'Help Documents', 'Post Type General Name', 'admin-help-docs' ), 'singular_name' => _x( 'Help Document', 'Post Type Singular Name', 'admin-help-docs' ), 'menu_name' => __( 'Help Documents', 'admin-help-docs' ), 'name_admin_bar' => __( 'Help Documents', 'admin-help-docs' ), 'archives' => __( 'Help Document Archives', 'admin-help-docs' ), 'attributes' => __( 'Help Document Attributes', 'admin-help-docs' ), 'parent_item_colon' => __( 'Parent Help Document:', 'admin-help-docs' ), 'all_items' => __( 'All Help Documents', 'admin-help-docs' ), 'add_new_item' => __( 'Add New Help Document', 'admin-help-docs' ), 'add_new' => __( 'Add New', 'admin-help-docs' ), 'new_item' => __( 'New Help Document', 'admin-help-docs' ), 'edit_item' => __( 'Edit Help Document', 'admin-help-docs' ), 'update_item' => __( 'Update Help Document', 'admin-help-docs' ), 'view_item' => __( 'View Help Document', 'admin-help-docs' ), 'view_items' => __( 'View Help Documents', 'admin-help-docs' ), 'search_items' => __( 'Search Help Documents', 'admin-help-docs' ), 'not_found' => __( 'Not found', 'admin-help-docs' ), 'not_found_in_trash' => __( 'Not found in Trash', 'admin-help-docs' ), 'insert_into_item' => __( 'Insert into help document', 'admin-help-docs' ), 'uploaded_to_this_item' => __( 'Uploaded to this help document', 'admin-help-docs' ), 'items_list' => __( 'Help Document list', 'admin-help-docs' ), 'items_list_navigation' => __( 'Help Document list navigation', 'admin-help-docs' ), 'filter_items_list' => __( 'Filter help document list', 'admin-help-docs' ), ]; // Allow filter for supports and taxonomies $supports = apply_filters( 'helpdocs_post_type_supports', [ 'title', 'editor' ] ); $taxonomies = apply_filters( 'helpdocs_post_type_taxonomies', [] ); // Set the CPT args $args = [ 'label' => __( 'Help Documents', 'admin-help-docs' ), 'description' => __( 'Help Documents', 'admin-help-docs' ), 'labels' => $labels, 'supports' => $supports, 'taxonomies' => $taxonomies, 'public' => true, 'show_ui' => true, 'show_in_menu' => false, 'show_in_admin_bar' => false, 'show_in_nav_menus' => false, 'can_export' => true, 'has_archive' => false, 'exclude_from_search' => true, 'publicly_queryable' => false, 'query_var' => self::$post_type, 'capability_type' => 'post', 'show_in_rest' => true, ]; // Register the CPT register_post_type( self::$post_type, $args ); } // End register_post_type() /** * Add the header to the top of the admin list page * * @return void */ public function add_header() { $screen = get_current_screen(); if ( 'edit-' . self::$post_type === $screen->id ) { add_action( 'in_admin_header', function() { include Bootstrap::path( 'inc/header.php' ); } ); } } // End add_header() /** * Add a search box to the subheader on the folders page * * @param string $current_tab The current admin tab * @return void */ public function render_search_box( string $current_tab ) { if ( $current_tab !== 'manage' ) { return; } $search_value = sanitize_text_field( wp_unslash( $_GET[ 's' ] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>
post_type === self::$post_type ) { // Define the IDs we want to KEEP $whitelist = [ 'submitdiv', // The Publish box 'formatdiv', // Post Formats 'categorydiv', // Standard Category box 'tagsdiv-post_tag', // Standard Tags box 'pageparentdiv', // Page Attributes 'postimagediv', // Featured Image 'tagsdiv-' . Folders::$taxonomy // Folders taxonomy box ]; // Loop through all contexts (normal, advanced, side) foreach ( [ 'normal', 'advanced', 'side' ] as $context ) { foreach ( [ 'high', 'core', 'default', 'low' ] as $priority ) { if ( ! isset( $wp_meta_boxes[ self::$post_type ][ $context ][ $priority ] ) ) { continue; } foreach ( $wp_meta_boxes[ self::$post_type ][ $context ][ $priority ] as $box_id => $box_data ) { // If the box ID is NOT in our whitelist, remove it if ( ! in_array( $box_id, $whitelist ) ) { remove_meta_box( $box_id, self::$post_type, $context ); } } } } // 2. Now that the deck is clear, add ONLY your meta box add_meta_box( 'helpdocs_locations', __( 'Location', 'admin-help-docs' ), [ $this, 'meta_box_content' ], self::$post_type, 'advanced', 'high' ); } } // End meta_boxes() /** * Rename excerpt meta box as description * * @param string $translation * @param string $original * @return string */ public function excerpt_meta_box( $translation, $original ) { // Make sure we are only looking at our post type global $post_type; if ( $post_type != self::$post_type ) { return $translation; } // Update the box if ( 'Excerpt' == $original ) { return __( 'Description', 'admin-help-docs' ); } elseif ( false !== strpos( $original, 'Excerpts are optional hand-crafted summaries of your' ) ) { 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' ); } return $translation; } // End excerpt_meta_box() /** * Meta box content * * @param object $post * @return void */ public function meta_box_content( $post ) { wp_nonce_field( 'help_location_nonce', 'help_location_nonce' ); $locations = get_post_meta( $post->ID, 'helpdocs_locations', true ); if ( 'auto-draft' === $post->post_status && isset( $_GET[ 'site_location' ] ) ) { // phpcs:ignore $locations = [ [ 'site_location' => sanitize_text_field( wp_unslash( $_GET[ 'site_location' ] ) ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended 'page_location' => 'top', // Default to top 'custom' => isset( $_GET[ 'custom_url' ] ) ? esc_url_raw( wp_unslash( $_GET[ 'custom_url' ] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended 'post_types' => isset( $_GET[ 'slpt' ] ) ? [ sanitize_text_field( wp_unslash( $_GET[ 'slpt' ] ) ) ] : [], // phpcs:ignore WordPress.Security.NonceVerification.Recommended 'order' => 0, 'toc' => false, 'css_selector' => '', 'addt_params' => true, ] ]; } if ( ! is_array( $locations ) || empty( $locations ) ) { // Backward compatibility: use legacy single-location fields $meta = get_post_meta( $post->ID ); $locations = [ [ 'site_location' => $meta[ 'helpdocs_site_location' ][ 0 ] ?? '', 'page_location' => $meta[ 'helpdocs_page_location' ][ 0 ] ?? '', 'custom' => $meta[ 'helpdocs_custom' ][ 0 ] ?? '', 'addt_params' => ! empty( $meta[ 'helpdocs_addt_params' ][ 0 ] ), 'post_types' => isset( $meta[ 'helpdocs_post_types' ][ 0 ] ) ? Helpers::normalize_meta_array( $meta[ 'helpdocs_post_types' ][ 0 ] ) : [], 'order' => isset( $meta[ 'helpdocs_order' ][ 0 ] ) ? intval( $meta[ 'helpdocs_order' ][ 0 ] ) : 0, 'toc' => isset( $meta[ 'helpdocs_toc' ][ 0 ] ) ? filter_var( $meta[ 'helpdocs_toc' ][ 0 ], FILTER_VALIDATE_BOOLEAN ) : false, 'css_selector' => $meta[ 'helpdocs_css_selector' ][ 0 ] ?? '', ] ]; } // Global fields $meta = get_post_meta( $post->ID ); $api = $meta[ 'helpdocs_api' ][ 0 ] ?? ''; $view_roles = isset( $meta[ 'helpdocs_view_roles' ][ 0 ] ) ? Helpers::normalize_meta_array( $meta[ 'helpdocs_view_roles' ][ 0 ] ) : []; $roles = Helpers::get_role_options(); $all_site_locations = self::site_locations(); $all_page_locations = self::page_locations(); $all_post_types = self::post_types(); ?>admin_help_doc(ID ); ?>);
' . esc_html( $loc[ 'custom' ] ) . '';
}
// 3. Post Types
if ( ! empty( $loc[ 'post_types' ] ) && is_array( $loc[ 'post_types' ] ) ) {
$pt_labels = [];
foreach ( $loc[ 'post_types' ] as $pt ) {
$pt_obj = get_post_type_object( $pt );
$pt_labels[] = $pt_obj ? $pt_obj->labels->name : $pt;
}
$details[] = 'Types: ' . esc_html( implode( ', ', $pt_labels ) );
}
// 4. CSS Selector
if ( ! empty( $loc[ 'css_selector' ] ) ) {
$details[] = 'Selector: ' . esc_html( $loc[ 'css_selector' ] ) . '';
}
// 5. Order
if ( isset( $loc[ 'order' ] ) && $loc[ 'order' ] !== '' ) {
$details[] = 'Order: ' . intval( $loc[ 'order' ] );
}
if ( ! empty( $details ) ) {
echo '