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/_capacity/create_booking.php +181 -132 11.8.3 → 11.9 View file →
@@ -213,10 +213,94 @@
213 213 // ---------------------------------------------------------------------------------------------------------------------
214 214 // == Save Booking
215 215 // ---------------------------------------------------------------------------------------------------------------------
216 216
217 -/**
218 - * Save Booking - ADD NEW or UPDATE exist booking
217 +/**
218 + * Resolve and validate the final booking destination against current storage.
219 + *
220 + * This function contains the availability, Appointment working-time, and
221 + * Appointment buffer checks that must be repeated if the database connection
222 + * loses its advisory lock before persistence. The caller clears the relevant
223 + * request-local cache before every invocation.
224 + *
225 + * @param array $local_params Parsed booking parameters, passed by reference because force-save mode fixes capacity at one.
226 + * @param array $cleaned_params Sanitized booking request parameters.
227 + * @param array $php_performance Performance measurements, passed by reference.
228 + *
229 + * @return array|WP_Error Validated storage destination, or a visitor-safe validation error.
230 + */
231 +function wpbc_booking_validate_save_availability( &$local_params, $cleaned_params, &$php_performance ) {
232 +
233 + // Privileged imports and other established integrations may intentionally force a save.
234 + if ( ! empty( $cleaned_params['save_booking_even_if_unavailable'] ) ) {
235 + $local_params['how_many_items_to_book'] = 1;
236 + $dates_keys_arr = array_values( $local_params['dates_only_sql_arr'] );
237 + $resources_in_dates = array_fill_keys( $dates_keys_arr, array( $local_params['initial_resource_id'] ) );
238 + $where_to_save_booking = array(
239 + 'result' => 'ok',
240 + 'resources_in_dates' => $resources_in_dates,
241 + 'time_to_book' => $local_params['time_as_his_arr'],
242 + 'main__resource_id' => $local_params['initial_resource_id'],
243 + );
244 + } else {
245 + $php_performance = wpbc_php_performance_START( 'wpbc__where_to_save_booking', $php_performance );
246 +
247 + $where_to_save_booking = wpbc__where_to_save_booking(
248 + array(
249 + 'resource_id' => $local_params['initial_resource_id'],
250 + 'skip_booking_id' => $local_params['skip_booking_id'],
251 + 'dates_only_sql_arr' => $local_params['dates_only_sql_arr'],
252 + 'time_as_seconds_arr' => $local_params['time_as_seconds_arr'],
253 + 'how_many_items_to_book' => $local_params['how_many_items_to_book'],
254 + 'request_uri' => $cleaned_params['request_uri'],
255 + 'allow_past' => ! empty( $cleaned_params['allow_past'] ),
256 + 'is_use_booking_recurrent_time' => $local_params['is_use_booking_recurrent_time'],
257 + 'time_override_source' => ! empty( $local_params['time_override_arr']['source'] ) ? $local_params['time_override_arr']['source'] : '',
258 + 'as_single_resource' => false,
259 + 'aggregate_resource_id_arr' => $local_params['aggregate_resource_id_arr'],
260 + 'aggregate_type' => $cleaned_params['aggregate_type'],
261 + 'custom_form' => $cleaned_params['custom_form'],
262 + )
263 + );
264 +
265 + if ( 'error' === $where_to_save_booking['result'] ) {
266 + return new WP_Error( 'booking_can_not_save', $where_to_save_booking['message'] );
267 + }
268 +
269 + $php_performance = wpbc_php_performance_END( 'wpbc__where_to_save_booking', $php_performance );
270 + }
271 +
272 + if ( ! empty( $local_params['appointment_service'] ) && function_exists( 'wpbc_appointment_services_check_working_time' ) ) {
273 + $working_time_check = wpbc_appointment_services_check_working_time(
274 + $local_params['appointment_service'],
275 + $where_to_save_booking['main__resource_id'],
276 + array_keys( $where_to_save_booking['resources_in_dates'] ),
277 + $local_params['time_as_seconds_arr']
278 + );
279 + if ( is_wp_error( $working_time_check ) ) {
280 + return $working_time_check;
281 + }
282 + }
283 +
284 + if ( ! empty( $local_params['appointment_service'] ) && function_exists( 'wpbc_appointment_services_check_buffer_conflicts' ) ) {
285 + $buffer_check = wpbc_appointment_services_check_buffer_conflicts(
286 + $local_params['appointment_service'],
287 + $where_to_save_booking['main__resource_id'],
288 + array_keys( $where_to_save_booking['resources_in_dates'] ),
289 + $local_params['time_as_seconds_arr'],
290 + $local_params['skip_booking_id']
291 + );
292 + if ( is_wp_error( $buffer_check ) ) {
293 + return $buffer_check;
294 + }
295 + }
296 +
297 + return $where_to_save_booking;
298 +}
299 +
300 +
301 +/**
302 + * Save Booking - ADD NEW or UPDATE exist booking
219 303 *
220 304 * @param $request_params = [
221 305 * resource_id = 2 REQUIRED Default: 1
222 306 * dates_ddmmyy_csv = '27.10.2023, 28.10.2023, 29.10.2023' REQUIRED
@@ -530,9 +614,9 @@
530 614
531 615 if ( $is_public_booking_create_request && ! $has_verified_classic_context ) {
532 616 $ajx_data_arr['status'] = 'error';
533 617 $ajx_data_arr['status_error'] = 'classic_booking_context_required';
534 - $ajx_data_arr['ajx_after_action_message'] = __( 'The booking form context has expired. Please reload the page and try again.', 'booking' );
618 + $ajx_data_arr['ajx_after_action_message'] = wpbc_classic_booking_context_get_visitor_message( 'message_booking_form_context_required', $re_cleaned_params['resource_id'] );
535 619 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
536 620 return array( 'ajx_data' => $ajx_data_arr );
537 621 }
538 622
@@ -619,143 +703,108 @@
619 703 // -----------------------------------------------------------------------------------------------------------------
620 704 // Here GO
621 705 // -----------------------------------------------------------------------------------------------------------------
622 706
623 - // Force - resource saving parameters, instead of wpbc__where_to_save_booking()
624 - if ( ! empty( $re_cleaned_params["save_booking_even_if_unavailable"] ) ) {
625 -
626 - $local_params['how_many_items_to_book'] = 1;
627 -
628 - $dates_keys_arr = array_values( $local_params['dates_only_sql_arr'] ); // [ '2023-09-23', '2023-09-24' ]
629 -
630 - $resources_in_dates = array_fill_keys( $dates_keys_arr , array( $local_params['initial_resource_id'] ) ); // [ 2023-09-23 = [ 2 ], 2023-09-24 = [ 2 ] ]
631 -
632 - $where_to_save_booking = array();
633 - $where_to_save_booking['result'] = 'ok';
634 - $where_to_save_booking['resources_in_dates'] = $resources_in_dates; // [ 2023-09-23 = [ 2, 10, 11 ], 2023-09-24 = [ 2, 10, 11 ]
635 - $where_to_save_booking['time_to_book'] = $local_params['time_as_his_arr']; // [ "00:00:00", "24:00:00" ]
636 - $where_to_save_booking['main__resource_id'] = $local_params['initial_resource_id']; // here edit or request (parent/single) resource
637 -
638 - } else {
639 - // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
640 - $php_performance = wpbc_php_performance_START( 'wpbc__where_to_save_booking' , $php_performance );
641 - // </editor-fold>
642 -
643 - /**
644 - * Get slots [] where we can save booking = [ 'resources_in_dates' => [ 2023-10-18 = [ 2, 12, 10, 11 ]
645 - * 2023-10-19 = [ 2, 12, 10, 11 ]
646 - * 2023-10-20 = [ 2, 12, 10, 11 ]
647 - * ],
648 - * 'time_to_book' => [ "14:00:01" , "12:00:01" ],
649 - * 'result' => 'ok'
650 - * 'main__resource_id' => 2
651 - * ]
652 - * OR
653 - * [ 'result' => 'error', 'message' => 'Booking can not be saved ...' ]
654 - */
655 - $where_to_save_booking = wpbc__where_to_save_booking( array(
656 - 'resource_id' => $local_params['initial_resource_id'], // 2 //TODO: If edit booking. What to pass 'edit' or 'parent' resource ID?
657 - 'skip_booking_id' => $local_params['skip_booking_id'], // '', | 125 if edit booking
658 - 'dates_only_sql_arr' => $local_params['dates_only_sql_arr'], // [ "2023-10-18", "2023-10-25", "2023-11-25" ]
659 - 'time_as_seconds_arr' => $local_params['time_as_seconds_arr'], // [ 36000, 39600 ]
660 - 'how_many_items_to_book' => $local_params['how_many_items_to_book'], // 1
661 - 'request_uri' => $re_cleaned_params['request_uri'], // 'http://beta/resource-id2/'
662 - 'allow_past' => ! empty( $re_cleaned_params['allow_past'] ),
663 - 'is_use_booking_recurrent_time' => $local_params['is_use_booking_recurrent_time'], // true | false
664 - 'time_override_source' => ! empty( $local_params['time_override_arr']['source'] ) ? $local_params['time_override_arr']['source'] : '',
665 - 'as_single_resource' => false, // false
666 - 'aggregate_resource_id_arr' => $local_params['aggregate_resource_id_arr'], // Optional can be ''
667 - 'aggregate_type' => $re_cleaned_params['aggregate_type'], //TODO: this parameter does not transfer during saving, so here will be always default value 'bookings_only' // FixIn: 10.0.0.7.
668 - 'custom_form' => $re_cleaned_params['custom_form'] // FixIn: 10.0.0.10.
669 - ));
670 - // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- NO SLOTS TO SAVE " >
671 - if ( 'error' == $where_to_save_booking['result'] ) {
672 - $ajx_data_arr['status'] = 'error';
673 - $ajx_data_arr['status_error'] = 'booking_can_not_save';
674 - $ajx_data_arr['ajx_after_action_message'] = $where_to_save_booking['message'];
675 - $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
676 - return array( 'ajx_data' => $ajx_data_arr );
677 - }
678 - // </editor-fold>
679 -
680 - // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
681 - $php_performance = wpbc_php_performance_END( 'wpbc__where_to_save_booking' , $php_performance );
682 - // </editor-fold>
707 + $availability_guard = wpbc_booking_availability_guard_acquire();
708 + if ( is_wp_error( $availability_guard ) ) {
709 + $ajx_data_arr['status'] = 'error';
710 + $ajx_data_arr['status_error'] = $availability_guard->get_error_code();
711 + $ajx_data_arr['ajx_after_action_message'] = $availability_guard->get_error_message();
712 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
713 +
714 + return array( 'ajx_data' => $ajx_data_arr );
683 715 }
684 716
685 - if ( ! empty( $local_params['appointment_service'] ) && function_exists( 'wpbc_appointment_services_check_buffer_conflicts' ) ) {
686 - $buffer_check = wpbc_appointment_services_check_buffer_conflicts(
687 - $local_params['appointment_service'],
688 - $where_to_save_booking['main__resource_id'],
689 - array_keys( $where_to_save_booking['resources_in_dates'] ),
690 - $local_params['time_as_seconds_arr'],
691 - $local_params['skip_booking_id']
692 - );
693 - if ( is_wp_error( $buffer_check ) ) {
694 - $ajx_data_arr['status'] = 'error';
695 - $ajx_data_arr['status_error'] = 'appointment_service_buffer_conflict';
696 - $ajx_data_arr['ajx_after_action_message'] = $buffer_check->get_error_message();
697 - $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
698 - return array( 'ajx_data' => $ajx_data_arr );
699 - }
700 - }
717 + $guard_revalidation_attempts = 0;
718 + try {
719 + while ( true ) {
720 + wpbc_cache__clear( 'wpbc__sql__get_booking_dates' );
721 + $where_to_save_booking = wpbc_booking_validate_save_availability( $local_params, $re_cleaned_params, $php_performance );
701 722
723 + if ( is_wp_error( $where_to_save_booking ) ) {
724 + $ajx_data_arr['status'] = 'error';
725 + $ajx_data_arr['status_error'] = $where_to_save_booking->get_error_code();
726 + $ajx_data_arr['ajx_after_action_message'] = $where_to_save_booking->get_error_message();
727 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
702 728
703 - // Get parameters, from REQUEST
704 - $create_params = $local_params;
705 - $create_params['resource_id'] = ( ! empty( $local_params['edit_resource_id'] ) )
706 - ? $local_params['edit_resource_id'] // If we edit, then use original resource ???
707 - : $where_to_save_booking['main__resource_id']; // Here is important TIP, resource can be where is free, and not where we submit
708 - /**
709 - * TODO: I think it's resolved! Just test about this situation, when we edit the booking - and it's means that we have $local_params['edit_resource_id']
710 - * but what, if $where_to_save_booking do not contain this $local_params['edit_resource_id'] as available resource.
711 - * or even we have $local_params['edit_resource_id'] = 2 and $where_to_save_booking contain resources like [ 1, 2, 3, 4 ]
712 - * we make booking for 3 slots
713 - * in this case, main resource will be 2
714 - * but then when we loop resources in wpbc_db__booking_save() we will save child booking resources for dates like: 2, 3, 4 ( and it's wrong )
715 - * "(205, '2023-10-04 00:00:00', 0, NULL)" <- main resource '2' e.g. $local_params['edit_resource_id'] = 2
716 - * "(205, '2023-10-04 00:00:00', 0, 2)" ? <- child resource '2' e.g. [ .., 2, .. ] in $where_to_save_booking WHICH IS WRONG
717 - */
718 - $create_params['is_emails_send'] = $re_cleaned_params['is_emails_send'];
719 - $create_params['custom_form'] = $re_cleaned_params['custom_form'];
720 -
721 - make_bk_action( 'check_multiuser_params_for_client_side', $create_params['resource_id'] ); // Activate working with specific user in WP MU
722 -
723 - // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
724 - $php_performance = wpbc_php_performance_START( 'wpbc_db__booking_save' , $php_performance );
725 - // </editor-fold>
726 -
727 - // -----------------------------------------------------------------------------------------------------------------
728 - // == CREATE_THE 'NEW_BOOKING' ==
729 - // -----------------------------------------------------------------------------------------------------------------
730 - $create_booking_params = array(
731 - 'resource_id' => $create_params['resource_id'],
732 - 'custom_form' => $create_params['custom_form'],
733 - 'all_booking_data_arr' => $create_params['all_booking_data_arr'],
734 - 'dates_only_sql_arr' => $create_params['dates_only_sql_arr'],
735 - 'time_as_his_arr' => $create_params['time_as_his_arr'],
736 - 'is_from_admin_panel' => $create_params['is_from_admin_panel'],
737 - 'is_edit_booking' => $create_params['is_edit_booking'],
738 - 'is_duplicate_booking' => $create_params['is_duplicate_booking'],
739 - 'is_approve_booking' => $create_params['is_approve_booking'],
740 - 'how_many_items_to_book' => $create_params['how_many_items_to_book'],
741 - 'is_use_booking_recurrent_time' => $create_params['is_use_booking_recurrent_time'] // true | false
742 - );
743 - if ( ! empty( $create_params['appointment_service'] ) ) { $create_booking_params['appointment_service'] = $create_params['appointment_service']; }
744 - if ( ! empty( $create_params['sync_gid'] ) ) { $create_booking_params['sync_gid'] = $create_params['sync_gid']; }
745 -
746 - $booking_new_arr = wpbc_db__booking_save( $create_booking_params, $where_to_save_booking );
747 -
748 - // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING CREATION " >
749 - if ( 'ok' !== $booking_new_arr['status'] ) {
750 - $ajx_data_arr['status'] = $booking_new_arr['status'];
751 - $ajx_data_arr['status_error'] = 'booking_can_not_save';
752 - $ajx_data_arr['ajx_after_action_message'] = $booking_new_arr['message'];
753 - $ajx_data_arr['ajx_after_action_message_status'] = 'error';
754 - return array( 'ajx_data' => $ajx_data_arr );
729 + return array( 'ajx_data' => $ajx_data_arr );
730 + }
731 +
732 + // Get parameters, from REQUEST.
733 + $create_params = $local_params;
734 + $create_params['resource_id'] = ( ! empty( $local_params['edit_resource_id'] ) )
735 + ? $local_params['edit_resource_id']
736 + : $where_to_save_booking['main__resource_id'];
737 + $create_params['is_emails_send'] = $re_cleaned_params['is_emails_send'];
738 + $create_params['custom_form'] = $re_cleaned_params['custom_form'];
739 +
740 + make_bk_action( 'check_multiuser_params_for_client_side', $create_params['resource_id'] );
741 +
742 + $create_booking_params = array(
743 + 'resource_id' => $create_params['resource_id'],
744 + 'custom_form' => $create_params['custom_form'],
745 + 'all_booking_data_arr' => $create_params['all_booking_data_arr'],
746 + 'dates_only_sql_arr' => $create_params['dates_only_sql_arr'],
747 + 'time_as_his_arr' => $create_params['time_as_his_arr'],
748 + 'is_from_admin_panel' => $create_params['is_from_admin_panel'],
749 + 'is_edit_booking' => $create_params['is_edit_booking'],
750 + 'is_duplicate_booking' => $create_params['is_duplicate_booking'],
751 + 'is_approve_booking' => $create_params['is_approve_booking'],
752 + 'how_many_items_to_book' => $create_params['how_many_items_to_book'],
753 + 'is_use_booking_recurrent_time' => $create_params['is_use_booking_recurrent_time'],
754 + );
755 + if ( ! empty( $create_params['appointment_service'] ) ) {
756 + $create_booking_params['appointment_service'] = $create_params['appointment_service'];
757 + }
758 + if ( ! empty( $create_params['sync_gid'] ) ) {
759 + $create_booking_params['sync_gid'] = $create_params['sync_gid'];
760 + }
761 +
762 + if ( ! wpbc_booking_availability_guard_is_owned( $availability_guard ) ) {
763 + wpbc_booking_availability_guard_release( $availability_guard );
764 + if ( 1 <= $guard_revalidation_attempts ) {
765 + $availability_guard = wpbc_booking_availability_guard_get_busy_error();
766 + } else {
767 + ++$guard_revalidation_attempts;
768 + $availability_guard = wpbc_booking_availability_guard_acquire();
769 + }
770 +
771 + if ( is_wp_error( $availability_guard ) ) {
772 + $ajx_data_arr['status'] = 'error';
773 + $ajx_data_arr['status_error'] = $availability_guard->get_error_code();
774 + $ajx_data_arr['ajx_after_action_message'] = $availability_guard->get_error_message();
775 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
776 +
777 + return array( 'ajx_data' => $ajx_data_arr );
778 + }
779 +
780 + continue;
781 + }
782 +
783 + $php_performance = wpbc_php_performance_START( 'wpbc_db__booking_save', $php_performance );
784 + $booking_new_arr = wpbc_db__booking_save( $create_booking_params, $where_to_save_booking );
785 + if ( 'ok' !== $booking_new_arr['status'] ) {
786 + $ajx_data_arr['status'] = $booking_new_arr['status'];
787 + $ajx_data_arr['status_error'] = 'booking_can_not_save';
788 + $ajx_data_arr['ajx_after_action_message'] = $booking_new_arr['message'];
789 + $ajx_data_arr['ajx_after_action_message_status'] = 'error';
790 +
791 + return array( 'ajx_data' => $ajx_data_arr );
792 + }
793 +
794 + // Appointment buffers must become visible before the serialized availability section ends.
795 + if ( function_exists( 'wpbc_appointment_services_after_booking_save' ) ) {
796 + wpbc_appointment_services_after_booking_save( $booking_new_arr['booking_id'], $create_booking_params, $where_to_save_booking );
797 + }
798 +
799 + break;
800 + }
801 + } finally {
802 + wpbc_cache__clear( 'wpbc__sql__get_booking_dates' );
803 + wpbc_booking_availability_guard_release( $availability_guard );
755 804 }
756 - // </editor-fold>
757 805
806 + // Released compatibility hook: arbitrary callbacks must not extend the database lock duration.
758 807 do_action( 'wpbc_booking_after_save', $booking_new_arr['booking_id'], $create_booking_params, $where_to_save_booking );
759 808
760 809 // FixIn: 9.9.0.36.
761 810 if (