# booking/11.6/includes/page-setup/setup_profiles.php

Booking Calendar, version 11.6. 1,198 lines.

- Page: https://pluginprobe.com/plugins/booking/11.6/code/includes/page-setup/setup_profiles.php
- Raw: https://pluginprobe.com/plugins/booking/11.6/raw/includes/page-setup/setup_profiles.php
- Modified: 2026-08-22T14:02:04+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/booking/11.6/code/includes/page-setup/setup_profiles.php#L10-L20`.

```php
<?php /**
 * @version 1.0
 * @description Booking profile helpers for Setup Wizard.
 * @category    Setup Wizard
 */

if ( ! defined( 'ABSPATH' ) ) exit;                                             // Exit if accessed directly.

/**
 * Store the WPBC admin fullscreen preference for the current user.
 *
 * The top navigation fullscreen button stores this value through WPBC_User_Custom_Data_Saver as
 * "booking_custom_is_full_screen" => array( 'value' => 'On|Off' ). Setup routing happens before redirects, so the
 * wizard updates the same preference and its immediate browser cookie when it moves between internal and external
 * setup screens. Synchronizing both values prevents an older cookie from overriding the active wizard state.
 * This sends a response cookie when headers remain available and always updates the cookie value for the current
 * request.
 *
 * @param bool $is_full_screen Whether WPBC admin pages should open in fullscreen mode.
 *
 * @return bool True when the saved user option changed; otherwise false.
 */
function wpbc_setup_wizard__set_full_screen_mode_for_current_user( $is_full_screen ) {

	$user_id = wpbc_get_current_user_id();

	if ( empty( $user_id ) ) {
		return false;
	}

	$full_screen_value = $is_full_screen ? 'On' : 'Off';
	$cookie_path       = ( defined( 'COOKIEPATH' ) && COOKIEPATH ) ? COOKIEPATH : '/';
	$cookie_domain     = defined( 'COOKIE_DOMAIN' ) ? COOKIE_DOMAIN : '';

	if ( ! headers_sent() ) {
		setcookie(
			'wpbc_admin_full_screen',
			$full_screen_value,
			time() + YEAR_IN_SECONDS,
			$cookie_path,
			$cookie_domain,
			is_ssl(),
			false
		);
	}

	// Keep cookie-aware page rendering deterministic during the current request.
	$_COOKIE['wpbc_admin_full_screen'] = $full_screen_value;

	return update_user_option(
		$user_id,
		'booking_custom_is_full_screen',
		array( 'value' => $full_screen_value )
	);
}

/**
 * Get persisted wizard choices.
 *
 * @return array
 */
function wpbc_setup_wizard__get_booking_wizard_data() {

	$wizard_data = get_bk_option( 'booking_wizard_data' );

	return ( empty( $wizard_data ) || ( ! is_array( $wizard_data ) ) ) ? array() : $wizard_data;
}

/**
 * Get selected booking type from wizard history or current options.
 *
 * @param array|null $wizard_data Wizard data.
 *
 * @return string
 */
function wpbc_setup_wizard__get_selected_booking_type( $wizard_data = null ) {

	if ( null === $wizard_data ) {
		$wizard_data = wpbc_setup_wizard__get_booking_wizard_data();
	}

	if (
		isset( $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_types'] )
		&& ( ! empty( $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_types'] ) )
	) {
		return (string) $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_types'];
	}

	if ( 'On' === get_bk_option( 'booking_range_selection_time_is_active' ) ) {
		return 'changeover_multi_dates_bookings';
	}

	if ( 'single' === get_bk_option( 'booking_type_of_day_selections' ) ) {
		return 'time_slots_appointments';
	}

	return 'full_days_bookings';
}

/**
 * Get selected appointment mode from wizard history or current options.
 *
 * @param array|null $wizard_data Wizard data.
 *
 * @return string
 */
function wpbc_setup_wizard__get_selected_appointments_type( $wizard_data = null ) {

	if ( null === $wizard_data ) {
		$wizard_data = wpbc_setup_wizard__get_booking_wizard_data();
	}

	// Appointment mode always uses the Service duration and start-time flow.
	if ( 'appointment' === wpbc_setup_wizard__get_selected_mode_id( $wizard_data ) ) {
		return 'durationtime';
	}

	if (
		isset( $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_appointments_type'] )
		&& ( ! empty( $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_appointments_type'] ) )
	) {
		return (string) $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_appointments_type'];
	}

	return 'rangetime';
}

/**
 * Check whether Booking Modes can resolve the current owner safely.
 *
 * Setup progress can be inspected while active plugins are still loading.
 * Before `init`, WordPress has not guaranteed that current-user pluggable
 * functions or just-in-time translation loading are ready. Mode registries
 * must therefore remain untouched until this boundary is reached.
 *
 * @return bool True when current-user and translation-dependent mode APIs can
 *              be used safely.
 */
function wpbc_setup_wizard__is_booking_modes_runtime_ready() {

	return did_action( 'init' ) && function_exists( 'wp_get_current_user' );
}

/**
 * Get the presentation mode selected in wizard history or owner settings.
 *
 * Missing historical mode data is expected on upgrades. In that case the
 * owner-scoped Booking Modes setting remains authoritative, with Classic as a
 * compatibility fallback when the feature module is unavailable.
 *
 * @param array|null $wizard_data Wizard data.
 *
 * @return string Allowed Booking Modes identifier.
 */
function wpbc_setup_wizard__get_selected_mode_id( $wizard_data = null ) {

	if ( null === $wizard_data ) {
		$wizard_data = wpbc_setup_wizard__get_booking_wizard_data();
	}

	$saved_mode_id = isset( $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_mode'] )
		? sanitize_key( (string) $wizard_data['save_and_continue__bookings_types']['wpbc_swp_booking_mode'] )
		: '';
	$standard_mode_ids = array( 'classic', 'appointment', 'rental' );

	if ( ! wpbc_setup_wizard__is_booking_modes_runtime_ready() ) {
		return in_array( $saved_mode_id, $standard_mode_ids, true ) ? $saved_mode_id : 'classic';
	}

	if ( function_exists( 'wpbc_booking_modes_get_allowed_mode_ids' ) ) {
		$allowed_mode_ids = wpbc_booking_modes_get_allowed_mode_ids();
		if ( in_array( $saved_mode_id, $allowed_mode_ids, true ) ) {
			return $saved_mode_id;
		}

		return wpbc_booking_modes_get_selected_mode_id();
	}

	return 'classic';
}

/**
 * Convert selected booking type to a setup profile.
 *
 * @param array|null $wizard_data Wizard data.
 *
 * @return string
 */
function wpbc_setup_wizard__get_selected_profile( $wizard_data = null ) {

	$booking_type = wpbc_setup_wizard__get_selected_booking_type( $wizard_data );

	if ( 'time_slots_appointments' === $booking_type ) {
		return ( 'durationtime' === wpbc_setup_wizard__get_selected_appointments_type( $wizard_data ) ) ? 'duration_appointments' : 'time_slots';
	}

	if ( 'changeover_multi_dates_bookings' === $booking_type ) {
		return 'changeover';
	}

	return 'full_day';
}

/**
 * Check whether profile uses booking times.
 *
 * @param string|null $profile Profile.
 *
 * @return bool
 */
function wpbc_setup_wizard__is_time_based_profile( $profile = null ) {

	$profile = ( null === $profile ) ? wpbc_setup_wizard__get_selected_profile() : $profile;

	return in_array( $profile, array( 'time_slots', 'duration_appointments' ), true );
}

/**
 * Check whether profile uses changeover mode.
 *
 * @param string|null $profile Profile.
 *
 * @return bool
 */
function wpbc_setup_wizard__is_changeover_profile( $profile = null ) {

	$profile = ( null === $profile ) ? wpbc_setup_wizard__get_selected_profile() : $profile;

	return ( 'changeover' === $profile );
}

/**
 * Get the internal setup wizard route before profile-specific external pages.
 *
 * @return array
 */
function wpbc_setup_wizard__get_intro_route() {

	$route = array( 'welcome' );
	if ( ! wpbc_is_this_demo() ) {
		$route[] = 'general_info';
	}
	$route[] = 'date_time_formats';
	$route[] = 'bookings_types';

	return $route;
}

/**
 * Get profile-specific setup route matrix after "Booking Type".
 *
 * Steps 1-4 stay inside the setup wizard. These compatibility routes remain
 * behavior-based; `wpbc_setup_wizard__get_profile_route()` can replace their
 * order for a presentation mode while reusing the same canonical controllers.
 *
 * @return array
 */
function wpbc_setup_wizard__get_profile_route_map() {

	$full_day_route = array(
		'form_structure',
		'date_selection',
		'date_availability',
		'color_theme',
		'wizard_publish',
		'get_started',
	);

	$changeover_route = array(
		'form_structure',
		'date_selection',
		'changeover_days',
		'date_availability',
		'color_theme',
		'wizard_publish',
		'get_started',
	);

	$time_based_route = array(
		'form_structure',
		'date_selection',
		'working_time',
		'time_slots_availability',
		'color_theme',
		'wizard_publish',
		'get_started',
	);

	return array(
		'full_day'              => $full_day_route,
		'changeover'            => $changeover_route,
		'time_slots'            => $time_based_route,
		'duration_appointments' => $time_based_route,
	);
}

/**
 * Get profile-specific setup route after "Booking Type".
 *
 * @param string|null $profile        Profile.
 * @param bool|null   $is_bfb_enabled Deprecated. Kept for backward-compatible calls.
 *
 * @return array
 */
function wpbc_setup_wizard__get_profile_route( $profile = null, $is_bfb_enabled = null ) {

	$profile   = ( null === $profile ) ? wpbc_setup_wizard__get_selected_profile() : $profile;
	$mode_id   = wpbc_setup_wizard__get_selected_mode_id();
	$route_map = wpbc_setup_wizard__get_profile_route_map();

	if ( 'appointment' === $mode_id ) {
		$route = array(
			'service_provider',
			'working_time',
			'date_availability',
			'time_slots_availability',
			'form_structure',
			'color_theme',
			'wizard_publish',
			'get_started',
		);
	} else {
		$route = isset( $route_map[ $profile ] ) ? $route_map[ $profile ] : $route_map['full_day'];
	}

	/**
	 * Filter the mode-aware Setup Wizard route after Step 4.
	 *
	 * @param array  $route   Ordered external setup steps.
	 * @param string $mode_id Active Booking Modes identifier.
	 * @param string $profile Booking behavior profile.
	 */
	$route = apply_filters( 'wpbc_setup_wizard_profile_route', $route, $mode_id, $profile );

	return is_array( $route ) ? array_values( $route ) : array();
}

/**
 * Get the next setup step from the current profile route.
 *
 * @param string $current_step Current step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_next_step_name( $current_step ) {

	$route = array_merge( wpbc_setup_wizard__get_intro_route(), wpbc_setup_wizard__get_profile_route() );

	$current_step_index = array_search( $current_step, $route, true );

	if ( false === $current_step_index ) {
		return '';
	}

	return isset( $route[ $current_step_index + 1 ] ) ? $route[ $current_step_index + 1 ] : '';
}

/**
 * Check whether the step is rendered inside the setup wizard page.
 *
 * @param string $step_name Step name.
 *
 * @return bool
 */
function wpbc_setup_wizard__is_internal_step( $step_name ) {

	return in_array( $step_name, array( 'welcome', 'general_info', 'date_time_formats', 'bookings_types' ), true );
}

/**
 * Detect the external setup step from the current WPBC admin page request.
 *
 * @return string
 */
function wpbc_setup_wizard__detect_step_from_admin_request() {

	// phpcs:ignore WordPress.Security.NonceVerification.Recommended
	$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended
	$tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : '';
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended
	$scroll_to_section = isset( $_GET['scroll_to_section'] ) ? sanitize_key( wp_unslash( $_GET['scroll_to_section'] ) ) : '';
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended
	$wpbc_ag_open = isset( $_GET['wpbc_ag_open'] ) ? sanitize_key( wp_unslash( $_GET['wpbc_ag_open'] ) ) : '';
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended
	$wpbc_calendar_section = isset( $_GET['wpbc_calendar_section'] ) ? sanitize_key( wp_unslash( $_GET['wpbc_calendar_section'] ) ) : '';
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended
	$wpbc_setup_step = isset( $_GET['wpbc_setup_step'] ) ? sanitize_key( wp_unslash( $_GET['wpbc_setup_step'] ) ) : '';

	if ( '' !== $wpbc_setup_step ) {
		return $wpbc_setup_step;
	}

	if ( 'wpbc-settings' === $page ) {
		if ( 'builder_booking_form' === $tab ) {
			return 'form_structure';
		}

		if ( 'form' === $tab ) {
			return 'form_structure';
		}

		if ( 'calendar_settings' === $tab ) {
			if ( 'changeover_times' === $wpbc_calendar_section ) {
				return 'changeover_days';
			}
			return 'date_selection';
		}

		if ( 'wpbc_general_settings_calendar_tab' === $scroll_to_section ) {
			return 'date_selection';
		}

		if ( 'themes' === $tab ) {
			return 'color_theme';
		}
	}

	if ( 'wpbc-availability' === $page ) {
		if ( 'time_slots_availability' === $tab ) {
			return 'time_slots_availability';
		}

		if ( 'working_time' === $wpbc_ag_open ) {
			return 'working_time';
		}

		return 'date_availability';
	}

	if ( 'wpbc-resources' === $page ) {
		return 'wizard_publish';
	}

	if ( 'wpbc-services' === $page && 'appointment_services' === $tab ) {
		return 'service_provider';
	}

	return '';
}

/**
 * Detect an external setup step from an admin URL.
 *
 * @param string $url URL.
 *
 * @return string
 */
function wpbc_setup_wizard__detect_step_from_admin_url( $url ) {

	if ( empty( $url ) ) {
		return '';
	}

	$url_parts = wp_parse_url( $url );
	if ( empty( $url_parts['query'] ) ) {
		return '';
	}

	$query_args = array();
	wp_parse_str( $url_parts['query'], $query_args );

	$page                  = isset( $query_args['page'] ) ? sanitize_key( $query_args['page'] ) : '';
	$tab                   = isset( $query_args['tab'] ) ? sanitize_key( $query_args['tab'] ) : '';
	$scroll_to_section     = isset( $query_args['scroll_to_section'] ) ? sanitize_key( $query_args['scroll_to_section'] ) : '';
	$wpbc_ag_open          = isset( $query_args['wpbc_ag_open'] ) ? sanitize_key( $query_args['wpbc_ag_open'] ) : '';
	$wpbc_calendar_section = isset( $query_args['wpbc_calendar_section'] ) ? sanitize_key( $query_args['wpbc_calendar_section'] ) : '';
	$wpbc_setup_step        = isset( $query_args['wpbc_setup_step'] ) ? sanitize_key( $query_args['wpbc_setup_step'] ) : '';

	if ( '' !== $wpbc_setup_step ) {
		return $wpbc_setup_step;
	}

	if ( 'wpbc-settings' === $page ) {
		if ( 'builder_booking_form' === $tab ) {
			return 'form_structure';
		}

		if ( 'form' === $tab ) {
			return 'form_structure';
		}

		if ( 'calendar_settings' === $tab ) {
			if ( 'changeover_times' === $wpbc_calendar_section ) {
				return 'changeover_days';
			}
			return 'date_selection';
		}

		if ( 'wpbc_general_settings_calendar_tab' === $scroll_to_section ) {
			return 'date_selection';
		}

		if ( 'themes' === $tab ) {
			return 'color_theme';
		}
	}

	if ( 'wpbc-availability' === $page ) {
		if ( 'time_slots_availability' === $tab ) {
			return 'time_slots_availability';
		}

		if ( 'working_time' === $wpbc_ag_open ) {
			return 'working_time';
		}

		return 'date_availability';
	}

	if ( 'wpbc-resources' === $page ) {
		return 'wizard_publish';
	}

	if ( 'wpbc-services' === $page && 'appointment_services' === $tab ) {
		return 'service_provider';
	}

	return '';
}

/**
 * Get human-readable setup step title.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_title( $step_name ) {
	$mode_id = wpbc_setup_wizard__get_selected_mode_id();

	if ( 'wizard_publish' === $step_name && 'appointment' === $mode_id ) {
		return __( 'Appointment Page', 'booking' );
	}

	if ( 'wizard_publish' === $step_name && 'rental' === $mode_id ) {
		return __( 'Rental Page', 'booking' );
	}

	if ( 'date_availability' === $step_name && 'appointment' === $mode_id ) {
		return __( 'Days Off', 'booking' );
	}

	$step_titles = array(
		'welcome'                 => __( 'Welcome', 'booking' ),
		'general_info'            => __( 'General Info', 'booking' ),
		'date_time_formats'       => __( 'Dates and Times', 'booking' ),
		'bookings_types'          => __( 'Booking Type', 'booking' ),
		'service_provider'        => __( 'Service and Provider', 'booking' ),
		'date_selection'          => __( 'Date Selection', 'booking' ),
		'changeover_days'         => __( 'Changeover Days', 'booking' ),
		'working_time'            => __( 'Working Time', 'booking' ),
		'time_slots_availability' => __( 'Time Slots', 'booking' ),
		'date_availability'       => __( 'Date Availability', 'booking' ),
		'form_structure'          => __( 'Booking Form', 'booking' ),
		'color_theme'             => __( 'Color Theme', 'booking' ),
		'wizard_publish'          => __( 'Publish', 'booking' ),
		'get_started'             => __( 'Get Started', 'booking' ),
	);

	return isset( $step_titles[ $step_name ] ) ? $step_titles[ $step_name ] : $step_name;
}

/**
 * Get action-oriented setup step heading shown in the floating setup bar.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_heading( $step_name ) {
	$mode_id = wpbc_setup_wizard__get_selected_mode_id();

	if ( 'wizard_publish' === $step_name && 'appointment' === $mode_id ) {
		return __( 'Review and test the Appointment page', 'booking' );
	}

	if ( 'wizard_publish' === $step_name && 'rental' === $mode_id ) {
		return __( 'Review and test the Rental page', 'booking' );
	}

	if ( 'date_availability' === $step_name && 'appointment' === $mode_id ) {
		return __( 'Block Provider days off and exceptions', 'booking' );
	}

	$step_headings = array(
		'welcome'                 => __( 'Start the initial setup', 'booking' ),
		'general_info'            => __( 'Confirm your business details', 'booking' ),
		'date_time_formats'       => __( 'Choose date and time formats', 'booking' ),
		'bookings_types'          => __( 'Choose the main booking workflow', 'booking' ),
		'service_provider'        => __( 'Review your first Service and Provider', 'booking' ),
		'form_structure'          => __( 'Select and save the booking form template', 'booking' ),
		'date_selection'          => __( 'Set how visitors select dates', 'booking' ),
		'changeover_days'         => __( 'Configure changeover days', 'booking' ),
		'working_time'            => __( 'Define when bookings can start and end', 'booking' ),
		'time_slots_availability' => __( 'Review availability for appointment slots', 'booking' ),
		'date_availability'       => __( 'Set date availability rules', 'booking' ),
		'color_theme'             => __( 'Choose the calendar appearance', 'booking' ),
		'wizard_publish'          => __( 'Publish the booking form on your site', 'booking' ),
		'get_started'             => __( 'Complete setup and manage bookings', 'booking' ),
	);

	return isset( $step_headings[ $step_name ] ) ? $step_headings[ $step_name ] : wpbc_setup_wizard__get_step_title( $step_name );
}

/**
 * Get setup step guidance shown in the floating setup bar.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_description( $step_name ) {
	$mode_id = wpbc_setup_wizard__get_selected_mode_id();

	if ( 'wizard_publish' === $step_name && 'appointment' === $mode_id ) {
		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' );
	}

	if ( 'wizard_publish' === $step_name && 'rental' === $mode_id ) {
		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' );
	}

	if ( 'date_availability' === $step_name && 'appointment' === $mode_id ) {
		return __( 'Use the canonical Days Availability calendar to block holidays, leave, and other Provider-specific exceptions.', 'booking' );
	}

	$step_descriptions = array(
		'welcome'                 => __( 'Begin the guided setup. You can leave the wizard at any time and continue later from the setup bar.', 'booking' ),
		'general_info'            => __( 'Review the business name, contact details, and basic information used while preparing the booking configuration.', 'booking' ),
		'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' ),
		'bookings_types'          => __( 'Select whether bookings use full days, appointment time slots, or changeover-style date ranges. This choice controls the next configuration steps.', 'booking' ),
		'service_provider'        => __( 'QuickStart created a starter Service assigned to an existing booking resource. Review its duration, buffers, price, Booking Form, and Provider assignment.', 'booking' ),
		'form_structure'          => __( 'Open the form template chooser, select the template that matches your booking type, and save the Booking Form page before continuing.', 'booking' ),
		'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' ),
		'changeover_days'         => __( 'Enable and review check-in/check-out changeover days, including diagonal or vertical markings and related changeover options.', 'booking' ),
		'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' ),
		'time_slots_availability' => __( 'Check the generated appointment slots and adjust any slot-specific availability rules before choosing the calendar appearance.', 'booking' ),
		'date_availability'       => __( 'Set which weekdays, dates, booking windows, and buffer rules should be available or unavailable for visitors.', 'booking' ),
		'color_theme'             => __( 'Choose the calendar skin and visual style so the booking form fits your site before you publish it.', 'booking' ),
		'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' ),
		'get_started'             => __( 'Finish the setup wizard. After this, the setup bar will disappear and you can start managing bookings normally.', 'booking' ),
	);

	return isset( $step_descriptions[ $step_name ] ) ? $step_descriptions[ $step_name ] : '';
}

/**
 * Get external setup step UI integration matrix.
 *
 * Each entry describes how the floating setup bar should connect a wizard step
 * to an existing WPBC admin page: what to open, what to scroll/highlight, which
 * form/save control belongs to this step, and which JS events mean the external
 * page has saved successfully.
 *
 * @return array
 */
function wpbc_setup_wizard__get_step_ui_matrix() {
	$publish_step_ui = wpbc_setup_wizard__get_publish_step_ui();
	$time_slots_step_ui = array(
		'save_behavior'      => 'link_only',
		'target_selector'    => '.wpbc_admin_page__tab__time_slots_availability, .wpbc_ts_page',
		'scroll_selector'    => '.wpbc_admin_page__tab__time_slots_availability:visible, .wpbc_ts_page:visible',
		'highlight_disabled' => '1',
	);
	$date_availability_step_ui = array(
		'save_behavior'      => 'link_only',
		'target_selector'    => '.wpbc_admin_page__tab__availability, .wpbc_ajx_availability_container',
		'scroll_selector'    => '.wpbc_ajx_availability_container:visible, .wpbc_admin_page__tab__availability:visible',
		'highlight_disabled' => '1',
	);

	if ( 'rental' === wpbc_setup_wizard__get_selected_mode_id() ) {
		$date_availability_step_ui = array(
			'save_behavior'      => 'manual_save_required',
			'target_selector'    => '.wpbc_admin_page__tab__general_availability, [data-wpbc-ag-settings-form="1"]',
			'scroll_selector'    => '[data-wpbc-ag-settings-form="1"]:visible, .wpbc_admin_page__tab__general_availability:visible',
			'highlight_disabled' => '1',
			'form_selector'      => '[data-wpbc-ag-settings-form="1"]',
			'save_selector'      => '[data-wpbc-ag-save="1"]',
			'save_ajax_action'   => 'WPBC_AJX_AVAILABILITY_GENERAL_SAVE',
			'save_events'        => 'wpbc:availability-general:settings-saved,wpbc:setup-wizard:step-saved',
		);
	}

	if ( 'appointment' === wpbc_setup_wizard__get_selected_mode_id() ) {
		$form_builder_url = wpbc_get_settings_url() . '&tab=builder_booking_form';
		$form_builder_url = add_query_arg(
			array(
				'wpbc_bfb_panel' => 'add_fields',
				'wpbc_bfb_group' => 'fields-times',
				'wpbc_bfb_focus' => 'weekday_starttime',
			),
			$form_builder_url
		);

		$time_slots_step_ui['secondary_action_url']   = wpbc_setup_wizard__add_setup_context_to_url( $form_builder_url, 'time_slots_availability' );
		$time_slots_step_ui['secondary_action_label'] = __( 'Adjust slot-specific availability rules', 'booking' );
	}

	return array(
		'service_provider'        => array(
			'save_behavior'      => 'link_only',
			'target_selector'    => '.wpbc_appointment_services_page',
			'scroll_selector'    => '.wpbc_appointment_services_page:visible',
			'highlight_disabled' => '1',
		),
		'form_structure'          => array(
			'save_behavior'      => 'manual_save_required',
			'target_selector'    => '.wpbc_admin_page__tab__builder_booking_form, #wpbc_form_field_free',
			'scroll_selector'    => '.wpbc_bfb_popup_modal__forms_loading_section:visible, #wpbc_form_field_free:visible, .wpbc_admin_page__tab__builder_booking_form:visible',
			'highlight_disabled' => '1',
			'form_selector'      => '#wpbc_form_field_free',
			'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]',
			'save_events'        => 'wpbc:bfb:form:ajax_saved,wpbc:bfb:form:saved,wpbc:bfb:save:done,wpbc:bfb:ajax_saved,wpbc:setup-wizard:step-saved',
		),
		'date_selection'          => array(
			'save_behavior'      => 'manual_save_required',
			'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"]',
			'scroll_selector'    => '[data-group="settings-calendar-days-selection"]:visible, .wpbc_calendar__inspector_settings:visible, [data-wpbc-calendar-settings-form="1"]:visible',
			'highlight_selector' => '[data-group="settings-calendar-days-selection"]:visible, .wpbc_calendar__inspector_settings:visible, [data-wpbc-calendar-settings-form="1"]:visible',
			'form_selector'      => '[data-wpbc-calendar-settings-form="1"], #wpbc_general_settings_form',
			'save_selector'      => '[data-wpbc-calendar-save="1"], #wpbc_general_settings_form .wpbc_submit_button_trigger, #wpbc_general_settings_form .wpbc_submit_button',
			'save_ajax_action'   => 'WPBC_AJX_SETTINGS_CALENDAR_SAVE',
			'save_events'        => 'wpbc:setup-wizard:step-saved',
		),
		'changeover_days'         => array(
			'save_behavior'      => 'manual_save_required',
			'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"]',
			'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',
			'highlight_selector' => '[data-group="settings-calendar-time"]:visible, [data-wpbc-calendar-changeover-settings="1"]:visible, [name="booking_range_selection_time_is_active"]:visible',
			'form_selector'      => '[data-wpbc-calendar-settings-form="1"]',
			'save_selector'      => '[data-wpbc-calendar-save="1"]',
			'save_ajax_action'   => 'WPBC_AJX_SETTINGS_CALENDAR_SAVE',
			'save_events'        => 'wpbc:setup-wizard:step-saved',
		),
		'working_time'            => array(
			'save_behavior'      => 'manual_save_required',
			'target_selector'    => '[data-group="general-availability-working-time"], .wpbc_ag_working_time_block, .wpbc_admin_page__tab__general_availability',
			'scroll_selector'    => '[data-group="general-availability-working-time"]:visible, .wpbc_ag_working_time_block:visible, [data-wpbc-ag-settings-form="1"]:visible',
			'highlight_selector' => '[data-group="general-availability-working-time"]:visible, .wpbc_ag_working_time_block:visible',
			'form_selector'      => '[data-wpbc-ag-settings-form="1"]',
			'save_selector'      => '[data-wpbc-ag-save="1"]',
			'save_ajax_action'   => 'WPBC_AJX_AVAILABILITY_GENERAL_SAVE',
			'save_events'        => 'wpbc:availability-general:settings-saved,wpbc:setup-wizard:step-saved',
			'open_action'        => 'availability_section',
		),
		'time_slots_availability' => $time_slots_step_ui,
		'date_availability'       => $date_availability_step_ui,
		'color_theme'             => array(
			'save_behavior'      => 'manual_save_required',
			'target_selector'    => '.wpbc_admin_page__tab__themes, [data-wpbc-theme-page="1"]',
			'scroll_selector'    => '.wpbc_theme__inspector_settings:visible, [data-wpbc-theme-settings-form="1"]:visible, .wpbc_theme_rightbar_panels:visible',
			'highlight_selector' => '.wpbc_theme__inspector_settings:visible, [data-wpbc-theme-settings-form="1"]:visible, .wpbc_theme_rightbar_panels:visible',
			'form_selector'      => '[data-wpbc-theme-settings-form="1"]',
			'save_selector'      => '[data-wpbc-theme-save="1"]',
			'save_ajax_action'   => 'WPBC_AJX_SETTINGS_THEMES_SAVE',
			'save_events'        => 'wpbc:setup-wizard:step-saved',
		),
		'wizard_publish'          => $publish_step_ui,
		'get_started'             => array(
			'save_behavior'      => 'complete',
			'target_selector'    => '.wpbc_admin_page',
			'scroll_selector'    => '.wpbc_admin_page:visible',
			'highlight_disabled' => '1',
		),
	);
}

/**
 * Determine whether Setup Wizard publishing should use the shared Resource catalog.
 *
 * The compatibility resolver remains authoritative so an older paid edition,
 * the temporary legacy preference, or the support rollback constant keeps the
 * wizard connected to the released legacy Resources renderer.
 *
 * @return bool True when the new Resource catalog is the effective renderer.
 */
function wpbc_setup_wizard__uses_new_resources_catalog() {
	return function_exists( 'wpbc_booking_resources_catalog_should_use_new_renderer' )
		&& wpbc_booking_resources_catalog_should_use_new_renderer();
}

/**
 * Get the renderer-aware Setup Wizard publishing integration.
 *
 * The new catalog is opened through its domain action event after the initial
 * AJAX response. The legacy selectors are intentionally retained unchanged
 * while the compatibility renderer remains available in Booking Calendar 11.6.
 *
 * @param bool|null $use_new_catalog Optional renderer override for deterministic tests. Null uses the effective compatibility resolver.
 *
 * @return array<string,string> Setup bar selector and action configuration.
 */
function wpbc_setup_wizard__get_publish_step_ui( $use_new_catalog = null ) {
	if ( null === $use_new_catalog ) {
		$use_new_catalog = wpbc_setup_wizard__uses_new_resources_catalog();
	}

	if ( true === $use_new_catalog ) {
		return array(
			'save_behavior'      => 'link_only',
			'target_selector'    => '#wpbc_catalog_booking_resources',
			'scroll_selector'    => '#wpbc_catalog_booking_resources:visible',
			'highlight_disabled' => '1',
			'open_action'        => 'catalog_publish',
		);
	}

	return array(
		'save_behavior'      => 'link_only',
		'target_selector'    => '.ui_group__publish_btn, .wpbc_resource_field__publish, .wpbc_resource_publish, .wpbc_publish_resources, [data-wpbc-resource-publish]',
		'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',
		'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',
		'highlight_all'      => '1',
		'open_action'        => 'publish_area',
	);
}

/**
 * Get one UI integration value from the external setup step matrix.
 *
 * @param string $step_name Step name.
 * @param string $key       Matrix key.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, $key ) {

	$ui_matrix = wpbc_setup_wizard__get_step_ui_matrix();

	return ( isset( $ui_matrix[ $step_name ][ $key ] ) ) ? (string) $ui_matrix[ $step_name ][ $key ] : '';
}

/**
 * Get save behavior for external setup step.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_save_behavior( $step_name ) {

	$save_behavior = wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'save_behavior' );

	return ( ! empty( $save_behavior ) ) ? $save_behavior : 'link_only';
}

/**
 * Get DOM selector to highlight/scroll on external setup pages.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_target_selector( $step_name ) {

	return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'target_selector' );
}

/**
 * Get DOM selector to scroll on external setup pages.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_scroll_selector( $step_name ) {

	$scroll_selector = wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'scroll_selector' );

	return ( ! empty( $scroll_selector ) ) ? $scroll_selector : wpbc_setup_wizard__get_step_target_selector( $step_name );
}

/**
 * Get DOM selector to highlight on external setup pages.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_highlight_selector( $step_name ) {

	if ( wpbc_setup_wizard__is_step_highlight_disabled( $step_name ) ) {
		return '';
	}

	$highlight_selector = wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'highlight_selector' );

	return ( ! empty( $highlight_selector ) ) ? $highlight_selector : wpbc_setup_wizard__get_step_target_selector( $step_name );
}

/**
 * Check if external setup page highlighting is disabled for a step.
 *
 * @param string $step_name Step name.
 *
 * @return bool
 */
function wpbc_setup_wizard__is_step_highlight_disabled( $step_name ) {

	return '1' === wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'highlight_disabled' );
}

/**
 * Get page open action for external setup pages.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_open_action( $step_name ) {

	return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'open_action' );
}

/**
 * Get browser events that confirm an external setup page save.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_save_events( $step_name ) {

	return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'save_events' );
}

/**
 * Get AJAX action name used by the existing page save button related to the setup step.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_save_ajax_action( $step_name ) {

	return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'save_ajax_action' );
}

/**
 * Get DOM selector for the existing page form related to the setup step.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_form_selector( $step_name ) {

	return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'form_selector' );
}

/**
 * Get selector for the existing page save button related to the setup step.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_save_selector( $step_name ) {

	return wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'save_selector' );
}

/**
 * Add setup guide context to a target URL.
 *
 * @param string $url       URL.
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__add_setup_context_to_url( $url, $step_name ) {

	return add_query_arg(
		array(
			'wpbc_setup'      => '1',
			'wpbc_setup_step' => $step_name,
		),
		$url
	);
}

/**
 * Get the Form Builder template search key for the selected booking type.
 *
 * @return string
 */
function wpbc_setup_wizard__get_bfb_template_search_key() {

	$booking_wizard_data_arr = get_bk_option( 'booking_wizard_data' );
	if (
		empty( $booking_wizard_data_arr )
		|| ! is_array( $booking_wizard_data_arr )
		|| empty( $booking_wizard_data_arr['save_and_continue__bookings_types'] )
		|| ! is_array( $booking_wizard_data_arr['save_and_continue__bookings_types'] )
	) {
		return '';
	}

	$booking_type = isset( $booking_wizard_data_arr['save_and_continue__bookings_types']['wpbc_swp_booking_types'] )
		? $booking_wizard_data_arr['save_and_continue__bookings_types']['wpbc_swp_booking_types']
		: '';
	$url_sep      = defined( 'WPBC_BFB_TEMPLATE_SEARCH_OR_SEPARATOR_URL' ) ? WPBC_BFB_TEMPLATE_SEARCH_OR_SEPARATOR_URL : '^';

	if ( 'full_days_bookings' === $booking_type ) {
		return 'full-days' . $url_sep . 'dates_form';
	}

	if ( 'time_slots_appointments' === $booking_type ) {
		if ( 'rangetime' === wpbc_setup_wizard__get_selected_appointments_type( $booking_wizard_data_arr ) ) {
			return 'times' . $url_sep . 'time slots';
		}

		return 'appointments' . $url_sep . 'service';
	}

	if ( 'changeover_multi_dates_bookings' === $booking_type ) {
		return 'changeover' . $url_sep . 'triangles';
	}

	return '';
}

/**
 * Get the existing admin page URL that should handle a setup step.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_target_url( $step_name ) {
	$target_fragment = '';

	if ( wpbc_setup_wizard__is_internal_step( $step_name ) ) {
		return add_query_arg( 'current_step', $step_name, wpbc_get_setup_wizard_page_url() );
	}

	switch ( $step_name ) {
		case 'service_provider':
			$url = function_exists( 'wpbc_booking_modes_get_canonical_page_url' )
				? wpbc_booking_modes_get_canonical_page_url( 'wpbc-services__appointment_services' )
				: admin_url( 'admin.php?page=wpbc-services&tab=appointment_services' );
			break;

		case 'date_selection':
			$url = function_exists( 'wpbc_get_settings_calendar_url' )
				? wpbc_get_settings_calendar_url()
				: wpbc_get_settings_url() . '&tab=calendar_settings';
			$url = add_query_arg( 'wpbc_calendar_section', 'days_selection', $url );
			break;

		case 'changeover_days':
			$url = function_exists( 'wpbc_get_settings_calendar_url' )
				? wpbc_get_settings_calendar_url()
				: wpbc_get_settings_url() . '&tab=calendar_settings';
			$url = add_query_arg( 'wpbc_calendar_section', 'changeover_times', $url );
			break;

		case 'working_time':
			$url = function_exists( 'wpbc_get_general_availability_url' )
				? wpbc_get_general_availability_url()
				: admin_url( 'admin.php?page=wpbc-availability&tab=general_availability' );
			$url = add_query_arg( 'wpbc_ag_open', 'working_time', $url );
			break;

		case 'time_slots_availability':
			$url = function_exists( 'wpbc_get_time_slots_availability_url' )
				? wpbc_get_time_slots_availability_url()
				: admin_url( 'admin.php?page=wpbc-availability&tab=time_slots_availability' );
			break;

		case 'date_availability':
			if ( 'rental' === wpbc_setup_wizard__get_selected_mode_id() ) {
				$url = function_exists( 'wpbc_get_general_availability_url' )
					? wpbc_get_general_availability_url()
					: admin_url( 'admin.php?page=wpbc-availability&tab=general_availability' );
				break;
			}

			$canonical_url = function_exists( 'wpbc_booking_modes_get_canonical_page_url' )
				? wpbc_booking_modes_get_canonical_page_url( 'wpbc-availability__availability' )
				: '';
			$url = ! empty( $canonical_url ) ? $canonical_url : admin_url( 'admin.php?page=wpbc-availability&tab=availability' );
			break;

		case 'form_structure':
			$url = wpbc_get_settings_url() . '&tab=builder_booking_form';

			$template_search_key = wpbc_setup_wizard__get_bfb_template_search_key();
			if ( ! empty( $template_search_key ) ) {
				$url = add_query_arg( 'auto_open_template', $template_search_key, $url );
			}
			break;

		case 'color_theme':
			$url = function_exists( 'wpbc_get_settings_themes_url' )
				? wpbc_get_settings_themes_url()
				: wpbc_get_settings_url() . '&tab=themes';
			break;

		case 'wizard_publish':
			$url = function_exists( 'wpbc_booking_modes_get_canonical_page_url' )
				? wpbc_booking_modes_get_canonical_page_url( 'wpbc-resources__resources' )
				: '';
			if ( empty( $url ) ) {
				$url = wpbc_get_resources_url();
			}
			$url             = add_query_arg( 'tab', 'resources', $url );
			$target_fragment = wpbc_setup_wizard__uses_new_resources_catalog()
				? 'wpbc_catalog_booking_resources'
				: 'wpbc_booking_resource_table';
			break;

		case 'get_started':
			$url = wpbc_get_bookings_url() . '&tab=vm_booking_listing';
			break;

		default:
			$url = wpbc_get_setup_wizard_page_url();
			break;
	}

	$url = wpbc_setup_wizard__add_setup_context_to_url( $url, $step_name );

	if ( ! empty( $target_fragment ) ) {
		$url .= '#' . $target_fragment;
	}

	return $url;
}

/**
 * Get URL that completes an external setup step and redirects to the next mapped step.
 *
 * @param string $step_name Step name.
 *
 * @return string
 */
function wpbc_setup_wizard__get_step_continue_url( $step_name ) {

	if ( wpbc_setup_wizard__is_internal_step( $step_name ) ) {
		return wpbc_setup_wizard__get_step_target_url( $step_name );
	}

	return add_query_arg(
		array(
			'wpbc_setup_wizard'        => 'continue',
			'wpbc_setup_step'          => $step_name,
			'wpbc_setup_from_page_step' => $step_name,
			'_wpnonce'                 => wp_create_nonce( 'wpbc_settings_url_nonce' ),
		),
		wpbc_get_setup_wizard_page_url()
	);
}

/**
 * Get setup step metadata for routing and UI.
 *
 * @param string $step_name Step name.
 *
 * @return array
 */
function wpbc_setup_wizard__get_step_route_metadata( $step_name ) {

	return array(
		'is_external'   => ! wpbc_setup_wizard__is_internal_step( $step_name ),
		'target_url'    => wpbc_setup_wizard__get_step_target_url( $step_name ),
		'title'         => wpbc_setup_wizard__get_step_title( $step_name ),
		'heading'       => wpbc_setup_wizard__get_step_heading( $step_name ),
		'description'   => wpbc_setup_wizard__get_step_description( $step_name ),
		'save_behavior' => wpbc_setup_wizard__get_step_save_behavior( $step_name ),
		'target_selector' => wpbc_setup_wizard__get_step_target_selector( $step_name ),
		'scroll_selector' => wpbc_setup_wizard__get_step_scroll_selector( $step_name ),
		'highlight_selector' => wpbc_setup_wizard__get_step_highlight_selector( $step_name ),
		'highlight_disabled' => wpbc_setup_wizard__is_step_highlight_disabled( $step_name ) ? '1' : '0',
		'form_selector'   => wpbc_setup_wizard__get_step_form_selector( $step_name ),
		'save_selector'   => wpbc_setup_wizard__get_step_save_selector( $step_name ),
		'save_ajax_action' => wpbc_setup_wizard__get_step_save_ajax_action( $step_name ),
		'save_events'     => wpbc_setup_wizard__get_step_save_events( $step_name ),
		'open_action'     => wpbc_setup_wizard__get_step_open_action( $step_name ),
		'secondary_action_url' => wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'secondary_action_url' ),
		'secondary_action_label' => wpbc_setup_wizard__get_step_ui_matrix_value( $step_name, 'secondary_action_label' ),
	);
}

/**
 * Get first step after "Booking Type" for current profile.
 *
 * @return string
 */
function wpbc_setup_wizard__get_first_profile_step() {

	$route = wpbc_setup_wizard__get_profile_route();

	return ( empty( $route ) ) ? 'color_theme' : $route[0];
}

```
