PluginProbe
Booking Calendar / 11.8.4
Booking Calendar v11.8.4
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 +749 -177 10.1.311.8.4 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2
3 -if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly //FixIn: 9.8.0.4
3 +if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly // FixIn: 9.8.0.4.
4 4
5 5 // ---------------------------------------------------------------------------------------------------------------------
6 6 // == Ajax Response on creation of new booking
7 7 // ---------------------------------------------------------------------------------------------------------------------
@@ -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.
@@ -20,9 +20,9 @@
20 20
21 21 // Security ------------------------------------------------------------------------------------------------------ // in Ajax Post: 'nonce': _wpbc.get_secure_param( 'nonce' ),
22 22 $action_name = 'wpbc_calendar_load_ajx' . '_wpbcnonce';
23 23 $nonce_post_key = 'nonce';
24 - if ( wpbc_is_use_nonce_at_front_end() ) { //FixIn: 10.1.1.2
24 + if ( wpbc_is_use_nonce_at_front_end() ) { // FixIn: 10.1.1.2.
25 25 $result_check = check_ajax_referer( $action_name, $nonce_post_key );
26 26 }
27 27
28 28 // Response AJAX parameters
@@ -28,89 +28,123 @@
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?'
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
33 35
34 - // Local parameters
35 - $local_params = array();
36 - $local_params['is_from_admin_panel'] = ( false !== strpos( $_SERVER['HTTP_REFERER'], $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
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 + ));
38 74
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'
45 -
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']
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'] );
67 84
68 - $request_params['request_uri'] = $_SERVER['HTTP_REFERER']; // Parameter needed for Error in booking saving and reloading calendar again with these actual parameters.
69 -
70 85 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- CAPTCHA " >
71 - wpbc_captcha__in_ajx__check( $request_params, $local_params['is_from_admin_panel'], $_REQUEST[ $request_prefix ] );
86 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
87 + wpbc_captcha__in_ajx__check( $request_params, $is_authorized_admin_booking_request, $_REQUEST[ $request_prefix ] );
72 88 // </editor-fold>
73 89
74 90 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING_RESOURCE ID " >
75 91 if ( $request_params['resource_id'] <= 0 ) {
76 - $ajx_data_arr['status'] = 'error';
77 - $ajx_data_arr['status_error'] = 'resource_id_incorrect';
92 + $ajx_data_arr['status'] = 'error';
93 + $ajx_data_arr['status_error'] = 'resource_id_incorrect';
94 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
78 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'] . ' ]';
79 - $ajx_data_arr['ajx_after_action_message_status'] = 'error';
80 - wp_send_json( array( 'ajx_data' => $ajx_data_arr,
81 - 'ajx_search_params' => $_REQUEST[ $request_prefix ],
82 - 'ajx_cleaned_params' => $request_params,
83 - 'resource_id' => $request_params['resource_id']
84 - ) );
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 + ) );
85 101 }
86 102 // </editor-fold>
87 103
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 */
88 105
89 - $request_save_params = array(
90 - 'resource_id' => $request_params['resource_id'],
91 - 'dates_ddmmyy_csv' => $request_params['dates_ddmmyy_csv'],
92 - 'form_data' => $request_params['formdata'],
93 - 'aggregate_resource_id_arr' => $request_params['aggregate_resource_id_arr'], // Optional can be ''
94 -
95 - 'booking_hash' => $request_params['booking_hash'],
96 - 'custom_form' => $request_params['custom_form'],
97 -
98 - 'is_emails_send' => $request_params['is_emails_send'],
99 - 'is_show_payment_form' => 1,
100 - 'user_id' => $local_params['user_id'],
101 - 'request_uri' => $_SERVER['HTTP_REFERER']
102 - );
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'];
103 136 $booking_save_arr = wpbc_booking_save( $request_save_params );
104 137
105 138 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING " >
106 139 if ( 'ok' !== $booking_save_arr['ajx_data']['status'] ) {
107 140
108 - wp_send_json( array( 'ajx_data' => $booking_save_arr['ajx_data'],
109 - 'ajx_search_params' => $_REQUEST[ $request_prefix ],
110 - 'ajx_cleaned_params' => $request_params,
111 - 'resource_id' => $request_params['resource_id']
112 - ));
141 + wp_send_json(
142 + array(
143 + 'ajx_data' => $booking_save_arr['ajx_data'],
144 + 'resource_id' => $request_params['resource_id'],
145 + )
146 + );
113 147 }
114 148 // </editor-fold>
115 149
116 150 $ajx_data_arr = $booking_save_arr['ajx_data'];
@@ -127,17 +161,17 @@
127 161 }
128 162
129 163 // $ajx_data_arr['ajx_after_action_message'] .= __( 'Booking was created with ID: ' . $booking_save_arr[ 'booking_id' ] , 'booking' );
130 164 // $ajx_data_arr['ajx_after_action_message'] .= '<hr>Total time: <strong>' . $booking_save_arr['php_performance']['total'] . ' s. </strong>';
131 - // $ajx_data_arr['ajx_after_action_message'] .= str_replace( array( ',', '{', '}' ), '<br>', json_encode( $booking_save_arr['php_performance'] ) );
165 + // $ajx_data_arr['ajx_after_action_message'] .= str_replace( array( ',', '{', '}' ), '<br>', wp_json_encode( $booking_save_arr['php_performance'] ) );
132 166 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
133 167
134 168
135 169
136 170 /* if admin edit ?
137 - var my_message = '<?php echo html_entity_decode( esc_js( __('Updated successfully' ,'booking') ),ENT_QUOTES) ; ?>';
171 + var my_message = '<?php echo esc_js( __('Updated successfully' ,'booking') ) ; ?>';
138 172 wpbc_admin_show_message( my_message, 'success', 3000 );
139 - 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'] ; ?>';
140 174 */
141 175
142 176
143 177 // -----------------------------------------------------------------------------------------------------------------
@@ -222,9 +256,9 @@
222 256 *
223 257 */
224 258 function wpbc_booking_save( $request_params ){
225 259 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
226 - $php_performance = php_performance_START( 'total', array() );
260 + $php_performance = wpbc_php_performance_START( 'total', array() );
227 261 // </editor-fold>
228 262 $ajx_data_arr = array();
229 263 $ajx_data_arr['status'] = 'ok';
230 264
@@ -230,11 +264,13 @@
230 264
231 265 // -----------------------------------------------------------------------------------------------------------------
232 266 // 1. Direct Clean Params
233 267 // -----------------------------------------------------------------------------------------------------------------
234 - $validate_arr_rules = array(
235 - 'resource_id' => array( 'validate' => 'd', 'default' => 1 ), // INT
236 - 'dates_ddmmyy_csv' => array( 'validate' => 'csv_dates', 'default' => '' ), //FixIn: 9.9.1.1
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 */
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 */
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.
237 273 'form_data' => array( 'validate' => 'strong', 'default' => '' ),
238 274 'booking_hash' => array( 'validate' => 'strong', 'default' => '' ),
239 275 'custom_form' => array( 'validate' => 'strong', 'default' => '' ),
240 276 'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
@@ -239,32 +275,128 @@
239 275 'custom_form' => array( 'validate' => 'strong', 'default' => '' ),
240 276 'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
241 277 'is_show_payment_form' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
242 278 'user_id' => array( 'validate' => 'd', 'default' => wpbc_get_current_user_id() ), // INT
243 - 'request_uri' => array( 'validate' => 'strong', 'default' => ( ( defined( 'DOING_AJAX' ) ) && ( DOING_AJAX ) ) ? $_SERVER['HTTP_REFERER'] : $_SERVER['REQUEST_URI'] ), // front-end: $_SERVER['REQUEST_URI'] | ajax: $_SERVER['HTTP_REFERER']
279 + 'allow_past' => array( 'validate' => 'd', 'default' => 0 ),
280 + 'classic_booking_context_token' => array( 'validate' => 'strong', 'default' => '' ),
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
244 282 // Really Optional:
245 283 'aggregate_resource_id_arr' => array( 'validate' => 'digit_or_csd', 'default' => '' ),
246 - //TODO: this parameter does not transfer during saving, so here will be always default value 'bookings_only' //FixIn: 10.0.0.7
284 + //TODO: this parameter does not transfer during saving, so here will be always default value 'bookings_only' // FixIn: 10.0.0.7.
247 285 'aggregate_type' => array( 'validate' => 'strong', 'default' => 'bookings_only' ), // Optional. 'all' | 'bookings_only' <- it is depends on shortcode parameter: options="{aggregate type=bookings_only}"
248 286 'is_approve_booking' => array( 'validate' => 'd', 'default' => 0 ), // 0 | 1
249 287 'save_booking_even_if_unavailable' => array( 'validate' => 'd', 'default' => 0 ), // 0 | 1
250 288 'sync_gid' => array( 'validate' => 'strong', 'default' => '' ),
251 - 'is_use_booking_recurrent_time' => array( 'validate' => 'd', 'default' => intval( ( 'On' === get_bk_option( 'booking_recurrent_time' ) ) ) )
252 - );
253 - $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' ) ) ) ),
254 290
255 - $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 + );
256 377
257 -
258 378 // -----------------------------------------------------------------------------------------------------------------
259 379 // Local parameters
260 380 // -----------------------------------------------------------------------------------------------------------------
261 - $local_params = array();
262 - $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;
263 384 $local_params['user_id'] = $re_cleaned_params['user_id']; // 1
264 - $local_params['sync_gid'] = $re_cleaned_params['sync_gid']; // ''
265 - $local_params['is_approve_booking'] = $re_cleaned_params['is_approve_booking']; // 0 | 1
266 - $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 );
267 399
268 400 // -----------------------------------------------------------------------------------------------------------------
269 401 // Parse Local parameters for later use
270 402 // -----------------------------------------------------------------------------------------------------------------
@@ -272,10 +404,91 @@
272 404 * Get parsed booking form: = [ name = "John", secondname = "Smith", email = "john.smith@server.com", visitors = "2",... ]
273 405 */
274 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' ) );
275 407 $local_params['all_booking_data_arr'] = wpbc_get_parsed_booking_data_arr( $re_cleaned_params["form_data"], $re_cleaned_params["resource_id"] );
276 - // Important! : [ 64800, 72000 ]
277 - $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 + }
278 491 // [ "18:00:00", "20:00:00" ]
279 492 $time_as_seconds_arr = $local_params['time_as_seconds_arr'];
280 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
281 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
@@ -287,19 +500,77 @@
287 500 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[0] ),
288 501 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[1] )
289 502 );
290 503 // [ '2023-09-10', '2023-09-11' ]
291 - $local_params['dates_only_sql_arr'] = wpbc_convert_dates_str__dd_mm_yyyy__to__yyyy_mm_dd( $re_cleaned_params["dates_ddmmyy_csv"] );
292 - $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 + }
293 561
294 562 $local_params['is_show_payment_form'] = $re_cleaned_params["is_show_payment_form"];
295 563
296 - //FixIn: 9.9.0.35
297 - if ( $local_params['is_show_payment_form'] ) {
298 - $local_params['is_show_payment_form'] = ( false !== strpos( $re_cleaned_params['request_uri'], 'is_show_payment_form=Off' ) )
299 - ? 0
300 - : $local_params['is_show_payment_form']; // 1|0
301 - }
564 + // FixIn: 9.9.0.35.
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 + }
302 573
303 574 // Get EDIT booking data
304 575 $local_params['edit_resource_id'] = '';
305 576 $local_params['skip_booking_id'] = '';
@@ -305,27 +576,44 @@
305 576 $local_params['skip_booking_id'] = '';
306 577 $local_params['is_edit_booking'] = 0;
307 578 $local_params['is_duplicate_booking'] = 0;
308 579 $is_edit_booking = wpbc_get_data__if_edit_booking( $re_cleaned_params['booking_hash'], $re_cleaned_params['request_uri'] );
309 - if ( false !== $is_edit_booking ) {
310 - $local_params['edit_resource_id'] = $is_edit_booking['resource_id']; // can be parent booking resource, where we edit the booking
311 - $local_params['skip_booking_id'] = $is_edit_booking['booking_id']; // booking ID
312 - $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
313 584 if (
314 585 ( ! empty( $local_params['structured_booking_data_arr']['wpbc_other_action'] ) )
315 586 && ( 'duplicate_booking' === $local_params['structured_booking_data_arr']['wpbc_other_action'] )
316 587 ){
317 - $local_params['is_duplicate_booking'] = 1;
318 - }
319 - }
320 - // 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)
321 609 $local_params['initial_resource_id'] = ( ! empty( $local_params['edit_resource_id'] ) ) ? $local_params['edit_resource_id'] : $re_cleaned_params['resource_id'];
322 610
323 - // 2
324 - $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'] );
325 -
326 -
327 - $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'] );
328 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.
329 617 $local_params['aggregate_resource_id_arr'] = array_unique( $local_params['aggregate_resource_id_arr'] ); // Erase duplicates
330 618
331 619 // -----------------------------------------------------------------------------------------------------------------
@@ -348,9 +636,9 @@
348 636 $where_to_save_booking['main__resource_id'] = $local_params['initial_resource_id']; // here edit or request (parent/single) resource
349 637
350 638 } else {
351 639 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
352 - $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 );
353 641 // </editor-fold>
354 642
355 643 /**
356 644 * Get slots [] where we can save booking = [ 'resources_in_dates' => [ 2023-10-18 = [ 2, 12, 10, 11 ]
@@ -370,13 +658,15 @@
370 658 'dates_only_sql_arr' => $local_params['dates_only_sql_arr'], // [ "2023-10-18", "2023-10-25", "2023-11-25" ]
371 659 'time_as_seconds_arr' => $local_params['time_as_seconds_arr'], // [ 36000, 39600 ]
372 660 'how_many_items_to_book' => $local_params['how_many_items_to_book'], // 1
373 661 'request_uri' => $re_cleaned_params['request_uri'], // 'http://beta/resource-id2/'
662 + 'allow_past' => ! empty( $re_cleaned_params['allow_past'] ),
374 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'] : '',
375 665 'as_single_resource' => false, // false
376 666 'aggregate_resource_id_arr' => $local_params['aggregate_resource_id_arr'], // Optional can be ''
377 - '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
378 - 'custom_form' => $re_cleaned_params['custom_form'] //FixIn: 10.0.0.10
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.
668 + 'custom_form' => $re_cleaned_params['custom_form'] // FixIn: 10.0.0.10.
379 669 ));
380 670 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- NO SLOTS TO SAVE " >
381 671 if ( 'error' == $where_to_save_booking['result'] ) {
382 672 $ajx_data_arr['status'] = 'error';
@@ -387,14 +677,31 @@
387 677 }
388 678 // </editor-fold>
389 679
390 680 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
391 - $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 );
392 682 // </editor-fold>
393 - }
394 -
395 -
396 - // 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
397 704 $create_params = $local_params;
398 705 $create_params['resource_id'] = ( ! empty( $local_params['edit_resource_id'] ) )
399 706 ? $local_params['edit_resource_id'] // If we edit, then use original resource ???
400 707 : $where_to_save_booking['main__resource_id']; // Here is important TIP, resource can be where is free, and not where we submit
@@ -413,15 +720,15 @@
413 720
414 721 make_bk_action( 'check_multiuser_params_for_client_side', $create_params['resource_id'] ); // Activate working with specific user in WP MU
415 722
416 723 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
417 - $php_performance = php_performance_START( 'wpbc_db__booking_save' , $php_performance );
724 + $php_performance = wpbc_php_performance_START( 'wpbc_db__booking_save' , $php_performance );
418 725 // </editor-fold>
419 726
420 727 // -----------------------------------------------------------------------------------------------------------------
421 728 // == CREATE_THE 'NEW_BOOKING' ==
422 729 // -----------------------------------------------------------------------------------------------------------------
423 - $create_booking_params = array(
730 + $create_booking_params = array(
424 731 'resource_id' => $create_params['resource_id'],
425 732 'custom_form' => $create_params['custom_form'],
426 733 'all_booking_data_arr' => $create_params['all_booking_data_arr'],
427 734 'dates_only_sql_arr' => $create_params['dates_only_sql_arr'],
@@ -430,25 +737,28 @@
430 737 'is_edit_booking' => $create_params['is_edit_booking'],
431 738 'is_duplicate_booking' => $create_params['is_duplicate_booking'],
432 739 'is_approve_booking' => $create_params['is_approve_booking'],
433 740 'how_many_items_to_book' => $create_params['how_many_items_to_book'],
434 - 'is_use_booking_recurrent_time' => $create_params['is_use_booking_recurrent_time'] // true | false
435 - );
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']; }
436 744 if ( ! empty( $create_params['sync_gid'] ) ) { $create_booking_params['sync_gid'] = $create_params['sync_gid']; }
437 745
438 746 $booking_new_arr = wpbc_db__booking_save( $create_booking_params, $where_to_save_booking );
439 747
440 748 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING CREATION " >
441 - if ( 'ok' !== $booking_new_arr['status'] ) {
749 + if ( 'ok' !== $booking_new_arr['status'] ) {
442 750 $ajx_data_arr['status'] = $booking_new_arr['status'];
443 751 $ajx_data_arr['status_error'] = 'booking_can_not_save';
444 752 $ajx_data_arr['ajx_after_action_message'] = $booking_new_arr['message'];
445 753 $ajx_data_arr['ajx_after_action_message_status'] = 'error';
446 754 return array( 'ajx_data' => $ajx_data_arr );
447 - }
448 - // </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 );
449 759
450 - //FixIn: 9.9.0.36
760 + // FixIn: 9.9.0.36.
451 761 if (
452 762 ( 0 !== $create_params['is_edit_booking'] ) // If edit booking
453 763 && ( 1 != $create_params['is_duplicate_booking'] ) // If not duplicate
454 764 ) {
@@ -458,9 +768,9 @@
458 768 $booking_note .= __( 'The booking has been edited', 'booking' ) . '. | Edit URL: ' . esc_url_raw( $re_cleaned_params['request_uri'] ) . '';
459 769 make_bk_action( 'wpdev_make_update_of_remark', $booking_new_arr['booking_id'], $booking_note, true );
460 770 }
461 771 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
462 - $php_performance = php_performance_END( 'wpbc_db__booking_save' , $php_performance );
772 + $php_performance = wpbc_php_performance_END( 'wpbc_db__booking_save' , $php_performance );
463 773 // </editor-fold>
464 774
465 775 // -----------------------------------------------------------------------------------------------------------------
466 776 // Get payment form(s) and Update COST of the booking
@@ -474,9 +784,10 @@
474 784 );
475 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']
476 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'
477 787 $payment_params['booking_id'] = $booking_new_arr['booking_id']; // REQUIRED -- '2'
478 - $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;
479 790 $payment_params['initial_resource_id'] = $local_params['initial_resource_id']; // REQUIRED -- '2' initial calendar - parent resource
480 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...'
481 792 $payment_params['times_array'] = array(
482 793 explode( ':', $where_to_save_booking['time_to_book'][0] ), // ["10","00","00"]
@@ -486,14 +797,15 @@
486 797 $payment_params['is_edit_booking'] = $create_params['is_edit_booking']; // => 0 0 | int - ID of the booking
487 798 $payment_params['custom_form'] = $create_params['custom_form']; // => '' '' | 'some_name'
488 799 $payment_params['is_duplicate_booking'] = $create_params['is_duplicate_booking']; // => 0 0 | 1
489 800 $payment_params['is_from_admin_panel'] = $create_params['is_from_admin_panel']; // => false true | false
490 - $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'];
491 803 if ( $payment_params['is_from_admin_panel'] ) {
492 - // $payment_params['is_show_payment_form'] = 0; //FixIn: 9.9.0.21
804 + // $payment_params['is_show_payment_form'] = 0; // FixIn: 9.9.0.21.
493 805 }
494 806 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
495 - $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 );
496 808 // </editor-fold>
497 809
498 810 // GET PAYMENT FORMS ===============================================================================================
499 811 if ( function_exists( 'wpbc_maybe_get_payment_form' ) ) {
@@ -530,11 +842,11 @@
530 842 }
531 843
532 844
533 845 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
534 - $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 );
535 847
536 - $php_performance = php_performance_START( 'emails_sending' , $php_performance );
848 + $php_performance = wpbc_php_performance_START( 'emails_sending' , $php_performance );
537 849 // </editor-fold>
538 850
539 851 // -----------------------------------------------------------------------------------------------------------------
540 852 // == Emails ===
@@ -548,9 +860,9 @@
548 860 ob_clean();
549 861
550 862 if (
551 863 ( 0 === $local_params['is_edit_booking'] )
552 - || ( 1 === $local_params['is_duplicate_booking'] ) //FixIn: 10.0.0.42
864 + || ( 1 === $local_params['is_duplicate_booking'] ) // FixIn: 10.0.0.42.
553 865 ){
554 866
555 867 // New booking to Admin
556 868 wpbc_send_email_new_admin( $payment_params['booking_id'], $payment_params['resource_id'], $email_content );
@@ -563,8 +875,10 @@
563 875 // New approved to Visitor / Admin
564 876 wpbc_send_email_approved( $payment_params['booking_id'], 1 );
565 877 }
566 878
879 + do_action( 'wpbc_booking_is_approved_during_creation' , $payment_params['booking_id'] , (int) $is_booking_approved ); // FixIn: 10.10.1.1.
880 +
567 881 // Payment request from admin panel, if needed
568 882 if(
569 883 ( $payment_params['is_from_admin_panel'] )
570 884 && ( 'On' == get_bk_option( 'booking_payment_request_auto_send_in_bap' ) )
@@ -573,10 +887,10 @@
573 887 $payment_reason = '';
574 888 $is_send = wpbc_send_email_payment_request( $payment_params['booking_id'], $payment_params['resource_id'], $email_content , $payment_reason );
575 889 }
576 890
577 - do_action( 'wpbc_booking_approved' , $payment_params['booking_id'] , (int) $is_booking_approved );
578 891
892 +
579 893 } else {
580 894
581 895 // Edited booking to Visitor / Admin
582 896 if ( function_exists( 'wpbc_send_email_modified' ) ) {
@@ -588,9 +902,9 @@
588 902 ob_end_clean();
589 903
590 904 if ( ! empty( $errors_on_email_sending_html ) ) {
591 905 // Show these messages as warning after creation of the booking
592 - $errors_on_email_sending_html = strip_tags( $errors_on_email_sending_html );
906 + $errors_on_email_sending_html = wp_strip_all_tags( $errors_on_email_sending_html );
593 907 $errors_on_email_sending_html = esc_attr( $errors_on_email_sending_html );
594 908 $errors_on_email_sending_html = str_replace( "\\n", '', $errors_on_email_sending_html );
595 909
596 910 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
@@ -598,9 +912,9 @@
598 912 }
599 913 }
600 914
601 915 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
602 - $php_performance = php_performance_END( 'emails_sending' , $php_performance );
916 + $php_performance = wpbc_php_performance_END( 'emails_sending' , $php_performance );
603 917 // </editor-fold>
604 918
605 919 // -----------------------------------------------------------------------------------------------------------------
606 920 // == Track booking - New | Edit ===
@@ -672,9 +986,9 @@
672 986 }
673 987
674 988
675 989 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
676 - $php_performance = php_performance_START( 'confirmation' , $php_performance );
990 + $php_performance = wpbc_php_performance_START( 'confirmation' , $php_performance );
677 991 // </editor-fold>
678 992
679 993 // <editor-fold defaultstate="collapsed" desc=" == Confirmation data == " >
680 994
@@ -705,15 +1019,15 @@
705 1019 if ( $payment_params['is_from_admin_panel'] ) {
706 1020
707 1021 $confirmation_params_arr['ty_is_redirect'] = 'message'; // Do not make redirect, if it's in admin panel!
708 1022
709 - // But if we edit / duplicate the booking, then do redirection to Booking Listing page //FixIn: 9.9.0.3
1023 + // But if we edit / duplicate the booking, then do redirection to Booking Listing page // FixIn: 9.9.0.3.
710 1024 if (
711 1025 ( 0 !== $local_params['is_edit_booking'] )
712 1026 // && ( empty( $local_params['is_duplicate_booking'] ) )
713 1027 ){
714 1028 $confirmation_params_arr['ty_is_redirect'] = 'page';
715 - $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'];
716 1030 }
717 1031 }
718 1032 $confirmation = wpbc_booking_confirmation( $confirmation_params_arr );
719 1033
@@ -719,9 +1033,9 @@
719 1033
720 1034 // </editor-fold>
721 1035
722 1036 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
723 - $php_performance = php_performance_END( 'confirmation' , $php_performance );
1037 + $php_performance = wpbc_php_performance_END( 'confirmation' , $php_performance );
724 1038 // </editor-fold>
725 1039
726 1040
727 1041 make_bk_action( 'finish_check_multiuser_params_for_client_side', $create_params['resource_id'] ); // Deactivate working with specific user in WP MU
@@ -727,9 +1041,9 @@
727 1041 make_bk_action( 'finish_check_multiuser_params_for_client_side', $create_params['resource_id'] ); // Deactivate working with specific user in WP MU
728 1042
729 1043
730 1044 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
731 - $php_performance = php_performance_END( 'total' , $php_performance );
1045 + $php_performance = wpbc_php_performance_END( 'total' , $php_performance );
732 1046 $php_performance['other_code'] = - 1 * array_reduce( $php_performance,
733 1047 function ( $sum, $item ) {
734 1048 $sum += $item;
735 1049 return $sum;
@@ -736,8 +1050,9 @@
736 1050 }
737 1051 , - 2 * $php_performance['total'] ); // PERFORMANCE OTHER - after TOTAL
738 1052 // </editor-fold>
739 1053
1054 + wpbc_clear_request_form_context();
740 1055
741 1056 return array( 'ajx_data' => $ajx_data_arr, // [ 'status' => "ok", 'wpbc_payment_output' => "<p>Dear John<br..." ]
742 1057 'booking_id' => $booking_new_arr['booking_id'], // 254
743 1058 'booking_arr' => $payment_params,
@@ -792,10 +1107,10 @@
792 1107 * 'message' => '' 'If error, then here can be description of error'
793 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,
794 1109 * ]
795 1110 */
796 -function wpbc_db__booking_save( $create_params, $where_to_save_booking ) {
797 -
1111 +function wpbc_db__booking_save( &$create_params, &$where_to_save_booking ) {
1112 + //FixIn: 10.11.5.4
798 1113 /**
799 1114 * Tip: $create_params['all_booking_data_arr'] - contain: [ 'field_name' => [ 'type' = "checkbox", 'original_name' = "fixed_fee2[]", 'name' = "fixed_fee", 'value' = "true" ] , ... ]
800 1115 * $create_params['structured_booking_data_arr'] - contain: [ 'field_name' => 'field_value' , ... ]
801 1116 */
@@ -847,10 +1162,10 @@
847 1162 ( 'On' === get_bk_option( 'booking_last_checkout_day_available' ) )
848 1163 && ( ! empty( $create_params['dates_only_sql_arr'] ) )
849 1164 && ( count( $create_params['dates_only_sql_arr'] ) > 1 )
850 1165 ) {
851 - unset( $create_params['dates_only_sql_arr'][ ( count( $create_params['dates_only_sql_arr'] ) - 1 ) ] ); // Remove LAST selected day in calendar //FixIn: 6.2.3.6
852 - // Delete last item //FixIn: 9.9.0.19
1166 + unset( $create_params['dates_only_sql_arr'][ ( count( $create_params['dates_only_sql_arr'] ) - 1 ) ] ); // Remove LAST selected day in calendar // FixIn: 6.2.3.6.
1167 + // Delete last item // FixIn: 9.9.0.19.
853 1168 $resources_in_dates_last_key = key( array_slice( $where_to_save_booking['resources_in_dates'], - 1, 1, true ) );
854 1169 unset( $where_to_save_booking['resources_in_dates'][ $resources_in_dates_last_key ] );
855 1170 }
856 1171
@@ -859,9 +1174,13 @@
859 1174 return array( 'status' => 'error', 'message' => 'Sent request with no dates.' );
860 1175 }
861 1176
862 1177 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- CHECK_IN_DATE_OLDER_THAN_CHECK_OUT " >
863 - 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 ?
864 1183
865 1184 // Is 'check in' date/time older than 'check out' date/time when SINGLE day for booking? Then show error.
866 1185
867 1186 /**
@@ -916,9 +1235,9 @@
916 1235 }
917 1236 add_filter( 'wpbc_get_booking_resources_arr_to_auto_approve', 'my_wpbc_get_booking_resources_arr_to_auto_approve' );
918 1237 */
919 1238 $booking_resources_to_approve = array();
920 - $booking_resources_to_approve = apply_filters( 'wpbc_get_booking_resources_arr_to_auto_approve', $booking_resources_to_approve ); //FixIn: 8.5.2.27
1239 + $booking_resources_to_approve = apply_filters( 'wpbc_get_booking_resources_arr_to_auto_approve', $booking_resources_to_approve ); // FixIn: 8.5.2.27.
921 1240 if ( in_array( $create_params['resource_id'], $booking_resources_to_approve ) ) {
922 1241 $is_approved_dates = 1;
923 1242 }
924 1243
@@ -924,12 +1243,21 @@
924 1243
925 1244 if (
926 1245 ( $create_params['is_from_admin_panel'] ) // true | false
927 1246 && ( get_bk_option( 'booking_auto_approve_bookings_if_added_in_admin_panel' ) == 'On' )
928 - ){ //FixIn: 8.1.3.27
1247 + ){ // FixIn: 8.1.3.27.
929 1248 $is_approved_dates = 1;
930 1249 }
931 1250
1251 + // If the booking auto-approved, then we need to mark it as "Read".
1252 + if ( $is_approved_dates ) {
1253 + $sql_field_arr[] = array(
1254 + 'name' => 'is_new',
1255 + 'type' => '%d',
1256 + 'value' => 0,
1257 + );
1258 + }
1259 +
932 1260 // <editor-fold defaultstate="collapsed" desc=" == Save Booking == " >
933 1261 // -----------------------------------------------------------------------------------------------------------------
934 1262 // Save Booking
935 1263 // -----------------------------------------------------------------------------------------------------------------
@@ -938,17 +1266,21 @@
938 1266 $sql_field_arr[] = array( 'name' => 'form', 'type' => '%s', 'value' => $form_data );
939 1267 $sql_field_arr[] = array( 'name' => 'booking_type', 'type' => '%d', 'value' => $create_params['resource_id'] );
940 1268 $sql_field_arr[] = array( 'name' => 'modification_date', 'type' => '%s', 'value' => gmdate( 'Y-m-d H:i:s' ) );
941 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] );
942 - $sql_field_arr[] = array( 'name' => 'hash', 'type' => 'MD5(%s)', 'value' => time() . '_' . rand( 1000, 1000000 ) );
1270 + $sql_field_arr[] = array( 'name' => 'hash', 'type' => '%s', 'value' => wpbc_hash__generate_booking_hash() );
943 1271
944 1272
945 1273 if (
946 - ( 0 == $create_params['is_edit_booking'] ) // If not edit, then INSERT
947 - || ( 1 == $create_params['is_duplicate_booking'] ) // If duplicate, then INSERT
948 - ){
1274 + ( 0 == $create_params['is_edit_booking'] ) || // If not edit, then INSERT.
1275 + ( 1 == $create_params['is_duplicate_booking'] ) // If duplicate, then INSERT.
1276 + ) {
949 1277 // Saved only for new booking creation.
950 - $sql_field_arr[] = array( 'name' => 'creation_date', 'type' => '%s', 'value' => gmdate( 'Y-m-d H:i:s' ) );
1278 + $sql_field_arr[] = array(
1279 + 'name' => 'creation_date',
1280 + 'type' => '%s',
1281 + 'value' => gmdate( 'Y-m-d H:i:s' ),
1282 + );
951 1283
952 1284 $sql_prepare_arr = array();
953 1285 $sql_prepare_arr['name'] = array_map( function ( $value ) { return $value['name']; }, $sql_field_arr );
954 1286 $sql_prepare_arr['type'] = array_map( function ( $value ) { return $value['type']; }, $sql_field_arr );
@@ -955,18 +1287,17 @@
955 1287 $sql_prepare_arr['value'] = array_map( function ( $value ) { return $value['value']; }, $sql_field_arr );
956 1288
957 1289 $sql_prepare_arr['name'] = implode( ', ', $sql_prepare_arr['name'] );
958 1290 $sql_prepare_arr['type'] = implode( ', ', $sql_prepare_arr['type'] );
959 -
960 - $sql = $wpdb->prepare( "INSERT INTO {$wpdb->prefix}booking "
961 - . " ( {$sql_prepare_arr['name']} )"
962 - . " VALUES ( {$sql_prepare_arr['type']} )"
963 - , $sql_prepare_arr['value']
964 - );
965 -
966 - if ( false === $wpdb->query( $sql ) ) {
967 - return array( 'status' => 'error','message' => 'Error. INSERT New Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql );
968 - }
1291 + /* phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare */
1292 + $sql = $wpdb->prepare( "INSERT INTO {$wpdb->prefix}booking " . " ( {$sql_prepare_arr['name']} )" . " VALUES ( {$sql_prepare_arr['type']} )", $sql_prepare_arr['value'] );
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 + }
969 1300 // Get ID of booking
970 1301 $booking_id = (int) $wpdb->insert_id;
971 1302
972 1303 } else { // Edit - UPDATE
@@ -978,27 +1309,30 @@
978 1309 $sql_prepare_arr['value'] = array_map( function ( $value ) { return $value['value']; }, $sql_field_arr );
979 1310
980 1311 $sql_prepare_arr['set'] = implode( ', ', $sql_prepare_arr['set'] );
981 1312
982 - $sql = $wpdb->prepare( "UPDATE {$wpdb->prefix}booking SET "
983 - . " {$sql_prepare_arr['set']} "
984 - . " WHERE booking_id={$booking_id};"
985 - , $sql_prepare_arr['value']
986 - );
987 - if ( false === $wpdb->query( $sql ) ){
988 - return array( 'status' => 'error','message' => 'Error. UPDATE Exist Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql );
989 - }
1313 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
1314 + $sql = $wpdb->prepare( "UPDATE {$wpdb->prefix}booking SET {$sql_prepare_arr['set']} WHERE booking_id={$booking_id};", $sql_prepare_arr['value'] );
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 + }
990 1322
991 1323 // Check if dates previously was approved.
992 1324 $slct_sql = "SELECT approved FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id}) LIMIT 0,1";
993 - $slct_sql_results = $wpdb->get_results( $slct_sql );
1325 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1326 + $slct_sql_results = $wpdb->get_results( $slct_sql );
994 1327 $is_approved_dates = ( count( $slct_sql_results ) > 0 ) ? $slct_sql_results[0]->approved : $is_approved_dates;
995 1328
996 1329
997 1330 $delete_sql = "DELETE FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id})";
998 - if ( false === $wpdb->query( $delete_sql ) ){
999 - return array( 'status' => 'error','message' => 'Error. DELETE Old Dates in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $delete_sql );
1000 - }
1331 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1332 + if ( false === $wpdb->query( $delete_sql ) ) {
1333 + return array( 'status' => 'error', 'message' => 'Error. DELETE Old Dates in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $delete_sql );
1334 + }
1001 1335 }
1002 1336 // </editor-fold>
1003 1337
1004 1338
@@ -1084,11 +1418,11 @@
1084 1418 }
1085 1419 }
1086 1420
1087 1421 $dates_sql .= implode( ', ', $insert_dates_arr );
1088 -
1422 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1089 1423 if ( false === $wpdb->query( $dates_sql ) ) {
1090 - return array( 'status' => 'error','message' => 'Error. INSERT "D A T E S" in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $dates_sql );
1424 + return array( 'status' => 'error', 'message' => 'Error. INSERT "D A T E S" in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $dates_sql );
1091 1425 }
1092 1426
1093 1427 // -----------------------------------------------------------------------------------------------------------------
1094 1428 // End D A T E S
@@ -1130,9 +1464,24 @@
1130 1464
1131 1465 return $insert_dates_arr;
1132 1466 }
1133 1467
1468 +// == Help functions ==
1469 +function wpbc_set_request_form_context( $ctx ) {
1470 + $GLOBALS['wpbc_request_form_context'] = ( is_array( $ctx ) ) ? $ctx : array();
1471 +}
1134 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 +
1135 1484 // ---------------------------------------------------------------------------------------------------------------------
1136 1485 // Support
1137 1486 // ---------------------------------------------------------------------------------------------------------------------
1138 1487
@@ -1155,10 +1504,11 @@
1155 1504 $is_edit_booking = array();
1156 1505 $is_edit_booking['booking_id'] = intval( $my_booking_id_type[0] );
1157 1506 $is_edit_booking['resource_id'] = intval( $my_booking_id_type[1] );
1158 1507
1159 - //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
1160 - if ( strpos( $server_request_url, 'resource_no_update' ) === false ) { //FixIn: 9.4.2.3
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.
1509 + // FixIn: 10.10.1.2
1510 + //if ( strpos( $server_request_url, 'resource_no_update' ) === false ) { // FixIn: 9.4.2.3.
1161 1511
1162 1512 if ( ( function_exists( 'wpbc_is_this_child_resource' ) ) && ( wpbc_is_this_child_resource( $is_edit_booking['resource_id'] ) ) ) {
1163 1513 $bk_parent_br_id = wpbc_get_parent_resource( $is_edit_booking['resource_id'] );
1164 1514
@@ -1163,9 +1513,9 @@
1163 1513 $bk_parent_br_id = wpbc_get_parent_resource( $is_edit_booking['resource_id'] );
1164 1514
1165 1515 $is_edit_booking['resource_id'] = intval( $bk_parent_br_id );
1166 1516 }
1167 - }
1517 + //}
1168 1518 }
1169 1519 }
1170 1520 return $is_edit_booking;
1171 1521 }
@@ -1193,9 +1543,9 @@
1193 1543 function wpbc_get__how_many_items_to_book__in_booking_form( $booking_form_data__arr, $resource_id ){
1194 1544
1195 1545 $how_many_items_to_book = 1;
1196 1546
1197 - //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
1198 1548 if (
1199 1549 ( class_exists( 'wpdev_bk_biz_l' ) )
1200 1550 && ( 0 !== wpbc_get_child_resources_number( $resource_id ) ) // Here several child booking resources
1201 1551 ) {
@@ -1260,9 +1610,9 @@
1260 1610 *
1261 1611 * // Now get start/end times as seconds: [ 64800, 72000 ]
1262 1612 * $time_as_seconds_arr = wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $structured_booking_data_arr );
1263 1613 */
1264 - 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 ){
1265 1615
1266 1616 $selected_time_fields = wpbc_get__selected_time_fields__in_booking_form__as_arr( $booking_form_data__arr );
1267 1617
1268 1618 // 2.2 Get selected SECONDS to book ---------------------------------------------------------------------------
@@ -1292,14 +1642,236 @@
1292 1642 foreach ( $selected_time_fields as $time_fields_obj ) { // { times_as_seconds: [ 21600 ], value_option_24h: '06:00', name: 'durationtime'}
1293 1643
1294 1644 if ( false !== strpos( $time_fields_obj['name'], 'durationtime' ) ) {
1295 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 + }
1296 1650 break;
1297 1651 }
1298 1652 }
1299 1653 }
1300 1654
1301 - 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 );
1302 1874 }
1303 1875
1304 1876
1305 1877 /**