PluginProbe
Booking Calendar / 11.6
Booking Calendar v11.6
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
booking / includes / _capacity / create_booking.php

create_booking.php in Booking Calendar 11.6, at includes/_capacity/create_booking.php

1,845 lines 106.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly // FixIn: 9.8.0.4.
4
5 // ---------------------------------------------------------------------------------------------------------------------
6 // == Ajax Response on creation of new booking
7 // ---------------------------------------------------------------------------------------------------------------------
8
9 /**
10 * Response to Ajax request, about loading calendar data
11 *
12 * @return void
13 */
14 function ajax_WPBC_AJX_BOOKING__CREATE() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound
15
16 /**
17 * Tip / translation /
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.
19 */
20
21 // Security ------------------------------------------------------------------------------------------------------ // in Ajax Post: 'nonce': _wpbc.get_secure_param( 'nonce' ),
22 $action_name = 'wpbc_calendar_load_ajx' . '_wpbcnonce';
23 $nonce_post_key = 'nonce';
24 if ( wpbc_is_use_nonce_at_front_end() ) { // FixIn: 10.1.1.2.
25 $result_check = check_ajax_referer( $action_name, $nonce_post_key );
26 }
27
28 // Response AJAX parameters
29 $ajx_data_arr = array();
30 $ajx_data_arr['status'] = 'ok';
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
38
39 // Request parameters for the released Appointment and Resource Selector workflows.
40 $workflow_request_rules = array(
41 'service_id' => array( 'validate' => 'd', 'default' => 0 ),
42 'appointment_service_required' => array( 'validate' => 'd', 'default' => 0 ),
43 'appointment_context_token' => array( 'validate' => 'strong', 'default' => '' ),
44 'resource_selector_required' => array( 'validate' => 'd', 'default' => 0 ),
45 'resource_selector_context_token' => array( 'validate' => 'strong', 'default' => '' ),
46 );
47
48 $user_request = new WPBC_AJX__REQUEST( array( // Using this class here only for escaping variables
49 'db_option_name' => 'booking__wpbc_booking_create__request_params', // Not necessary, because we not save request, only sanitize it
50 'user_id' => $local_params['user_id'], // Not necessary, because we not save request, only sanitize it
51 'request_rules_structure' => array_merge( array(
52 'resource_id' => array( 'validate' => 'd', 'default' => 1 ), // 'digit_or_csd'.
53 'aggregate_resource_id_arr' => array( 'validate' => 'digit_or_csd', 'default' => '' ),
54 'dates_ddmmyy_csv' => array( 'validate' => 'csv_dates', 'default' => '' ), // FixIn: 9.9.1.1.
55 'formdata' => array( 'validate' => 'strong', 'default' => '' ),
56 'booking_hash' => array( 'validate' => 'strong', 'default' => '' ),
57 'custom_form' => array( 'validate' => 'strong', 'default' => '' ),
58 'captcha_chalange' => array( 'validate' => 'strong', 'default' => '' ),
59 'captcha_user_input' => array( 'validate' => 'strong', 'default' => '' ),
60 'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ),
61 'active_locale' => array( 'validate' => 'strong', 'default' => '' ),
62 'form_status' => array( 'validate' => 'strong', 'default' => 'published' ),
63 'allow_past' => array( 'validate' => 'd', 'default' => 0 ),
64 'classic_booking_context_token' => array( 'validate' => 'strong', 'default' => '' ),
65 'wpbc_bfb_preview' => array( 'validate' => 'd', 'default' => 0 ),
66 'wpbc_bfb_preview_token' => array( 'validate' => 'strong', 'default' => '' ),
67 'wpbc_bfb_preview_form_id' => array( 'validate' => 'd', 'default' => 0 ),
68 'wpbc_bfb_preview_nonce' => array( 'validate' => 'strong', 'default' => '' ),
69 'wpbc_time_override_enabled' => array( 'validate' => 'd', 'default' => 0 ),
70 'wpbc_time_override_source' => array( 'validate' => 'strong', 'default' => '' ),
71 'wpbc_time_override_start' => array( 'validate' => 'strong', 'default' => '' ),
72 'wpbc_time_override_end' => array( 'validate' => 'strong', 'default' => '' ),
73 ), $workflow_request_rules )
74 ));
75
76 // Escape of request params in Ajax Post. We use prefix 'calendar_request_params', if Ajax sent - $_REQUEST['calendar_request_params']['resource_id'], ...
77 $request_prefix = 'calendar_request_params';
78
79 //$_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";
80
81 $request_params = $user_request->get_sanitized__in_request__value_or_default( $request_prefix ); // NOT Direct: $_REQUEST['calendar_request_params']['resource_id']
82 $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 */
83 $request_params['request_uri'] = $server_http_referer_uri; // Parameter needed for Error in booking saving and reloading calendar again with these actual parameters.
84
85 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- CAPTCHA " >
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, $local_params['is_from_admin_panel'], $_REQUEST[ $request_prefix ] );
88 // </editor-fold>
89
90 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING_RESOURCE ID " >
91 if ( $request_params['resource_id'] <= 0 ) {
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
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'] . ' ]';
96 $ajx_data_arr['ajx_after_action_message_status'] = 'error';
97 wp_send_json( array(
98 'ajx_data' => $ajx_data_arr,
99 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
100 'ajx_search_params' => $_REQUEST[ $request_prefix ],
101 'ajx_cleaned_params' => $request_params,
102 'resource_id' => $request_params['resource_id'],
103 ) );
104 }
105 // </editor-fold>
106
107 $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 */
108
109 $request_save_params = array(
110 'resource_id' => $request_params['resource_id'],
111 'dates_ddmmyy_csv' => $request_params['dates_ddmmyy_csv'],
112 'form_data' => $request_params['formdata'],
113 'aggregate_resource_id_arr' => $request_params['aggregate_resource_id_arr'], // Optional can be ''.
114 'booking_hash' => $request_params['booking_hash'],
115 'custom_form' => $request_params['custom_form'],
116 'is_emails_send' => $request_params['is_emails_send'],
117 'is_show_payment_form' => 1,
118 'user_id' => $local_params['user_id'],
119 'request_uri' => $server_http_referer_uri,
120 'form_status' => $request_params['form_status'],
121 'allow_past' => $request_params['allow_past'],
122 'classic_booking_context_token' => $request_params['classic_booking_context_token'],
123 'wpbc_bfb_preview' => $request_params['wpbc_bfb_preview'],
124 'wpbc_bfb_preview_token' => $request_params['wpbc_bfb_preview_token'],
125 'wpbc_bfb_preview_form_id' => $request_params['wpbc_bfb_preview_form_id'],
126 'wpbc_bfb_preview_nonce' => $request_params['wpbc_bfb_preview_nonce'],
127 'wpbc_time_override_enabled' => $request_params['wpbc_time_override_enabled'],
128 'wpbc_time_override_source' => $request_params['wpbc_time_override_source'],
129 'wpbc_time_override_start' => $request_params['wpbc_time_override_start'],
130 'wpbc_time_override_end' => $request_params['wpbc_time_override_end'],
131 );
132 $request_save_params['service_id'] = $request_params['service_id'];
133 $request_save_params['appointment_service_required'] = $request_params['appointment_service_required'];
134 $request_save_params['appointment_context_token'] = $request_params['appointment_context_token'];
135 $request_save_params['resource_selector_required'] = $request_params['resource_selector_required'];
136 $request_save_params['resource_selector_context_token'] = $request_params['resource_selector_context_token'];
137 $booking_save_arr = wpbc_booking_save( $request_save_params );
138
139 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING " >
140 if ( 'ok' !== $booking_save_arr['ajx_data']['status'] ) {
141
142 wp_send_json( array( 'ajx_data' => $booking_save_arr['ajx_data'],
143 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
144 'ajx_search_params' => $_REQUEST[ $request_prefix ],
145 'ajx_cleaned_params' => $request_params,
146 'resource_id' => $request_params['resource_id']
147 ));
148 }
149 // </editor-fold>
150
151 $ajx_data_arr = $booking_save_arr['ajx_data'];
152
153
154
155 // TODO: If we have the calendar with specific capacity, then maybe showing by dots (the booked child booking resources) the slots and not the time slot !
156
157 if ( empty( $ajx_data_arr['ajx_after_action_message_status'] ) ) {
158 $ajx_data_arr['ajx_after_action_message_status'] = 'success';
159 }
160 if ( empty( $ajx_data_arr['ajx_after_action_message'] ) ) {
161 $ajx_data_arr['ajx_after_action_message'] = '';
162 }
163
164 // $ajx_data_arr['ajx_after_action_message'] .= __( 'Booking was created with ID: ' . $booking_save_arr[ 'booking_id' ] , 'booking' );
165 // $ajx_data_arr['ajx_after_action_message'] .= '<hr>Total time: <strong>' . $booking_save_arr['php_performance']['total'] . ' s. </strong>';
166 // $ajx_data_arr['ajx_after_action_message'] .= str_replace( array( ',', '{', '}' ), '<br>', wp_json_encode( $booking_save_arr['php_performance'] ) );
167 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
168
169
170
171 /* if admin edit ?
172 var my_message = '<?php echo esc_js( __('Updated successfully' ,'booking') ) ; ?>';
173 wpbc_admin_show_message( my_message, 'success', 3000 );
174 location.href='<?php echo wpbc_get_bookings_url() ;?>&tab=vm_booking_listing&wh_booking_id=<?php echo $is_edit_booking['booking_id'] ; ?>';
175 */
176
177
178 // -----------------------------------------------------------------------------------------------------------------
179 // == Ajax ===
180 // -----------------------------------------------------------------------------------------------------------------
181 /**
182 * Send JSON. It will make "wp_json_encode" - so pass only array, and This function call wp_die( '', '', array( 'response' => null, ) ) .
183 * Pass JS OBJ: response_data in "jQuery.post " function on success.
184 *
185 * Other End Ajax actions:
186 * $error_obj = new WP_Error( 'WPBC_CREATE', __( 'test error.' ), 'some/data' ); wp_send_json_error( $error_obj );
187 * wp_send_json_error( array( 'message' => 'invalid-api-key' ) );
188 * wp_send_json_success( array( 'message' =>'Ok:)' ) );
189 */
190 wp_send_json( array(
191 'booking_id' => $booking_save_arr['booking_id'],
192 'resource_id' => $request_params['resource_id'],
193 'ajx_data' => $ajx_data_arr,
194 'ajx_confirmation' => $booking_save_arr['confirmation'],
195
196 // For debug purpose <- comment in live serv
197 'php_process_times' => $booking_save_arr['php_performance'],
198 'booking_arr' => $booking_save_arr ['booking_arr'],
199
200 // Not needed ?
201 // 'ajx_search_params' => $_REQUEST[ $request_prefix ],
202 // 'ajx_cleaned_params' => $request_params,
203
204 ) );
205 }
206
207 // Ajax Hooks
208 if ( is_admin() && ( defined( 'DOING_AJAX' ) ) && ( DOING_AJAX ) ) {
209 add_action( 'wp_ajax_nopriv_' . 'WPBC_AJX_BOOKING__CREATE', 'ajax_' . 'WPBC_AJX_BOOKING__CREATE' ); // Client (not logged in)
210 add_action( 'wp_ajax_' . 'WPBC_AJX_BOOKING__CREATE', 'ajax_' . 'WPBC_AJX_BOOKING__CREATE' ); // Logged In users (or admin panel)
211 }
212
213
214 // ---------------------------------------------------------------------------------------------------------------------
215 // == Save Booking
216 // ---------------------------------------------------------------------------------------------------------------------
217
218 /**
219 * Save Booking - ADD NEW or UPDATE exist booking
220 *
221 * @param $request_params = [
222 * resource_id = 2 REQUIRED Default: 1
223 * dates_ddmmyy_csv = '27.10.2023, 28.10.2023, 29.10.2023' REQUIRED
224 * form_data = 'text^selected_short_dates_hint2^Fri, ...9, 2023~text^...' REQUIRED
225 * booking_hash = '' Optional Default: ''
226 * custom_form = '' Optional Default: ''
227 * is_emails_send = 1 Optional Default: 1
228 * is_show_payment_form => 1 Optional Default: 1
229 * user_id = 1 Optional Default: 0 or ID of logged-in user
230 * request_uri = 'http://beta/resource-id2/', // Optional Default: for front-end: $_SERVER['REQUEST_URI'] | ajax: $_SERVER['HTTP_REFERER']
231 *
232 * 'sync_gid' => 'ghjgjgjgh5f5f45f' // Really Optional: can be passed only during import .ics
233 * 'is_approve_booking' => 0 // Really Optional: 0 | 1
234 * 'save_booking_even_if_unavailable' => 0 // Really Optional: 0 | 1, if 1 then force save booking even if dates unavailable.
235 * ]
236 *
237 * @return [] ok: [
238 *
239 * ]
240 * error: [
241 * 'ajx_data': [ 'status':'error', 'status_error':'booking_can_not_save', 'ajx_after_action_message': 'Can not save booking', 'ajx_after_action_message_status': 'warning' ]
242 * ]
243 *
244 * Example:
245 *
246 * wpbc_booking_save( array(
247 * 'resource_id' => 2,
248 * 'dates_ddmmyy_csv' => '04.10.2023, 05.10.2023, 06.10.2023',
249 * 'form_data' => 'text^cost_hint2^150.00฿~selectbox-multiple^rangetime2[]^14:00 - 16:00~text^name2^John~text^secondname2^Smith~email^email2^john.smith@server.com~selectbox-one^visitors2^2~selectbox-one^children2^0~textarea^details2^test',
250 * 'booking_hash' => '',
251 * 'custom_form' => '',
252 * 'is_emails_send' => 1,
253 * 'is_show_payment_form' => 1,
254 * 'user_id' => 1,
255 * 'request_uri' => 'http://beta/resource-id2/'
256 * ) );
257 *
258 */
259 function wpbc_booking_save( $request_params ){
260 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
261 $php_performance = wpbc_php_performance_START( 'total', array() );
262 // </editor-fold>
263 $ajx_data_arr = array();
264 $ajx_data_arr['status'] = 'ok';
265
266 // -----------------------------------------------------------------------------------------------------------------
267 // 1. Direct Clean Params
268 // -----------------------------------------------------------------------------------------------------------------
269 $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 */
270 $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 */
271 $validate_arr_rules = array(
272 'resource_id' => array( 'validate' => 'd', 'default' => 1 ), // INT
273 'dates_ddmmyy_csv' => array( 'validate' => 'csv_dates', 'default' => '' ), // FixIn: 9.9.1.1.
274 'form_data' => array( 'validate' => 'strong', 'default' => '' ),
275 'booking_hash' => array( 'validate' => 'strong', 'default' => '' ),
276 'custom_form' => array( 'validate' => 'strong', 'default' => '' ),
277 'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
278 'is_show_payment_form' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
279 'user_id' => array( 'validate' => 'd', 'default' => wpbc_get_current_user_id() ), // INT
280 'allow_past' => array( 'validate' => 'd', 'default' => 0 ),
281 'classic_booking_context_token' => array( 'validate' => 'strong', 'default' => '' ),
282 '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
283 // Really Optional:
284 'aggregate_resource_id_arr' => array( 'validate' => 'digit_or_csd', 'default' => '' ),
285 //TODO: this parameter does not transfer during saving, so here will be always default value 'bookings_only' // FixIn: 10.0.0.7.
286 'aggregate_type' => array( 'validate' => 'strong', 'default' => 'bookings_only' ), // Optional. 'all' | 'bookings_only' <- it is depends on shortcode parameter: options="{aggregate type=bookings_only}"
287 'is_approve_booking' => array( 'validate' => 'd', 'default' => 0 ), // 0 | 1
288 'save_booking_even_if_unavailable' => array( 'validate' => 'd', 'default' => 0 ), // 0 | 1
289 'sync_gid' => array( 'validate' => 'strong', 'default' => '' ),
290 'is_use_booking_recurrent_time' => array( 'validate' => 'd', 'default' => intval( ( 'On' === get_bk_option( 'booking_recurrent_time' ) ) ) ),
291
292 'form_status' => array( 'validate' => 'strong', 'default' => 'published' ),
293 'wpbc_bfb_preview' => array( 'validate' => 'd', 'default' => 0 ),
294 'wpbc_bfb_preview_token' => array( 'validate' => 'strong', 'default' => '' ),
295 'wpbc_bfb_preview_form_id' => array( 'validate' => 'd', 'default' => 0 ),
296 'wpbc_bfb_preview_nonce' => array( 'validate' => 'strong', 'default' => '' ),
297 'wpbc_time_override_enabled' => array( 'validate' => 'd', 'default' => 0 ),
298 'wpbc_time_override_source' => array( 'validate' => 'strong', 'default' => '' ),
299 'wpbc_time_override_start' => array( 'validate' => 'strong', 'default' => '' ),
300 'wpbc_time_override_end' => 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 $re_cleaned_params = wpbc_sanitize_params_in_arr( $request_params, $validate_arr_rules );
308 if ( ! empty( $re_cleaned_params['appointment_service_required'] ) && empty( $re_cleaned_params['service_id'] ) ) {
309 $ajx_data_arr['status'] = 'error';
310 $ajx_data_arr['status_error'] = 'appointment_service_required';
311 $ajx_data_arr['ajx_after_action_message'] = __( 'Please select a Service.', 'booking' );
312 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
313 return array( 'ajx_data' => $ajx_data_arr );
314 }
315 if ( ! empty( $re_cleaned_params['service_id'] ) ) {
316 if ( ! function_exists( 'wpbc_booking_appointment_validate_submission_context' ) ) {
317 $appointment_context_check = new WP_Error( 'appointment_context_unavailable', __( 'The Appointment selection cannot be verified. Please reload the page and try again.', 'booking' ) );
318 } else {
319 $appointment_context_check = wpbc_booking_appointment_validate_submission_context(
320 $re_cleaned_params['appointment_context_token'],
321 $re_cleaned_params['service_id'],
322 $re_cleaned_params['resource_id']
323 );
324 }
325 if ( is_wp_error( $appointment_context_check ) ) {
326 $ajx_data_arr['status'] = 'error';
327 $ajx_data_arr['status_error'] = $appointment_context_check->get_error_code();
328 $ajx_data_arr['ajx_after_action_message'] = $appointment_context_check->get_error_message();
329 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
330 return array( 'ajx_data' => $ajx_data_arr );
331 }
332
333 // A client value cannot enable past Appointment creation; trust only the site-authored signed context.
334 $re_cleaned_params['allow_past'] = wpbc_booking_appointment_is_past_booking_enabled( $appointment_context_check ) ? 1 : 0;
335 }
336 if ( ! empty( $re_cleaned_params['resource_selector_required'] ) ) {
337 if ( ! function_exists( 'wpbc_booking_resource_selector_validate_submission_context' ) ) {
338 $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' ) );
339 } else {
340 $resource_selector_context_check = wpbc_booking_resource_selector_validate_submission_context(
341 $re_cleaned_params['resource_selector_context_token'],
342 $re_cleaned_params['resource_id']
343 );
344 }
345 if ( is_wp_error( $resource_selector_context_check ) ) {
346 $ajx_data_arr['status'] = 'error';
347 $ajx_data_arr['status_error'] = $resource_selector_context_check->get_error_code();
348 $ajx_data_arr['ajx_after_action_message'] = $resource_selector_context_check->get_error_message();
349 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
350 return array( 'ajx_data' => $ajx_data_arr );
351 }
352
353 // Trust only the site-authored signed selector context for public past bookings.
354 $re_cleaned_params['allow_past'] = wpbc_booking_resource_selector_is_past_booking_enabled( $resource_selector_context_check ) ? 1 : 0;
355 }
356
357 $admin_uri = ltrim( str_replace( get_site_url( null, '', 'admin' ), '', admin_url( 'admin.php?' ) ), '/' ); // wp-admin/admin.php?
358
359 $re_cleaned_params['form_status'] = sanitize_key( $re_cleaned_params['form_status'] );
360 if ( 'preview' !== $re_cleaned_params['form_status'] ) {
361 $re_cleaned_params['form_status'] = 'published';
362 }
363 // FixIn: 2026-02-05 - make preview/published available to form parsing/templates during this request.
364 wpbc_set_request_form_context(
365 array(
366 'form_status' => $re_cleaned_params['form_status'],
367 'user_id' => $re_cleaned_params['user_id'],
368 'wpbc_bfb_preview' => absint( $re_cleaned_params['wpbc_bfb_preview'] ),
369 'wpbc_bfb_preview_token' => sanitize_key( $re_cleaned_params['wpbc_bfb_preview_token'] ),
370 'wpbc_bfb_preview_form_id' => absint( $re_cleaned_params['wpbc_bfb_preview_form_id'] ),
371 'wpbc_bfb_preview_nonce' => (string) $re_cleaned_params['wpbc_bfb_preview_nonce'],
372 )
373 );
374
375 // -----------------------------------------------------------------------------------------------------------------
376 // Local parameters
377 // -----------------------------------------------------------------------------------------------------------------
378 $local_params = array();
379 $local_params['is_from_admin_panel'] = ( false !== strpos( $re_cleaned_params['request_uri'], $admin_uri ) ); // true | false
380 $local_params['user_id'] = $re_cleaned_params['user_id']; // 1
381 $local_params['sync_gid'] = $re_cleaned_params['sync_gid']; // ''
382 $local_params['is_approve_booking'] = $re_cleaned_params['is_approve_booking']; // 0 | 1
383 $local_params['is_use_booking_recurrent_time'] = ( 1 === $re_cleaned_params['is_use_booking_recurrent_time'] ); // false | true
384 $request_action = isset( $_REQUEST['action'] ) && is_scalar( $_REQUEST['action'] )
385 ? sanitize_key( (string) wp_unslash( $_REQUEST['action'] ) )
386 : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
387 $is_authorized_admin_booking_request = $local_params['is_from_admin_panel']
388 && is_user_logged_in()
389 && class_exists( 'WPBC_Add_Booking_Component' )
390 && WPBC_Add_Booking_Component::current_user_can_add_booking()
391 && wpbc_is_mu_user_can_be_here( 'activated_user' );
392 $is_public_booking_create_request = wp_doing_ajax()
393 && 'wpbc_ajx_booking__create' === strtolower( $request_action )
394 && ! $is_authorized_admin_booking_request;
395
396 // Time overrides belong exclusively to the capability-protected Add Booking administration workflow.
397 $re_cleaned_params = wpbc_restrict_booking_time_override_to_authorized_admin( $re_cleaned_params, $is_authorized_admin_booking_request );
398
399 // -----------------------------------------------------------------------------------------------------------------
400 // Parse Local parameters for later use
401 // -----------------------------------------------------------------------------------------------------------------
402 /**
403 * Get parsed booking form: = [ name = "John", secondname = "Smith", email = "john.smith@server.com", visitors = "2",... ]
404 */
405 $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' ) );
406 $local_params['all_booking_data_arr'] = wpbc_get_parsed_booking_data_arr( $re_cleaned_params["form_data"], $re_cleaned_params["resource_id"] );
407 $local_params['time_override_arr'] = wpbc_get_booking_time_override__as_arr( $re_cleaned_params );
408 if ( ! empty( $local_params['time_override_arr'] ) ) {
409 unset( $local_params['structured_booking_data_arr']['rangetime'], $local_params['structured_booking_data_arr']['durationtime'] );
410 $local_params['structured_booking_data_arr']['starttime'] = $local_params['time_override_arr']['start'];
411 $local_params['structured_booking_data_arr']['endtime'] = $local_params['time_override_arr']['end'];
412
413 unset( $local_params['all_booking_data_arr']['rangetime'], $local_params['all_booking_data_arr']['durationtime'] );
414 $local_params['all_booking_data_arr']['starttime'] = array(
415 'type' => 'text',
416 'original_name' => 'starttime' . $re_cleaned_params['resource_id'],
417 'name' => 'starttime',
418 'value' => $local_params['time_override_arr']['start'],
419 );
420 $local_params['all_booking_data_arr']['endtime'] = array(
421 'type' => 'text',
422 'original_name' => 'endtime' . $re_cleaned_params['resource_id'],
423 'name' => 'endtime',
424 'value' => $local_params['time_override_arr']['end'],
425 );
426 }
427 // Important! : [ 64800, 72000 ]
428 $local_params['time_as_seconds_arr'] = wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $local_params['structured_booking_data_arr'] );
429 $local_params['appointment_service'] = array();
430 if ( ! empty( $re_cleaned_params['service_id'] ) && function_exists( 'wpbc_appointment_services_repository' ) ) {
431 $range_time_value = isset( $local_params['structured_booking_data_arr']['rangetime'] ) ? $local_params['structured_booking_data_arr']['rangetime'] : '';
432 $start_time_value = isset( $local_params['structured_booking_data_arr']['starttime'] ) ? $local_params['structured_booking_data_arr']['starttime'] : '';
433 $range_time_value = is_array( $range_time_value ) ? implode( '', $range_time_value ) : $range_time_value;
434 $start_time_value = is_array( $start_time_value ) ? implode( '', $start_time_value ) : $start_time_value;
435 $has_appointment_time = ! empty( $local_params['time_override_arr'] )
436 || '' !== trim( (string) $range_time_value )
437 || '' !== trim( (string) $start_time_value );
438 if ( ! $has_appointment_time ) {
439 $ajx_data_arr['status'] = 'error';
440 $ajx_data_arr['status_error'] = 'appointment_service_time_required';
441 $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' );
442 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
443 return array( 'ajx_data' => $ajx_data_arr );
444 }
445 $appointment_service = wpbc_appointment_services_repository()->find_active_for_resource( $re_cleaned_params['service_id'], $re_cleaned_params['resource_id'] );
446 if ( is_wp_error( $appointment_service ) ) {
447 $ajx_data_arr['status'] = 'error';
448 $ajx_data_arr['status_error'] = 'appointment_service_unavailable';
449 $ajx_data_arr['ajx_after_action_message'] = $appointment_service->get_error_message();
450 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
451 return array( 'ajx_data' => $ajx_data_arr );
452 }
453 if ( count( $local_params['time_as_seconds_arr'] ) < 2 || ! function_exists( 'wpbc_appointment_services_resolve_end_seconds' ) ) {
454 $ajx_data_arr['status'] = 'error';
455 $ajx_data_arr['status_error'] = 'appointment_service_duration_invalid';
456 $ajx_data_arr['ajx_after_action_message'] = __( 'The selected Service duration is invalid. Please contact the website administrator.', 'booking' );
457 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
458 return array( 'ajx_data' => $ajx_data_arr );
459 }
460 $maximum_duration_minutes = absint( apply_filters( 'wpbc_booking_appointment_maximum_duration_minutes', 24 * 60, array() ) );
461 $service_end_second = wpbc_appointment_services_resolve_end_seconds( $appointment_service, $local_params['time_as_seconds_arr'][0], $maximum_duration_minutes );
462 if ( is_wp_error( $service_end_second ) ) {
463 $ajx_data_arr['status'] = 'error';
464 $ajx_data_arr['status_error'] = $service_end_second->get_error_code();
465 $ajx_data_arr['ajx_after_action_message'] = $service_end_second->get_error_message();
466 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
467 return array( 'ajx_data' => $ajx_data_arr );
468 }
469 $local_params['time_as_seconds_arr'][1] = $service_end_second;
470 $local_params['appointment_service'] = $appointment_service;
471 $service_start_time = wpbc_transform__seconds__in__24_hours_his( $local_params['time_as_seconds_arr'][0] );
472 $service_end_time = wpbc_transform__seconds__in__24_hours_his( $local_params['time_as_seconds_arr'][1] );
473 unset( $local_params['structured_booking_data_arr']['rangetime'], $local_params['structured_booking_data_arr']['durationtime'] );
474 $local_params['structured_booking_data_arr']['starttime'] = $service_start_time;
475 $local_params['structured_booking_data_arr']['endtime'] = $service_end_time;
476 unset( $local_params['all_booking_data_arr']['rangetime'], $local_params['all_booking_data_arr']['durationtime'] );
477 $local_params['all_booking_data_arr']['starttime'] = array( 'type' => 'text', 'original_name' => 'starttime' . $re_cleaned_params['resource_id'], 'name' => 'starttime', 'value' => $service_start_time );
478 $local_params['all_booking_data_arr']['endtime'] = array( 'type' => 'text', 'original_name' => 'endtime' . $re_cleaned_params['resource_id'], 'name' => 'endtime', 'value' => $service_end_time );
479 }
480 if ( function_exists( 'wpbc_appointment_services_sync_service_hint_booking_data' ) ) {
481 $service_hint_booking_data = wpbc_appointment_services_sync_service_hint_booking_data(
482 $local_params['structured_booking_data_arr'],
483 $local_params['all_booking_data_arr'],
484 $local_params['appointment_service'],
485 $re_cleaned_params['resource_id']
486 );
487 $local_params['structured_booking_data_arr'] = $service_hint_booking_data['structured_booking_data'];
488 $local_params['all_booking_data_arr'] = $service_hint_booking_data['all_booking_data'];
489 }
490 // [ "18:00:00", "20:00:00" ]
491 $time_as_seconds_arr = $local_params['time_as_seconds_arr'];
492 $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
493 $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
494 if ( ( 0 != $time_as_seconds_arr[0] ) && ( ( 24 * 60 * 60 ) == $time_as_seconds_arr[1] ) ) {
495 //FixIn: 10.0.0.49 - in case if we have start time != 00:00 and end time as 24:00 then set end time as 23:59:52
496 $time_as_seconds_arr[1] += - 8;
497 }
498 $local_params['time_as_his_arr'] = array(
499 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[0] ),
500 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[1] )
501 );
502 // [ '2023-09-10', '2023-09-11' ]
503 $local_params['dates_only_sql_arr'] = wpbc_convert_dates_str__dd_mm_yyyy__to__yyyy_mm_dd( $re_cleaned_params["dates_ddmmyy_csv"] );
504 $local_params['dates_only_sql_arr'] = explode( ',', $local_params['dates_only_sql_arr'] );
505
506 $has_verified_classic_context = false;
507 if ( ! empty( $re_cleaned_params['classic_booking_context_token'] ) && function_exists( 'wpbc_classic_booking_context_validate_submission' ) ) {
508 $classic_context = wpbc_classic_booking_context_validate_submission(
509 $re_cleaned_params['classic_booking_context_token'],
510 $re_cleaned_params['resource_id'],
511 $local_params['dates_only_sql_arr'],
512 $re_cleaned_params['custom_form'],
513 $re_cleaned_params['aggregate_resource_id_arr']
514 );
515 if ( is_wp_error( $classic_context ) ) {
516 $ajx_data_arr['status'] = 'error';
517 $ajx_data_arr['status_error'] = $classic_context->get_error_code();
518 $ajx_data_arr['ajx_after_action_message'] = $classic_context->get_error_message();
519 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
520 return array( 'ajx_data' => $ajx_data_arr );
521 }
522
523 $has_verified_classic_context = true;
524 $re_cleaned_params['allow_past'] = ! empty( $classic_context['allow_past'] ) ? 1 : 0;
525 }
526
527 $has_verified_workflow_context = (
528 ( ! empty( $re_cleaned_params['service_id'] ) && ! empty( $re_cleaned_params['appointment_context_token'] ) )
529 || ( ! empty( $re_cleaned_params['resource_selector_required'] ) && ! empty( $re_cleaned_params['resource_selector_context_token'] ) )
530 );
531 if ( $is_public_booking_create_request && ! $has_verified_classic_context && ! $has_verified_workflow_context ) {
532 $re_cleaned_params['allow_past'] = 0;
533 $re_cleaned_params['request_uri'] = remove_query_arg( 'allow_past', $re_cleaned_params['request_uri'] );
534 }
535
536 if (
537 ( ! empty( $local_params['time_override_arr'] ) )
538 && ( 'times_availability' === $local_params['time_override_arr']['source'] )
539 && ( count( array_filter( $local_params['dates_only_sql_arr'] ) ) > 1 )
540 ) {
541 $local_params['is_use_booking_recurrent_time'] = true;
542 }
543
544 $local_params['is_show_payment_form'] = $re_cleaned_params["is_show_payment_form"];
545
546 // FixIn: 9.9.0.35.
547 if ( $local_params['is_show_payment_form'] ) {
548 $local_params['is_show_payment_form'] = ( false !== strpos( $re_cleaned_params['request_uri'], 'is_show_payment_form=Off' ) )
549 ? 0
550 : $local_params['is_show_payment_form']; // 1|0
551 }
552
553 // Get EDIT booking data
554 $local_params['edit_resource_id'] = '';
555 $local_params['skip_booking_id'] = '';
556 $local_params['is_edit_booking'] = 0;
557 $local_params['is_duplicate_booking'] = 0;
558 $is_edit_booking = wpbc_get_data__if_edit_booking( $re_cleaned_params['booking_hash'], $re_cleaned_params['request_uri'] );
559 if ( false !== $is_edit_booking ) {
560 $local_params['edit_resource_id'] = $is_edit_booking['resource_id']; // can be parent booking resource, where we edit the booking
561 $local_params['skip_booking_id'] = $is_edit_booking['booking_id']; // booking ID
562 $local_params['is_edit_booking'] = $is_edit_booking['booking_id']; // booking ID
563 if (
564 ( ! empty( $local_params['structured_booking_data_arr']['wpbc_other_action'] ) )
565 && ( 'duplicate_booking' === $local_params['structured_booking_data_arr']['wpbc_other_action'] )
566 ){
567 $local_params['is_duplicate_booking'] = 1;
568 }
569 }
570
571 $is_frontend_ajax_edit = wp_doing_ajax()
572 && 'wpbc_ajx_booking__create' === strtolower( $request_action )
573 && 0 !== $local_params['is_edit_booking'];
574 $is_authorized_admin_edit = $is_authorized_admin_booking_request;
575
576 if (
577 $is_frontend_ajax_edit
578 && ! $is_authorized_admin_edit
579 && ! wpbc_is_visitor_booking_action_allowed( $local_params['is_edit_booking'] )
580 ) {
581 $ajx_data_arr['status'] = 'error';
582 $ajx_data_arr['status_error'] = 'visitor_booking_dates_in_past';
583 $ajx_data_arr['ajx_after_action_message'] = __( 'This booking can no longer be edited because its dates have already passed.', 'booking' );
584 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
585 return array( 'ajx_data' => $ajx_data_arr );
586 }
587 // It can be request resource ID or if we edit booking, it can be 'edit resource' - (e.g. child resource)
588 $local_params['initial_resource_id'] = ( ! empty( $local_params['edit_resource_id'] ) ) ? $local_params['edit_resource_id'] : $re_cleaned_params['resource_id'];
589
590 // 2
591 $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'] );
592
593
594 $local_params['aggregate_resource_id_arr'] = explode( ',', $re_cleaned_params['aggregate_resource_id_arr'] );
595 $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.
596 $local_params['aggregate_resource_id_arr'] = array_unique( $local_params['aggregate_resource_id_arr'] ); // Erase duplicates
597
598 // -----------------------------------------------------------------------------------------------------------------
599 // Here GO
600 // -----------------------------------------------------------------------------------------------------------------
601
602 // Force - resource saving parameters, instead of wpbc__where_to_save_booking()
603 if ( ! empty( $re_cleaned_params["save_booking_even_if_unavailable"] ) ) {
604
605 $local_params['how_many_items_to_book'] = 1;
606
607 $dates_keys_arr = array_values( $local_params['dates_only_sql_arr'] ); // [ '2023-09-23', '2023-09-24' ]
608
609 $resources_in_dates = array_fill_keys( $dates_keys_arr , array( $local_params['initial_resource_id'] ) ); // [ 2023-09-23 = [ 2 ], 2023-09-24 = [ 2 ] ]
610
611 $where_to_save_booking = array();
612 $where_to_save_booking['result'] = 'ok';
613 $where_to_save_booking['resources_in_dates'] = $resources_in_dates; // [ 2023-09-23 = [ 2, 10, 11 ], 2023-09-24 = [ 2, 10, 11 ]
614 $where_to_save_booking['time_to_book'] = $local_params['time_as_his_arr']; // [ "00:00:00", "24:00:00" ]
615 $where_to_save_booking['main__resource_id'] = $local_params['initial_resource_id']; // here edit or request (parent/single) resource
616
617 } else {
618 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
619 $php_performance = wpbc_php_performance_START( 'wpbc__where_to_save_booking' , $php_performance );
620 // </editor-fold>
621
622 /**
623 * Get slots [] where we can save booking = [ 'resources_in_dates' => [ 2023-10-18 = [ 2, 12, 10, 11 ]
624 * 2023-10-19 = [ 2, 12, 10, 11 ]
625 * 2023-10-20 = [ 2, 12, 10, 11 ]
626 * ],
627 * 'time_to_book' => [ "14:00:01" , "12:00:01" ],
628 * 'result' => 'ok'
629 * 'main__resource_id' => 2
630 * ]
631 * OR
632 * [ 'result' => 'error', 'message' => 'Booking can not be saved ...' ]
633 */
634 $where_to_save_booking = wpbc__where_to_save_booking( array(
635 'resource_id' => $local_params['initial_resource_id'], // 2 //TODO: If edit booking. What to pass 'edit' or 'parent' resource ID?
636 'skip_booking_id' => $local_params['skip_booking_id'], // '', | 125 if edit booking
637 'dates_only_sql_arr' => $local_params['dates_only_sql_arr'], // [ "2023-10-18", "2023-10-25", "2023-11-25" ]
638 'time_as_seconds_arr' => $local_params['time_as_seconds_arr'], // [ 36000, 39600 ]
639 'how_many_items_to_book' => $local_params['how_many_items_to_book'], // 1
640 'request_uri' => $re_cleaned_params['request_uri'], // 'http://beta/resource-id2/'
641 'allow_past' => ! empty( $re_cleaned_params['allow_past'] ),
642 'is_use_booking_recurrent_time' => $local_params['is_use_booking_recurrent_time'], // true | false
643 'time_override_source' => ! empty( $local_params['time_override_arr']['source'] ) ? $local_params['time_override_arr']['source'] : '',
644 'as_single_resource' => false, // false
645 'aggregate_resource_id_arr' => $local_params['aggregate_resource_id_arr'], // Optional can be ''
646 '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.
647 'custom_form' => $re_cleaned_params['custom_form'] // FixIn: 10.0.0.10.
648 ));
649 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- NO SLOTS TO SAVE " >
650 if ( 'error' == $where_to_save_booking['result'] ) {
651 $ajx_data_arr['status'] = 'error';
652 $ajx_data_arr['status_error'] = 'booking_can_not_save';
653 $ajx_data_arr['ajx_after_action_message'] = $where_to_save_booking['message'];
654 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
655 return array( 'ajx_data' => $ajx_data_arr );
656 }
657 // </editor-fold>
658
659 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
660 $php_performance = wpbc_php_performance_END( 'wpbc__where_to_save_booking' , $php_performance );
661 // </editor-fold>
662 }
663
664 if ( ! empty( $local_params['appointment_service'] ) && function_exists( 'wpbc_appointment_services_check_buffer_conflicts' ) ) {
665 $buffer_check = wpbc_appointment_services_check_buffer_conflicts(
666 $local_params['appointment_service'],
667 $where_to_save_booking['main__resource_id'],
668 array_keys( $where_to_save_booking['resources_in_dates'] ),
669 $local_params['time_as_seconds_arr'],
670 $local_params['skip_booking_id']
671 );
672 if ( is_wp_error( $buffer_check ) ) {
673 $ajx_data_arr['status'] = 'error';
674 $ajx_data_arr['status_error'] = 'appointment_service_buffer_conflict';
675 $ajx_data_arr['ajx_after_action_message'] = $buffer_check->get_error_message();
676 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
677 return array( 'ajx_data' => $ajx_data_arr );
678 }
679 }
680
681
682 // Get parameters, from REQUEST
683 $create_params = $local_params;
684 $create_params['resource_id'] = ( ! empty( $local_params['edit_resource_id'] ) )
685 ? $local_params['edit_resource_id'] // If we edit, then use original resource ???
686 : $where_to_save_booking['main__resource_id']; // Here is important TIP, resource can be where is free, and not where we submit
687 /**
688 * TODO: I think it's resolved! Just test about this situation, when we edit the booking - and it's means that we have $local_params['edit_resource_id']
689 * but what, if $where_to_save_booking do not contain this $local_params['edit_resource_id'] as available resource.
690 * or even we have $local_params['edit_resource_id'] = 2 and $where_to_save_booking contain resources like [ 1, 2, 3, 4 ]
691 * we make booking for 3 slots
692 * in this case, main resource will be 2
693 * but then when we loop resources in wpbc_db__booking_save() we will save child booking resources for dates like: 2, 3, 4 ( and it's wrong )
694 * "(205, '2023-10-04 00:00:00', 0, NULL)" <- main resource '2' e.g. $local_params['edit_resource_id'] = 2
695 * "(205, '2023-10-04 00:00:00', 0, 2)" ? <- child resource '2' e.g. [ .., 2, .. ] in $where_to_save_booking WHICH IS WRONG
696 */
697 $create_params['is_emails_send'] = $re_cleaned_params['is_emails_send'];
698 $create_params['custom_form'] = $re_cleaned_params['custom_form'];
699
700 make_bk_action( 'check_multiuser_params_for_client_side', $create_params['resource_id'] ); // Activate working with specific user in WP MU
701
702 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
703 $php_performance = wpbc_php_performance_START( 'wpbc_db__booking_save' , $php_performance );
704 // </editor-fold>
705
706 // -----------------------------------------------------------------------------------------------------------------
707 // == CREATE_THE 'NEW_BOOKING' ==
708 // -----------------------------------------------------------------------------------------------------------------
709 $create_booking_params = array(
710 'resource_id' => $create_params['resource_id'],
711 'custom_form' => $create_params['custom_form'],
712 'all_booking_data_arr' => $create_params['all_booking_data_arr'],
713 'dates_only_sql_arr' => $create_params['dates_only_sql_arr'],
714 'time_as_his_arr' => $create_params['time_as_his_arr'],
715 'is_from_admin_panel' => $create_params['is_from_admin_panel'],
716 'is_edit_booking' => $create_params['is_edit_booking'],
717 'is_duplicate_booking' => $create_params['is_duplicate_booking'],
718 'is_approve_booking' => $create_params['is_approve_booking'],
719 'how_many_items_to_book' => $create_params['how_many_items_to_book'],
720 'is_use_booking_recurrent_time' => $create_params['is_use_booking_recurrent_time'] // true | false
721 );
722 if ( ! empty( $create_params['appointment_service'] ) ) { $create_booking_params['appointment_service'] = $create_params['appointment_service']; }
723 if ( ! empty( $create_params['sync_gid'] ) ) { $create_booking_params['sync_gid'] = $create_params['sync_gid']; }
724
725 $booking_new_arr = wpbc_db__booking_save( $create_booking_params, $where_to_save_booking );
726
727 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING CREATION " >
728 if ( 'ok' !== $booking_new_arr['status'] ) {
729 $ajx_data_arr['status'] = $booking_new_arr['status'];
730 $ajx_data_arr['status_error'] = 'booking_can_not_save';
731 $ajx_data_arr['ajx_after_action_message'] = $booking_new_arr['message'];
732 $ajx_data_arr['ajx_after_action_message_status'] = 'error';
733 return array( 'ajx_data' => $ajx_data_arr );
734 }
735 // </editor-fold>
736
737 do_action( 'wpbc_booking_after_save', $booking_new_arr['booking_id'], $create_booking_params, $where_to_save_booking );
738
739 // FixIn: 9.9.0.36.
740 if (
741 ( 0 !== $create_params['is_edit_booking'] ) // If edit booking
742 && ( 1 != $create_params['is_duplicate_booking'] ) // If not duplicate
743 ) {
744 // Log the cost info.
745 $is_add_timezone_offset = true;
746 $booking_note = wpbc_date_localized( gmdate( 'Y-m-d H:i:s' ), '[Y-m-d H:i]', $is_add_timezone_offset ) . ' ';
747 $booking_note .= __( 'The booking has been edited', 'booking' ) . '. | Edit URL: ' . esc_url_raw( $re_cleaned_params['request_uri'] ) . '';
748 make_bk_action( 'wpdev_make_update_of_remark', $booking_new_arr['booking_id'], $booking_note, true );
749 }
750 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
751 $php_performance = wpbc_php_performance_END( 'wpbc_db__booking_save' , $php_performance );
752 // </editor-fold>
753
754 // -----------------------------------------------------------------------------------------------------------------
755 // Get payment form(s) and Update COST of the booking
756 // -----------------------------------------------------------------------------------------------------------------
757 $payment_params = array();
758
759 // Usually we have this: ( $str_dates__dd_mm_yyyy == $create_params['dates_only_sql_arr'] ) - but for ensure, use saved dates, e.g. $str_dates__dd_mm_yyyy
760 $payment_params['booked_dates_times_arr'] = array(
761 'dates_ymd_arr' => array_keys( $where_to_save_booking['resources_in_dates'] ), // [ 2023-10-20=>[], 2023-10-25=>[] ] -> [ "2023-10-20", "2023-10-25" ]
762 'times_his_arr' => $where_to_save_booking['time_to_book'] // ['16:00:01', '18:00:02']
763 );
764 $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']
765 $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'
766 $payment_params['booking_id'] = $booking_new_arr['booking_id']; // REQUIRED -- '2'
767 $payment_params['resource_id'] = $create_params['resource_id']; // REQUIRED -- '2' can be child resource (changed in wpbc_where_to_save() )
768 $payment_params['service_id'] = ! empty( $create_params['appointment_service']['service_id'] ) ? absint( $create_params['appointment_service']['service_id'] ) : 0;
769 $payment_params['initial_resource_id'] = $local_params['initial_resource_id']; // REQUIRED -- '2' initial calendar - parent resource
770 $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...'
771 $payment_params['times_array'] = array(
772 explode( ':', $where_to_save_booking['time_to_book'][0] ), // ["10","00","00"]
773 explode( ':', $where_to_save_booking['time_to_book'][1] ) // ["12","00","00"]
774 );
775 // Additional options
776 $payment_params['is_edit_booking'] = $create_params['is_edit_booking']; // => 0 0 | int - ID of the booking
777 $payment_params['custom_form'] = $create_params['custom_form']; // => '' '' | 'some_name'
778 $payment_params['is_duplicate_booking'] = $create_params['is_duplicate_booking']; // => 0 0 | 1
779 $payment_params['is_from_admin_panel'] = $create_params['is_from_admin_panel']; // => false true | false
780 $payment_params['is_show_payment_form'] = $create_params['is_show_payment_form']; // => 1 0 | 1
781 if ( $payment_params['is_from_admin_panel'] ) {
782 // $payment_params['is_show_payment_form'] = 0; // FixIn: 9.9.0.21.
783 }
784 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
785 $php_performance = wpbc_php_performance_START( 'wpbc_maybe_get_payment_form' , $php_performance );
786 // </editor-fold>
787
788 // GET PAYMENT FORMS ===============================================================================================
789 if ( function_exists( 'wpbc_maybe_get_payment_form' ) ) {
790
791 $response__payment_form__arr = wpbc_maybe_get_payment_form( $payment_params );
792
793 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- COSTS || PAYMENT_SYSTEMS " >
794 if (
795 ( ! empty( $response__payment_form__arr['status'] ) )
796 && ( 'ok' != $response__payment_form__arr['status'] )
797 ) {
798 $ajx_data_arr['status'] = $response__payment_form__arr['status'];
799 $ajx_data_arr['status_error'] = 'booking_can_not_save';
800 $ajx_data_arr['ajx_after_action_message'] = $response__payment_form__arr['message'];
801 $ajx_data_arr['ajx_after_action_message_status'] = 'error';
802 return array( 'ajx_data' => $ajx_data_arr );
803 }
804 // </editor-fold>
805
806 if ( ! empty( $response__payment_form__arr['costs_arr']['form_data'] ) ) {
807 $payment_params['form_data'] = $response__payment_form__arr['costs_arr']['form_data']; // we re-save it, because here can be [cost_correction] shortcode data
808 }
809 //TODO: Do we really need this in output $ajx_data_arr ???, because it stored in $confirmation
810 if ( ! empty( $response__payment_form__arr['gateways_output_arr'] ) ) {
811 $ajx_data_arr['gateways_output_arr'] = $response__payment_form__arr['gateways_output_arr'];
812 }
813 if ( function_exists( 'wpbc_if_zero_cost__approve_booking_dates' ) ) {
814 wpbc_if_zero_cost__approve_booking_dates( $payment_params['booking_id'] );
815 }
816
817 } else {
818 $response__payment_form__arr = $payment_params;
819 $response__payment_form__arr['status'] = 'ok';
820 }
821
822
823 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
824 $php_performance = wpbc_php_performance_END( 'wpbc_maybe_get_payment_form' , $php_performance );
825
826 $php_performance = wpbc_php_performance_START( 'emails_sending' , $php_performance );
827 // </editor-fold>
828
829 // -----------------------------------------------------------------------------------------------------------------
830 // == Emails ===
831 // -----------------------------------------------------------------------------------------------------------------
832 if ( 1 == $re_cleaned_params['is_emails_send'] ) {
833
834 $email_content = $payment_params['form_data'];
835
836 // If we output any text, then probably it's errors or warnings, we need to catch them
837 ob_start();
838 ob_clean();
839
840 if (
841 ( 0 === $local_params['is_edit_booking'] )
842 || ( 1 === $local_params['is_duplicate_booking'] ) // FixIn: 10.0.0.42.
843 ){
844
845 // New booking to Admin
846 wpbc_send_email_new_admin( $payment_params['booking_id'], $payment_params['resource_id'], $email_content );
847
848 // New pending to Visitor
849 wpbc_send_email_new_visitor( $payment_params['booking_id'], $payment_params['resource_id'], $email_content ) ;
850
851 $is_booking_approved = wpbc_is_booking_approved( $payment_params['booking_id'] );
852 if ( $is_booking_approved ) {
853 // New approved to Visitor / Admin
854 wpbc_send_email_approved( $payment_params['booking_id'], 1 );
855 }
856
857 do_action( 'wpbc_booking_is_approved_during_creation' , $payment_params['booking_id'] , (int) $is_booking_approved ); // FixIn: 10.10.1.1.
858
859 // Payment request from admin panel, if needed
860 if(
861 ( $payment_params['is_from_admin_panel'] )
862 && ( 'On' == get_bk_option( 'booking_payment_request_auto_send_in_bap' ) )
863 && ( function_exists( 'wpbc_send_email_payment_request' ) )
864 ){
865 $payment_reason = '';
866 $is_send = wpbc_send_email_payment_request( $payment_params['booking_id'], $payment_params['resource_id'], $email_content , $payment_reason );
867 }
868
869
870
871 } else {
872
873 // Edited booking to Visitor / Admin
874 if ( function_exists( 'wpbc_send_email_modified' ) ) {
875 wpbc_send_email_modified( $payment_params['booking_id'], $payment_params['resource_id'], $email_content );
876 }
877 }
878
879 $errors_on_email_sending_html = ob_get_contents();
880 ob_end_clean();
881
882 if ( ! empty( $errors_on_email_sending_html ) ) {
883 // Show these messages as warning after creation of the booking
884 $errors_on_email_sending_html = wp_strip_all_tags( $errors_on_email_sending_html );
885 $errors_on_email_sending_html = esc_attr( $errors_on_email_sending_html );
886 $errors_on_email_sending_html = str_replace( "\\n", '', $errors_on_email_sending_html );
887
888 $ajx_data_arr['ajx_after_action_message_status'] = 'warning';
889 $ajx_data_arr['ajx_after_action_message'] = $errors_on_email_sending_html;
890 }
891 }
892
893 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
894 $php_performance = wpbc_php_performance_END( 'emails_sending' , $php_performance );
895 // </editor-fold>
896
897 // -----------------------------------------------------------------------------------------------------------------
898 // == Track booking - New | Edit ===
899 // -----------------------------------------------------------------------------------------------------------------
900 /**
901 * Useful hook for Google Ads Conversion tracking. How to use this hook?
902 *
903 * Add code similar to this in your functions.php file in your theme, or in some other php file:
904 *
905 // Track adding new booking
906 //
907 // @param $params = array (
908 // 'str_dates__dd_mm_yyyy' => '08.10.2023,09.10.2023,10.10.2023,11.10.2023',
909 // 'booking_id' => 254,
910 // 'resource_id' => 11, // child or parent or single
911 // 'initial_resource_id' => 2, // parent or single
912 // 'form_data' => 'text^selected_short_dates_hint11^Sun...',
913 // 'times_array' => array ( array ( '14', '00', '01' ), array( '12', '00', '02' ) ),
914 // 'is_edit_booking' => 0,
915 // 'custom_form' => '',
916 // 'is_duplicate_booking' => 0,
917 // 'is_from_admin_panel' => false,
918 // 'is_show_payment_form' => 1
919 // )
920 function my_booking_tracking( $params ){
921 // Your code here
922 ?><!-- Google Code for Booking Conversion Page -->
923 <script type="text/javascript">
924 // Insert bellow your Google Conversion Code
925 </script><?php
926 }
927 add_action( 'wpbc_track_new_booking', 'my_booking_tracking' );
928 *
929 *
930 *
931 * Useful hook booking edit tracking
932 *
933 * Add code similar to this in your functions.php file in your theme, or in some other php file:
934 *
935 // Track edit existing booking
936 //
937 // @param $params = array (
938 // 'str_dates__dd_mm_yyyy' => '08.10.2023,09.10.2023,10.10.2023,11.10.2023',
939 // 'booking_id' => 254,
940 // 'resource_id' => 11, // child or parent or single
941 // 'initial_resource_id' => 2, // parent or single
942 // 'form_data' => 'text^selected_short_dates_hint11^Sun...',
943 // 'times_array' => array ( array ( '14', '00', '01' ), array( '12', '00', '02' ) ),
944 // 'is_edit_booking' => 1,
945 // 'custom_form' => '',
946 // 'is_duplicate_booking' => 0,
947 // 'is_from_admin_panel' => false,
948 // 'is_show_payment_form' => 1
949 // )
950 function my_edit_booking_tracking( $params ){
951 // Your code here
952 ?><!-- Google Code for Booking Conversion Page -->
953 <script type="text/javascript">
954 // Insert bellow your Google Conversion Code
955 </script><?php
956
957 }
958 add_action( 'wpbc_track_edit_booking', 'my_edit_booking_tracking' );
959 */
960 if ( 0 === $local_params['is_edit_booking'] ) {
961 do_action( 'wpbc_track_new_booking', $payment_params );
962 } else {
963 do_action( 'wpbc_track_edit_booking', $payment_params );
964 }
965
966
967 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
968 $php_performance = wpbc_php_performance_START( 'confirmation' , $php_performance );
969 // </editor-fold>
970
971 // <editor-fold defaultstate="collapsed" desc=" == Confirmation data == " >
972
973
974 $confirmation_params_arr = array(
975 'is_from_admin_panel' => $payment_params['is_from_admin_panel'],
976
977 'booking_id' => $booking_new_arr['booking_id'], // 16102023
978 'resource_id' => $payment_params['resource_id'], // 1
979 'form_data' => $payment_params['form_data'], // 'text^selected_short_dates_hint11^Sun...',
980 'dates_ymd_arr' => $payment_params['booked_dates_times_arr']['dates_ymd_arr'], // [ '2023-10-20', '2023-10-25' ]
981 'times_his_arr' => $payment_params['booked_dates_times_arr']['times_his_arr'], // [ '16:00:01', '18:00:02' ]
982
983 'total_cost' => ( isset( $response__payment_form__arr['costs_arr'] ) && isset( $response__payment_form__arr['costs_arr']['total_cost'] ) )
984 ? $response__payment_form__arr['costs_arr']['total_cost']
985 : 0,
986 'deposit_cost' => ( isset( $response__payment_form__arr['costs_arr'] ) && isset( $response__payment_form__arr['costs_arr']['deposit_cost'] ) )
987 ? $response__payment_form__arr['costs_arr']['deposit_cost']
988 : 0,
989 'booking_summary' => ( ( ! empty( $response__payment_form__arr['gateways_output_arr'] ) ) && ( ! empty( $response__payment_form__arr['gateways_output_arr']['booking_summary'] ) ) )
990 ? $response__payment_form__arr['gateways_output_arr']['booking_summary']
991 : '',
992 'gateway_rows' => ( ( ! empty( $response__payment_form__arr['gateways_output_arr'] ) ) && ( ! empty( $response__payment_form__arr['gateways_output_arr']['gateway_rows'] ) ) )
993 ? $response__payment_form__arr['gateways_output_arr']['gateway_rows']
994 : ''
995 );
996 // It will not show payment form in Booking > Add booking page and if defined, do not make redirect
997 if ( $payment_params['is_from_admin_panel'] ) {
998
999 $confirmation_params_arr['ty_is_redirect'] = 'message'; // Do not make redirect, if it's in admin panel!
1000
1001 // But if we edit / duplicate the booking, then do redirection to Booking Listing page // FixIn: 9.9.0.3.
1002 if (
1003 ( 0 !== $local_params['is_edit_booking'] )
1004 // && ( empty( $local_params['is_duplicate_booking'] ) )
1005 ){
1006 $confirmation_params_arr['ty_is_redirect'] = 'page';
1007 $confirmation_params_arr['ty_url'] = wpbc_get_bookings_url() . '&tab=vm_booking_listing&wh_booking_id=' . $confirmation_params_arr['booking_id'];
1008 }
1009 }
1010 $confirmation = wpbc_booking_confirmation( $confirmation_params_arr );
1011
1012 // </editor-fold>
1013
1014 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
1015 $php_performance = wpbc_php_performance_END( 'confirmation' , $php_performance );
1016 // </editor-fold>
1017
1018
1019 make_bk_action( 'finish_check_multiuser_params_for_client_side', $create_params['resource_id'] ); // Deactivate working with specific user in WP MU
1020
1021
1022 // <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
1023 $php_performance = wpbc_php_performance_END( 'total' , $php_performance );
1024 $php_performance['other_code'] = - 1 * array_reduce( $php_performance,
1025 function ( $sum, $item ) {
1026 $sum += $item;
1027 return $sum;
1028 }
1029 , - 2 * $php_performance['total'] ); // PERFORMANCE OTHER - after TOTAL
1030 // </editor-fold>
1031
1032 wpbc_clear_request_form_context();
1033
1034 return array( 'ajx_data' => $ajx_data_arr, // [ 'status' => "ok", 'wpbc_payment_output' => "<p>Dear John<br..." ]
1035 'booking_id' => $booking_new_arr['booking_id'], // 254
1036 'booking_arr' => $payment_params,
1037 'php_performance' => $php_performance, // [ ... ]
1038 'confirmation' => $confirmation // [ ]
1039 );
1040 }
1041
1042
1043 // ---------------------------------------------------------------------------------------------------------------------
1044 // New booking creation sub functions
1045 // ---------------------------------------------------------------------------------------------------------------------
1046
1047 /**
1048 * Save booking in DB
1049 *
1050 * @param array $create_params = [
1051 * 'resource_id' => 10,
1052 * 'dates_only_sql_arr' => [ '2023-10-04', '2023-10-05', '2023-10-06'],
1053 * 'time_as_his_arr' => [ '14:00:01', '16:00:02' ],
1054 * 'is_from_admin_panel' => false,
1055 * 'is_edit_booking' => 0,
1056 * 'is_duplicate_booking' => 0,
1057 * 'is_approve_booking' => 0,
1058 * 'how_many_items_to_book' => 2,
1059 * 'custom_form' => '',
1060 * 'is_use_booking_recurrent_time' => false,
1061 * 'all_booking_data_arr' => [ 'rangetime' => [
1062 * 'type' => 'selectbox-multiple',
1063 * 'original_name' => 'rangetime2[]', //NOTE: here RESOURCE ID (2) can be from Parent resource, but resource_id can rely on child (10) resource
1064 * 'name' => 'rangetime',
1065 * 'value' => '14:00 - 16:00',
1066 * ],
1067 * 'name' => [ 'type' => 'text', 'original_name' => 'name2', ... ],
1068 * ...
1069 * ]
1070 * ]
1071 *
1072 * @param $where_to_save_booking = [
1073 * 'result' = 'ok',
1074 * 'main__resource_id' = 2
1075 * 'resources_in_dates' = [ 2023-10-18 = [ 2, 12, 10, 11 ]
1076 * 2023-10-19 = [ 2, 12, 10, 11 ]
1077 * 2023-10-20 = [ 2, 12, 10, 11 ]
1078 * ],
1079 * 'time_to_book' = [ "14:00:01" , "16:00:02" ]
1080 * ]
1081 *
1082 * @return [
1083 * 'status' => 'ok' 'ok' | 'error' | 'warning'
1084 * 'booking_id' => 100 int
1085 * 'message' => '' 'If error, then here can be description of error'
1086 * '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,
1087 * ]
1088 */
1089 function wpbc_db__booking_save( &$create_params, &$where_to_save_booking ) {
1090 //FixIn: 10.11.5.4
1091 /**
1092 * Tip: $create_params['all_booking_data_arr'] - contain: [ 'field_name' => [ 'type' = "checkbox", 'original_name' = "fixed_fee2[]", 'name' = "fixed_fee", 'value' = "true" ] , ... ]
1093 * $create_params['structured_booking_data_arr'] - contain: [ 'field_name' => 'field_value' , ... ]
1094 */
1095
1096 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- 'Wrong resource ID " >
1097 if ( empty( $create_params['resource_id'] ) ) {
1098 return array( 'status' => 'error', 'message' => 'Wrong ID of booking resource: ' . $create_params['resource_id'] );
1099 }
1100 // </editor-fold>
1101
1102
1103 $defaults = array(
1104 'is_use_booking_recurrent_time' => ( 'On' === get_bk_option( 'booking_recurrent_time' ) )
1105 );
1106 $create_params = wp_parse_args( $create_params, $defaults );
1107
1108
1109 $sql_field_arr = array();
1110
1111
1112
1113 // Is it was used custom booking form ?
1114 if ( ! empty( $create_params['custom_form'] ) ) {
1115 $create_params['all_booking_data_arr']['wpbc_custom_booking_form'] = array(
1116 'type' => 'text',
1117 'original_name' => 'wpbc_custom_booking_form' . $create_params['resource_id'],
1118 'name' => 'wpbc_custom_booking_form',
1119 'value' => $create_params['custom_form']
1120 );
1121 }
1122
1123 if ( ! empty( $create_params['sync_gid'] ) ) {
1124 /**
1125 * Such fields are comming from '../wp-content/plugins/booking/core/sync/wpbc-gcal-class.php' in function run()
1126 */
1127
1128 // Escape any XSS injection from values in booking form
1129 list( $create_params['sync_gid'] ) = wpbc_escape_any_xss_in_arr( array( $create_params['sync_gid'] ) );
1130
1131 $sql_field_arr[] = array( 'name' => 'sync_gid', 'type' => '%s', 'value' => $create_params['sync_gid'] );
1132 }
1133
1134 // Escape any XSS injection from values in booking form
1135 $create_params['all_booking_data_arr'] = wpbc_escape_any_xss_in_arr( $create_params['all_booking_data_arr'] );
1136
1137
1138 //Set LAST check out day as AVAILABLE - remove it
1139 if (
1140 ( 'On' === get_bk_option( 'booking_last_checkout_day_available' ) )
1141 && ( ! empty( $create_params['dates_only_sql_arr'] ) )
1142 && ( count( $create_params['dates_only_sql_arr'] ) > 1 )
1143 ) {
1144 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.
1145 // Delete last item // FixIn: 9.9.0.19.
1146 $resources_in_dates_last_key = key( array_slice( $where_to_save_booking['resources_in_dates'], - 1, 1, true ) );
1147 unset( $where_to_save_booking['resources_in_dates'][ $resources_in_dates_last_key ] );
1148 }
1149
1150 // :: ERROR ::
1151 if ( empty( $create_params['dates_only_sql_arr'] ) ) { // No dates :?
1152 return array( 'status' => 'error', 'message' => 'Sent request with no dates.' );
1153 }
1154
1155 // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- CHECK_IN_DATE_OLDER_THAN_CHECK_OUT " >
1156 $is_no_dates_booking = (
1157 function_exists( 'wpbc_is_these_dates__for__no_dates' )
1158 && wpbc_is_these_dates__for__no_dates( $create_params['dates_only_sql_arr'] )
1159 );
1160 if ( ( count( $create_params['dates_only_sql_arr'] ) == 1 ) && ( ! $is_no_dates_booking ) ) { // Is it single selected date ?
1161
1162 // Is 'check in' date/time older than 'check out' date/time when SINGLE day for booking? Then show error.
1163
1164 /**
1165 * If we are having "change over" days activated and selected only 1 day in calendar,
1166 * then we can have error: "Warning! Number of check in != check out times.", because "check in" day older than "check out" date.
1167 */
1168
1169 $is_apply__check_in_out__10s = false;
1170 $datestamp_check_in = wpbc_convert__sql_date__to_seconds( $create_params['dates_only_sql_arr'][0] . ' ' . $create_params['time_as_his_arr'][0], $is_apply__check_in_out__10s );
1171 $datestamp_check_out = wpbc_convert__sql_date__to_seconds( $create_params['dates_only_sql_arr'][0] . ' ' . $create_params['time_as_his_arr'][1], $is_apply__check_in_out__10s );
1172
1173
1174 if ( $datestamp_check_in > $datestamp_check_out ) {
1175 $error_message = sprintf( 'Error! Your check in date %s older than check out date %s. <br>Try to select more dates or use different time.'
1176 , '<strong>' . wpbc_convert__seconds__to_sql_date( $datestamp_check_in, $is_apply__check_in_out__10s ) . '</strong>'
1177 , '<strong>' . wpbc_convert__seconds__to_sql_date( $datestamp_check_out, $is_apply__check_in_out__10s ) . '</strong>'
1178 );
1179 $error_message .= '<br>' . '<strong>Settings that can be reason of the issue:</strong> ';
1180 if( 'On' === get_bk_option( 'booking_last_checkout_day_available' )){
1181 $error_message .= '<br>' . 'You have enabled <strong>"Set check out date as available"</strong> option at Booking > Settings General page in "Calendar" section. ';
1182 }
1183 if ( 'On' === get_bk_option( 'booking_range_selection_time_is_active' ) ){
1184 $error_message .= '<br>' . 'You have enabled <strong>"Use changeover days"</strong> option at Booking > Settings General page in "Calendar" section with check-in/out times: ' . get_bk_option( 'booking_range_selection_start_time' ) . '/' . get_bk_option( 'booking_range_selection_end_time' );
1185 }
1186 if ( $create_params['is_use_booking_recurrent_time'] ) {
1187 $error_message .= '<br>' . 'You have enabled <strong>"Use time selections as recurrent time slots"</strong> option at Booking > Settings General page in "Calendar" section. ';
1188 }
1189 return array( 'status' => 'error', 'message' => $error_message );
1190 }
1191 }
1192 // </editor-fold>
1193
1194 // Compose form data for DB. Can be different resource: 'selectbox-multiple^rangetime10[]^14..' <-> 'selectbox-multiple^rangetime2[]^14..' -> 'rangetime10' - child res. Previously 'rangetime2' - parent
1195 $form_data = wpbc_encode_booking_data_to_string( $create_params['all_booking_data_arr'], $create_params['resource_id'] );
1196
1197
1198 // -----------------------------------------------------------------------------------------------------------------
1199 // APPROVED / PENDING
1200 // -----------------------------------------------------------------------------------------------------------------
1201 // Is approve booking
1202 $auto_approve_new_bookings_is_active = trim( get_bk_option( 'booking_auto_approve_new_bookings_is_active' ) );
1203 $is_approved_dates = ( $auto_approve_new_bookings_is_active == 'On' ) ? 1 : intval( $create_params['is_approve_booking'] );
1204
1205 // Auto approve only for specific booking resources
1206 /**
1207 * How to use "Auto approve bookings only for specific booking resources" ?
1208 * Add code similar to this in your functions.php file in your theme, or in some other php file:
1209 *
1210 function my_wpbc_get_booking_resources_arr_to_auto_approve( $resources_to_approve ) {
1211 $resources_to_approve = array( 9, 12, 33 ); // Array of booking resources ID, which you need to auto approve
1212 return $resources_to_approve;
1213 }
1214 add_filter( 'wpbc_get_booking_resources_arr_to_auto_approve', 'my_wpbc_get_booking_resources_arr_to_auto_approve' );
1215 */
1216 $booking_resources_to_approve = array();
1217 $booking_resources_to_approve = apply_filters( 'wpbc_get_booking_resources_arr_to_auto_approve', $booking_resources_to_approve ); // FixIn: 8.5.2.27.
1218 if ( in_array( $create_params['resource_id'], $booking_resources_to_approve ) ) {
1219 $is_approved_dates = 1;
1220 }
1221
1222 if (
1223 ( $create_params['is_from_admin_panel'] ) // true | false
1224 && ( get_bk_option( 'booking_auto_approve_bookings_if_added_in_admin_panel' ) == 'On' )
1225 ){ // FixIn: 8.1.3.27.
1226 $is_approved_dates = 1;
1227 }
1228
1229 // If the booking auto-approved, then we need to mark it as "Read".
1230 if ( $is_approved_dates ) {
1231 $sql_field_arr[] = array(
1232 'name' => 'is_new',
1233 'type' => '%d',
1234 'value' => 0,
1235 );
1236 }
1237
1238 // <editor-fold defaultstate="collapsed" desc=" == Save Booking == " >
1239 // -----------------------------------------------------------------------------------------------------------------
1240 // Save Booking
1241 // -----------------------------------------------------------------------------------------------------------------
1242 global $wpdb;
1243
1244 $sql_field_arr[] = array( 'name' => 'form', 'type' => '%s', 'value' => $form_data );
1245 $sql_field_arr[] = array( 'name' => 'booking_type', 'type' => '%d', 'value' => $create_params['resource_id'] );
1246 $sql_field_arr[] = array( 'name' => 'modification_date', 'type' => '%s', 'value' => gmdate( 'Y-m-d H:i:s' ) );
1247 $sql_field_arr[] = array( 'name' => 'sort_date', 'type' => '%s', 'value' => $create_params['dates_only_sql_arr'][0] . ' ' . $create_params['time_as_his_arr'][0] );
1248 $sql_field_arr[] = array( 'name' => 'hash', 'type' => 'MD5(%s)', 'value' => time() . '_' . wp_rand( 1000, 1000000 ) );
1249
1250
1251 if (
1252 ( 0 == $create_params['is_edit_booking'] ) || // If not edit, then INSERT.
1253 ( 1 == $create_params['is_duplicate_booking'] ) // If duplicate, then INSERT.
1254 ) {
1255 // Saved only for new booking creation.
1256 $sql_field_arr[] = array(
1257 'name' => 'creation_date',
1258 'type' => '%s',
1259 'value' => gmdate( 'Y-m-d H:i:s' ),
1260 );
1261
1262 $sql_prepare_arr = array();
1263 $sql_prepare_arr['name'] = array_map( function ( $value ) { return $value['name']; }, $sql_field_arr );
1264 $sql_prepare_arr['type'] = array_map( function ( $value ) { return $value['type']; }, $sql_field_arr );
1265 $sql_prepare_arr['value'] = array_map( function ( $value ) { return $value['value']; }, $sql_field_arr );
1266
1267 $sql_prepare_arr['name'] = implode( ', ', $sql_prepare_arr['name'] );
1268 $sql_prepare_arr['type'] = implode( ', ', $sql_prepare_arr['type'] );
1269 /* phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare */
1270 $sql = $wpdb->prepare( "INSERT INTO {$wpdb->prefix}booking " . " ( {$sql_prepare_arr['name']} )" . " VALUES ( {$sql_prepare_arr['type']} )", $sql_prepare_arr['value'] );
1271 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1272 if ( false === $wpdb->query( $sql ) ) {
1273 return array( 'status' => 'error', 'message' => 'Error. INSERT New Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql );
1274 }
1275 // Get ID of booking
1276 $booking_id = (int) $wpdb->insert_id;
1277
1278 } else { // Edit - UPDATE
1279
1280 $booking_id = (int) $create_params['is_edit_booking'];
1281
1282 $sql_prepare_arr = array();
1283 $sql_prepare_arr['set'] = array_map( function ( $value ) { return $value['name'] . '=' . $value['type']; }, $sql_field_arr );
1284 $sql_prepare_arr['value'] = array_map( function ( $value ) { return $value['value']; }, $sql_field_arr );
1285
1286 $sql_prepare_arr['set'] = implode( ', ', $sql_prepare_arr['set'] );
1287
1288 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
1289 $sql = $wpdb->prepare( "UPDATE {$wpdb->prefix}booking SET {$sql_prepare_arr['set']} WHERE booking_id={$booking_id};", $sql_prepare_arr['value'] );
1290 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1291 if ( false === $wpdb->query( $sql ) ) {
1292 return array( 'status' => 'error',
1293 'message' => 'Error. UPDATE Exist Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql,
1294 );
1295 }
1296
1297 // Check if dates previously was approved.
1298 $slct_sql = "SELECT approved FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id}) LIMIT 0,1";
1299 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1300 $slct_sql_results = $wpdb->get_results( $slct_sql );
1301 $is_approved_dates = ( count( $slct_sql_results ) > 0 ) ? $slct_sql_results[0]->approved : $is_approved_dates;
1302
1303
1304 $delete_sql = "DELETE FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id})";
1305 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1306 if ( false === $wpdb->query( $delete_sql ) ) {
1307 return array( 'status' => 'error', 'message' => 'Error. DELETE Old Dates in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $delete_sql );
1308 }
1309 }
1310 // </editor-fold>
1311
1312
1313 // <editor-fold defaultstate="collapsed" desc=" == Compose D A T E S for DB == " >
1314 // -----------------------------------------------------------------------------------------------------------------
1315 // Compose D A T E S for DB
1316 // -----------------------------------------------------------------------------------------------------------------
1317
1318 if ( class_exists( 'wpdev_bk_biz_l' ) ) {
1319 $field_names_arr = array( 'booking_id', 'booking_date', 'approved', 'type_id' );
1320 } else {
1321 $field_names_arr = array( 'booking_id', 'booking_date', 'approved' );
1322 }
1323 $field_names = implode( ', ', $field_names_arr );
1324
1325 $dates_sql = "INSERT INTO {$wpdb->prefix}bookingdates ( {$field_names} ) VALUES ";
1326 $insert_dates_arr = array();
1327
1328 $how_many_items_to_book = $create_params['how_many_items_to_book'];
1329 // $i - INDEX of child booking resource to book (if $how_many_items_to_book=1, then index always = 0 ) in [ 'resources_in_dates' = [ '2023-10-18' = [ 9, 12, 10, 11 ], ... ]...] - e.g. here = 9
1330 for( $i = 0; $i < $how_many_items_to_book; $i++) {
1331
1332 $date_number = 0;
1333 foreach ( $where_to_save_booking['resources_in_dates'] as $only_date_sql => $resources_in_date_arr ) {
1334
1335 $date_resource_id = $resources_in_date_arr[ $i ];
1336
1337 // $create_params['resource_id'] <- Main resource (saved in wp_booking )
1338 // $only_date_sql <- '2023-09-12'
1339 // $date_resource_id <- Child resource (need to save in wp_bookingdates) OR if ( $create_params['resource_id'] == $date_resource_id ) then NULL
1340
1341 // ---------------------------------------------------------------------------------------------------------
1342 // Is full day booking:
1343 if (
1344 ( '00:00' === substr( $where_to_save_booking['time_to_book'][0], 0, 5 ) )
1345 && (
1346 ( '24:00' === substr( $where_to_save_booking['time_to_book'][1], 0, 5 ) )
1347 || ( '00:00' === substr( $where_to_save_booking['time_to_book'][1], 0, 5 ) )
1348 )
1349 ){
1350 // Full days *******************************************************************************************
1351 $full_date_sql = $only_date_sql . ' 00:00:00';
1352
1353 $insert_dates_arr[] = wpbc_prepare_date_row( $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id, $create_params['resource_id'] );
1354
1355 } else { // Times
1356
1357 if (
1358 ( $create_params['is_use_booking_recurrent_time'] ) // Activated option to book times as 'time-slots' OR
1359 || ( 1 === count( $where_to_save_booking['resources_in_dates'] ) ) // Selected only 1 date, so use time as time-slot
1360 ) {
1361 // Time slots in each day **************************************************************************
1362
1363 // Start Time
1364 $full_date_sql = $only_date_sql . ' ' . $where_to_save_booking['time_to_book'][0];
1365 $insert_dates_arr[] = wpbc_prepare_date_row( $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id, $create_params['resource_id'] );
1366
1367 // End Time
1368 $full_date_sql = $only_date_sql . ' ' . $where_to_save_booking['time_to_book'][1];
1369 $insert_dates_arr[] = wpbc_prepare_date_row( $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id, $create_params['resource_id'] );
1370
1371 } else {
1372 // Check in/out ************************************************************************************
1373
1374 if ( 0 == $date_number ) { // Is check in ?
1375
1376 $full_date_sql = $only_date_sql . ' ' . $where_to_save_booking['time_to_book'][0];
1377
1378 } else if ( ( count( $where_to_save_booking['resources_in_dates'] ) - 1 ) == $date_number ) { // Is check out ?
1379
1380 $full_date_sql = $only_date_sql . ' ' . $where_to_save_booking['time_to_book'][1];
1381
1382 } else { // Middle date - Full Date
1383 $full_date_sql = $only_date_sql . ' 00:00:00';
1384 }
1385
1386 $insert_dates_arr[] = wpbc_prepare_date_row( $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id, $create_params['resource_id'] );
1387
1388 }
1389 }
1390 // ---------------------------------------------------------------------------------------------------------
1391 $date_number++;
1392 }
1393 }
1394
1395 $dates_sql .= implode( ', ', $insert_dates_arr );
1396 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
1397 if ( false === $wpdb->query( $dates_sql ) ) {
1398 return array( 'status' => 'error', 'message' => 'Error. INSERT "D A T E S" in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $dates_sql );
1399 }
1400
1401 // -----------------------------------------------------------------------------------------------------------------
1402 // End D A T E S
1403 // -----------------------------------------------------------------------------------------------------------------
1404 // </editor-fold>
1405
1406
1407 return array(
1408 'status' => 'ok'
1409 , 'booking_id' => $booking_id
1410 , 'form_data' => $form_data
1411 , 'message' => ''
1412 );
1413 }
1414
1415
1416 /**
1417 * Get SQL ROWs of VALUES for INSERT to DB
1418 *
1419 * @param int $booking_id 101 ID of the booking
1420 * @param string $full_date_sql '2023-09-12 10:00:01' SQL date
1421 * @param int $is_approved_dates 1 1 - approved, 0 - pending
1422 * @param int $date_resource_id ( >= biz_l ): 4 ID of child booking resource (if we save ONE booking in SEVERAL booking resources) field 'type_id' in wp_bookingdates
1423 * @param int $main_resource_id ( >= biz_l ): 1 ID of main (parent booking resource)
1424 *
1425 * @return string - SQL for dates VALUES to insert
1426 */
1427 function wpbc_prepare_date_row( $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id, $main_resource_id ) {
1428
1429 global $wpdb;
1430
1431 if ( class_exists( 'wpdev_bk_biz_l' ) ) {
1432 $insert_dates_arr = ( $main_resource_id != $date_resource_id )
1433 ? $wpdb->prepare( "(%d, %s, %d, %d)", $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id )
1434 : $wpdb->prepare( "(%d, %s, %d, NULL)", $booking_id, $full_date_sql, $is_approved_dates );
1435 } else {
1436 $insert_dates_arr = $wpdb->prepare( "(%d, %s, %d)", $booking_id, $full_date_sql, $is_approved_dates );
1437 }
1438
1439 return $insert_dates_arr;
1440 }
1441
1442 // == Help functions ==
1443 function wpbc_set_request_form_context( $ctx ) {
1444 $GLOBALS['wpbc_request_form_context'] = ( is_array( $ctx ) ) ? $ctx : array();
1445 }
1446
1447 function wpbc_get_request_form_context() {
1448 return ( isset( $GLOBALS['wpbc_request_form_context'] ) && is_array( $GLOBALS['wpbc_request_form_context'] ) )
1449 ? $GLOBALS['wpbc_request_form_context'] : array();
1450 }
1451
1452 function wpbc_clear_request_form_context() {
1453 if ( isset( $GLOBALS['wpbc_request_form_context'] ) ) {
1454 unset( $GLOBALS['wpbc_request_form_context'] );
1455 }
1456 }
1457
1458 // ---------------------------------------------------------------------------------------------------------------------
1459 // Support
1460 // ---------------------------------------------------------------------------------------------------------------------
1461
1462 /**
1463 * Get booking_id and resource_id if we are editing the booking, by booking hash
1464 *
1465 * @param $booking_hash
1466 * @param $server_request_url
1467 *
1468 * @return array | false false if not edit Otherwise [ 'booking_id': 100, 'resource_id': 3 ]
1469 */
1470 function wpbc_get_data__if_edit_booking( $booking_hash, $server_request_url ) {
1471
1472 $is_edit_booking = false;
1473 if ( ! empty( $booking_hash ) ) {
1474
1475 $my_booking_id_type = wpbc_hash__get_booking_id__resource_id( $booking_hash );
1476 if ( $my_booking_id_type !== false ) {
1477
1478 $is_edit_booking = array();
1479 $is_edit_booking['booking_id'] = intval( $my_booking_id_type[0] );
1480 $is_edit_booking['resource_id'] = intval( $my_booking_id_type[1] );
1481
1482 //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.
1483 // FixIn: 10.10.1.2
1484 //if ( strpos( $server_request_url, 'resource_no_update' ) === false ) { // FixIn: 9.4.2.3.
1485
1486 if ( ( function_exists( 'wpbc_is_this_child_resource' ) ) && ( wpbc_is_this_child_resource( $is_edit_booking['resource_id'] ) ) ) {
1487 $bk_parent_br_id = wpbc_get_parent_resource( $is_edit_booking['resource_id'] );
1488
1489 $is_edit_booking['resource_id'] = intval( $bk_parent_br_id );
1490 }
1491 //}
1492 }
1493 }
1494 return $is_edit_booking;
1495 }
1496
1497
1498 /**
1499 * Get how many items to book. Usually it's from [selectbox visitors "1" ... ] field.
1500 *
1501 * @param booking_form_data__arr = [
1502 * selected_short_dates_hint = "September 27, 2023 - September 28, 2023"
1503 * days_number_hint = "2"
1504 * rangetime = "16:00 - 18:00"
1505 * starttime = "21:00"
1506 * durationtime = "00:30"
1507 * name = "John"
1508 * secondname = "Smith"
1509 * email = "john.smith@server.com"
1510 * visitors = "1"
1511 * children = "0"
1512 * details = ""
1513 * ]
1514 *
1515 * @return int
1516 */
1517 function wpbc_get__how_many_items_to_book__in_booking_form( $booking_form_data__arr, $resource_id ){
1518
1519 $how_many_items_to_book = 1;
1520
1521 //TODO: Check about some URL parameter: '&resource_no_update' to book parent resource as single resource! // FixIn: 10.10.1.2
1522 if (
1523 ( class_exists( 'wpdev_bk_biz_l' ) )
1524 && ( 0 !== wpbc_get_child_resources_number( $resource_id ) ) // Here several child booking resources
1525 ) {
1526 $booking_capacity_field = wpbc_get__booking_capacity_field__name();
1527 if (
1528 ( ! empty( $booking_capacity_field ) )
1529 && ( isset( $booking_form_data__arr[ $booking_capacity_field ] ) )
1530 ){
1531 $how_many_items_to_book = intval( $booking_form_data__arr[ $booking_capacity_field ] ); // Get value of how many booking resources to book
1532 $how_many_items_to_book = ( $how_many_items_to_book > 0 ) ? $how_many_items_to_book : 1;
1533 }
1534 }
1535
1536 return $how_many_items_to_book;
1537 }
1538
1539
1540 /**
1541 * Get capacity field -- 'pure name'
1542 *
1543 * @return string - field name, or empty string - '' if not defined
1544 *
1545 * In DB, we are saved field name type and possible name of custom booking form in format: 'custom_form_name^field_type^capacity_field_name' -> 'minimal^select^adults'
1546 * or for standard form: 'select^visitors'
1547 * and here we get only field name: 'visitors'
1548 */
1549 function wpbc_get__booking_capacity_field__name() {
1550
1551 if ( class_exists( 'wpdev_bk_biz_l' ) ) {
1552
1553 if ( 'On' == get_bk_option( 'booking_quantity_control' ) ) {
1554
1555 $booking_capacity_field = get_bk_option( 'booking_capacity_field' );
1556
1557 if ( ! empty( $booking_capacity_field ) ) {
1558
1559 $booking_capacity_field = explode( '^', $booking_capacity_field );
1560
1561 if ( ! empty( $booking_capacity_field ) ) {
1562
1563 $booking_capacity_field_name = $booking_capacity_field[ count( $booking_capacity_field ) - 1 ];
1564
1565 return $booking_capacity_field_name;
1566 }
1567 }
1568 }
1569 }
1570 return '';
1571 }
1572
1573
1574 /**
1575 * Get time for booking from the booking form, as array of seconds: [ 0, 24 * 60 * 60 ] OR [ 10*60*60, 14*60*60 ]. If timefields not exist, then get time for full day booking.
1576 * @param $booking_form_data__arr
1577 *
1578 * @return array [ 0, 24 * 60 * 60 ] OR [ 10*60*60, 14*60*60 ] <- start and end time in seconds
1579 *
1580 * Example:
1581 *
1582 * // Firstly, we need to Get parsed booking form: [ name = "John", secondname = "Smith", email = "john.smith@server.com", visitors = "2",... ]
1583 * $structured_booking_data_arr = wpbc_get_parsed_booking_data_arr( $params["form_data"], $params["resource_id"], array( 'get' => 'value' ) );
1584 *
1585 * // Now get start/end times as seconds: [ 64800, 72000 ]
1586 * $time_as_seconds_arr = wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $structured_booking_data_arr );
1587 */
1588 function wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $booking_form_data__arr ){
1589
1590 $selected_time_fields = wpbc_get__selected_time_fields__in_booking_form__as_arr( $booking_form_data__arr );
1591
1592 // 2.2 Get selected SECONDS to book ---------------------------------------------------------------------------
1593 $time_as_seconds_arr = array( 0, 24 * 60 * 60 ); // Full day booking by default
1594
1595 foreach ( $selected_time_fields as $time_fields_obj ) { // { times_as_seconds: [ 21600, 23400 ], value_option_24h: '06:00 - 06:30', name: 'rangetime'}
1596
1597 if ( false !== strpos( $time_fields_obj['name'], 'rangetime' ) ) {
1598 $time_as_seconds_arr[ 0 ] = $time_fields_obj['times_as_seconds'][ 0 ];
1599 $time_as_seconds_arr[ 1 ] = $time_fields_obj['times_as_seconds'][ 1 ];
1600 //break; // If we have range-time then skip this loop
1601 }
1602 if ( false !== strpos( $time_fields_obj['name'], 'starttime' ) ) {
1603 $time_as_seconds_arr[ 0 ] = $time_fields_obj['times_as_seconds'][ 0 ];
1604 }
1605 if ( false !== strpos( $time_fields_obj['name'], 'endtime' ) ) {
1606 $time_as_seconds_arr[ 1 ] = $time_fields_obj['times_as_seconds'][ 0 ];
1607 }
1608 }
1609
1610 // For duration time we need to make a new loop, because we need to be sure that was defined START_TIME before this,
1611 // and end time was NOT defined, e.g. == (otherwise it's means that we already used END_TIME or RANGE_TIME)
1612 if (
1613 ( ( 0 ) !== $time_as_seconds_arr[ 0 ] )
1614 && ( (24 * 60 * 60 ) === $time_as_seconds_arr[ 1 ] )
1615 ){
1616 foreach ( $selected_time_fields as $time_fields_obj ) { // { times_as_seconds: [ 21600 ], value_option_24h: '06:00', name: 'durationtime'}
1617
1618 if ( false !== strpos( $time_fields_obj['name'], 'durationtime' ) ) {
1619 $time_as_seconds_arr[ 1 ] = $time_as_seconds_arr[ 0 ] + $time_fields_obj['times_as_seconds'][ 0 ];
1620 // FixIn: 10.14.7.1.
1621 while ( $time_as_seconds_arr[1] > ( 24 * 60 * 60 ) ) {
1622 $time_as_seconds_arr[1] = $time_as_seconds_arr[1] - ( 24 * 60 * 60 );
1623 }
1624 break;
1625 }
1626 }
1627 }
1628
1629 return $time_as_seconds_arr;
1630 }
1631
1632
1633 /**
1634 * Remove administrator time-override values from an unauthorized booking request.
1635 *
1636 * The public booking endpoint intentionally accepts unauthenticated requests, so
1637 * sanitizing these values is not sufficient authorization. Clearing every related
1638 * value here prevents a public client from replacing the Booking Form's configured
1639 * time while preserving the capability-protected Add Booking workflow.
1640 *
1641 * @param array $request_params Sanitized booking request parameters.
1642 * @param bool $is_authorized_admin_booking_request Whether the current request is an authorized Add Booking administration request.
1643 *
1644 * @return array Booking request parameters with unauthorized override values removed.
1645 */
1646 function wpbc_restrict_booking_time_override_to_authorized_admin( $request_params, $is_authorized_admin_booking_request ) {
1647
1648 $request_params = is_array( $request_params ) ? $request_params : array();
1649 if ( $is_authorized_admin_booking_request ) {
1650 return $request_params;
1651 }
1652
1653 $request_params['wpbc_time_override_enabled'] = 0;
1654 $request_params['wpbc_time_override_source'] = '';
1655 $request_params['wpbc_time_override_start'] = '';
1656 $request_params['wpbc_time_override_end'] = '';
1657
1658 return $request_params;
1659 }
1660
1661
1662 /**
1663 * Get explicit admin-selected time override from Add Booking modal request.
1664 *
1665 * @param array $request_params Sanitized booking request params.
1666 *
1667 * @return array Empty array or array with start/end HH:MM values.
1668 */
1669 function wpbc_get_booking_time_override__as_arr( $request_params ) {
1670
1671 if ( empty( $request_params['wpbc_time_override_enabled'] ) ) {
1672 return array();
1673 }
1674
1675 $start_time = wpbc_sanitize_booking_time_override__hm( isset( $request_params['wpbc_time_override_start'] ) ? $request_params['wpbc_time_override_start'] : '' );
1676 $end_time = wpbc_sanitize_booking_time_override__hm( isset( $request_params['wpbc_time_override_end'] ) ? $request_params['wpbc_time_override_end'] : '' );
1677
1678 if (
1679 ( '' === $start_time )
1680 || ( '' === $end_time )
1681 || ( wpbc_booking_time_override__hm_to_seconds( $start_time ) >= wpbc_booking_time_override__hm_to_seconds( $end_time ) )
1682 ) {
1683 return array();
1684 }
1685
1686 return array(
1687 'start' => $start_time,
1688 'end' => $end_time,
1689 'source' => isset( $request_params['wpbc_time_override_source'] ) ? sanitize_key( $request_params['wpbc_time_override_source'] ) : '',
1690 );
1691 }
1692
1693
1694 /**
1695 * Sanitize HH:MM value for admin booking time override.
1696 *
1697 * @param string $time_value Time value.
1698 *
1699 * @return string
1700 */
1701 function wpbc_sanitize_booking_time_override__hm( $time_value ) {
1702
1703 $time_value = trim( sanitize_text_field( (string) $time_value ) );
1704
1705 if ( ! preg_match( '/^([0-9]{1,2}):([0-9]{2})$/', $time_value, $matches ) ) {
1706 return '';
1707 }
1708
1709 $hour = absint( $matches[1] );
1710 $minute = absint( $matches[2] );
1711
1712 if ( $hour > 24 || $minute > 59 || ( 24 === $hour && 0 !== $minute ) ) {
1713 return '';
1714 }
1715
1716 return sprintf( '%02d:%02d', $hour, $minute );
1717 }
1718
1719
1720 /**
1721 * Convert HH:MM to seconds.
1722 *
1723 * @param string $time_value Time value.
1724 *
1725 * @return int
1726 */
1727 function wpbc_booking_time_override__hm_to_seconds( $time_value ) {
1728
1729 $time_arr = explode( ':', (string) $time_value );
1730
1731 return ( absint( $time_arr[0] ) * 60 * 60 ) + ( absint( $time_arr[1] ) * 60 );
1732 }
1733
1734
1735 /**
1736 * Get all time fields in the booking form as array of objects
1737 *
1738 * @param booking_form_data__arr = [
1739 * selected_short_dates_hint = "September 27, 2023 - September 28, 2023"
1740 * days_number_hint = "2"
1741 * rangetime = "16:00 - 18:00"
1742 * starttime = "21:00"
1743 * durationtime = "00:30"
1744 * name = "John"
1745 * secondname = "Smith"
1746 * email = "john.smith@server.com"
1747 * visitors = "1"
1748 * children = "0"
1749 * details = ""
1750 * ]
1751 * @returns []
1752 *
1753 * Example:
1754 * [
1755 * [
1756 * name = "rangetime"
1757 * value_option_24h = "16:00 - 18:00"
1758 * times_as_seconds = [ 57600, 64800 ]
1759 * ]
1760 * [
1761 * name = "starttime"
1762 * value_option_24h = "21:00"
1763 * times_as_seconds = [ 75600 ]
1764 * ]
1765 * [
1766 * name = "durationtime"
1767 * value_option_24h = "00:30"
1768 * times_as_seconds = [ 1800 ]
1769 * ]
1770 * ]
1771 */
1772 function wpbc_get__selected_time_fields__in_booking_form__as_arr( $booking_form_data__arr ){
1773
1774 /**
1775 * Fields with [] like this select[name="rangetime1[]"]
1776 * it's when we have 'multiple' in shortcode: [select* rangetime multiple "06:00 - 06:30" ... ]
1777 */
1778 $time_fields_arr = array( 'rangetime', 'starttime', 'endtime', 'durationtime' );
1779
1780 $time_fields_obj_arr = array();
1781
1782 // Loop all Time Fields
1783 for ( $ctf = 0; $ctf < count( $time_fields_arr ); $ctf ++ ) {
1784
1785 $time_field = $time_fields_arr[ $ctf ];
1786 if ( isset( $booking_form_data__arr[ $time_field ] ) ) {
1787
1788 $value_option_seconds_arr = explode( '-', $booking_form_data__arr[ $time_field ] );
1789 $times_as_seconds_arr = array();
1790
1791 foreach ( $value_option_seconds_arr as $time_val ) {
1792 $time_val = trim( $time_val );
1793 if ( ! empty( $time_val ) ) {
1794 $start_end_times_arr = explode( ':', $time_val );
1795 $time_in_seconds = intval( $start_end_times_arr[0] ) * 60 * 60 + intval( $start_end_times_arr[1] ) * 60;
1796 $times_as_seconds_arr[] = $time_in_seconds;
1797 }
1798 }
1799
1800 if (! empty($times_as_seconds_arr)) {
1801
1802 $time_fields_obj_arr[] = array(
1803 'name' => $time_field,
1804 'value_option_24h' => $booking_form_data__arr[ $time_field ],
1805 'times_as_seconds' => $times_as_seconds_arr
1806 );
1807 }
1808 }
1809 }
1810
1811 return $time_fields_obj_arr;
1812 }
1813
1814
1815 //TODO: 2023-10-13 16:37
1816 // - create new function for confirmation section
1817 // - Update payment request
1818 // - create redirection to the "Thank you." page and show there conformation, and payment request"
1819 // - define in the settings new Stripe and PayPal button design as default !
1820 // - Be sure that 'booking_log_booking_actions' active by default
1821 // - test it in dark theme
1822 // - test closing booked dates if all time slot was booked - it's relative to 'different time slots in dif dates'
1823 // - add migrate support old dates selection variables
1824 // - check and remove other global Js vars
1825 // - Create wizard booking form style with steps ?
1826 // - Check Booking > Availability page relative new functionality of calendar_load !
1827 // - Update last func in wpbc-booking-new.php and remove it.
1828 // - Test capacity in dates and time slots
1829 // - Next 9.9 Customizer Wizard
1830 // - Next 9.9 Update search admin UI
1831 // - Next 9.9 - update functionality in Search functionality based on similar to where_to_save function.
1832 // - Next 9.9 refactor Dates functions.
1833 // Done. - Next 9.9 Update Booking > Settings General page to show tabs vertically in left column
1834 // Create the same navigation panel at the Pro Booking > Settings > Form page. At left column custom forms, at top tolbar selectio of Booking form and "Content of booking fields data" form.
1835 // Create the same navigation for Emails.
1836 /**
1837 * TODO: Performance improvement:
1838 * "other_code": 0.003080129623413086
1839 * "wpbc__where_to_save_booking": 0.060331106185913086
1840 * "wpbc_db__booking_save": 0.023384809494018555
1841 * "wpbc_maybe_get_payment_form": 1.3650128841400146 <-
1842 * "emails_sending": 1.5024309158325195 <-
1843 * "total": 2.9542438983917236
1844 */
1845