| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | -if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly //FixIn: 9.8.0.4 | |
| 3 | +if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly // FixIn: 9.8.0.4. | |
| 4 | 4 | |
| 5 | 5 | // --------------------------------------------------------------------------------------------------------------------- |
| 6 | 6 | // Check where to save the booking |
| 7 | 7 | // --------------------------------------------------------------------------------------------------------------------- |
| @@ -6,8 +6,33 @@ | ||
| 6 | 6 | // Check where to save the booking |
| 7 | 7 | // --------------------------------------------------------------------------------------------------------------------- |
| 8 | 8 | |
| 9 | 9 | /** |
| 10 | + * Check if these dates contain marker '2981-01-13', which is means that this booking was submited without dates - as Contact Form. | |
| 11 | + * | |
| 12 | + * @param array|string $dates_sql_arr - array of SQL dates. | |
| 13 | + * | |
| 14 | + * @return bool | |
| 15 | + */ | |
| 16 | +function wpbc_is_these_dates__for__no_dates( $dates_sql_arr ){ | |
| 17 | + | |
| 18 | + // FixIn: 2981-01-13 13 Jan 2981. | |
| 19 | + | |
| 20 | + if ( is_array( $dates_sql_arr ) ) { | |
| 21 | + foreach ( $dates_sql_arr as $date_ymd ) { | |
| 22 | + if ( '2981-01-13' === gmdate( 'Y-m-d', mysql2date( 'U', $date_ymd ) ) ) { | |
| 23 | + return true; | |
| 24 | + } | |
| 25 | + } | |
| 26 | + } elseif ( '2981-01-13' === gmdate( 'Y-m-d', mysql2date( 'U', $dates_sql_arr ) ) ) { | |
| 27 | + return true; | |
| 28 | + } | |
| 29 | + | |
| 30 | + return false; | |
| 31 | +} | |
| 32 | + | |
| 33 | + | |
| 34 | +/** | |
| 10 | 35 | * Get booking resources for each selected date, where we can save the booking, or false if we can not save it |
| 11 | 36 | * |
| 12 | 37 | * @param array $params = [ |
| 13 | 38 | * 'resource_id' => 2 |
| @@ -25,12 +50,16 @@ | ||
| 25 | 50 | * = [ 'result' => 'error', 'message' => 'Booking can not be saved ...' ] |
| 26 | 51 | */ |
| 27 | 52 | function wpbc__where_to_save_booking( $local_params ){ |
| 28 | 53 | |
| 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 | + $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 */ | |
| 29 | 56 | $defaults = array( |
| 30 | - 'request_uri' => ( ( ( defined( 'DOING_AJAX' ) ) && ( DOING_AJAX ) ) ? $_SERVER['HTTP_REFERER'] : $_SERVER['REQUEST_URI'] ), // front-end: $_SERVER['REQUEST_URI'] | ajax: $_SERVER['HTTP_REFERER'] // It different in Ajax requests. It's used for change-over days to detect for exception at specific pages | |
| 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, | |
| 31 | 59 | 'as_single_resource' => false, |
| 32 | 60 | 'is_use_booking_recurrent_time' => ( 'On' === get_bk_option( 'booking_recurrent_time' ) ), |
| 61 | + 'time_override_source' => '', | |
| 33 | 62 | 'aggregate_resource_id_arr' => array(), |
| 34 | 63 | 'custom_form' => '' //FixIn: 10.0.0.10 // Required for checking all available time-slots and compare with booked time slots |
| 35 | 64 | ); |
| 36 | 65 | $local_params = wp_parse_args( $local_params, $defaults ); |
| @@ -45,9 +74,13 @@ | ||
| 45 | 74 | $local_params['time_as_his_arr'] = array( |
| 46 | 75 | wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[0] ), |
| 47 | 76 | wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[1] ) |
| 48 | 77 | ); |
| 49 | - | |
| 78 | + // FixIn: 2981-01-13 13 Jan 2981 | |
| 79 | + $availability_per_days_arr__params = array(); | |
| 80 | + if ( ( ! empty( $local_params['dates_only_sql_arr'] ) ) && ( is_array( $local_params['dates_only_sql_arr'] ) ) && ( '2981-01-13' === $local_params['dates_only_sql_arr'][0] ) ) { | |
| 81 | + $availability_per_days_arr__params['is_days_always_available'] = true; | |
| 82 | + } | |
| 50 | 83 | // If we are using the unavailable before/after booking dates, then we need to extend "$local_params['dates_only_sql_arr']" to such dates. |
| 51 | 84 | if ( function_exists( 'wpbc__extend_checking_dates_range__if_used__extra_seconds__before_after' ) ) { |
| 52 | 85 | $maybe_extended__dates_to_check_arr = wpbc__extend_checking_dates_range__if_used__extra_seconds__before_after( $local_params['dates_only_sql_arr'] ); |
| 53 | 86 | } else { |
| @@ -53,21 +86,55 @@ | ||
| 53 | 86 | } else { |
| 54 | 87 | $maybe_extended__dates_to_check_arr = $local_params['dates_only_sql_arr']; |
| 55 | 88 | } |
| 56 | 89 | // [ "dates": [ "2023-11-05": [ "day_availability":4, ... "2": [ "is_day_unavailable":false, ...]], '2023-11-06':[...] ] ,"resources_id_arr__in_dates":[2,12,10,11]} |
| 57 | - $availability_per_days = wpbc_get_availability_per_days_arr( array( | |
| 58 | - 'resource_id' => $local_params['resource_id'], | |
| 59 | - 'skip_booking_id' => $local_params['skip_booking_id'], | |
| 60 | - 'dates_to_check' => $maybe_extended__dates_to_check_arr, | |
| 61 | - 'request_uri' => $local_params['request_uri'], | |
| 62 | - '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' | |
| 63 | - 'additional_bk_types' => $local_params['aggregate_resource_id_arr'], | |
| 64 | - 'aggregate_type' => $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 | |
| 65 | - 'custom_form' => $local_params['custom_form'] //FixIn: 10.0.0.10 | |
| 66 | - ) ); | |
| 67 | 90 | |
| 91 | + $availability_per_days = wpbc_get_availability_per_days_arr( wp_parse_args( array( | |
| 92 | + 'resource_id' => $local_params['resource_id'], | |
| 93 | + 'skip_booking_id' => $local_params['skip_booking_id'], | |
| 94 | + 'dates_to_check' => $maybe_extended__dates_to_check_arr, | |
| 95 | + 'request_uri' => $local_params['request_uri'], | |
| 96 | + 'allow_past' => ! empty( $local_params['allow_past'] ), | |
| 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' . | |
| 98 | + 'additional_bk_types' => $local_params['aggregate_resource_id_arr'], | |
| 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. | |
| 100 | + 'custom_form' => $local_params['custom_form'], // FixIn: 10.0.0.10. | |
| 101 | + ) , $availability_per_days_arr__params ) ); | |
| 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 | + | |
| 68 | 134 | // Get value of how many booking resources to book |
| 69 | 135 | $how_many_items_to_book = $local_params['how_many_items_to_book']; |
| 136 | + | |
| 70 | 137 | // ------------------------------------------------------------------------------------------------------------- |
| 71 | 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: [...] ] |
| 72 | 139 | $available_slots = wpbc__get_available_slots__for_selected_dates_times__bl( array( |
| 73 | 140 | 'dates_sql__arr' => $local_params['dates_only_sql_arr'], |
| @@ -72,14 +139,15 @@ | ||
| 72 | 139 | $available_slots = wpbc__get_available_slots__for_selected_dates_times__bl( array( |
| 73 | 140 | 'dates_sql__arr' => $local_params['dates_only_sql_arr'], |
| 74 | 141 | 'time_as_seconds_arr' => $local_params['time_as_seconds_arr'], |
| 75 | 142 | 'is_use_booking_recurrent_time' => $local_params['is_use_booking_recurrent_time'], |
| 143 | + 'time_override_source' => $local_params['time_override_source'], | |
| 76 | 144 | 'availability_per_days' => $availability_per_days |
| 77 | 145 | ) ); |
| 78 | 146 | |
| 79 | 147 | $main__resource_id = 0; |
| 80 | 148 | // == In SAME 'child' booking resources ============================================================ |
| 81 | - 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' ) ) ) { | |
| 82 | 150 | |
| 83 | 151 | $availability_in_same_child_resources = $availability_per_days['resources_id_arr__in_dates']; |
| 84 | 152 | |
| 85 | 153 | foreach ( $availability_per_days['resources_id_arr__in_dates'] as $child_resource_id ) { |
| @@ -117,26 +185,26 @@ | ||
| 117 | 185 | } else { |
| 118 | 186 | // Bad not save |
| 119 | 187 | return array( |
| 120 | 188 | 'result' => 'error', |
| 121 | - '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'] ) | |
| 122 | 190 | . ' <br> ' |
| 123 | - . __( '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'] ) | |
| 124 | 192 | . ' <br> ' |
| 125 | - . __( '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'] ) | |
| 126 | 194 | . ' <a href="javascript:void(0)" onclick="' |
| 127 | 195 | .'jQuery( this ).next().toggle();' |
| 128 | 196 | .'jQuery( this).hide();' |
| 129 | 197 | .'jQuery( this ).parents(\'.wpbc_alert_message\').parent().on(\'hide\',function(even){jQuery(this).show();});' |
| 130 | 198 | .'">' |
| 131 | - . __( 'Show more details' ) | |
| 199 | + . __( 'Show more details', 'booking' ) | |
| 132 | 200 | . '</a>' |
| 133 | 201 | . ' <div style="display:none;">' |
| 134 | 202 | . '<p><hr><code>' |
| 135 | - . json_encode( $available_slots ) | |
| 203 | + . wp_json_encode( $available_slots ) | |
| 136 | 204 | . '</code></p>' |
| 137 | 205 | . '</div>' |
| 138 | - . ' <div style="display:none;">' . json_encode( $available_slots ) . '</div>' | |
| 206 | + . ' <div style="display:none;">' . wp_json_encode( $available_slots ) . '</div>' | |
| 139 | 207 | ); |
| 140 | 208 | } |
| 141 | 209 | |
| 142 | 210 | } else { |
| @@ -166,24 +234,26 @@ | ||
| 166 | 234 | // Bad not save |
| 167 | 235 | // We can not store booking in this date day_sql_key2, number of available child booking resources less than required |
| 168 | 236 | return array( |
| 169 | 237 | 'result' => 'error', |
| 170 | - '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'] ) | |
| 171 | 239 | . ' <br> ' |
| 172 | - . __( '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'] ) | |
| 173 | 241 | . ' <a href="javascript:void(0)" onclick="' |
| 174 | 242 | .'jQuery( this ).next().toggle();' |
| 175 | 243 | .'jQuery( this).hide();' |
| 176 | 244 | .'jQuery( this ).parents(\'.wpbc_alert_message\').parent().on(\'hide\',function(even){jQuery(this).show();});' |
| 177 | 245 | .'">' |
| 178 | - . __( 'Show more details' ) | |
| 246 | + . __( 'Show more details', 'booking' ) | |
| 179 | 247 | . '</a>' |
| 180 | 248 | . ' <div style="display:none;">' |
| 181 | 249 | . '<p><strong>' |
| 182 | - . sprintf( __( 'Booking can not be saved in this date %s, number of available (single or child) booking resource(s) (%d) less than required (%d).', 'booking' ) | |
| 250 | + /* translators: 1: ... */ | |
| 251 | + . sprintf( __( 'Booking can not be saved in this date %1$s, number of available (single or child) booking resource(s) (%2$d) less than required (%3$d).', 'booking' ) | |
| 183 | 252 | , $day_sql_key2, count( $available_res_in_day_arr ), $how_many_items_to_book ) |
| 184 | 253 | . '</strong><hr><code>' |
| 185 | - . json_encode( $available_slots ) | |
| 254 | + . wp_json_encode( $available_slots ) | |
| 255 | + // . wp_json_encode( $local_params, $availability_per_days ) | |
| 186 | 256 | . '</code></p>' |
| 187 | 257 | . '</div>' |
| 188 | 258 | |
| 189 | 259 | ); |
| @@ -233,8 +303,9 @@ | ||
| 233 | 303 | $defaults = array( |
| 234 | 304 | 'dates_sql__arr' => array(), |
| 235 | 305 | 'time_as_seconds_arr' => array(), |
| 236 | 306 | 'availability_per_days' => array(), |
| 307 | + 'time_override_source' => '', | |
| 237 | 308 | 'is_use_booking_recurrent_time' => ( 'On' === get_bk_option( 'booking_recurrent_time' ) ) |
| 238 | 309 | ); |
| 239 | 310 | $params = wp_parse_args( $params, $defaults ); |
| 240 | 311 | |
| @@ -280,9 +351,9 @@ | ||
| 280 | 351 | $date_bookings_obj = $params['availability_per_days']['dates'][ $sql_class_day ]; |
| 281 | 352 | |
| 282 | 353 | foreach ( $params['availability_per_days']['resources_id_arr__in_dates'] as $child_resource_id ) { |
| 283 | 354 | |
| 284 | - $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'] ); | |
| 285 | 356 | |
| 286 | 357 | $is_intersect = wpbc_is_intersect__merged_seconds_arr__and__seconds_arr( $merged_seconds_arr['merged_seconds'], $this_date_time_as_seconds_arr ); |
| 287 | 358 | |
| 288 | 359 | $this_date_available_resource = array( |
| @@ -326,10 +397,24 @@ | ||
| 326 | 397 | * 'merged_seconds' => $merged_seconds, |
| 327 | 398 | * 'merged_readable' => $merged_readable |
| 328 | 399 | * ] |
| 329 | 400 | */ |
| 330 | - 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 = '' ) { | |
| 331 | 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 | + | |
| 332 | 417 | if ( $date_booking_obj->is_day_unavailable ){ |
| 333 | 418 | $merged_seconds = array( array( 0, 24 * 60 * 60 ) ); // Unavailable |
| 334 | 419 | $merged_readable = array( wpbc_transform_timerange__seconds_arr__in__formated_hours( array( 0, 24 * 60 * 60 ) ) ); |
| 335 | 420 | } else { |
| @@ -340,8 +425,48 @@ | ||
| 340 | 425 | return array( |
| 341 | 426 | 'merged_seconds' => $merged_seconds, |
| 342 | 427 | 'merged_readable' => $merged_readable |
| 343 | 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 | + ); | |
| 344 | 469 | } |
| 345 | 470 | |
| 346 | 471 | |
| 347 | 472 | /** |