PluginProbe
Booking Calendar / 11.5
Booking Calendar v11.5
11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 10.11 All 204 releases
booking / includes / _capacity / create_booking.php

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

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