'administrator', 'number' => 1, 'fields' => array( 'ID' ), ) ); if ( empty( $users ) ) { return 0; } if ( is_object( $users[0] ) && isset( $users[0]->ID ) ) { return (int) $users[0]->ID; } if ( is_array( $users[0] ) && isset( $users[0]['ID'] ) ) { return (int) $users[0]['ID']; } return 0; } /** * Temporarily switch current user to an admin user if the current user * cannot create private pages (activation edge-cases like WP-CLI). * * @return int Previous user ID (0 if none). Caller should restore with wp_set_current_user(). */ function wpbc_bfb_activation__maybe_switch_to_admin_user() { $prev_user_id = (int) get_current_user_id(); // If current user can create pages, do nothing. if ( $prev_user_id > 0 && user_can( $prev_user_id, 'publish_pages' ) ) { return $prev_user_id; } $admin_id = wpbc_bfb_activation__get_any_admin_user_id(); if ( $admin_id > 0 ) { wp_set_current_user( $admin_id ); } return $prev_user_id; } /** * Restore previous user after temporary switch. * * @param int $prev_user_id Previous user ID. * * @return void */ function wpbc_bfb_activation__restore_user( $prev_user_id ) { $prev_user_id = (int) $prev_user_id; // Restore to previous user if we had one; otherwise, reset to 0. if ( $prev_user_id > 0 ) { wp_set_current_user( $prev_user_id ); } else { wp_set_current_user( 0 ); } } /** * Validate that a page ID is a real WP page. * * @param int $page_id Page ID. * * @return bool */ function wpbc_bfb_activation__is_valid_page_id( $page_id ) { $page_id = (int) $page_id; if ( $page_id <= 0 ) { return false; } $page = get_post( $page_id ); return ( $page instanceof WP_Post && 'page' === $page->post_type ); } /** * Try to locate an existing preview page by meta marker. * * @return int Page ID or 0. */ function wpbc_bfb_activation__find_preview_page_by_meta() { $meta_key = wpbc_bfb_activation__get_preview_page_meta_key(); $q = new WP_Query( array( 'post_type' => 'page', 'post_status' => array( 'private', 'publish', 'draft', 'pending' ), 'posts_per_page' => 1, 'fields' => 'ids', 'no_found_rows' => true, 'suppress_filters' => true, // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.SuppressFilters_suppress_filters 'meta_key' => $meta_key, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 'meta_value' => '1', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value ) ); if ( empty( $q->posts ) ) { return 0; } return (int) $q->posts[0]; } /** * Try to locate an existing preview page by slug. * * @return int Page ID or 0. */ function wpbc_bfb_activation__find_preview_page_by_slug() { $slug = wpbc_bfb_activation__get_preview_page_slug(); $page = get_page_by_path( $slug, OBJECT, 'page' ); if ( $page instanceof WP_Post ) { return (int) $page->ID; } return 0; } /** * Force a safe template for the BFB Preview page. * * Why: * - The Preview page is a technical/service page. * - It should not inherit decorative page templates like "Page with wide Image". * - For block themes, prefer a clean template like "page-no-title" or "page". * - For classic themes, use the default page template. // FixIn: 10.15.2.1 * * @param int $page_id Page ID. * * @return bool */ function wpbc_bfb_preview__force_safe_template( $page_id ) { $page_id = absint( $page_id ); if ( $page_id <= 0 ) { return false; } /* * Always clear any previously assigned template first. * This is important when an existing Preview page was previously using * an unwanted template, for example "Page with wide Image". */ delete_post_meta( $page_id, '_wp_page_template' ); // Block themes: prefer a cleaner built-in page template, if available. if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() && function_exists( 'get_block_templates' ) ) { $theme_slug = get_stylesheet(); $templates = get_block_templates( array( 'post_type' => 'page', ), 'wp_template' ); $safe_template_slug = ''; foreach ( $templates as $template ) { if ( empty( $template->slug ) || empty( $template->theme ) ) { continue; } if ( $theme_slug !== $template->theme ) { continue; } if ( 'page-no-title' === $template->slug ) { $safe_template_slug = 'page-no-title'; break; } if ( ( '' === $safe_template_slug ) && ( 'page' === $template->slug ) ) { $safe_template_slug = 'page'; } } if ( '' !== $safe_template_slug ) { $result = wp_update_post( array( 'ID' => $page_id, 'page_template' => $safe_template_slug, ), true ); return ( ! is_wp_error( $result ) ); } } return true; } /** * The preview page has status 'publish', because the secure the output by your nonce + cap * * Preview security is already enforced by: * * wpbc_bfb_preview=1 + token + nonce * * is_user_logged_in() + current_user_can( wpbc_bfb_get_manage_cap() ) * * So the preview page itself does not need to be private. * * @return false|int */ function wpbc_bfb_activation__create_preview_page_raw() { $page_data = array( 'post_title' => __( 'Booking Form Preview', 'booking' ), 'post_content' => '', // Content will be injected dynamically at preview time by bfb-preview.php. 'post_status' => 'publish', 'post_type' => 'page', 'post_name' => wpbc_bfb_activation__get_preview_page_slug(), 'comment_status' => 'closed', 'ping_status' => 'closed', ); $page_id = wp_insert_post( $page_data, true ); if ( ( ! is_wp_error( $page_id ) ) && ( ! empty( $page_id ) ) ) { // Success. wpbc_bfb_preview__force_safe_template( $page_id ); } else { if ( is_wp_error( $page_id ) ) { return false; } if ( ! $page_id ) { return false; } $page_id = false; } return (int) $page_id; } /** * Create the "Booking Form Preview" page and store option. * * @return int|false Page ID on success, false on failure. */ function wpbc_bfb_activation__create_preview_page() { $prev_user_id = wpbc_bfb_activation__maybe_switch_to_admin_user(); $page_id = wpbc_bfb_activation__create_preview_page_raw(); // Restore user context. wpbc_bfb_activation__restore_user( $prev_user_id ); if ( empty( $page_id ) || is_wp_error( $page_id ) ) { return false; } $page_id = (int) $page_id; // Mark page for recovery if option is deleted later. add_post_meta( $page_id, wpbc_bfb_activation__get_preview_page_meta_key(), '1', true ); // Store the page ID in option (same key used by preview service). update_option( wpbc_bfb_activation__get_preview_page_option_key(), $page_id ); return $page_id; } /** * Ensure preview page exists (activation-time). * * - Validates stored option page ID. * - If missing, tries to find by meta marker. * - If missing, tries to find by slug. * - If still missing, creates a new private page. * * @return int|false Page ID, or false on failure. */ function wpbc_bfb_activation__ensure_preview_page_exists() { $option_key = wpbc_bfb_activation__get_preview_page_option_key(); $page_id = (int) get_option( $option_key ); // 1) Option points to a valid page. if ( wpbc_bfb_activation__is_valid_page_id( $page_id ) ) { // Enforce publish status (best effort). $page = get_post( $page_id ); if ( $page instanceof WP_Post && 'publish' !== $page->post_status ) { wp_update_post( array( 'ID' => (int) $page_id, 'post_status' => 'publish', ) ); } // Ensure meta marker exists. wpbc_bfb_preview__force_safe_template( (int) $page_id ); add_post_meta( (int) $page_id, wpbc_bfb_activation__get_preview_page_meta_key(), '1', true ); return (int) $page_id; } // 2) Try recovery by meta marker. $found_id = wpbc_bfb_activation__find_preview_page_by_meta(); if ( $found_id > 0 && wpbc_bfb_activation__is_valid_page_id( $found_id ) ) { $found_page = get_post( $found_id ); if ( $found_page instanceof WP_Post && 'publish' !== $found_page->post_status ) { wp_update_post( array( 'ID' => (int) $found_id, 'post_status' => 'publish', ) ); } wpbc_bfb_preview__force_safe_template( (int) $found_id ); update_option( $option_key, (int) $found_id ); return (int) $found_id; } // 3) Try recovery by slug. $found_id = wpbc_bfb_activation__find_preview_page_by_slug(); if ( $found_id > 0 && wpbc_bfb_activation__is_valid_page_id( $found_id ) ) { $found_page = get_post( $found_id ); if ( $found_page instanceof WP_Post && 'publish' !== $found_page->post_status ) { wp_update_post( array( 'ID' => (int) $found_id, 'post_status' => 'publish', ) ); } wpbc_bfb_preview__force_safe_template( (int) $found_id ); // Mark it as ours (so next time meta lookup works). add_post_meta( (int) $found_id, wpbc_bfb_activation__get_preview_page_meta_key(), '1', true ); update_option( $option_key, (int) $found_id ); return (int) $found_id; } // 4) Create. return wpbc_bfb_activation__create_preview_page(); } /** * Activation hook entry: ensure the preview page exists. * * @return void */ function wpbc_bfb_activation__preview_page() { wpbc_bfb_activation__ensure_preview_page_exists(); } add_bk_action( 'wpbc_free_version_activation', 'wpbc_bfb_activation__preview_page' ); /** * A c t i v a t e - Create new DB Table for Booking Form Builder structures. * * This table stores: * - Canonical form builder structure as JSON (structure_json). * - Additional settings as JSON (settings_json). * - Optional precompiled HTML can be added later (not in this schema). * - Ownership via owner_user_id (for MultiUser). * * @return void */ function wpbc_bfb_activation__form_structures_table() { global $wpdb; $charset_collate = ( ! empty( $wpdb->charset ) ) ? 'DEFAULT CHARACTER SET ' . $wpdb->charset : ''; $charset_collate .= ( ! empty( $wpdb->collate ) ) ? ' COLLATE ' . $wpdb->collate : ''; if ( ! wpbc_is_table_exists( 'booking_form_structures' ) ) { $simple_sql = "CREATE TABLE {$wpdb->prefix}booking_form_structures ( booking_form_id bigint(20) unsigned NOT NULL auto_increment, form_slug VARCHAR(191) NOT NULL, status VARCHAR(32) NOT NULL, owner_user_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0, scope varchar(64) NOT NULL default 'global', version int(11) NOT NULL default 1, booking_resource_id BIGINT UNSIGNED NULL, engine varchar(32) NOT NULL default 'bfb', engine_version varchar(16) NOT NULL default '1.0', structure_json LONGTEXT NOT NULL, settings_json LONGTEXT NULL, advanced_form LONGTEXT NULL, content_form LONGTEXT NULL, is_default TINYINT(1) NOT NULL DEFAULT 0, title VARCHAR(255) NOT NULL, description TEXT NULL, picture_url text NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by bigint(20) unsigned DEFAULT NULL, updated_by bigint(20) unsigned DEFAULT NULL, PRIMARY KEY (booking_form_id), UNIQUE KEY form_slug_status (form_slug, status, owner_user_id) ) {$charset_collate}"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter $wpdb->query( $simple_sql ); do_action( 'wpbc_bfb_activation__form_structures_table__after_create' ); wpbc_bfb__import_old_booking_forms(); } else { do_action( 'wpbc_bfb_activation__form_structures_table__table_already_exists' ); } } add_bk_action( 'wpbc_free_version_activation', 'wpbc_bfb_activation__form_structures_table' ); /** * D e a c t i v a t e - Drop Booking Form Builder structures table. * * @return void */ function wpbc_bfb_deactivation__form_structures_table() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}booking_form_structures" ); delete_bk_option( 'wpbc_bfb_legacy_import_done_for_pro' ); } add_bk_action( 'wpbc_free_version_deactivation', 'wpbc_bfb_deactivation__form_structures_table' ); /** * Import legacy "standard" booking form as initial BFB structure. * * @return void */ function wpbc_bfb_is_brand_new_install__before_version_update() { $stored_version = get_option( 'booking_version_num', false ); return ( false === $stored_version ); } /** * Let the initial standard BFB form follow the global calendar days-selection setting. * * Some bundled templates intentionally override days selection for their own use case. * The installed "standard" form should not carry that template-level override, because * the global Settings > Calendar page is the canonical default for a new site. * * @param array $template_record Form Builder template record. * * @return array */ function wpbc_bfb_activation__set_standard_form_default_days_selection( $template_record ) { if ( empty( $template_record ) || ! is_array( $template_record ) ) { return $template_record; } $settings = array(); if ( ! empty( $template_record['settings_json'] ) ) { $decoded = json_decode( (string) $template_record['settings_json'], true ); if ( is_array( $decoded ) ) { $settings = $decoded; } } if ( empty( $settings['options'] ) || ! is_array( $settings['options'] ) ) { $settings['options'] = array(); } $settings['options']['booking_type_of_day_selections'] = ''; $template_record['settings_json'] = function_exists( 'wpbc_form_config__encode_json' ) ? wpbc_form_config__encode_json( $settings ) : wp_json_encode( $settings ); return $template_record; } /** * Maybe create the initial standard BFB form from a bundled template. * * Used only during brand new plugin activation, before booking_version_num * gets updated. Upgrades keep migrating the existing legacy configuration. * * @return bool */ function wpbc_bfb_maybe_create_initial_standard_form_from_template() { if ( ! wpbc_bfb_is_brand_new_install__before_version_update() ) { return false; } if ( ! class_exists( 'WPBC_BFB_Form_Storage' ) ) { return false; } if ( function_exists( 'wpbc_get_bfb_template_record_by_key' ) ) { // Default Booking Form Template - Just after Booking Calendar activation! // $template_record = wpbc_get_bfb_template_record_by_key( 'time_appointments_3_steps_review_with_hints' ); // Time appointments. $template_record = wpbc_get_bfb_template_record_by_key( 'dates_2_columns_sidebar_hints' ); // Full Days - 2 Columns - with dates hints. } else { $template_record = array(); } if ( empty( $template_record ) || ! is_array( $template_record ) ) { return false; } $template_record = wpbc_bfb_activation__set_standard_form_default_days_selection( $template_record ); $template_record['form_slug'] = 'standard'; $template_record['status'] = 'published'; $template_record['scope'] = 'global'; $template_record['owner_user_id'] = 0; $template_record['booking_resource_id'] = null; $template_record['is_default'] = 1; $template_record['title'] = __( 'Standard', 'booking' ); $template_record['description'] = ''; $template_record['picture_url'] = isset( $template_record['picture_url'] ) ? (string) $template_record['picture_url'] : ''; if ( function_exists( 'wpbc_bfb_resolve_picture_url' ) ) { $template_record['picture_url'] = wpbc_bfb_resolve_picture_url( $template_record['picture_url'] ); } $booking_form_id = WPBC_BFB_Form_Storage::save_form( $template_record ); return ! empty( $booking_form_id ); } /** * Get the dedicated bundled template for the current live-demo edition. * * @param int $owner_user_id Optional MultiUser owner ID. * * @return string Template key, or an empty string outside a supported demo. */ function wpbc_bfb_live_demo__get_template_key( $owner_user_id = 0 ) { if ( ! function_exists( 'wpbc_is_this_demo' ) || ! wpbc_is_this_demo() ) { return ''; } $owner_user_id = absint( $owner_user_id ); if ( class_exists( 'wpdev_bk_multiuser' ) ) { return ( $owner_user_id > 0 ) ? 'demo_multiuser_owner_1' : 'demo_multiuser'; } if ( class_exists( 'wpdev_bk_biz_l' ) ) { return 'demo_business_large'; } if ( class_exists( 'wpdev_bk_biz_m' ) ) { return 'demo_business_medium'; } if ( class_exists( 'wpdev_bk_biz_s' ) ) { return 'demo_business_small'; } if ( class_exists( 'wpdev_bk_personal' ) ) { return 'demo_personal'; } return ''; } /** * Get the global Form Style used by the current live-demo edition. * * @return string Form Style preset key. */ function wpbc_bfb_live_demo__get_form_style() { if ( class_exists( 'wpdev_bk_multiuser' ) || class_exists( 'wpdev_bk_biz_l' ) ) { return 'light_bordered'; } if ( class_exists( 'wpdev_bk_biz_m' ) ) { return 'light_soft'; } if ( class_exists( 'wpdev_bk_biz_s' ) ) { return 'light_bordered'; } if ( class_exists( 'wpdev_bk_personal' ) ) { return 'light_none'; } return 'light_bordered'; } /** * Provision a live-demo standard form from a real bundled BFB template. * * Normal owner activation is idempotent and preserves an existing form. The * explicit demo reset path may request replacement to restore fixture data. * * @param int $owner_user_id Owner ID, or 0 for the global/Super Admin form. * @param string $template_key Optional explicit bundled template key. * @param bool $replace Whether to replace an existing standard form. * * @return int|false Booking form ID on success, false on failure. */ function wpbc_bfb_live_demo__provision_standard_form( $owner_user_id = 0, $template_key = '', $replace = false ) { if ( ! function_exists( 'wpbc_is_this_demo' ) || ! wpbc_is_this_demo() ) { return false; } if ( ! class_exists( 'WPBC_BFB_Form_Storage' ) ) { return false; } if ( function_exists( 'wpbc_is_table_exists' ) && ! wpbc_is_table_exists( 'booking_form_structures' ) ) { return false; } $owner_user_id = absint( $owner_user_id ); $template_key = sanitize_key( (string) $template_key ); if ( '' === $template_key ) { $template_key = wpbc_bfb_live_demo__get_template_key( $owner_user_id ); } if ( '' === $template_key ) { return false; } $existing = WPBC_BFB_Form_Storage::get_current_form_by_key( 'standard', $owner_user_id, 'published' ); if ( ! empty( $existing->booking_form_id ) && ! $replace ) { return (int) $existing->booking_form_id; } $template_record = function_exists( 'wpbc_get_bfb_template_record_by_key' ) ? wpbc_get_bfb_template_record_by_key( $template_key ) : array(); if ( empty( $template_record ) || ! is_array( $template_record ) || empty( $template_record['structure_json'] ) || empty( $template_record['engine'] ) || 'bfb' !== (string) $template_record['engine'] ) { return false; } $template_record['form_slug'] = 'standard'; $template_record['status'] = 'published'; $template_record['scope'] = ( $owner_user_id > 0 ) ? 'user' : 'global'; $template_record['owner_user_id'] = $owner_user_id; $template_record['booking_resource_id'] = null; $template_record['is_default'] = 1; $template_record['title'] = __( 'Standard', 'booking' ); $template_record['description'] = ''; $template_record['picture_url'] = isset( $template_record['picture_url'] ) ? (string) $template_record['picture_url'] : ''; if ( function_exists( 'wpbc_bfb_resolve_picture_url' ) ) { $template_record['picture_url'] = wpbc_bfb_resolve_picture_url( $template_record['picture_url'] ); } $booking_form_id = WPBC_BFB_Form_Storage::save_form( $template_record ); return ! empty( $booking_form_id ) ? (int) $booking_form_id : false; } /** * Return regular-owner fixtures used by the MultiUser live demo. * * @return array Owner user ID => bundled template key. */ function wpbc_bfb_live_demo__get_multiuser_owner_template_map() { $owner_templates = array( 3 => 'demo_multiuser_owner_1', 4 => 'demo_multiuser_owner_2', 5 => 'demo_multiuser_owner_3', ); /** * Allow a private demo installation to use different owner IDs while * retaining the same centralized provisioning flow. * * @param array $owner_templates Owner user ID => template key. */ $owner_templates = apply_filters( 'wpbc_bfb_live_demo_multiuser_owner_templates', $owner_templates ); return is_array( $owner_templates ) ? $owner_templates : array(); } /** * Apply all standard-form fixtures required by the active live demo. * * This is the canonical final activation step. It intentionally replaces demo * fixtures, but never runs on customer installations. * * @return bool True when every applicable fixture was saved. */ function wpbc_bfb_live_demo__provision_all_standard_forms() { if ( ! function_exists( 'wpbc_is_this_demo' ) || ! wpbc_is_this_demo() ) { return false; } // Pro-only reactivation does not necessarily rerun the Free activation // callback that normally synchronizes bundled template rows. if ( function_exists( 'wpbc_bfb_activation__seed_templates' ) ) { wpbc_bfb_activation__seed_templates(); } $is_success = ( false !== wpbc_bfb_live_demo__provision_standard_form( 0, '', true ) ); // Form Style is a site-wide setting, not part of an individual form row. // Write directly to the global option so the result is deterministic even // when finalization runs from a regular MultiUser administrator session. update_option( 'booking_form_style', wpbc_bfb_live_demo__get_form_style() ); if ( class_exists( 'wpdev_bk_multiuser' ) ) { foreach ( wpbc_bfb_live_demo__get_multiuser_owner_template_map() as $owner_user_id => $template_key ) { $owner_user_id = absint( $owner_user_id ); $template_key = sanitize_key( (string) $template_key ); if ( $owner_user_id <= 0 || '' === $template_key ) { $is_success = false; continue; } if ( function_exists( 'get_userdata' ) && ! get_userdata( $owner_user_id ) ) { $is_success = false; continue; } if ( false === wpbc_bfb_live_demo__provision_standard_form( $owner_user_id, $template_key, true ) ) { $is_success = false; } } } return $is_success; } /** * Ensure that a regular MultiUser owner has an independent standard BFB form. * * Existing owner forms are never overwritten. On normal installations the * current global standard form is cloned, preserving the Super Admin's chosen * Builder structure. Live demos use their dedicated bundled fixture instead. * * @param int $owner_user_id Regular MultiUser owner ID. * * @return int|false Booking form ID on success, false on failure. */ function wpbc_bfb_ensure_standard_form_for_owner( $owner_user_id ) { $owner_user_id = absint( $owner_user_id ); if ( $owner_user_id <= 0 || ! class_exists( 'WPBC_BFB_Form_Storage' ) ) { return false; } if ( function_exists( 'wpbc_is_table_exists' ) && ! wpbc_is_table_exists( 'booking_form_structures' ) ) { return false; } $existing = WPBC_BFB_Form_Storage::get_current_form_by_key( 'standard', $owner_user_id, 'published' ); if ( ! empty( $existing->booking_form_id ) ) { return (int) $existing->booking_form_id; } if ( function_exists( 'wpbc_is_this_demo' ) && wpbc_is_this_demo() ) { return wpbc_bfb_live_demo__provision_standard_form( $owner_user_id, '', false ); } $global_standard = WPBC_BFB_Form_Storage::get_current_form_by_key( 'standard', 0, 'published' ); $form_record = ! empty( $global_standard ) ? get_object_vars( $global_standard ) : array(); if ( empty( $form_record['structure_json'] ) && function_exists( 'wpbc_get_bfb_template_record_by_key' ) ) { $form_record = wpbc_get_bfb_template_record_by_key( 'dates_2_columns_sidebar_hints' ); } if ( empty( $form_record ) || ! is_array( $form_record ) || empty( $form_record['structure_json'] ) || empty( $form_record['engine'] ) || 'bfb' !== (string) $form_record['engine'] ) { return false; } unset( $form_record['booking_form_id'], $form_record['created_at'], $form_record['created_by'], $form_record['updated_at'], $form_record['updated_by'] ); $form_record['form_slug'] = 'standard'; $form_record['status'] = 'published'; $form_record['scope'] = 'user'; $form_record['owner_user_id'] = $owner_user_id; $form_record['booking_resource_id'] = null; $form_record['is_default'] = 1; $form_record['title'] = __( 'Standard', 'booking' ); $form_record['description'] = ''; $booking_form_id = WPBC_BFB_Form_Storage::save_form( $form_record ); return ! empty( $booking_form_id ) ? (int) $booking_form_id : false; } /** * Import legacy "standard" booking form as initial BFB structure. * * @return void */ function wpbc_bfb__import_old_booking_forms() { if ( wpbc_bfb_maybe_create_initial_standard_form_from_template() ) { return; } if ( function_exists( 'wpbc_bfb_import_legacy_forms' ) ) { wpbc_bfb_import_legacy_forms( array( 'mode' => 'all_global', 'import_standard' => true, 'import_custom' => true, 'skip_if_exists' => true, 'set_bfb_form_not_defined' => false, ) ); } } /** * Remove BFB Preview page from rendered nav menus. * * @param array $sorted_menu_items Menu item objects. * @param stdClass $args Menu args. * * @return array */ function wpbc_bfb_preview__exclude_from_nav_menus( $sorted_menu_items, $args ) { $page_id = absint( get_option( wpbc_bfb_activation__get_preview_page_option_key() ) ); if ( $page_id <= 0 ) { return $sorted_menu_items; } $filtered_items = array(); foreach ( $sorted_menu_items as $menu_item ) { $object_id = isset( $menu_item->object_id ) ? absint( $menu_item->object_id ) : 0; $object = isset( $menu_item->object ) ? (string) $menu_item->object : ''; if ( ( 'page' === $object ) && ( $page_id === $object_id ) ) { continue; } $filtered_items[] = $menu_item; } return $filtered_items; } add_filter( 'wp_nav_menu_objects', 'wpbc_bfb_preview__exclude_from_nav_menus', 10, 2 ); /** * Exclude BFB Preview page from wp_list_pages() outputs. * * @param array $exclude_array Excluded page IDs. * * @return array */ function wpbc_bfb_preview__exclude_from_page_lists( $exclude_array ) { $page_id = absint( get_option( wpbc_bfb_activation__get_preview_page_option_key() ) ); if ( $page_id > 0 ) { $exclude_array[] = $page_id; } return array_unique( array_map( 'absint', $exclude_array ) ); } add_filter( 'wp_list_pages_excludes', 'wpbc_bfb_preview__exclude_from_page_lists' ); /** * Remove BFB Preview page from get_pages() results. * * This is important for block themes, because the core Page List block * renders its items from get_pages(). * * @param array $pages Array of page objects. * @param array $parsed_args Parsed get_pages() args. * * @return array */ function wpbc_bfb_preview__exclude_from_get_pages( $pages, $parsed_args ) { $page_id = absint( get_option( wpbc_bfb_activation__get_preview_page_option_key() ) ); if ( $page_id <= 0 ) { return $pages; } $filtered_pages = array(); foreach ( $pages as $page_obj ) { if ( empty( $page_obj ) || empty( $page_obj->ID ) ) { $filtered_pages[] = $page_obj; continue; } if ( absint( $page_obj->ID ) === $page_id ) { continue; } $filtered_pages[] = $page_obj; } return $filtered_pages; } add_filter( 'get_pages', 'wpbc_bfb_preview__exclude_from_get_pages', 10, 2 ); /** * Remove BFB Preview page from rendered Navigation Link blocks. * * This handles block-theme menus where the Preview page was manually * inserted as a Navigation Link item. * * @param string $block_content Rendered block HTML. * @param array $block Parsed block data. * @param WP_Block $instance Block instance. * * @return string */ function wpbc_bfb_preview__exclude_from_navigation_link_block( $block_content, $block, $instance ) { $page_id = absint( get_option( wpbc_bfb_activation__get_preview_page_option_key() ) ); if ( $page_id <= 0 ) { return $block_content; } if ( empty( $block['attrs'] ) || ! is_array( $block['attrs'] ) ) { return $block_content; } $attrs = $block['attrs']; $preview_url = get_permalink( $page_id ); $block_id = isset( $attrs['id'] ) ? absint( $attrs['id'] ) : 0; $block_type = isset( $attrs['type'] ) ? (string) $attrs['type'] : ''; $block_kind = isset( $attrs['kind'] ) ? (string) $attrs['kind'] : ''; $block_url = isset( $attrs['url'] ) ? (string) $attrs['url'] : ''; if ( $block_id === $page_id ) { return ''; } if ( ( 'page' === $block_type ) && ( ( 'post-type' === $block_kind ) || ( '' === $block_kind ) ) && ( ! empty( $block_id ) ) && ( $block_id === $page_id ) ) { return ''; } if ( ( ! empty( $preview_url ) ) && ( ! empty( $block_url ) ) ) { $normalized_preview_url = untrailingslashit( $preview_url ); $normalized_block_url = untrailingslashit( $block_url ); if ( $normalized_preview_url === $normalized_block_url ) { return ''; } } return $block_content; } add_filter( 'render_block_core/navigation-link', 'wpbc_bfb_preview__exclude_from_navigation_link_block', 10, 3 ); /** * Remove BFB Preview page from rendered Page List Item blocks. * * This is important for block themes like Twenty Twenty-Four, * because the Navigation block can use a Page List block that renders * individual core/page-list-item blocks. * * @param string $block_content Rendered block HTML. * @param array $block Parsed block data. * @param WP_Block $instance Block instance. * * @return string */ function wpbc_bfb_preview__exclude_from_page_list_item_block( $block_content, $block, $instance ) { $page_id = absint( get_option( wpbc_bfb_activation__get_preview_page_option_key() ) ); if ( $page_id <= 0 ) { return $block_content; } if ( empty( $block['attrs'] ) || ! is_array( $block['attrs'] ) ) { return $block_content; } $attrs = $block['attrs']; $preview_url = get_permalink( $page_id ); $item_id = isset( $attrs['id'] ) ? absint( $attrs['id'] ) : 0; $item_link = isset( $attrs['link'] ) ? (string) $attrs['link'] : ''; if ( $item_id === $page_id ) { return ''; } if ( ( ! empty( $preview_url ) ) && ( ! empty( $item_link ) ) ) { if ( untrailingslashit( $preview_url ) === untrailingslashit( $item_link ) ) { return ''; } } return $block_content; } add_filter( 'render_block_core/page-list-item', 'wpbc_bfb_preview__exclude_from_page_list_item_block', 10, 3 ); // FixIn: 10.15.3.1. /** * Check whether current request is the BFB Preview page request. * * @return bool */ function wpbc_bfb_is_preview_request() { if ( empty( $_GET['wpbc_bfb_preview'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return false; } return ( '1' === sanitize_text_field( wp_unslash( $_GET['wpbc_bfb_preview'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended } /** * Add noindex / noarchive robots directives for BFB Preview. * * @param array $robots Robots directives. * * @return array */ function wpbc_bfb_preview__wp_robots( $robots ) { if ( ! wpbc_bfb_is_preview_request() ) { return $robots; } return wp_robots_sensitive_page( $robots ); } add_filter( 'wp_robots', 'wpbc_bfb_preview__wp_robots' ); /** * Send no-cache headers for BFB Preview responses. * * @return void */ function wpbc_bfb_preview__nocache_headers() { if ( ! wpbc_bfb_is_preview_request() ) { return; } if ( ! headers_sent() ) { nocache_headers(); } } add_action( 'template_redirect', 'wpbc_bfb_preview__nocache_headers', 1 ); /** * Import booking forms on activation of Pro versions of Booking Calendar. * * $mode = 'current_context'; // Import booking forms only for the == Current User == (usually - "Regular User"). * $mode = 'all_global'; // Import booking forms for == Super Booking Admin == user ( user_id = 0 ). or for pro versions in all other situations. * $mode = 'all_users'; // Import booking forms for == All Users ==. * * @return void */ function wpbc_bfb_import_legacy_forms__on_activation() { if ( ! is_admin() ) { return; } if ( false === ( $is_in_action_late_activation = get_transient( 'wpbc_pro_versions__late_activation_60' ) ) ) { return; } delete_transient( 'wpbc_pro_versions__late_activation_60' ); $import_version = '11.3.1'; // Live demos use dedicated BFB fixtures as their canonical source. Importing // legacy options here would overwrite the fixtures installed during the // activation request, especially because the old demo importer used // skip_if_exists=false. if ( wpbc_is_this_demo() ) { if ( wpbc_bfb_live_demo__provision_all_standard_forms() ) { update_option( 'wpbc_bfb_legacy_import_done_for_pro', $import_version ); } else { // Keep a bounded retry available if activation dependencies were not // ready yet on this request. $retry_count = is_numeric( $is_in_action_late_activation ) ? absint( $is_in_action_late_activation ) : 0; if ( $retry_count < 3 ) { set_transient( 'wpbc_pro_versions__late_activation_60', $retry_count + 1, HOUR_IN_SECONDS ); } } return; } if ( $import_version === get_option( 'wpbc_bfb_legacy_import_done_for_pro', '' ) ) { return; } $summary = wpbc_bfb_import_legacy_forms( array( 'mode' => ( class_exists( 'wpdev_bk_multiuser' ) ) ? 'all_users' : 'all_global', 'import_standard' => true, 'import_custom' => true, 'skip_if_exists' => true, 'set_bfb_form_not_defined' => true, ) ); update_option( 'wpbc_bfb_legacy_import_done_for_pro', $import_version ); } add_action( 'init', 'wpbc_bfb_import_legacy_forms__on_activation', 1000 ); /** * Schedule one deferred legacy-form import after Pro activation. The import runs on a later request while this transient exists. * * @return void */ function wpbc_pro_versions__late_activation(){ // A regular MultiUser owner activation runs the Pro activation callbacks in // its own context. It must not schedule a later demo-wide reset. if ( false !== apply_bk_filter( 'wpbc_is_user_in_activation_process' ) ) { return; } // Get any existing copy of our transient data if ( false === ( $is_in_action_late_activation = get_transient( 'wpbc_pro_versions__late_activation_60' ) ) ) { // It wasn't there, so regenerate the data and save the transient $is_in_action_late_activation = true; set_transient( 'wpbc_pro_versions__late_activation_60', $is_in_action_late_activation, HOUR_IN_SECONDS ); } } add_bk_action( 'wpbc_other_versions_activation', 'wpbc_pro_versions__late_activation' );