PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 All 205 releases
← All changes | includes/page-form-builder/save-load/bfb-activate.php +398 -389 11.8 → 11.9 View file →
@@ -1,4 +1,4 @@
1 1 <?php
2 2 /**
3 3 * Booking Form Builder - Activation helpers.
4 4 *
@@ -273,16 +273,16 @@
273 273 * So the preview page itself does not need to be private.
274 274 *
275 275 * @return false|int
276 276 */
277 -function wpbc_bfb_activation__create_preview_page_raw() {
278 -
279 - /* Reuse an existing preview page on demos, but never auto-create one. */
280 - if ( function_exists( 'wpbc_is_this_demo' ) && wpbc_is_this_demo() ) {
281 - return false;
282 - }
283 -
284 - $page_data = array(
277 +function wpbc_bfb_activation__create_preview_page_raw() {
278 +
279 + /* Reuse an existing preview page on demos, but never auto-create one. */
280 + if ( function_exists( 'wpbc_is_this_demo' ) && wpbc_is_this_demo() ) {
281 + return false;
282 + }
283 +
284 + $page_data = array(
285 285 'post_title' => __( 'Booking Form Preview', 'booking' ),
286 286 'post_content' => '', // Content will be injected dynamically at preview time by bfb-preview.php.
287 287 'post_status' => 'publish',
288 288 'post_type' => 'page',
@@ -414,8 +414,14 @@
414 414 *
415 415 * @return void
416 416 */
417 417 function wpbc_bfb_activation__preview_page() {
418 + // Activation callbacks also run during version upgrades. Keep page creation
419 + // strictly inside the verified first-install boundary.
420 + if ( ! function_exists( 'wpbc_should_create_activation_pages' ) || ! wpbc_should_create_activation_pages() ) {
421 + return;
422 + }
423 +
418 424 wpbc_bfb_activation__ensure_preview_page_exists();
419 425 }
420 426 add_bk_action( 'wpbc_free_version_activation', 'wpbc_bfb_activation__preview_page' );
421 427
@@ -505,17 +511,20 @@
505 511 add_bk_action( 'wpbc_free_version_deactivation', 'wpbc_bfb_deactivation__form_structures_table' );
506 512
507 513
508 514 /**
509 - * Import legacy "standard" booking form as initial BFB structure.
515 + * Check the shared activation decision before provisioning a Standard form.
510 516 *
511 - * @return void
517 + * The wrapper name is retained for compatibility with existing Form Builder
518 + * callers and tests. The core detector additionally requires the canonical
519 + * booking table to be absent, so a missing version option on an established
520 + * installation cannot be mistaken for a new install.
521 + *
522 + * @return bool True only during a genuine first-install activation request.
512 523 */
513 524 function wpbc_bfb_is_brand_new_install__before_version_update() {
514 525
515 - $stored_version = get_option( 'booking_version_num', false );
516 -
517 - return ( false === $stored_version );
526 + return function_exists( 'wpbc_is_plugin_initial_install' ) && wpbc_is_plugin_initial_install();
518 527 }
519 528
520 529 /**
521 530 * Let the initial standard BFB form follow the global calendar days-selection setting.
@@ -527,9 +536,9 @@
527 536 * @param array $template_record Form Builder template record.
528 537 *
529 538 * @return array
530 539 */
531 -function wpbc_bfb_activation__set_standard_form_default_days_selection( $template_record ) {
540 +function wpbc_bfb_activation__set_standard_form_default_days_selection( $template_record ) {
532 541
533 542 if ( empty( $template_record ) || ! is_array( $template_record ) ) {
534 543 return $template_record;
535 544 }
@@ -551,26 +560,26 @@
551 560 $template_record['settings_json'] = function_exists( 'wpbc_form_config__encode_json' )
552 561 ? wpbc_form_config__encode_json( $settings )
553 562 : wp_json_encode( $settings );
554 563
555 - return $template_record;
556 -}
557 -
558 -/**
559 - * Get the bundled template key used for a brand-new Standard form.
560 - *
561 - * Every customer edition starts with the spacious vertical full-day form. The
562 - * surrounding activation gate limits this choice to a genuine first install,
563 - * so upgrades, reactivations, and existing forms remain unchanged.
564 - *
565 - * @return string Stable bundled template key for a genuine first install.
566 - */
567 -function wpbc_bfb_activation__get_initial_standard_template_key() {
568 -
569 - return 'dates_vertical_hints_full_days';
570 -}
564 + return $template_record;
565 +}
571 566
572 567 /**
568 + * Get the bundled template key used for a brand-new Standard form.
569 + *
570 + * Every customer edition starts with the spacious vertical full-day form. The
571 + * surrounding activation gate limits this choice to a genuine first install,
572 + * so upgrades, reactivations, and existing forms remain unchanged.
573 + *
574 + * @return string Stable bundled template key for a genuine first install.
575 + */
576 +function wpbc_bfb_activation__get_initial_standard_template_key() {
577 +
578 + return 'dates_vertical_hints_full_days';
579 +}
580 +
581 +/**
573 582 * Maybe create the initial standard BFB form from a bundled template.
574 583 *
575 584 * Used only during brand new plugin activation, before booking_version_num
576 585 * gets updated. Upgrades keep migrating the existing legacy configuration.
@@ -576,9 +585,9 @@
576 585 * gets updated. Upgrades keep migrating the existing legacy configuration.
577 586 *
578 587 * @return bool
579 588 */
580 -function wpbc_bfb_maybe_create_initial_standard_form_from_template() {
589 +function wpbc_bfb_maybe_create_initial_standard_form_from_template() {
581 590
582 591 if ( ! wpbc_bfb_is_brand_new_install__before_version_update() ) {
583 592 return false;
584 593 }
@@ -586,21 +595,21 @@
586 595 if ( ! class_exists( 'WPBC_BFB_Form_Storage' ) ) {
587 596 return false;
588 597 }
589 598
590 - if ( function_exists( 'wpbc_get_bfb_template_record_by_key' ) ) {
591 - $template_key = wpbc_bfb_activation__get_initial_standard_template_key();
592 - $template_record = wpbc_get_bfb_template_record_by_key( $template_key );
593 -
594 - // Preserve a usable first-install form if a registry filter removes the
595 - // new starter template.
596 - if ( empty( $template_record ) && 'dates_2_columns_sidebar_hints' !== $template_key ) {
597 - $template_record = wpbc_get_bfb_template_record_by_key( 'dates_2_columns_sidebar_hints' );
598 - }
599 - } else {
600 - $template_record = array();
601 - }
599 + if ( function_exists( 'wpbc_get_bfb_template_record_by_key' ) ) {
600 + $template_key = wpbc_bfb_activation__get_initial_standard_template_key();
601 + $template_record = wpbc_get_bfb_template_record_by_key( $template_key );
602 602
603 + // Preserve a usable first-install form if a registry filter removes the
604 + // new starter template.
605 + if ( empty( $template_record ) && 'dates_2_columns_sidebar_hints' !== $template_key ) {
606 + $template_record = wpbc_get_bfb_template_record_by_key( 'dates_2_columns_sidebar_hints' );
607 + }
608 + } else {
609 + $template_record = array();
610 + }
611 +
603 612 if ( empty( $template_record ) || ! is_array( $template_record ) ) {
604 613 return false;
605 614 }
606 615
@@ -620,320 +629,320 @@
620 629 }
621 630
622 631 $booking_form_id = WPBC_BFB_Form_Storage::save_form( $template_record );
623 632
624 - return ! empty( $booking_form_id );
625 -}
626 -
627 -
628 -/**
629 - * Get the dedicated bundled template for the current live-demo edition.
630 - *
631 - * @param int $owner_user_id Optional MultiUser owner ID.
632 - *
633 - * @return string Template key, or an empty string outside a supported demo.
634 - */
635 -function wpbc_bfb_live_demo__get_template_key( $owner_user_id = 0 ) {
636 -
637 - if ( ! function_exists( 'wpbc_is_this_demo' ) || ! wpbc_is_this_demo() ) {
638 - return '';
639 - }
640 -
641 - $owner_user_id = absint( $owner_user_id );
642 -
643 - if ( class_exists( 'wpdev_bk_multiuser' ) ) {
644 - return ( $owner_user_id > 0 ) ? 'demo_multiuser_owner_1' : 'demo_multiuser';
645 - }
646 - if ( class_exists( 'wpdev_bk_biz_l' ) ) {
647 - return 'demo_business_large';
648 - }
649 - if ( class_exists( 'wpdev_bk_biz_m' ) ) {
650 - return 'demo_business_medium';
651 - }
652 - if ( class_exists( 'wpdev_bk_biz_s' ) ) {
653 - return 'demo_business_small';
654 - }
655 - if ( class_exists( 'wpdev_bk_personal' ) ) {
656 - return 'demo_personal';
657 - }
658 -
659 - return '';
660 -}
661 -
662 -
663 -/**
664 - * Get the global Form Style used by the current live-demo edition.
665 - *
666 - * @return string Form Style preset key.
667 - */
668 -function wpbc_bfb_live_demo__get_form_style() {
669 -
670 - if ( class_exists( 'wpdev_bk_multiuser' ) || class_exists( 'wpdev_bk_biz_l' ) ) {
671 - return 'light_bordered';
672 - }
673 - if ( class_exists( 'wpdev_bk_biz_m' ) ) {
674 - return 'light_soft';
675 - }
676 - if ( class_exists( 'wpdev_bk_biz_s' ) ) {
677 - return 'light_bordered';
678 - }
679 - if ( class_exists( 'wpdev_bk_personal' ) ) {
680 - return 'light_none';
681 - }
682 -
683 - return 'light_bordered';
684 -}
685 -
686 -
687 -/**
688 - * Provision a live-demo standard form from a real bundled BFB template.
689 - *
690 - * Normal owner activation is idempotent and preserves an existing form. The
691 - * explicit demo reset path may request replacement to restore fixture data.
692 - *
693 - * @param int $owner_user_id Owner ID, or 0 for the global/Super Admin form.
694 - * @param string $template_key Optional explicit bundled template key.
695 - * @param bool $replace Whether to replace an existing standard form.
696 - *
697 - * @return int|false Booking form ID on success, false on failure.
698 - */
699 -function wpbc_bfb_live_demo__provision_standard_form( $owner_user_id = 0, $template_key = '', $replace = false ) {
700 -
701 - if ( ! function_exists( 'wpbc_is_this_demo' ) || ! wpbc_is_this_demo() ) {
702 - return false;
703 - }
704 - if ( ! class_exists( 'WPBC_BFB_Form_Storage' ) ) {
705 - return false;
706 - }
707 - if ( function_exists( 'wpbc_is_table_exists' ) && ! wpbc_is_table_exists( 'booking_form_structures' ) ) {
708 - return false;
709 - }
710 -
711 - $owner_user_id = absint( $owner_user_id );
712 - $template_key = sanitize_key( (string) $template_key );
713 - if ( '' === $template_key ) {
714 - $template_key = wpbc_bfb_live_demo__get_template_key( $owner_user_id );
715 - }
716 - if ( '' === $template_key ) {
717 - return false;
718 - }
719 -
720 - $existing = WPBC_BFB_Form_Storage::get_current_form_by_key( 'standard', $owner_user_id, 'published' );
721 - if ( ! empty( $existing->booking_form_id ) && ! $replace ) {
722 - return (int) $existing->booking_form_id;
723 - }
724 -
725 - $template_record = function_exists( 'wpbc_get_bfb_template_record_by_key' )
726 - ? wpbc_get_bfb_template_record_by_key( $template_key )
727 - : array();
728 -
729 - if (
730 - empty( $template_record ) ||
731 - ! is_array( $template_record ) ||
732 - empty( $template_record['structure_json'] ) ||
733 - empty( $template_record['engine'] ) ||
734 - 'bfb' !== (string) $template_record['engine']
735 - ) {
736 - return false;
737 - }
738 -
739 - $template_record['form_slug'] = 'standard';
740 - $template_record['status'] = 'published';
741 - $template_record['scope'] = ( $owner_user_id > 0 ) ? 'user' : 'global';
742 - $template_record['owner_user_id'] = $owner_user_id;
743 - $template_record['booking_resource_id'] = null;
744 - $template_record['is_default'] = 1;
745 - $template_record['title'] = __( 'Standard', 'booking' );
746 - $template_record['description'] = '';
747 - $template_record['picture_url'] = isset( $template_record['picture_url'] ) ? (string) $template_record['picture_url'] : '';
748 -
749 - if ( function_exists( 'wpbc_bfb_resolve_picture_url' ) ) {
750 - $template_record['picture_url'] = wpbc_bfb_resolve_picture_url( $template_record['picture_url'] );
751 - }
752 -
753 - $booking_form_id = WPBC_BFB_Form_Storage::save_form( $template_record );
754 -
755 - return ! empty( $booking_form_id ) ? (int) $booking_form_id : false;
756 -}
757 -
758 -
759 -/**
760 - * Return regular-owner fixtures used by the MultiUser live demo.
761 - *
762 - * @return array Owner user ID => bundled template key.
763 - */
764 -function wpbc_bfb_live_demo__get_multiuser_owner_template_map() {
765 -
766 - $owner_templates = array(
767 - 3 => 'demo_multiuser_owner_1',
768 - 4 => 'demo_multiuser_owner_2',
769 - 5 => 'demo_multiuser_owner_3',
770 - );
771 -
772 - /**
773 - * Allow a private demo installation to use different owner IDs while
774 - * retaining the same centralized provisioning flow.
775 - *
776 - * @param array $owner_templates Owner user ID => template key.
777 - */
778 - $owner_templates = apply_filters( 'wpbc_bfb_live_demo_multiuser_owner_templates', $owner_templates );
779 -
780 - return is_array( $owner_templates ) ? $owner_templates : array();
781 -}
782 -
783 -
784 -/**
785 - * Apply all standard-form fixtures required by the active live demo.
786 - *
787 - * This is the canonical final activation step. It intentionally replaces demo
788 - * fixtures, but never runs on customer installations.
789 - *
790 - * @return bool True when every applicable fixture was saved.
791 - */
792 -function wpbc_bfb_live_demo__provision_all_standard_forms() {
793 -
794 - if ( ! function_exists( 'wpbc_is_this_demo' ) || ! wpbc_is_this_demo() ) {
795 - return false;
796 - }
797 -
798 - // Pro-only reactivation does not necessarily rerun the Free activation
799 - // callback that normally synchronizes bundled template rows.
800 - if ( function_exists( 'wpbc_bfb_activation__seed_templates' ) ) {
801 - wpbc_bfb_activation__seed_templates();
802 - }
803 -
804 - $is_success = ( false !== wpbc_bfb_live_demo__provision_standard_form( 0, '', true ) );
805 -
806 - // Form Style is a site-wide setting, not part of an individual form row.
807 - // Write directly to the global option so the result is deterministic even
808 - // when finalization runs from a regular MultiUser administrator session.
809 - update_option( 'booking_form_style', wpbc_bfb_live_demo__get_form_style() );
810 -
811 - // Live demos intentionally showcase the shared accent experience. Customer
812 - // installations keep the backward-compatible disabled default.
813 - $form_accent_defaults = function_exists( 'wpbc_bfb_settings__get_default_form_accent_options' )
814 - ? wpbc_bfb_settings__get_default_form_accent_options()
815 - : array( 'booking_form_accent_color' => WPBC_DEFAULT_FORM_ACCENT_COLOR );
816 - update_option( 'booking_form_accent_enabled', 'On' );
817 - update_option( 'booking_form_accent_color', $form_accent_defaults['booking_form_accent_color'] );
818 -
819 - if ( class_exists( 'wpdev_bk_multiuser' ) ) {
820 - foreach ( wpbc_bfb_live_demo__get_multiuser_owner_template_map() as $owner_user_id => $template_key ) {
821 - $owner_user_id = absint( $owner_user_id );
822 - $template_key = sanitize_key( (string) $template_key );
823 - if ( $owner_user_id <= 0 || '' === $template_key ) {
824 - $is_success = false;
825 - continue;
826 - }
827 -
828 - if ( function_exists( 'get_userdata' ) && ! get_userdata( $owner_user_id ) ) {
829 - $is_success = false;
830 - continue;
831 - }
832 -
833 - if ( false === wpbc_bfb_live_demo__provision_standard_form( $owner_user_id, $template_key, true ) ) {
834 - $is_success = false;
835 - }
836 - }
837 - }
838 -
839 - return $is_success;
840 -}
841 -
842 -
843 -/**
844 - * Ensure that a regular MultiUser owner has an independent standard BFB form.
845 - *
846 - * Existing owner forms are never overwritten. On normal installations the
847 - * current global standard form is cloned, preserving the Super Admin's chosen
848 - * Builder structure. Live demos use their dedicated bundled fixture instead.
849 - *
850 - * @param int $owner_user_id Regular MultiUser owner ID.
851 - *
852 - * @return int|false Booking form ID on success, false on failure.
853 - */
854 -function wpbc_bfb_ensure_standard_form_for_owner( $owner_user_id ) {
855 -
856 - $owner_user_id = absint( $owner_user_id );
857 - if ( $owner_user_id <= 0 || ! class_exists( 'WPBC_BFB_Form_Storage' ) ) {
858 - return false;
859 - }
860 - if ( function_exists( 'wpbc_is_table_exists' ) && ! wpbc_is_table_exists( 'booking_form_structures' ) ) {
861 - return false;
862 - }
863 -
864 - $existing = WPBC_BFB_Form_Storage::get_current_form_by_key( 'standard', $owner_user_id, 'published' );
865 - if ( ! empty( $existing->booking_form_id ) ) {
866 - return (int) $existing->booking_form_id;
867 - }
868 -
869 - if ( function_exists( 'wpbc_is_this_demo' ) && wpbc_is_this_demo() ) {
870 - return wpbc_bfb_live_demo__provision_standard_form( $owner_user_id, '', false );
871 - }
872 -
873 - $global_standard = WPBC_BFB_Form_Storage::get_current_form_by_key( 'standard', 0, 'published' );
874 - $form_record = ! empty( $global_standard ) ? get_object_vars( $global_standard ) : array();
875 -
876 - if ( empty( $form_record['structure_json'] ) && function_exists( 'wpbc_get_bfb_template_record_by_key' ) ) {
877 - $form_record = wpbc_get_bfb_template_record_by_key( 'dates_2_columns_sidebar_hints' );
878 - }
879 - if (
880 - empty( $form_record ) ||
881 - ! is_array( $form_record ) ||
882 - empty( $form_record['structure_json'] ) ||
883 - empty( $form_record['engine'] ) ||
884 - 'bfb' !== (string) $form_record['engine']
885 - ) {
886 - return false;
887 - }
888 -
889 - unset(
890 - $form_record['booking_form_id'],
891 - $form_record['created_at'],
892 - $form_record['created_by'],
893 - $form_record['updated_at'],
894 - $form_record['updated_by']
895 - );
896 -
897 - $form_record['form_slug'] = 'standard';
898 - $form_record['status'] = 'published';
899 - $form_record['scope'] = 'user';
900 - $form_record['owner_user_id'] = $owner_user_id;
901 - $form_record['booking_resource_id'] = null;
902 - $form_record['is_default'] = 1;
903 - $form_record['title'] = __( 'Standard', 'booking' );
904 - $form_record['description'] = '';
905 -
906 - $booking_form_id = WPBC_BFB_Form_Storage::save_form( $form_record );
907 -
908 - return ! empty( $booking_form_id ) ? (int) $booking_form_id : false;
909 -}
633 + return ! empty( $booking_form_id );
634 +}
910 635
636 +
911 637 /**
638 + * Get the dedicated bundled template for the current live-demo edition.
639 + *
640 + * @param int $owner_user_id Optional MultiUser owner ID.
641 + *
642 + * @return string Template key, or an empty string outside a supported demo.
643 + */
644 +function wpbc_bfb_live_demo__get_template_key( $owner_user_id = 0 ) {
645 +
646 + if ( ! function_exists( 'wpbc_is_this_demo' ) || ! wpbc_is_this_demo() ) {
647 + return '';
648 + }
649 +
650 + $owner_user_id = absint( $owner_user_id );
651 +
652 + if ( class_exists( 'wpdev_bk_multiuser' ) ) {
653 + return ( $owner_user_id > 0 ) ? 'demo_multiuser_owner_1' : 'demo_multiuser';
654 + }
655 + if ( class_exists( 'wpdev_bk_biz_l' ) ) {
656 + return 'demo_business_large';
657 + }
658 + if ( class_exists( 'wpdev_bk_biz_m' ) ) {
659 + return 'demo_business_medium';
660 + }
661 + if ( class_exists( 'wpdev_bk_biz_s' ) ) {
662 + return 'demo_business_small';
663 + }
664 + if ( class_exists( 'wpdev_bk_personal' ) ) {
665 + return 'demo_personal';
666 + }
667 +
668 + return '';
669 +}
670 +
671 +
672 +/**
673 + * Get the global Form Style used by the current live-demo edition.
674 + *
675 + * @return string Form Style preset key.
676 + */
677 +function wpbc_bfb_live_demo__get_form_style() {
678 +
679 + if ( class_exists( 'wpdev_bk_multiuser' ) || class_exists( 'wpdev_bk_biz_l' ) ) {
680 + return 'light_bordered';
681 + }
682 + if ( class_exists( 'wpdev_bk_biz_m' ) ) {
683 + return 'light_soft';
684 + }
685 + if ( class_exists( 'wpdev_bk_biz_s' ) ) {
686 + return 'light_bordered';
687 + }
688 + if ( class_exists( 'wpdev_bk_personal' ) ) {
689 + return 'light_none';
690 + }
691 +
692 + return 'light_bordered';
693 +}
694 +
695 +
696 +/**
697 + * Provision a live-demo standard form from a real bundled BFB template.
698 + *
699 + * Normal owner activation is idempotent and preserves an existing form. The
700 + * explicit demo reset path may request replacement to restore fixture data.
701 + *
702 + * @param int $owner_user_id Owner ID, or 0 for the global/Super Admin form.
703 + * @param string $template_key Optional explicit bundled template key.
704 + * @param bool $replace Whether to replace an existing standard form.
705 + *
706 + * @return int|false Booking form ID on success, false on failure.
707 + */
708 +function wpbc_bfb_live_demo__provision_standard_form( $owner_user_id = 0, $template_key = '', $replace = false ) {
709 +
710 + if ( ! function_exists( 'wpbc_is_this_demo' ) || ! wpbc_is_this_demo() ) {
711 + return false;
712 + }
713 + if ( ! class_exists( 'WPBC_BFB_Form_Storage' ) ) {
714 + return false;
715 + }
716 + if ( function_exists( 'wpbc_is_table_exists' ) && ! wpbc_is_table_exists( 'booking_form_structures' ) ) {
717 + return false;
718 + }
719 +
720 + $owner_user_id = absint( $owner_user_id );
721 + $template_key = sanitize_key( (string) $template_key );
722 + if ( '' === $template_key ) {
723 + $template_key = wpbc_bfb_live_demo__get_template_key( $owner_user_id );
724 + }
725 + if ( '' === $template_key ) {
726 + return false;
727 + }
728 +
729 + $existing = WPBC_BFB_Form_Storage::get_current_form_by_key( 'standard', $owner_user_id, 'published' );
730 + if ( ! empty( $existing->booking_form_id ) && ! $replace ) {
731 + return (int) $existing->booking_form_id;
732 + }
733 +
734 + $template_record = function_exists( 'wpbc_get_bfb_template_record_by_key' )
735 + ? wpbc_get_bfb_template_record_by_key( $template_key )
736 + : array();
737 +
738 + if (
739 + empty( $template_record ) ||
740 + ! is_array( $template_record ) ||
741 + empty( $template_record['structure_json'] ) ||
742 + empty( $template_record['engine'] ) ||
743 + 'bfb' !== (string) $template_record['engine']
744 + ) {
745 + return false;
746 + }
747 +
748 + $template_record['form_slug'] = 'standard';
749 + $template_record['status'] = 'published';
750 + $template_record['scope'] = ( $owner_user_id > 0 ) ? 'user' : 'global';
751 + $template_record['owner_user_id'] = $owner_user_id;
752 + $template_record['booking_resource_id'] = null;
753 + $template_record['is_default'] = 1;
754 + $template_record['title'] = __( 'Standard', 'booking' );
755 + $template_record['description'] = '';
756 + $template_record['picture_url'] = isset( $template_record['picture_url'] ) ? (string) $template_record['picture_url'] : '';
757 +
758 + if ( function_exists( 'wpbc_bfb_resolve_picture_url' ) ) {
759 + $template_record['picture_url'] = wpbc_bfb_resolve_picture_url( $template_record['picture_url'] );
760 + }
761 +
762 + $booking_form_id = WPBC_BFB_Form_Storage::save_form( $template_record );
763 +
764 + return ! empty( $booking_form_id ) ? (int) $booking_form_id : false;
765 +}
766 +
767 +
768 +/**
769 + * Return regular-owner fixtures used by the MultiUser live demo.
770 + *
771 + * @return array Owner user ID => bundled template key.
772 + */
773 +function wpbc_bfb_live_demo__get_multiuser_owner_template_map() {
774 +
775 + $owner_templates = array(
776 + 3 => 'demo_multiuser_owner_1',
777 + 4 => 'demo_multiuser_owner_2',
778 + 5 => 'demo_multiuser_owner_3',
779 + );
780 +
781 + /**
782 + * Allow a private demo installation to use different owner IDs while
783 + * retaining the same centralized provisioning flow.
784 + *
785 + * @param array $owner_templates Owner user ID => template key.
786 + */
787 + $owner_templates = apply_filters( 'wpbc_bfb_live_demo_multiuser_owner_templates', $owner_templates );
788 +
789 + return is_array( $owner_templates ) ? $owner_templates : array();
790 +}
791 +
792 +
793 +/**
794 + * Apply all standard-form fixtures required by the active live demo.
795 + *
796 + * This is the canonical final activation step. It intentionally replaces demo
797 + * fixtures, but never runs on customer installations.
798 + *
799 + * @return bool True when every applicable fixture was saved.
800 + */
801 +function wpbc_bfb_live_demo__provision_all_standard_forms() {
802 +
803 + if ( ! function_exists( 'wpbc_is_this_demo' ) || ! wpbc_is_this_demo() ) {
804 + return false;
805 + }
806 +
807 + // Pro-only reactivation does not necessarily rerun the Free activation
808 + // callback that normally synchronizes bundled template rows.
809 + if ( function_exists( 'wpbc_bfb_activation__seed_templates' ) ) {
810 + wpbc_bfb_activation__seed_templates();
811 + }
812 +
813 + $is_success = ( false !== wpbc_bfb_live_demo__provision_standard_form( 0, '', true ) );
814 +
815 + // Form Style is a site-wide setting, not part of an individual form row.
816 + // Write directly to the global option so the result is deterministic even
817 + // when finalization runs from a regular MultiUser administrator session.
818 + update_option( 'booking_form_style', wpbc_bfb_live_demo__get_form_style() );
819 +
820 + // Live demos intentionally showcase the shared accent experience. Customer
821 + // installations keep the backward-compatible disabled default.
822 + $form_accent_defaults = function_exists( 'wpbc_bfb_settings__get_default_form_accent_options' )
823 + ? wpbc_bfb_settings__get_default_form_accent_options()
824 + : array( 'booking_form_accent_color' => WPBC_DEFAULT_FORM_ACCENT_COLOR );
825 + update_option( 'booking_form_accent_enabled', 'On' );
826 + update_option( 'booking_form_accent_color', $form_accent_defaults['booking_form_accent_color'] );
827 +
828 + if ( class_exists( 'wpdev_bk_multiuser' ) ) {
829 + foreach ( wpbc_bfb_live_demo__get_multiuser_owner_template_map() as $owner_user_id => $template_key ) {
830 + $owner_user_id = absint( $owner_user_id );
831 + $template_key = sanitize_key( (string) $template_key );
832 + if ( $owner_user_id <= 0 || '' === $template_key ) {
833 + $is_success = false;
834 + continue;
835 + }
836 +
837 + if ( function_exists( 'get_userdata' ) && ! get_userdata( $owner_user_id ) ) {
838 + $is_success = false;
839 + continue;
840 + }
841 +
842 + if ( false === wpbc_bfb_live_demo__provision_standard_form( $owner_user_id, $template_key, true ) ) {
843 + $is_success = false;
844 + }
845 + }
846 + }
847 +
848 + return $is_success;
849 +}
850 +
851 +
852 +/**
853 + * Ensure that a regular MultiUser owner has an independent standard BFB form.
854 + *
855 + * Existing owner forms are never overwritten. On normal installations the
856 + * current global standard form is cloned, preserving the Super Admin's chosen
857 + * Builder structure. Live demos use their dedicated bundled fixture instead.
858 + *
859 + * @param int $owner_user_id Regular MultiUser owner ID.
860 + *
861 + * @return int|false Booking form ID on success, false on failure.
862 + */
863 +function wpbc_bfb_ensure_standard_form_for_owner( $owner_user_id ) {
864 +
865 + $owner_user_id = absint( $owner_user_id );
866 + if ( $owner_user_id <= 0 || ! class_exists( 'WPBC_BFB_Form_Storage' ) ) {
867 + return false;
868 + }
869 + if ( function_exists( 'wpbc_is_table_exists' ) && ! wpbc_is_table_exists( 'booking_form_structures' ) ) {
870 + return false;
871 + }
872 +
873 + $existing = WPBC_BFB_Form_Storage::get_current_form_by_key( 'standard', $owner_user_id, 'published' );
874 + if ( ! empty( $existing->booking_form_id ) ) {
875 + return (int) $existing->booking_form_id;
876 + }
877 +
878 + if ( function_exists( 'wpbc_is_this_demo' ) && wpbc_is_this_demo() ) {
879 + return wpbc_bfb_live_demo__provision_standard_form( $owner_user_id, '', false );
880 + }
881 +
882 + $global_standard = WPBC_BFB_Form_Storage::get_current_form_by_key( 'standard', 0, 'published' );
883 + $form_record = ! empty( $global_standard ) ? get_object_vars( $global_standard ) : array();
884 +
885 + if ( empty( $form_record['structure_json'] ) && function_exists( 'wpbc_get_bfb_template_record_by_key' ) ) {
886 + $form_record = wpbc_get_bfb_template_record_by_key( 'dates_2_columns_sidebar_hints' );
887 + }
888 + if (
889 + empty( $form_record ) ||
890 + ! is_array( $form_record ) ||
891 + empty( $form_record['structure_json'] ) ||
892 + empty( $form_record['engine'] ) ||
893 + 'bfb' !== (string) $form_record['engine']
894 + ) {
895 + return false;
896 + }
897 +
898 + unset(
899 + $form_record['booking_form_id'],
900 + $form_record['created_at'],
901 + $form_record['created_by'],
902 + $form_record['updated_at'],
903 + $form_record['updated_by']
904 + );
905 +
906 + $form_record['form_slug'] = 'standard';
907 + $form_record['status'] = 'published';
908 + $form_record['scope'] = 'user';
909 + $form_record['owner_user_id'] = $owner_user_id;
910 + $form_record['booking_resource_id'] = null;
911 + $form_record['is_default'] = 1;
912 + $form_record['title'] = __( 'Standard', 'booking' );
913 + $form_record['description'] = '';
914 +
915 + $booking_form_id = WPBC_BFB_Form_Storage::save_form( $form_record );
916 +
917 + return ! empty( $booking_form_id ) ? (int) $booking_form_id : false;
918 +}
919 +
920 +/**
912 921 * Import legacy "standard" booking form as initial BFB structure.
913 922 *
914 923 * @return void
915 924 */
916 -function wpbc_bfb__import_old_booking_forms() {
917 -
918 - if ( wpbc_bfb_maybe_create_initial_standard_form_from_template() ) {
919 - return;
920 - }
921 -
922 - if ( function_exists( 'wpbc_bfb_import_legacy_forms' ) ) {
923 - wpbc_bfb_import_legacy_forms(
924 - array(
925 - 'mode' => 'all_global',
926 - 'import_standard' => true,
927 - 'import_custom' => true,
928 - 'skip_if_exists' => true,
929 - 'set_bfb_form_not_defined' => false,
930 - )
931 - );
932 - }
933 -}
925 +function wpbc_bfb__import_old_booking_forms() {
934 926
927 + if ( wpbc_bfb_maybe_create_initial_standard_form_from_template() ) {
928 + return;
929 + }
935 930
931 + if ( function_exists( 'wpbc_bfb_import_legacy_forms' ) ) {
932 + wpbc_bfb_import_legacy_forms(
933 + array(
934 + 'mode' => 'all_global',
935 + 'import_standard' => true,
936 + 'import_custom' => true,
937 + 'skip_if_exists' => true,
938 + 'set_bfb_form_not_defined' => false,
939 + )
940 + );
941 + }
942 +}
943 +
944 +
936 945 /**
937 946 * Remove BFB Preview page from rendered nav menus.
938 947 *
939 948 * @param array $sorted_menu_items Menu item objects.
@@ -1194,50 +1203,50 @@
1194 1203 * $mode = 'all_users'; // Import booking forms for == All Users ==.
1195 1204 *
1196 1205 * @return void
1197 1206 */
1198 -function wpbc_bfb_import_legacy_forms__on_activation() {
1207 +function wpbc_bfb_import_legacy_forms__on_activation() {
1199 1208
1200 1209 if ( ! is_admin() ) {
1201 1210 return;
1202 1211 }
1203 1212
1204 - if ( false === ( $is_in_action_late_activation = get_transient( 'wpbc_pro_versions__late_activation_60' ) ) ) {
1205 - return;
1206 - }
1207 -
1208 - delete_transient( 'wpbc_pro_versions__late_activation_60' );
1209 -
1210 - $import_version = '11.3.1';
1211 -
1212 - // Live demos use dedicated BFB fixtures as their canonical source. Importing
1213 - // legacy options here would overwrite the fixtures installed during the
1214 - // activation request, especially because the old demo importer used
1215 - // skip_if_exists=false.
1216 - if ( wpbc_is_this_demo() ) {
1217 - if ( wpbc_bfb_live_demo__provision_all_standard_forms() ) {
1218 - update_option( 'wpbc_bfb_legacy_import_done_for_pro', $import_version );
1219 - } else {
1220 - // Keep a bounded retry available if activation dependencies were not
1221 - // ready yet on this request.
1222 - $retry_count = is_numeric( $is_in_action_late_activation ) ? absint( $is_in_action_late_activation ) : 0;
1223 - if ( $retry_count < 3 ) {
1224 - set_transient( 'wpbc_pro_versions__late_activation_60', $retry_count + 1, HOUR_IN_SECONDS );
1225 - }
1226 - }
1227 - return;
1228 - }
1229 -
1230 - if ( $import_version === get_option( 'wpbc_bfb_legacy_import_done_for_pro', '' ) ) {
1231 - return;
1232 - }
1233 -
1234 - $summary = wpbc_bfb_import_legacy_forms(
1213 + if ( false === ( $is_in_action_late_activation = get_transient( 'wpbc_pro_versions__late_activation_60' ) ) ) {
1214 + return;
1215 + }
1216 +
1217 + delete_transient( 'wpbc_pro_versions__late_activation_60' );
1218 +
1219 + $import_version = '11.3.1';
1220 +
1221 + // Live demos use dedicated BFB fixtures as their canonical source. Importing
1222 + // legacy options here would overwrite the fixtures installed during the
1223 + // activation request, especially because the old demo importer used
1224 + // skip_if_exists=false.
1225 + if ( wpbc_is_this_demo() ) {
1226 + if ( wpbc_bfb_live_demo__provision_all_standard_forms() ) {
1227 + update_option( 'wpbc_bfb_legacy_import_done_for_pro', $import_version );
1228 + } else {
1229 + // Keep a bounded retry available if activation dependencies were not
1230 + // ready yet on this request.
1231 + $retry_count = is_numeric( $is_in_action_late_activation ) ? absint( $is_in_action_late_activation ) : 0;
1232 + if ( $retry_count < 3 ) {
1233 + set_transient( 'wpbc_pro_versions__late_activation_60', $retry_count + 1, HOUR_IN_SECONDS );
1234 + }
1235 + }
1236 + return;
1237 + }
1238 +
1239 + if ( $import_version === get_option( 'wpbc_bfb_legacy_import_done_for_pro', '' ) ) {
1240 + return;
1241 + }
1242 +
1243 + $summary = wpbc_bfb_import_legacy_forms(
1235 1244 array(
1236 1245 'mode' => ( class_exists( 'wpdev_bk_multiuser' ) ) ? 'all_users' : 'all_global',
1237 1246 'import_standard' => true,
1238 1247 'import_custom' => true,
1239 - 'skip_if_exists' => true,
1248 + 'skip_if_exists' => true,
1240 1249 'set_bfb_form_not_defined' => true,
1241 1250 )
1242 1251 );
1243 1252 update_option( 'wpbc_bfb_legacy_import_done_for_pro', $import_version );
@@ -1249,17 +1258,17 @@
1249 1258 * Schedule one deferred legacy-form import after Pro activation. The import runs on a later request while this transient exists.
1250 1259 *
1251 1260 * @return void
1252 1261 */
1253 -function wpbc_pro_versions__late_activation(){
1254 -
1255 - // A regular MultiUser owner activation runs the Pro activation callbacks in
1256 - // its own context. It must not schedule a later demo-wide reset.
1257 - if ( false !== apply_bk_filter( 'wpbc_is_user_in_activation_process' ) ) {
1258 - return;
1259 - }
1260 -
1261 - // Get any existing copy of our transient data
1262 +function wpbc_pro_versions__late_activation(){
1263 +
1264 + // A regular MultiUser owner activation runs the Pro activation callbacks in
1265 + // its own context. It must not schedule a later demo-wide reset.
1266 + if ( false !== apply_bk_filter( 'wpbc_is_user_in_activation_process' ) ) {
1267 + return;
1268 + }
1269 +
1270 + // Get any existing copy of our transient data
1262 1271 if ( false === ( $is_in_action_late_activation = get_transient( 'wpbc_pro_versions__late_activation_60' ) ) ) {
1263 1272 // It wasn't there, so regenerate the data and save the transient
1264 1273 $is_in_action_late_activation = true;
1265 1274 set_transient( 'wpbc_pro_versions__late_activation_60', $is_in_action_late_activation, HOUR_IN_SECONDS );