PluginProbe
Booking Calendar / 11.8.3
Booking Calendar v11.8.3
11.8.4 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 All 204 releases
← All changes | includes/_capacity/create_booking.php +316 -115 11.511.8.3 View file →
@@ -28,26 +28,21 @@
28 28 // Response AJAX parameters
29 29 $ajx_data_arr = array();
30 30 $ajx_data_arr['status'] = 'ok';
31 31
32 - $admin_uri = ltrim( str_replace( get_site_url( null, '', 'admin' ), '', admin_url( 'admin.php?' ) ), '/' ); // 'wp-admin/admin.php?'
33 - $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 */
34 - // Local parameters
35 - $local_params = array();
36 - $local_params['is_from_admin_panel'] = ( false !== strpos( $server_http_referer_uri, $admin_uri ) ); // true | false
37 - $local_params['user_id'] = ( isset( $_REQUEST['wpbc_ajx_user_id'] ) ) ? intval( $_REQUEST['wpbc_ajx_user_id'] ) : wpbc_get_current_user_id(); // 1
32 + // Local parameters
33 + $local_params = array();
34 + $local_params['user_id'] = ( isset( $_REQUEST['wpbc_ajx_user_id'] ) ) ? intval( $_REQUEST['wpbc_ajx_user_id'] ) : wpbc_get_current_user_id(); // 1
38 35
39 - // Request parameters.
40 - $experimental_request_rules = array();
41 - if ( function_exists( 'wpbc_is_11_5_features_enabled' ) && wpbc_is_11_5_features_enabled() ) {
42 - $experimental_request_rules = array(
43 - 'service_id' => array( 'validate' => 'd', 'default' => 0 ),
44 - 'appointment_service_required' => array( 'validate' => 'd', 'default' => 0 ),
45 - 'appointment_context_token' => array( 'validate' => 'strong', 'default' => '' ),
46 - 'resource_selector_required' => array( 'validate' => 'd', 'default' => 0 ),
47 - 'resource_selector_context_token' => array( 'validate' => 'strong', 'default' => '' ),
48 - );
49 - }
36 + // Request parameters for the released Appointment and Resource Selector workflows.
37 + $workflow_request_rules = array(
38 + 'service_id' => array( 'validate' => 'd', 'default' => 0 ),
39 + 'appointment_service_required' => array( 'validate' => 'd', 'default' => 0 ),
40 + 'appointment_context_token' => array( 'validate' => 'strong', 'default' => '' ),
41 + 'resource_selector_required' => array( 'validate' => 'd', 'default' => 0 ),
42 + 'resource_selector_context_token' => array( 'validate' => 'strong', 'default' => '' ),
43 + 'wpbc_admin_booking_nonce' => array( 'validate' => 'strong', 'default' => '' ),
44 + );
50 45
51 46 $user_request = new WPBC_AJX__REQUEST( array( // Using this class here only for escaping variables
52 47 'db_option_name' => 'booking__wpbc_booking_create__request_params', // Not necessary, because we not save request, only sanitize it
53 48 'user_id' => $local_params['user_id'], // Not necessary, because we not save request, only sanitize it
@@ -62,18 +57,20 @@
62 57 'captcha_user_input' => array( 'validate' => 'strong', 'default' => '' ),
63 58 'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ),
64 59 'active_locale' => array( 'validate' => 'strong', 'default' => '' ),
65 60 'form_status' => array( 'validate' => 'strong', 'default' => 'published' ),
66 - 'allow_past' => array( 'validate' => 'd', 'default' => 0 ),
61 + 'allow_past' => array( 'validate' => 'd', 'default' => 0 ),
62 + 'classic_booking_context_token' => array( 'validate' => 'strong', 'default' => '' ),
67 63 'wpbc_bfb_preview' => array( 'validate' => 'd', 'default' => 0 ),
68 64 'wpbc_bfb_preview_token' => array( 'validate' => 'strong', 'default' => '' ),
69 65 'wpbc_bfb_preview_form_id' => array( 'validate' => 'd', 'default' => 0 ),
70 66 'wpbc_bfb_preview_nonce' => array( 'validate' => 'strong', 'default' => '' ),
71 67 'wpbc_time_override_enabled' => array( 'validate' => 'd', 'default' => 0 ),
72 - 'wpbc_time_override_source' => array( 'validate' => 'strong', 'default' => '' ),
73 - 'wpbc_time_override_start' => array( 'validate' => 'strong', 'default' => '' ),
74 - 'wpbc_time_override_end' => array( 'validate' => 'strong', 'default' => '' ),
75 - ), $experimental_request_rules )
68 + 'wpbc_time_override_source' => array( 'validate' => 'strong', 'default' => '' ),
69 + 'wpbc_time_override_start' => array( 'validate' => 'strong', 'default' => '' ),
70 + 'wpbc_time_override_end' => array( 'validate' => 'strong', 'default' => '' ),
71 + 'wpbc_admin_cost_correction' => array( 'validate' => 'strong', 'default' => '' ),
72 + ), $workflow_request_rules )
76 73 ));
77 74
78 75 // Escape of request params in Ajax Post. We use prefix 'calendar_request_params', if Ajax sent - $_REQUEST['calendar_request_params']['resource_id'], ...
79 76 $request_prefix = 'calendar_request_params';
@@ -79,15 +76,16 @@
79 76 $request_prefix = 'calendar_request_params';
80 77
81 78 //$_REQUEST['calendar_request_params']['dates_ddmmyy_csv'] .= "'%2b(select+'box'+from(select+sleep(2)+from+dual+where+1=1*)a)%2b'-02-21+00:00:00";
82 79
83 - $request_params = $user_request->get_sanitized__in_request__value_or_default( $request_prefix ); // NOT Direct: $_REQUEST['calendar_request_params']['resource_id']
84 - $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 */
85 - $request_params['request_uri'] = $server_http_referer_uri; // Parameter needed for Error in booking saving and reloading calendar again with these actual parameters.
80 + $request_params = $user_request->get_sanitized__in_request__value_or_default( $request_prefix ); // NOT Direct: $_REQUEST['calendar_request_params']['resource_id']
81 + $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 */
82 + $request_params['request_uri'] = $server_http_referer_uri; // Parameter needed for Error in booking saving and reloading calendar again with these actual parameters.
83 + $is_authorized_admin_booking_request = wpbc_is_authorized_admin_booking_request( $request_params['wpbc_admin_booking_nonce'] );
86 84
87 85 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- CAPTCHA " >
88 86 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
89 - wpbc_captcha__in_ajx__check( $request_params, $local_params['is_from_admin_panel'], $_REQUEST[ $request_prefix ] );
87 + wpbc_captcha__in_ajx__check( $request_params, $is_authorized_admin_booking_request, $_REQUEST[ $request_prefix ] );
90 88 // </editor-fold>
91 89
92 90 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING_RESOURCE ID " >
93 91 if ( $request_params['resource_id'] <= 0 ) {
@@ -94,16 +92,13 @@
94 92 $ajx_data_arr['status'] = 'error';
95 93 $ajx_data_arr['status_error'] = 'resource_id_incorrect';
96 94 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
97 95 $ajx_data_arr['ajx_after_action_message'] = 'Wrong ID of booking resource: ' . ' [ request ID: ' . $_REQUEST['calendar_request_params']['resource_id'] . ' | parsed ID: ' . $request_params['resource_id'] . ' ]';
98 - $ajx_data_arr['ajx_after_action_message_status'] = 'error';
99 - wp_send_json( array(
100 - 'ajx_data' => $ajx_data_arr,
101 - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
102 - 'ajx_search_params' => $_REQUEST[ $request_prefix ],
103 - 'ajx_cleaned_params' => $request_params,
104 - 'resource_id' => $request_params['resource_id'],
105 - ) );
96 + $ajx_data_arr['ajx_after_action_message_status'] = 'error';
97 + wp_send_json( array(
98 + 'ajx_data' => $ajx_data_arr,
99 + 'resource_id' => $request_params['resource_id'],
100 + ) );
106 101 }
107 102 // </editor-fold>
108 103
109 104 $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 */
@@ -119,36 +114,37 @@
119 114 'is_show_payment_form' => 1,
120 115 'user_id' => $local_params['user_id'],
121 116 'request_uri' => $server_http_referer_uri,
122 117 'form_status' => $request_params['form_status'],
123 - 'allow_past' => $request_params['allow_past'],
118 + 'allow_past' => $request_params['allow_past'],
119 + 'classic_booking_context_token' => $request_params['classic_booking_context_token'],
124 120 'wpbc_bfb_preview' => $request_params['wpbc_bfb_preview'],
125 121 'wpbc_bfb_preview_token' => $request_params['wpbc_bfb_preview_token'],
126 122 'wpbc_bfb_preview_form_id' => $request_params['wpbc_bfb_preview_form_id'],
127 123 'wpbc_bfb_preview_nonce' => $request_params['wpbc_bfb_preview_nonce'],
128 124 'wpbc_time_override_enabled' => $request_params['wpbc_time_override_enabled'],
129 - 'wpbc_time_override_source' => $request_params['wpbc_time_override_source'],
130 - 'wpbc_time_override_start' => $request_params['wpbc_time_override_start'],
125 + 'wpbc_time_override_source' => $request_params['wpbc_time_override_source'],
126 + 'wpbc_time_override_start' => $request_params['wpbc_time_override_start'],
131 127 'wpbc_time_override_end' => $request_params['wpbc_time_override_end'],
128 + 'wpbc_admin_cost_correction' => $request_params['wpbc_admin_cost_correction'],
132 129 );
133 - if ( wpbc_is_11_5_features_enabled() ) {
134 - $request_save_params['service_id'] = $request_params['service_id'];
135 - $request_save_params['appointment_service_required'] = $request_params['appointment_service_required'];
136 - $request_save_params['appointment_context_token'] = $request_params['appointment_context_token'];
137 - $request_save_params['resource_selector_required'] = $request_params['resource_selector_required'];
138 - $request_save_params['resource_selector_context_token'] = $request_params['resource_selector_context_token'];
139 - }
130 + $request_save_params['service_id'] = $request_params['service_id'];
131 + $request_save_params['appointment_service_required'] = $request_params['appointment_service_required'];
132 + $request_save_params['appointment_context_token'] = $request_params['appointment_context_token'];
133 + $request_save_params['resource_selector_required'] = $request_params['resource_selector_required'];
134 + $request_save_params['resource_selector_context_token'] = $request_params['resource_selector_context_token'];
135 + $request_save_params['wpbc_admin_booking_nonce'] = $request_params['wpbc_admin_booking_nonce'];
140 136 $booking_save_arr = wpbc_booking_save( $request_save_params );
141 137
142 138 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING " >
143 139 if ( 'ok' !== $booking_save_arr['ajx_data']['status'] ) {
144 140
145 - wp_send_json( array( 'ajx_data' => $booking_save_arr['ajx_data'],
146 - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
147 - 'ajx_search_params' => $_REQUEST[ $request_prefix ],
148 - 'ajx_cleaned_params' => $request_params,
149 - 'resource_id' => $request_params['resource_id']
150 - ));
141 + wp_send_json(
142 + array(
143 + 'ajx_data' => $booking_save_arr['ajx_data'],
144 + 'resource_id' => $request_params['resource_id'],
145 + )
146 + );
151 147 }
152 148 // </editor-fold>
153 149
154 150 $ajx_data_arr = $booking_save_arr['ajx_data'];
@@ -279,9 +275,10 @@
279 275 'custom_form' => array( 'validate' => 'strong', 'default' => '' ),
280 276 'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
281 277 'is_show_payment_form' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
282 278 'user_id' => array( 'validate' => 'd', 'default' => wpbc_get_current_user_id() ), // INT
283 - 'allow_past' => array( 'validate' => 'd', 'default' => 0 ),
279 + 'allow_past' => array( 'validate' => 'd', 'default' => 0 ),
280 + 'classic_booking_context_token' => array( 'validate' => 'strong', 'default' => '' ),
284 281 'request_uri' => array( 'validate' => 'strong', 'default' => ( ( defined( 'DOING_AJAX' ) ) && ( DOING_AJAX ) ) ? $server_http_referer_uri : $server_request_uri ), // front-end: $server_request_uri | ajax: $server_http_referer_uri
285 282 // Really Optional:
286 283 'aggregate_resource_id_arr' => array( 'validate' => 'digit_or_csd', 'default' => '' ),
287 284 //TODO: this parameter does not transfer during saving, so here will be always default value 'bookings_only' // FixIn: 10.0.0.7.
@@ -296,21 +293,23 @@
296 293 'wpbc_bfb_preview_token' => array( 'validate' => 'strong', 'default' => '' ),
297 294 'wpbc_bfb_preview_form_id' => array( 'validate' => 'd', 'default' => 0 ),
298 295 'wpbc_bfb_preview_nonce' => array( 'validate' => 'strong', 'default' => '' ),
299 296 'wpbc_time_override_enabled' => array( 'validate' => 'd', 'default' => 0 ),
300 - 'wpbc_time_override_source' => array( 'validate' => 'strong', 'default' => '' ),
301 - 'wpbc_time_override_start' => array( 'validate' => 'strong', 'default' => '' ),
297 + 'wpbc_time_override_source' => array( 'validate' => 'strong', 'default' => '' ),
298 + 'wpbc_time_override_start' => array( 'validate' => 'strong', 'default' => '' ),
302 299 'wpbc_time_override_end' => array( 'validate' => 'strong', 'default' => '' ),
303 - );
304 - if ( wpbc_is_11_5_features_enabled() ) {
305 - $validate_arr_rules['service_id'] = array( 'validate' => 'd', 'default' => 0 );
306 - $validate_arr_rules['appointment_service_required'] = array( 'validate' => 'd', 'default' => 0 );
307 - $validate_arr_rules['appointment_context_token'] = array( 'validate' => 'strong', 'default' => '' );
308 - $validate_arr_rules['resource_selector_required'] = array( 'validate' => 'd', 'default' => 0 );
309 - $validate_arr_rules['resource_selector_context_token'] = array( 'validate' => 'strong', 'default' => '' );
310 - }
300 + 'wpbc_admin_cost_correction' => array( 'validate' => 'strong', 'default' => '' ),
301 + );
302 + $validate_arr_rules['service_id'] = array( 'validate' => 'd', 'default' => 0 );
303 + $validate_arr_rules['appointment_service_required'] = array( 'validate' => 'd', 'default' => 0 );
304 + $validate_arr_rules['appointment_context_token'] = array( 'validate' => 'strong', 'default' => '' );
305 + $validate_arr_rules['resource_selector_required'] = array( 'validate' => 'd', 'default' => 0 );
306 + $validate_arr_rules['resource_selector_context_token'] = array( 'validate' => 'strong', 'default' => '' );
307 + $validate_arr_rules['wpbc_admin_booking_nonce'] = array( 'validate' => 'strong', 'default' => '' );
311 308 $re_cleaned_params = wpbc_sanitize_params_in_arr( $request_params, $validate_arr_rules );
312 - if ( wpbc_is_11_5_features_enabled() && ! empty( $re_cleaned_params['appointment_service_required'] ) && empty( $re_cleaned_params['service_id'] ) ) {
309 + $has_verified_appointment_context = false;
310 + $has_verified_resource_selector_context = false;
311 + if ( ! empty( $re_cleaned_params['appointment_service_required'] ) && empty( $re_cleaned_params['service_id'] ) ) {
313 312 $ajx_data_arr['status'] = 'error';
314 313 $ajx_data_arr['status_error'] = 'appointment_service_required';
315 314 $ajx_data_arr['ajx_after_action_message'] = __( 'Please select a Service.', 'booking' );
316 315 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
@@ -315,9 +314,9 @@
315 314 $ajx_data_arr['ajx_after_action_message'] = __( 'Please select a Service.', 'booking' );
316 315 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
317 316 return array( 'ajx_data' => $ajx_data_arr );
318 317 }
319 - if ( wpbc_is_11_5_features_enabled() && ! empty( $re_cleaned_params['service_id'] ) ) {
318 + if ( ! empty( $re_cleaned_params['service_id'] ) ) {
320 319 if ( ! function_exists( 'wpbc_booking_appointment_validate_submission_context' ) ) {
321 320 $appointment_context_check = new WP_Error( 'appointment_context_unavailable', __( 'The Appointment selection cannot be verified. Please reload the page and try again.', 'booking' ) );
322 321 } else {
323 322 $appointment_context_check = wpbc_booking_appointment_validate_submission_context(
@@ -332,13 +331,14 @@
332 331 $ajx_data_arr['ajx_after_action_message'] = $appointment_context_check->get_error_message();
333 332 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
334 333 return array( 'ajx_data' => $ajx_data_arr );
335 334 }
335 + $has_verified_appointment_context = true;
336 336
337 337 // A client value cannot enable past Appointment creation; trust only the site-authored signed context.
338 338 $re_cleaned_params['allow_past'] = wpbc_booking_appointment_is_past_booking_enabled( $appointment_context_check ) ? 1 : 0;
339 339 }
340 - if ( wpbc_is_11_5_features_enabled() && ! empty( $re_cleaned_params['resource_selector_required'] ) ) {
340 + if ( ! empty( $re_cleaned_params['resource_selector_required'] ) || ! empty( $re_cleaned_params['resource_selector_context_token'] ) ) {
341 341 if ( ! function_exists( 'wpbc_booking_resource_selector_validate_submission_context' ) ) {
342 342 $resource_selector_context_check = new WP_Error( 'resource_selector_context_unavailable', __( 'The Booking Resource selection cannot be verified. Please reload the page and try again.', 'booking' ) );
343 343 } else {
344 344 $resource_selector_context_check = wpbc_booking_resource_selector_validate_submission_context(
@@ -352,16 +352,15 @@
352 352 $ajx_data_arr['ajx_after_action_message'] = $resource_selector_context_check->get_error_message();
353 353 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
354 354 return array( 'ajx_data' => $ajx_data_arr );
355 355 }
356 + $has_verified_resource_selector_context = true;
356 357
357 358 // Trust only the site-authored signed selector context for public past bookings.
358 359 $re_cleaned_params['allow_past'] = wpbc_booking_resource_selector_is_past_booking_enabled( $resource_selector_context_check ) ? 1 : 0;
359 360 }
360 361
361 - $admin_uri = ltrim( str_replace( get_site_url( null, '', 'admin' ), '', admin_url( 'admin.php?' ) ), '/' ); // wp-admin/admin.php?
362 -
363 - $re_cleaned_params['form_status'] = sanitize_key( $re_cleaned_params['form_status'] );
362 + $re_cleaned_params['form_status'] = sanitize_key( $re_cleaned_params['form_status'] );
364 363 if ( 'preview' !== $re_cleaned_params['form_status'] ) {
365 364 $re_cleaned_params['form_status'] = 'published';
366 365 }
367 366 // FixIn: 2026-02-05 - make preview/published available to form parsing/templates during this request.
@@ -378,14 +377,26 @@
378 377
379 378 // -----------------------------------------------------------------------------------------------------------------
380 379 // Local parameters
381 380 // -----------------------------------------------------------------------------------------------------------------
382 - $local_params = array();
383 - $local_params['is_from_admin_panel'] = ( false !== strpos( $re_cleaned_params['request_uri'], $admin_uri ) ); // true | false
381 + $local_params = array();
382 + $is_authorized_admin_booking_request = wpbc_is_authorized_admin_booking_request( $re_cleaned_params['wpbc_admin_booking_nonce'] );
383 + $local_params['is_from_admin_panel'] = $is_authorized_admin_booking_request;
384 384 $local_params['user_id'] = $re_cleaned_params['user_id']; // 1
385 - $local_params['sync_gid'] = $re_cleaned_params['sync_gid']; // ''
386 - $local_params['is_approve_booking'] = $re_cleaned_params['is_approve_booking']; // 0 | 1
387 - $local_params['is_use_booking_recurrent_time'] = ( 1 === $re_cleaned_params['is_use_booking_recurrent_time'] ); // false | true
385 + $local_params['sync_gid'] = $re_cleaned_params['sync_gid']; // ''
386 + $local_params['is_approve_booking'] = $re_cleaned_params['is_approve_booking']; // 0 | 1
387 + $local_params['is_use_booking_recurrent_time'] = ( 1 === $re_cleaned_params['is_use_booking_recurrent_time'] ); // false | true
388 + $request_action = isset( $_REQUEST['action'] ) && is_scalar( $_REQUEST['action'] )
389 + ? sanitize_key( (string) wp_unslash( $_REQUEST['action'] ) )
390 + : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
391 + $is_public_booking_create_request = wp_doing_ajax()
392 + && 'wpbc_ajx_booking__create' === strtolower( $request_action )
393 + && ! $is_authorized_admin_booking_request;
394 +
395 + // Time overrides belong exclusively to the capability-protected Add Booking administration workflow.
396 + $re_cleaned_params = wpbc_restrict_booking_time_override_to_authorized_admin( $re_cleaned_params, $is_authorized_admin_booking_request );
397 + // Cost corrections belong exclusively to capability-protected administrator booking workflows.
398 + $re_cleaned_params = wpbc_restrict_booking_cost_correction_to_authorized_admin( $re_cleaned_params, $is_authorized_admin_booking_request );
388 399
389 400 // -----------------------------------------------------------------------------------------------------------------
390 401 // Parse Local parameters for later use
391 402 // -----------------------------------------------------------------------------------------------------------------
@@ -416,9 +427,9 @@
416 427 }
417 428 // Important! : [ 64800, 72000 ]
418 429 $local_params['time_as_seconds_arr'] = wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $local_params['structured_booking_data_arr'] );
419 430 $local_params['appointment_service'] = array();
420 - if ( wpbc_is_11_5_features_enabled() && ! empty( $re_cleaned_params['service_id'] ) && function_exists( 'wpbc_appointment_services_repository' ) ) {
431 + if ( ! empty( $re_cleaned_params['service_id'] ) && function_exists( 'wpbc_appointment_services_repository' ) ) {
421 432 $range_time_value = isset( $local_params['structured_booking_data_arr']['rangetime'] ) ? $local_params['structured_booking_data_arr']['rangetime'] : '';
422 433 $start_time_value = isset( $local_params['structured_booking_data_arr']['starttime'] ) ? $local_params['structured_booking_data_arr']['starttime'] : '';
423 434 $range_time_value = is_array( $range_time_value ) ? implode( '', $range_time_value ) : $range_time_value;
424 435 $start_time_value = is_array( $start_time_value ) ? implode( '', $start_time_value ) : $start_time_value;
@@ -489,12 +500,59 @@
489 500 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[0] ),
490 501 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[1] )
491 502 );
492 503 // [ '2023-09-10', '2023-09-11' ]
493 - $local_params['dates_only_sql_arr'] = wpbc_convert_dates_str__dd_mm_yyyy__to__yyyy_mm_dd( $re_cleaned_params["dates_ddmmyy_csv"] );
494 - $local_params['dates_only_sql_arr'] = explode( ',', $local_params['dates_only_sql_arr'] );
495 -
496 - if (
504 + $local_params['dates_only_sql_arr'] = wpbc_convert_dates_str__dd_mm_yyyy__to__yyyy_mm_dd( $re_cleaned_params["dates_ddmmyy_csv"] );
505 + $local_params['dates_only_sql_arr'] = explode( ',', $local_params['dates_only_sql_arr'] );
506 +
507 + $classic_context = array();
508 + $has_verified_classic_context = false;
509 + if ( ! empty( $re_cleaned_params['classic_booking_context_token'] ) && function_exists( 'wpbc_classic_booking_context_validate_submission' ) ) {
510 + $classic_context = wpbc_classic_booking_context_validate_submission(
511 + $re_cleaned_params['classic_booking_context_token'],
512 + $re_cleaned_params['resource_id'],
513 + $local_params['dates_only_sql_arr'],
514 + $re_cleaned_params['custom_form'],
515 + $re_cleaned_params['aggregate_resource_id_arr']
516 + );
517 + if ( is_wp_error( $classic_context ) ) {
518 + $ajx_data_arr['status'] = 'error';
519 + $ajx_data_arr['status_error'] = $classic_context->get_error_code();
520 + $ajx_data_arr['ajx_after_action_message'] = $classic_context->get_error_message();
521 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
522 + return array( 'ajx_data' => $ajx_data_arr );
523 + }
524 +
525 + $has_verified_classic_context = true;
526 + $re_cleaned_params['allow_past'] = ! empty( $classic_context['allow_past'] ) ? 1 : 0;
527 + // Pass only the signed canonical set into final availability and persistence decisions.
528 + $re_cleaned_params['aggregate_resource_id_arr'] = implode( ',', $classic_context['aggregate_resource_ids'] );
529 + }
530 +
531 + if ( $is_public_booking_create_request && ! $has_verified_classic_context ) {
532 + $ajx_data_arr['status'] = 'error';
533 + $ajx_data_arr['status_error'] = 'classic_booking_context_required';
534 + $ajx_data_arr['ajx_after_action_message'] = __( 'The booking form context has expired. Please reload the page and try again.', 'booking' );
535 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
536 + return array( 'ajx_data' => $ajx_data_arr );
537 + }
538 +
539 + if ( $has_verified_classic_context ) {
540 + $workflow_context_error = wpbc_booking_create_validate_required_workflow(
541 + $classic_context,
542 + $has_verified_appointment_context,
543 + $has_verified_resource_selector_context
544 + );
545 + if ( is_wp_error( $workflow_context_error ) ) {
546 + $ajx_data_arr['status'] = 'error';
547 + $ajx_data_arr['status_error'] = $workflow_context_error->get_error_code();
548 + $ajx_data_arr['ajx_after_action_message'] = $workflow_context_error->get_error_message();
549 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
550 + return array( 'ajx_data' => $ajx_data_arr );
551 + }
552 + }
553 +
554 + if (
497 555 ( ! empty( $local_params['time_override_arr'] ) )
498 556 && ( 'times_availability' === $local_params['time_override_arr']['source'] )
499 557 && ( count( array_filter( $local_params['dates_only_sql_arr'] ) ) > 1 )
500 558 ) {
@@ -503,13 +561,16 @@
503 561
504 562 $local_params['is_show_payment_form'] = $re_cleaned_params["is_show_payment_form"];
505 563
506 564 // FixIn: 9.9.0.35.
507 - if ( $local_params['is_show_payment_form'] ) {
508 - $local_params['is_show_payment_form'] = ( false !== strpos( $re_cleaned_params['request_uri'], 'is_show_payment_form=Off' ) )
509 - ? 0
510 - : $local_params['is_show_payment_form']; // 1|0
511 - }
565 + if ( $local_params['is_show_payment_form'] ) {
566 + $local_params['is_show_payment_form'] = (
567 + $is_authorized_admin_booking_request
568 + && false !== strpos( $re_cleaned_params['request_uri'], 'is_show_payment_form=Off' )
569 + )
570 + ? 0
571 + : $local_params['is_show_payment_form']; // 1|0
572 + }
512 573
513 574 // Get EDIT booking data
514 575 $local_params['edit_resource_id'] = '';
515 576 $local_params['skip_booking_id'] = '';
@@ -527,20 +588,12 @@
527 588 $local_params['is_duplicate_booking'] = 1;
528 589 }
529 590 }
530 591
531 - $request_action = isset( $_REQUEST['action'] ) && is_scalar( $_REQUEST['action'] )
532 - ? sanitize_key( (string) wp_unslash( $_REQUEST['action'] ) )
533 - : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
534 - $is_frontend_ajax_edit = defined( 'DOING_AJAX' )
535 - && DOING_AJAX
592 + $is_frontend_ajax_edit = wp_doing_ajax()
536 593 && 'wpbc_ajx_booking__create' === strtolower( $request_action )
537 594 && 0 !== $local_params['is_edit_booking'];
538 - $is_authorized_admin_edit = $local_params['is_from_admin_panel']
539 - && is_user_logged_in()
540 - && class_exists( 'WPBC_Add_Booking_Component' )
541 - && WPBC_Add_Booking_Component::current_user_can_add_booking()
542 - && wpbc_is_mu_user_can_be_here( 'activated_user' );
595 + $is_authorized_admin_edit = $is_authorized_admin_booking_request;
543 596
544 597 if (
545 598 $is_frontend_ajax_edit
546 599 && ! $is_authorized_admin_edit
@@ -628,9 +681,9 @@
628 681 $php_performance = wpbc_php_performance_END( 'wpbc__where_to_save_booking' , $php_performance );
629 682 // </editor-fold>
630 683 }
631 684
632 - if ( wpbc_is_11_5_features_enabled() && ! empty( $local_params['appointment_service'] ) && function_exists( 'wpbc_appointment_services_check_buffer_conflicts' ) ) {
685 + if ( ! empty( $local_params['appointment_service'] ) && function_exists( 'wpbc_appointment_services_check_buffer_conflicts' ) ) {
633 686 $buffer_check = wpbc_appointment_services_check_buffer_conflicts(
634 687 $local_params['appointment_service'],
635 688 $where_to_save_booking['main__resource_id'],
636 689 array_keys( $where_to_save_booking['resources_in_dates'] ),
@@ -732,11 +785,9 @@
732 785 $str_dates__dd_mm_yyyy = wpbc_convert_dates_arr__yyyy_mm_dd__to__dd_mm_yyyy( $payment_params['booked_dates_times_arr']['dates_ymd_arr'] ); // ['2023-10-20','2023-10-25'] => ['20.10.2023','25.10.2023']
733 786 $payment_params['str_dates__dd_mm_yyyy'] = implode( ',', $str_dates__dd_mm_yyyy ); // REQUIRED -- '14.11.2023, 15.11.2023, 16.11.2023, 17.11.2023'
734 787 $payment_params['booking_id'] = $booking_new_arr['booking_id']; // REQUIRED -- '2'
735 788 $payment_params['resource_id'] = $create_params['resource_id']; // REQUIRED -- '2' can be child resource (changed in wpbc_where_to_save() )
736 - if ( wpbc_is_11_5_features_enabled() ) {
737 - $payment_params['service_id'] = ! empty( $create_params['appointment_service']['service_id'] ) ? absint( $create_params['appointment_service']['service_id'] ) : 0;
738 - }
789 + $payment_params['service_id'] = ! empty( $create_params['appointment_service']['service_id'] ) ? absint( $create_params['appointment_service']['service_id'] ) : 0;
739 790 $payment_params['initial_resource_id'] = $local_params['initial_resource_id']; // REQUIRED -- '2' initial calendar - parent resource
740 791 $payment_params['form_data'] = $booking_new_arr['form_data']; // we re-save it, because here can be sync_guid and custom form new data from wpbc_db__booking_save(..) // REQUIRED -- 'text^selected_short_timedates_hint4^06/11/2018 14:00...'
741 792 $payment_params['times_array'] = array(
742 793 explode( ':', $where_to_save_booking['time_to_book'][0] ), // ["10","00","00"]
@@ -746,9 +797,10 @@
746 797 $payment_params['is_edit_booking'] = $create_params['is_edit_booking']; // => 0 0 | int - ID of the booking
747 798 $payment_params['custom_form'] = $create_params['custom_form']; // => '' '' | 'some_name'
748 799 $payment_params['is_duplicate_booking'] = $create_params['is_duplicate_booking']; // => 0 0 | 1
749 800 $payment_params['is_from_admin_panel'] = $create_params['is_from_admin_panel']; // => false true | false
750 - $payment_params['is_show_payment_form'] = $create_params['is_show_payment_form']; // => 1 0 | 1
801 + $payment_params['is_show_payment_form'] = $create_params['is_show_payment_form']; // => 1 0 | 1
802 + $payment_params['wpbc_admin_cost_correction'] = $re_cleaned_params['wpbc_admin_cost_correction'];
751 803 if ( $payment_params['is_from_admin_panel'] ) {
752 804 // $payment_params['is_show_payment_form'] = 0; // FixIn: 9.9.0.21.
753 805 }
754 806 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
@@ -1214,9 +1266,9 @@
1214 1266 $sql_field_arr[] = array( 'name' => 'form', 'type' => '%s', 'value' => $form_data );
1215 1267 $sql_field_arr[] = array( 'name' => 'booking_type', 'type' => '%d', 'value' => $create_params['resource_id'] );
1216 1268 $sql_field_arr[] = array( 'name' => 'modification_date', 'type' => '%s', 'value' => gmdate( 'Y-m-d H:i:s' ) );
1217 1269 $sql_field_arr[] = array( 'name' => 'sort_date', 'type' => '%s', 'value' => $create_params['dates_only_sql_arr'][0] . ' ' . $create_params['time_as_his_arr'][0] );
1218 - $sql_field_arr[] = array( 'name' => 'hash', 'type' => 'MD5(%s)', 'value' => time() . '_' . wp_rand( 1000, 1000000 ) );
1270 + $sql_field_arr[] = array( 'name' => 'hash', 'type' => '%s', 'value' => wpbc_hash__generate_booking_hash() );
1219 1271
1220 1272
1221 1273 if (
1222 1274 ( 0 == $create_params['is_edit_booking'] ) || // If not edit, then INSERT.
@@ -1237,12 +1289,15 @@
1237 1289 $sql_prepare_arr['name'] = implode( ', ', $sql_prepare_arr['name'] );
1238 1290 $sql_prepare_arr['type'] = implode( ', ', $sql_prepare_arr['type'] );
1239 1291 /* phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare */
1240 1292 $sql = $wpdb->prepare( "INSERT INTO {$wpdb->prefix}booking " . " ( {$sql_prepare_arr['name']} )" . " VALUES ( {$sql_prepare_arr['type']} )", $sql_prepare_arr['value'] );
1241 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1242 - if ( false === $wpdb->query( $sql ) ) {
1243 - return array( 'status' => 'error', 'message' => 'Error. INSERT New Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql );
1244 - }
1293 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1294 + if ( false === $wpdb->query( $sql ) ) {
1295 + return array(
1296 + 'status' => 'error',
1297 + 'message' => __( 'The booking could not be saved because of a database error. Please try again or contact the website administrator.', 'booking' ),
1298 + );
1299 + }
1245 1300 // Get ID of booking
1246 1301 $booking_id = (int) $wpdb->insert_id;
1247 1302
1248 1303 } else { // Edit - UPDATE
@@ -1256,14 +1311,15 @@
1256 1311 $sql_prepare_arr['set'] = implode( ', ', $sql_prepare_arr['set'] );
1257 1312
1258 1313 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
1259 1314 $sql = $wpdb->prepare( "UPDATE {$wpdb->prefix}booking SET {$sql_prepare_arr['set']} WHERE booking_id={$booking_id};", $sql_prepare_arr['value'] );
1260 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1261 - if ( false === $wpdb->query( $sql ) ) {
1262 - return array( 'status' => 'error',
1263 - 'message' => 'Error. UPDATE Exist Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql,
1264 - );
1265 - }
1315 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1316 + if ( false === $wpdb->query( $sql ) ) {
1317 + return array(
1318 + 'status' => 'error',
1319 + 'message' => __( 'The booking could not be updated because of a database error. Please try again or contact the website administrator.', 'booking' ),
1320 + );
1321 + }
1266 1322
1267 1323 // Check if dates previously was approved.
1268 1324 $slct_sql = "SELECT approved FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id}) LIMIT 0,1";
1269 1325 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
@@ -1554,9 +1610,9 @@
1554 1610 *
1555 1611 * // Now get start/end times as seconds: [ 64800, 72000 ]
1556 1612 * $time_as_seconds_arr = wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $structured_booking_data_arr );
1557 1613 */
1558 - function wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $booking_form_data__arr ){
1614 + function wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $booking_form_data__arr ){
1559 1615
1560 1616 $selected_time_fields = wpbc_get__selected_time_fields__in_booking_form__as_arr( $booking_form_data__arr );
1561 1617
1562 1618 // 2.2 Get selected SECONDS to book ---------------------------------------------------------------------------
@@ -1595,14 +1651,159 @@
1595 1651 }
1596 1652 }
1597 1653 }
1598 1654
1599 - return $time_as_seconds_arr;
1600 - }
1601 -
1602 -
1603 - /**
1604 - * Get explicit admin-selected time override from Add Booking modal request.
1655 + return $time_as_seconds_arr;
1656 + }
1657 +
1658 +
1659 + /**
1660 + * Determine whether a booking-create request is an authorized administration workflow.
1661 + *
1662 + * The public booking action is intentionally available to signed-out visitors. A
1663 + * Referer, request path, or caller-supplied Boolean therefore cannot establish an
1664 + * administrator security context. The Add Booking UI supplies this user-bound nonce,
1665 + * and the server independently rechecks login, capability, and MultiUser access.
1666 + *
1667 + * @param mixed $admin_booking_nonce Candidate Add Booking administration nonce.
1668 + *
1669 + * @return bool True only for an authorized Add Booking administration request.
1670 + */
1671 + function wpbc_is_authorized_admin_booking_request( $admin_booking_nonce ) {
1672 +
1673 + if (
1674 + ! is_scalar( $admin_booking_nonce )
1675 + || '' === trim( (string) $admin_booking_nonce )
1676 + || ! is_user_logged_in()
1677 + || ! wp_verify_nonce( sanitize_text_field( (string) $admin_booking_nonce ), 'wpbc_admin_booking_create' )
1678 + || ! class_exists( 'WPBC_Add_Booking_Component' )
1679 + || ! WPBC_Add_Booking_Component::current_user_can_add_booking()
1680 + || ! wpbc_is_mu_user_can_be_here( 'activated_user' )
1681 + ) {
1682 + return false;
1683 + }
1684 +
1685 + return true;
1686 + }
1687 +
1688 +
1689 + /**
1690 + * Require the signed workflow proof declared by a verified Booking Form context.
1691 + *
1692 + * Appointment and Resource Selector JavaScript flags are presentation hints only.
1693 + * The signed Booking Form context identifies the server-rendered workflow, so removing
1694 + * a flag or domain token cannot downgrade that form to a different workflow.
1695 + *
1696 + * @param array $classic_context Verified Booking Form context.
1697 + * @param bool $has_verified_appointment_context Whether Service and Provider proof passed validation.
1698 + * @param bool $has_verified_resource_selector_context Whether Resource Selector proof passed validation.
1699 + *
1700 + * @return true|WP_Error True when the required proof is present, otherwise a safe validation error.
1701 + */
1702 + function wpbc_booking_create_validate_required_workflow( $classic_context, $has_verified_appointment_context, $has_verified_resource_selector_context ) {
1703 +
1704 + $booking_workflow = isset( $classic_context['booking_workflow'] ) ? sanitize_key( $classic_context['booking_workflow'] ) : '';
1705 + if ( 'appointment' === $booking_workflow && ! $has_verified_appointment_context ) {
1706 + return new WP_Error( 'appointment_context_required', __( 'The Appointment selection has expired. Please start over and try again.', 'booking' ) );
1707 + }
1708 + if ( 'resource_selector' === $booking_workflow && ! $has_verified_resource_selector_context ) {
1709 + return new WP_Error( 'resource_selector_context_required', __( 'The Booking Resource selection has expired. Please start over and try again.', 'booking' ) );
1710 + }
1711 +
1712 + return true;
1713 + }
1714 +
1715 +
1716 + /**
1717 + * Remove administrator time-override values from an unauthorized booking request.
1718 + *
1719 + * The public booking endpoint intentionally accepts unauthenticated requests, so
1720 + * sanitizing these values is not sufficient authorization. Clearing every related
1721 + * value here prevents a public client from replacing the Booking Form's configured
1722 + * time while preserving the capability-protected Add Booking workflow.
1723 + *
1724 + * @param array $request_params Sanitized booking request parameters.
1725 + * @param bool $is_authorized_admin_booking_request Whether the current request is an authorized Add Booking administration request.
1726 + *
1727 + * @return array Booking request parameters with unauthorized override values removed.
1728 + */
1729 + function wpbc_restrict_booking_time_override_to_authorized_admin( $request_params, $is_authorized_admin_booking_request ) {
1730 +
1731 + $request_params = is_array( $request_params ) ? $request_params : array();
1732 + if ( $is_authorized_admin_booking_request ) {
1733 + return $request_params;
1734 + }
1735 +
1736 + $request_params['wpbc_time_override_enabled'] = 0;
1737 + $request_params['wpbc_time_override_source'] = '';
1738 + $request_params['wpbc_time_override_start'] = '';
1739 + $request_params['wpbc_time_override_end'] = '';
1740 +
1741 + return $request_params;
1742 + }
1743 +
1744 +
1745 + /**
1746 + * Authorize and normalize an administrator cost-correction request value.
1747 + *
1748 + * Booking creation is intentionally public, so a sanitized numeric value is
1749 + * not sufficient authorization. Only capability-protected Add Booking and
1750 + * Add Appointment workflows in Business Small or higher may retain this value.
1751 + * Missing, malformed, out-of-range, public, and unsupported-edition values
1752 + * are reduced to an empty sentinel, which preserves automatic calculation.
1753 + *
1754 + * @param array $request_params Sanitized booking request parameters.
1755 + * @param bool $is_authorized_admin_booking_request Whether this is an authorized administrator booking request.
1756 + *
1757 + * @return array Booking request parameters with a normalized or empty cost correction.
1758 + */
1759 + function wpbc_restrict_booking_cost_correction_to_authorized_admin( $request_params, $is_authorized_admin_booking_request ) {
1760 +
1761 + $request_params = is_array( $request_params ) ? $request_params : array();
1762 + $raw_cost = isset( $request_params['wpbc_admin_cost_correction'] ) ? $request_params['wpbc_admin_cost_correction'] : '';
1763 +
1764 + $request_params['wpbc_admin_cost_correction'] = '';
1765 + if ( ! $is_authorized_admin_booking_request || ! class_exists( 'wpdev_bk_biz_s' ) ) {
1766 + return $request_params;
1767 + }
1768 +
1769 + $request_params['wpbc_admin_cost_correction'] = wpbc_sanitize_booking_cost_correction( $raw_cost );
1770 +
1771 + return $request_params;
1772 + }
1773 +
1774 +
1775 + /**
1776 + * Sanitize one exact administrator-entered Booking total.
1777 + *
1778 + * @param mixed $raw_cost Raw request value.
1779 + *
1780 + * @return string Normalized decimal without trailing zeroes, or an empty string when invalid.
1781 + */
1782 + function wpbc_sanitize_booking_cost_correction( $raw_cost ) {
1783 +
1784 + if ( ! is_scalar( $raw_cost ) ) {
1785 + return '';
1786 + }
1787 +
1788 + $raw_cost = trim( sanitize_text_field( (string) $raw_cost ) );
1789 + if ( '' === $raw_cost || ! preg_match( '/^[0-9]{1,10}(?:\.[0-9]{1,8})?$/', $raw_cost ) ) {
1790 + return '';
1791 + }
1792 +
1793 + $normalized_cost = (float) $raw_cost;
1794 + if ( ! is_finite( $normalized_cost ) || $normalized_cost < 0 || $normalized_cost > 1000000000 ) {
1795 + return '';
1796 + }
1797 +
1798 + $normalized_cost = rtrim( rtrim( number_format( $normalized_cost, 8, '.', '' ), '0' ), '.' );
1799 +
1800 + return '' === $normalized_cost ? '0' : $normalized_cost;
1801 + }
1802 +
1803 +
1804 + /**
1805 + * Get explicit admin-selected time override from Add Booking modal request.
1605 1806 *
1606 1807 * @param array $request_params Sanitized booking request params.
1607 1808 *
1608 1809 * @return array Empty array or array with start/end HH:MM values.