PluginProbe
Booking Calendar / 11.5
Booking Calendar v11.5
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 10.11 All 204 releases
booking / includes / page-setup / setup_profiles.php

setup_profiles.php in Booking Calendar 11.5, at includes/page-setup/setup_profiles.php

1,145 lines 41.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php /**
2 * @version 1.0
3 * @description Booking profile helpers for Setup Wizard.
4 * @category Setup Wizard
5 */
6
7 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly.
8
9 /**
10 * Store the WPBC admin fullscreen preference for the current user.
11 *
12 * The top navigation fullscreen button stores this value through WPBC_User_Custom_Data_Saver as
13 * "booking_custom_is_full_screen" => array( 'value' => 'On|Off' ). Setup routing happens before redirects, so the
14 * wizard updates the same preference and its immediate browser cookie when it moves between internal and external
15 * setup screens. Synchronizing both values prevents an older cookie from overriding the active wizard state.
16 * This sends a response cookie when headers remain available and always updates the cookie value for the current
17 * request.
18 *
19 * @param bool $is_full_screen Whether WPBC admin pages should open in fullscreen mode.
20 *
21 * @return bool True when the saved user option changed; otherwise false.
22 */
23 function wpbc_setup_wizard__set_full_screen_mode_for_current_user( $is_full_screen ) {
24
25 $user_id = wpbc_get_current_user_id();
26
27 if ( empty( $user_id ) ) {
28 return false;
29 }
30
31 $full_screen_value = $is_full_screen ? 'On' : 'Off';
32 $cookie_path = ( defined( 'COOKIEPATH' ) && COOKIEPATH ) ? COOKIEPATH : '/';
33 $cookie_domain = defined( 'COOKIE_DOMAIN' ) ? COOKIE_DOMAIN : '';
34
35 if ( ! headers_sent() ) {
36 setcookie(
37 'wpbc_admin_full_screen',
38 $full_screen_value,
39 time() + YEAR_IN_SECONDS,
40 $cookie_path,
41 $cookie_domain,
42 is_ssl(),
43 false
44 );
45 }
46
47 // Keep cookie-aware page rendering deterministic during the current request.
48 $_COOKIE['wpbc_admin_full_screen'] = $full_screen_value;
49
50 return update_user_option(
51 $user_id,
52 'booking_custom_is_full_screen',
53 array( 'value' => $full_screen_value )
54 );
55 }
56
57 /**
58 * Get persisted wizard choices.
59 *
60 * @return array
61 */
62 function wpbc_setup_wizard__get_booking_wizard_data() {
63
64 $wizard_data = get_bk_option( 'booking_wizard_data' );
65
66 return ( empty( $wizard_data ) || ( ! is_array( $wizard_data ) ) ) ? array() : $wizard_data;
67 }
68
69 /**
70 * Get selected booking type from wizard history or current options.
71 *
72 * @param array|null $wizard_data Wizard data.
73 *
74 * @return string
75 */
76 function wpbc_setup_wizard__get_selected_booking_type( $wizard_data = null ) {
77
78 if ( null === $wizard_data ) {
79 $wizard_data = wpbc_setup_wizard__get_booking_wizard_data();
80 }
81
82 if (
83 isset( $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_types'] )
84 && ( ! empty( $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_types'] ) )
85 ) {
86 return (string) $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_types'];
87 }
88
89 if ( 'On' === get_bk_option( 'booking_range_selection_time_is_active' ) ) {
90 return 'changeover_multi_dates_bookings';
91 }
92
93 if ( 'single' === get_bk_option( 'booking_type_of_day_selections' ) ) {
94 return 'time_slots_appointments';
95 }
96
97 return 'full_days_bookings';
98 }
99
100 /**
101 * Get selected appointment mode from wizard history or current options.
102 *
103 * @param array|null $wizard_data Wizard data.
104 *
105 * @return string
106 */
107 function wpbc_setup_wizard__get_selected_appointments_type( $wizard_data = null ) {
108
109 if ( null === $wizard_data ) {
110 $wizard_data = wpbc_setup_wizard__get_booking_wizard_data();
111 }
112
113 // Appointment mode always uses the Service duration and start-time flow.
114 if ( 'appointment' === wpbc_setup_wizard__get_selected_mode_id( $wizard_data ) ) {
115 return 'durationtime';
116 }
117
118 if (
119 isset( $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_appointments_type'] )
120 && ( ! empty( $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_appointments_type'] ) )
121 ) {
122 return (string) $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_appointments_type'];
123 }
124
125 return 'rangetime';
126 }
127
128 /**
129 * Check whether Booking Modes can resolve the current owner safely.
130 *
131 * Setup progress can be inspected while active plugins are still loading.
132 * Before `init`, WordPress has not guaranteed that current-user pluggable
133 * functions or just-in-time translation loading are ready. Mode registries
134 * must therefore remain untouched until this boundary is reached.
135 *
136 * @return bool True when current-user and translation-dependent mode APIs can
137 * be used safely.
138 */
139 function wpbc_setup_wizard__is_booking_modes_runtime_ready() {
140
141 return did_action( 'init' ) && function_exists( 'wp_get_current_user' );
142 }
143
144 /**
145 * Get the presentation mode selected in wizard history or owner settings.
146 *
147 * Missing historical mode data is expected on upgrades. In that case the
148 * owner-scoped Booking Modes setting remains authoritative, with Classic as a
149 * compatibility fallback when the feature module is unavailable.
150 *
151 * @param array|null $wizard_data Wizard data.
152 *
153 * @return string Allowed Booking Modes identifier.
154 */
155 function wpbc_setup_wizard__get_selected_mode_id( $wizard_data = null ) {
156
157 if ( null === $wizard_data ) {
158 $wizard_data = wpbc_setup_wizard__get_booking_wizard_data();
159 }
160
161 $saved_mode_id = isset( $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_mode'] )
162 ? sanitize_key( (string) $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_mode'] )
163 : '';
164 $standard_mode_ids = array( 'classic', 'appointment', 'rental' );
165
166 if ( ! wpbc_setup_wizard__is_booking_modes_runtime_ready() ) {
167 return in_array( $saved_mode_id, $standard_mode_ids, true ) ? $saved_mode_id : 'classic';
168 }
169
170 if ( function_exists( 'wpbc_booking_modes_get_allowed_mode_ids' ) ) {
171 $allowed_mode_ids = wpbc_booking_modes_get_allowed_mode_ids();
172 if ( in_array( $saved_mode_id, $allowed_mode_ids, true ) ) {
173 return $saved_mode_id;
174 }
175
176 return wpbc_booking_modes_get_selected_mode_id();
177 }
178
179 return 'classic';
180 }
181
182 /**
183 * Convert selected booking type to a setup profile.
184 *
185 * @param array|null $wizard_data Wizard data.
186 *
187 * @return string
188 */
189 function wpbc_setup_wizard__get_selected_profile( $wizard_data = null ) {
190
191 $booking_type = wpbc_setup_wizard__get_selected_booking_type( $wizard_data );
192
193 if ( 'time_slots_appointments' === $booking_type ) {
194 return ( 'durationtime' === wpbc_setup_wizard__get_selected_appointments_type( $wizard_data ) ) ? 'duration_appointments' : 'time_slots';
195 }
196
197 if ( 'changeover_multi_dates_bookings' === $booking_type ) {
198 return 'changeover';
199 }
200
201 return 'full_day';
202 }
203
204 /**
205 * Check whether profile uses booking times.
206 *
207 * @param string|null $profile Profile.
208 *
209 * @return bool
210 */
211 function wpbc_setup_wizard__is_time_based_profile( $profile = null ) {
212
213 $profile = ( null === $profile ) ? wpbc_setup_wizard__get_selected_profile() : $profile;
214
215 return in_array( $profile, array( 'time_slots', 'duration_appointments' ), true );
216 }
217
218 /**
219 * Check whether profile uses changeover mode.
220 *
221 * @param string|null $profile Profile.
222 *
223 * @return bool
224 */
225 function wpbc_setup_wizard__is_changeover_profile( $profile = null ) {
226
227 $profile = ( null === $profile ) ? wpbc_setup_wizard__get_selected_profile() : $profile;
228
229 return ( 'changeover' === $profile );
230 }
231
232 /**
233 * Get the internal setup wizard route before profile-specific external pages.
234 *
235 * @return array
236 */
237 function wpbc_setup_wizard__get_intro_route() {
238
239 $route = array( 'welcome' );
240 if ( ! wpbc_is_this_demo() ) {
241 $route[] = 'general_info';
242 }
243 $route[] = 'date_time_formats';
244 $route[] = 'bookings_types';
245
246 return $route;
247 }
248
249 /**
250 * Get profile-specific setup route matrix after "Booking Type".
251 *
252 * Steps 1-4 stay inside the setup wizard. These compatibility routes remain
253 * behavior-based; `wpbc_setup_wizard__get_profile_route()` can replace their
254 * order for a presentation mode while reusing the same canonical controllers.
255 *
256 * @return array
257 */
258 function wpbc_setup_wizard__get_profile_route_map() {
259
260 $full_day_route = array(
261 'form_structure',
262 'date_selection',
263 'date_availability',
264 'color_theme',
265 'wizard_publish',
266 'get_started',
267 );
268
269 $changeover_route = array(
270 'form_structure',
271 'date_selection',
272 'changeover_days',
273 'date_availability',
274 'color_theme',
275 'wizard_publish',
276 'get_started',
277 );
278
279 $time_based_route = array(
280 'form_structure',
281 'date_selection',
282 'working_time',
283 'time_slots_availability',
284 'color_theme',
285 'wizard_publish',
286 'get_started',
287 );
288
289 return array(
290 'full_day' => $full_day_route,
291 'changeover' => $changeover_route,
292 'time_slots' => $time_based_route,
293 'duration_appointments' => $time_based_route,
294 );
295 }
296
297 /**
298 * Get profile-specific setup route after "Booking Type".
299 *
300 * @param string|null $profile Profile.
301 * @param bool|null $is_bfb_enabled Deprecated. Kept for backward-compatible calls.
302 *
303 * @return array
304 */
305 function wpbc_setup_wizard__get_profile_route( $profile = null, $is_bfb_enabled = null ) {
306
307 $profile = ( null === $profile ) ? wpbc_setup_wizard__get_selected_profile() : $profile;
308 $mode_id = wpbc_setup_wizard__get_selected_mode_id();
309 $route_map = wpbc_setup_wizard__get_profile_route_map();
310
311 if ( 'appointment' === $mode_id ) {
312 $route = array(
313 'service_provider',
314 'working_time',
315 'date_availability',
316 'time_slots_availability',
317 'form_structure',
318 'color_theme',
319 'wizard_publish',
320 'get_started',
321 );
322 } else {
323 $route = isset( $route_map[ $profile ] ) ? $route_map[ $profile ] : $route_map['full_day'];
324 }
325
326 /**
327 * Filter the mode-aware Setup Wizard route after Step 4.
328 *
329 * @param array $route Ordered external setup steps.
330 * @param string $mode_id Active Booking Modes identifier.
331 * @param string $profile Booking behavior profile.
332 */
333 $route = apply_filters( 'wpbc_setup_wizard_profile_route', $route, $mode_id, $profile );
334
335 return is_array( $route ) ? array_values( $route ) : array();
336 }
337
338 /**
339 * Get the next setup step from the current profile route.
340 *
341 * @param string $current_step Current step name.
342 *
343 * @return string
344 */
345 function wpbc_setup_wizard__get_next_step_name( $current_step ) {
346
347 $route = array_merge( wpbc_setup_wizard__get_intro_route(), wpbc_setup_wizard__get_profile_route() );
348
349 $current_step_index = array_search( $current_step, $route, true );
350
351 if ( false === $current_step_index ) {
352 return '';
353 }
354
355 return isset( $route[ $current_step_index + 1 ] ) ? $route[ $current_step_index + 1 ] : '';
356 }
357
358 /**
359 * Check whether the step is rendered inside the setup wizard page.
360 *
361 * @param string $step_name Step name.
362 *
363 * @return bool
364 */
365 function wpbc_setup_wizard__is_internal_step( $step_name ) {
366
367 return in_array( $step_name, array( 'welcome', 'general_info', 'date_time_formats', 'bookings_types' ), true );
368 }
369
370 /**
371 * Detect the external setup step from the current WPBC admin page request.
372 *
373 * @return string
374 */
375 function wpbc_setup_wizard__detect_step_from_admin_request() {
376
377 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
378 $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
379 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
380 $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : '';
381 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
382 $scroll_to_section = isset( $_GET['scroll_to_section'] ) ? sanitize_key( wp_unslash( $_GET['scroll_to_section'] ) ) : '';
383 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
384 $wpbc_ag_open = isset( $_GET['wpbc_ag_open'] ) ? sanitize_key( wp_unslash( $_GET['wpbc_ag_open'] ) ) : '';
385 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
386 $wpbc_calendar_section = isset( $_GET['wpbc_calendar_section'] ) ? sanitize_key( wp_unslash( $_GET['wpbc_calendar_section'] ) ) : '';
387 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
388 $wpbc_setup_step = isset( $_GET['wpbc_setup_step'] ) ? sanitize_key( wp_unslash( $_GET['wpbc_setup_step'] ) ) : '';
389
390 if ( '' !== $wpbc_setup_step ) {
391 return $wpbc_setup_step;
392 }
393
394 if ( 'wpbc-settings' === $page ) {
395 if ( 'builder_booking_form' === $tab ) {
396 return 'form_structure';
397 }
398
399 if ( 'form' === $tab ) {
400 return 'form_structure';
401 }
402
403 if ( 'calendar_settings' === $tab ) {
404 if ( 'changeover_times' === $wpbc_calendar_section ) {
405 return 'changeover_days';
406 }
407 return 'date_selection';
408 }
409
410 if ( 'wpbc_general_settings_calendar_tab' === $scroll_to_section ) {
411 return 'date_selection';
412 }
413
414 if ( 'themes' === $tab ) {
415 return 'color_theme';
416 }
417 }
418
419 if ( 'wpbc-availability' === $page ) {
420 if ( 'time_slots_availability' === $tab ) {
421 return 'time_slots_availability';
422 }
423
424 if ( 'working_time' === $wpbc_ag_open ) {
425 return 'working_time';
426 }
427
428 return 'date_availability';
429 }
430
431 if ( 'wpbc-resources' === $page ) {
432 return 'wizard_publish';
433 }
434
435 if ( 'wpbc-services' === $page && 'appointment_services' === $tab ) {
436 return 'service_provider';
437 }
438
439 return '';
440 }
441
442 /**
443 * Detect an external setup step from an admin URL.
444 *
445 * @param string $url URL.
446 *
447 * @return string
448 */
449 function wpbc_setup_wizard__detect_step_from_admin_url( $url ) {
450
451 if ( empty( $url ) ) {
452 return '';
453 }
454
455 $url_parts = wp_parse_url( $url );
456 if ( empty( $url_parts['query'] ) ) {
457 return '';
458 }
459
460 $query_args = array();
461 wp_parse_str( $url_parts['query'], $query_args );
462
463 $page = isset( $query_args['page'] ) ? sanitize_key( $query_args['page'] ) : '';
464 $tab = isset( $query_args['tab'] ) ? sanitize_key( $query_args['tab'] ) : '';
465 $scroll_to_section = isset( $query_args['scroll_to_section'] ) ? sanitize_key( $query_args['scroll_to_section'] ) : '';
466 $wpbc_ag_open = isset( $query_args['wpbc_ag_open'] ) ? sanitize_key( $query_args['wpbc_ag_open'] ) : '';
467 $wpbc_calendar_section = isset( $query_args['wpbc_calendar_section'] ) ? sanitize_key( $query_args['wpbc_calendar_section'] ) : '';
468 $wpbc_setup_step = isset( $query_args['wpbc_setup_step'] ) ? sanitize_key( $query_args['wpbc_setup_step'] ) : '';
469
470 if ( '' !== $wpbc_setup_step ) {
471 return $wpbc_setup_step;
472 }
473
474 if ( 'wpbc-settings' === $page ) {
475 if ( 'builder_booking_form' === $tab ) {
476 return 'form_structure';
477 }
478
479 if ( 'form' === $tab ) {
480 return 'form_structure';
481 }
482
483 if ( 'calendar_settings' === $tab ) {
484 if ( 'changeover_times' === $wpbc_calendar_section ) {
485 return 'changeover_days';
486 }
487 return 'date_selection';
488 }
489
490 if ( 'wpbc_general_settings_calendar_tab' === $scroll_to_section ) {
491 return 'date_selection';
492 }
493
494 if ( 'themes' === $tab ) {
495 return 'color_theme';
496 }
497 }
498
499 if ( 'wpbc-availability' === $page ) {
500 if ( 'time_slots_availability' === $tab ) {
501 return 'time_slots_availability';
502 }
503
504 if ( 'working_time' === $wpbc_ag_open ) {
505 return 'working_time';
506 }
507
508 return 'date_availability';
509 }
510
511 if ( 'wpbc-resources' === $page ) {
512 return 'wizard_publish';
513 }
514
515 if ( 'wpbc-services' === $page && 'appointment_services' === $tab ) {
516 return 'service_provider';
517 }
518
519 return '';
520 }
521
522 /**
523 * Get human-readable setup step title.
524 *
525 * @param string $step_name Step name.
526 *
527 * @return string
528 */
529 function wpbc_setup_wizard__get_step_title( $step_name ) {
530 $mode_id = wpbc_setup_wizard__get_selected_mode_id();
531
532 if ( 'wizard_publish' === $step_name && 'appointment' === $mode_id ) {
533 return __( 'Appointment Page', 'booking' );
534 }
535
536 if ( 'wizard_publish' === $step_name && 'rental' === $mode_id ) {
537 return __( 'Rental Page', 'booking' );
538 }
539
540 if ( 'date_availability' === $step_name && 'appointment' === $mode_id ) {
541 return __( 'Days Off', 'booking' );
542 }
543
544 $step_titles = array(
545 'welcome' => __( 'Welcome', 'booking' ),
546 'general_info' => __( 'General Info', 'booking' ),
547 'date_time_formats' => __( 'Dates and Times', 'booking' ),
548 'bookings_types' => __( 'Booking Type', 'booking' ),
549 'service_provider' => __( 'Service and Provider', 'booking' ),
550 'date_selection' => __( 'Date Selection', 'booking' ),
551 'changeover_days' => __( 'Changeover Days', 'booking' ),
552 'working_time' => __( 'Working Time', 'booking' ),
553 'time_slots_availability' => __( 'Time Slots', 'booking' ),
554 'date_availability' => __( 'Date Availability', 'booking' ),
555 'form_structure' => __( 'Booking Form', 'booking' ),
556 'color_theme' => __( 'Color Theme', 'booking' ),
557 'wizard_publish' => __( 'Publish', 'booking' ),
558 'get_started' => __( 'Get Started', 'booking' ),
559 );
560
561 return isset( $step_titles[ $step_name ] ) ? $step_titles[ $step_name ] : $step_name;
562 }
563
564 /**
565 * Get action-oriented setup step heading shown in the floating setup bar.
566 *
567 * @param string $step_name Step name.
568 *
569 * @return string
570 */
571 function wpbc_setup_wizard__get_step_heading( $step_name ) {
572 $mode_id = wpbc_setup_wizard__get_selected_mode_id();
573
574 if ( 'wizard_publish' === $step_name && 'appointment' === $mode_id ) {
575 return __( 'Review and test the Appointment page', 'booking' );
576 }
577
578 if ( 'wizard_publish' === $step_name && 'rental' === $mode_id ) {
579 return __( 'Review and test the Rental page', 'booking' );
580 }
581
582 if ( 'date_availability' === $step_name && 'appointment' === $mode_id ) {
583 return __( 'Block Provider days off and exceptions', 'booking' );
584 }
585
586 $step_headings = array(
587 'welcome' => __( 'Start the initial setup', 'booking' ),
588 'general_info' => __( 'Confirm your business details', 'booking' ),
589 'date_time_formats' => __( 'Choose date and time formats', 'booking' ),
590 'bookings_types' => __( 'Choose the main booking workflow', 'booking' ),
591 'service_provider' => __( 'Review your first Service and Provider', 'booking' ),
592 'form_structure' => __( 'Select and save the booking form template', 'booking' ),
593 'date_selection' => __( 'Set how visitors select dates', 'booking' ),
594 'changeover_days' => __( 'Configure changeover days', 'booking' ),
595 'working_time' => __( 'Define when bookings can start and end', 'booking' ),
596 'time_slots_availability' => __( 'Review availability for appointment slots', 'booking' ),
597 'date_availability' => __( 'Set date availability rules', 'booking' ),
598 'color_theme' => __( 'Choose the calendar appearance', 'booking' ),
599 'wizard_publish' => __( 'Publish the booking form on your site', 'booking' ),
600 'get_started' => __( 'Complete setup and manage bookings', 'booking' ),
601 );
602
603 return isset( $step_headings[ $step_name ] ) ? $step_headings[ $step_name ] : wpbc_setup_wizard__get_step_title( $step_name );
604 }
605
606 /**
607 * Get setup step guidance shown in the floating setup bar.
608 *
609 * @param string $step_name Step name.
610 *
611 * @return string
612 */
613 function wpbc_setup_wizard__get_step_description( $step_name ) {
614 $mode_id = wpbc_setup_wizard__get_selected_mode_id();
615
616 if ( 'wizard_publish' === $step_name && 'appointment' === $mode_id ) {
617 return __( 'Open a published Appointment page to test the Service, Provider, date, and time flow. If no page is listed, run Appointment QuickStart or publish the shortcode first.', 'booking' );
618 }
619
620 if ( 'wizard_publish' === $step_name && 'rental' === $mode_id ) {
621 return __( 'Open a published Property booking page to test the Rental flow. If no page is listed, run Rental QuickStart or publish the booking form first.', 'booking' );
622 }
623
624 if ( 'date_availability' === $step_name && 'appointment' === $mode_id ) {
625 return __( 'Use the canonical Days Availability calendar to block holidays, leave, and other Provider-specific exceptions.', 'booking' );
626 }
627
628 $step_descriptions = array(
629 'welcome' => __( 'Begin the guided setup. You can leave the wizard at any time and continue later from the setup bar.', 'booking' ),
630 'general_info' => __( 'Review the business name, contact details, and basic information used while preparing the booking configuration.', 'booking' ),
631 'date_time_formats' => __( 'Pick the date and time display formats that should be used in the admin panel, booking form, and customer-facing messages.', 'booking' ),
632 'bookings_types' => __( 'Select whether bookings use full days, appointment time slots, or changeover-style date ranges. This choice controls the next configuration steps.', 'booking' ),
633 'service_provider' => __( 'QuickStart created a starter Service assigned to an existing booking resource. Review its duration, buffers, price, Booking Form, and Provider assignment.', 'booking' ),
634 'form_structure' => __( 'Open the form template chooser, select the template that matches your booking type, and save the Booking Form page before continuing.', 'booking' ),
635 'date_selection' => __( 'Choose whether visitors can select one day, multiple individual days, or an available date range in the calendar. Save the General Settings page after changing this option.', 'booking' ),
636 'changeover_days' => __( 'Enable and review check-in/check-out changeover days, including diagonal or vertical markings and related changeover options.', 'booking' ),
637 'working_time' => __( 'Set the daily working hours that should be available for bookings. These hours are used as the base schedule for time-based availability.', 'booking' ),
638 'time_slots_availability' => __( 'Check the generated appointment slots and adjust any slot-specific availability rules before choosing the calendar appearance.', 'booking' ),
639 'date_availability' => __( 'Set which weekdays, dates, booking windows, and buffer rules should be available or unavailable for visitors.', 'booking' ),
640 'color_theme' => __( 'Choose the calendar skin and visual style so the booking form fits your site before you publish it.', 'booking' ),
641 'wizard_publish' => __( 'Use the Publish buttons for each booking resource to insert the booking form into an existing page, create a new page, or copy the shortcode.', 'booking' ),
642 'get_started' => __( 'Finish the setup wizard. After this, the setup bar will disappear and you can start managing bookings normally.', 'booking' ),
643 );
644
645 return isset( $step_descriptions[ $step_name ] ) ? $step_descriptions[ $step_name ] : '';
646 }
647
648 /**
649 * Get external setup step UI integration matrix.
650 *
651 * Each entry describes how the floating setup bar should connect a wizard step
652 * to an existing WPBC admin page: what to open, what to scroll/highlight, which
653 * form/save control belongs to this step, and which JS events mean the external
654 * page has saved successfully.
655 *
656 * @return array
657 */
658 function wpbc_setup_wizard__get_step_ui_matrix() {
659 $publish_step_ui = array(
660 'save_behavior' => 'link_only',
661 'target_selector' => '.ui_group__publish_btn, .wpbc_resource_field__publish, .wpbc_resource_publish, .wpbc_publish_resources, [data-wpbc-resource-publish]',
662 'scroll_selector' => '.ui_group__publish_btn:visible, .wpbc_resource_field__publish:visible, .wpbc_resource_publish:visible, .wpbc_publish_resources:visible, [data-wpbc-resource-publish]:visible',
663 'highlight_selector' => '.ui_group__publish_btn:visible, .wpbc_resource_field__publish:visible, .wpbc_resource_publish:visible, .wpbc_publish_resources:visible, [data-wpbc-resource-publish]:visible',
664 'highlight_all' => '1',
665 'open_action' => 'publish_area',
666 );
667 $time_slots_step_ui = array(
668 'save_behavior' => 'link_only',
669 'target_selector' => '.wpbc_admin_page__tab__time_slots_availability, .wpbc_ts_page',
670 'scroll_selector' => '.wpbc_admin_page__tab__time_slots_availability:visible, .wpbc_ts_page:visible',
671 'highlight_disabled' => '1',
672 );
673 $date_availability_step_ui = array(
674 'save_behavior' => 'link_only',
675 'target_selector' => '.wpbc_admin_page__tab__availability, .wpbc_ajx_availability_container',
676 'scroll_selector' => '.wpbc_ajx_availability_container:visible, .wpbc_admin_page__tab__availability:visible',
677 'highlight_disabled' => '1',
678 );
679
680 if ( 'rental' === wpbc_setup_wizard__get_selected_mode_id() ) {
681 $date_availability_step_ui = array(
682 'save_behavior' => 'manual_save_required',
683 'target_selector' => '.wpbc_admin_page__tab__general_availability, [data-wpbc-ag-settings-form="1"]',
684 'scroll_selector' => '[data-wpbc-ag-settings-form="1"]:visible, .wpbc_admin_page__tab__general_availability:visible',
685 'highlight_disabled' => '1',
686 'form_selector' => '[data-wpbc-ag-settings-form="1"]',
687 'save_selector' => '[data-wpbc-ag-save="1"]',
688 'save_ajax_action' => 'WPBC_AJX_AVAILABILITY_GENERAL_SAVE',
689 'save_events' => 'wpbc:availability-general:settings-saved,wpbc:setup-wizard:step-saved',
690 );
691 }
692
693 if ( 'appointment' === wpbc_setup_wizard__get_selected_mode_id() ) {
694 $form_builder_url = wpbc_get_settings_url() . '&tab=builder_booking_form';
695 $form_builder_url = add_query_arg(
696 array(
697 'wpbc_bfb_panel' => 'add_fields',
698 'wpbc_bfb_group' => 'fields-times',
699 'wpbc_bfb_focus' => 'weekday_starttime',
700 ),
701 $form_builder_url
702 );
703
704 $time_slots_step_ui['secondary_action_url'] = wpbc_setup_wizard__add_setup_context_to_url( $form_builder_url, 'time_slots_availability' );
705 $time_slots_step_ui['secondary_action_label'] = __( 'Adjust slot-specific availability rules', 'booking' );
706 }
707
708 return array(
709 'service_provider' => array(
710 'save_behavior' => 'link_only',
711 'target_selector' => '.wpbc_appointment_services_page',
712 'scroll_selector' => '.wpbc_appointment_services_page:visible',
713 'highlight_disabled' => '1',
714 ),
715 'form_structure' => array(
716 'save_behavior' => 'manual_save_required',
717 'target_selector' => '.wpbc_admin_page__tab__builder_booking_form, #wpbc_form_field_free',
718 'scroll_selector' => '.wpbc_bfb_popup_modal__forms_loading_section:visible, #wpbc_form_field_free:visible, .wpbc_admin_page__tab__builder_booking_form:visible',
719 'highlight_disabled' => '1',
720 'form_selector' => '#wpbc_form_field_free',
721 'save_selector' => '#wpbc_form_field_free .wpbc_submit_button_trigger, #wpbc_form_field_free .wpbc_submit_button, [onclick*="wpbc_bfb__ajax_save_current_form"], [data-wpbc-bfb-save-source]',
722 'save_events' => 'wpbc:bfb:form:ajax_saved,wpbc:bfb:form:saved,wpbc:bfb:save:done,wpbc:bfb:ajax_saved,wpbc:setup-wizard:step-saved',
723 ),
724 'date_selection' => array(
725 'save_behavior' => 'manual_save_required',
726 'target_selector' => '.wpbc_admin_page__tab__calendar_settings, [data-wpbc-calendar-page="1"], [data-group="settings-calendar-days-selection"], [data-wpbc-calendar-settings-form="1"]',
727 'scroll_selector' => '[data-group="settings-calendar-days-selection"]:visible, .wpbc_calendar__inspector_settings:visible, [data-wpbc-calendar-settings-form="1"]:visible',
728 'highlight_selector' => '[data-group="settings-calendar-days-selection"]:visible, .wpbc_calendar__inspector_settings:visible, [data-wpbc-calendar-settings-form="1"]:visible',
729 'form_selector' => '[data-wpbc-calendar-settings-form="1"], #wpbc_general_settings_form',
730 'save_selector' => '[data-wpbc-calendar-save="1"], #wpbc_general_settings_form .wpbc_submit_button_trigger, #wpbc_general_settings_form .wpbc_submit_button',
731 'save_ajax_action' => 'WPBC_AJX_SETTINGS_CALENDAR_SAVE',
732 'save_events' => 'wpbc:setup-wizard:step-saved',
733 ),
734 'changeover_days' => array(
735 'save_behavior' => 'manual_save_required',
736 'target_selector' => '[data-group="settings-calendar-time"], [data-wpbc-calendar-changeover-settings="1"], [name="booking_range_selection_time_is_active"], [data-wpbc-calendar-page="1"]',
737 'scroll_selector' => '[data-group="settings-calendar-time"]:visible, [data-wpbc-calendar-changeover-settings="1"]:visible, [name="booking_range_selection_time_is_active"]:visible, [data-wpbc-calendar-page="1"]:visible',
738 'highlight_selector' => '[data-group="settings-calendar-time"]:visible, [data-wpbc-calendar-changeover-settings="1"]:visible, [name="booking_range_selection_time_is_active"]:visible',
739 'form_selector' => '[data-wpbc-calendar-settings-form="1"]',
740 'save_selector' => '[data-wpbc-calendar-save="1"]',
741 'save_ajax_action' => 'WPBC_AJX_SETTINGS_CALENDAR_SAVE',
742 'save_events' => 'wpbc:setup-wizard:step-saved',
743 ),
744 'working_time' => array(
745 'save_behavior' => 'manual_save_required',
746 'target_selector' => '[data-group="general-availability-working-time"], .wpbc_ag_working_time_block, .wpbc_admin_page__tab__general_availability',
747 'scroll_selector' => '[data-group="general-availability-working-time"]:visible, .wpbc_ag_working_time_block:visible, [data-wpbc-ag-settings-form="1"]:visible',
748 'highlight_selector' => '[data-group="general-availability-working-time"]:visible, .wpbc_ag_working_time_block:visible',
749 'form_selector' => '[data-wpbc-ag-settings-form="1"]',
750 'save_selector' => '[data-wpbc-ag-save="1"]',
751 'save_ajax_action' => 'WPBC_AJX_AVAILABILITY_GENERAL_SAVE',
752 'save_events' => 'wpbc:availability-general:settings-saved,wpbc:setup-wizard:step-saved',
753 'open_action' => 'availability_section',
754 ),
755 'time_slots_availability' => $time_slots_step_ui,
756 'date_availability' => $date_availability_step_ui,
757 'color_theme' => array(
758 'save_behavior' => 'manual_save_required',
759 'target_selector' => '.wpbc_admin_page__tab__themes, [data-wpbc-theme-page="1"]',
760 'scroll_selector' => '.wpbc_theme__inspector_settings:visible, [data-wpbc-theme-settings-form="1"]:visible, .wpbc_theme_rightbar_panels:visible',
761 'highlight_selector' => '.wpbc_theme__inspector_settings:visible, [data-wpbc-theme-settings-form="1"]:visible, .wpbc_theme_rightbar_panels:visible',
762 'form_selector' => '[data-wpbc-theme-settings-form="1"]',
763 'save_selector' => '[data-wpbc-theme-save="1"]',
764 'save_ajax_action' => 'WPBC_AJX_SETTINGS_THEMES_SAVE',
765 'save_events' => 'wpbc:setup-wizard:step-saved',
766 ),
767 'wizard_publish' => $publish_step_ui,
768 'get_started' => array(
769 'save_behavior' => 'complete',
770 'target_selector' => '.wpbc_admin_page',
771 'scroll_selector' => '.wpbc_admin_page:visible',
772 'highlight_disabled' => '1',
773 ),
774 );
775 }
776
777 /**
778 * Get one UI integration value from the external setup step matrix.
779 *
780 * @param string $step_name Step name.
781 * @param string $key Matrix key.
782 *
783 * @return string
784 */
785 function wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, $key ) {
786
787 $ui_matrix = wpbc_setup_wizard__get_step_ui_matrix();
788
789 return ( isset( $ui_matrix[ $step_name ][ $key ] ) ) ? (string) $ui_matrix[ $step_name ][ $key ] : '';
790 }
791
792 /**
793 * Get save behavior for external setup step.
794 *
795 * @param string $step_name Step name.
796 *
797 * @return string
798 */
799 function wpbc_setup_wizard__get_step_save_behavior( $step_name ) {
800
801 $save_behavior = wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'save_behavior' );
802
803 return ( ! empty( $save_behavior ) ) ? $save_behavior : 'link_only';
804 }
805
806 /**
807 * Get DOM selector to highlight/scroll on external setup pages.
808 *
809 * @param string $step_name Step name.
810 *
811 * @return string
812 */
813 function wpbc_setup_wizard__get_step_target_selector( $step_name ) {
814
815 return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'target_selector' );
816 }
817
818 /**
819 * Get DOM selector to scroll on external setup pages.
820 *
821 * @param string $step_name Step name.
822 *
823 * @return string
824 */
825 function wpbc_setup_wizard__get_step_scroll_selector( $step_name ) {
826
827 $scroll_selector = wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'scroll_selector' );
828
829 return ( ! empty( $scroll_selector ) ) ? $scroll_selector : wpbc_setup_wizard__get_step_target_selector( $step_name );
830 }
831
832 /**
833 * Get DOM selector to highlight on external setup pages.
834 *
835 * @param string $step_name Step name.
836 *
837 * @return string
838 */
839 function wpbc_setup_wizard__get_step_highlight_selector( $step_name ) {
840
841 if ( wpbc_setup_wizard__is_step_highlight_disabled( $step_name ) ) {
842 return '';
843 }
844
845 $highlight_selector = wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'highlight_selector' );
846
847 return ( ! empty( $highlight_selector ) ) ? $highlight_selector : wpbc_setup_wizard__get_step_target_selector( $step_name );
848 }
849
850 /**
851 * Check if external setup page highlighting is disabled for a step.
852 *
853 * @param string $step_name Step name.
854 *
855 * @return bool
856 */
857 function wpbc_setup_wizard__is_step_highlight_disabled( $step_name ) {
858
859 return '1' === wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'highlight_disabled' );
860 }
861
862 /**
863 * Get page open action for external setup pages.
864 *
865 * @param string $step_name Step name.
866 *
867 * @return string
868 */
869 function wpbc_setup_wizard__get_step_open_action( $step_name ) {
870
871 return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'open_action' );
872 }
873
874 /**
875 * Get browser events that confirm an external setup page save.
876 *
877 * @param string $step_name Step name.
878 *
879 * @return string
880 */
881 function wpbc_setup_wizard__get_step_save_events( $step_name ) {
882
883 return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'save_events' );
884 }
885
886 /**
887 * Get AJAX action name used by the existing page save button related to the setup step.
888 *
889 * @param string $step_name Step name.
890 *
891 * @return string
892 */
893 function wpbc_setup_wizard__get_step_save_ajax_action( $step_name ) {
894
895 return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'save_ajax_action' );
896 }
897
898 /**
899 * Get DOM selector for the existing page form related to the setup step.
900 *
901 * @param string $step_name Step name.
902 *
903 * @return string
904 */
905 function wpbc_setup_wizard__get_step_form_selector( $step_name ) {
906
907 return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'form_selector' );
908 }
909
910 /**
911 * Get selector for the existing page save button related to the setup step.
912 *
913 * @param string $step_name Step name.
914 *
915 * @return string
916 */
917 function wpbc_setup_wizard__get_step_save_selector( $step_name ) {
918
919 return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'save_selector' );
920 }
921
922 /**
923 * Add setup guide context to a target URL.
924 *
925 * @param string $url URL.
926 * @param string $step_name Step name.
927 *
928 * @return string
929 */
930 function wpbc_setup_wizard__add_setup_context_to_url( $url, $step_name ) {
931
932 return add_query_arg(
933 array(
934 'wpbc_setup' => '1',
935 'wpbc_setup_step' => $step_name,
936 ),
937 $url
938 );
939 }
940
941 /**
942 * Get the Form Builder template search key for the selected booking type.
943 *
944 * @return string
945 */
946 function wpbc_setup_wizard__get_bfb_template_search_key() {
947
948 $booking_wizard_data_arr = get_bk_option( 'booking_wizard_data' );
949 if (
950 empty( $booking_wizard_data_arr )
951 || ! is_array( $booking_wizard_data_arr )
952 || empty( $booking_wizard_data_arr['save_and_continue__bookings_types'] )
953 || ! is_array( $booking_wizard_data_arr['save_and_continue__bookings_types'] )
954 ) {
955 return '';
956 }
957
958 $booking_type = isset( $booking_wizard_data_arr['save_and_continue__bookings_types']['wpbc_swp_booking_types'] )
959 ? $booking_wizard_data_arr['save_and_continue__bookings_types']['wpbc_swp_booking_types']
960 : '';
961 $url_sep = defined( 'WPBC_BFB_TEMPLATE_SEARCH_OR_SEPARATOR_URL' ) ? WPBC_BFB_TEMPLATE_SEARCH_OR_SEPARATOR_URL : '^';
962
963 if ( 'full_days_bookings' === $booking_type ) {
964 return 'full-days' . $url_sep . 'dates_form';
965 }
966
967 if ( 'time_slots_appointments' === $booking_type ) {
968 if ( 'rangetime' === wpbc_setup_wizard__get_selected_appointments_type( $booking_wizard_data_arr ) ) {
969 return 'times' . $url_sep . 'time slots';
970 }
971
972 return 'appointments' . $url_sep . 'service';
973 }
974
975 if ( 'changeover_multi_dates_bookings' === $booking_type ) {
976 return 'changeover' . $url_sep . 'triangles';
977 }
978
979 return '';
980 }
981
982 /**
983 * Get the existing admin page URL that should handle a setup step.
984 *
985 * @param string $step_name Step name.
986 *
987 * @return string
988 */
989 function wpbc_setup_wizard__get_step_target_url( $step_name ) {
990
991 if ( wpbc_setup_wizard__is_internal_step( $step_name ) ) {
992 return add_query_arg( 'current_step', $step_name, wpbc_get_setup_wizard_page_url() );
993 }
994
995 switch ( $step_name ) {
996 case 'service_provider':
997 $url = function_exists( 'wpbc_booking_modes_get_canonical_page_url' )
998 ? wpbc_booking_modes_get_canonical_page_url( 'wpbc-services__appointment_services' )
999 : admin_url( 'admin.php?page=wpbc-services&tab=appointment_services' );
1000 break;
1001
1002 case 'date_selection':
1003 $url = function_exists( 'wpbc_get_settings_calendar_url' )
1004 ? wpbc_get_settings_calendar_url()
1005 : wpbc_get_settings_url() . '&tab=calendar_settings';
1006 $url = add_query_arg( 'wpbc_calendar_section', 'days_selection', $url );
1007 break;
1008
1009 case 'changeover_days':
1010 $url = function_exists( 'wpbc_get_settings_calendar_url' )
1011 ? wpbc_get_settings_calendar_url()
1012 : wpbc_get_settings_url() . '&tab=calendar_settings';
1013 $url = add_query_arg( 'wpbc_calendar_section', 'changeover_times', $url );
1014 break;
1015
1016 case 'working_time':
1017 $url = function_exists( 'wpbc_get_general_availability_url' )
1018 ? wpbc_get_general_availability_url()
1019 : admin_url( 'admin.php?page=wpbc-availability&tab=general_availability' );
1020 $url = add_query_arg( 'wpbc_ag_open', 'working_time', $url );
1021 break;
1022
1023 case 'time_slots_availability':
1024 $url = function_exists( 'wpbc_get_time_slots_availability_url' )
1025 ? wpbc_get_time_slots_availability_url()
1026 : admin_url( 'admin.php?page=wpbc-availability&tab=time_slots_availability' );
1027 break;
1028
1029 case 'date_availability':
1030 if ( 'rental' === wpbc_setup_wizard__get_selected_mode_id() ) {
1031 $url = function_exists( 'wpbc_get_general_availability_url' )
1032 ? wpbc_get_general_availability_url()
1033 : admin_url( 'admin.php?page=wpbc-availability&tab=general_availability' );
1034 break;
1035 }
1036
1037 $canonical_url = function_exists( 'wpbc_booking_modes_get_canonical_page_url' )
1038 ? wpbc_booking_modes_get_canonical_page_url( 'wpbc-availability__availability' )
1039 : '';
1040 $url = ! empty( $canonical_url ) ? $canonical_url : admin_url( 'admin.php?page=wpbc-availability&tab=availability' );
1041 break;
1042
1043 case 'form_structure':
1044 $url = wpbc_get_settings_url() . '&tab=builder_booking_form';
1045
1046 $template_search_key = wpbc_setup_wizard__get_bfb_template_search_key();
1047 if ( ! empty( $template_search_key ) ) {
1048 $url = add_query_arg( 'auto_open_template', $template_search_key, $url );
1049 }
1050 break;
1051
1052 case 'color_theme':
1053 $url = function_exists( 'wpbc_get_settings_themes_url' )
1054 ? wpbc_get_settings_themes_url()
1055 : wpbc_get_settings_url() . '&tab=themes';
1056 break;
1057
1058 case 'wizard_publish':
1059 $url = function_exists( 'wpbc_booking_modes_get_canonical_page_url' )
1060 ? wpbc_booking_modes_get_canonical_page_url( 'wpbc-resources__resources' )
1061 : '';
1062 if ( empty( $url ) ) {
1063 $url = add_query_arg( 'tab', 'resources', wpbc_get_resources_url() );
1064 }
1065 $url .= '#wpbc_booking_resource_table';
1066 break;
1067
1068 case 'get_started':
1069 $url = wpbc_get_bookings_url() . '&tab=vm_booking_listing';
1070 break;
1071
1072 default:
1073 $url = wpbc_get_setup_wizard_page_url();
1074 break;
1075 }
1076
1077 return wpbc_setup_wizard__add_setup_context_to_url( $url, $step_name );
1078 }
1079
1080 /**
1081 * Get URL that completes an external setup step and redirects to the next mapped step.
1082 *
1083 * @param string $step_name Step name.
1084 *
1085 * @return string
1086 */
1087 function wpbc_setup_wizard__get_step_continue_url( $step_name ) {
1088
1089 if ( wpbc_setup_wizard__is_internal_step( $step_name ) ) {
1090 return wpbc_setup_wizard__get_step_target_url( $step_name );
1091 }
1092
1093 return add_query_arg(
1094 array(
1095 'wpbc_setup_wizard' => 'continue',
1096 'wpbc_setup_step' => $step_name,
1097 'wpbc_setup_from_page_step' => $step_name,
1098 '_wpnonce' => wp_create_nonce( 'wpbc_settings_url_nonce' ),
1099 ),
1100 wpbc_get_setup_wizard_page_url()
1101 );
1102 }
1103
1104 /**
1105 * Get setup step metadata for routing and UI.
1106 *
1107 * @param string $step_name Step name.
1108 *
1109 * @return array
1110 */
1111 function wpbc_setup_wizard__get_step_route_metadata( $step_name ) {
1112
1113 return array(
1114 'is_external' => ! wpbc_setup_wizard__is_internal_step( $step_name ),
1115 'target_url' => wpbc_setup_wizard__get_step_target_url( $step_name ),
1116 'title' => wpbc_setup_wizard__get_step_title( $step_name ),
1117 'heading' => wpbc_setup_wizard__get_step_heading( $step_name ),
1118 'description' => wpbc_setup_wizard__get_step_description( $step_name ),
1119 'save_behavior' => wpbc_setup_wizard__get_step_save_behavior( $step_name ),
1120 'target_selector' => wpbc_setup_wizard__get_step_target_selector( $step_name ),
1121 'scroll_selector' => wpbc_setup_wizard__get_step_scroll_selector( $step_name ),
1122 'highlight_selector' => wpbc_setup_wizard__get_step_highlight_selector( $step_name ),
1123 'highlight_disabled' => wpbc_setup_wizard__is_step_highlight_disabled( $step_name ) ? '1' : '0',
1124 'form_selector' => wpbc_setup_wizard__get_step_form_selector( $step_name ),
1125 'save_selector' => wpbc_setup_wizard__get_step_save_selector( $step_name ),
1126 'save_ajax_action' => wpbc_setup_wizard__get_step_save_ajax_action( $step_name ),
1127 'save_events' => wpbc_setup_wizard__get_step_save_events( $step_name ),
1128 'open_action' => wpbc_setup_wizard__get_step_open_action( $step_name ),
1129 'secondary_action_url' => wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'secondary_action_url' ),
1130 'secondary_action_label' => wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'secondary_action_label' ),
1131 );
1132 }
1133
1134 /**
1135 * Get first step after "Booking Type" for current profile.
1136 *
1137 * @return string
1138 */
1139 function wpbc_setup_wizard__get_first_profile_step() {
1140
1141 $route = wpbc_setup_wizard__get_profile_route();
1142
1143 return ( empty( $route ) ) ? 'color_theme' : $route[0];
1144 }
1145