false, 'persona' => '', 'look_skin' => '', 'top_bar_items' => array(), 'presets' => array(), 'default_preset' => 'system_friend', 'role_assignments' => array(), 'role_visibility' => array(), 'theme' => EDMINBOOST_Theme::get_defaults(), 'behavior' => array( 'drawer_width' => 'standard', 'drawer_width_custom' => self::DRAWER_CUSTOM_WIDTH_DEFAULT, 'animation_speed' => 'normal', 'glassmorphism' => false, 'autosave_interval' => 60, 'badge_refresh_rate' => 60, 'badge_style' => 'pill', 'hide_wp_logo' => false, 'hide_update_counters' => false, 'hide_howdy' => false, 'hide_comments' => false, 'hide_new_content' => false, 'hide_customize' => false, ), 'menu_studio' => self::get_menu_studio_defaults(), ); /** * Filter Command Center defaults (premium builds may adjust default_preset). * * @param array $defaults Default Command Center settings. */ return apply_filters( 'edminboost_command_center_defaults', $defaults ); } /** * Default Menu Studio settings shape. * * @return array */ public static function get_menu_studio_defaults() { return array( 'enabled' => false, 'order' => array(), 'submenu_order' => array(), 'hidden_items' => array(), 'custom_items' => array(), 'use_colors' => true, 'menu_width' => 160, 'font_size' => 14, 'line_height' => 20, 'letter_spacing' => 0, 'display_mode' => 'both', 'padding' => array( 'wrapper_top' => 0, 'wrapper_right' => 0, 'wrapper_bottom' => 0, 'wrapper_left' => 0, 'submenu_top' => 0, 'submenu_right' => 0, 'submenu_bottom' => 0, 'submenu_left' => 0, ), 'colors' => array( 'parent_bg' => '', 'parent_text' => '', 'parent_active' => '', 'submenu_bg' => '', 'submenu_text' => '', 'submenu_hover_text' => '', 'notification_bg' => '', 'notification_text' => '', ), ); } /** * Animation speed options for drawer transitions (value => label + duration). * * @return array */ public static function get_animation_speed_options() { return array( 'fast' => array( 'label' => __( 'Fast (150ms)', 'edminboost-admin-customization' ), 'ms' => 150, ), 'normal' => array( 'label' => __( 'Normal (300ms)', 'edminboost-admin-customization' ), 'ms' => 300, ), 'slow' => array( 'label' => __( 'Slow (500ms)', 'edminboost-admin-customization' ), 'ms' => 500, ), ); } /** * Drawer animation duration in milliseconds for a speed key. * * @param string $speed Speed key (fast, normal, slow). Empty uses saved behavior. * @return int */ public static function get_animation_duration_ms( $speed = '' ) { $options = self::get_animation_speed_options(); if ( '' === $speed ) { $behavior = self::get_settings()['behavior']; $speed = isset( $behavior['animation_speed'] ) ? $behavior['animation_speed'] : 'normal'; } if ( isset( $options[ $speed ]['ms'] ) ) { return (int) $options[ $speed ]['ms']; } return 300; } /** * Get merged Command Center settings from plugin options. * * @return array */ public static function get_settings() { $settings = EDMINBOOST_Settings::get(); $defaults = self::get_defaults(); if ( ! isset( $settings['command_center'] ) || ! is_array( $settings['command_center'] ) ) { return $defaults; } $merged = wp_parse_args( $settings['command_center'], $defaults ); if ( isset( $merged['behavior'] ) && is_array( $merged['behavior'] ) ) { $merged['behavior'] = wp_parse_args( $merged['behavior'], $defaults['behavior'] ); } if ( isset( $merged['theme'] ) && is_array( $merged['theme'] ) ) { $merged['theme'] = wp_parse_args( $merged['theme'], $defaults['theme'] ); } if ( isset( $merged['menu_studio'] ) && is_array( $merged['menu_studio'] ) ) { $ms_defaults = self::get_menu_studio_defaults(); $merged['menu_studio'] = wp_parse_args( $merged['menu_studio'], $ms_defaults ); if ( isset( $merged['menu_studio']['colors'] ) && is_array( $merged['menu_studio']['colors'] ) ) { $merged['menu_studio']['colors'] = wp_parse_args( $merged['menu_studio']['colors'], $ms_defaults['colors'] ); } } /** * Filter merged Command Center settings after defaults are applied. * * @param array $merged Merged Command Center settings. */ return apply_filters( 'edminboost_command_center_merged_settings', $merged ); } /** * Quick links to main plugin pages (Dashboard hub, sidebar). * * @return array[] */ public static function get_page_links() { $base = EDMINBOOST_Admin::PAGE_SLUG; $items = array( array( 'slug' => $base, 'label' => __( 'Dashboard', 'edminboost-admin-customization' ), ), array( 'slug' => $base . self::PAGE_PRESETS, 'label' => __( 'Layouts', 'edminboost-admin-customization' ), ), array( 'slug' => $base . self::PAGE_APPEARANCE, 'label' => __( 'Theme', 'edminboost-admin-customization' ), ), array( 'slug' => $base . self::PAGE_MAPPER, 'label' => __( 'Top Bar', 'edminboost-admin-customization' ), ), array( 'slug' => $base . self::PAGE_MENU_STUDIO, 'label' => __( 'Menu Studio', 'edminboost-admin-customization' ), ), ); $items[] = array( 'slug' => $base . self::PAGE_BILLING, 'label' => __( 'Billing', 'edminboost-admin-customization' ), ); /** * Filter Command Center sidebar page links. * * @param array[] $items Page link rows (slug, label). */ return apply_filters( 'edminboost_command_center_page_links', $items ); } /** * Command Center tab navigation (includes pages hidden from the sidebar). * * @return array[] */ public static function get_nav_items() { $base = EDMINBOOST_Admin::PAGE_SLUG; $items = array( array( 'slug' => $base, 'label' => __( 'Dashboard', 'edminboost-admin-customization' ), ), array( 'slug' => $base . self::PAGE_PRESETS, 'label' => __( 'Layouts', 'edminboost-admin-customization' ), ), array( 'slug' => $base . self::PAGE_APPEARANCE, 'label' => __( 'Theme', 'edminboost-admin-customization' ), ), array( 'slug' => $base . self::PAGE_MAPPER, 'label' => __( 'Top Bar', 'edminboost-admin-customization' ), ), array( 'slug' => $base . self::PAGE_MENU_STUDIO, 'label' => __( 'Menu Studio', 'edminboost-admin-customization' ), ), array( 'slug' => $base . self::PAGE_PRODUCTIVITY, 'label' => __( 'Productivity', 'edminboost-admin-customization' ), ), array( 'slug' => $base . self::PAGE_SECURITY, 'label' => __( 'Security', 'edminboost-admin-customization' ), ), array( 'slug' => $base . self::PAGE_PERFORMANCE, 'label' => __( 'Performance', 'edminboost-admin-customization' ), ), ); $items[] = array( 'slug' => $base . self::PAGE_BILLING, 'label' => __( 'Billing', 'edminboost-admin-customization' ), ); /** * Filter Command Center tab navigation items. * * @param array[] $items Nav rows (slug, label). */ return apply_filters( 'edminboost_command_center_nav_items', $items ); } /** * Whether the site has a saved top bar layout or completed setup. * * @param array|null $cc_settings Optional CC settings; loads from options when null. * @return bool */ public static function is_setup_complete( $cc_settings = null ) { if ( null === $cc_settings ) { $cc_settings = self::get_settings(); } if ( ! empty( $cc_settings['onboarding_completed'] ) ) { return true; } return ! empty( $cc_settings['top_bar_items'] ) && is_array( $cc_settings['top_bar_items'] ); } /** * Look-only skin presets (behavior bundles). * * @return array[] */ public static function get_look_skins() { $defaults = self::get_defaults()['behavior']; return array( 'clean' => array( 'name' => __( 'Clean', 'edminboost-admin-customization' ), 'description' => __( 'Standard panel, subtle badges, hides logo and profile text.', 'edminboost-admin-customization' ), 'icon' => 'dashicons-art', 'behavior' => array_merge( $defaults, array( 'drawer_width' => 'standard', 'animation_speed' => 'normal', 'badge_style' => 'dot', 'hide_wp_logo' => true, 'hide_howdy' => true, 'hide_update_counters' => false, 'hide_comments' => false, ) ), ), 'focused' => array( 'name' => __( 'Focused', 'edminboost-admin-customization' ), 'description' => __( 'Compact panel, counter badges, minimal admin bar clutter.', 'edminboost-admin-customization' ), 'icon' => 'dashicons-visibility', 'behavior' => array_merge( $defaults, array( 'drawer_width' => 'compact', 'animation_speed' => 'fast', 'badge_style' => 'pill', 'hide_wp_logo' => true, 'hide_update_counters' => true, 'hide_howdy' => false, 'hide_comments' => true, ) ), ), 'full' => array( 'name' => __( 'Full', 'edminboost-admin-customization' ), 'description' => __( 'Standard panel with accent badges and the full native admin bar.', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-site-alt3', 'behavior' => array_merge( $defaults, array( 'drawer_width' => 'standard', 'animation_speed' => 'normal', 'badge_style' => 'accent', 'hide_wp_logo' => false, 'hide_update_counters' => false, 'hide_howdy' => false, 'hide_comments' => false, ) ), ), ); } /** * Detect which look skin matches stored behavior settings. * * @param array|null $behavior Behavior settings. * @return string Skin id or empty string. */ public static function detect_look_skin( $behavior = null ) { if ( null === $behavior ) { $behavior = self::get_settings()['behavior']; } foreach ( self::get_look_skins() as $skin_id => $skin ) { $matches = true; foreach ( $skin['behavior'] as $key => $value ) { if ( ! isset( $behavior[ $key ] ) ) { $matches = false; break; } if ( (bool) $behavior[ $key ] !== (bool) $value ) { $matches = false; break; } if ( ! is_bool( $value ) && (string) $behavior[ $key ] !== (string) $value ) { $matches = false; break; } } if ( $matches ) { return $skin_id; } } return ''; } /** * Persona preset cards for onboarding. * * @return array[] */ public static function get_personas() { $personas = array( 'friend' => array( 'title' => __( 'Friend\'s Website', 'edminboost-admin-customization' ), 'description' => __( 'You help a friend keep their blog or portfolio updated — content-first shortcuts without technical clutter.', 'edminboost-admin-customization' ), 'icon' => 'dashicons-groups', 'preset' => 'system_friend', ), 'family' => array( 'title' => __( 'Family Member\'s Site', 'edminboost-admin-customization' ), 'description' => __( 'A gentle setup for parents or relatives who only need pages, photos, and the basics.', 'edminboost-admin-customization' ), 'icon' => 'dashicons-heart', 'preset' => 'system_family', ), ); /** * Filter onboarding persona cards (premium builds add Pro personas). * * @param array $personas Persona key to card metadata. */ return apply_filters( 'edminboost_personas', $personas ); } /** * Built-in preset category labels for the preset library UI. * * @return array */ public static function get_preset_categories() { return array( 'source' => __( 'Current layout', 'edminboost-admin-customization' ), 'scenario' => __( 'By use case', 'edminboost-admin-customization' ), 'workflow' => __( 'By role', 'edminboost-admin-customization' ), 'saved' => __( 'Your saved layouts', 'edminboost-admin-customization' ), ); } /** * Virtual layout presets shown at the top of picker dropdowns. * * @return array */ public static function get_virtual_layout_presets() { return array( 'default' => array( 'name' => __( 'Default', 'edminboost-admin-customization' ), 'description' => __( 'Your site\'s default layout preset.', 'edminboost-admin-customization' ), 'virtual' => true, 'category' => 'source', ), 'custom' => array( 'name' => __( 'Custom', 'edminboost-admin-customization' ), 'description' => __( 'Fine-tune the top bar and sidebar in Top Bar and Menu Studio', 'edminboost-admin-customization' ), 'virtual' => true, 'category' => 'source', ), ); } /** * Preset catalog for picker UIs (virtual entries + system + saved). * * @param bool $include_virtual Whether to prepend Default and Custom options. * @return array */ public static function get_picker_presets( $include_virtual = true ) { $presets = self::get_all_presets(); if ( ! $include_virtual ) { return $presets; } return array_merge( self::get_virtual_layout_presets(), $presets ); } /** * Resolve a virtual preset id to a concrete preset id. * * @param string $preset_id Preset identifier (may be virtual). * @param array|null $cc_settings Optional Command Center settings. * @return string */ public static function resolve_effective_preset_id( $preset_id, $cc_settings = null ) { $preset_id = sanitize_key( $preset_id ); if ( 'default' !== $preset_id ) { return $preset_id; } if ( null === $cc_settings ) { $cc_settings = self::get_settings(); } $fallback = self::get_defaults()['default_preset']; $all_presets = array_keys( self::get_all_presets() ); $stored = isset( $cc_settings['default_preset'] ) && '' !== $cc_settings['default_preset'] ? sanitize_key( $cc_settings['default_preset'] ) : $fallback; if ( in_array( $stored, $all_presets, true ) ) { return $stored; } return in_array( $fallback, $all_presets, true ) ? $fallback : 'system_client'; } /** * Fingerprint top bar items for layout comparison. * * @param array $item Top bar item. * @return string */ private static function get_top_bar_item_fingerprint( $item ) { if ( ! is_array( $item ) || empty( $item['slug'] ) ) { return ''; } $slug = isset( $item['slug'] ) ? (string) $item['slug'] : ''; $anchor = isset( $item['anchor'] ) ? (string) $item['anchor'] : ''; $interaction = isset( $item['interaction'] ) ? (string) $item['interaction'] : 'redirect'; $badge = isset( $item['badge_source'] ) ? (string) $item['badge_source'] : ''; return $slug . "\0" . $anchor . "\0" . $interaction . "\0" . $badge; } /** * Whether two top bar item lists are structurally equivalent. * * @param array $left First item list. * @param array $right Second item list. * @return bool */ public static function top_bar_items_match( $left, $right ) { if ( ! apply_filters( 'edminboost_show_topbar_interaction_ui', false ) ) { $left = self::normalize_top_bar_items_for_preset_match( $left ); $right = self::normalize_top_bar_items_for_preset_match( $right ); } $left_fps = array(); $right_fps = array(); foreach ( (array) $left as $item ) { $fp = self::get_top_bar_item_fingerprint( $item ); if ( '' !== $fp ) { $left_fps[] = $fp; } } foreach ( (array) $right as $item ) { $fp = self::get_top_bar_item_fingerprint( $item ); if ( '' !== $fp ) { $right_fps[] = $fp; } } return $left_fps === $right_fps; } /** * Align top bar items with free-tier stored shape for preset matching. * * @param array $items Top bar items. * @return array */ private static function normalize_top_bar_items_for_preset_match( $items ) { $normalized = array(); foreach ( (array) $items as $item ) { if ( ! is_array( $item ) || empty( $item['slug'] ) ) { continue; } $normalized[] = array( 'slug' => (string) $item['slug'], 'anchor' => isset( $item['anchor'] ) ? (string) $item['anchor'] : '', 'interaction' => 'redirect', 'badge_source' => '', ); } return $normalized; } /** * Detect which picker preset matches the saved top bar layout. * * @param array|null $cc_settings Optional Command Center settings. * @return string Preset id, including virtual `default` or `custom`. */ public static function detect_active_layout_preset( $cc_settings = null ) { if ( null === $cc_settings ) { $cc_settings = self::get_settings(); } $current = isset( $cc_settings['top_bar_items'] ) && is_array( $cc_settings['top_bar_items'] ) ? $cc_settings['top_bar_items'] : array(); if ( empty( $current ) ) { return 'default'; } $current_menu = isset( $cc_settings['menu_studio'] ) && is_array( $cc_settings['menu_studio'] ) ? $cc_settings['menu_studio'] : self::get_menu_studio_defaults(); $default_preset = self::resolve_effective_preset_id( 'default', $cc_settings ); $default_items = self::resolve_preset_top_bar_items( $default_preset, $cc_settings ); $default_menu = self::resolve_preset_menu_studio( $default_preset, $cc_settings ); if ( self::top_bar_items_match( $current, $default_items ) && self::menu_studio_settings_match( $current_menu, $default_menu ) ) { return 'default'; } foreach ( self::get_all_presets() as $preset_id => $preset ) { unset( $preset ); $items = self::resolve_preset_top_bar_items( $preset_id, $cc_settings ); $menu = self::resolve_preset_menu_studio( $preset_id, $cc_settings ); if ( self::top_bar_items_match( $current, $items ) && self::menu_studio_settings_match( $current_menu, $menu ) ) { return $preset_id; } } return 'custom'; } /** * Whether two Menu Studio configuration snapshots are equivalent for preset matching. * * @param array $left First menu_studio settings. * @param array $right Second menu_studio settings. * @return bool */ public static function menu_studio_settings_match( $left, $right ) { $left_enabled = ! empty( $left['enabled'] ); $right_enabled = ! empty( $right['enabled'] ); if ( ! $left_enabled && ! $right_enabled ) { return true; } // Preset snapshots keep Menu Studio disabled until the user saves that screen. if ( ! $right_enabled ) { return self::menu_studio_sidebar_shape_match( $left, $right ); } if ( ! $left_enabled ) { return false; } return self::menu_studio_sidebar_shape_match( $left, $right ); } /** * Compare sidebar order/hidden/custom shape for preset matching. * * @param array $left First menu_studio settings. * @param array $right Second menu_studio settings. * @return bool */ private static function menu_studio_sidebar_shape_match( $left, $right ) { $left_order = isset( $left['order'] ) && is_array( $left['order'] ) ? $left['order'] : array(); $right_order = isset( $right['order'] ) && is_array( $right['order'] ) ? $right['order'] : array(); $left_hidden = isset( $left['hidden_items'] ) && is_array( $left['hidden_items'] ) ? $left['hidden_items'] : array(); $right_hidden = isset( $right['hidden_items'] ) && is_array( $right['hidden_items'] ) ? $right['hidden_items'] : array(); $left_sub = isset( $left['submenu_order'] ) && is_array( $left['submenu_order'] ) ? $left['submenu_order'] : array(); $right_sub = isset( $right['submenu_order'] ) && is_array( $right['submenu_order'] ) ? $right['submenu_order'] : array(); $left_custom = isset( $left['custom_items'] ) && is_array( $left['custom_items'] ) ? $left['custom_items'] : array(); $right_custom = isset( $right['custom_items'] ) && is_array( $right['custom_items'] ) ? $right['custom_items'] : array(); $left_custom_ids = array(); $right_custom_ids = array(); foreach ( $left_custom as $item ) { if ( ! empty( $item['id'] ) ) { $left_custom_ids[] = sanitize_key( $item['id'] ); } } foreach ( $right_custom as $item ) { if ( ! empty( $item['id'] ) ) { $right_custom_ids[] = sanitize_key( $item['id'] ); } } return $left_order === $right_order && $left_hidden === $right_hidden && $left_sub === $right_sub && $left_custom_ids === $right_custom_ids; } /** * Built-in system presets. * * @return array[] */ public static function get_system_presets() { $presets = array( 'system_friend' => array( 'name' => __( 'Friend\'s Website', 'edminboost-admin-customization' ), 'description' => __( 'Help a friend publish posts, update pages, and moderate comments — no dev tools.', 'edminboost-admin-customization' ), 'system' => true, 'category' => 'scenario', 'persona' => 'friend', ), 'system_family' => array( 'name' => __( 'Family Member\'s Site', 'edminboost-admin-customization' ), 'description' => __( 'Ultra-simple bar with dashboard, pages, and media for non-technical relatives.', 'edminboost-admin-customization' ), 'system' => true, 'category' => 'scenario', 'persona' => 'family', ), ); /** * Filter built-in scenario presets before role workflow presets are merged. * * Premium builds append Pro scenario presets via EDMINBOOST_Command_Center_Catalog_Pro. * * @param array $presets Scenario preset metadata keyed by preset ID. */ $presets = apply_filters( 'edminboost_system_presets', $presets ); return array_merge( $presets, self::get_role_system_presets() ); } /** * Preferred top bar slugs per system preset (resolved against discovered menus). * * @return array */ public static function get_preset_layout_definitions() { $definitions = array( 'system_friend' => array( array( 'slug' => 'index.php', 'label' => __( 'Dashboard', 'edminboost-admin-customization' ), 'icon' => 'dashicons-dashboard', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit.php', 'label' => __( 'Posts', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-post', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit.php?post_type=page', 'label' => __( 'Pages', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-page', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'upload.php', 'label' => __( 'Media', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-media', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit-comments.php', 'label' => __( 'Comments', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-comments', 'interaction' => 'redirect', 'badge_source' => 'comments', ), ), 'system_family' => array( array( 'slug' => 'index.php', 'label' => __( 'Dashboard', 'edminboost-admin-customization' ), 'icon' => 'dashicons-dashboard', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit.php?post_type=page', 'label' => __( 'Pages', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-page', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'upload.php', 'label' => __( 'Media', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-media', 'interaction' => 'redirect', 'badge_source' => '', ), ), ); /** * Filter layout definitions before role workflow layouts are merged. * * @param array $definitions Preset ID to top bar item list. */ $definitions = apply_filters( 'edminboost_preset_layout_definitions', $definitions ); return array_merge( $definitions, self::get_role_preset_layout_definitions() ); } /** * Resolve top bar items for the current user, honoring role preset assignments. * * @param array|null $cc_settings Optional CC settings. * @return array[] */ public static function resolve_top_bar_items_for_user( $cc_settings = null ) { if ( null === $cc_settings ) { $cc_settings = self::get_settings(); } $items = isset( $cc_settings['top_bar_items'] ) && is_array( $cc_settings['top_bar_items'] ) ? $cc_settings['top_bar_items'] : array(); $user = wp_get_current_user(); if ( empty( $user->roles ) ) { return $items; } $assignments = isset( $cc_settings['role_assignments'] ) && is_array( $cc_settings['role_assignments'] ) ? $cc_settings['role_assignments'] : array(); foreach ( $user->roles as $role ) { if ( empty( $assignments[ $role ] ) ) { continue; } $preset_items = self::resolve_preset_top_bar_items( $assignments[ $role ], $cc_settings ); if ( ! empty( $preset_items ) ) { return $preset_items; } } return $items; } /** * Resolve Menu Studio settings for the current user, honoring role preset assignments. * * @param array|null $cc_settings Optional CC settings. * @return array */ public static function resolve_menu_studio_for_user( $cc_settings = null ) { if ( null === $cc_settings ) { $cc_settings = self::get_settings(); } $defaults = self::get_menu_studio_defaults(); $menu_studio = isset( $cc_settings['menu_studio'] ) && is_array( $cc_settings['menu_studio'] ) ? wp_parse_args( $cc_settings['menu_studio'], $defaults ) : $defaults; $user = wp_get_current_user(); if ( empty( $user->roles ) ) { /** * Filter Menu Studio settings resolved for the current user. * * @param array $menu_studio Resolved Menu Studio settings. * @param array $cc_settings Command Center settings. * @param WP_User $user Current user. */ return apply_filters( 'edminboost_menu_studio_for_user', $menu_studio, $cc_settings, $user ); } $assignments = isset( $cc_settings['role_assignments'] ) && is_array( $cc_settings['role_assignments'] ) ? $cc_settings['role_assignments'] : array(); $saved_menu = isset( $cc_settings['menu_studio'] ) && is_array( $cc_settings['menu_studio'] ) ? $cc_settings['menu_studio'] : array(); foreach ( $user->roles as $role ) { if ( empty( $assignments[ $role ] ) ) { continue; } $menu_studio = self::resolve_preset_menu_studio( $assignments[ $role ], $cc_settings ); break; } $menu_studio = self::merge_saved_custom_menu_items( $menu_studio, $saved_menu ); /** * Filter Menu Studio settings resolved for the current user. * * @param array $menu_studio Resolved Menu Studio settings. * @param array $cc_settings Command Center settings. * @param WP_User $user Current user. */ return apply_filters( 'edminboost_menu_studio_for_user', $menu_studio, $cc_settings, $user ); } /** * Preserve saved Menu Studio custom sidebar links when preset layouts are applied. * * @param array $menu_studio Resolved Menu Studio settings. * @param array $saved_menu Stored Menu Studio settings from settings. * @return array */ public static function merge_saved_custom_menu_items( $menu_studio, $saved_menu ) { if ( ! is_array( $menu_studio ) || ! is_array( $saved_menu ) ) { return $menu_studio; } $saved_custom = isset( $saved_menu['custom_items'] ) && is_array( $saved_menu['custom_items'] ) ? $saved_menu['custom_items'] : array(); if ( empty( $saved_custom ) ) { return $menu_studio; } $defaults = self::get_menu_studio_defaults(); $menu_studio = wp_parse_args( $menu_studio, $defaults ); $menu_studio['custom_items'] = $saved_custom; $order = isset( $menu_studio['order'] ) && is_array( $menu_studio['order'] ) ? $menu_studio['order'] : array(); $submenu_order = isset( $menu_studio['submenu_order'] ) && is_array( $menu_studio['submenu_order'] ) ? $menu_studio['submenu_order'] : array(); foreach ( $saved_custom as $item ) { if ( empty( $item['id'] ) ) { continue; } $slug = EDMINBOOST_Menu_Studio::get_custom_menu_slug( $item['id'] ); $parent = isset( $item['parent'] ) ? (string) $item['parent'] : ''; if ( '' !== $parent ) { if ( ! isset( $submenu_order[ $parent ] ) || ! is_array( $submenu_order[ $parent ] ) ) { $submenu_order[ $parent ] = array(); } if ( ! in_array( $slug, $submenu_order[ $parent ], true ) ) { $submenu_order[ $parent ][] = $slug; } continue; } if ( ! in_array( $slug, $order, true ) ) { $order[] = $slug; } } $menu_studio['order'] = $order; $menu_studio['submenu_order'] = $submenu_order; return $menu_studio; } /** * Cached map of admin menu slug => required capability. * * @var array|null */ private static $menu_capability_map = null; /** * Build a map of admin menu slugs to their required capabilities. * * @return array */ public static function get_menu_capability_map() { if ( null !== self::$menu_capability_map && ! empty( self::$menu_capability_map ) ) { return self::$menu_capability_map; } $globals = self::get_menu_capability_globals(); $menu = $globals['menu']; $submenu = $globals['submenu']; $map = array(); if ( is_array( $menu ) ) { foreach ( $menu as $menu_item ) { if ( empty( $menu_item[2] ) ) { continue; } $slug = (string) $menu_item[2]; if ( self::is_ignorable_menu_slug( $slug ) ) { continue; } $map[ $slug ] = isset( $menu_item[1] ) ? (string) $menu_item[1] : ''; } } if ( is_array( $submenu ) ) { foreach ( $submenu as $parent_slug => $submenu_items ) { if ( ! is_array( $submenu_items ) ) { continue; } foreach ( $submenu_items as $submenu_item ) { if ( empty( $submenu_item[2] ) ) { continue; } $slug = (string) $submenu_item[2]; if ( self::is_ignorable_menu_slug( $slug ) ) { continue; } $map[ $slug ] = isset( $submenu_item[1] ) ? (string) $submenu_item[1] : ''; } } } if ( empty( $map ) ) { self::$menu_capability_map = null; return $map; } self::$menu_capability_map = $map; return self::$menu_capability_map; } /** * Required capability for an admin menu slug. * * @param string $slug Menu slug or admin URL. * @return string Empty when unknown (custom links). */ public static function get_menu_slug_capability( $slug ) { $slug = self::normalize_admin_menu_slug( $slug ); if ( '' === $slug ) { return ''; } $map = self::get_menu_capability_map(); if ( isset( $map[ $slug ] ) ) { return $map[ $slug ]; } $top_slug = self::resolve_top_level_menu_slug( $slug ); if ( '' !== $top_slug && isset( $map[ $top_slug ] ) ) { return $map[ $top_slug ]; } $builtin = self::get_builtin_menu_capabilities(); if ( isset( $builtin[ $slug ] ) ) { return $builtin[ $slug ]; } if ( '' !== $top_slug && isset( $builtin[ $top_slug ] ) ) { return $builtin[ $top_slug ]; } return ''; } /** * Fallback capability map for common wp-admin slugs. * * @return array */ private static function get_builtin_menu_capabilities() { return array( 'index.php' => 'read', 'edit.php' => 'edit_posts', 'post-new.php' => 'edit_posts', 'upload.php' => 'upload_files', 'edit-comments.php' => 'edit_posts', 'themes.php' => 'switch_themes', 'plugins.php' => 'activate_plugins', 'users.php' => 'list_users', 'tools.php' => 'manage_options', 'options-general.php' => 'manage_options', 'import.php' => 'import', 'export.php' => 'export', 'edit-tags.php' => 'manage_categories', 'term.php' => 'manage_categories', 'media-new.php' => 'upload_files', 'profile.php' => 'read', 'update-core.php' => 'update_core', 'site-health.php' => 'view_site_health_checks', 'privacy.php' => 'manage_privacy_options', ); } /** * Normalize a menu slug or admin URL to a relative wp-admin path. * * @param string $slug Menu slug or admin URL. * @return string */ public static function normalize_admin_menu_slug( $slug ) { return EDMINBOOST_Command_Center_Bar::normalize_item_slug( $slug ); } /** * Whether a WordPress role can access an admin menu slug. * * @param string $role_key Role slug. * @param string $slug Menu slug or admin URL. * @return bool */ public static function role_can_access_menu_slug( $role_key, $slug ) { $role_key = sanitize_key( $role_key ); if ( '' === $role_key ) { return false; } $capability = self::get_menu_slug_capability( $slug ); if ( '' === $capability ) { return false; } return self::role_has_capability( $role_key, $capability ); } /** * Whether any of the user's roles can access an admin menu slug. * * @param string $slug Menu slug or admin URL. * @param string[] $user_roles Role slugs. * @return bool */ public static function user_roles_can_access_menu_slug( $slug, $user_roles ) { if ( empty( $user_roles ) || ! is_array( $user_roles ) ) { return false; } foreach ( $user_roles as $role ) { if ( self::role_can_access_menu_slug( $role, $slug ) ) { return true; } } return false; } /** * Whether a role has a capability (runs through WordPress meta-cap mapping). * * @param string $role_key Role slug. * @param string $capability Capability to check. * @return bool */ public static function role_has_capability( $role_key, $capability ) { $role_key = sanitize_key( $role_key ); $capability = (string) $capability; if ( '' === $role_key || '' === $capability ) { return false; } $role = get_role( $role_key ); if ( ! $role ) { return false; } return $role->has_cap( $capability ); } /** * Protected sidebar slugs that a role is allowed to access. * * @param string $role_key Role slug. * @return string[] */ public static function get_protected_slugs_for_role( $role_key ) { $protected = array(); foreach ( EDMINBOOST_Menu_Studio::get_protected_slugs() as $slug ) { if ( self::role_can_access_menu_slug( $role_key, $slug ) ) { $protected[] = $slug; } } return $protected; } /** * Accessible top-level menu slugs keyed by assignable role. * * @return array */ public static function get_role_accessible_menu_slugs() { $accessible = array(); foreach ( self::get_assignable_roles() as $role_key => $role_name ) { unset( $role_name ); $accessible[ $role_key ] = wp_list_pluck( self::get_role_matrix_menu_items( $role_key ), 'slug' ); } return $accessible; } /** * Protected sidebar slugs keyed by assignable role. * * @return array */ public static function get_protected_slugs_by_role() { $protected = array(); foreach ( self::get_assignable_roles() as $role_key => $role_name ) { unset( $role_name ); $protected[ $role_key ] = self::get_protected_slugs_for_role( $role_key ); } return $protected; } /** * Remove top bar items the current user cannot access. * * @param array[] $items Top bar item definitions. * @return array[] */ public static function filter_top_bar_items_for_user_capabilities( $items ) { if ( empty( $items ) || ! is_array( $items ) ) { return array(); } $user = wp_get_current_user(); if ( empty( $user->roles ) ) { return $items; } $filtered = array(); foreach ( $items as $item ) { $slug = isset( $item['slug'] ) ? (string) $item['slug'] : ''; if ( '' === $slug ) { continue; } if ( self::user_roles_can_access_menu_slug( $slug, $user->roles ) ) { $filtered[] = $item; } } return $filtered; } /** * Admin menu items shown in the role visibility matrix (top-level + submenu). * * @param string $role_key Optional role slug; when set, only items that role can access are returned. * @return array[] */ public static function get_role_matrix_menu_items( $role_key = '' ) { $items = array(); foreach ( self::get_discovered_menu_tree() as $item ) { if ( empty( $item['slug'] ) ) { continue; } $slug = (string) $item['slug']; $item_label = isset( $item['label'] ) ? (string) $item['label'] : $slug; $item_icon = isset( $item['icon'] ) ? (string) $item['icon'] : 'dashicons-admin-generic'; if ( '' === $role_key || self::role_can_access_menu_slug( $role_key, $slug ) ) { $items[] = array( 'slug' => $slug, 'label' => $item_label, 'icon' => $item_icon, 'icon_raw' => '', 'source' => 'top', 'parent_slug' => '', 'parent_label' => '', ); } if ( empty( $item['children'] ) || ! is_array( $item['children'] ) ) { continue; } foreach ( $item['children'] as $child ) { if ( empty( $child['slug'] ) ) { continue; } $child_slug = (string) $child['slug']; if ( '' !== $role_key && ! self::role_can_access_menu_slug( $role_key, $child_slug ) ) { continue; } $items[] = array( 'slug' => $child_slug, 'label' => isset( $child['label'] ) ? (string) $child['label'] : $child_slug, 'icon' => $item_icon, 'icon_raw' => '', 'source' => 'submenu', 'parent_slug' => $slug, 'parent_label' => $item_label, ); } } return $items; } /** * Resolve a menu slug to its top-level parent slug in the discovered tree. * * @param string $slug Menu slug. * @return string */ public static function get_top_level_menu_slug( $slug ) { return self::resolve_top_level_menu_slug( $slug ); } /** * Visible menu slugs for a Menu Studio configuration. * * @param array $menu_studio Menu Studio settings. * @return string[] */ public static function get_visible_menu_slugs_for_menu_studio( $menu_studio ) { $defaults = self::get_menu_studio_defaults(); $menu_studio = wp_parse_args( $menu_studio, $defaults ); $visible = array(); if ( empty( $menu_studio['enabled'] ) ) { foreach ( self::get_discovered_menu_items() as $item ) { if ( ! empty( $item['slug'] ) ) { $visible[] = (string) $item['slug']; } } return $visible; } $hidden = isset( $menu_studio['hidden_items'] ) && is_array( $menu_studio['hidden_items'] ) ? $menu_studio['hidden_items'] : array(); foreach ( self::get_discovered_menu_tree() as $item ) { if ( empty( $item['slug'] ) ) { continue; } $top_slug = (string) $item['slug']; if ( in_array( $top_slug, $hidden, true ) ) { continue; } $visible[] = $top_slug; if ( empty( $item['children'] ) || ! is_array( $item['children'] ) ) { continue; } foreach ( $item['children'] as $child ) { if ( empty( $child['slug'] ) ) { continue; } $child_slug = (string) $child['slug']; if ( ! in_array( $child_slug, $hidden, true ) ) { $visible[] = $child_slug; } } } return array_values( array_unique( $visible ) ); } /** * Visible menu slugs for a layout preset. * * @param string $preset_id Preset identifier. * @param array|null $cc_settings Optional Command Center settings. * @return string[] */ public static function get_preset_visible_menu_slugs( $preset_id, $cc_settings = null ) { $menu_studio = self::resolve_preset_menu_studio( $preset_id, $cc_settings ); return self::get_visible_menu_slugs_for_menu_studio( $menu_studio ); } /** * Visible top-level menu slugs for a layout preset (role matrix). * * @param string $preset_id Preset identifier. * @param array|null $cc_settings Optional Command Center settings. * @return string[] */ public static function get_preset_visible_top_level_menu_slugs( $preset_id, $cc_settings = null ) { $matrix_slugs = wp_list_pluck( self::get_role_matrix_menu_items(), 'slug' ); $top_level = array(); foreach ( self::get_preset_visible_menu_slugs( $preset_id, $cc_settings ) as $slug ) { $parent = self::resolve_top_level_menu_slug( $slug ); if ( '' !== $parent && in_array( $parent, $matrix_slugs, true ) ) { $top_level[] = $parent; } } foreach ( self::resolve_preset_top_bar_items( $preset_id, $cc_settings ) as $item ) { if ( empty( $item['slug'] ) ) { continue; } $parent = self::resolve_top_level_menu_slug( (string) $item['slug'] ); if ( '' !== $parent && in_array( $parent, $matrix_slugs, true ) ) { $top_level[] = $parent; } } return array_values( array_unique( $top_level ) ); } /** * Resolve preset top bar items against the current admin menu. * * @param string $preset_id Preset identifier. * @return array[] */ public static function resolve_preset_top_bar_items( $preset_id, $cc_settings = null ) { $preset_id = sanitize_key( $preset_id ); if ( null === $cc_settings ) { $cc_settings = self::get_settings(); } if ( 'custom' === $preset_id ) { return isset( $cc_settings['top_bar_items'] ) && is_array( $cc_settings['top_bar_items'] ) ? $cc_settings['top_bar_items'] : array(); } if ( 'default' === $preset_id ) { $preset_id = self::resolve_effective_preset_id( 'default', $cc_settings ); } $all_presets = self::get_all_presets(); if ( isset( $all_presets[ $preset_id ]['top_bar_items'] ) && is_array( $all_presets[ $preset_id ]['top_bar_items'] ) ) { return $all_presets[ $preset_id ]['top_bar_items']; } $definitions = self::get_preset_layout_definitions(); if ( ! isset( $definitions[ $preset_id ] ) ) { return array(); } $discovered = self::get_discovered_menu_items(); $by_slug = array(); foreach ( $discovered as $item ) { if ( empty( $item['slug'] ) ) { continue; } $by_slug[ $item['slug'] ] = $item; } $resolved = array(); foreach ( $definitions[ $preset_id ] as $definition ) { $slug = isset( $definition['slug'] ) ? (string) $definition['slug'] : ''; if ( '' === $slug ) { continue; } if ( isset( $by_slug[ $slug ] ) ) { $definition_icon = self::normalize_dashicon_class( isset( $definition['icon'] ) ? $definition['icon'] : '' ); $raw_discovered = isset( $by_slug[ $slug ]['icon_raw'] ) ? (string) $by_slug[ $slug ]['icon_raw'] : ''; $use_discovered = self::is_dashicon_menu_source( $raw_discovered ); $discovered_icon = self::normalize_dashicon_class( $raw_discovered, '' ); $resolved[] = array_merge( $definition, array( 'label' => $by_slug[ $slug ]['label'], 'icon' => $use_discovered ? $discovered_icon : $definition_icon, ) ); continue; } // Include core slugs even when discovery misses them (e.g. during PHPUnit). if ( self::is_core_admin_slug( $slug ) ) { $resolved[] = $definition; } } return $resolved; } /** * Preferred sidebar menu slugs per layout preset. * * @return array */ public static function get_preset_menu_studio_definitions() { $definitions = array( 'system_friend' => array( 'index.php', 'edit.php', 'edit.php?post_type=page', 'upload.php', 'edit-comments.php', ), 'system_family' => array( 'index.php', 'edit.php?post_type=page', 'upload.php', ), ); /** * Filter sidebar menu slug lists per layout preset. * * @param array $definitions Preset ID to menu slug list. */ $definitions = apply_filters( 'edminboost_preset_menu_studio_definitions', $definitions ); return array_merge( $definitions, self::get_role_preset_menu_studio_definitions() ); } /** * Sidebar menu definitions for per-role system presets. * * @return array */ private static function get_role_preset_menu_studio_definitions() { $definitions = array(); $templates = self::get_role_layout_templates(); foreach ( self::get_editable_roles_list() as $role_key => $role_details ) { $role_key = sanitize_key( $role_key ); $template_key = self::resolve_role_layout_template_key( $role_key, $role_details ); $template_key = isset( $templates[ $template_key ] ) ? $template_key : 'subscriber'; $slugs = array(); foreach ( $templates[ $template_key ] as $item ) { if ( empty( $item['slug'] ) ) { continue; } $slug = (string) $item['slug']; if ( self::role_can_access_menu_slug( $role_key, $slug ) ) { $slugs[] = $slug; } } $definitions[ self::get_role_system_preset_id( $role_key ) ] = $slugs; } return $definitions; } /** * Resolve Menu Studio settings for a layout preset. * * @param string $preset_id Preset identifier. * @param array|null $cc_settings Optional Command Center settings. * @return array */ public static function resolve_preset_menu_studio( $preset_id, $cc_settings = null ) { $preset_id = sanitize_key( $preset_id ); $defaults = self::get_menu_studio_defaults(); if ( null === $cc_settings ) { $cc_settings = self::get_settings(); } $current_menu = isset( $cc_settings['menu_studio'] ) && is_array( $cc_settings['menu_studio'] ) ? wp_parse_args( $cc_settings['menu_studio'], $defaults ) : $defaults; if ( 'custom' === $preset_id ) { return $current_menu; } if ( 'default' === $preset_id ) { $preset_id = self::resolve_effective_preset_id( 'default', $cc_settings ); } $all_presets = self::get_all_presets(); if ( isset( $all_presets[ $preset_id ]['menu_studio'] ) && is_array( $all_presets[ $preset_id ]['menu_studio'] ) ) { return wp_parse_args( $all_presets[ $preset_id ]['menu_studio'], $defaults ); } $definitions = self::get_preset_menu_studio_definitions(); $visible = array(); if ( isset( $definitions[ $preset_id ] ) ) { $visible = $definitions[ $preset_id ]; } else { foreach ( self::resolve_preset_top_bar_items( $preset_id, $cc_settings ) as $item ) { if ( ! empty( $item['slug'] ) ) { $visible[] = (string) $item['slug']; } } } if ( empty( $visible ) ) { return $current_menu; } return self::build_menu_studio_from_visible_order( $visible, $current_menu ); } /** * Ordered sidebar preview items for a layout preset. * * @param string $preset_id Preset identifier. * @param array|null $cc_settings Optional Command Center settings. * @return array[] */ public static function resolve_preset_sidebar_preview_items( $preset_id, $cc_settings = null ) { $menu_studio = self::resolve_preset_menu_studio( $preset_id, $cc_settings ); if ( empty( $menu_studio['enabled'] ) ) { $items = array(); $limit = 8; foreach ( self::get_discovered_menu_tree() as $item ) { if ( count( $items ) >= $limit ) { break; } $items[] = array( 'slug' => $item['slug'], 'label' => $item['label'], 'icon' => $item['icon'], ); } return $items; } $ordered = self::resolve_menu_studio_order( $menu_studio ); $items = array(); foreach ( $ordered as $item ) { $items[] = array( 'slug' => $item['slug'], 'label' => $item['label'], 'icon' => isset( $item['icon'] ) ? $item['icon'] : 'dashicons-admin-generic', ); } return $items; } /** * Build Menu Studio settings that show an ordered subset of sidebar menus. * * @param string[] $visible_slugs Slugs to show (top-level or submenu). * @param array $base Existing menu_studio settings to preserve styling fields from. * @return array */ private static function build_menu_studio_from_visible_order( $visible_slugs, $base = array() ) { $defaults = self::get_menu_studio_defaults(); $merged = wp_parse_args( $base, $defaults ); $tree = self::get_discovered_menu_tree(); $protected = EDMINBOOST_Menu_Studio::get_protected_slugs(); $top_slugs = array(); foreach ( $tree as $item ) { if ( ! empty( $item['slug'] ) ) { $top_slugs[] = (string) $item['slug']; } } $resolved_visible = array(); foreach ( $visible_slugs as $slug ) { $slug = (string) $slug; if ( '' === $slug ) { continue; } $top_slug = self::resolve_top_level_menu_slug( $slug, $tree ); if ( '' === $top_slug || in_array( $top_slug, $resolved_visible, true ) ) { continue; } $resolved_visible[] = $top_slug; } foreach ( $protected as $slug ) { if ( ! in_array( $slug, $resolved_visible, true ) && in_array( $slug, $top_slugs, true ) ) { $resolved_visible[] = $slug; } } $merged['enabled'] = false; $merged['order'] = $resolved_visible; $merged['hidden_items'] = array(); $merged['submenu_order'] = array(); $merged['custom_items'] = array(); return $merged; } /** * Map a menu slug to its top-level parent in the discovered tree. * * @param string $slug Menu slug. * @param array $tree Optional discovered menu tree. * @return string */ private static function resolve_top_level_menu_slug( $slug, $tree = null ) { $slug = (string) $slug; if ( '' === $slug ) { return ''; } if ( null === $tree ) { $tree = self::get_discovered_menu_tree(); } foreach ( $tree as $item ) { if ( empty( $item['slug'] ) ) { continue; } if ( $item['slug'] === $slug ) { return (string) $item['slug']; } if ( ! empty( $item['children'] ) && is_array( $item['children'] ) ) { foreach ( $item['children'] as $child ) { if ( isset( $child['slug'] ) && $child['slug'] === $slug ) { return (string) $item['slug']; } } } } $parent_map = array( 'edit.php?post_type=shop_order' => 'woocommerce', 'edit.php?post_type=product' => 'woocommerce', 'wc-admin' => 'woocommerce', ); if ( isset( $parent_map[ $slug ] ) ) { return $parent_map[ $slug ]; } if ( self::is_core_admin_slug( $slug ) ) { return $slug; } return ''; } /** * Whether a slug is a common WordPress admin screen. * * @param string $slug Menu slug. * @return bool */ private static function is_core_admin_slug( $slug ) { $core_slugs = array( 'index.php', 'edit.php', 'upload.php', 'edit.php?post_type=page', 'edit-comments.php', 'plugins.php', 'themes.php', 'tools.php', 'options-general.php', 'users.php', 'woocommerce', 'edit.php?post_type=shop_order', 'edit.php?post_type=product', 'wc-admin', ); return in_array( $slug, $core_slugs, true ); } /** * Apply a layout preset to plugin settings. * * @param string $preset_id Preset identifier. * @param bool $mark_setup_complete Whether to mark onboarding complete. * @return bool True when applied. */ public static function apply_preset( $preset_id, $mark_setup_complete = true ) { $preset_id = sanitize_key( $preset_id ); if ( 'custom' === $preset_id ) { return false; } if ( 'default' === $preset_id ) { $preset_id = self::resolve_effective_preset_id( 'default' ); } $all = self::get_all_presets(); if ( ! isset( $all[ $preset_id ] ) ) { return false; } $items = self::resolve_preset_top_bar_items( $preset_id ); if ( empty( $items ) ) { return false; } $settings = EDMINBOOST_Settings::get(); $cc = isset( $settings['command_center'] ) && is_array( $settings['command_center'] ) ? $settings['command_center'] : self::get_defaults(); $preserve_onboarding = ! $mark_setup_complete && empty( $cc['onboarding_completed'] ); $settings['command_center'] = $cc; $settings['command_center']['_apply_preset'] = $preset_id; $settings['enabled'] = true; $sanitized = EDMINBOOST_Settings::sanitize( $settings ); if ( $preserve_onboarding ) { $sanitized['command_center']['onboarding_completed'] = false; } update_option( EDMINBOOST_Settings::OPTION_NAME, $sanitized, false ); return true; } /** * Apply a look skin (behavior only). * * @param string $skin_id Skin identifier. * @param bool $mark_setup_complete Whether to mark onboarding complete. * @return bool True when applied. */ public static function apply_look_skin( $skin_id, $mark_setup_complete = true ) { $skin_id = sanitize_key( $skin_id ); $skins = self::get_look_skins(); if ( ! isset( $skins[ $skin_id ] ) ) { return false; } $settings = EDMINBOOST_Settings::get(); $cc = isset( $settings['command_center'] ) && is_array( $settings['command_center'] ) ? $settings['command_center'] : self::get_defaults(); $cc['behavior'] = $skins[ $skin_id ]['behavior']; $cc['look_skin'] = $skin_id; $cc['onboarding_completed'] = $mark_setup_complete || ! empty( $cc['onboarding_completed'] ); $settings['command_center'] = $cc; $settings['enabled'] = true; $sanitized = EDMINBOOST_Settings::sanitize( $settings ); update_option( EDMINBOOST_Settings::OPTION_NAME, $sanitized, false ); return true; } /** * Human-readable label for the active default preset. * * @param array|null $cc_settings Optional CC settings. * @return string */ public static function get_active_preset_label( $cc_settings = null ) { if ( null === $cc_settings ) { $cc_settings = self::get_settings(); } $active = self::detect_active_layout_preset( $cc_settings ); $picker = self::get_picker_presets( true ); if ( isset( $picker[ $active ]['name'] ) ) { return $picker[ $active ]['name']; } return __( 'Custom layout', 'edminboost-admin-customization' ); } /** * All presets (system + user-saved). * * @return array[] */ public static function get_all_presets() { $cc_settings = self::get_settings(); $system = self::get_system_presets(); $custom = isset( $cc_settings['presets'] ) && is_array( $cc_settings['presets'] ) ? $cc_settings['presets'] : array(); return array_merge( $system, $custom ); } /** * Register WordPress hooks for Command Center helpers. * * @return void */ public static function register_hooks() { add_action( 'admin_menu', array( __CLASS__, 'cache_admin_menu_snapshot' ), 900 ); } /** * Capture the full admin menu before Menu Studio hides or reorders items. * * @return void */ public static function cache_admin_menu_snapshot() { if ( null !== self::$discovery_snapshot ) { return; } global $menu, $submenu; if ( ! is_array( $menu ) || empty( $menu ) || ! self::admin_menu_has_dashboard( $menu ) ) { return; } $menu_copy = array_values( $menu ); $submenu_copy = is_array( $submenu ) ? $submenu : array(); self::$discovery_snapshot = array( 'menu' => $menu_copy, 'submenu' => $submenu_copy, 'tree' => self::build_discovered_menu_tree_from_globals( $menu_copy, $submenu_copy ), ); self::persist_discovery_snapshot_for_user( $menu_copy, $submenu_copy ); } /** * Ensure a discovery snapshot exists (AJAX tab loads skip admin_menu by default). * * @return void */ public static function ensure_discovery_menu_snapshot() { if ( null !== self::$discovery_snapshot ) { return; } // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WordPress core hook. if ( ! did_action( 'admin_menu' ) ) { do_action( 'admin_menu' ); } // phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound self::cache_admin_menu_snapshot(); if ( null !== self::$discovery_snapshot ) { return; } self::restore_discovery_snapshot_for_user(); } /** * Store the captured menu snapshot for the current user (AJAX discovery fallback). * * @param array $menu Top-level admin menu copy. * @param array $submenu Admin submenu copy. * @return void */ private static function persist_discovery_snapshot_for_user( $menu, $submenu ) { $user_id = get_current_user_id(); if ( ! $user_id ) { return; } update_user_meta( $user_id, self::MENU_DISCOVERY_USER_META_KEY, array( 'blog_id' => get_current_blog_id(), 'menu' => $menu, 'submenu' => $submenu, ) ); } /** * Restore a previously captured menu snapshot for a user. * * @param int $user_id User ID (defaults to current user). * @return bool True when a snapshot was restored. */ private static function restore_discovery_snapshot_for_user( $user_id = 0 ) { if ( ! $user_id ) { $user_id = get_current_user_id(); } if ( ! $user_id ) { return false; } $stored = get_user_meta( $user_id, self::MENU_DISCOVERY_USER_META_KEY, true ); if ( ! is_array( $stored ) || empty( $stored['menu'] ) || ! is_array( $stored['menu'] ) ) { return false; } if ( isset( $stored['blog_id'] ) && (int) $stored['blog_id'] !== get_current_blog_id() ) { return false; } $menu_copy = array_values( $stored['menu'] ); $submenu_copy = isset( $stored['submenu'] ) && is_array( $stored['submenu'] ) ? $stored['submenu'] : array(); if ( ! self::admin_menu_has_dashboard( $menu_copy ) ) { return false; } self::$discovery_snapshot = array( 'menu' => $menu_copy, 'submenu' => $submenu_copy, 'tree' => self::build_discovered_menu_tree_from_globals( $menu_copy, $submenu_copy ), ); return true; } /** * Menu globals used for discovery UIs (full sidebar before Menu Studio filters). * * @return array{menu: array, submenu: array} */ private static function get_discovery_menu_globals() { self::ensure_discovery_menu_snapshot(); if ( null !== self::$discovery_snapshot ) { return array( 'menu' => self::$discovery_snapshot['menu'], 'submenu' => self::$discovery_snapshot['submenu'], ); } return array( 'menu' => array(), 'submenu' => array(), ); } /** * Admin menu globals used to resolve required capabilities for menu slugs. * * Builds the map from an administrator context so slugs hidden from the * current user still resolve to the correct capability checks. * * @return array{menu: array, submenu: array} */ private static function get_menu_capability_globals() { $previous_user_id = get_current_user_id(); $switched_user = false; if ( ! user_can( $previous_user_id, EDMINBOOST_Settings::CAPABILITY ) ) { $admin_users = get_users( array( 'role' => 'administrator', 'number' => 1, 'fields' => array( 'ID' ), ) ); if ( ! empty( $admin_users ) ) { wp_set_current_user( (int) $admin_users[0]->ID ); $switched_user = true; self::$discovery_snapshot = null; self::$menu_capability_map = null; self::ensure_discovery_menu_snapshot(); } } $globals = self::get_discovery_menu_globals(); if ( $switched_user ) { wp_set_current_user( $previous_user_id ); self::$discovery_snapshot = null; self::$menu_capability_map = null; } return $globals; } /** * Build a discovered menu tree from raw admin menu globals. * * @param array $menu Global admin menu. * @param array $submenu Global admin submenu. * @return array[] */ private static function build_discovered_menu_tree_from_globals( $menu, $submenu ) { $tree = array(); if ( ! is_array( $menu ) ) { return $tree; } foreach ( $menu as $menu_item ) { if ( empty( $menu_item[2] ) ) { continue; } $slug = (string) $menu_item[2]; if ( self::is_ignorable_menu_slug( $slug ) ) { continue; } $label = wp_strip_all_tags( (string) $menu_item[0] ); if ( '' === $label ) { $label = $slug; } $icon = self::normalize_menu_icon( isset( $menu_item[6] ) ? $menu_item[6] : '' ); $children = array(); if ( is_array( $submenu ) && isset( $submenu[ $slug ] ) && is_array( $submenu[ $slug ] ) ) { foreach ( $submenu[ $slug ] as $submenu_item ) { if ( empty( $submenu_item[2] ) ) { continue; } $child_slug = (string) $submenu_item[2]; if ( self::is_ignorable_menu_slug( $child_slug ) || $child_slug === $slug ) { continue; } $child_label = wp_strip_all_tags( (string) $submenu_item[0] ); if ( '' === $child_label ) { $child_label = $child_slug; } $children[] = array( 'slug' => $child_slug, 'label' => $child_label, ); } } $tree[] = array( 'slug' => $slug, 'label' => $label, 'icon' => $icon, 'children' => $children, ); } return $tree; } /** * Whether the global admin menu includes the Dashboard entry. * * @param array $menu Global admin menu array. * @return bool */ private static function admin_menu_has_dashboard( $menu ) { if ( ! is_array( $menu ) ) { return false; } foreach ( $menu as $menu_item ) { if ( isset( $menu_item[2] ) && 'index.php' === (string) $menu_item[2] ) { return true; } } return false; } /** * Discover installed admin menu items for the layout studio. * * Includes top-level sidebar entries and submenu pages (e.g. taxonomy screens). * * @return array[] Each item: slug, label, icon, source (top|submenu). */ public static function get_discovered_menu_items() { $globals = self::get_discovery_menu_globals(); $menu = $globals['menu']; $submenu = $globals['submenu']; $items = array(); $seen_slugs = array(); $parents = array(); if ( is_array( $menu ) ) { foreach ( $menu as $menu_item ) { if ( empty( $menu_item[2] ) ) { continue; } $slug = (string) $menu_item[2]; if ( self::is_ignorable_menu_slug( $slug ) ) { continue; } $label = wp_strip_all_tags( (string) $menu_item[0] ); if ( '' === $label ) { $label = $slug; } $icon_raw = isset( $menu_item[6] ) ? (string) $menu_item[6] : ''; $icon = self::normalize_menu_icon( $icon_raw ); $parents[ $slug ] = array( 'label' => $label, 'icon' => $icon, 'icon_raw' => $icon_raw, ); $items[] = array( 'slug' => $slug, 'label' => $label, 'icon' => $icon, 'icon_raw' => $icon_raw, 'source' => 'top', ); $seen_slugs[] = $slug; } } if ( is_array( $submenu ) ) { foreach ( $submenu as $parent_slug => $submenu_items ) { if ( ! is_array( $submenu_items ) ) { continue; } $parent_slug = (string) $parent_slug; $parent_label = isset( $parents[ $parent_slug ]['label'] ) ? $parents[ $parent_slug ]['label'] : $parent_slug; $parent_icon = isset( $parents[ $parent_slug ]['icon'] ) ? $parents[ $parent_slug ]['icon'] : 'dashicons-admin-generic'; $parent_raw = isset( $parents[ $parent_slug ]['icon_raw'] ) ? $parents[ $parent_slug ]['icon_raw'] : ''; foreach ( $submenu_items as $submenu_item ) { if ( empty( $submenu_item[2] ) ) { continue; } $slug = (string) $submenu_item[2]; if ( self::is_ignorable_menu_slug( $slug ) || $slug === $parent_slug ) { continue; } if ( in_array( $slug, $seen_slugs, true ) ) { continue; } $sub_label = wp_strip_all_tags( (string) $submenu_item[0] ); if ( '' === $sub_label ) { $sub_label = $slug; } $items[] = array( 'slug' => $slug, 'label' => sprintf( /* translators: 1: parent menu label, 2: submenu label */ __( '%1$s → %2$s', 'edminboost-admin-customization' ), $parent_label, $sub_label ), 'icon' => $parent_icon, 'icon_raw' => $parent_raw, 'source' => 'submenu', ); $seen_slugs[] = $slug; } } } usort( $items, function ( $a, $b ) { return strcasecmp( $a['label'], $b['label'] ); } ); return $items; } /** * Discover admin menu as a hierarchical tree for Menu Studio. * * @return array[] Each item: slug, label, icon, children[]. */ public static function get_discovered_menu_tree() { self::ensure_discovery_menu_snapshot(); if ( null !== self::$discovery_snapshot && ! empty( self::$discovery_snapshot['tree'] ) ) { return self::$discovery_snapshot['tree']; } $globals = self::get_discovery_menu_globals(); return self::build_discovered_menu_tree_from_globals( $globals['menu'], $globals['submenu'] ); } /** * Resolve Menu Studio sidebar order for the editor canvas. * * @param array|null $menu_studio Menu Studio settings. * @return array[] Ordered top-level items with slug, label, icon, children. */ public static function resolve_menu_studio_order( $menu_studio = null ) { if ( null === $menu_studio ) { $cc_settings = self::get_settings(); $menu_studio = isset( $cc_settings['menu_studio'] ) && is_array( $cc_settings['menu_studio'] ) ? $cc_settings['menu_studio'] : self::get_menu_studio_defaults(); } $tree = self::get_discovered_menu_tree(); $hidden = isset( $menu_studio['hidden_items'] ) && is_array( $menu_studio['hidden_items'] ) ? $menu_studio['hidden_items'] : array(); $saved_order = isset( $menu_studio['order'] ) && is_array( $menu_studio['order'] ) ? $menu_studio['order'] : array(); $submenu_map = isset( $menu_studio['submenu_order'] ) && is_array( $menu_studio['submenu_order'] ) ? $menu_studio['submenu_order'] : array(); $custom_items = isset( $menu_studio['custom_items'] ) && is_array( $menu_studio['custom_items'] ) ? $menu_studio['custom_items'] : array(); $by_slug = array(); foreach ( $tree as $item ) { $by_slug[ $item['slug'] ] = $item; } foreach ( $custom_items as $custom ) { if ( empty( $custom['id'] ) || empty( $custom['label'] ) ) { continue; } $slug = EDMINBOOST_Menu_Studio::get_custom_menu_slug( $custom['id'] ); $parent = isset( $custom['parent'] ) ? (string) $custom['parent'] : ''; if ( '' !== $parent ) { if ( ! isset( $by_slug[ $parent ] ) ) { continue; } if ( ! isset( $by_slug[ $parent ]['children'] ) || ! is_array( $by_slug[ $parent ]['children'] ) ) { $by_slug[ $parent ]['children'] = array(); } $by_slug[ $parent ]['children'][] = array( 'slug' => $slug, 'label' => $custom['label'], 'custom' => true, ); continue; } $by_slug[ $slug ] = array( 'slug' => $slug, 'label' => $custom['label'], 'icon' => isset( $custom['icon'] ) ? $custom['icon'] : 'dashicons-admin-links', 'children' => array(), 'custom' => true, ); } $ordered = array(); $used = array(); foreach ( $saved_order as $slug ) { $slug = (string) $slug; if ( in_array( $slug, $hidden, true ) || ! isset( $by_slug[ $slug ] ) ) { continue; } $item = $by_slug[ $slug ]; if ( ! empty( $item['children'] ) && isset( $submenu_map[ $slug ] ) && is_array( $submenu_map[ $slug ] ) ) { $item['children'] = self::reorder_submenu_children( $item['children'], $submenu_map[ $slug ] ); } $ordered[] = $item; $used[ $slug ] = true; } foreach ( $by_slug as $slug => $item ) { if ( ! empty( $used[ $slug ] ) || in_array( $slug, $hidden, true ) ) { continue; } if ( ! empty( $item['children'] ) && isset( $submenu_map[ $slug ] ) && is_array( $submenu_map[ $slug ] ) ) { $item['children'] = self::reorder_submenu_children( $item['children'], $submenu_map[ $slug ] ); } $ordered[] = $item; } return $ordered; } /** * Reorder submenu children according to a saved slug list. * * @param array $children Discovered children. * @param string[] $slug_order Desired slug order. * @return array[] */ private static function reorder_submenu_children( $children, $slug_order ) { $by_slug = array(); foreach ( $children as $child ) { $by_slug[ $child['slug'] ] = $child; } $ordered = array(); $used = array(); foreach ( $slug_order as $slug ) { $slug = (string) $slug; if ( ! isset( $by_slug[ $slug ] ) ) { continue; } $ordered[] = $by_slug[ $slug ]; $used[ $slug ] = true; } foreach ( $children as $child ) { if ( empty( $used[ $child['slug'] ] ) ) { $ordered[] = $child; } } return $ordered; } /** * Whether a menu slug should be excluded from discovery. * * @param string $slug Menu slug. * @return bool */ private static function is_ignorable_menu_slug( $slug ) { if ( '' === $slug || '---' === $slug ) { return true; } return 0 === strpos( $slug, 'separator' ); } /** * Whether a raw admin menu icon value is a dashicon slug/class (not an image URL). * * @param string $icon Raw menu icon value. * @return bool */ private static function is_dashicon_menu_source( $icon ) { $icon = trim( (string) $icon ); if ( '' === $icon || 'none' === $icon ) { return false; } if ( false !== strpos( $icon, '://' ) || 0 === strpos( $icon, 'data:' ) ) { return false; } return (bool) preg_match( '/\bdashicons-[a-z0-9-]+\b/', $icon ) || (bool) preg_match( '/^[a-z0-9-]+$/', $icon ); } /** * Normalize a dashicon class name. * * @param mixed $icon Raw icon value. * @param string $fallback Fallback dashicon class. * @return string */ public static function normalize_dashicon_class( $icon, $fallback = 'dashicons-admin-generic' ) { if ( empty( $icon ) ) { return $fallback; } $icon = trim( (string) $icon ); if ( 'none' === $icon || false !== strpos( $icon, '://' ) || 0 === strpos( $icon, 'data:' ) ) { return $fallback; } if ( preg_match( '/\bdashicons-([a-z0-9-]+)\b/', $icon, $matches ) ) { return 'dashicons-' . $matches[1]; } $icon = preg_replace( '/\s+/', '', $icon ); if ( preg_match( '/^[a-z0-9-]+$/', $icon ) ) { return 'dashicons-' . $icon; } return $fallback; } /** * Normalize a WordPress admin menu icon value to a dashicons class. * * @param mixed $icon Raw menu icon value. * @return string */ private static function normalize_menu_icon( $icon ) { return self::normalize_dashicon_class( $icon ); } /** * Editable roles list (same source as wp-admin/user-new.php). * * @return array */ private static function get_editable_roles_list() { if ( ! function_exists( 'get_editable_roles' ) ) { require_once ABSPATH . 'wp-admin/includes/user.php'; } if ( ! function_exists( 'get_editable_roles' ) ) { return array(); } return get_editable_roles(); } /** * System preset id for a WordPress role key. * * @param string $role_key Role slug. * @return string */ public static function get_role_system_preset_id( $role_key ) { return 'system_role_' . sanitize_key( $role_key ); } /** * Built-in layout presets for each editable WordPress role. * * @return array */ public static function get_role_system_presets() { $presets = array(); foreach ( self::get_editable_roles_list() as $role_key => $role_details ) { $role_key = sanitize_key( $role_key ); $role_name = isset( $role_details['name'] ) ? $role_details['name'] : $role_key; $label = translate_user_role( $role_name ); $preset_id = self::get_role_system_preset_id( $role_key ); $presets[ $preset_id ] = array( 'name' => $label, 'description' => sprintf( /* translators: %s: WordPress user role name */ __( 'Top bar layout tuned for the %s role.', 'edminboost-admin-customization' ), $label ), 'system' => true, 'category' => 'workflow', 'role' => $role_key, ); } return $presets; } /** * Pick a layout template key for a role. * * @param string $role_key Role slug. * @param array $role_details Role data from get_editable_roles(). * @return string */ private static function resolve_role_layout_template_key( $role_key, $role_details ) { $known_roles = array( 'administrator' => 'administrator', 'editor' => 'editor', 'author' => 'author', 'contributor' => 'contributor', 'subscriber' => 'subscriber', 'shop_manager' => 'shop_manager', 'customer' => 'customer', ); if ( isset( $known_roles[ $role_key ] ) ) { return $known_roles[ $role_key ]; } $caps = isset( $role_details['capabilities'] ) && is_array( $role_details['capabilities'] ) ? $role_details['capabilities'] : array(); if ( ! empty( $caps['manage_options'] ) ) { return 'administrator'; } if ( ! empty( $caps['edit_others_posts'] ) ) { return 'editor'; } if ( ! empty( $caps['upload_files'] ) ) { return 'author'; } if ( ! empty( $caps['edit_posts'] ) ) { return 'contributor'; } return 'subscriber'; } /** * Top bar layout templates keyed by role workflow. * * @return array */ private static function get_role_layout_templates() { return array( 'administrator' => array( array( 'slug' => 'index.php', 'label' => __( 'Dashboard', 'edminboost-admin-customization' ), 'icon' => 'dashicons-dashboard', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit.php', 'label' => __( 'Posts', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-post', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'plugins.php', 'label' => __( 'Plugins', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-plugins', 'interaction' => 'drawer', 'badge_source' => 'updates', ), array( 'slug' => 'themes.php', 'label' => __( 'Appearance', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-appearance', 'interaction' => 'drawer', 'badge_source' => '', ), array( 'slug' => 'tools.php', 'label' => __( 'Tools', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-tools', 'interaction' => 'drawer', 'badge_source' => '', ), array( 'slug' => 'options-general.php', 'label' => __( 'Settings', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-settings', 'interaction' => 'drawer', 'badge_source' => '', ), ), 'editor' => array( array( 'slug' => 'index.php', 'label' => __( 'Dashboard', 'edminboost-admin-customization' ), 'icon' => 'dashicons-dashboard', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit.php', 'label' => __( 'Posts', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-post', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'upload.php', 'label' => __( 'Media', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-media', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit.php?post_type=page', 'label' => __( 'Pages', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-page', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit-comments.php', 'label' => __( 'Comments', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-comments', 'interaction' => 'redirect', 'badge_source' => 'comments', ), ), 'author' => array( array( 'slug' => 'index.php', 'label' => __( 'Dashboard', 'edminboost-admin-customization' ), 'icon' => 'dashicons-dashboard', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit.php', 'label' => __( 'Posts', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-post', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'upload.php', 'label' => __( 'Media', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-media', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit-comments.php', 'label' => __( 'Comments', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-comments', 'interaction' => 'redirect', 'badge_source' => 'comments', ), ), 'contributor' => array( array( 'slug' => 'index.php', 'label' => __( 'Dashboard', 'edminboost-admin-customization' ), 'icon' => 'dashicons-dashboard', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit.php', 'label' => __( 'Posts', 'edminboost-admin-customization' ), 'icon' => 'dashicons-admin-post', 'interaction' => 'redirect', 'badge_source' => '', ), ), 'subscriber' => array( array( 'slug' => 'index.php', 'label' => __( 'Dashboard', 'edminboost-admin-customization' ), 'icon' => 'dashicons-dashboard', 'interaction' => 'redirect', 'badge_source' => '', ), ), 'shop_manager' => array( array( 'slug' => 'index.php', 'label' => __( 'Dashboard', 'edminboost-admin-customization' ), 'icon' => 'dashicons-dashboard', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'woocommerce', 'label' => __( 'WooCommerce', 'edminboost-admin-customization' ), 'icon' => 'dashicons-cart', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'edit.php?post_type=shop_order', 'label' => __( 'Orders', 'edminboost-admin-customization' ), 'icon' => 'dashicons-list-view', 'interaction' => 'redirect', 'badge_source' => 'wc_orders', ), array( 'slug' => 'edit.php?post_type=product', 'label' => __( 'Products', 'edminboost-admin-customization' ), 'icon' => 'dashicons-products', 'interaction' => 'redirect', 'badge_source' => '', ), array( 'slug' => 'wc-admin', 'label' => __( 'Analytics', 'edminboost-admin-customization' ), 'icon' => 'dashicons-chart-bar', 'interaction' => 'drawer', 'badge_source' => '', ), ), 'customer' => array( array( 'slug' => 'index.php', 'label' => __( 'Dashboard', 'edminboost-admin-customization' ), 'icon' => 'dashicons-dashboard', 'interaction' => 'redirect', 'badge_source' => '', ), ), ); } /** * Layout definitions for per-role system presets. * * @return array */ private static function get_role_preset_layout_definitions() { $definitions = array(); $templates = self::get_role_layout_templates(); foreach ( self::get_editable_roles_list() as $role_key => $role_details ) { $role_key = sanitize_key( $role_key ); $template_key = self::resolve_role_layout_template_key( $role_key, $role_details ); $template_key = isset( $templates[ $template_key ] ) ? $template_key : 'subscriber'; $definitions[ self::get_role_system_preset_id( $role_key ) ] = $templates[ $template_key ]; } return $definitions; } /** * Get WordPress roles for the role assignment matrix. * * @return array Associative role key => display name. */ public static function get_assignable_roles() { $roles = array(); foreach ( self::get_editable_roles_list() as $role_key => $role_details ) { $role_key = sanitize_key( $role_key ); if ( '' === $role_key ) { continue; } $roles[ $role_key ] = isset( $role_details['name'] ) ? $role_details['name'] : $role_key; } return $roles; } /** * Badge telemetry source options. * * @return array */ public static function get_badge_sources() { return array( '' => __( 'None', 'edminboost-admin-customization' ), 'wc_orders' => __( 'WooCommerce — unread orders', 'edminboost-admin-customization' ), 'wc_reviews' => __( 'WooCommerce — pending reviews', 'edminboost-admin-customization' ), 'comments' => __( 'WordPress — pending comments', 'edminboost-admin-customization' ), 'updates' => __( 'WordPress — available updates', 'edminboost-admin-customization' ), 'forms_entries' => __( 'WPForms — unread entries', 'edminboost-admin-customization' ), ); } /** * Popular dashicons for the icon picker. * * @return string[] */ public static function get_dashicon_options() { return array( 'dashicons-admin-generic', 'dashicons-admin-plugins', 'dashicons-admin-settings', 'dashicons-admin-tools', 'dashicons-analytics', 'dashicons-art', 'dashicons-awards', 'dashicons-backup', 'dashicons-calendar-alt', 'dashicons-cart', 'dashicons-chart-bar', 'dashicons-clipboard', 'dashicons-cloud', 'dashicons-dashboard', 'dashicons-edit', 'dashicons-email', 'dashicons-forms', 'dashicons-format-gallery', 'dashicons-groups', 'dashicons-hammer', 'dashicons-images-alt2', 'dashicons-layout', 'dashicons-media-document', 'dashicons-megaphone', 'dashicons-performance', 'dashicons-products', 'dashicons-search', 'dashicons-shield', 'dashicons-store', 'dashicons-tag', 'dashicons-welcome-view-site', ); } }