features = apply_filters( 'admin_hero_features', [] ); // 4) Validate & initialize each feature foreach ( $this->features as $index => &$feature ) { if ( ! $this->validate_feature( $feature ) ) { unset( $this->features[ $index ] ); continue; } $this->initialize_feature( $feature, $index ); } unset( $feature ); // 5) Sort by priority usort( $this->features, function( $a, $b ) { return ( $a['priority'] ?? 50 ) <=> ( $b['priority'] ?? 50 ); } ); } /** * Ensure a feature has the required structure. */ private function validate_feature( $feature ) { if ( ! is_array( $feature ) || empty( $feature['id'] ) || empty( $feature['name'] ) ) { return false; } $feature['id'] = sanitize_key( $feature['id'] ); $feature['name'] = sanitize_text_field( $feature['name'] ); $feature['priority'] = absint( $feature['priority'] ?? 50 ); $feature['sanitize_callback'] = $feature['sanitize_callback'] ?? 'sanitize_text_field'; return true; } /** * Initialize a single feature: set enabled state, enqueue assets, register settings UI. */ private function initialize_feature( &$feature, $index ) { $user_id = get_current_user_id(); $saved = get_user_meta( $user_id, "admin_hero_feature_{$feature['id']}", true ); $feature['enabled'] = ( $saved !== '' ) ? (bool) $saved : false; $this->features[ $index ] = $feature; // --- enqueue CSS/JS --- if ( ! empty( $feature['enqueue_callback'] ) && is_callable( $feature['enqueue_callback'] ) ) { add_action( 'admin_enqueue_scripts', $feature['enqueue_callback'] ); add_action( 'wp_enqueue_scripts', $feature['enqueue_callback'] ); } else { $base_dir = is_dir( ADMIN_HERO_DIR . "panels/{$feature['id']}/assets/" ) ? 'panels' : 'features'; $asset_dir = ADMIN_HERO_DIR . "{$base_dir}/{$feature['id']}/assets/"; $asset_url = ADMIN_HERO_URL . "{$base_dir}/{$feature['id']}/assets/"; // CSS $css_files = glob( $asset_dir . "{$feature['id']}*.css" ); if ( $css_files ) { rsort( $css_files ); $css_file = basename( $css_files[0] ); $css_cb = function() use ( $asset_url, $css_file ) { wp_enqueue_style( "admin-hero-{$feature['id']}", $asset_url . $css_file, [ 'admin-hero-css' ], ADMIN_HERO_VERSION ); }; add_action( 'admin_enqueue_scripts', $css_cb ); add_action( 'wp_enqueue_scripts', $css_cb ); } // JS $js_files = glob( $asset_dir . "{$feature['id']}*.js" ); if ( $js_files ) { rsort( $js_files ); $js_file = basename( $js_files[0] ); $js_cb = function() use ( $asset_url, $js_file ) { wp_enqueue_script( "admin-hero-{$feature['id']}", $asset_url . $js_file, [ 'admin-hero-js' ], ADMIN_HERO_VERSION, true ); }; add_action( 'admin_enqueue_scripts', $js_cb ); add_action( 'wp_enqueue_scripts', $js_cb ); } } // --- settings panel toggle registration --- $show_in_settings = isset( $feature['show_in_settings'] ) ? (bool) $feature['show_in_settings'] : true; if ( $show_in_settings ) { // if the feature defines its own settings-ui callback, use it if ( ! empty( $feature['settings_ui_callback'] ) && is_callable( $feature['settings_ui_callback'] ) ) { add_action( 'admin_hero_settings_ui', $feature['settings_ui_callback'] ); } // otherwise, if a templates/settings.php exists under that feature, include it else { $tmpl = ADMIN_HERO_DIR . "features/{$feature['id']}/templates/settings.php"; if ( file_exists( $tmpl ) ) { add_action( 'admin_hero_settings_ui', function() use ( $tmpl ) { include $tmpl; } ); } } } } /** * Return all loaded features. * * @return array */ public function get_features() { return $this->features; } /** * Return a single feature by ID or null. */ public function get_feature( $feature_id ) { foreach ( $this->features as $f ) { if ( $f['id'] === $feature_id ) { return $f; } } return null; } }