PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 All 205 releases
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

210 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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