| @@ -54,10 +54,12 @@ | ||
| 54 | 54 | $server_request_uri = ( ( isset( $_SERVER['REQUEST_URI'] ) ) ? sanitize_text_field( $_SERVER['REQUEST_URI'] ) : '' ); /* phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash */ /* FixIn: sanitize_unslash */ |
| 55 | 55 | $server_http_referer_uri = ( ( isset( $_SERVER['HTTP_REFERER'] ) ) ? sanitize_text_field( $_SERVER['HTTP_REFERER'] ) : '' ); /* phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash */ /* FixIn: sanitize_unslash */ |
| 56 | 56 | $defaults = array( |
| 57 | 57 | 'request_uri' => ( ( ( defined( 'DOING_AJAX' ) ) && ( DOING_AJAX ) ) ? $server_http_referer_uri : $server_request_uri ), // front-end: $server_request_uri | ajax: $server_http_referer_uri // It different in Ajax requests. It's used for change-over days to detect for exception at specific pages |
| 58 | + 'allow_past' => false, | |
| 58 | 59 | 'as_single_resource' => false, |
| 59 | 60 | 'is_use_booking_recurrent_time' => ( 'On' === get_bk_option( 'booking_recurrent_time' ) ), |
| 61 | + 'time_override_source' => '', | |
| 60 | 62 | 'aggregate_resource_id_arr' => array(), |
| 61 | 63 | 'custom_form' => '' //FixIn: 10.0.0.10 // Required for checking all available time-slots and compare with booked time slots |
| 62 | 64 | ); |
| 63 | 65 | $local_params = wp_parse_args( $local_params, $defaults ); |
| @@ -90,8 +92,9 @@ | ||
| 90 | 92 | 'resource_id' => $local_params['resource_id'], |
| 91 | 93 | 'skip_booking_id' => $local_params['skip_booking_id'], |
| 92 | 94 | 'dates_to_check' => $maybe_extended__dates_to_check_arr, |
| 93 | 95 | 'request_uri' => $local_params['request_uri'], |
| 96 | + 'allow_past' => ! empty( $local_params['allow_past'] ), | |
| 94 | 97 | 'as_single_resource' => $local_params['as_single_resource'], // default FALSE :: get dates as for 'single resource' or 'parent' resource including bookings in all 'child booking resources' . |
| 95 | 98 | 'additional_bk_types' => $local_params['aggregate_resource_id_arr'], |
| 96 | 99 | 'aggregate_type' => empty( $local_params['aggregate_type'] ) ? 'bookings_only' : $local_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. |
| 97 | 100 | 'custom_form' => $local_params['custom_form'], // FixIn: 10.0.0.10. |
| @@ -96,16 +99,42 @@ | ||
| 96 | 99 | 'aggregate_type' => empty( $local_params['aggregate_type'] ) ? 'bookings_only' : $local_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. |
| 97 | 100 | 'custom_form' => $local_params['custom_form'], // FixIn: 10.0.0.10. |
| 98 | 101 | ) , $availability_per_days_arr__params ) ); |
| 99 | 102 | |
| 103 | + // A contact form has no calendar and uses one synthetic date only to satisfy the booking storage schema. | |
| 104 | + // Keep its requested resource, because calendar availability rules do not apply to a no-date submission. | |
| 105 | + $is_no_dates_booking = ( is_array( $local_params['dates_only_sql_arr'] ) && ( 1 === count( $local_params['dates_only_sql_arr'] ) ) && | |
| 106 | + wpbc_is_these_dates__for__no_dates( $local_params['dates_only_sql_arr'] ) ); | |
| 107 | + if ( $is_no_dates_booking ) { | |
| 108 | + $requested_resource_id = absint( $local_params['resource_id'] ); | |
| 109 | + $loaded_resource_ids = isset( $availability_per_days['resources_id_arr__in_dates'] ) ? array_map( 'absint', (array) $availability_per_days['resources_id_arr__in_dates'] ) : array(); | |
| 110 | + | |
| 111 | + if ( ( 0 === $requested_resource_id ) || ! in_array( $requested_resource_id, $loaded_resource_ids, true ) ) { | |
| 112 | + return array( | |
| 113 | + 'result' => 'error', | |
| 114 | + 'message' => 'Wrong ID of booking resource: ' . $requested_resource_id, | |
| 115 | + ); | |
| 116 | + } | |
| 117 | + | |
| 118 | + $storage_resource_ids = array_values( | |
| 119 | + array_unique( array_merge( array( $requested_resource_id ), $loaded_resource_ids ) ) | |
| 120 | + ); | |
| 121 | + $resources_in_dates = array_fill_keys( | |
| 122 | + array_values( $local_params['dates_only_sql_arr'] ), | |
| 123 | + $storage_resource_ids | |
| 124 | + ); | |
| 125 | + | |
| 126 | + return array( | |
| 127 | + 'result' => 'ok', | |
| 128 | + 'resources_in_dates' => $resources_in_dates, | |
| 129 | + 'time_to_book' => $local_params['time_as_his_arr'], | |
| 130 | + 'main__resource_id' => $requested_resource_id, | |
| 131 | + ); | |
| 132 | + } | |
| 133 | + | |
| 100 | 134 | // Get value of how many booking resources to book |
| 101 | 135 | $how_many_items_to_book = $local_params['how_many_items_to_book']; |
| 102 | 136 | |
| 103 | - // FixIn: 2981-01-13 13 Jan 2981. | |
| 104 | - if ( ( ! empty( $local_params['dates_only_sql_arr'] ) ) && ( wpbc_is_these_dates__for__no_dates( $local_params['dates_only_sql_arr'] ) ) ) { | |
| 105 | - $how_many_items_to_book = 0; | |
| 106 | - } | |
| 107 | - | |
| 108 | 137 | // ------------------------------------------------------------------------------------------------------------- |
| 109 | 138 | // [ 2023-10-18: [ 0:[ resource_id: 2, is_available: true, booked__seconds: [], booked__readable: [], time_to_book__seconds: [ 36030, 39570 ], time_to_book__readable: "10:00:00", "11:00:00" ] ], 1: [...], 2: [...], 3: [...] ], 2023-10-25: [...], 2023-11-25: [...] ] |
| 110 | 139 | $available_slots = wpbc__get_available_slots__for_selected_dates_times__bl( array( |
| 111 | 140 | 'dates_sql__arr' => $local_params['dates_only_sql_arr'], |
| @@ -110,14 +139,15 @@ | ||
| 110 | 139 | $available_slots = wpbc__get_available_slots__for_selected_dates_times__bl( array( |
| 111 | 140 | 'dates_sql__arr' => $local_params['dates_only_sql_arr'], |
| 112 | 141 | 'time_as_seconds_arr' => $local_params['time_as_seconds_arr'], |
| 113 | 142 | 'is_use_booking_recurrent_time' => $local_params['is_use_booking_recurrent_time'], |
| 143 | + 'time_override_source' => $local_params['time_override_source'], | |
| 114 | 144 | 'availability_per_days' => $availability_per_days |
| 115 | 145 | ) ); |
| 116 | 146 | |
| 117 | 147 | $main__resource_id = 0; |
| 118 | 148 | // == In SAME 'child' booking resources ============================================================ |
| 119 | - if ( 'On' === get_bk_option( 'booking_is_dissbale_booking_for_different_sub_resources' ) ) { | |
| 149 | + if ( class_exists( 'wpdev_bk_biz_l' ) && ( 'On' === get_bk_option( 'booking_is_dissbale_booking_for_different_sub_resources' ) ) ) { | |
| 120 | 150 | |
| 121 | 151 | $availability_in_same_child_resources = $availability_per_days['resources_id_arr__in_dates']; |
| 122 | 152 | |
| 123 | 153 | foreach ( $availability_per_days['resources_id_arr__in_dates'] as $child_resource_id ) { |
| @@ -155,13 +185,13 @@ | ||
| 155 | 185 | } else { |
| 156 | 186 | // Bad not save |
| 157 | 187 | return array( |
| 158 | 188 | 'result' => 'error', |
| 159 | - 'message' => __( 'These dates and times in this calendar are already booked or unavailable.', 'booking' ) | |
| 189 | + 'message' => wpbc_frontend_messages__get( 'message_dates_times_unavailable', array(), $local_params['resource_id'] ) | |
| 160 | 190 | . ' <br> ' |
| 161 | - . __( 'It is not possible to store this sequence of the dates into the one same resource.' , 'booking' ) | |
| 191 | + . wpbc_frontend_messages__get( 'message_cannot_save_in_one_resource', array(), $local_params['resource_id'] ) | |
| 162 | 192 | . ' <br> ' |
| 163 | - . __( 'Please choose alternative date(s), times, or adjust the number of slots booked.' , 'booking' ) | |
| 193 | + . wpbc_frontend_messages__get( 'message_choose_alternative_dates', array(), $local_params['resource_id'] ) | |
| 164 | 194 | . ' <a href="javascript:void(0)" onclick="' |
| 165 | 195 | .'jQuery( this ).next().toggle();' |
| 166 | 196 | .'jQuery( this).hide();' |
| 167 | 197 | .'jQuery( this ).parents(\'.wpbc_alert_message\').parent().on(\'hide\',function(even){jQuery(this).show();});' |
| @@ -204,11 +234,11 @@ | ||
| 204 | 234 | // Bad not save |
| 205 | 235 | // We can not store booking in this date day_sql_key2, number of available child booking resources less than required |
| 206 | 236 | return array( |
| 207 | 237 | 'result' => 'error', |
| 208 | - 'message' => __( 'These dates and times in this calendar are already booked or unavailable.', 'booking' ) | |
| 238 | + 'message' => wpbc_frontend_messages__get( 'message_dates_times_unavailable', array(), $local_params['resource_id'] ) | |
| 209 | 239 | . ' <br> ' |
| 210 | - . __( 'Please choose alternative date(s), times, or adjust the number of slots booked.' , 'booking' ) | |
| 240 | + . wpbc_frontend_messages__get( 'message_choose_alternative_dates', array(), $local_params['resource_id'] ) | |
| 211 | 241 | . ' <a href="javascript:void(0)" onclick="' |
| 212 | 242 | .'jQuery( this ).next().toggle();' |
| 213 | 243 | .'jQuery( this).hide();' |
| 214 | 244 | .'jQuery( this ).parents(\'.wpbc_alert_message\').parent().on(\'hide\',function(even){jQuery(this).show();});' |
| @@ -273,8 +303,9 @@ | ||
| 273 | 303 | $defaults = array( |
| 274 | 304 | 'dates_sql__arr' => array(), |
| 275 | 305 | 'time_as_seconds_arr' => array(), |
| 276 | 306 | 'availability_per_days' => array(), |
| 307 | + 'time_override_source' => '', | |
| 277 | 308 | 'is_use_booking_recurrent_time' => ( 'On' === get_bk_option( 'booking_recurrent_time' ) ) |
| 278 | 309 | ); |
| 279 | 310 | $params = wp_parse_args( $params, $defaults ); |
| 280 | 311 | |
| @@ -320,9 +351,9 @@ | ||
| 320 | 351 | $date_bookings_obj = $params['availability_per_days']['dates'][ $sql_class_day ]; |
| 321 | 352 | |
| 322 | 353 | foreach ( $params['availability_per_days']['resources_id_arr__in_dates'] as $child_resource_id ) { |
| 323 | 354 | |
| 324 | - $merged_seconds_arr = wpbc_get__booking_obj__merged_seconds_arr( $date_bookings_obj[ $child_resource_id ] ); | |
| 355 | + $merged_seconds_arr = wpbc_get__booking_obj__merged_seconds_arr( $date_bookings_obj[ $child_resource_id ], $params['time_override_source'] ); | |
| 325 | 356 | |
| 326 | 357 | $is_intersect = wpbc_is_intersect__merged_seconds_arr__and__seconds_arr( $merged_seconds_arr['merged_seconds'], $this_date_time_as_seconds_arr ); |
| 327 | 358 | |
| 328 | 359 | $this_date_available_resource = array( |
| @@ -366,10 +397,24 @@ | ||
| 366 | 397 | * 'merged_seconds' => $merged_seconds, |
| 367 | 398 | * 'merged_readable' => $merged_readable |
| 368 | 399 | * ] |
| 369 | 400 | */ |
| 370 | - function wpbc_get__booking_obj__merged_seconds_arr( $date_booking_obj ) { | |
| 401 | + function wpbc_get__booking_obj__merged_seconds_arr( $date_booking_obj, $time_override_source = '' ) { | |
| 371 | 402 | |
| 403 | + $is_times_availability_full_day_status = ( | |
| 404 | + ( 'times_availability' === $time_override_source ) | |
| 405 | + && ( ! empty( $date_booking_obj->is_day_unavailable ) ) | |
| 406 | + && ( ! empty( $date_booking_obj->_day_status ) ) | |
| 407 | + && ( 'full_day_booking' === $date_booking_obj->_day_status ) | |
| 408 | + ); | |
| 409 | + | |
| 410 | + if ( $is_times_availability_full_day_status ) { | |
| 411 | + $merged_seconds_arr = wpbc_get__booking_obj__booked_details_seconds_arr( $date_booking_obj ); | |
| 412 | + if ( ! empty( $merged_seconds_arr['merged_seconds'] ) ) { | |
| 413 | + return $merged_seconds_arr; | |
| 414 | + } | |
| 415 | + } | |
| 416 | + | |
| 372 | 417 | if ( $date_booking_obj->is_day_unavailable ){ |
| 373 | 418 | $merged_seconds = array( array( 0, 24 * 60 * 60 ) ); // Unavailable |
| 374 | 419 | $merged_readable = array( wpbc_transform_timerange__seconds_arr__in__formated_hours( array( 0, 24 * 60 * 60 ) ) ); |
| 375 | 420 | } else { |
| @@ -380,8 +425,48 @@ | ||
| 380 | 425 | return array( |
| 381 | 426 | 'merged_seconds' => $merged_seconds, |
| 382 | 427 | 'merged_readable' => $merged_readable |
| 383 | 428 | ); |
| 429 | + } | |
| 430 | + | |
| 431 | + | |
| 432 | + /** | |
| 433 | + * Rebuild real booked intervals from details after capacity marks the whole day unavailable. | |
| 434 | + * | |
| 435 | + * @param object $date_booking_obj Availability object for a date/resource. | |
| 436 | + * | |
| 437 | + * @return array | |
| 438 | + */ | |
| 439 | + function wpbc_get__booking_obj__booked_details_seconds_arr( $date_booking_obj ) { | |
| 440 | + | |
| 441 | + $seconds_arr = array(); | |
| 442 | + $readable_arr = array(); | |
| 443 | + | |
| 444 | + if ( empty( $date_booking_obj->__booked__times__details ) || ! is_array( $date_booking_obj->__booked__times__details ) ) { | |
| 445 | + return array( | |
| 446 | + 'merged_seconds' => array(), | |
| 447 | + 'merged_readable' => array(), | |
| 448 | + ); | |
| 449 | + } | |
| 450 | + | |
| 451 | + foreach ( $date_booking_obj->__booked__times__details as $time_key => $details ) { | |
| 452 | + if ( '0' === (string) $time_key || false === strpos( (string) $time_key, '-' ) ) { | |
| 453 | + $seconds_arr[] = array( 0, 24 * 60 * 60 ); | |
| 454 | + } else { | |
| 455 | + $seconds_arr[] = wpbc_transform_timerange__s_range__in__seconds_arr( $time_key ); | |
| 456 | + } | |
| 457 | + } | |
| 458 | + | |
| 459 | + $seconds_arr = wpbc_merge_intersected_intervals( $seconds_arr ); | |
| 460 | + | |
| 461 | + foreach ( $seconds_arr as $booked_time_slots__in_seconds ) { | |
| 462 | + $readable_arr[] = wpbc_transform_timerange__seconds_arr__in__formated_hours( $booked_time_slots__in_seconds ); | |
| 463 | + } | |
| 464 | + | |
| 465 | + return array( | |
| 466 | + 'merged_seconds' => $seconds_arr, | |
| 467 | + 'merged_readable' => $readable_arr, | |
| 468 | + ); | |
| 384 | 469 | } |
| 385 | 470 | |
| 386 | 471 | |
| 387 | 472 | /** |