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

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

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