booking
/
includes
/
page-setup-wizard
/
first-run-launcher
/
class-wpbc-setup-wizard-first-run-launcher.php
class-wpbc-setup-wizard-first-run-launcher.php in Booking Calendar 11.9, at includes/page-setup-wizard/first-run-launcher/class-wpbc-setup-wizard-first-run-launcher.php
| 1 | <?php |
| 2 | /** |
| 3 | * First-install Setup Wizard launcher component. |
| 4 | * |
| 5 | * @package Booking Calendar |
| 6 | */ |
| 7 | |
| 8 | if ( ! defined( 'ABSPATH' ) ) { |
| 9 | exit; |
| 10 | } |
| 11 | |
| 12 | /** |
| 13 | * Own the one-time Booking Listing invitation to start the Setup Wizard. |
| 14 | * |
| 15 | * Existing installation option names and AJAX contracts are preserved. The |
| 16 | * new owner/site-scoped checkpoint is authoritative for completion suppression; |
| 17 | * the released legacy completion option is deliberately not consulted. |
| 18 | */ |
| 19 | final class WPBC_Setup_Wizard_First_Run_Launcher { |
| 20 | |
| 21 | /** Logged-in WordPress AJAX action used to consume the invitation. */ |
| 22 | const AJAX_ACTION = 'WPBC_AJX_SETUP_WIZARD_FIRST_RUN_PROMPT'; |
| 23 | |
| 24 | /** Nonce action protecting invitation state changes. */ |
| 25 | const NONCE_ACTION = 'wpbc_setup_wizard_first_run_prompt_wpbcnonce'; |
| 26 | |
| 27 | /** Runtime browser adapter handle. */ |
| 28 | const SCRIPT_HANDLE = 'wpbc-setup-wizard-first-run-launcher'; |
| 29 | |
| 30 | /** Runtime presentation handle. */ |
| 31 | const STYLE_HANDLE = 'wpbc-setup-wizard-first-run-launcher'; |
| 32 | |
| 33 | /** @var bool Whether launcher configuration was localized in this request. */ |
| 34 | private static $assets_configured = false; |
| 35 | |
| 36 | /** |
| 37 | * Register route-scoped assets, template rendering, and the AJAX endpoint. |
| 38 | * |
| 39 | * @return void |
| 40 | */ |
| 41 | public static function register() { |
| 42 | add_action( 'wpbc_enqueue_js_files', array( __CLASS__, 'enqueue_scripts' ), 60 ); |
| 43 | add_action( 'wpbc_enqueue_css_files', array( __CLASS__, 'enqueue_styles' ), 60 ); |
| 44 | add_action( 'wpbc_hook_settings_page_footer', array( __CLASS__, 'render_template' ) ); |
| 45 | add_action( 'wp_ajax_' . self::AJAX_ACTION, array( __CLASS__, 'ajax_dismiss' ) ); |
| 46 | } |
| 47 | |
| 48 | /** |
| 49 | * Build the current owner/site checkpoint store. |
| 50 | * |
| 51 | * @return WPBC_Setup_Wizard_Draft_Store Context-bound checkpoint store. |
| 52 | */ |
| 53 | private static function get_checkpoint_store() { |
| 54 | $module_registry = new WPBC_Setup_Wizard_Step_Module_Registry(); |
| 55 | $step_registry = new WPBC_Setup_Wizard_Step_Registry( $module_registry ); |
| 56 | $step_data = new WPBC_Setup_Wizard_Step_Data( $module_registry ); |
| 57 | |
| 58 | return new WPBC_Setup_Wizard_Draft_Store( $step_registry, new WPBC_Setup_Wizard_Draft_Validator( $step_data ) ); |
| 59 | } |
| 60 | |
| 61 | /** |
| 62 | * Check whether the production checkpoint is complete for this context. |
| 63 | * |
| 64 | * @return bool True when the normalized checkpoint has terminal status. |
| 65 | */ |
| 66 | private static function is_checkpoint_completed() { |
| 67 | $checkpoint = self::get_checkpoint_store()->load(); |
| 68 | |
| 69 | return isset( $checkpoint['status'] ) && 'completed' === $checkpoint['status']; |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * Check whether the first-install invitation remains pending. |
| 74 | * |
| 75 | * @return bool True when this site has an unconsumed, incomplete invitation. |
| 76 | */ |
| 77 | public static function is_prompt_pending() { |
| 78 | return ! WPBC_Setup_Wizard_First_Install_State::is_automatic_onboarding_restricted() |
| 79 | && WPBC_Setup_Wizard_First_Install_State::is_initial_install_site() |
| 80 | && 'On' === get_bk_option( 'booking_setup_wizard_first_run_prompt' ) |
| 81 | && ! self::is_checkpoint_completed(); |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * Check whether the launcher belongs on this request. |
| 86 | * |
| 87 | * @return bool True for the eligible Booking Listing request. |
| 88 | */ |
| 89 | private static function should_render() { |
| 90 | return WPBC_Setup_Wizard_Booking_Pages::is_booking_listing_route() |
| 91 | && self::is_prompt_pending(); |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | * Enqueue and localize the compiled launcher adapter. |
| 96 | * |
| 97 | * @param string $where_to_load Booking Calendar asset context. |
| 98 | * |
| 99 | * @return void |
| 100 | */ |
| 101 | public static function enqueue_scripts( $where_to_load ) { |
| 102 | if ( self::$assets_configured || ! in_array( $where_to_load, array( 'admin', 'both' ), true ) || ! self::should_render() ) { |
| 103 | return; |
| 104 | } |
| 105 | |
| 106 | $script_path = __DIR__ . '/_out/first-run-launcher.js'; |
| 107 | $version = file_exists( $script_path ) ? (string) filemtime( $script_path ) : WP_BK_VERSION_NUM; |
| 108 | wp_enqueue_script( |
| 109 | self::SCRIPT_HANDLE, |
| 110 | wpbc_plugin_url( '/includes/page-setup-wizard/first-run-launcher/_out/first-run-launcher.js' ), |
| 111 | array( 'jquery', 'wp-util', 'wpbc-modal' ), |
| 112 | $version, |
| 113 | true |
| 114 | ); |
| 115 | wp_localize_script( |
| 116 | self::SCRIPT_HANDLE, |
| 117 | 'wpbc_setup_wizard_first_run_launcher_vars', |
| 118 | array( |
| 119 | 'ajax_url' => admin_url( 'admin-ajax.php', 'relative' ), |
| 120 | 'action' => self::AJAX_ACTION, |
| 121 | 'nonce' => wp_create_nonce( self::NONCE_ACTION ), |
| 122 | 'setup_url' => wpbc_get_setup_wizard_page_url(), |
| 123 | 'show' => true, |
| 124 | 'show_booking_pages_after_dismiss' => WPBC_Setup_Wizard_Booking_Pages::can_follow_setup_prompt(), |
| 125 | ) |
| 126 | ); |
| 127 | |
| 128 | self::$assets_configured = true; |
| 129 | } |
| 130 | |
| 131 | /** |
| 132 | * Enqueue the compiled launcher presentation. |
| 133 | * |
| 134 | * @param string $where_to_load Booking Calendar asset context. |
| 135 | * |
| 136 | * @return void |
| 137 | */ |
| 138 | public static function enqueue_styles( $where_to_load ) { |
| 139 | if ( ! in_array( $where_to_load, array( 'admin', 'both' ), true ) || ! self::should_render() ) { |
| 140 | return; |
| 141 | } |
| 142 | |
| 143 | $style_path = __DIR__ . '/_out/first-run-launcher.css'; |
| 144 | $version = file_exists( $style_path ) ? (string) filemtime( $style_path ) : WP_BK_VERSION_NUM; |
| 145 | wp_enqueue_style( |
| 146 | self::STYLE_HANDLE, |
| 147 | wpbc_plugin_url( '/includes/page-setup-wizard/first-run-launcher/_out/first-run-launcher.css' ), |
| 148 | array(), |
| 149 | $version |
| 150 | ); |
| 151 | } |
| 152 | |
| 153 | /** |
| 154 | * Render the fixed launcher template in the Booking Listing footer. |
| 155 | * |
| 156 | * @param string $page Booking Calendar footer context. |
| 157 | * |
| 158 | * @return void |
| 159 | */ |
| 160 | public static function render_template( $page ) { |
| 161 | if ( 'wpbc-ajx_booking' !== $page || ! self::should_render() ) { |
| 162 | return; |
| 163 | } |
| 164 | |
| 165 | $template_path = __DIR__ . '/templates/first-run-popup-wptpl.php'; |
| 166 | if ( is_readable( $template_path ) ) { |
| 167 | require $template_path; |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | * Persist that the one-time invitation must not be shown again. |
| 173 | * |
| 174 | * @return void |
| 175 | */ |
| 176 | public static function ajax_dismiss() { |
| 177 | if ( ! check_ajax_referer( self::NONCE_ACTION, 'nonce', false ) ) { |
| 178 | wp_send_json_error( array( 'message' => __( 'Security check failed.', 'booking' ) ), 403 ); |
| 179 | } |
| 180 | |
| 181 | if ( ! WPBC_Setup_Wizard_Access::current_user_can_access() ) { |
| 182 | wp_send_json_error( array( 'message' => __( 'You do not have access to Initial Setup.', 'booking' ) ), 403 ); |
| 183 | } |
| 184 | |
| 185 | if ( WPBC_Setup_Wizard_First_Install_State::is_automatic_onboarding_restricted() ) { |
| 186 | wp_send_json_error( array( 'message' => __( 'The Initial Setup prompt is not available in this environment.', 'booking' ) ), 409 ); |
| 187 | } |
| 188 | |
| 189 | // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified above with check_ajax_referer(). |
| 190 | $choice = isset( $_POST['choice'] ) && is_scalar( $_POST['choice'] ) |
| 191 | // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified above with check_ajax_referer(). |
| 192 | ? sanitize_key( wp_unslash( $_POST['choice'] ) ) |
| 193 | : ''; |
| 194 | if ( ! in_array( $choice, array( 'dismiss', 'start' ), true ) ) { |
| 195 | wp_send_json_error( array( 'message' => __( 'The Initial Setup choice is not valid.', 'booking' ) ), 400 ); |
| 196 | } |
| 197 | |
| 198 | if ( ! WPBC_Setup_Wizard_First_Install_State::is_initial_install_site() ) { |
| 199 | wp_send_json_error( array( 'message' => __( 'The Initial Setup prompt is not available for this installation.', 'booking' ) ), 409 ); |
| 200 | } |
| 201 | |
| 202 | update_bk_option( 'booking_setup_wizard_first_run_prompt', 'Off' ); |
| 203 | if ( 'Off' !== get_bk_option( 'booking_setup_wizard_first_run_prompt' ) ) { |
| 204 | wp_send_json_error( array( 'message' => __( 'The Initial Setup choice could not be saved.', 'booking' ) ), 500 ); |
| 205 | } |
| 206 | |
| 207 | wp_send_json_success( array( 'choice' => $choice ) ); |
| 208 | } |
| 209 | } |
| 210 |