| @@ -165,12 +165,11 @@ | ||
| 165 | 165 | {$sql_skip_bookings} |
| 166 | 166 | ORDER BY dt.booking_date"; |
| 167 | 167 | } |
| 168 | 168 | |
| 169 | - // Show past bookings, as well | |
| 170 | - // $my_sql = str_replace( 'AND dt.booking_date >= CURDATE()', '', $my_sql ); | |
| 171 | 169 | |
| 172 | - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared | |
| 170 | + | |
| 171 | + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 173 | 172 | $sql__dates_obj__arr = $wpdb->get_results( $my_sql ); |
| 174 | 173 | |
| 175 | 174 | |
| 176 | 175 | // <editor-fold defaultstate="collapsed" desc=" = CACHE - SAVE :: = " > |
| @@ -185,9 +184,9 @@ | ||
| 185 | 184 | * Get SQL WHERE condition for Dates |
| 186 | 185 | * |
| 187 | 186 | * @param array $params [ 'dates_to_check' => 'CURDATE' ] | [ 'dates_to_check' => 'ALL' ] | [ 'dates_to_check' => [ '2023-07-15' , '2023-07-21' ] ] |
| 188 | 187 | * |
| 189 | - * @return string ' AND ( dt.booking_date >= CURDATE() ) ' | |
| 188 | + * @return string ' AND ( dt.booking_date >= C URDATE() ) ' | |
| 190 | 189 | */ |
| 191 | 190 | function wpbc_get__sql_where__for__dates( $params ){ |
| 192 | 191 | |
| 193 | 192 | $defaults = array( |
| @@ -199,29 +198,68 @@ | ||
| 199 | 198 | if ( 'ALL' == $params['dates_to_check'] ) { // All dates -> '' |
| 200 | 199 | |
| 201 | 200 | return ''; |
| 202 | 201 | |
| 203 | - } else if ( 'CURDATE' == $params['dates_to_check'] ) { // Current dates -> CURDATE() | |
| 202 | + } else if ( 'CURDATE' == $params['dates_to_check'] ) { // Current dates -> CURDATE | |
| 204 | 203 | |
| 205 | - return ' AND ( dt.booking_date >= CURDATE() ) ' ; | |
| 204 | + return ' AND ( dt.booking_date >= ' . wpbc_sql_date_math_expr_explicit('', 'curdate') . ' ) ' ; | |
| 206 | 205 | |
| 207 | - } else if ( is_array( $params['dates_to_check'] ) ) { // Specific Date(s) -> [ '2023-07-15' , '2023-07-21' ] | |
| 206 | + } else if ( is_array( $params['dates_to_check'] ) ) { // Specific Date(s). | |
| 208 | 207 | |
| 209 | - $dates_sql_array = array(); | |
| 208 | + // FixIn: 10.14.9.1. | |
| 210 | 209 | |
| 211 | - for( $i = 0; $i < count( $params['dates_to_check'] ); $i++) { | |
| 210 | + // Sanitize and validate all incoming dates first. | |
| 211 | + $dates_to_check = wpbc_sanitize_dates_to_check( $params['dates_to_check'] ); | |
| 212 | 212 | |
| 213 | - $dates_sql_array[] = "( ( dt.booking_date >= '{$params['dates_to_check'][$i]} 00:00:00' ) AND ( dt.booking_date <= '{$params['dates_to_check'][$i]} 23:59:59' ) )"; | |
| 213 | + // If nothing valid left – do not add any date filter (or return empty string). | |
| 214 | + if ( empty( $dates_to_check ) ) { | |
| 215 | + return ''; | |
| 214 | 216 | } |
| 215 | 217 | |
| 216 | - $dates_sql_str = implode( ' OR ', $dates_sql_array ); | |
| 218 | + global $wpdb; | |
| 217 | 219 | |
| 218 | - return " AND ( {$dates_sql_str} ) " ; | |
| 220 | + $count_dates = count( $dates_to_check ); | |
| 219 | 221 | |
| 222 | + // Case 1: date range (2 items: start / end). | |
| 223 | + if ( 2 === $count_dates ) { | |
| 224 | + $start = $dates_to_check[0] . ' 00:00:00'; | |
| 225 | + $end = $dates_to_check[1] . ' 23:59:59'; | |
| 226 | + | |
| 227 | + // %s will be properly escaped and quoted by $wpdb->prepare(). | |
| 228 | + $dates_sql_str = $wpdb->prepare( ' ( ( dt.booking_date >= %s ) AND ( dt.booking_date <= %s ) ) ', $start, $end ); | |
| 229 | + | |
| 230 | + // Case 2: one date – from that day forward. | |
| 231 | + } else if ( 1 === $count_dates ) { | |
| 232 | + | |
| 233 | + $start = $dates_to_check[0] . ' 00:00:00'; | |
| 234 | + | |
| 235 | + $dates_sql_str = $wpdb->prepare( ' ( dt.booking_date >= %s ) ', $start ); | |
| 236 | + | |
| 237 | + // Case 3: list of specific separate dates – OR between each day. | |
| 238 | + } else { | |
| 239 | + | |
| 240 | + $conditions = array(); | |
| 241 | + $values = array(); | |
| 242 | + | |
| 243 | + foreach ( $dates_to_check as $date ) { | |
| 244 | + | |
| 245 | + $conditions[] = '( ( dt.booking_date >= %s ) AND ( dt.booking_date <= %s ) )'; | |
| 246 | + | |
| 247 | + $values[] = $date . ' 00:00:00'; | |
| 248 | + $values[] = $date . ' 23:59:59'; | |
| 249 | + } | |
| 250 | + | |
| 251 | + $conditions_sql = implode( ' OR ', $conditions ); | |
| 252 | + | |
| 253 | + // $values is an array; $wpdb->prepare() will expand it safely. | |
| 254 | + $dates_sql_str = $wpdb->prepare( $conditions_sql, $values ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared | |
| 255 | + } | |
| 256 | + | |
| 257 | + return ' AND ( ' . $dates_sql_str . ' ) '; | |
| 220 | 258 | } |
| 221 | 259 | |
| 222 | 260 | // Default CURDATE |
| 223 | - return ' AND ( dt.booking_date >= CURDATE() ) '; | |
| 261 | + return ' AND ( dt.booking_date >= ' . wpbc_sql_date_math_expr_explicit('', 'curdate') . ' ) '; | |
| 224 | 262 | } |
| 225 | 263 | |
| 226 | 264 | |
| 227 | 265 | |
| @@ -341,8 +379,10 @@ | ||
| 341 | 379 | ); |
| 342 | 380 | $params_for_dates = wp_parse_args( $params, $defaults ); |
| 343 | 381 | |
| 344 | 382 | |
| 383 | + // $params_for_dates['is_days_always_available'] = in_array( $params_for_dates['resource_id'], array( '1' ) ); // Set some resources as always avavailable. | |
| 384 | + | |
| 345 | 385 | if ( $params_for_dates['is_days_always_available'] ) { // All bookings showing as available dates. |
| 346 | 386 | return array(); |
| 347 | 387 | } |
| 348 | 388 | |
| @@ -514,9 +554,11 @@ | ||
| 514 | 554 | 'as_single_resource' => false, // get dates as for 'single resource' or 'parent' resource including bookings in all 'child booking resources'. |
| 515 | 555 | 'max_days_count' => wpbc_get_max_visible_days_in_calendar(), // 365. |
| 516 | 556 | 'timeslots_to_check_intersect' => array(), // arr: '12:20 - 12:55', '13:00 - 14:00' ) // TODO: ? do we really need it, because below we get it from function. |
| 517 | 557 | '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. |
| 558 | + 'allow_past' => false, | |
| 518 | 559 | 'custom_form' => '', // Required for checking all available time-slots and compare with booked time slots. |
| 560 | + 'force_check_from_today_unavailable' => false, // Optional admin tools can force relative availability checks inside explicit date ranges. | |
| 519 | 561 | ); |
| 520 | 562 | |
| 521 | 563 | $params = wp_parse_args( $params, $defaults ); |
| 522 | 564 | |
| @@ -521,11 +563,12 @@ | ||
| 521 | 563 | $params = wp_parse_args( $params, $defaults ); |
| 522 | 564 | |
| 523 | 565 | // FixIn: 10.7.1.2. //FixIn: 10.10.3.2. |
| 524 | 566 | if ( |
| 567 | + ( ! empty( $params['allow_past'] ) ) || | |
| 525 | 568 | ( false !== strpos( $params['request_uri'], 'allow_past' ) ) || |
| 526 | 569 | ( |
| 527 | - ( false !== strpos( $params['request_uri'], 'page=wpbc-new' ) ) && | |
| 570 | + wpbc_is_new_booking_page_url( $params['request_uri'] ) && | |
| 528 | 571 | ( false !== strpos( $params['request_uri'], 'booking_hash' ) ) |
| 529 | 572 | ) |
| 530 | 573 | ) { |
| 531 | 574 | $params['dates_to_check'] = 'ALL'; |
| @@ -580,10 +623,15 @@ | ||
| 580 | 623 | // ----------------------------------------------------------------------------------------------------------------- |
| 581 | 624 | // Main function to get "Bookings": ['2023-08-30'][ > resource_id < ][ > time_seconds_range < ] => booking_date_obj[ booking_id:45, approved:1, ... |
| 582 | 625 | $bookings__in_dates = wpbc_get__booked_dates__per_resources__arr( $params ); |
| 583 | 626 | |
| 627 | + // ----------------------------------------------------------------------------------------------------------------- | |
| 628 | + // Time slots availability blocks. | |
| 629 | + // ----------------------------------------------------------------------------------------------------------------- | |
| 630 | + $availability_timeslots__in_dates = array(); // FixIn: 10.16.1. | |
| 584 | 631 | |
| 585 | 632 | |
| 633 | + | |
| 586 | 634 | // ----------------------------------------------------------------------------------------------------------------- |
| 587 | 635 | // Booking > Availability page |
| 588 | 636 | // ----------------------------------------------------------------------------------------------------------------- |
| 589 | 637 | $search_dates = $params['dates_to_check']; // 'CURDATE' | 'ALL' | [ "2023-08-03" , "2023-08-04" , "2023-08-05" ] |
| @@ -602,9 +650,28 @@ | ||
| 602 | 650 | } |
| 603 | 651 | $unavailable_dates__per_resources__arr = wpbc_aggregate_merge_availability( $unavailable_dates__per_resources__arr, |
| 604 | 652 | $resource_id__with_children__arr, |
| 605 | 653 | $aggregate_resource_id_arr ); |
| 654 | + // FixIn: 10.16.1. | |
| 655 | + if ( function_exists( 'wpbc_availability_timeslots__get_intervals_for_resources' ) ) { | |
| 656 | + $availability_timeslots__resource_ids = wpbc_get_unique_array_of_resources_id( $resource_id__with_children__arr, $aggregate_resource_id_arr ); | |
| 657 | + $availability_timeslots__in_dates = wpbc_availability_timeslots__get_intervals_for_resources( | |
| 658 | + array( | |
| 659 | + 'resource_id' => $availability_timeslots__resource_ids, | |
| 660 | + 'dates_to_check' => $search_dates, | |
| 661 | + 'max_days_count' => $params['max_days_count'], | |
| 662 | + ) | |
| 663 | + ); | |
| 606 | 664 | |
| 665 | + if ( function_exists( 'wpbc_availability_timeslots__aggregate_merge_intervals' ) ) { | |
| 666 | + $availability_timeslots__in_dates = wpbc_availability_timeslots__aggregate_merge_intervals( | |
| 667 | + $availability_timeslots__in_dates, | |
| 668 | + $resource_id__with_children__arr, | |
| 669 | + $aggregate_resource_id_arr | |
| 670 | + ); | |
| 671 | + } | |
| 672 | + } | |
| 673 | + | |
| 607 | 674 | // ----------------------------------------------------------------------------------------------------------------- |
| 608 | 675 | // Booking > Resources > Availability page |
| 609 | 676 | // ----------------------------------------------------------------------------------------------------------------- |
| 610 | 677 | $season_filters_availability = array(); |
| @@ -621,14 +688,14 @@ | ||
| 621 | 688 | // P a r a m e t e r s f o r L O O P |
| 622 | 689 | // ----------------------------------------------------------------------------------------------------------------- |
| 623 | 690 | |
| 624 | 691 | // - Is this Booking > Add booking page ? -------------------------------------------------------------------------- |
| 625 | - $is_this_bap_page = ( false !== strpos( $params['request_uri'], 'page=wpbc-new' ) ) ? true : false; | |
| 692 | + $is_this_bap_page = wpbc_is_new_booking_page_url( $params['request_uri'] ); | |
| 626 | 693 | $is_this_hash_page = ( false !== strpos( $params['request_uri'], 'booking_hash' ) ) ? true : false; // Set it to TRUE for adding booking in past at Booking > Add booking page |
| 627 | 694 | |
| 628 | 695 | // FixIn: 10.7.1.2. |
| 629 | 696 | if ( ! $is_this_hash_page ) { |
| 630 | - $is_this_hash_page = ( false !== strpos( $params['request_uri'], 'allow_past' ) ) ? true : false; | |
| 697 | + $is_this_hash_page = ( ( ! empty( $params['allow_past'] ) ) || ( false !== strpos( $params['request_uri'], 'allow_past' ) ) ) ? true : false; | |
| 631 | 698 | } |
| 632 | 699 | |
| 633 | 700 | if ( ( $is_this_bap_page ) && ( $is_this_hash_page ) ) { // Start days in calendar from = PAST |
| 634 | 701 | $params['dates_to_check'] = 'ALL'; |
| @@ -643,9 +710,9 @@ | ||
| 643 | 710 | $start_date_unix = strtotime( '-' . intval( $params['max_days_count'] ) . ' days' ); // - 365 days |
| 644 | 711 | $date_with_wp_timezone = wpbc_datetime_localized__use_wp_timezone( gmdate( 'Y-m-d H:i:s', $start_date_unix ), 'Y-m-d 00:00:00' ); |
| 645 | 712 | $today_inix_timestamp = strtotime( $date_with_wp_timezone ); |
| 646 | 713 | |
| 647 | - } else if ( 'CURDATE' == $params['dates_to_check'] ) { // Current dates -> CURDATE() | |
| 714 | + } else if ( 'CURDATE' == $params['dates_to_check'] ) { // Current dates -> CURDATE | |
| 648 | 715 | |
| 649 | 716 | $start_day_number = 1; |
| 650 | 717 | // $today_inix_timestamp = strtotime( gmdate( 'Y-m-d 00:00:00', strtotime( 'now' ) ) ); // Today 00:00:00 |
| 651 | 718 | |
| @@ -657,12 +724,12 @@ | ||
| 657 | 724 | } else if ( is_array( $params['dates_to_check'] ) ) { |
| 658 | 725 | |
| 659 | 726 | $start_day_number = 1; |
| 660 | 727 | |
| 661 | - $today_inix_timestamp = strtotime( $params['dates_to_check'][0] . ' 00:00:00' ); // '2023-09-03 00:00:00' | |
| 728 | + $today_inix_timestamp = strtotime( $params['dates_to_check'][0] . ' 00:00:00 UTC' ); // '2023-09-03 00:00:00' | |
| 662 | 729 | |
| 663 | - $dif_in_days = strtotime( $params['dates_to_check'][ ( count( $params['dates_to_check'] ) - 1 ) ] . ' 00:00:00' ) | |
| 664 | - - strtotime( $params['dates_to_check'][0] . ' 00:00:00' ); | |
| 730 | + $dif_in_days = strtotime( $params['dates_to_check'][ ( count( $params['dates_to_check'] ) - 1 ) ] . ' 00:00:00 UTC' ) | |
| 731 | + - strtotime( $params['dates_to_check'][0] . ' 00:00:00 UTC' ); | |
| 665 | 732 | |
| 666 | 733 | $dif_in_days = $dif_in_days / ( 24 * 60 * 60 ); |
| 667 | 734 | $params['max_days_count'] = ceil( $dif_in_days ) + 1; |
| 668 | 735 | } |
| @@ -717,8 +784,9 @@ | ||
| 717 | 784 | $availability_per_day[ $my_day_tag ]['statuses']['bookings_status'] = array(); // per each child booking resource |
| 718 | 785 | $availability_per_day[ $my_day_tag ]['summary'] = array(); |
| 719 | 786 | $availability_per_day[ $my_day_tag ]['summary']['status_for_day'] = ''; // available | time_slots_booking | full_day_booking | ... |
| 720 | 787 | $availability_per_day[ $my_day_tag ]['summary']['status_for_bookings'] = ''; // pending | approved | CO: pending_pending | pending_approved | approved_pending | approved_approved |
| 788 | + $availability_per_day[ $my_day_tag ]['summary']['status_for_bookings_arr'] = array(); // Structured statuses array, e.g. array( 'approved', 'approved_approved' ). | |
| 721 | 789 | $availability_per_day[ $my_day_tag ]['summary']['tooltip_times'] = ''; |
| 722 | 790 | $availability_per_day[ $my_day_tag ]['summary']['tooltip_availability'] = ''; |
| 723 | 791 | $availability_per_day[ $my_day_tag ]['summary']['tooltip_day_cost'] = ''; |
| 724 | 792 | $availability_per_day[ $my_day_tag ]['summary']['tooltip_booking_details'] = ''; |
| @@ -891,11 +959,20 @@ | ||
| 891 | 959 | |
| 892 | 960 | // ===================================================================================================== |
| 893 | 961 | // Change over days = |
| 894 | 962 | // ===================================================================================================== |
| 963 | + $is_booking_used_check_in_out_time = null; | |
| 964 | + if ( function_exists( 'wpbc_settings_calendar__preview_is_changeover_enabled' ) ) { | |
| 965 | + $is_booking_used_check_in_out_time = wpbc_settings_calendar__preview_is_changeover_enabled( $resource_id, $params['request_uri'] ); | |
| 966 | + } | |
| 967 | + if ( null === $is_booking_used_check_in_out_time ) { | |
| 968 | + $is_booking_used_check_in_out_time = ( | |
| 969 | + ( function_exists( 'wpbc_is_booking_used_check_in_out_time' ) ) | |
| 970 | + && ( wpbc_is_booking_used_check_in_out_time( $params['request_uri'], $resource_id ) ) | |
| 971 | + ); | |
| 972 | + } | |
| 895 | 973 | if ( |
| 896 | - ( function_exists( 'wpbc_is_booking_used_check_in_out_time' ) ) | |
| 897 | - && ( wpbc_is_booking_used_check_in_out_time( $params[ 'request_uri' ] ) ) | |
| 974 | + $is_booking_used_check_in_out_time | |
| 898 | 975 | && ( ! $availability_per_day[ $my_day_tag ][ $resource_id ]->is_day_unavailable ) // And this date still free false |
| 899 | 976 | ) { |
| 900 | 977 | |
| 901 | 978 | /** |
| @@ -980,9 +1057,48 @@ | ||
| 980 | 1057 | } |
| 981 | 1058 | // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 982 | 1059 | // B O O K I N G S - E n d |
| 983 | 1060 | // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 1061 | + // Working Time applies only to time-based booking forms: rangetime, start/end, or start/duration. | |
| 1062 | + $working_time__timeslots_to_check_intersect = $as_seconds_timeslots_arr__to_check_intersect; | |
| 1063 | + if ( empty( $working_time__timeslots_to_check_intersect ) && ! empty( $params['timeslots_to_check_intersect'] ) ) { | |
| 1064 | + if ( ! is_array( $params['timeslots_to_check_intersect'] ) ) { | |
| 1065 | + $params['timeslots_to_check_intersect'] = array( $params['timeslots_to_check_intersect'] ); | |
| 1066 | + } | |
| 1067 | + $working_time__timeslots_to_check_intersect = array(); | |
| 1068 | + foreach ( $params['timeslots_to_check_intersect'] as $time_slot_readable ) { | |
| 1069 | + $working_time__timeslots_to_check_intersect[] = wpbc_convert__readable_time_slot__to__time_range_in_seconds( $time_slot_readable ); | |
| 1070 | + } | |
| 1071 | + } | |
| 984 | 1072 | |
| 1073 | + if ( | |
| 1074 | + ! empty( $working_time__timeslots_to_check_intersect ) | |
| 1075 | + && function_exists( 'wpbc_working_time__get_non_working_intervals_for_date' ) | |
| 1076 | + && function_exists( 'wpbc_availability_timeslots__apply_blocks_to_availability_obj' ) | |
| 1077 | + ) { | |
| 1078 | + $working_time__blocked_intervals = wpbc_working_time__get_non_working_intervals_for_date( $resource_id, $my_day_tag ); | |
| 1079 | + | |
| 1080 | + if ( ! empty( $working_time__blocked_intervals ) ) { | |
| 1081 | + $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_availability_timeslots__apply_blocks_to_availability_obj( | |
| 1082 | + $availability_per_day[ $my_day_tag ][ $resource_id ], | |
| 1083 | + $working_time__blocked_intervals, | |
| 1084 | + $working_time__timeslots_to_check_intersect | |
| 1085 | + ); | |
| 1086 | + } | |
| 1087 | + } | |
| 1088 | + | |
| 1089 | + // FixIn: 10.16.1. | |
| 1090 | + if ( | |
| 1091 | + function_exists( 'wpbc_availability_timeslots__apply_blocks_to_availability_obj' ) | |
| 1092 | + && isset( $availability_timeslots__in_dates[ $my_day_tag ][ $resource_id ] ) | |
| 1093 | + ) { | |
| 1094 | + $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_availability_timeslots__apply_blocks_to_availability_obj( | |
| 1095 | + $availability_per_day[ $my_day_tag ][ $resource_id ], | |
| 1096 | + $availability_timeslots__in_dates[ $my_day_tag ][ $resource_id ], | |
| 1097 | + $as_seconds_timeslots_arr__to_check_intersect | |
| 1098 | + ); | |
| 1099 | + } | |
| 1100 | + | |
| 985 | 1101 | //========================================================================================================== |
| 986 | 1102 | // $availability_per_day[..]->_day_status = 'season_filter' | ... |
| 987 | 1103 | //========================================================================================================== |
| 988 | 1104 | // Booking > Resources > Availability page >>> 'season_filter' |
| @@ -990,19 +1106,36 @@ | ||
| 990 | 1106 | |
| 991 | 1107 | // Booking > Availability page >>> 'resource_availability' |
| 992 | 1108 | $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_support_capacity__day_status__resource_availability( $availability_per_day, $my_day_tag, $resource_id, $unavailable_dates__per_resources__arr ); |
| 993 | 1109 | |
| 994 | - // Week Days unavailable >>> 'weekday_unavailable' | |
| 995 | - $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_support_capacity__day_status__weekday_unavailable( $availability_per_day, $my_day_tag, $resource_id ); | |
| 1110 | + if ( empty( $params['skip_general_availability'] ) ) { | |
| 1111 | + // Week Days unavailable >>> 'weekday_unavailable' | |
| 1112 | + $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_support_capacity__day_status__weekday_unavailable( $availability_per_day, $my_day_tag, $resource_id ); | |
| 1113 | + } | |
| 996 | 1114 | |
| 997 | - if ( ( ! $is_this_bap_page ) || ( ! $is_this_hash_page ) ) { | |
| 1115 | + // if ( ( ! $is_this_bap_page ) || ( ! $is_this_hash_page ) ) { | |
| 1116 | + if ( empty( $params['skip_general_availability'] ) && ( ! $is_this_bap_page ) && ( ! $is_this_hash_page ) ) { | |
| 998 | 1117 | // Do not apply these settings at Booking > Add booking page, when we edit booking - e.g. exist 'booking_hash' |
| 999 | 1118 | |
| 1000 | - // Unavailable days from today >>> 'from_today_unavailable' | |
| 1001 | - $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_support_capacity__day_status__from_today_unavailable( $availability_per_day, $my_day_tag, $resource_id ); | |
| 1119 | + // Skip checking unavailable from today, if defined the calendar_dates_start='2025-02-10' calendar_dates_end='2025-12-31' in shortcode. // FixIn: 10.13.1.4. | |
| 1120 | + $is_skip_check__from_today_unavailable = false; | |
| 1121 | + if ( is_array( $params['dates_to_check'] ) && empty( $params['force_check_from_today_unavailable'] ) ) { | |
| 1122 | + if ( ( ( count( $params['dates_to_check'] ) ) > 0 ) && ( wpbc_is_date_less_than( $params['dates_to_check'][0], $my_day_tag ) ) ) { | |
| 1123 | + $is_skip_check__from_today_unavailable = true; | |
| 1124 | + } | |
| 1125 | + if ( ( ( count( $params['dates_to_check'] ) ) > 1 ) && ( wpbc_is_date_higher_than( $params['dates_to_check'][1], $my_day_tag ) ) ) { | |
| 1126 | + $is_skip_check__from_today_unavailable = true; | |
| 1127 | + } | |
| 1128 | + } | |
| 1002 | 1129 | |
| 1003 | - // Limit available days from today >>> 'limit_available_from_today' | |
| 1004 | - $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_support_capacity__day_status__limit_available_from_today( $availability_per_day, $my_day_tag, $resource_id ); | |
| 1130 | + if ( ! $is_skip_check__from_today_unavailable ) { | |
| 1131 | + | |
| 1132 | + // Unavailable days from today >>> 'from_today_unavailable'. | |
| 1133 | + $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_support_capacity__day_status__from_today_unavailable( $availability_per_day, $my_day_tag, $resource_id ); | |
| 1134 | + | |
| 1135 | + // Limit available days from today >>> 'limit_available_from_today'. | |
| 1136 | + $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_support_capacity__day_status__limit_available_from_today( $availability_per_day, $my_day_tag, $resource_id ); | |
| 1137 | + } | |
| 1005 | 1138 | } |
| 1006 | 1139 | //========================================================================================================== |
| 1007 | 1140 | |
| 1008 | 1141 | |
| @@ -1022,21 +1155,24 @@ | ||
| 1022 | 1155 | // ========================================================================================================= |
| 1023 | 1156 | // Entire D A Y statuses |
| 1024 | 1157 | // ========================================================================================================= |
| 1025 | 1158 | |
| 1026 | - if ( $availability_per_day[ $my_day_tag ][ $resource_id ]->is_day_unavailable ) { | |
| 1027 | - // Reduce DAY day_availability, if some season or days unavailable or full by bookings | |
| 1028 | - $availability_per_day[ $my_day_tag ][ 'day_availability' ]--; | |
| 1159 | + if ( $availability_per_day[ $my_day_tag ][ $resource_id ]->is_day_unavailable ) { | |
| 1160 | + // Set merged time interval as fully unavailable. | |
| 1161 | + $availability_per_day[ $my_day_tag ][ $resource_id ]->booked_time_slots['merged_seconds'] = array( array( 0, 86400 ) ); // FixIn: 10.11.5.1. | |
| 1162 | + // Reduce DAY day_availability, if some season or days unavailable or full by bookings. | |
| 1163 | + --$availability_per_day[ $my_day_tag ]['day_availability']; | |
| 1029 | 1164 | } |
| 1030 | 1165 | |
| 1031 | - // 'resource_availability' | 'weekday_unavailable' | 'from_today_unavailable' | 'limit_available_from_today' | 'season_filter' | 'full_day_booking' | 'time_slots_booking' | 'available' | |
| 1166 | + // 'resource_availability' | 'weekday_unavailable' | 'from_today_unavailable' | 'limit_available_from_today' | 'season_filter' | 'full_day_booking' | 'time_slots_booking' | 'available'. | |
| 1032 | 1167 | $availability_per_day[ $my_day_tag ]['statuses']['day_status'][] = $availability_per_day[ $my_day_tag ][ $resource_id ]->_day_status; |
| 1033 | 1168 | |
| 1034 | - // 'approved' | 'pending' | |
| 1169 | + // 'approved' | 'pending'. | |
| 1035 | 1170 | $availability_per_day[ $my_day_tag ]['statuses']['bookings_status'][] = implode( '|', $availability_per_day[ $my_day_tag ][ $resource_id ]->pending_approved ); |
| 1036 | 1171 | |
| 1037 | - // Go to next child booking resource | |
| 1172 | + // Go to next child booking resource. | |
| 1038 | 1173 | } |
| 1174 | + | |
| 1039 | 1175 | // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 1040 | 1176 | // R E S O U R C E S - E n d |
| 1041 | 1177 | // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 1042 | 1178 | |
| @@ -1121,8 +1257,9 @@ | ||
| 1121 | 1257 | $temp_status[] = $bookings_status_element; |
| 1122 | 1258 | } |
| 1123 | 1259 | } |
| 1124 | 1260 | $temp_status = array_filter( $temp_status ); // All entries of array equal to FALSE (0, '', '0' ) will be removed. |
| 1261 | + $temp_status_counts = array_count_values( $temp_status ); | |
| 1125 | 1262 | $temp_status = array_unique( $temp_status ); // Erase duplicates |
| 1126 | 1263 | $availability_per_this_day['summary']['status_for_day'] = $temp_status; |
| 1127 | 1264 | |
| 1128 | 1265 | /** |
| @@ -1147,10 +1284,34 @@ | ||
| 1147 | 1284 | // Priority: available > time_slots_booking > full_day_booking |
| 1148 | 1285 | // > season_filter > resource_availability |
| 1149 | 1286 | // > weekday_unavailable > from_today_unavailable > limit_available_from_today |
| 1150 | 1287 | |
| 1151 | - // if at least one booking "available" then day has this status | |
| 1288 | + // Capacity resources can be available even when every child has a check-in/out or full-day status. | |
| 1289 | + $max_capacity = isset( $availability_per_this_day['max_capacity'] ) ? intval( $availability_per_this_day['max_capacity'] ) : 1; | |
| 1290 | + $blocking_statuses = array( | |
| 1291 | + 'full_day_booking', | |
| 1292 | + 'season_filter', | |
| 1293 | + 'resource_availability', | |
| 1294 | + 'weekday_unavailable', | |
| 1295 | + 'from_today_unavailable', | |
| 1296 | + 'limit_available_from_today', | |
| 1297 | + 'change_over', | |
| 1298 | + ); | |
| 1299 | + $blocked_capacity = 0; | |
| 1300 | + foreach ( $blocking_statuses as $blocking_status ) { | |
| 1301 | + $blocked_capacity += isset( $temp_status_counts[ $blocking_status ] ) ? intval( $temp_status_counts[ $blocking_status ] ) : 0; | |
| 1302 | + } | |
| 1303 | + $booked_capacity__before_check_in = $blocked_capacity + ( isset( $temp_status_counts['check_out'] ) ? intval( $temp_status_counts['check_out'] ) : 0 ); | |
| 1304 | + $booked_capacity__after_check_in = $blocked_capacity + ( isset( $temp_status_counts['check_in'] ) ? intval( $temp_status_counts['check_in'] ) : 0 ); | |
| 1305 | + $is_capacity_available_during_change_over = ( | |
| 1306 | + ( $max_capacity > 1 ) | |
| 1307 | + && ( $max_capacity > max( $booked_capacity__before_check_in, $booked_capacity__after_check_in ) ) | |
| 1308 | + && ( ! in_array( 'time_slots_booking', $availability_per_this_day['summary']['status_for_day'] ) ) | |
| 1309 | + ); | |
| 1310 | + | |
| 1311 | + // if at least one resource is available then day has this status | |
| 1152 | 1312 | if ( in_array( 'available', $availability_per_this_day['summary']['status_for_day'] ) ) { $availability_per_this_day['summary']['status_for_day'] = 'available'; |
| 1313 | + } else if ( $is_capacity_available_during_change_over ) { $availability_per_this_day['summary']['status_for_day'] = 'available'; | |
| 1153 | 1314 | } else if ( in_array( 'time_slots_booking', $availability_per_this_day['summary']['status_for_day'] ) ) { $availability_per_this_day['summary']['status_for_day'] = 'time_slots_booking'; |
| 1154 | 1315 | } else if ( ( ! in_array( 'check_out', $availability_per_this_day['summary']['status_for_day'] ) ) |
| 1155 | 1316 | && ( in_array( 'check_in', $availability_per_this_day['summary']['status_for_day'] ) ) ) { $availability_per_this_day['summary']['status_for_day'] = 'check_in'; |
| 1156 | 1317 | } else if ( ( ! in_array( 'check_in', $availability_per_this_day['summary']['status_for_day'] ) ) |
| @@ -1176,19 +1337,21 @@ | ||
| 1176 | 1337 | $temp_status[] = $bookings_status_element; |
| 1177 | 1338 | } |
| 1178 | 1339 | } |
| 1179 | 1340 | $temp_status = array_filter( $temp_status ); // All entries of array equal to FALSE (0, '', '0' ) will be removed. |
| 1180 | - $temp_status = array_unique( $temp_status ); | |
| 1181 | - $availability_per_this_day['summary']['status_for_bookings'] = $temp_status; | |
| 1341 | + $temp_status = array_values( array_unique( $temp_status ) ); | |
| 1182 | 1342 | |
| 1343 | + // Structured booking statuses contract. Keep numeric keys sequential so JSON encodes this as a JS array. | |
| 1344 | + $availability_per_this_day['summary']['status_for_bookings_arr'] = $temp_status; | |
| 1345 | + | |
| 1183 | 1346 | // Priority: pending > approved > '' |
| 1184 | 1347 | |
| 1185 | 1348 | // if at least one booking pending then day is pending |
| 1186 | - if ( in_array( 'pending', $availability_per_this_day['summary']['status_for_bookings'] ) ) { $availability_per_this_day['summary']['status_for_bookings'] = 'pending'; | |
| 1187 | - } else if ( in_array( 'pending_pending', $availability_per_this_day['summary']['status_for_bookings'] ) ) { $availability_per_this_day['summary']['status_for_bookings'] = 'pending_pending'; | |
| 1349 | + if ( in_array( 'pending', $temp_status ) ) { $availability_per_this_day['summary']['status_for_bookings'] = 'pending'; | |
| 1350 | + } else if ( in_array( 'pending_pending', $temp_status ) ) { $availability_per_this_day['summary']['status_for_bookings'] = 'pending_pending'; | |
| 1188 | 1351 | // Usual situations for change over days: pending_pending | pending_approved | approved_pending | approved_approved |
| 1189 | 1352 | } else { |
| 1190 | - $availability_per_this_day['summary']['status_for_bookings'] = implode( ' ', $availability_per_this_day['summary']['status_for_bookings'] ); | |
| 1353 | + $availability_per_this_day['summary']['status_for_bookings'] = implode( ' ', $temp_status ); | |
| 1191 | 1354 | } |
| 1192 | 1355 | |
| 1193 | 1356 | |
| 1194 | 1357 | // ------------------------------------------------------------------------------------------------------------- |
| @@ -1281,28 +1444,32 @@ | ||
| 1281 | 1444 | $tooltip_title_word = ( empty( $tooltip_title_word ) ) |
| 1282 | 1445 | ? '' |
| 1283 | 1446 | : '<div class="wpbc_tooltip_title">' . $tooltip_title_word . '</div>' . ' '; |
| 1284 | 1447 | |
| 1285 | - $id_of_first_resource = $resource_id_arr[0]; | |
| 1286 | - $tooltip_content_header = ''; | |
| 1448 | + if ( isset( $resource_id_arr[0] ) ) { | |
| 1287 | 1449 | |
| 1288 | - if ( ! empty( $availability_per_this_day[ $id_of_first_resource ]->date_cost_rate ) ) { | |
| 1450 | + $id_of_first_resource = $resource_id_arr[0]; | |
| 1451 | + $tooltip_content_header = ''; | |
| 1289 | 1452 | |
| 1290 | - $cur_sym = get_bk_option( 'booking_cost_in_date_cell_currency' ); // in Booking > Settings General page in "Calendar" section | |
| 1291 | - //$cur_sym = wpbc_get_currency_symbol(); // in Booking > Settings > Payment page | |
| 1453 | + if ( ! empty( $availability_per_this_day[ $id_of_first_resource ]->date_cost_rate ) ) { | |
| 1292 | 1454 | |
| 1293 | - $cost_text = wpbc_formate_cost_hint__no_html( $availability_per_this_day[ $id_of_first_resource ]->date_cost_rate, $cur_sym ); | |
| 1294 | - $cost_text = html_entity_decode( $cost_text ); | |
| 1455 | + $cur_sym = get_bk_option( 'booking_cost_in_date_cell_currency' ); // in Booking > Settings General page in "Calendar" section | |
| 1456 | + //$cur_sym = wpbc_get_currency_symbol(); // in Booking > Settings > Payment page | |
| 1295 | 1457 | |
| 1296 | - $tooltip = '<div class="wpbc_tooltip_section tooltip__day_cost">' | |
| 1297 | - . $tooltip_title_word | |
| 1298 | - . '<div class="wpbc_tooltip_resource_container">' | |
| 1299 | - . $tooltip_content_header | |
| 1300 | - . '<div class="wpbc_tooltip_item">' | |
| 1301 | - . $cost_text | |
| 1458 | + $cost_text = wpbc_formate_cost_hint__no_html( $availability_per_this_day[ $id_of_first_resource ]->date_cost_rate, $cur_sym ); | |
| 1459 | + $cost_text = html_entity_decode( $cost_text ); | |
| 1460 | + | |
| 1461 | + $tooltip = '<div class="wpbc_tooltip_section tooltip__day_cost">' | |
| 1462 | + . $tooltip_title_word | |
| 1463 | + . '<div class="wpbc_tooltip_resource_container">' | |
| 1464 | + . $tooltip_content_header | |
| 1465 | + . '<div class="wpbc_tooltip_item">' | |
| 1466 | + . $cost_text | |
| 1467 | + . '</div>' | |
| 1302 | 1468 | . '</div>' |
| 1303 | - . '</div>' | |
| 1304 | - . '</div>'; | |
| 1469 | + . '</div>'; | |
| 1470 | + } | |
| 1471 | + | |
| 1305 | 1472 | } |
| 1306 | 1473 | } |
| 1307 | 1474 | return $tooltip; |
| 1308 | 1475 | } |
| @@ -1861,9 +2028,9 @@ | ||
| 1861 | 2028 | // all_selected_dates_arr Can be: [ "2023-10-05", "2023-10-06", "2023-10-07", … ] |
| 1862 | 2029 | // PHP actions: |
| 1863 | 2030 | // from: make_bk_action('wpdev_booking_post_inserted', $booking_id, $bktype, $str_dates__dd_mm_yyyy, array($start_time, $end_time ) , $formdata ); |
| 1864 | 2031 | // removed! |
| 1865 | -// . | |
| 2032 | +// . // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound | |
| 1866 | 2033 | // from: apply_filters('wpdev_booking_form_content', $form , $resource_id); |
| 1867 | 2034 | // to: apply_filters( 'wpbc_booking_form_html__update__append_change_over_times', $form, $resource_id ); |
| 1868 | 2035 | // . |
| 1869 | 2036 | // Removed PHP actions: |
| @@ -1877,5 +2044,5 @@ | ||
| 1877 | 2044 | // * // , 'wpdev_active_locale' => 'en_US' |
| 1878 | 2045 | // * ) ); |
| 1879 | 2046 | // . |
| 1880 | 2047 | // add_bk_filter('wpbc_add_new_booking_filter' , 'wpbc_add_new_booking' ); |
| 1881 | -// TODO: recheck hooks in wpbc-dev-api.php | |
| 2048 | +// TODO: recheck hooks in wpbc-dev-api.php | |