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 | core/wpbc-dates.php +129 -30 10.1011.8.2 View file →
@@ -1,4 +1,4 @@
1 1 <?php
2 2 /**
3 3 * @version 1.0
4 4 * @package Booking Calendar
@@ -135,10 +135,40 @@
135 135 return $time_m_h . ':' . $time_m_m;
136 136 }
137 137
138 138
139 -/**
140 - * Get Times from booking Form, if these times fields exist
139 +/**
140 + * Parse a duration field value into numeric hours and minutes.
141 + *
142 + * Duration values come from serialized front-end form data. Placeholder or
143 + * otherwise malformed values must not reach arithmetic because PHP 8 throws
144 + * a TypeError when a non-numeric string is multiplied by an integer.
145 + *
146 + * @param string $duration_time_value Duration in `HH:MM` format.
147 + *
148 + * @return array|false Numeric hours and minutes, or false for an invalid duration.
149 + */
150 +function wpbc_parse_duration_time_value( $duration_time_value ) {
151 +
152 + $duration_time_value = trim( (string) $duration_time_value );
153 +
154 + if ( ! preg_match( '/^([0-9]+):([0-9]{1,2})$/', $duration_time_value, $duration_time_matches ) ) {
155 + return false;
156 + }
157 +
158 + $duration_hours = intval( $duration_time_matches[1] );
159 + $duration_minutes = intval( $duration_time_matches[2] );
160 +
161 + if ( 59 < $duration_minutes ) {
162 + return false;
163 + }
164 +
165 + return array( $duration_hours, $duration_minutes );
166 +}
167 +
168 +
169 +/**
170 + * Get Times from booking Form, if these times fields exist
141 171 *
142 172 * @param type $booking_form_data
143 173 * @param type $booking_type
144 174 * @return mixed
@@ -233,34 +263,36 @@
233 263 } else {
234 264 $end_time = explode( ':', $end_time );
235 265 }
236 266
237 - if ( strpos( $booking_form_data, 'durationtime' . $booking_type ) !== false ) { // Get END TIME From form request
238 - $pos1 = strpos( $booking_form_data, 'durationtime' . $booking_type ); // Find start time pos
239 - $pos1 = strpos( $booking_form_data, '^', $pos1 ) + 1; // Find TIME pos
240 - $pos2 = strpos( $booking_form_data, '~', $pos1 ); // Find TIME length
267 + if ( strpos( $booking_form_data, 'durationtime' . $booking_type ) !== false ) { // Get END TIME From form request
268 + $pos1 = strpos( $booking_form_data, 'durationtime' . $booking_type ); // Find start time pos
269 + $pos1 = strpos( $booking_form_data, '^', $pos1 ) + 1; // Find TIME pos
270 + $pos2 = strpos( $booking_form_data, '~', $pos1 ); // Find TIME length
241 271 if ( $pos2 === false ) {
242 272 $pos2 = strlen( $booking_form_data );
243 273 }
244 - $pos2 = $pos2 - $pos1;
245 - $end_time = substr( $booking_form_data, $pos1, $pos2 );
274 + $pos2 = $pos2 - $pos1;
275 + $duration_time_value = substr( $booking_form_data, $pos1, $pos2 );
276 + $duration_time_parts = wpbc_parse_duration_time_value( $duration_time_value );
277 +
278 + if ( false !== $duration_time_parts ) {
279 + $is_time_exist = true;
280 +
281 + // Get the selected start time and add the validated duration to calculate the end time.
282 + $new_end_time = mktime( intval( $start_time[0] ), intval( $start_time[1] ) );
283 + $new_end_time += $duration_time_parts[0] * 60 * 60;
284 + $new_end_time += $duration_time_parts[1] * 60;
285 + $end_time = gmdate( 'H:i', $new_end_time );
286 +
287 + if ( '00:00' === $end_time ) {
288 + $end_time = '23:59';
289 + }
290 + $end_time = explode( ':', $end_time );
291 + $end_time[2] = '02';
292 + }
293 + }
246 294
247 - $is_time_exist = true;
248 -
249 - $end_time = explode( ':', $end_time );
250 -
251 - // Here we are get start time and add duration for end time
252 - $new_end_time = mktime( intval( $start_time[0] ), intval( $start_time[1] ) );
253 - $new_end_time = $new_end_time + $end_time[0] * 60 * 60 + $end_time[1] * 60;
254 - $end_time = gmdate( 'H:i', $new_end_time );
255 -
256 - if ( $end_time == '00:00' ) {
257 - $end_time = '23:59';
258 - }
259 - $end_time = explode( ':', $end_time );
260 - $end_time[2] = '02';
261 - }
262 -
263 295 }
264 296
265 297 if ( $is_time_exist ) {
266 298 return array( $start_time, $end_time );
@@ -318,9 +350,9 @@
318 350 function wpbc_db__get_sql_dates__in_booking__as_str( $booking_id_str ) {
319 351
320 352 global $wpdb;
321 353
322 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
354 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
323 355 $dates_result = $wpdb->get_results( "SELECT DISTINCT booking_date FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id_str}) ORDER BY booking_date" );
324 356
325 357 $dates_str = array();
326 358
@@ -651,9 +683,9 @@
651 683 *
652 684 * @param string $some_day : '2015-05-29'
653 685 * @return boolean : true | false
654 686 */
655 -function wpbc_is_date_in_past( $some_day ) {
687 +function wpbc_is_date_in_past( $some_day ) {
656 688
657 689 $some_day_d = gmdate( 'm.d.Y', mysql2date( 'U', $some_day ) );
658 690 $some_array = explode( '.', $some_day_d );
659 691 $some_day = mktime( 0, 0, 0, intval($some_array[0]), ( intval($some_array[1]) + 1 ), intval($some_array[2]) );
@@ -664,12 +696,74 @@
664 696 return true;
665 697 } else {
666 698 return false;
667 699 }
700 +}
701 +
702 +
703 +/**
704 + * Check whether a booking can still be changed by a visitor.
705 + *
706 + * Visitor edit and cancellation actions must fail closed when the booking does
707 + * not exist, is in the trash, has no dates, or its final booked date has
708 + * already passed. The comparison deliberately uses wpbc_is_date_in_past() so
709 + * the rule remains compatible with the established date-based front-end edit
710 + * restriction and does not unexpectedly expire time-slot bookings mid-day.
711 + *
712 + * @param int $booking_id Booking ID resolved from a verified visitor hash.
713 + *
714 + * @return bool True when at least one booked date is current or future.
715 + */
716 +function wpbc_is_visitor_booking_action_allowed( $booking_id ) {
717 + global $wpdb;
718 +
719 + $booking_id = absint( $booking_id );
720 + if ( empty( $booking_id ) ) {
721 + return false;
722 + }
723 +
724 + $last_booking_date = $wpdb->get_var(
725 + $wpdb->prepare(
726 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
727 + "SELECT MAX(dt.booking_date)
728 + FROM {$wpdb->prefix}bookingdates AS dt
729 + INNER JOIN {$wpdb->prefix}booking AS bk ON bk.booking_id = dt.booking_id
730 + WHERE bk.booking_id = %d AND bk.trash = 0",
731 + $booking_id
732 + )
733 + ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
734 +
735 + if ( empty( $last_booking_date ) || ! is_string( $last_booking_date ) ) {
736 + return false;
737 + }
738 +
739 + return ! wpbc_is_date_in_past( $last_booking_date );
740 +}
741 +
742 +
743 +/**
744 + * Check if the booking was made without calendar ( no booking date submited.)
745 + *
746 + * @param $is_no_date
747 + * @param $date
748 + *
749 + * @return mixed|true
750 + */
751 +function wpbc_maybe_no_booking_date( $is_no_date, $date_ymd ) {
752 +
753 + if ( ( ! empty( $date_ymd ) ) && ( is_array( $date_ymd ) ) ) {
754 + $date_ymd = $date_ymd[0];
755 + }
756 + // FixIn: 2981-01-13 13 Jan 2981.
757 + if ( '2981-01-13' === gmdate( 'Y-m-d', mysql2date( 'U', $date_ymd ) ) ) {
758 + $is_no_date = true;
759 + }
760 +
761 + return $is_no_date;
668 762 }
763 +add_filter( 'wpbc_maybe_no_booking_date', 'wpbc_maybe_no_booking_date', 10, 2 );
669 764
670 765
671 -
672 766 //TODO: refactor it, by replacig date_i18n to wp_loc_date... (check depndencies of this function in other usage functions...)
673 767 /**
674 768 * Change date / time format
675 769 *
@@ -679,8 +773,13 @@
679 773 * @return array( 'DATE in custom Format', 'TIME in custom Format' )
680 774 */
681 775 function wpbc_get_date_in_correct_format( $dt, $date_format = false, $time_format = false ) {
682 776
777 + $is_no_date = apply_filters( 'wpbc_maybe_no_booking_date', false, $dt );
778 + if ( $is_no_date ) {
779 + return array( '---', '' );
780 + }
781 +
683 782 if ( $date_format === false ) $date_format = get_bk_option( 'booking_date_format');
684 783 if ( empty( $date_format ) ) $date_format = "m / d / Y, D";
685 784
686 785 if ( $time_format === false ) $time_format = get_bk_option( 'booking_time_format');
@@ -806,9 +905,9 @@
806 905
807 906 // W H E R E
808 907 $sql_where = '';
809 908 $sql_where .= ( '' != $params['approved'] ) ? " AND ( dt.approved = {$params['approved']} ) " : ''; // Approved (1) or Pending (0) or All // int
810 - $sql_where .= " AND dt.booking_date >= CURDATE() "; // Only actual bookings
909 + $sql_where .= " AND dt.booking_date >= " . wpbc_sql_date_math_expr_explicit('', 'curdate') . " "; // Only actual bookings
811 910 $sql_where .= " AND bk.trash != 1 "; // Not in Trash // int
812 911 $sql_where .= " AND bk.booking_type IN ( {$params['resource_id']} ) "; // For specific calendar (booking resource) // int
813 912 $sql_where .= ( '' != $params['skip_booking_id'] ) ? " AND dt.booking_id NOT IN ( {$params['skip_booking_id']} ) " : '' ; // Skip some bookings ? Usually, during booking edit.
814 913
@@ -821,9 +920,9 @@
821 920 [1] => stdClass Object ( [booking_date] => 2022-12-28 00:00:00, [approved] => 1, [booking_id] => 26 )
822 921 ...
823 922 */
824 923
825 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
924 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
826 925 $result_arr = $wpdb->get_results( $sql . $sql_where . $sql_order );
827 926
828 927 // P A R S E
829 928 $prior_check_out_date = false;
@@ -982,5 +1081,5 @@
982 1081
983 1082 }
984 1083
985 1084 return $days_availability;
986 - }
1085 + }