PluginProbe
Booking Calendar / 11.8.2
Booking Calendar v11.8.2
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 10.11 10.11.2 All 203 releases
← All changes | includes/_capacity/capacity.php +225 -58 10.11.311.8.2 View file →
@@ -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