hooks(); } return self::$instance; } /** * Private constructor — go through instance(). */ private function __construct() {} /** * Hooks the WordPress actions needed by the admin. * * @return void */ private function hooks() { add_action( 'admin_menu', array( $this, 'register_menu' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); // The contextual help must be registered BEFORE WordPress prints the top of the screen // (the Help button): `current_screen` runs just before, the page callback just after. add_action( 'current_screen', array( $this, 'register_help' ) ); add_filter( 'submenu_file', array( $this, 'highlight_submenu' ) ); } /** * Registers the menu: one entry with six pages under it. The number of bookings that wait for the * owner's validation appears on the entry and on the Bookings page, like the update counters of * WordPress. * * @return void */ public function register_menu() { $awaiting = VEFMB_Admin_Tabs::awaiting_validation_count(); $bubble = static function ( $count ) { return $count > 0 ? sprintf( ' %s', esc_html( number_format_i18n( $count ) ) ) : ''; }; add_menu_page( __( 'VEFmultiBooking', 'vefmultibooking' ), __( 'VEFmultiBooking', 'vefmultibooking' ) . $bubble( $awaiting ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- the counter is escaped above. VEFMB_Capabilities::MANAGE_BOOKINGS, self::PAGE_SLUG, array( $this, 'render_page' ), 'dashicons-calendar-alt', 26 ); foreach ( VEFMB_Admin_Tabs::get_groups() as $slug => $group ) { $label = $group['title'] . ( 'bookings' === $slug ? $bubble( $awaiting ) : '' ); add_submenu_page( self::PAGE_SLUG, /* translators: %s: name of the page (Overview, Bookings, Catalog...) */ sprintf( __( 'VEFmultiBooking — %s', 'vefmultibooking' ), $group['title'] ), $label, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- the title is plain text from the plugin, the counter is escaped above. VEFMB_Capabilities::MANAGE_BOOKINGS, $group['page'], array( $this, 'render_page' ) ); } } /** * Highlights, in the menu, the page that holds the tab being displayed (an older address such as * `page=vefmultibooking&tab=offers` is shown on the Catalog page). * * @param string|null $submenu_file Submenu slug WordPress chose. * @return string|null */ public function highlight_submenu( $submenu_file ) { if ( ! VEFMB_Admin_Tabs::is_plugin_page() ) { return $submenu_file; } $groups = VEFMB_Admin_Tabs::get_groups(); $group = VEFMB_Admin_Tabs::group_of( VEFMB_Admin_Tabs::get_active_tab() ); return isset( $groups[ $group ] ) ? $groups[ $group ]['page'] : $submenu_file; } /** * Adds the contextual help (the Help button at the top right of the screen) of the tab being * displayed — before WordPress prints the top of the screen. * * @param WP_Screen $screen Screen being loaded. * @return void */ public function register_help( $screen ) { if ( ! $screen || ! VEFMB_Admin_Tabs::is_plugin_page() || ! current_user_can( VEFMB_Capabilities::MANAGE_BOOKINGS ) ) { return; } VEFMB_Admin_Tabs::register_contextual_help( VEFMB_Admin_Tabs::get_active_tab(), $screen ); } /** * Loads the admin CSS/JS only on the plugin screen — never * globally (avoids any conflict with other plugins/WP screens). * * @param string $hook_suffix Identifier of the current admin screen. * @return void */ public function enqueue_assets( $hook_suffix ) { if ( ! VEFMB_Admin_Tabs::is_plugin_page() ) { return; } wp_enqueue_style( 'vefmb-admin', VEFMB_PLUGIN_URL . 'assets/css/admin.css', array(), VEFMB_VERSION ); // Color pickers of the Style tab (WordPress core component). wp_enqueue_style( 'wp-color-picker' ); wp_enqueue_script( 'vefmb-admin', VEFMB_PLUGIN_URL . 'assets/js/admin.js', array( 'wp-i18n', 'jquery', 'wp-color-picker' ), VEFMB_VERSION, true ); wp_set_script_translations( 'vefmb-admin', 'vefmultibooking' ); wp_localize_script( 'vefmb-admin', 'vefmbDisplay', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'vefmb_flow_display' ), ) ); } /** * Draws an admin page: the header of the page, the tabs of its group, the content of the active * tab. (The contextual help is registered earlier, by register_help().) * * @return void */ public function render_page() { if ( ! current_user_can( VEFMB_Capabilities::MANAGE_BOOKINGS ) ) { return; } // The tab asked decides the page it is shown on (older addresses keep working). $active_tab = VEFMB_Admin_Tabs::get_active_tab(); $active_group = VEFMB_Admin_Tabs::group_of( $active_tab ); require VEFMB_PLUGIN_DIR . 'includes/admin/views/partials/admin-header.php'; require VEFMB_PLUGIN_DIR . 'includes/admin/views/partials/tab-nav.php'; if ( VEFMB_Settings::is_online_booking_closed() ) { ?>