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 / class-wpbc-setup-wizard-page.php

class-wpbc-setup-wizard-page.php in Booking Calendar 11.9, at includes/page-setup-wizard/class-wpbc-setup-wizard-page.php

360 lines 14.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Native administration page for the production Setup Wizard shell.
4 *
5 * @package Booking Calendar
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 /**
13 * Render the resumable wizard or its completed native administration overview.
14 */
15 final class WPBC_Setup_Wizard_Page extends WPBC_Page_Structure {
16
17 /** @var string Safe Review-summary email error retained for this request. */
18 private $review_email_error = '';
19
20 /** @var array<string,mixed>|null Request-local normalized checkpoint cache. */
21 private $current_checkpoint = null;
22
23 /** @var array<string,mixed>|null Request-local completed overview cache. */
24 private $setup_overview_context = null;
25
26 /**
27 * Attach page-scoped assets and body classes.
28 */
29 public function __construct() {
30 parent::__construct();
31
32 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
33 add_filter( 'admin_body_class', array( $this, 'add_body_class' ) );
34 }
35
36 /**
37 * Return the canonical menu slug for this page.
38 *
39 * @return string Canonical Setup Wizard menu slug.
40 */
41 public function in_page() {
42 return 'wpbc-setup';
43 }
44
45 /**
46 * Define either the resumable full-screen wizard or completed native overview.
47 *
48 * @return array<string,array<string,mixed>> Native page tab definition.
49 */
50 public function tabs() {
51 if ( $this->is_completed_checkpoint() ) {
52 return array(
53 'overview' => array(
54 'is_force_full_screen' => false,
55 'is_force_normal_screen' => true,
56 'is_default_full_screen' => false,
57 'left_navigation__default_view_mode' => '',
58 'left_navigation__force_view_mode' => '',
59 'is_show_top_path' => true,
60 'is_show_top_navigation' => false,
61 'top_path_title' => __( 'Setup overview', 'booking' ),
62 'title' => __( 'Setup overview', 'booking' ),
63 'page_title' => __( 'Setup overview', 'booking' ),
64 'page_description' => __( 'Review your booking settings and choose what to do next.', 'booking' ),
65 'hint' => __( 'Review your completed Booking Calendar setup.', 'booking' ),
66 'font_icon' => 'wpbc-bi-list-check',
67 'default' => true,
68 'disabled' => false,
69 'hided' => false,
70 'subtabs' => array(),
71 'content' => 'content',
72 ),
73 );
74 }
75
76 return array(
77 'wizard' => array(
78 'is_force_full_screen' => true,
79 'left_navigation__default_view_mode' => 'none',
80 'left_navigation__force_view_mode' => 'none',
81 'is_show_top_path' => false,
82 'is_show_top_navigation' => false,
83 'title' => __( 'Setup Wizard', 'booking' ),
84 'page_title' => false,
85 'default' => true,
86 'disabled' => false,
87 'hided' => false,
88 'subtabs' => array(),
89 'content' => 'content',
90 ),
91 );
92 }
93
94 /**
95 * Add a body class only for the authorized Setup Wizard request.
96 *
97 * @param string $classes Space-delimited administration body classes.
98 *
99 * @return string Updated body classes.
100 */
101 public function add_body_class( $classes ) {
102 if ( self::is_current_request() && WPBC_Setup_Wizard_Access::current_user_can_access() ) {
103 $classes .= $this->is_completed_checkpoint() ? ' wpbc_setup_overview_page' : ' wpbc_setup_wizard_page';
104 }
105
106 return $classes;
107 }
108
109 /**
110 * Enqueue only compiled runtime assets on the matching authorized page.
111 *
112 * @return void
113 */
114 public function enqueue_assets() {
115 if ( ! self::is_current_request() || ! WPBC_Setup_Wizard_Access::current_user_can_access() ) {
116 return;
117 }
118
119 $module_url = trailingslashit( plugins_url( '', __FILE__ ) );
120 $asset_url = $module_url . '_out/';
121 $asset_version = defined( 'WP_BK_VERSION_NUM' ) ? WP_BK_VERSION_NUM : false;
122
123 if ( $this->is_completed_checkpoint() ) {
124 $overview_asset_url = $module_url . 'setup-overview/_out/';
125 $overview = $this->get_setup_overview_context();
126 $published_pages = isset( $overview['published_pages'] ) && is_array( $overview['published_pages'] ) ? $overview['published_pages'] : array();
127 $script_dependencies = array();
128 if (
129 ! empty( $published_pages )
130 && class_exists( 'WPBC_Setup_Wizard_Booking_Pages' )
131 && WPBC_Setup_Wizard_Booking_Pages::enqueue_manual_dialog( $published_pages )
132 ) {
133 $script_dependencies[] = WPBC_Setup_Wizard_Booking_Pages::SCRIPT_HANDLE;
134 }
135 wp_enqueue_style( 'wpbc-setup-overview', $overview_asset_url . 'setup-overview.css', array(), $asset_version );
136 wp_enqueue_script( 'wpbc-setup-overview', $overview_asset_url . 'setup-overview.js', $script_dependencies, $asset_version, true );
137
138 return;
139 }
140
141 $module_registry = new WPBC_Setup_Wizard_Step_Module_Registry();
142 $step_registry = new WPBC_Setup_Wizard_Step_Registry( $module_registry );
143 $step_data = new WPBC_Setup_Wizard_Step_Data( $module_registry );
144 $draft_store = new WPBC_Setup_Wizard_Draft_Store( $step_registry, new WPBC_Setup_Wizard_Draft_Validator( $step_data ) );
145 $current_draft = $this->get_current_checkpoint();
146
147 if ( 'completed' !== $current_draft['status'] && 'review_setup' === $current_draft['current_step'] ) {
148 $review_context = $step_data->get_step_context( 'review_setup', $current_draft['values'], $current_draft['step_results'] );
149 $email_result = ( new WPBC_Setup_Wizard_Summary_Email( $draft_store, new WPBC_Setup_Wizard_WordPress_Operation_Lock() ) )->maybe_send( $review_context );
150 if ( is_wp_error( $email_result ) ) {
151 $this->review_email_error = sanitize_text_field( $email_result->get_error_message() );
152 }
153 $current_draft = $draft_store->load();
154 $this->current_checkpoint = $current_draft;
155 }
156
157 wp_enqueue_style( 'wpbc-setup-wizard', $asset_url . 'setup-wizard.css', array(), $asset_version );
158 wp_enqueue_script( 'wpbc-setup-wizard', $asset_url . 'setup-wizard.js', array( 'jquery' ), $asset_version, true );
159 wp_localize_script(
160 'wpbc-setup-wizard',
161 'wpbc_setup_wizard',
162 array(
163 'ajax_url' => admin_url( 'admin-ajax.php' ),
164 'nonce' => wp_create_nonce( WPBC_Setup_Wizard_Ajax::NONCE_ACTION ),
165 'current_step' => $current_draft['current_step'],
166 'revision' => (int) $current_draft['revision'],
167 'action_save_continue' => WPBC_Setup_Wizard_Ajax::ACTION_SAVE_CONTINUE,
168 'action_back' => WPBC_Setup_Wizard_Ajax::ACTION_BACK,
169 'action_edit' => WPBC_Setup_Wizard_Ajax::ACTION_EDIT,
170 'action_restart' => WPBC_Setup_Wizard_Ajax::ACTION_RESTART,
171 'action_skip' => WPBC_Setup_Wizard_Ajax::ACTION_SKIP,
172 'i18n' => array(
173 'error' => __( 'The Setup Wizard request could not be completed. Try again.', 'booking' ),
174 'saved' => __( 'Your Setup Wizard progress was saved.', 'booking' ),
175 'required' => __( 'This field is required.', 'booking' ),
176 'invalid_email' => __( 'Enter a valid booking email address.', 'booking' ),
177 'validation_error' => __( 'Review the highlighted fields before continuing.', 'booking' ),
178 'working' => __( 'Processing', 'booking' ) . '...',
179 'loading' => __( 'Loading', 'booking' ) . '...',
180 'completed' => __( 'Your Setup Wizard checkpoint is complete.', 'booking' ),
181 ),
182 )
183 );
184
185 $module_registry->enqueue_assets(
186 $current_draft['current_step'],
187 $module_url,
188 $asset_version,
189 'wpbc-setup-wizard',
190 'wpbc-setup-wizard'
191 );
192 }
193
194 /**
195 * Render the authorized full-screen shell and current allow-listed step.
196 *
197 * @return void
198 */
199 public function content() {
200 if ( ! WPBC_Setup_Wizard_Access::current_user_can_access() ) {
201 wp_die(
202 esc_html__( 'You are not allowed to use this Setup Wizard.', 'booking' ),
203 esc_html__( 'Access denied', 'booking' ),
204 array( 'response' => 403 )
205 );
206 }
207
208 if ( $this->is_completed_checkpoint() ) {
209 $this->render_setup_overview();
210
211 return;
212 }
213
214 $module_registry = new WPBC_Setup_Wizard_Step_Module_Registry();
215 $step_registry = new WPBC_Setup_Wizard_Step_Registry( $module_registry );
216 $step_data = new WPBC_Setup_Wizard_Step_Data( $module_registry );
217 $draft_store = new WPBC_Setup_Wizard_Draft_Store( $step_registry, new WPBC_Setup_Wizard_Draft_Validator( $step_data ) );
218 $templates = new WPBC_Setup_Wizard_Templates( $module_registry );
219 $current_draft = $this->get_current_checkpoint();
220 $current_step = $step_registry->get_step( $current_draft['current_step'] );
221 $is_review_step = 'review_setup' === $current_draft['current_step'];
222 $bookings_url = function_exists( 'wpbc_get_bookings_url' ) ? wpbc_get_bookings_url() : admin_url( 'admin.php?page=wpbc' );
223 $current_step_data = $step_data->get_step_context( $current_draft['current_step'], $current_draft['values'], $current_draft['step_results'] );
224 $settings_hint = ( new WPBC_Setup_Wizard_Settings_Hints() )->get_hint( $current_draft['current_step'] );
225 if ( $is_review_step ) {
226 $current_step_data['email_state'] = isset( $current_draft['email'] ) && is_array( $current_draft['email'] ) ? $current_draft['email'] : array();
227 $current_step_data['email_error'] = $this->review_email_error;
228 }
229 $save_registry = WPBC_Setup_Wizard_Progressive_Save_Factory::create_save_registry( $step_registry );
230 $continue_label = $save_registry->get_primary_action_label( $current_draft['current_step'], $current_draft, $current_step_data );
231 $footer_note = isset( $current_step['footer_note'] ) && is_scalar( $current_step['footer_note'] )
232 ? sanitize_text_field( (string) $current_step['footer_note'] )
233 : '';
234 if ( '' === $footer_note ) {
235 $footer_note = $is_review_step
236 ? __( 'Finish setup revalidates the active route and completes the wizard. Settings and publishing changes saved on earlier steps remain active.', 'booking' )
237 : __( 'Your changes are saved as you continue. Back only navigates and does not roll back completed steps.', 'booking' );
238 }
239
240 $template_result = $templates->render(
241 'shell',
242 array(
243 'templates' => $templates,
244 'step_registry' => $step_registry,
245 'draft' => $current_draft,
246 'current_step' => $current_step,
247 'rail_items' => $step_registry->get_rail_items( $current_draft['current_step'], $current_draft['values'] ),
248 'rail_note' => $step_registry->get_rail_note( $current_draft['current_step'], $current_draft['values'] ),
249 'has_previous_step' => $step_registry->has_previous_step( $current_draft['current_step'], $current_draft['values'] ),
250 'can_continue' => 'completed' !== $current_draft['status'],
251 'has_step_form' => 'welcome' !== $current_draft['current_step'],
252 'continue_label' => '' !== $continue_label ? $continue_label : __( 'Save & continue', 'booking' ),
253 'show_continue_icon' => ! $is_review_step,
254 'footer_note' => $footer_note,
255 'bookings_url' => $bookings_url,
256 'close_url' => $bookings_url,
257 'header_template_id' => isset( $current_step['header_template'] ) ? (string) $current_step['header_template'] : '',
258 'step_template_id' => $current_step['template'],
259 'step_data' => $current_step_data,
260 'settings_hint' => $settings_hint,
261 )
262 );
263
264 if ( is_wp_error( $template_result ) ) {
265 $templates->render( 'error', array( 'message' => $template_result->get_error_message() ) );
266 }
267 }
268
269 /**
270 * Render the normal WordPress administration overview after completion.
271 *
272 * Canonical-backed dependencies are refreshed by Step Data before the Review
273 * presenter creates summaries. The overview therefore remains current when a
274 * setting is changed outside the wizard after completion.
275 *
276 * @return void
277 */
278 private function render_setup_overview() {
279 $overview = $this->get_setup_overview_context();
280 $overview_template = __DIR__ . '/setup-overview/template.php';
281
282 if ( ! is_readable( $overview_template ) ) {
283 wp_die(
284 esc_html__( 'The Setup overview template is unavailable.', 'booking' ),
285 esc_html__( 'Setup overview unavailable', 'booking' ),
286 array( 'response' => 500 )
287 );
288 }
289
290 require $overview_template;
291 }
292
293 /**
294 * Build the current completed overview once for assets and rendering.
295 *
296 * Canonical-backed values are refreshed through Step Data before the
297 * presentation service discovers authorized public booking-page cards.
298 * Sharing this request-local result prevents the enqueue and render paths
299 * from performing the same owner-scoped queries twice.
300 *
301 * @return array<string,mixed> Authorized completed overview context.
302 */
303 private function get_setup_overview_context() {
304 if ( is_array( $this->setup_overview_context ) ) {
305 return $this->setup_overview_context;
306 }
307
308 $checkpoint = $this->get_current_checkpoint();
309 $module_registry = new WPBC_Setup_Wizard_Step_Module_Registry();
310 $step_data = new WPBC_Setup_Wizard_Step_Data( $module_registry );
311 $review_context = $step_data->get_step_context( 'review_setup', $checkpoint['values'], $checkpoint['step_results'] );
312
313 $this->setup_overview_context = ( new WPBC_Setup_Wizard_Setup_Overview() )->get_context( $checkpoint, $review_context );
314
315 return $this->setup_overview_context;
316 }
317
318 /**
319 * Return the owner/site-scoped checkpoint once per administration request.
320 *
321 * @return array<string,mixed> Normalized Setup Wizard checkpoint.
322 */
323 private function get_current_checkpoint() {
324 if ( is_array( $this->current_checkpoint ) ) {
325 return $this->current_checkpoint;
326 }
327
328 $module_registry = new WPBC_Setup_Wizard_Step_Module_Registry();
329 $step_registry = new WPBC_Setup_Wizard_Step_Registry( $module_registry );
330 $step_data = new WPBC_Setup_Wizard_Step_Data( $module_registry );
331 $draft_store = new WPBC_Setup_Wizard_Draft_Store( $step_registry, new WPBC_Setup_Wizard_Draft_Validator( $step_data ) );
332 $this->current_checkpoint = $draft_store->load();
333
334 return $this->current_checkpoint;
335 }
336
337 /**
338 * Determine whether the persisted checkpoint should render the overview.
339 *
340 * @return bool True only after the progressive setup is complete.
341 */
342 private function is_completed_checkpoint() {
343 $checkpoint = $this->get_current_checkpoint();
344
345 return isset( $checkpoint['status'] ) && 'completed' === $checkpoint['status'];
346 }
347
348 /**
349 * Determine whether the current request targets this module page.
350 *
351 * @return bool True only for the canonical Setup Wizard slug.
352 */
353 private static function is_current_request() {
354 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only administration routing.
355 $page_slug = isset( $_GET['page'] ) && is_scalar( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
356
357 return 'wpbc-setup' === $page_slug;
358 }
359 }
360