'Version' ] ); define( 'ADMIN_HERO_VERSION', $plugin_data['Version'] ); define( 'ADMIN_HERO_DIR', plugin_dir_path( __FILE__ ) ); define( 'ADMIN_HERO_URL', plugin_dir_url( __FILE__ ) ); // ----------------------------------------------------------------------------- // LOAD FEATURE LOADER (dynamic versioned file) // ----------------------------------------------------------------------------- $loader_files = glob( ADMIN_HERO_DIR . 'includes/class-feature-loader*.php' ); if ( ! empty( $loader_files ) ) { rsort( $loader_files ); require_once $loader_files[0]; } elseif ( file_exists( ADMIN_HERO_DIR . 'includes/class-feature-loader.php' ) ) { require_once ADMIN_HERO_DIR . 'includes/class-feature-loader.php'; } else { add_action( 'admin_notices', function() { echo '

AdminHero error: feature-loader file is missing.

'; } ); return; } class Admin_Hero { /** @var Admin_Hero_Feature_Loader */ private $feature_loader; public function __construct() { $this->feature_loader = new Admin_Hero_Feature_Loader(); $this->setup_hooks(); } private function setup_hooks() { // 1) Load all panel- and feature-bootstrappers add_action( 'plugins_loaded', [ $this, 'load_features' ] ); // 2) Enqueue & render modal in Dashboard and front-end add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] ); add_action( 'admin_footer', [ $this, 'render_modal' ] ); add_action( 'wp_footer', [ $this, 'render_modal' ] ); // 3) AJAX handlers add_action( 'wp_ajax_admin_hero_save_note', [ $this, 'save_note' ] ); add_action( 'wp_ajax_admin_hero_refresh_nonce', [ $this, 'refresh_nonce' ] ); add_action( 'wp_ajax_admin_hero_save_settings', [ $this, 'save_settings' ] ); add_action( 'wp_ajax_admin_hero_mark_onboarding_seen', [ $this, 'mark_onboarding_seen' ] ); add_action( 'wp_ajax_admin_hero_reset_onboarding', [ $this, 'reset_onboarding' ] ); } public function load_features() { // panel bootstrappers $info_boot = glob( ADMIN_HERO_DIR . 'panels/info/info*.php' ); if ( ! empty( $info_boot ) ) { require_once $info_boot[0]; } $tasks_boot = glob( ADMIN_HERO_DIR . 'panels/tasks/tasks*.php' ); if ( ! empty( $tasks_boot ) ) { require_once $tasks_boot[0]; } // real features $this->feature_loader->load_features( ADMIN_HERO_DIR . 'features/' ); } public function enqueue_assets() { if ( ! current_user_can( 'edit_posts' ) ) { return; } $is_admin = is_admin(); $frontend_feat = $this->feature_loader->get_feature( 'frontend' ); $is_frontend = ! $is_admin && $frontend_feat && ! empty( $frontend_feat['enabled'] ); if ( ! ( $is_admin || $is_frontend ) ) { return; } // core CSS $css_files = glob( ADMIN_HERO_DIR . 'assets/css/admin-hero*.css' ); if ( ! empty( $css_files ) ) { rsort( $css_files ); $css_file = basename( $css_files[0] ); } else { $css_file = 'admin-hero.css'; } wp_enqueue_style( 'admin-hero-css', ADMIN_HERO_URL . 'assets/css/' . $css_file, [], ADMIN_HERO_VERSION ); // core JS $js_files = glob( ADMIN_HERO_DIR . 'assets/js/admin-hero*.js' ); if ( ! empty( $js_files ) ) { rsort( $js_files ); $js_file = basename( $js_files[0] ); } else { $js_file = 'admin-hero.js'; } wp_enqueue_script( 'admin-hero-js', ADMIN_HERO_URL . 'assets/js/' . $js_file, [ 'jquery' ], ADMIN_HERO_VERSION, true ); // third-party assets wp_enqueue_style( 'font-awesome', ADMIN_HERO_URL . 'assets/fontawesome/css/all.min.css', [], '6.7.2' ); wp_enqueue_style( 'quill-css', ADMIN_HERO_URL . 'assets/quilleditor/quill.snow.css', [], '2.0.3' ); wp_enqueue_script( 'quill-js', ADMIN_HERO_URL . 'assets/quilleditor/quill.min.js', [], '2.0.3', true ); // get default panel setting $all_settings = get_option( 'admin_hero_settings', [] ); $default_panel = isset( $all_settings['default_panel'] ) ? $all_settings['default_panel'] : 'settings'; // localize wp_localize_script( 'admin-hero-js', 'AdminHero', [ 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'admin_hero_action' ), 'version' => ADMIN_HERO_VERSION, 'is_admin' => $is_admin, 'is_pro' => defined( 'ADMIN_HERO_PRO_VERSION' ) && ADMIN_HERO_PRO_VERSION, 'logo_url' => ADMIN_HERO_URL . 'assets/img/ah-logo.png', 'logo_pro_url' => ADMIN_HERO_URL . 'assets/img/ah-logo-pro.webp', 'features' => array_values( $this->feature_loader->get_features() ), 'defaultOpenPanel' => $default_panel, 'onboardingSeen' => get_user_meta( get_current_user_id(), 'admin_hero_onboarding_seen', true ) ? true : false, ] ); // per-feature enqueue callbacks foreach ( $this->feature_loader->get_features() as $feature ) { if ( ! empty( $feature['enabled'] ) && ! empty( $feature['enqueue_callback'] ) && is_callable( $feature['enqueue_callback'] ) ) { call_user_func( $feature['enqueue_callback'] ); } } } public function render_modal() { if ( ! current_user_can( 'edit_posts' ) ) { return; } $is_admin = is_admin(); $frontend_feat = $this->feature_loader->get_feature( 'frontend' ); $is_frontend = ! $is_admin && $frontend_feat && ! empty( $frontend_feat['enabled'] ); if ( ! ( $is_admin || $is_frontend ) ) { return; } $user = wp_get_current_user(); $note = get_user_meta( $user->ID, 'admin_hero_text', true ); $last_saved = get_user_meta( $user->ID, 'admin_hero_last_saved', true ); $timestamp = 'Last saved: ' . ( $last_saved ? date_i18n( ADMIN_HERO_TIMESTAMP_FORMAT, strtotime( $last_saved ) ) : 'Never saved' ); $all_settings = get_option( 'admin_hero_settings', [] ); $default = isset( $all_settings['default_panel'] ) ? $all_settings['default_panel'] : 'settings'; $nonce = wp_create_nonce( 'admin_hero_action' ); $onboarding_flag = ! get_user_meta( $user->ID, 'admin_hero_onboarding_seen', true ); do_action( 'admin_hero_render_modal_before', $user, $note, $timestamp, $nonce ); // dynamic modal template $modal_files = glob( ADMIN_HERO_DIR . 'templates/modal*.php' ); $modal_file = ! empty( $modal_files ) ? $modal_files[0] : ADMIN_HERO_DIR . 'templates/modal.php'; $modal_file = apply_filters( 'admin_hero_modal_template', $modal_file ); if ( file_exists( $modal_file ) ) { include $modal_file; } do_action( 'admin_hero_render_modal_after' ); } public function save_note() { check_ajax_referer( 'admin_hero_action', 'nonce' ); if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( [ 'message' => 'Unauthorized' ] ); } $user_id = get_current_user_id(); $note = wp_kses_post( wp_unslash( $_POST['note'] ?? '' ) ); update_user_meta( $user_id, 'admin_hero_text', $note ); update_user_meta( $user_id, 'admin_hero_last_saved', current_time( 'mysql' ) ); wp_send_json_success([ 'message' => 'Note saved', 'timestamp' => date_i18n( ADMIN_HERO_TIMESTAMP_FORMAT, current_time( 'timestamp' ) ), ]); } public function refresh_nonce() { if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( [ 'message' => 'Unauthorized' ] ); } wp_send_json_success( [ 'nonce' => wp_create_nonce( 'admin_hero_action' ) ] ); } public function save_settings() { check_ajax_referer( 'admin_hero_action', 'nonce' ); if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( [ 'message' => 'Unauthorized' ] ); } // Grab entire settings array $raw_settings = filter_input( INPUT_POST, 'settings', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY ); $settings = is_array( $raw_settings ) ? wp_unslash( $raw_settings ) : []; // Sanitize default_panel if ( isset( $settings['default_panel'] ) ) { $valid_panels = [ 'notes', 'tasks', 'info', 'settings' ]; $settings['default_panel'] = in_array( $settings['default_panel'], $valid_panels ) ? $settings['default_panel'] : 'settings'; } // Persist full settings array to options update_option( 'admin_hero_settings', $settings ); // Save individual feature flags to usermeta foreach ( $settings as $feature_id => $value ) { if ( 'default_panel' === $feature_id ) { continue; } $feature = $this->feature_loader->get_feature( $feature_id ); if ( $feature && ! empty( $feature['sanitize_callback'] ) ) { $value = call_user_func( $feature['sanitize_callback'], $value ); } update_user_meta( get_current_user_id(), "admin_hero_feature_{$feature_id}", sanitize_text_field( $value ) ); } wp_send_json_success( [ 'message' => 'Settings saved' ] ); } public function mark_onboarding_seen() { check_ajax_referer( 'admin_hero_action', 'nonce' ); if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( [ 'message' => 'Unauthorized' ] ); } update_user_meta( get_current_user_id(), 'admin_hero_onboarding_seen', true ); wp_send_json_success(); } public function reset_onboarding() { check_ajax_referer( 'admin_hero_action', 'nonce' ); if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( [ 'message' => 'Unauthorized' ] ); } delete_user_meta( get_current_user_id(), 'admin_hero_onboarding_seen' ); wp_send_json_success(); } } new Admin_Hero();