PluginProbe
Booking Calendar / 11.1
Booking Calendar v11.1
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.1, at includes/_capacity/create_booking.php

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