PluginProbe
Booking Calendar / 11.8.3
Booking Calendar v11.8.3
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 +249 -74 10.10.111.8.3 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
@@ -497,30 +537,41 @@
497 537 )
498 538 */
499 539 function wpbc_get_availability_per_days_arr( $params ) {
500 540
501 - if ( 0 ) { //FixIn: 9.8.3.1 ?
541 + if ( 0 ) { // FixIn: 9.8.3.1 ?
502 542 wpbc_set_limit_php( 300 ); // Set 300 seconds for php execution.
503 543 }
504 544
505 - $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 */
545 + $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 */
506 546 $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 */
507 - $defaults = array(
508 - 'dates_to_check' => 'CURDATE' // 'CURDATE' | 'ALL' | [ '2023-07-15' , '2023-07-21' ]
509 - , 'approved' => ( ( get_bk_option( 'booking_is_show_pending_days_as_available' ) == 'On' ) ? '1' : 'all' ) // 'all' | 0 | 1
510 - , 'resource_id' => '1' // Single or parent booking resource!!!! // arrays | CSD | int ???
511 - , 'additional_bk_types' => array() // arrays | CSD | int // OPTIONAL -> aggregate_resource_id_arr ... it is array of booking resources from aggregate shortcode parameter
512 - , 'skip_booking_id' => wpbc_get_booking_id__from_hash_in_url() // int | '' // CSD '3,5'
513 - , 'as_single_resource' => false // get dates as for 'single resource' or 'parent' resource including bookings in all 'child booking resources'
514 - , 'max_days_count' => wpbc_get_max_visible_days_in_calendar() // 365
515 - , 'timeslots_to_check_intersect' => array() // array( '12:20 - 12:55', '13:00 - 14:00' ) //TODO: ? do we really need it, because below we get it from function
516 - , '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
517 - , 'custom_form' => '' // Required for checking all available time-slots and compare with booked time slots
518 - );
519 - $params = wp_parse_args( $params, $defaults );
520 547
521 - // FixIn: 10.7.1.2.
522 - if ( false !== strpos( $params['request_uri'], 'allow_past' ) ) {
548 + $defaults = array(
549 + 'dates_to_check' => 'CURDATE', // v: 'CURDATE' | 'ALL' | arr: '2023-07-15' , '2023-07-21'.
550 + 'approved' => ( ( 'On' === get_bk_option( 'booking_is_show_pending_days_as_available' ) ) ? '1' : 'all' ), // v: 'all' | 0 | 1.
551 + 'resource_id' => '1', // Single or parent booking resource!!!! // arrays | CSD | int ???.
552 + 'additional_bk_types' => array(), // arrays | CSD | int // OPTIONAL -> aggregate_resource_id_arr ... it is array of booking resources from aggregate shortcode parameter.
553 + 'skip_booking_id' => wpbc_get_booking_id__from_hash_in_url(), // int | '' // CSD '3,5'.
554 + 'as_single_resource' => false, // get dates as for 'single resource' or 'parent' resource including bookings in all 'child booking resources'.
555 + 'max_days_count' => wpbc_get_max_visible_days_in_calendar(), // 365.
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.
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,
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.
561 + );
562 +
563 + $params = wp_parse_args( $params, $defaults );
564 +
565 + // FixIn: 10.7.1.2. //FixIn: 10.10.3.2.
566 + if (
567 + ( ! empty( $params['allow_past'] ) ) ||
568 + ( false !== strpos( $params['request_uri'], 'allow_past' ) ) ||
569 + (
570 + wpbc_is_new_booking_page_url( $params['request_uri'] ) &&
571 + ( false !== strpos( $params['request_uri'], 'booking_hash' ) )
572 + )
573 + ) {
523 574 $params['dates_to_check'] = 'ALL';
524 575 }
525 576
526 577 // -----------------------------------------------------------------------------------------------------------------
@@ -572,10 +623,15 @@
572 623 // -----------------------------------------------------------------------------------------------------------------
573 624 // Main function to get "Bookings": ['2023-08-30'][ > resource_id < ][ > time_seconds_range < ] => booking_date_obj[ booking_id:45, approved:1, ...
574 625 $bookings__in_dates = wpbc_get__booked_dates__per_resources__arr( $params );
575 626
627 + // -----------------------------------------------------------------------------------------------------------------
628 + // Time slots availability blocks.
629 + // -----------------------------------------------------------------------------------------------------------------
630 + $availability_timeslots__in_dates = array(); // FixIn: 10.16.1.
576 631
577 632
633 +
578 634 // -----------------------------------------------------------------------------------------------------------------
579 635 // Booking > Availability page
580 636 // -----------------------------------------------------------------------------------------------------------------
581 637 $search_dates = $params['dates_to_check']; // 'CURDATE' | 'ALL' | [ "2023-08-03" , "2023-08-04" , "2023-08-05" ]
@@ -594,9 +650,28 @@
594 650 }
595 651 $unavailable_dates__per_resources__arr = wpbc_aggregate_merge_availability( $unavailable_dates__per_resources__arr,
596 652 $resource_id__with_children__arr,
597 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 + );
598 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 +
599 674 // -----------------------------------------------------------------------------------------------------------------
600 675 // Booking > Resources > Availability page
601 676 // -----------------------------------------------------------------------------------------------------------------
602 677 $season_filters_availability = array();
@@ -613,14 +688,14 @@
613 688 // P a r a m e t e r s f o r L O O P
614 689 // -----------------------------------------------------------------------------------------------------------------
615 690
616 691 // - Is this Booking > Add booking page ? --------------------------------------------------------------------------
617 - $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'] );
618 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
619 694
620 695 // FixIn: 10.7.1.2.
621 696 if ( ! $is_this_hash_page ) {
622 - $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;
623 698 }
624 699
625 700 if ( ( $is_this_bap_page ) && ( $is_this_hash_page ) ) { // Start days in calendar from = PAST
626 701 $params['dates_to_check'] = 'ALL';
@@ -635,9 +710,9 @@
635 710 $start_date_unix = strtotime( '-' . intval( $params['max_days_count'] ) . ' days' ); // - 365 days
636 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' );
637 712 $today_inix_timestamp = strtotime( $date_with_wp_timezone );
638 713
639 - } else if ( 'CURDATE' == $params['dates_to_check'] ) { // Current dates -> CURDATE()
714 + } else if ( 'CURDATE' == $params['dates_to_check'] ) { // Current dates -> CURDATE
640 715
641 716 $start_day_number = 1;
642 717 // $today_inix_timestamp = strtotime( gmdate( 'Y-m-d 00:00:00', strtotime( 'now' ) ) ); // Today 00:00:00
643 718
@@ -649,12 +724,12 @@
649 724 } else if ( is_array( $params['dates_to_check'] ) ) {
650 725
651 726 $start_day_number = 1;
652 727
653 - $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'
654 729
655 - $dif_in_days = strtotime( $params['dates_to_check'][ ( count( $params['dates_to_check'] ) - 1 ) ] . ' 00:00:00' )
656 - - 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' );
657 732
658 733 $dif_in_days = $dif_in_days / ( 24 * 60 * 60 );
659 734 $params['max_days_count'] = ceil( $dif_in_days ) + 1;
660 735 }
@@ -709,8 +784,9 @@
709 784 $availability_per_day[ $my_day_tag ]['statuses']['bookings_status'] = array(); // per each child booking resource
710 785 $availability_per_day[ $my_day_tag ]['summary'] = array();
711 786 $availability_per_day[ $my_day_tag ]['summary']['status_for_day'] = ''; // available | time_slots_booking | full_day_booking | ...
712 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' ).
713 789 $availability_per_day[ $my_day_tag ]['summary']['tooltip_times'] = '';
714 790 $availability_per_day[ $my_day_tag ]['summary']['tooltip_availability'] = '';
715 791 $availability_per_day[ $my_day_tag ]['summary']['tooltip_day_cost'] = '';
716 792 $availability_per_day[ $my_day_tag ]['summary']['tooltip_booking_details'] = '';
@@ -883,11 +959,20 @@
883 959
884 960 // =====================================================================================================
885 961 // Change over days =
886 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 + }
887 973 if (
888 - ( function_exists( 'wpbc_is_booking_used_check_in_out_time' ) )
889 - && ( wpbc_is_booking_used_check_in_out_time( $params[ 'request_uri' ] ) )
974 + $is_booking_used_check_in_out_time
890 975 && ( ! $availability_per_day[ $my_day_tag ][ $resource_id ]->is_day_unavailable ) // And this date still free false
891 976 ) {
892 977
893 978 /**
@@ -972,9 +1057,48 @@
972 1057 }
973 1058 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
974 1059 // B O O K I N G S - E n d
975 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 + }
976 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 +
977 1101 //==========================================================================================================
978 1102 // $availability_per_day[..]->_day_status = 'season_filter' | ...
979 1103 //==========================================================================================================
980 1104 // Booking > Resources > Availability page >>> 'season_filter'
@@ -982,19 +1106,36 @@
982 1106
983 1107 // Booking > Availability page >>> 'resource_availability'
984 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 );
985 1109
986 - // Week Days unavailable >>> 'weekday_unavailable'
987 - $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 + }
988 1114
989 - 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 ) ) {
990 1117 // Do not apply these settings at Booking > Add booking page, when we edit booking - e.g. exist 'booking_hash'
991 1118
992 - // Unavailable days from today >>> 'from_today_unavailable'
993 - $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 + }
994 1129
995 - // Limit available days from today >>> 'limit_available_from_today'
996 - $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 + }
997 1138 }
998 1139 //==========================================================================================================
999 1140
1000 1141
@@ -1014,21 +1155,24 @@
1014 1155 // =========================================================================================================
1015 1156 // Entire D A Y statuses
1016 1157 // =========================================================================================================
1017 1158
1018 - if ( $availability_per_day[ $my_day_tag ][ $resource_id ]->is_day_unavailable ) {
1019 - // Reduce DAY day_availability, if some season or days unavailable or full by bookings
1020 - $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'];
1021 1164 }
1022 1165
1023 - // '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'.
1024 1167 $availability_per_day[ $my_day_tag ]['statuses']['day_status'][] = $availability_per_day[ $my_day_tag ][ $resource_id ]->_day_status;
1025 1168
1026 - // 'approved' | 'pending'
1169 + // 'approved' | 'pending'.
1027 1170 $availability_per_day[ $my_day_tag ]['statuses']['bookings_status'][] = implode( '|', $availability_per_day[ $my_day_tag ][ $resource_id ]->pending_approved );
1028 1171
1029 - // Go to next child booking resource
1172 + // Go to next child booking resource.
1030 1173 }
1174 +
1031 1175 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1032 1176 // R E S O U R C E S - E n d
1033 1177 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1034 1178
@@ -1113,8 +1257,9 @@
1113 1257 $temp_status[] = $bookings_status_element;
1114 1258 }
1115 1259 }
1116 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 );
1117 1262 $temp_status = array_unique( $temp_status ); // Erase duplicates
1118 1263 $availability_per_this_day['summary']['status_for_day'] = $temp_status;
1119 1264
1120 1265 /**
@@ -1139,10 +1284,34 @@
1139 1284 // Priority: available > time_slots_booking > full_day_booking
1140 1285 // > season_filter > resource_availability
1141 1286 // > weekday_unavailable > from_today_unavailable > limit_available_from_today
1142 1287
1143 - // 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
1144 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';
1145 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';
1146 1315 } else if ( ( ! in_array( 'check_out', $availability_per_this_day['summary']['status_for_day'] ) )
1147 1316 && ( in_array( 'check_in', $availability_per_this_day['summary']['status_for_day'] ) ) ) { $availability_per_this_day['summary']['status_for_day'] = 'check_in';
1148 1317 } else if ( ( ! in_array( 'check_in', $availability_per_this_day['summary']['status_for_day'] ) )
@@ -1168,19 +1337,21 @@
1168 1337 $temp_status[] = $bookings_status_element;
1169 1338 }
1170 1339 }
1171 1340 $temp_status = array_filter( $temp_status ); // All entries of array equal to FALSE (0, '', '0' ) will be removed.
1172 - $temp_status = array_unique( $temp_status );
1173 - $availability_per_this_day['summary']['status_for_bookings'] = $temp_status;
1341 + $temp_status = array_values( array_unique( $temp_status ) );
1174 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 +
1175 1346 // Priority: pending > approved > ''
1176 1347
1177 1348 // if at least one booking pending then day is pending
1178 - if ( in_array( 'pending', $availability_per_this_day['summary']['status_for_bookings'] ) ) { $availability_per_this_day['summary']['status_for_bookings'] = 'pending';
1179 - } 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';
1180 1351 // Usual situations for change over days: pending_pending | pending_approved | approved_pending | approved_approved
1181 1352 } else {
1182 - $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 );
1183 1354 }
1184 1355
1185 1356
1186 1357 // -------------------------------------------------------------------------------------------------------------
@@ -1273,28 +1444,32 @@
1273 1444 $tooltip_title_word = ( empty( $tooltip_title_word ) )
1274 1445 ? ''
1275 1446 : '<div class="wpbc_tooltip_title">' . $tooltip_title_word . '</div>' . ' ';
1276 1447
1277 - $id_of_first_resource = $resource_id_arr[0];
1278 - $tooltip_content_header = '';
1448 + if ( isset( $resource_id_arr[0] ) ) {
1279 1449
1280 - 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 = '';
1281 1452
1282 - $cur_sym = get_bk_option( 'booking_cost_in_date_cell_currency' ); // in Booking > Settings General page in "Calendar" section
1283 - //$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 ) ) {
1284 1454
1285 - $cost_text = wpbc_formate_cost_hint__no_html( $availability_per_this_day[ $id_of_first_resource ]->date_cost_rate, $cur_sym );
1286 - $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
1287 1457
1288 - $tooltip = '<div class="wpbc_tooltip_section tooltip__day_cost">'
1289 - . $tooltip_title_word
1290 - . '<div class="wpbc_tooltip_resource_container">'
1291 - . $tooltip_content_header
1292 - . '<div class="wpbc_tooltip_item">'
1293 - . $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>'
1294 1468 . '</div>'
1295 - . '</div>'
1296 - . '</div>';
1469 + . '</div>';
1470 + }
1471 +
1297 1472 }
1298 1473 }
1299 1474 return $tooltip;
1300 1475 }
@@ -1853,9 +2028,9 @@
1853 2028 // all_selected_dates_arr Can be: [ "2023-10-05", "2023-10-06", "2023-10-07", … ]
1854 2029 // PHP actions:
1855 2030 // from: make_bk_action('wpdev_booking_post_inserted', $booking_id, $bktype, $str_dates__dd_mm_yyyy, array($start_time, $end_time ) , $formdata );
1856 2031 // removed!
1857 -// .
2032 +// . // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
1858 2033 // from: apply_filters('wpdev_booking_form_content', $form , $resource_id);
1859 2034 // to: apply_filters( 'wpbc_booking_form_html__update__append_change_over_times', $form, $resource_id );
1860 2035 // .
1861 2036 // Removed PHP actions:
@@ -1869,5 +2044,5 @@
1869 2044 // * // , 'wpdev_active_locale' => 'en_US'
1870 2045 // * ) );
1871 2046 // .
1872 2047 // add_bk_filter('wpbc_add_new_booking_filter' , 'wpbc_add_new_booking' );
1873 -// TODO: recheck hooks in wpbc-dev-api.php
2048 +// TODO: recheck hooks in wpbc-dev-api.php