PluginProbe
Booking Calendar / 10.15.7
Booking Calendar v10.15.7
11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 10.11 10.11.2 10.11.3 All 202 releases
booking / includes / _capacity / create_booking.php

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

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