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/create_booking.php +697 -143 10.1011.8.3 View file →
@@ -1,4 +1,4 @@
1 1 <?php
2 2
3 3 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly // FixIn: 9.8.0.4.
4 4
@@ -10,9 +10,9 @@
10 10 * Response to Ajax request, about loading calendar data
11 11 *
12 12 * @return void
13 13 */
14 -function ajax_WPBC_AJX_BOOKING__CREATE() {
14 +function ajax_WPBC_AJX_BOOKING__CREATE() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound
15 15
16 16 /**
17 17 * Tip / translation /
18 18 * Please note, translation was loaded on hook add_action( 'plugins_loaded', 'wpbc_load_translation', 1000 ); and use $_REQUEST['wpbc_ajx_locale'], so do not worry about it.
@@ -28,49 +28,64 @@
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 - $user_request = new WPBC_AJX__REQUEST( array( // Using this class here only for escaping variables
41 - 'db_option_name' => 'booking__wpbc_booking_create__request_params', // Not necessary, because we not save request, only sanitize it
42 - 'user_id' => $local_params['user_id'], // Not necessary, because we not save request, only sanitize it
43 - 'request_rules_structure' => array(
44 - 'resource_id' => array( 'validate' => 'd', 'default' => 1 ), // 'digit_or_csd'
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 + );
45 +
46 + $user_request = new WPBC_AJX__REQUEST( array( // Using this class here only for escaping variables
47 + 'db_option_name' => 'booking__wpbc_booking_create__request_params', // Not necessary, because we not save request, only sanitize it
48 + 'user_id' => $local_params['user_id'], // Not necessary, because we not save request, only sanitize it
49 + 'request_rules_structure' => array_merge( array(
50 + 'resource_id' => array( 'validate' => 'd', 'default' => 1 ), // 'digit_or_csd'.
51 + 'aggregate_resource_id_arr' => array( 'validate' => 'digit_or_csd', 'default' => '' ),
52 + 'dates_ddmmyy_csv' => array( 'validate' => 'csv_dates', 'default' => '' ), // FixIn: 9.9.1.1.
53 + 'formdata' => array( 'validate' => 'strong', 'default' => '' ),
54 + 'booking_hash' => array( 'validate' => 'strong', 'default' => '' ),
55 + 'custom_form' => array( 'validate' => 'strong', 'default' => '' ),
56 + 'captcha_chalange' => array( 'validate' => 'strong', 'default' => '' ),
57 + 'captcha_user_input' => array( 'validate' => 'strong', 'default' => '' ),
58 + 'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ),
59 + 'active_locale' => array( 'validate' => 'strong', 'default' => '' ),
60 + 'form_status' => array( 'validate' => 'strong', 'default' => 'published' ),
61 + 'allow_past' => array( 'validate' => 'd', 'default' => 0 ),
62 + 'classic_booking_context_token' => array( 'validate' => 'strong', 'default' => '' ),
63 + 'wpbc_bfb_preview' => array( 'validate' => 'd', 'default' => 0 ),
64 + 'wpbc_bfb_preview_token' => array( 'validate' => 'strong', 'default' => '' ),
65 + 'wpbc_bfb_preview_form_id' => array( 'validate' => 'd', 'default' => 0 ),
66 + 'wpbc_bfb_preview_nonce' => array( 'validate' => 'strong', 'default' => '' ),
67 + 'wpbc_time_override_enabled' => array( 'validate' => 'd', 'default' => 0 ),
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 )
73 + ));
45 74
46 - 'aggregate_resource_id_arr' => array( 'validate' => 'digit_or_csd', 'default' => '' ),
47 -
48 - 'dates_ddmmyy_csv' => array( 'validate' => 'csv_dates', 'default' => '' ), // FixIn: 9.9.1.1.
49 - 'formdata' => array( 'validate' => 'strong', 'default' => '' ),
50 - 'booking_hash' => array( 'validate' => 'strong', 'default' => '' ),
51 - 'custom_form' => array( 'validate' => 'strong', 'default' => '' ),
52 -
53 - 'captcha_chalange' => array( 'validate' => 'strong', 'default' => '' ),
54 - 'captcha_user_input' => array( 'validate' => 'strong', 'default' => '' ),
55 -
56 - 'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ),
57 - 'active_locale' => array( 'validate' => 'strong', 'default' => '' )
58 - )
59 - ));
60 -
61 75 // Escape of request params in Ajax Post. We use prefix 'calendar_request_params', if Ajax sent - $_REQUEST['calendar_request_params']['resource_id'], ...
62 76 $request_prefix = 'calendar_request_params';
63 77
64 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";
65 79
66 - $request_params = $user_request->get_sanitized__in_request__value_or_default( $request_prefix ); // NOT Direct: $_REQUEST['calendar_request_params']['resource_id']
67 - $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 */
68 - $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'] );
69 84
70 85 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- CAPTCHA " >
71 86 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
72 - 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 ] );
73 88 // </editor-fold>
74 89
75 90 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING_RESOURCE ID " >
76 91 if ( $request_params['resource_id'] <= 0 ) {
@@ -77,45 +92,59 @@
77 92 $ajx_data_arr['status'] = 'error';
78 93 $ajx_data_arr['status_error'] = 'resource_id_incorrect';
79 94 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
80 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'] . ' ]';
81 - $ajx_data_arr['ajx_after_action_message_status'] = 'error';
82 - wp_send_json( array(
83 - 'ajx_data' => $ajx_data_arr,
84 - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
85 - 'ajx_search_params' => $_REQUEST[ $request_prefix ],
86 - 'ajx_cleaned_params' => $request_params,
87 - 'resource_id' => $request_params['resource_id'],
88 - ) );
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 + ) );
89 101 }
90 102 // </editor-fold>
91 103
92 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 */
93 - $request_save_params = array(
94 - 'resource_id' => $request_params['resource_id'],
95 - 'dates_ddmmyy_csv' => $request_params['dates_ddmmyy_csv'],
96 - 'form_data' => $request_params['formdata'],
97 - 'aggregate_resource_id_arr' => $request_params['aggregate_resource_id_arr'], // Optional can be ''
98 105
99 - 'booking_hash' => $request_params['booking_hash'],
100 - 'custom_form' => $request_params['custom_form'],
101 -
102 - 'is_emails_send' => $request_params['is_emails_send'],
103 - 'is_show_payment_form' => 1,
104 - 'user_id' => $local_params['user_id'],
105 - 'request_uri' => $server_http_referer_uri
106 - );
106 + $request_save_params = array(
107 + 'resource_id' => $request_params['resource_id'],
108 + 'dates_ddmmyy_csv' => $request_params['dates_ddmmyy_csv'],
109 + 'form_data' => $request_params['formdata'],
110 + 'aggregate_resource_id_arr' => $request_params['aggregate_resource_id_arr'], // Optional can be ''.
111 + 'booking_hash' => $request_params['booking_hash'],
112 + 'custom_form' => $request_params['custom_form'],
113 + 'is_emails_send' => $request_params['is_emails_send'],
114 + 'is_show_payment_form' => 1,
115 + 'user_id' => $local_params['user_id'],
116 + 'request_uri' => $server_http_referer_uri,
117 + 'form_status' => $request_params['form_status'],
118 + 'allow_past' => $request_params['allow_past'],
119 + 'classic_booking_context_token' => $request_params['classic_booking_context_token'],
120 + 'wpbc_bfb_preview' => $request_params['wpbc_bfb_preview'],
121 + 'wpbc_bfb_preview_token' => $request_params['wpbc_bfb_preview_token'],
122 + 'wpbc_bfb_preview_form_id' => $request_params['wpbc_bfb_preview_form_id'],
123 + 'wpbc_bfb_preview_nonce' => $request_params['wpbc_bfb_preview_nonce'],
124 + 'wpbc_time_override_enabled' => $request_params['wpbc_time_override_enabled'],
125 + 'wpbc_time_override_source' => $request_params['wpbc_time_override_source'],
126 + 'wpbc_time_override_start' => $request_params['wpbc_time_override_start'],
127 + 'wpbc_time_override_end' => $request_params['wpbc_time_override_end'],
128 + 'wpbc_admin_cost_correction' => $request_params['wpbc_admin_cost_correction'],
129 + );
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'];
107 136 $booking_save_arr = wpbc_booking_save( $request_save_params );
108 137
109 138 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING " >
110 139 if ( 'ok' !== $booking_save_arr['ajx_data']['status'] ) {
111 140
112 - wp_send_json( array( 'ajx_data' => $booking_save_arr['ajx_data'],
113 - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
114 - 'ajx_search_params' => $_REQUEST[ $request_prefix ],
115 - 'ajx_cleaned_params' => $request_params,
116 - 'resource_id' => $request_params['resource_id']
117 - ));
141 + wp_send_json(
142 + array(
143 + 'ajx_data' => $booking_save_arr['ajx_data'],
144 + 'resource_id' => $request_params['resource_id'],
145 + )
146 + );
118 147 }
119 148 // </editor-fold>
120 149
121 150 $ajx_data_arr = $booking_save_arr['ajx_data'];
@@ -140,9 +169,9 @@
140 169
141 170 /* if admin edit ?
142 171 var my_message = '<?php echo esc_js( __('Updated successfully' ,'booking') ) ; ?>';
143 172 wpbc_admin_show_message( my_message, 'success', 3000 );
144 - location.href='<?php echo wpbc_get_bookings_url() ;?>&view_mode=vm_listing&tab=actions&wh_booking_id=<?php echo $is_edit_booking['booking_id'] ; ?>';
173 + location.href='<?php echo wpbc_get_bookings_url() ;?>&tab=vm_booking_listing&wh_booking_id=<?php echo $is_edit_booking['booking_id'] ; ?>';
145 174 */
146 175
147 176
148 177 // -----------------------------------------------------------------------------------------------------------------
@@ -227,9 +256,9 @@
227 256 *
228 257 */
229 258 function wpbc_booking_save( $request_params ){
230 259 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
231 - $php_performance = php_performance_START( 'total', array() );
260 + $php_performance = wpbc_php_performance_START( 'total', array() );
232 261 // </editor-fold>
233 262 $ajx_data_arr = array();
234 263 $ajx_data_arr['status'] = 'ok';
235 264
@@ -237,11 +266,11 @@
237 266 // 1. Direct Clean Params
238 267 // -----------------------------------------------------------------------------------------------------------------
239 268 $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 */
240 269 $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 */
241 - $validate_arr_rules = array(
242 - 'resource_id' => array( 'validate' => 'd', 'default' => 1 ), // INT
243 - 'dates_ddmmyy_csv' => array( 'validate' => 'csv_dates', 'default' => '' ), // FixIn: 9.9.1.1.
270 + $validate_arr_rules = array(
271 + 'resource_id' => array( 'validate' => 'd', 'default' => 1 ), // INT
272 + 'dates_ddmmyy_csv' => array( 'validate' => 'csv_dates', 'default' => '' ), // FixIn: 9.9.1.1.
244 273 'form_data' => array( 'validate' => 'strong', 'default' => '' ),
245 274 'booking_hash' => array( 'validate' => 'strong', 'default' => '' ),
246 275 'custom_form' => array( 'validate' => 'strong', 'default' => '' ),
247 276 'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
@@ -246,8 +275,10 @@
246 275 'custom_form' => array( 'validate' => 'strong', 'default' => '' ),
247 276 'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
248 277 'is_show_payment_form' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
249 278 'user_id' => array( 'validate' => 'd', 'default' => wpbc_get_current_user_id() ), // INT
279 + 'allow_past' => array( 'validate' => 'd', 'default' => 0 ),
280 + 'classic_booking_context_token' => array( 'validate' => 'strong', 'default' => '' ),
250 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
251 282 // Really Optional:
252 283 'aggregate_resource_id_arr' => array( 'validate' => 'digit_or_csd', 'default' => '' ),
253 284 //TODO: this parameter does not transfer during saving, so here will be always default value 'bookings_only' // FixIn: 10.0.0.7.
@@ -254,24 +285,118 @@
254 285 'aggregate_type' => array( 'validate' => 'strong', 'default' => 'bookings_only' ), // Optional. 'all' | 'bookings_only' <- it is depends on shortcode parameter: options="{aggregate type=bookings_only}"
255 286 'is_approve_booking' => array( 'validate' => 'd', 'default' => 0 ), // 0 | 1
256 287 'save_booking_even_if_unavailable' => array( 'validate' => 'd', 'default' => 0 ), // 0 | 1
257 288 'sync_gid' => array( 'validate' => 'strong', 'default' => '' ),
258 - 'is_use_booking_recurrent_time' => array( 'validate' => 'd', 'default' => intval( ( 'On' === get_bk_option( 'booking_recurrent_time' ) ) ) )
259 - );
260 - $re_cleaned_params = wpbc_sanitize_params_in_arr( $request_params, $validate_arr_rules );
289 + 'is_use_booking_recurrent_time' => array( 'validate' => 'd', 'default' => intval( ( 'On' === get_bk_option( 'booking_recurrent_time' ) ) ) ),
261 290
262 - $admin_uri = ltrim( str_replace( get_site_url( null, '', 'admin' ), '', admin_url( 'admin.php?' ) ), '/' ); // wp-admin/admin.php?
291 + 'form_status' => array( 'validate' => 'strong', 'default' => 'published' ),
292 + 'wpbc_bfb_preview' => array( 'validate' => 'd', 'default' => 0 ),
293 + 'wpbc_bfb_preview_token' => array( 'validate' => 'strong', 'default' => '' ),
294 + 'wpbc_bfb_preview_form_id' => array( 'validate' => 'd', 'default' => 0 ),
295 + 'wpbc_bfb_preview_nonce' => array( 'validate' => 'strong', 'default' => '' ),
296 + 'wpbc_time_override_enabled' => array( 'validate' => 'd', 'default' => 0 ),
297 + 'wpbc_time_override_source' => array( 'validate' => 'strong', 'default' => '' ),
298 + 'wpbc_time_override_start' => array( 'validate' => 'strong', 'default' => '' ),
299 + 'wpbc_time_override_end' => array( 'validate' => 'strong', 'default' => '' ),
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' => '' );
308 + $re_cleaned_params = wpbc_sanitize_params_in_arr( $request_params, $validate_arr_rules );
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'] ) ) {
312 + $ajx_data_arr['status'] = 'error';
313 + $ajx_data_arr['status_error'] = 'appointment_service_required';
314 + $ajx_data_arr['ajx_after_action_message'] = __( 'Please select a Service.', 'booking' );
315 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
316 + return array( 'ajx_data' => $ajx_data_arr );
317 + }
318 + if ( ! empty( $re_cleaned_params['service_id'] ) ) {
319 + if ( ! function_exists( 'wpbc_booking_appointment_validate_submission_context' ) ) {
320 + $appointment_context_check = new WP_Error( 'appointment_context_unavailable', __( 'The Appointment selection cannot be verified. Please reload the page and try again.', 'booking' ) );
321 + } else {
322 + $appointment_context_check = wpbc_booking_appointment_validate_submission_context(
323 + $re_cleaned_params['appointment_context_token'],
324 + $re_cleaned_params['service_id'],
325 + $re_cleaned_params['resource_id']
326 + );
327 + }
328 + if ( is_wp_error( $appointment_context_check ) ) {
329 + $ajx_data_arr['status'] = 'error';
330 + $ajx_data_arr['status_error'] = $appointment_context_check->get_error_code();
331 + $ajx_data_arr['ajx_after_action_message'] = $appointment_context_check->get_error_message();
332 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
333 + return array( 'ajx_data' => $ajx_data_arr );
334 + }
335 + $has_verified_appointment_context = true;
336 +
337 + // A client value cannot enable past Appointment creation; trust only the site-authored signed context.
338 + $re_cleaned_params['allow_past'] = wpbc_booking_appointment_is_past_booking_enabled( $appointment_context_check ) ? 1 : 0;
339 + }
340 + if ( ! empty( $re_cleaned_params['resource_selector_required'] ) || ! empty( $re_cleaned_params['resource_selector_context_token'] ) ) {
341 + if ( ! function_exists( 'wpbc_booking_resource_selector_validate_submission_context' ) ) {
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 + } else {
344 + $resource_selector_context_check = wpbc_booking_resource_selector_validate_submission_context(
345 + $re_cleaned_params['resource_selector_context_token'],
346 + $re_cleaned_params['resource_id']
347 + );
348 + }
349 + if ( is_wp_error( $resource_selector_context_check ) ) {
350 + $ajx_data_arr['status'] = 'error';
351 + $ajx_data_arr['status_error'] = $resource_selector_context_check->get_error_code();
352 + $ajx_data_arr['ajx_after_action_message'] = $resource_selector_context_check->get_error_message();
353 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
354 + return array( 'ajx_data' => $ajx_data_arr );
355 + }
356 + $has_verified_resource_selector_context = true;
357 +
358 + // Trust only the site-authored signed selector context for public past bookings.
359 + $re_cleaned_params['allow_past'] = wpbc_booking_resource_selector_is_past_booking_enabled( $resource_selector_context_check ) ? 1 : 0;
360 + }
361 +
362 + $re_cleaned_params['form_status'] = sanitize_key( $re_cleaned_params['form_status'] );
363 + if ( 'preview' !== $re_cleaned_params['form_status'] ) {
364 + $re_cleaned_params['form_status'] = 'published';
365 + }
366 + // FixIn: 2026-02-05 - make preview/published available to form parsing/templates during this request.
367 + wpbc_set_request_form_context(
368 + array(
369 + 'form_status' => $re_cleaned_params['form_status'],
370 + 'user_id' => $re_cleaned_params['user_id'],
371 + 'wpbc_bfb_preview' => absint( $re_cleaned_params['wpbc_bfb_preview'] ),
372 + 'wpbc_bfb_preview_token' => sanitize_key( $re_cleaned_params['wpbc_bfb_preview_token'] ),
373 + 'wpbc_bfb_preview_form_id' => absint( $re_cleaned_params['wpbc_bfb_preview_form_id'] ),
374 + 'wpbc_bfb_preview_nonce' => (string) $re_cleaned_params['wpbc_bfb_preview_nonce'],
375 + )
376 + );
263 377
264 -
265 378 // -----------------------------------------------------------------------------------------------------------------
266 379 // Local parameters
267 380 // -----------------------------------------------------------------------------------------------------------------
268 - $local_params = array();
269 - $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;
270 384 $local_params['user_id'] = $re_cleaned_params['user_id']; // 1
271 - $local_params['sync_gid'] = $re_cleaned_params['sync_gid']; // ''
272 - $local_params['is_approve_booking'] = $re_cleaned_params['is_approve_booking']; // 0 | 1
273 - $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 );
274 399
275 400 // -----------------------------------------------------------------------------------------------------------------
276 401 // Parse Local parameters for later use
277 402 // -----------------------------------------------------------------------------------------------------------------
@@ -279,10 +404,91 @@
279 404 * Get parsed booking form: = [ name = "John", secondname = "Smith", email = "john.smith@server.com", visitors = "2",... ]
280 405 */
281 406 $local_params['structured_booking_data_arr'] = wpbc_get_parsed_booking_data_arr( $re_cleaned_params["form_data"], $re_cleaned_params["resource_id"], array( 'get' => 'value' ) );
282 407 $local_params['all_booking_data_arr'] = wpbc_get_parsed_booking_data_arr( $re_cleaned_params["form_data"], $re_cleaned_params["resource_id"] );
283 - // Important! : [ 64800, 72000 ]
284 - $local_params['time_as_seconds_arr'] = wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $local_params['structured_booking_data_arr'] );
408 + $local_params['time_override_arr'] = wpbc_get_booking_time_override__as_arr( $re_cleaned_params );
409 + if ( ! empty( $local_params['time_override_arr'] ) ) {
410 + unset( $local_params['structured_booking_data_arr']['rangetime'], $local_params['structured_booking_data_arr']['durationtime'] );
411 + $local_params['structured_booking_data_arr']['starttime'] = $local_params['time_override_arr']['start'];
412 + $local_params['structured_booking_data_arr']['endtime'] = $local_params['time_override_arr']['end'];
413 +
414 + unset( $local_params['all_booking_data_arr']['rangetime'], $local_params['all_booking_data_arr']['durationtime'] );
415 + $local_params['all_booking_data_arr']['starttime'] = array(
416 + 'type' => 'text',
417 + 'original_name' => 'starttime' . $re_cleaned_params['resource_id'],
418 + 'name' => 'starttime',
419 + 'value' => $local_params['time_override_arr']['start'],
420 + );
421 + $local_params['all_booking_data_arr']['endtime'] = array(
422 + 'type' => 'text',
423 + 'original_name' => 'endtime' . $re_cleaned_params['resource_id'],
424 + 'name' => 'endtime',
425 + 'value' => $local_params['time_override_arr']['end'],
426 + );
427 + }
428 + // Important! : [ 64800, 72000 ]
429 + $local_params['time_as_seconds_arr'] = wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $local_params['structured_booking_data_arr'] );
430 + $local_params['appointment_service'] = array();
431 + if ( ! empty( $re_cleaned_params['service_id'] ) && function_exists( 'wpbc_appointment_services_repository' ) ) {
432 + $range_time_value = isset( $local_params['structured_booking_data_arr']['rangetime'] ) ? $local_params['structured_booking_data_arr']['rangetime'] : '';
433 + $start_time_value = isset( $local_params['structured_booking_data_arr']['starttime'] ) ? $local_params['structured_booking_data_arr']['starttime'] : '';
434 + $range_time_value = is_array( $range_time_value ) ? implode( '', $range_time_value ) : $range_time_value;
435 + $start_time_value = is_array( $start_time_value ) ? implode( '', $start_time_value ) : $start_time_value;
436 + $has_appointment_time = ! empty( $local_params['time_override_arr'] )
437 + || '' !== trim( (string) $range_time_value )
438 + || '' !== trim( (string) $start_time_value );
439 + if ( ! $has_appointment_time ) {
440 + $ajx_data_arr['status'] = 'error';
441 + $ajx_data_arr['status_error'] = 'appointment_service_time_required';
442 + $ajx_data_arr['ajx_after_action_message'] = __( 'A Service appointment requires a start time. Add a time field to the Booking Form and select a time.', 'booking' );
443 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
444 + return array( 'ajx_data' => $ajx_data_arr );
445 + }
446 + $appointment_service = wpbc_appointment_services_repository()->find_active_for_resource( $re_cleaned_params['service_id'], $re_cleaned_params['resource_id'] );
447 + if ( is_wp_error( $appointment_service ) ) {
448 + $ajx_data_arr['status'] = 'error';
449 + $ajx_data_arr['status_error'] = 'appointment_service_unavailable';
450 + $ajx_data_arr['ajx_after_action_message'] = $appointment_service->get_error_message();
451 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
452 + return array( 'ajx_data' => $ajx_data_arr );
453 + }
454 + if ( count( $local_params['time_as_seconds_arr'] ) < 2 || ! function_exists( 'wpbc_appointment_services_resolve_end_seconds' ) ) {
455 + $ajx_data_arr['status'] = 'error';
456 + $ajx_data_arr['status_error'] = 'appointment_service_duration_invalid';
457 + $ajx_data_arr['ajx_after_action_message'] = __( 'The selected Service duration is invalid. Please contact the website administrator.', 'booking' );
458 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
459 + return array( 'ajx_data' => $ajx_data_arr );
460 + }
461 + $maximum_duration_minutes = absint( apply_filters( 'wpbc_booking_appointment_maximum_duration_minutes', 24 * 60, array() ) );
462 + $service_end_second = wpbc_appointment_services_resolve_end_seconds( $appointment_service, $local_params['time_as_seconds_arr'][0], $maximum_duration_minutes );
463 + if ( is_wp_error( $service_end_second ) ) {
464 + $ajx_data_arr['status'] = 'error';
465 + $ajx_data_arr['status_error'] = $service_end_second->get_error_code();
466 + $ajx_data_arr['ajx_after_action_message'] = $service_end_second->get_error_message();
467 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
468 + return array( 'ajx_data' => $ajx_data_arr );
469 + }
470 + $local_params['time_as_seconds_arr'][1] = $service_end_second;
471 + $local_params['appointment_service'] = $appointment_service;
472 + $service_start_time = wpbc_transform__seconds__in__24_hours_his( $local_params['time_as_seconds_arr'][0] );
473 + $service_end_time = wpbc_transform__seconds__in__24_hours_his( $local_params['time_as_seconds_arr'][1] );
474 + unset( $local_params['structured_booking_data_arr']['rangetime'], $local_params['structured_booking_data_arr']['durationtime'] );
475 + $local_params['structured_booking_data_arr']['starttime'] = $service_start_time;
476 + $local_params['structured_booking_data_arr']['endtime'] = $service_end_time;
477 + unset( $local_params['all_booking_data_arr']['rangetime'], $local_params['all_booking_data_arr']['durationtime'] );
478 + $local_params['all_booking_data_arr']['starttime'] = array( 'type' => 'text', 'original_name' => 'starttime' . $re_cleaned_params['resource_id'], 'name' => 'starttime', 'value' => $service_start_time );
479 + $local_params['all_booking_data_arr']['endtime'] = array( 'type' => 'text', 'original_name' => 'endtime' . $re_cleaned_params['resource_id'], 'name' => 'endtime', 'value' => $service_end_time );
480 + }
481 + if ( function_exists( 'wpbc_appointment_services_sync_service_hint_booking_data' ) ) {
482 + $service_hint_booking_data = wpbc_appointment_services_sync_service_hint_booking_data(
483 + $local_params['structured_booking_data_arr'],
484 + $local_params['all_booking_data_arr'],
485 + $local_params['appointment_service'],
486 + $re_cleaned_params['resource_id']
487 + );
488 + $local_params['structured_booking_data_arr'] = $service_hint_booking_data['structured_booking_data'];
489 + $local_params['all_booking_data_arr'] = $service_hint_booking_data['all_booking_data'];
490 + }
285 491 // [ "18:00:00", "20:00:00" ]
286 492 $time_as_seconds_arr = $local_params['time_as_seconds_arr'];
287 493 $time_as_seconds_arr[0] = ( 0 != $time_as_seconds_arr[0] ) ? $time_as_seconds_arr[0] + 1 : $time_as_seconds_arr[0]; // set check in time with ended 1 second
288 494 $time_as_seconds_arr[1] = ( ( 24 * 60 * 60 ) != $time_as_seconds_arr[1] ) ? $time_as_seconds_arr[1] + 2 : $time_as_seconds_arr[1]; // set check out time with ended 2 seconds
@@ -294,19 +500,77 @@
294 500 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[0] ),
295 501 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[1] )
296 502 );
297 503 // [ '2023-09-10', '2023-09-11' ]
298 - $local_params['dates_only_sql_arr'] = wpbc_convert_dates_str__dd_mm_yyyy__to__yyyy_mm_dd( $re_cleaned_params["dates_ddmmyy_csv"] );
299 - $local_params['dates_only_sql_arr'] = explode( ',', $local_params['dates_only_sql_arr'] );
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 (
555 + ( ! empty( $local_params['time_override_arr'] ) )
556 + && ( 'times_availability' === $local_params['time_override_arr']['source'] )
557 + && ( count( array_filter( $local_params['dates_only_sql_arr'] ) ) > 1 )
558 + ) {
559 + $local_params['is_use_booking_recurrent_time'] = true;
560 + }
300 561
301 562 $local_params['is_show_payment_form'] = $re_cleaned_params["is_show_payment_form"];
302 563
303 564 // FixIn: 9.9.0.35.
304 - if ( $local_params['is_show_payment_form'] ) {
305 - $local_params['is_show_payment_form'] = ( false !== strpos( $re_cleaned_params['request_uri'], 'is_show_payment_form=Off' ) )
306 - ? 0
307 - : $local_params['is_show_payment_form']; // 1|0
308 - }
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 + }
309 573
310 574 // Get EDIT booking data
311 575 $local_params['edit_resource_id'] = '';
312 576 $local_params['skip_booking_id'] = '';
@@ -312,27 +576,44 @@
312 576 $local_params['skip_booking_id'] = '';
313 577 $local_params['is_edit_booking'] = 0;
314 578 $local_params['is_duplicate_booking'] = 0;
315 579 $is_edit_booking = wpbc_get_data__if_edit_booking( $re_cleaned_params['booking_hash'], $re_cleaned_params['request_uri'] );
316 - if ( false !== $is_edit_booking ) {
317 - $local_params['edit_resource_id'] = $is_edit_booking['resource_id']; // can be parent booking resource, where we edit the booking
318 - $local_params['skip_booking_id'] = $is_edit_booking['booking_id']; // booking ID
319 - $local_params['is_edit_booking'] = $is_edit_booking['booking_id']; // booking ID
580 + if ( false !== $is_edit_booking ) {
581 + $local_params['edit_resource_id'] = $is_edit_booking['resource_id']; // can be parent booking resource, where we edit the booking
582 + $local_params['skip_booking_id'] = $is_edit_booking['booking_id']; // booking ID
583 + $local_params['is_edit_booking'] = $is_edit_booking['booking_id']; // booking ID
320 584 if (
321 585 ( ! empty( $local_params['structured_booking_data_arr']['wpbc_other_action'] ) )
322 586 && ( 'duplicate_booking' === $local_params['structured_booking_data_arr']['wpbc_other_action'] )
323 587 ){
324 - $local_params['is_duplicate_booking'] = 1;
325 - }
326 - }
327 - // It can be request resource ID or if we edit booking, it can be 'edit resource' - (e.g. child resource)
588 + $local_params['is_duplicate_booking'] = 1;
589 + }
590 + }
591 +
592 + $is_frontend_ajax_edit = wp_doing_ajax()
593 + && 'wpbc_ajx_booking__create' === strtolower( $request_action )
594 + && 0 !== $local_params['is_edit_booking'];
595 + $is_authorized_admin_edit = $is_authorized_admin_booking_request;
596 +
597 + if (
598 + $is_frontend_ajax_edit
599 + && ! $is_authorized_admin_edit
600 + && ! wpbc_is_visitor_booking_action_allowed( $local_params['is_edit_booking'] )
601 + ) {
602 + $ajx_data_arr['status'] = 'error';
603 + $ajx_data_arr['status_error'] = 'visitor_booking_dates_in_past';
604 + $ajx_data_arr['ajx_after_action_message'] = __( 'This booking can no longer be edited because its dates have already passed.', 'booking' );
605 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
606 + return array( 'ajx_data' => $ajx_data_arr );
607 + }
608 + // It can be request resource ID or if we edit booking, it can be 'edit resource' - (e.g. child resource)
328 609 $local_params['initial_resource_id'] = ( ! empty( $local_params['edit_resource_id'] ) ) ? $local_params['edit_resource_id'] : $re_cleaned_params['resource_id'];
329 610
330 - // 2
331 - $local_params['how_many_items_to_book'] = wpbc_get__how_many_items_to_book__in_booking_form( $local_params['structured_booking_data_arr'], $local_params['initial_resource_id'] );
332 -
333 -
334 - $local_params['aggregate_resource_id_arr'] = explode( ',', $re_cleaned_params['aggregate_resource_id_arr'] );
611 + // 2
612 + $local_params['how_many_items_to_book'] = wpbc_get__how_many_items_to_book__in_booking_form( $local_params['structured_booking_data_arr'], $local_params['initial_resource_id'] );
613 +
614 +
615 + $local_params['aggregate_resource_id_arr'] = explode( ',', $re_cleaned_params['aggregate_resource_id_arr'] );
335 616 $local_params['aggregate_resource_id_arr'] = array_filter( $local_params['aggregate_resource_id_arr'] ); // All entries of array equal to FALSE (0, '', '0' ) will be removed.
336 617 $local_params['aggregate_resource_id_arr'] = array_unique( $local_params['aggregate_resource_id_arr'] ); // Erase duplicates
337 618
338 619 // -----------------------------------------------------------------------------------------------------------------
@@ -355,9 +636,9 @@
355 636 $where_to_save_booking['main__resource_id'] = $local_params['initial_resource_id']; // here edit or request (parent/single) resource
356 637
357 638 } else {
358 639 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
359 - $php_performance = php_performance_START( 'wpbc__where_to_save_booking' , $php_performance );
640 + $php_performance = wpbc_php_performance_START( 'wpbc__where_to_save_booking' , $php_performance );
360 641 // </editor-fold>
361 642
362 643 /**
363 644 * Get slots [] where we can save booking = [ 'resources_in_dates' => [ 2023-10-18 = [ 2, 12, 10, 11 ]
@@ -377,9 +658,11 @@
377 658 'dates_only_sql_arr' => $local_params['dates_only_sql_arr'], // [ "2023-10-18", "2023-10-25", "2023-11-25" ]
378 659 'time_as_seconds_arr' => $local_params['time_as_seconds_arr'], // [ 36000, 39600 ]
379 660 'how_many_items_to_book' => $local_params['how_many_items_to_book'], // 1
380 661 'request_uri' => $re_cleaned_params['request_uri'], // 'http://beta/resource-id2/'
662 + 'allow_past' => ! empty( $re_cleaned_params['allow_past'] ),
381 663 'is_use_booking_recurrent_time' => $local_params['is_use_booking_recurrent_time'], // true | false
664 + 'time_override_source' => ! empty( $local_params['time_override_arr']['source'] ) ? $local_params['time_override_arr']['source'] : '',
382 665 'as_single_resource' => false, // false
383 666 'aggregate_resource_id_arr' => $local_params['aggregate_resource_id_arr'], // Optional can be ''
384 667 'aggregate_type' => $re_cleaned_params['aggregate_type'], //TODO: this parameter does not transfer during saving, so here will be always default value 'bookings_only' // FixIn: 10.0.0.7.
385 668 'custom_form' => $re_cleaned_params['custom_form'] // FixIn: 10.0.0.10.
@@ -394,14 +677,31 @@
394 677 }
395 678 // </editor-fold>
396 679
397 680 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
398 - $php_performance = php_performance_END( 'wpbc__where_to_save_booking' , $php_performance );
681 + $php_performance = wpbc_php_performance_END( 'wpbc__where_to_save_booking' , $php_performance );
399 682 // </editor-fold>
400 - }
401 -
402 -
403 - // Get parameters, from REQUEST
683 + }
684 +
685 + if ( ! empty( $local_params['appointment_service'] ) && function_exists( 'wpbc_appointment_services_check_buffer_conflicts' ) ) {
686 + $buffer_check = wpbc_appointment_services_check_buffer_conflicts(
687 + $local_params['appointment_service'],
688 + $where_to_save_booking['main__resource_id'],
689 + array_keys( $where_to_save_booking['resources_in_dates'] ),
690 + $local_params['time_as_seconds_arr'],
691 + $local_params['skip_booking_id']
692 + );
693 + if ( is_wp_error( $buffer_check ) ) {
694 + $ajx_data_arr['status'] = 'error';
695 + $ajx_data_arr['status_error'] = 'appointment_service_buffer_conflict';
696 + $ajx_data_arr['ajx_after_action_message'] = $buffer_check->get_error_message();
697 + $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
698 + return array( 'ajx_data' => $ajx_data_arr );
699 + }
700 + }
701 +
702 +
703 + // Get parameters, from REQUEST
404 704 $create_params = $local_params;
405 705 $create_params['resource_id'] = ( ! empty( $local_params['edit_resource_id'] ) )
406 706 ? $local_params['edit_resource_id'] // If we edit, then use original resource ???
407 707 : $where_to_save_booking['main__resource_id']; // Here is important TIP, resource can be where is free, and not where we submit
@@ -420,15 +720,15 @@
420 720
421 721 make_bk_action( 'check_multiuser_params_for_client_side', $create_params['resource_id'] ); // Activate working with specific user in WP MU
422 722
423 723 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
424 - $php_performance = php_performance_START( 'wpbc_db__booking_save' , $php_performance );
724 + $php_performance = wpbc_php_performance_START( 'wpbc_db__booking_save' , $php_performance );
425 725 // </editor-fold>
426 726
427 727 // -----------------------------------------------------------------------------------------------------------------
428 728 // == CREATE_THE 'NEW_BOOKING' ==
429 729 // -----------------------------------------------------------------------------------------------------------------
430 - $create_booking_params = array(
730 + $create_booking_params = array(
431 731 'resource_id' => $create_params['resource_id'],
432 732 'custom_form' => $create_params['custom_form'],
433 733 'all_booking_data_arr' => $create_params['all_booking_data_arr'],
434 734 'dates_only_sql_arr' => $create_params['dates_only_sql_arr'],
@@ -437,23 +737,26 @@
437 737 'is_edit_booking' => $create_params['is_edit_booking'],
438 738 'is_duplicate_booking' => $create_params['is_duplicate_booking'],
439 739 'is_approve_booking' => $create_params['is_approve_booking'],
440 740 'how_many_items_to_book' => $create_params['how_many_items_to_book'],
441 - 'is_use_booking_recurrent_time' => $create_params['is_use_booking_recurrent_time'] // true | false
442 - );
741 + 'is_use_booking_recurrent_time' => $create_params['is_use_booking_recurrent_time'] // true | false
742 + );
743 + if ( ! empty( $create_params['appointment_service'] ) ) { $create_booking_params['appointment_service'] = $create_params['appointment_service']; }
443 744 if ( ! empty( $create_params['sync_gid'] ) ) { $create_booking_params['sync_gid'] = $create_params['sync_gid']; }
444 745
445 746 $booking_new_arr = wpbc_db__booking_save( $create_booking_params, $where_to_save_booking );
446 747
447 748 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING CREATION " >
448 - if ( 'ok' !== $booking_new_arr['status'] ) {
749 + if ( 'ok' !== $booking_new_arr['status'] ) {
449 750 $ajx_data_arr['status'] = $booking_new_arr['status'];
450 751 $ajx_data_arr['status_error'] = 'booking_can_not_save';
451 752 $ajx_data_arr['ajx_after_action_message'] = $booking_new_arr['message'];
452 753 $ajx_data_arr['ajx_after_action_message_status'] = 'error';
453 754 return array( 'ajx_data' => $ajx_data_arr );
454 - }
455 - // </editor-fold>
755 + }
756 + // </editor-fold>
757 +
758 + do_action( 'wpbc_booking_after_save', $booking_new_arr['booking_id'], $create_booking_params, $where_to_save_booking );
456 759
457 760 // FixIn: 9.9.0.36.
458 761 if (
459 762 ( 0 !== $create_params['is_edit_booking'] ) // If edit booking
@@ -465,9 +768,9 @@
465 768 $booking_note .= __( 'The booking has been edited', 'booking' ) . '. | Edit URL: ' . esc_url_raw( $re_cleaned_params['request_uri'] ) . '';
466 769 make_bk_action( 'wpdev_make_update_of_remark', $booking_new_arr['booking_id'], $booking_note, true );
467 770 }
468 771 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
469 - $php_performance = php_performance_END( 'wpbc_db__booking_save' , $php_performance );
772 + $php_performance = wpbc_php_performance_END( 'wpbc_db__booking_save' , $php_performance );
470 773 // </editor-fold>
471 774
472 775 // -----------------------------------------------------------------------------------------------------------------
473 776 // Get payment form(s) and Update COST of the booking
@@ -481,9 +784,10 @@
481 784 );
482 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']
483 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'
484 787 $payment_params['booking_id'] = $booking_new_arr['booking_id']; // REQUIRED -- '2'
485 - $payment_params['resource_id'] = $create_params['resource_id']; // REQUIRED -- '2' can be child resource (changed in wpbc_where_to_save() )
788 + $payment_params['resource_id'] = $create_params['resource_id']; // REQUIRED -- '2' can be child resource (changed in wpbc_where_to_save() )
789 + $payment_params['service_id'] = ! empty( $create_params['appointment_service']['service_id'] ) ? absint( $create_params['appointment_service']['service_id'] ) : 0;
486 790 $payment_params['initial_resource_id'] = $local_params['initial_resource_id']; // REQUIRED -- '2' initial calendar - parent resource
487 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...'
488 792 $payment_params['times_array'] = array(
489 793 explode( ':', $where_to_save_booking['time_to_book'][0] ), // ["10","00","00"]
@@ -493,14 +797,15 @@
493 797 $payment_params['is_edit_booking'] = $create_params['is_edit_booking']; // => 0 0 | int - ID of the booking
494 798 $payment_params['custom_form'] = $create_params['custom_form']; // => '' '' | 'some_name'
495 799 $payment_params['is_duplicate_booking'] = $create_params['is_duplicate_booking']; // => 0 0 | 1
496 800 $payment_params['is_from_admin_panel'] = $create_params['is_from_admin_panel']; // => false true | false
497 - $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'];
498 803 if ( $payment_params['is_from_admin_panel'] ) {
499 804 // $payment_params['is_show_payment_form'] = 0; // FixIn: 9.9.0.21.
500 805 }
501 806 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
502 - $php_performance = php_performance_START( 'wpbc_maybe_get_payment_form' , $php_performance );
807 + $php_performance = wpbc_php_performance_START( 'wpbc_maybe_get_payment_form' , $php_performance );
503 808 // </editor-fold>
504 809
505 810 // GET PAYMENT FORMS ===============================================================================================
506 811 if ( function_exists( 'wpbc_maybe_get_payment_form' ) ) {
@@ -537,11 +842,11 @@
537 842 }
538 843
539 844
540 845 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
541 - $php_performance = php_performance_END( 'wpbc_maybe_get_payment_form' , $php_performance );
846 + $php_performance = wpbc_php_performance_END( 'wpbc_maybe_get_payment_form' , $php_performance );
542 847
543 - $php_performance = php_performance_START( 'emails_sending' , $php_performance );
848 + $php_performance = wpbc_php_performance_START( 'emails_sending' , $php_performance );
544 849 // </editor-fold>
545 850
546 851 // -----------------------------------------------------------------------------------------------------------------
547 852 // == Emails ===
@@ -570,8 +875,10 @@
570 875 // New approved to Visitor / Admin
571 876 wpbc_send_email_approved( $payment_params['booking_id'], 1 );
572 877 }
573 878
879 + do_action( 'wpbc_booking_is_approved_during_creation' , $payment_params['booking_id'] , (int) $is_booking_approved ); // FixIn: 10.10.1.1.
880 +
574 881 // Payment request from admin panel, if needed
575 882 if(
576 883 ( $payment_params['is_from_admin_panel'] )
577 884 && ( 'On' == get_bk_option( 'booking_payment_request_auto_send_in_bap' ) )
@@ -580,10 +887,10 @@
580 887 $payment_reason = '';
581 888 $is_send = wpbc_send_email_payment_request( $payment_params['booking_id'], $payment_params['resource_id'], $email_content , $payment_reason );
582 889 }
583 890
584 - do_action( 'wpbc_booking_approved' , $payment_params['booking_id'] , (int) $is_booking_approved );
585 891
892 +
586 893 } else {
587 894
588 895 // Edited booking to Visitor / Admin
589 896 if ( function_exists( 'wpbc_send_email_modified' ) ) {
@@ -605,9 +912,9 @@
605 912 }
606 913 }
607 914
608 915 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
609 - $php_performance = php_performance_END( 'emails_sending' , $php_performance );
916 + $php_performance = wpbc_php_performance_END( 'emails_sending' , $php_performance );
610 917 // </editor-fold>
611 918
612 919 // -----------------------------------------------------------------------------------------------------------------
613 920 // == Track booking - New | Edit ===
@@ -679,9 +986,9 @@
679 986 }
680 987
681 988
682 989 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
683 - $php_performance = php_performance_START( 'confirmation' , $php_performance );
990 + $php_performance = wpbc_php_performance_START( 'confirmation' , $php_performance );
684 991 // </editor-fold>
685 992
686 993 // <editor-fold defaultstate="collapsed" desc=" == Confirmation data == " >
687 994
@@ -718,9 +1025,9 @@
718 1025 ( 0 !== $local_params['is_edit_booking'] )
719 1026 // && ( empty( $local_params['is_duplicate_booking'] ) )
720 1027 ){
721 1028 $confirmation_params_arr['ty_is_redirect'] = 'page';
722 - $confirmation_params_arr['ty_url'] = wpbc_get_bookings_url() . '&view_mode=vm_listing&tab=actions&wh_booking_id=' . $confirmation_params_arr['booking_id'];
1029 + $confirmation_params_arr['ty_url'] = wpbc_get_bookings_url() . '&tab=vm_booking_listing&wh_booking_id=' . $confirmation_params_arr['booking_id'];
723 1030 }
724 1031 }
725 1032 $confirmation = wpbc_booking_confirmation( $confirmation_params_arr );
726 1033
@@ -726,9 +1033,9 @@
726 1033
727 1034 // </editor-fold>
728 1035
729 1036 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
730 - $php_performance = php_performance_END( 'confirmation' , $php_performance );
1037 + $php_performance = wpbc_php_performance_END( 'confirmation' , $php_performance );
731 1038 // </editor-fold>
732 1039
733 1040
734 1041 make_bk_action( 'finish_check_multiuser_params_for_client_side', $create_params['resource_id'] ); // Deactivate working with specific user in WP MU
@@ -734,9 +1041,9 @@
734 1041 make_bk_action( 'finish_check_multiuser_params_for_client_side', $create_params['resource_id'] ); // Deactivate working with specific user in WP MU
735 1042
736 1043
737 1044 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
738 - $php_performance = php_performance_END( 'total' , $php_performance );
1045 + $php_performance = wpbc_php_performance_END( 'total' , $php_performance );
739 1046 $php_performance['other_code'] = - 1 * array_reduce( $php_performance,
740 1047 function ( $sum, $item ) {
741 1048 $sum += $item;
742 1049 return $sum;
@@ -743,8 +1050,9 @@
743 1050 }
744 1051 , - 2 * $php_performance['total'] ); // PERFORMANCE OTHER - after TOTAL
745 1052 // </editor-fold>
746 1053
1054 + wpbc_clear_request_form_context();
747 1055
748 1056 return array( 'ajx_data' => $ajx_data_arr, // [ 'status' => "ok", 'wpbc_payment_output' => "<p>Dear John<br..." ]
749 1057 'booking_id' => $booking_new_arr['booking_id'], // 254
750 1058 'booking_arr' => $payment_params,
@@ -799,10 +1107,10 @@
799 1107 * 'message' => '' 'If error, then here can be description of error'
800 1108 * 'form_data' => If 'ok' form data can be different here, 'custom_form' parameter, so it can add 'wpbc_custom_booking_form' field for identification, what custom booking form was used,
801 1109 * ]
802 1110 */
803 -function wpbc_db__booking_save( $create_params, $where_to_save_booking ) {
804 -
1111 +function wpbc_db__booking_save( &$create_params, &$where_to_save_booking ) {
1112 + //FixIn: 10.11.5.4
805 1113 /**
806 1114 * Tip: $create_params['all_booking_data_arr'] - contain: [ 'field_name' => [ 'type' = "checkbox", 'original_name' = "fixed_fee2[]", 'name' = "fixed_fee", 'value' = "true" ] , ... ]
807 1115 * $create_params['structured_booking_data_arr'] - contain: [ 'field_name' => 'field_value' , ... ]
808 1116 */
@@ -866,9 +1174,13 @@
866 1174 return array( 'status' => 'error', 'message' => 'Sent request with no dates.' );
867 1175 }
868 1176
869 1177 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- CHECK_IN_DATE_OLDER_THAN_CHECK_OUT " >
870 - if ( count( $create_params['dates_only_sql_arr'] ) == 1 ) { // Is it single selected date ?
1178 + $is_no_dates_booking = (
1179 + function_exists( 'wpbc_is_these_dates__for__no_dates' )
1180 + && wpbc_is_these_dates__for__no_dates( $create_params['dates_only_sql_arr'] )
1181 + );
1182 + if ( ( count( $create_params['dates_only_sql_arr'] ) == 1 ) && ( ! $is_no_dates_booking ) ) { // Is it single selected date ?
871 1183
872 1184 // Is 'check in' date/time older than 'check out' date/time when SINGLE day for booking? Then show error.
873 1185
874 1186 /**
@@ -954,9 +1266,9 @@
954 1266 $sql_field_arr[] = array( 'name' => 'form', 'type' => '%s', 'value' => $form_data );
955 1267 $sql_field_arr[] = array( 'name' => 'booking_type', 'type' => '%d', 'value' => $create_params['resource_id'] );
956 1268 $sql_field_arr[] = array( 'name' => 'modification_date', 'type' => '%s', 'value' => gmdate( 'Y-m-d H:i:s' ) );
957 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] );
958 - $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() );
959 1271
960 1272
961 1273 if (
962 1274 ( 0 == $create_params['is_edit_booking'] ) || // If not edit, then INSERT.
@@ -977,12 +1289,15 @@
977 1289 $sql_prepare_arr['name'] = implode( ', ', $sql_prepare_arr['name'] );
978 1290 $sql_prepare_arr['type'] = implode( ', ', $sql_prepare_arr['type'] );
979 1291 /* phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare */
980 1292 $sql = $wpdb->prepare( "INSERT INTO {$wpdb->prefix}booking " . " ( {$sql_prepare_arr['name']} )" . " VALUES ( {$sql_prepare_arr['type']} )", $sql_prepare_arr['value'] );
981 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
982 - if ( false === $wpdb->query( $sql ) ) {
983 - return array( 'status' => 'error', 'message' => 'Error. INSERT New Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql );
984 - }
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 + }
985 1300 // Get ID of booking
986 1301 $booking_id = (int) $wpdb->insert_id;
987 1302
988 1303 } else { // Edit - UPDATE
@@ -996,24 +1311,25 @@
996 1311 $sql_prepare_arr['set'] = implode( ', ', $sql_prepare_arr['set'] );
997 1312
998 1313 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
999 1314 $sql = $wpdb->prepare( "UPDATE {$wpdb->prefix}booking SET {$sql_prepare_arr['set']} WHERE booking_id={$booking_id};", $sql_prepare_arr['value'] );
1000 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
1001 - if ( false === $wpdb->query( $sql ) ) {
1002 - return array( 'status' => 'error',
1003 - 'message' => 'Error. UPDATE Exist Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql,
1004 - );
1005 - }
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 + }
1006 1322
1007 1323 // Check if dates previously was approved.
1008 1324 $slct_sql = "SELECT approved FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id}) LIMIT 0,1";
1009 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
1325 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1010 1326 $slct_sql_results = $wpdb->get_results( $slct_sql );
1011 1327 $is_approved_dates = ( count( $slct_sql_results ) > 0 ) ? $slct_sql_results[0]->approved : $is_approved_dates;
1012 1328
1013 1329
1014 1330 $delete_sql = "DELETE FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id})";
1015 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
1331 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1016 1332 if ( false === $wpdb->query( $delete_sql ) ) {
1017 1333 return array( 'status' => 'error', 'message' => 'Error. DELETE Old Dates in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $delete_sql );
1018 1334 }
1019 1335 }
@@ -1102,9 +1418,9 @@
1102 1418 }
1103 1419 }
1104 1420
1105 1421 $dates_sql .= implode( ', ', $insert_dates_arr );
1106 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
1422 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1107 1423 if ( false === $wpdb->query( $dates_sql ) ) {
1108 1424 return array( 'status' => 'error', 'message' => 'Error. INSERT "D A T E S" in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $dates_sql );
1109 1425 }
1110 1426
@@ -1148,9 +1464,24 @@
1148 1464
1149 1465 return $insert_dates_arr;
1150 1466 }
1151 1467
1468 +// == Help functions ==
1469 +function wpbc_set_request_form_context( $ctx ) {
1470 + $GLOBALS['wpbc_request_form_context'] = ( is_array( $ctx ) ) ? $ctx : array();
1471 +}
1152 1472
1473 +function wpbc_get_request_form_context() {
1474 + return ( isset( $GLOBALS['wpbc_request_form_context'] ) && is_array( $GLOBALS['wpbc_request_form_context'] ) )
1475 + ? $GLOBALS['wpbc_request_form_context'] : array();
1476 +}
1477 +
1478 +function wpbc_clear_request_form_context() {
1479 + if ( isset( $GLOBALS['wpbc_request_form_context'] ) ) {
1480 + unset( $GLOBALS['wpbc_request_form_context'] );
1481 + }
1482 +}
1483 +
1153 1484 // ---------------------------------------------------------------------------------------------------------------------
1154 1485 // Support
1155 1486 // ---------------------------------------------------------------------------------------------------------------------
1156 1487
@@ -1174,9 +1505,10 @@
1174 1505 $is_edit_booking['booking_id'] = intval( $my_booking_id_type[0] );
1175 1506 $is_edit_booking['resource_id'] = intval( $my_booking_id_type[1] );
1176 1507
1177 1508 //TODO: test it. Check situation when we have editing "child booking resource", so need to re-update calendar and form to have it for parent resource. // FixIn: 6.1.1.9.
1178 - if ( strpos( $server_request_url, 'resource_no_update' ) === false ) { // FixIn: 9.4.2.3.
1509 + // FixIn: 10.10.1.2
1510 + //if ( strpos( $server_request_url, 'resource_no_update' ) === false ) { // FixIn: 9.4.2.3.
1179 1511
1180 1512 if ( ( function_exists( 'wpbc_is_this_child_resource' ) ) && ( wpbc_is_this_child_resource( $is_edit_booking['resource_id'] ) ) ) {
1181 1513 $bk_parent_br_id = wpbc_get_parent_resource( $is_edit_booking['resource_id'] );
1182 1514
@@ -1181,9 +1513,9 @@
1181 1513 $bk_parent_br_id = wpbc_get_parent_resource( $is_edit_booking['resource_id'] );
1182 1514
1183 1515 $is_edit_booking['resource_id'] = intval( $bk_parent_br_id );
1184 1516 }
1185 - }
1517 + //}
1186 1518 }
1187 1519 }
1188 1520 return $is_edit_booking;
1189 1521 }
@@ -1211,9 +1543,9 @@
1211 1543 function wpbc_get__how_many_items_to_book__in_booking_form( $booking_form_data__arr, $resource_id ){
1212 1544
1213 1545 $how_many_items_to_book = 1;
1214 1546
1215 - //TODO: Check about some URL parameter: '&resource_no_update' to book parent resource as single resource!
1547 + //TODO: Check about some URL parameter: '&resource_no_update' to book parent resource as single resource! // FixIn: 10.10.1.2
1216 1548 if (
1217 1549 ( class_exists( 'wpdev_bk_biz_l' ) )
1218 1550 && ( 0 !== wpbc_get_child_resources_number( $resource_id ) ) // Here several child booking resources
1219 1551 ) {
@@ -1278,9 +1610,9 @@
1278 1610 *
1279 1611 * // Now get start/end times as seconds: [ 64800, 72000 ]
1280 1612 * $time_as_seconds_arr = wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $structured_booking_data_arr );
1281 1613 */
1282 - 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 ){
1283 1615
1284 1616 $selected_time_fields = wpbc_get__selected_time_fields__in_booking_form__as_arr( $booking_form_data__arr );
1285 1617
1286 1618 // 2.2 Get selected SECONDS to book ---------------------------------------------------------------------------
@@ -1310,14 +1642,236 @@
1310 1642 foreach ( $selected_time_fields as $time_fields_obj ) { // { times_as_seconds: [ 21600 ], value_option_24h: '06:00', name: 'durationtime'}
1311 1643
1312 1644 if ( false !== strpos( $time_fields_obj['name'], 'durationtime' ) ) {
1313 1645 $time_as_seconds_arr[ 1 ] = $time_as_seconds_arr[ 0 ] + $time_fields_obj['times_as_seconds'][ 0 ];
1646 + // FixIn: 10.14.7.1.
1647 + while ( $time_as_seconds_arr[1] > ( 24 * 60 * 60 ) ) {
1648 + $time_as_seconds_arr[1] = $time_as_seconds_arr[1] - ( 24 * 60 * 60 );
1649 + }
1314 1650 break;
1315 1651 }
1316 1652 }
1317 1653 }
1318 1654
1319 - return $time_as_seconds_arr;
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.
1806 + *
1807 + * @param array $request_params Sanitized booking request params.
1808 + *
1809 + * @return array Empty array or array with start/end HH:MM values.
1810 + */
1811 + function wpbc_get_booking_time_override__as_arr( $request_params ) {
1812 +
1813 + if ( empty( $request_params['wpbc_time_override_enabled'] ) ) {
1814 + return array();
1815 + }
1816 +
1817 + $start_time = wpbc_sanitize_booking_time_override__hm( isset( $request_params['wpbc_time_override_start'] ) ? $request_params['wpbc_time_override_start'] : '' );
1818 + $end_time = wpbc_sanitize_booking_time_override__hm( isset( $request_params['wpbc_time_override_end'] ) ? $request_params['wpbc_time_override_end'] : '' );
1819 +
1820 + if (
1821 + ( '' === $start_time )
1822 + || ( '' === $end_time )
1823 + || ( wpbc_booking_time_override__hm_to_seconds( $start_time ) >= wpbc_booking_time_override__hm_to_seconds( $end_time ) )
1824 + ) {
1825 + return array();
1826 + }
1827 +
1828 + return array(
1829 + 'start' => $start_time,
1830 + 'end' => $end_time,
1831 + 'source' => isset( $request_params['wpbc_time_override_source'] ) ? sanitize_key( $request_params['wpbc_time_override_source'] ) : '',
1832 + );
1833 + }
1834 +
1835 +
1836 + /**
1837 + * Sanitize HH:MM value for admin booking time override.
1838 + *
1839 + * @param string $time_value Time value.
1840 + *
1841 + * @return string
1842 + */
1843 + function wpbc_sanitize_booking_time_override__hm( $time_value ) {
1844 +
1845 + $time_value = trim( sanitize_text_field( (string) $time_value ) );
1846 +
1847 + if ( ! preg_match( '/^([0-9]{1,2}):([0-9]{2})$/', $time_value, $matches ) ) {
1848 + return '';
1849 + }
1850 +
1851 + $hour = absint( $matches[1] );
1852 + $minute = absint( $matches[2] );
1853 +
1854 + if ( $hour > 24 || $minute > 59 || ( 24 === $hour && 0 !== $minute ) ) {
1855 + return '';
1856 + }
1857 +
1858 + return sprintf( '%02d:%02d', $hour, $minute );
1859 + }
1860 +
1861 +
1862 + /**
1863 + * Convert HH:MM to seconds.
1864 + *
1865 + * @param string $time_value Time value.
1866 + *
1867 + * @return int
1868 + */
1869 + function wpbc_booking_time_override__hm_to_seconds( $time_value ) {
1870 +
1871 + $time_arr = explode( ':', (string) $time_value );
1872 +
1873 + return ( absint( $time_arr[0] ) * 60 * 60 ) + ( absint( $time_arr[1] ) * 60 );
1320 1874 }
1321 1875
1322 1876
1323 1877 /**