get_booking_form() * - simple : fallback wpbc_simple_form__get_booking_form__as_html() * * Keeps legacy behavior by default: * - If BFB is disabled => never touches DB and returns legacy/simple only. * * @package Booking Calendar * @since 11.0.x * @file ../includes/_front_end/class-fe-form-source-resolver.php */ if ( ! defined( 'ABSPATH' ) ) { exit; } require_once WPBC_PLUGIN_DIR . '/includes/_front_end/class-fe-settings.php'; /** * MultiUser ownership helper (core-safe; MU add-on hooks via filters). * * @since 11.0.x */ class WPBC_FE_MU { public static function get_owner_user_id_for_resource( $resource_id ) { $resource_id = (int) $resource_id; $user_id = apply_bk_filter( 'get_user_of_this_bk_resource', false, $resource_id ); if ( $user_id ) { return $user_id; } return 0; } public static function is_user_super_booking_admin( $user_id ) { $user_id = intval( $user_id ); if ( ( class_exists( 'wpdev_bk_multiuser' ) ) && ( $user_id > 0 ) ) { $is_user_super_admin = apply_bk_filter( 'is_user_super_admin', $user_id ); if ( ! $is_user_super_admin ) { return false; } } return true; } public static function get_current_user_id() { return wpbc_get_current_user_id(); } } /** * Resolver: decides BFB vs legacy vs simple and returns normalized loader args. * * @since 11.0.x */ class WPBC_FE_Form_Source_Resolver { /** * Resolve form source. * * Input keys: * - resource_id (int) * - form_slug (string) // from shortcode form_type * - form_status (string) // published|preview * - custom_params (array) // parsed from options parser * - legacy_instance (wpdev_booking|null) // optional * * Output: * - engine: 'bfb_db'|'legacy'|'simple' * - apply_after_load_filter: bool * - bfb_loader_args: array * - fallback_chain: array * * @param array $req * * @return array */ public static function resolve( $req ) { $req = is_array( $req ) ? $req : array(); $resource_id = isset( $req['resource_id'] ) ? (int) $req['resource_id'] : 0; $form_slug_raw = isset( $req['form_slug'] ) ? (string) $req['form_slug'] : ''; $form_status_raw = isset( $req['form_status'] ) ? (string) $req['form_status'] : ''; $custom_params = ( isset( $req['custom_params'] ) && is_array( $req['custom_params'] ) ) ? $req['custom_params'] : array(); $legacy_instance = isset( $req['legacy_instance'] ) ? $req['legacy_instance'] : null; // --------------------------------------------------------------------- // Step A: determine slug + status (contract). // --------------------------------------------------------------------- $form_slug = sanitize_text_field( $form_slug_raw ); if ( '' === $form_slug ) { $form_slug = 'standard'; } $status = sanitize_key( $form_status_raw ); // Normalize synonyms. if ( in_array( $status, array( 'publish', 'published' ), true ) ) { $status = 'published'; } if ( 'preview' !== $status ) { $status = 'published'; } // --------------------------------------------------------------------- // If BFB is NOT enabled => legacy only (no DB touches). // --------------------------------------------------------------------- if ( ! class_exists( 'WPBC_Frontend_Settings' ) || ! WPBC_Frontend_Settings::is_bfb_enabled( null ) ) { return self::fallback_to_legacy_or_simple( $legacy_instance ); } // If BFB runtime is not present => legacy only. if ( ! class_exists( 'WPBC_BFB_Form_Loader' ) ) { return self::fallback_to_legacy_or_simple( $legacy_instance ); } // If storage is not available => legacy only. if ( ! class_exists( 'WPBC_BFB_Form_Storage' ) || ! method_exists( 'WPBC_BFB_Form_Storage', 'get_form_row_by_key' ) ) { return self::fallback_to_legacy_or_simple( $legacy_instance ); } // Optional: if table does not exist, skip DB. if ( function_exists( 'wpbc_is_table_exists' ) && ! wpbc_is_table_exists( 'booking_form_structures' ) ) { return self::fallback_to_legacy_or_simple( $legacy_instance ); } // --------------------------------------------------------------------- // Step B: determine owner_user_id (MultiUser, filterable). // --------------------------------------------------------------------- $owner_user_id = 0; // Caller may explicitly pass owner_user_id for future scopes. if ( isset( $req['owner_user_id'] ) ) { $owner_user_id = max( 0, (int) $req['owner_user_id'] ); } else { $owner_user_id = WPBC_FE_MU::get_owner_user_id_for_resource( $resource_id ); $is_super_user = WPBC_FE_MU::is_user_super_booking_admin( $owner_user_id ); // If owner_user_id is is_super_user booking admin => treat as global (0). if ( $is_super_user ) { $owner_user_id = 0; } else { $owner_user_id = max( 0, (int) $owner_user_id ); if ( $owner_user_id > 0 ) { // If this is "Regular user"? Check if we can load "custom booking form" ? if ( ( get_bk_option( 'booking_is_custom_forms_for_regular_users' ) !== 'On' ) && ( 'standard' !== $form_slug ) ) { $form_slug = 'standard'; } } } } // --------------------------------------------------------------------- // Step C: query order (owner_user_id -> global -> standard fallback). // Resolver decides the final key to ask the loader for. // --------------------------------------------------------------------- $fallback_chain = array(); // Info: Hook for addons. $allow_standard_fallback = (bool) apply_filters( 'wpbc_fe_resolver_allow_fallback_to_standard', true, $form_slug, $status, $owner_user_id, $req ); // Try: (slug,status,owner_user_id) then (slug,status,global) Return like this: $found = [ "form_id":1, "form_slug":"standard", "status":"published", "owner_user_id":0 ] $found = self::try_find_row( $form_slug, $status, $owner_user_id, $fallback_chain ); // If not found, and user is not super booking admin, then find row for "SUPER BOOKING ADMIN". // INFO: block this falback. 2026-03-05 20:25. Do not fallback to supr booking admin user. Fallback to legacy mode, instead. // if ( ! $found && ( $owner_user_id > 0 ) ) { // $found = self::try_find_row( $form_slug, $status, 0, $fallback_chain ); // } // If missing and slug != standard => try STANDARD (same status). if ( ! $found && $allow_standard_fallback && ( 'standard' !== $form_slug ) ) { $found = self::try_find_row( 'standard', $status, $owner_user_id, $fallback_chain ); // The same for the super booking admin. if ( ! $found && ( $owner_user_id > 0 ) ) { $found = self::try_find_row( 'standard', $status, 0, $fallback_chain ); } } if ( $found ) { // Build args for BFB loader. $bfb_args = array( 'form_slug' => (string) $found['form_slug'], 'status' => (string) $found['status'], 'owner_user_id' => (int) $found['owner_user_id'], 'resource_id' => (int) $resource_id, ); // Pass form_id if known (fast, deterministic). if ( ! empty( $found['form_id'] ) ) { $bfb_args['form_id'] = (int) $found['form_id']; } $result = array( 'engine' => 'bfb_db', 'apply_after_load_filter'=> true, 'bfb_loader_args' => $bfb_args, 'fallback_chain' => $fallback_chain, ); /** * // Info: Hook for addons. Final override point (future scopes, preview rules, etc). * * @param array $result * @param array $req */ return (array) apply_filters( 'wpbc_fe_form_source_resolution', $result, $req ); } // Not found in DB => legacy fallback (existing behavior). return self::fallback_to_legacy_or_simple( $legacy_instance ); } // --------------------------------------------------------------------- // Internals // --------------------------------------------------------------------- /** * Try find DB row for (slug,status,owner) and record attempt in chain. * * @param string $slug * @param string $status * @param int $owner_user_id * @param array $chain (by ref) * * @return array|false */ private static function try_find_row( $slug, $status, $owner_user_id, &$chain ) { $slug = sanitize_text_field( (string) $slug ); $status = sanitize_key( (string) $status ); $owner_user_id = max( 0, (int) $owner_user_id ); $chain[] = array( 'form_slug' => $slug, 'status' => $status, 'owner_user_id' => $owner_user_id, ); // Here we already have the booking form configuration: row.advanced_form = "
..." also row.content_form = '...', row.settings_json = '{...}' $row = WPBC_BFB_Form_Storage::get_form_row_by_key( $slug, $status, $owner_user_id ); if ( ! $row || empty( $row->booking_form_id ) ) { return false; } return array( 'form_id' => (int) $row->booking_form_id, 'form_slug' => $slug, 'status' => $status, 'owner_user_id' => isset( $row->owner_user_id ) ? max( 0, (int) $row->owner_user_id ) : $owner_user_id, ); } /** * Legacy/simple fallback result. * * @param mixed $legacy_instance * * @return array */ private static function fallback_to_legacy_or_simple( $legacy_instance ) { if ( ( ! empty( $legacy_instance ) ) && ( false !== $legacy_instance->wpdev_bk_personal ) && ( 'On' != get_bk_option( 'booking_is_use_simple_booking_form' ) ) ) { return array( 'engine' => 'legacy', 'apply_after_load_filter' => false, 'bfb_loader_args' => array(), 'fallback_chain' => array(), ); } return array( 'engine' => 'simple', 'apply_after_load_filter' => true, 'bfb_loader_args' => array(), 'fallback_chain' => array(), ); } } /** * BFB resolver for "Booking Form Data" template (content_form). * * Purpose: * - When BFB DB engine is used for a form, the correct booking "data template" * must come from wp_booking_form_structures.content_form (not booking_form_show option). * - This is used by wpbc_get__booking_form_data__show() to replace [field] shortcodes * with values from booking form_data string. * * Design notes: * - Uses WPBC_FE_Form_Source_Resolver::resolve() to keep the same decision chain as front-end rendering. * - Reads the template via wpbc_bfb_get_booking_form_pair() (must expose content_form). * - Safe no-op when BFB / resolver is not available. * * @since 11.0.x */ class WPBC_BFB_Booking_Data_Content_Resolver { /** * Try to override legacy $booking_form_show by BFB content_form (resolver-driven). * * @param string $booking_form_show Legacy resolved booking_form_show. * @param int $resource_id Booking resource ID. * @param string $form_slug Form slug/name (legacy: 'standard' or custom form name). * @param string $form_data Booking form_data string from DB. * @param array $params Optional. Context override (e.g. form_status/preview token). * * @return string Possibly overridden template. */ public static function maybe_override_booking_form_show_by_bfb( $booking_form_show, $resource_id, $form_slug, $form_data, $params = array() ) { $booking_form_show = (string) $booking_form_show; $resource_id = (int) $resource_id; $form_slug = (string) $form_slug; $form_data = (string) $form_data; if ( '' === $form_slug ) { $form_slug = 'standard'; } // Hard requirements for BFB resolver path. if ( ! class_exists( 'WPBC_FE_Form_Source_Resolver' ) ) { return $booking_form_show; } if ( ! class_exists( 'WPBC_BFB_Form_Loader' ) ) { return $booking_form_show; } // ------------------------------------------------------------ // Preview-aware context (critical fix). // ------------------------------------------------------------ $ctx = array(); if ( function_exists( 'wpbc_get_request_form_context' ) ) { $ctx = wpbc_get_request_form_context(); } $ctx = wp_parse_args( ( is_array( $params ) ? $params : array() ), ( is_array( $ctx ) ? $ctx : array() ) ); $form_status = ( isset( $ctx['form_status'] ) ) ? sanitize_key( $ctx['form_status'] ) : 'published'; if ( in_array( $form_status, array( 'publish', 'published' ), true ) ) { $form_status = 'published'; } if ( 'preview' !== $form_status ) { $form_status = 'published'; } // If preview session has exported content_form payload => use it (fast + correct). if ( ( 'preview' === $form_status ) && function_exists( 'wpbc_bfb_preview__maybe_get_payload_from_context' ) ) { $payload = wpbc_bfb_preview__maybe_get_payload_from_context( $ctx ); if ( ! empty( $payload ) && isset( $payload['content_form'] ) && ( '' !== (string) $payload['content_form'] ) ) { $content_form = (string) $payload['content_form']; // Keep legacy behavior: custom html shortcodes + language. $content_form = wpbc_bf__replace_custom_html_shortcodes( $content_form ); $content_form = wpbc_lang( $content_form ); return $content_form; } } // ------------------------------------------------------------ // Resolver-driven DB load (status must NOT be hard-coded). // ------------------------------------------------------------ $custom_params = array(); // Optional: allow future-proof explicit form_id embedded into booking form_data. $bfb_form_id = self::get_bfb_form_id_from_form_data( $form_data, $resource_id ); if ( $bfb_form_id > 0 ) { $custom_params['bfb_form_id'] = (int) $bfb_form_id; } $req = array( 'resource_id' => $resource_id, 'form_slug' => $form_slug, 'form_status' => $form_status, // <-- critical: use preview/published 'custom_params' => $custom_params, 'legacy_instance' => null, // Future-proof: let filters access the full preview context if they need it. 'ctx' => $ctx, ); $resolved = WPBC_FE_Form_Source_Resolver::resolve( $req ); // If preview status is requested but nothing found, try published as fallback (keeps BFB). if ( ( empty( $resolved['engine'] ) || ( 'bfb_db' !== $resolved['engine'] ) ) && ( 'preview' === $form_status ) ) { $req['form_status'] = 'published'; $resolved = WPBC_FE_Form_Source_Resolver::resolve( $req ); } if ( empty( $resolved['engine'] ) || ( 'bfb_db' !== $resolved['engine'] ) ) { return $booking_form_show; } $bfb_loader_args = array(); if ( ! empty( $resolved['bfb_loader_args'] ) && is_array( $resolved['bfb_loader_args'] ) ) { $bfb_loader_args = $resolved['bfb_loader_args']; } // Keep old behavior: allow explicit override from booking data (if present). if ( ( $bfb_form_id > 0 ) && empty( $bfb_loader_args['form_id'] ) ) { $bfb_loader_args['form_id'] = (int) $bfb_form_id; } // Signal loader that we need the "content_form" (booking fields data template). $bfb_loader_args['return'] = 'content_form'; $bfb_pair = wpbc_bfb_get_booking_form_pair( $bfb_loader_args ); $content_form = self::extract_content_form_from_pair( $bfb_pair ); if ( '' === trim( $content_form ) ) { return $booking_form_show; } // Keep legacy behavior: custom html shortcodes + language. $content_form = wpbc_bf__replace_custom_html_shortcodes( $content_form ); $content_form = wpbc_lang( $content_form ); return $content_form; } /** * Extract content template from loader return (supports multiple formats). * * Supported formats: * 1) array( 'content' => '...' ) <- current WPBC_BFB_Form_Loader output * 2) array( 'content_form' => '...' ) <- optional future alias * 3) array( 'form' => array( 'content' => '...' ) ) <- future-proof * 4) array( 'form' => array( 'content_form' => '...' ) ) <- future-proof * * @param mixed $bfb_pair Loader result. * * @return string */ private static function extract_content_form_from_pair( $bfb_pair ) { if ( ! is_array( $bfb_pair ) ) { return ''; } /** * Safety: if loader fell back to legacy, do NOT override booking_form_show. * WPBC_BFB_Form_Loader returns 'source' => 'builder'|'legacy'. */ if ( isset( $bfb_pair['source'] ) && ( 'builder' !== $bfb_pair['source'] ) ) { return ''; } // Current loader output key. if ( isset( $bfb_pair['content'] ) && is_string( $bfb_pair['content'] ) ) { return (string) $bfb_pair['content']; } // Optional alias (if some implementations already return it). if ( isset( $bfb_pair['content_form'] ) && is_string( $bfb_pair['content_form'] ) ) { return (string) $bfb_pair['content_form']; } // Future-proof nested formats. if ( isset( $bfb_pair['form'] ) && is_array( $bfb_pair['form'] ) ) { if ( isset( $bfb_pair['form']['content'] ) && is_string( $bfb_pair['form']['content'] ) ) { return (string) $bfb_pair['form']['content']; } if ( isset( $bfb_pair['form']['content_form'] ) && is_string( $bfb_pair['form']['content_form'] ) ) { return (string) $bfb_pair['form']['content_form']; } } return ''; } /** * (Optional / future-proof) Parse BFB form_id stored inside booking form_data. * * Supported patterns: * - "wpbc_bfb_form_id{resource_id}^{ID}" * - "wpbc_bfb_form_id^{ID}" * * @param string $form_data * @param int $resource_id * * @return int */ private static function get_bfb_form_id_from_form_data( $form_data, $resource_id ) { $form_data = (string) $form_data; $resource_id = (int) $resource_id; if ( '' === $form_data ) { return 0; } $prefixes = array( 'wpbc_bfb_form_id' . $resource_id . '^', 'wpbc_bfb_form_id^', ); foreach ( $prefixes as $prefix ) { $pos = strpos( $form_data, $prefix ); if ( false === $pos ) { continue; } $chunk = substr( $form_data, $pos + strlen( $prefix ) ); // Ends at "~" if present. if ( false !== strpos( $chunk, '~' ) ) { $chunk = substr( $chunk, 0, strpos( $chunk, '~' ) ); } $chunk = trim( $chunk ); if ( '' === $chunk ) { continue; } $form_id = (int) $chunk; if ( $form_id > 0 ) { return $form_id; } } return 0; } }