<?php
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly // FixIn: 9.8.0.4.
// ---------------------------------------------------------------------------------------------------------------------
// == Ajax Response on creation of new booking
// ---------------------------------------------------------------------------------------------------------------------
/**
* Response to Ajax request, about loading calendar data
*
* @return void
*/
function ajax_WPBC_AJX_BOOKING__CREATE() {
/**
* Tip / translation /
* 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.
*/
// Security ------------------------------------------------------------------------------------------------------ // in Ajax Post: 'nonce': _wpbc.get_secure_param( 'nonce' ),
$action_name = 'wpbc_calendar_load_ajx' . '_wpbcnonce';
$nonce_post_key = 'nonce';
if ( wpbc_is_use_nonce_at_front_end() ) { // FixIn: 10.1.1.2.
$result_check = check_ajax_referer( $action_name, $nonce_post_key );
}
// Response AJAX parameters
$ajx_data_arr = array();
$ajx_data_arr['status'] = 'ok';
$admin_uri = ltrim( str_replace( get_site_url( null, '', 'admin' ), '', admin_url( 'admin.php?' ) ), '/' ); // 'wp-admin/admin.php?'
$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 */
// Local parameters
$local_params = array();
$local_params['is_from_admin_panel'] = ( false !== strpos( $server_http_referer_uri, $admin_uri ) ); // true | false
$local_params['user_id'] = ( isset( $_REQUEST['wpbc_ajx_user_id'] ) ) ? intval( $_REQUEST['wpbc_ajx_user_id'] ) : wpbc_get_current_user_id(); // 1
// Request parameters
$user_request = new WPBC_AJX__REQUEST( array( // Using this class here only for escaping variables
'db_option_name' => 'booking__wpbc_booking_create__request_params', // Not necessary, because we not save request, only sanitize it
'user_id' => $local_params['user_id'], // Not necessary, because we not save request, only sanitize it
'request_rules_structure' => array(
'resource_id' => array( 'validate' => 'd', 'default' => 1 ), // 'digit_or_csd'
'aggregate_resource_id_arr' => array( 'validate' => 'digit_or_csd', 'default' => '' ),
'dates_ddmmyy_csv' => array( 'validate' => 'csv_dates', 'default' => '' ), // FixIn: 9.9.1.1.
'formdata' => array( 'validate' => 'strong', 'default' => '' ),
'booking_hash' => array( 'validate' => 'strong', 'default' => '' ),
'custom_form' => array( 'validate' => 'strong', 'default' => '' ),
'captcha_chalange' => array( 'validate' => 'strong', 'default' => '' ),
'captcha_user_input' => array( 'validate' => 'strong', 'default' => '' ),
'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ),
'active_locale' => array( 'validate' => 'strong', 'default' => '' )
)
));
// Escape of request params in Ajax Post. We use prefix 'calendar_request_params', if Ajax sent - $_REQUEST['calendar_request_params']['resource_id'], ...
$request_prefix = 'calendar_request_params';
//$_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";
$request_params = $user_request->get_sanitized__in_request__value_or_default( $request_prefix ); // NOT Direct: $_REQUEST['calendar_request_params']['resource_id']
$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 */
$request_params['request_uri'] = $server_http_referer_uri; // Parameter needed for Error in booking saving and reloading calendar again with these actual parameters.
// <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- CAPTCHA " >
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
wpbc_captcha__in_ajx__check( $request_params, $local_params['is_from_admin_panel'], $_REQUEST[ $request_prefix ] );
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING_RESOURCE ID " >
if ( $request_params['resource_id'] <= 0 ) {
$ajx_data_arr['status'] = 'error';
$ajx_data_arr['status_error'] = 'resource_id_incorrect';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$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'] . ' ]';
$ajx_data_arr['ajx_after_action_message_status'] = 'error';
wp_send_json( array(
'ajx_data' => $ajx_data_arr,
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
'ajx_search_params' => $_REQUEST[ $request_prefix ],
'ajx_cleaned_params' => $request_params,
'resource_id' => $request_params['resource_id'],
) );
}
// </editor-fold>
$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 */
$request_save_params = array(
'resource_id' => $request_params['resource_id'],
'dates_ddmmyy_csv' => $request_params['dates_ddmmyy_csv'],
'form_data' => $request_params['formdata'],
'aggregate_resource_id_arr' => $request_params['aggregate_resource_id_arr'], // Optional can be ''
'booking_hash' => $request_params['booking_hash'],
'custom_form' => $request_params['custom_form'],
'is_emails_send' => $request_params['is_emails_send'],
'is_show_payment_form' => 1,
'user_id' => $local_params['user_id'],
'request_uri' => $server_http_referer_uri
);
$booking_save_arr = wpbc_booking_save( $request_save_params );
// <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING " >
if ( 'ok' !== $booking_save_arr['ajx_data']['status'] ) {
wp_send_json( array( 'ajx_data' => $booking_save_arr['ajx_data'],
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
'ajx_search_params' => $_REQUEST[ $request_prefix ],
'ajx_cleaned_params' => $request_params,
'resource_id' => $request_params['resource_id']
));
}
// </editor-fold>
$ajx_data_arr = $booking_save_arr['ajx_data'];
// 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 !
if ( empty( $ajx_data_arr['ajx_after_action_message_status'] ) ) {
$ajx_data_arr['ajx_after_action_message_status'] = 'success';
}
if ( empty( $ajx_data_arr['ajx_after_action_message'] ) ) {
$ajx_data_arr['ajx_after_action_message'] = '';
}
// $ajx_data_arr['ajx_after_action_message'] .= __( 'Booking was created with ID: ' . $booking_save_arr[ 'booking_id' ] , 'booking' );
// $ajx_data_arr['ajx_after_action_message'] .= '<hr>Total time: <strong>' . $booking_save_arr['php_performance']['total'] . ' s. </strong>';
// $ajx_data_arr['ajx_after_action_message'] .= str_replace( array( ',', '{', '}' ), '<br>', wp_json_encode( $booking_save_arr['php_performance'] ) );
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* if admin edit ?
var my_message = '<?php echo esc_js( __('Updated successfully' ,'booking') ) ; ?>';
wpbc_admin_show_message( my_message, 'success', 3000 );
location.href='<?php echo wpbc_get_bookings_url() ;?>&tab=vm_booking_listing&wh_booking_id=<?php echo $is_edit_booking['booking_id'] ; ?>';
*/
// -----------------------------------------------------------------------------------------------------------------
// == Ajax ===
// -----------------------------------------------------------------------------------------------------------------
/**
* Send JSON. It will make "wp_json_encode" - so pass only array, and This function call wp_die( '', '', array( 'response' => null, ) ) .
* Pass JS OBJ: response_data in "jQuery.post " function on success.
*
* Other End Ajax actions:
* $error_obj = new WP_Error( 'WPBC_CREATE', __( 'test error.' ), 'some/data' ); wp_send_json_error( $error_obj );
* wp_send_json_error( array( 'message' => 'invalid-api-key' ) );
* wp_send_json_success( array( 'message' =>'Ok:)' ) );
*/
wp_send_json( array(
'booking_id' => $booking_save_arr['booking_id'],
'resource_id' => $request_params['resource_id'],
'ajx_data' => $ajx_data_arr,
'ajx_confirmation' => $booking_save_arr['confirmation'],
// For debug purpose <- comment in live serv
'php_process_times' => $booking_save_arr['php_performance'],
'booking_arr' => $booking_save_arr ['booking_arr'],
// Not needed ?
// 'ajx_search_params' => $_REQUEST[ $request_prefix ],
// 'ajx_cleaned_params' => $request_params,
) );
}
// Ajax Hooks
if ( is_admin() && ( defined( 'DOING_AJAX' ) ) && ( DOING_AJAX ) ) {
add_action( 'wp_ajax_nopriv_' . 'WPBC_AJX_BOOKING__CREATE', 'ajax_' . 'WPBC_AJX_BOOKING__CREATE' ); // Client (not logged in)
add_action( 'wp_ajax_' . 'WPBC_AJX_BOOKING__CREATE', 'ajax_' . 'WPBC_AJX_BOOKING__CREATE' ); // Logged In users (or admin panel)
}
// ---------------------------------------------------------------------------------------------------------------------
// == Save Booking
// ---------------------------------------------------------------------------------------------------------------------
/**
* Save Booking - ADD NEW or UPDATE exist booking
*
* @param $request_params = [
* resource_id = 2 REQUIRED Default: 1
* dates_ddmmyy_csv = '27.10.2023, 28.10.2023, 29.10.2023' REQUIRED
* form_data = 'text^selected_short_dates_hint2^Fri, ...9, 2023~text^...' REQUIRED
* booking_hash = '' Optional Default: ''
* custom_form = '' Optional Default: ''
* is_emails_send = 1 Optional Default: 1
* is_show_payment_form => 1 Optional Default: 1
* user_id = 1 Optional Default: 0 or ID of logged-in user
* request_uri = 'http://beta/resource-id2/', // Optional Default: for front-end: $_SERVER['REQUEST_URI'] | ajax: $_SERVER['HTTP_REFERER']
*
* 'sync_gid' => 'ghjgjgjgh5f5f45f' // Really Optional: can be passed only during import .ics
* 'is_approve_booking' => 0 // Really Optional: 0 | 1
* 'save_booking_even_if_unavailable' => 0 // Really Optional: 0 | 1, if 1 then force save booking even if dates unavailable.
* ]
*
* @return [] ok: [
*
* ]
* error: [
* 'ajx_data': [ 'status':'error', 'status_error':'booking_can_not_save', 'ajx_after_action_message': 'Can not save booking', 'ajx_after_action_message_status': 'warning' ]
* ]
*
* Example:
*
* wpbc_booking_save( array(
* 'resource_id' => 2,
* 'dates_ddmmyy_csv' => '04.10.2023, 05.10.2023, 06.10.2023',
* 'form_data' => 'text^cost_hint2^150.00฿~selectbox-multiple^rangetime2[]^14:00 - 16:00~text^name2^John~text^secondname2^Smith~email^email2^[email protected]~selectbox-one^visitors2^2~selectbox-one^children2^0~textarea^details2^test',
* 'booking_hash' => '',
* 'custom_form' => '',
* 'is_emails_send' => 1,
* 'is_show_payment_form' => 1,
* 'user_id' => 1,
* 'request_uri' => 'http://beta/resource-id2/'
* ) );
*
*/
function wpbc_booking_save( $request_params ){
// <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
$php_performance = php_performance_START( 'total', array() );
// </editor-fold>
$ajx_data_arr = array();
$ajx_data_arr['status'] = 'ok';
// -----------------------------------------------------------------------------------------------------------------
// 1. Direct Clean Params
// -----------------------------------------------------------------------------------------------------------------
$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 */
$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 */
$validate_arr_rules = array(
'resource_id' => array( 'validate' => 'd', 'default' => 1 ), // INT
'dates_ddmmyy_csv' => array( 'validate' => 'csv_dates', 'default' => '' ), // FixIn: 9.9.1.1.
'form_data' => array( 'validate' => 'strong', 'default' => '' ),
'booking_hash' => array( 'validate' => 'strong', 'default' => '' ),
'custom_form' => array( 'validate' => 'strong', 'default' => '' ),
'is_emails_send' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
'is_show_payment_form' => array( 'validate' => 'd', 'default' => 1 ), // 0 | 1
'user_id' => array( 'validate' => 'd', 'default' => wpbc_get_current_user_id() ), // INT
'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
// Really Optional:
'aggregate_resource_id_arr' => array( 'validate' => 'digit_or_csd', 'default' => '' ),
//TODO: this parameter does not transfer during saving, so here will be always default value 'bookings_only' // FixIn: 10.0.0.7.
'aggregate_type' => array( 'validate' => 'strong', 'default' => 'bookings_only' ), // Optional. 'all' | 'bookings_only' <- it is depends on shortcode parameter: options="{aggregate type=bookings_only}"
'is_approve_booking' => array( 'validate' => 'd', 'default' => 0 ), // 0 | 1
'save_booking_even_if_unavailable' => array( 'validate' => 'd', 'default' => 0 ), // 0 | 1
'sync_gid' => array( 'validate' => 'strong', 'default' => '' ),
'is_use_booking_recurrent_time' => array( 'validate' => 'd', 'default' => intval( ( 'On' === get_bk_option( 'booking_recurrent_time' ) ) ) )
);
$re_cleaned_params = wpbc_sanitize_params_in_arr( $request_params, $validate_arr_rules );
$admin_uri = ltrim( str_replace( get_site_url( null, '', 'admin' ), '', admin_url( 'admin.php?' ) ), '/' ); // wp-admin/admin.php?
// -----------------------------------------------------------------------------------------------------------------
// Local parameters
// -----------------------------------------------------------------------------------------------------------------
$local_params = array();
$local_params['is_from_admin_panel'] = ( false !== strpos( $re_cleaned_params['request_uri'], $admin_uri ) ); // true | false
$local_params['user_id'] = $re_cleaned_params['user_id']; // 1
$local_params['sync_gid'] = $re_cleaned_params['sync_gid']; // ''
$local_params['is_approve_booking'] = $re_cleaned_params['is_approve_booking']; // 0 | 1
$local_params['is_use_booking_recurrent_time'] = ( 1 === $re_cleaned_params['is_use_booking_recurrent_time'] ); // false | true
// -----------------------------------------------------------------------------------------------------------------
// Parse Local parameters for later use
// -----------------------------------------------------------------------------------------------------------------
/**
* Get parsed booking form: = [ name = "John", secondname = "Smith", email = "[email protected]", visitors = "2",... ]
*/
$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' ) );
$local_params['all_booking_data_arr'] = wpbc_get_parsed_booking_data_arr( $re_cleaned_params["form_data"], $re_cleaned_params["resource_id"] );
// Important! : [ 64800, 72000 ]
$local_params['time_as_seconds_arr'] = wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $local_params['structured_booking_data_arr'] );
// [ "18:00:00", "20:00:00" ]
$time_as_seconds_arr = $local_params['time_as_seconds_arr'];
$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
$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
if ( ( 0 != $time_as_seconds_arr[0] ) && ( ( 24 * 60 * 60 ) == $time_as_seconds_arr[1] ) ) {
//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
$time_as_seconds_arr[1] += - 8;
}
$local_params['time_as_his_arr'] = array(
wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[0] ),
wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[1] )
);
// [ '2023-09-10', '2023-09-11' ]
$local_params['dates_only_sql_arr'] = wpbc_convert_dates_str__dd_mm_yyyy__to__yyyy_mm_dd( $re_cleaned_params["dates_ddmmyy_csv"] );
$local_params['dates_only_sql_arr'] = explode( ',', $local_params['dates_only_sql_arr'] );
$local_params['is_show_payment_form'] = $re_cleaned_params["is_show_payment_form"];
// FixIn: 9.9.0.35.
if ( $local_params['is_show_payment_form'] ) {
$local_params['is_show_payment_form'] = ( false !== strpos( $re_cleaned_params['request_uri'], 'is_show_payment_form=Off' ) )
? 0
: $local_params['is_show_payment_form']; // 1|0
}
// Get EDIT booking data
$local_params['edit_resource_id'] = '';
$local_params['skip_booking_id'] = '';
$local_params['is_edit_booking'] = 0;
$local_params['is_duplicate_booking'] = 0;
$is_edit_booking = wpbc_get_data__if_edit_booking( $re_cleaned_params['booking_hash'], $re_cleaned_params['request_uri'] );
if ( false !== $is_edit_booking ) {
$local_params['edit_resource_id'] = $is_edit_booking['resource_id']; // can be parent booking resource, where we edit the booking
$local_params['skip_booking_id'] = $is_edit_booking['booking_id']; // booking ID
$local_params['is_edit_booking'] = $is_edit_booking['booking_id']; // booking ID
if (
( ! empty( $local_params['structured_booking_data_arr']['wpbc_other_action'] ) )
&& ( 'duplicate_booking' === $local_params['structured_booking_data_arr']['wpbc_other_action'] )
){
$local_params['is_duplicate_booking'] = 1;
}
}
// It can be request resource ID or if we edit booking, it can be 'edit resource' - (e.g. child resource)
$local_params['initial_resource_id'] = ( ! empty( $local_params['edit_resource_id'] ) ) ? $local_params['edit_resource_id'] : $re_cleaned_params['resource_id'];
// 2
$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'] );
$local_params['aggregate_resource_id_arr'] = explode( ',', $re_cleaned_params['aggregate_resource_id_arr'] );
$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.
$local_params['aggregate_resource_id_arr'] = array_unique( $local_params['aggregate_resource_id_arr'] ); // Erase duplicates
// -----------------------------------------------------------------------------------------------------------------
// Here GO
// -----------------------------------------------------------------------------------------------------------------
// Force - resource saving parameters, instead of wpbc__where_to_save_booking()
if ( ! empty( $re_cleaned_params["save_booking_even_if_unavailable"] ) ) {
$local_params['how_many_items_to_book'] = 1;
$dates_keys_arr = array_values( $local_params['dates_only_sql_arr'] ); // [ '2023-09-23', '2023-09-24' ]
$resources_in_dates = array_fill_keys( $dates_keys_arr , array( $local_params['initial_resource_id'] ) ); // [ 2023-09-23 = [ 2 ], 2023-09-24 = [ 2 ] ]
$where_to_save_booking = array();
$where_to_save_booking['result'] = 'ok';
$where_to_save_booking['resources_in_dates'] = $resources_in_dates; // [ 2023-09-23 = [ 2, 10, 11 ], 2023-09-24 = [ 2, 10, 11 ]
$where_to_save_booking['time_to_book'] = $local_params['time_as_his_arr']; // [ "00:00:00", "24:00:00" ]
$where_to_save_booking['main__resource_id'] = $local_params['initial_resource_id']; // here edit or request (parent/single) resource
} else {
// <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
$php_performance = php_performance_START( 'wpbc__where_to_save_booking' , $php_performance );
// </editor-fold>
/**
* Get slots [] where we can save booking = [ 'resources_in_dates' => [ 2023-10-18 = [ 2, 12, 10, 11 ]
* 2023-10-19 = [ 2, 12, 10, 11 ]
* 2023-10-20 = [ 2, 12, 10, 11 ]
* ],
* 'time_to_book' => [ "14:00:01" , "12:00:01" ],
* 'result' => 'ok'
* 'main__resource_id' => 2
* ]
* OR
* [ 'result' => 'error', 'message' => 'Booking can not be saved ...' ]
*/
$where_to_save_booking = wpbc__where_to_save_booking( array(
'resource_id' => $local_params['initial_resource_id'], // 2 //TODO: If edit booking. What to pass 'edit' or 'parent' resource ID?
'skip_booking_id' => $local_params['skip_booking_id'], // '', | 125 if edit booking
'dates_only_sql_arr' => $local_params['dates_only_sql_arr'], // [ "2023-10-18", "2023-10-25", "2023-11-25" ]
'time_as_seconds_arr' => $local_params['time_as_seconds_arr'], // [ 36000, 39600 ]
'how_many_items_to_book' => $local_params['how_many_items_to_book'], // 1
'request_uri' => $re_cleaned_params['request_uri'], // 'http://beta/resource-id2/'
'is_use_booking_recurrent_time' => $local_params['is_use_booking_recurrent_time'], // true | false
'as_single_resource' => false, // false
'aggregate_resource_id_arr' => $local_params['aggregate_resource_id_arr'], // Optional can be ''
'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.
'custom_form' => $re_cleaned_params['custom_form'] // FixIn: 10.0.0.10.
));
// <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- NO SLOTS TO SAVE " >
if ( 'error' == $where_to_save_booking['result'] ) {
$ajx_data_arr['status'] = 'error';
$ajx_data_arr['status_error'] = 'booking_can_not_save';
$ajx_data_arr['ajx_after_action_message'] = $where_to_save_booking['message'];
$ajx_data_arr['ajx_after_action_message_status'] = 'warning';
return array( 'ajx_data' => $ajx_data_arr );
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
$php_performance = php_performance_END( 'wpbc__where_to_save_booking' , $php_performance );
// </editor-fold>
}
// Get parameters, from REQUEST
$create_params = $local_params;
$create_params['resource_id'] = ( ! empty( $local_params['edit_resource_id'] ) )
? $local_params['edit_resource_id'] // If we edit, then use original resource ???
: $where_to_save_booking['main__resource_id']; // Here is important TIP, resource can be where is free, and not where we submit
/**
* 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']
* but what, if $where_to_save_booking do not contain this $local_params['edit_resource_id'] as available resource.
* or even we have $local_params['edit_resource_id'] = 2 and $where_to_save_booking contain resources like [ 1, 2, 3, 4 ]
* we make booking for 3 slots
* in this case, main resource will be 2
* 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 )
* "(205, '2023-10-04 00:00:00', 0, NULL)" <- main resource '2' e.g. $local_params['edit_resource_id'] = 2
* "(205, '2023-10-04 00:00:00', 0, 2)" ? <- child resource '2' e.g. [ .., 2, .. ] in $where_to_save_booking WHICH IS WRONG
*/
$create_params['is_emails_send'] = $re_cleaned_params['is_emails_send'];
$create_params['custom_form'] = $re_cleaned_params['custom_form'];
make_bk_action( 'check_multiuser_params_for_client_side', $create_params['resource_id'] ); // Activate working with specific user in WP MU
// <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
$php_performance = php_performance_START( 'wpbc_db__booking_save' , $php_performance );
// </editor-fold>
// -----------------------------------------------------------------------------------------------------------------
// == CREATE_THE 'NEW_BOOKING' ==
// -----------------------------------------------------------------------------------------------------------------
$create_booking_params = array(
'resource_id' => $create_params['resource_id'],
'custom_form' => $create_params['custom_form'],
'all_booking_data_arr' => $create_params['all_booking_data_arr'],
'dates_only_sql_arr' => $create_params['dates_only_sql_arr'],
'time_as_his_arr' => $create_params['time_as_his_arr'],
'is_from_admin_panel' => $create_params['is_from_admin_panel'],
'is_edit_booking' => $create_params['is_edit_booking'],
'is_duplicate_booking' => $create_params['is_duplicate_booking'],
'is_approve_booking' => $create_params['is_approve_booking'],
'how_many_items_to_book' => $create_params['how_many_items_to_book'],
'is_use_booking_recurrent_time' => $create_params['is_use_booking_recurrent_time'] // true | false
);
if ( ! empty( $create_params['sync_gid'] ) ) { $create_booking_params['sync_gid'] = $create_params['sync_gid']; }
$booking_new_arr = wpbc_db__booking_save( $create_booking_params, $where_to_save_booking );
// <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING CREATION " >
if ( 'ok' !== $booking_new_arr['status'] ) {
$ajx_data_arr['status'] = $booking_new_arr['status'];
$ajx_data_arr['status_error'] = 'booking_can_not_save';
$ajx_data_arr['ajx_after_action_message'] = $booking_new_arr['message'];
$ajx_data_arr['ajx_after_action_message_status'] = 'error';
return array( 'ajx_data' => $ajx_data_arr );
}
// </editor-fold>
// FixIn: 9.9.0.36.
if (
( 0 !== $create_params['is_edit_booking'] ) // If edit booking
&& ( 1 != $create_params['is_duplicate_booking'] ) // If not duplicate
) {
// Log the cost info.
$is_add_timezone_offset = true;
$booking_note = wpbc_date_localized( gmdate( 'Y-m-d H:i:s' ), '[Y-m-d H:i]', $is_add_timezone_offset ) . ' ';
$booking_note .= __( 'The booking has been edited', 'booking' ) . '. | Edit URL: ' . esc_url_raw( $re_cleaned_params['request_uri'] ) . '';
make_bk_action( 'wpdev_make_update_of_remark', $booking_new_arr['booking_id'], $booking_note, true );
}
// <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
$php_performance = php_performance_END( 'wpbc_db__booking_save' , $php_performance );
// </editor-fold>
// -----------------------------------------------------------------------------------------------------------------
// Get payment form(s) and Update COST of the booking
// -----------------------------------------------------------------------------------------------------------------
$payment_params = array();
// 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
$payment_params['booked_dates_times_arr'] = array(
'dates_ymd_arr' => array_keys( $where_to_save_booking['resources_in_dates'] ), // [ 2023-10-20=>[], 2023-10-25=>[] ] -> [ "2023-10-20", "2023-10-25" ]
'times_his_arr' => $where_to_save_booking['time_to_book'] // ['16:00:01', '18:00:02']
);
$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']
$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'
$payment_params['booking_id'] = $booking_new_arr['booking_id']; // REQUIRED -- '2'
$payment_params['resource_id'] = $create_params['resource_id']; // REQUIRED -- '2' can be child resource (changed in wpbc_where_to_save() )
$payment_params['initial_resource_id'] = $local_params['initial_resource_id']; // REQUIRED -- '2' initial calendar - parent resource
$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...'
$payment_params['times_array'] = array(
explode( ':', $where_to_save_booking['time_to_book'][0] ), // ["10","00","00"]
explode( ':', $where_to_save_booking['time_to_book'][1] ) // ["12","00","00"]
);
// Additional options
$payment_params['is_edit_booking'] = $create_params['is_edit_booking']; // => 0 0 | int - ID of the booking
$payment_params['custom_form'] = $create_params['custom_form']; // => '' '' | 'some_name'
$payment_params['is_duplicate_booking'] = $create_params['is_duplicate_booking']; // => 0 0 | 1
$payment_params['is_from_admin_panel'] = $create_params['is_from_admin_panel']; // => false true | false
$payment_params['is_show_payment_form'] = $create_params['is_show_payment_form']; // => 1 0 | 1
if ( $payment_params['is_from_admin_panel'] ) {
// $payment_params['is_show_payment_form'] = 0; // FixIn: 9.9.0.21.
}
// <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
$php_performance = php_performance_START( 'wpbc_maybe_get_payment_form' , $php_performance );
// </editor-fold>
// GET PAYMENT FORMS ===============================================================================================
if ( function_exists( 'wpbc_maybe_get_payment_form' ) ) {
$response__payment_form__arr = wpbc_maybe_get_payment_form( $payment_params );
// <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- COSTS || PAYMENT_SYSTEMS " >
if (
( ! empty( $response__payment_form__arr['status'] ) )
&& ( 'ok' != $response__payment_form__arr['status'] )
) {
$ajx_data_arr['status'] = $response__payment_form__arr['status'];
$ajx_data_arr['status_error'] = 'booking_can_not_save';
$ajx_data_arr['ajx_after_action_message'] = $response__payment_form__arr['message'];
$ajx_data_arr['ajx_after_action_message_status'] = 'error';
return array( 'ajx_data' => $ajx_data_arr );
}
// </editor-fold>
if ( ! empty( $response__payment_form__arr['costs_arr']['form_data'] ) ) {
$payment_params['form_data'] = $response__payment_form__arr['costs_arr']['form_data']; // we re-save it, because here can be [cost_correction] shortcode data
}
//TODO: Do we really need this in output $ajx_data_arr ???, because it stored in $confirmation
if ( ! empty( $response__payment_form__arr['gateways_output_arr'] ) ) {
$ajx_data_arr['gateways_output_arr'] = $response__payment_form__arr['gateways_output_arr'];
}
if ( function_exists( 'wpbc_if_zero_cost__approve_booking_dates' ) ) {
wpbc_if_zero_cost__approve_booking_dates( $payment_params['booking_id'] );
}
} else {
$response__payment_form__arr = $payment_params;
$response__payment_form__arr['status'] = 'ok';
}
// <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
$php_performance = php_performance_END( 'wpbc_maybe_get_payment_form' , $php_performance );
$php_performance = php_performance_START( 'emails_sending' , $php_performance );
// </editor-fold>
// -----------------------------------------------------------------------------------------------------------------
// == Emails ===
// -----------------------------------------------------------------------------------------------------------------
if ( 1 == $re_cleaned_params['is_emails_send'] ) {
$email_content = $payment_params['form_data'];
// If we output any text, then probably it's errors or warnings, we need to catch them
ob_start();
ob_clean();
if (
( 0 === $local_params['is_edit_booking'] )
|| ( 1 === $local_params['is_duplicate_booking'] ) // FixIn: 10.0.0.42.
){
// New booking to Admin
wpbc_send_email_new_admin( $payment_params['booking_id'], $payment_params['resource_id'], $email_content );
// New pending to Visitor
wpbc_send_email_new_visitor( $payment_params['booking_id'], $payment_params['resource_id'], $email_content ) ;
$is_booking_approved = wpbc_is_booking_approved( $payment_params['booking_id'] );
if ( $is_booking_approved ) {
// New approved to Visitor / Admin
wpbc_send_email_approved( $payment_params['booking_id'], 1 );
}
do_action( 'wpbc_booking_is_approved_during_creation' , $payment_params['booking_id'] , (int) $is_booking_approved ); // FixIn: 10.10.1.1.
// Payment request from admin panel, if needed
if(
( $payment_params['is_from_admin_panel'] )
&& ( 'On' == get_bk_option( 'booking_payment_request_auto_send_in_bap' ) )
&& ( function_exists( 'wpbc_send_email_payment_request' ) )
){
$payment_reason = '';
$is_send = wpbc_send_email_payment_request( $payment_params['booking_id'], $payment_params['resource_id'], $email_content , $payment_reason );
}
} else {
// Edited booking to Visitor / Admin
if ( function_exists( 'wpbc_send_email_modified' ) ) {
wpbc_send_email_modified( $payment_params['booking_id'], $payment_params['resource_id'], $email_content );
}
}
$errors_on_email_sending_html = ob_get_contents();
ob_end_clean();
if ( ! empty( $errors_on_email_sending_html ) ) {
// Show these messages as warning after creation of the booking
$errors_on_email_sending_html = wp_strip_all_tags( $errors_on_email_sending_html );
$errors_on_email_sending_html = esc_attr( $errors_on_email_sending_html );
$errors_on_email_sending_html = str_replace( "\\n", '', $errors_on_email_sending_html );
$ajx_data_arr['ajx_after_action_message_status'] = 'warning';
$ajx_data_arr['ajx_after_action_message'] = $errors_on_email_sending_html;
}
}
// <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
$php_performance = php_performance_END( 'emails_sending' , $php_performance );
// </editor-fold>
// -----------------------------------------------------------------------------------------------------------------
// == Track booking - New | Edit ===
// -----------------------------------------------------------------------------------------------------------------
/**
* Useful hook for Google Ads Conversion tracking. How to use this hook?
*
* Add code similar to this in your functions.php file in your theme, or in some other php file:
*
// Track adding new booking
//
// @param $params = array (
// 'str_dates__dd_mm_yyyy' => '08.10.2023,09.10.2023,10.10.2023,11.10.2023',
// 'booking_id' => 254,
// 'resource_id' => 11, // child or parent or single
// 'initial_resource_id' => 2, // parent or single
// 'form_data' => 'text^selected_short_dates_hint11^Sun...',
// 'times_array' => array ( array ( '14', '00', '01' ), array( '12', '00', '02' ) ),
// 'is_edit_booking' => 0,
// 'custom_form' => '',
// 'is_duplicate_booking' => 0,
// 'is_from_admin_panel' => false,
// 'is_show_payment_form' => 1
// )
function my_booking_tracking( $params ){
// Your code here
?><!-- Google Code for Booking Conversion Page -->
<script type="text/javascript">
// Insert bellow your Google Conversion Code
</script><?php
}
add_action( 'wpbc_track_new_booking', 'my_booking_tracking' );
*
*
*
* Useful hook booking edit tracking
*
* Add code similar to this in your functions.php file in your theme, or in some other php file:
*
// Track edit existing booking
//
// @param $params = array (
// 'str_dates__dd_mm_yyyy' => '08.10.2023,09.10.2023,10.10.2023,11.10.2023',
// 'booking_id' => 254,
// 'resource_id' => 11, // child or parent or single
// 'initial_resource_id' => 2, // parent or single
// 'form_data' => 'text^selected_short_dates_hint11^Sun...',
// 'times_array' => array ( array ( '14', '00', '01' ), array( '12', '00', '02' ) ),
// 'is_edit_booking' => 1,
// 'custom_form' => '',
// 'is_duplicate_booking' => 0,
// 'is_from_admin_panel' => false,
// 'is_show_payment_form' => 1
// )
function my_edit_booking_tracking( $params ){
// Your code here
?><!-- Google Code for Booking Conversion Page -->
<script type="text/javascript">
// Insert bellow your Google Conversion Code
</script><?php
}
add_action( 'wpbc_track_edit_booking', 'my_edit_booking_tracking' );
*/
if ( 0 === $local_params['is_edit_booking'] ) {
do_action( 'wpbc_track_new_booking', $payment_params );
} else {
do_action( 'wpbc_track_edit_booking', $payment_params );
}
// <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
$php_performance = php_performance_START( 'confirmation' , $php_performance );
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc=" == Confirmation data == " >
$confirmation_params_arr = array(
'is_from_admin_panel' => $payment_params['is_from_admin_panel'],
'booking_id' => $booking_new_arr['booking_id'], // 16102023
'resource_id' => $payment_params['resource_id'], // 1
'form_data' => $payment_params['form_data'], // 'text^selected_short_dates_hint11^Sun...',
'dates_ymd_arr' => $payment_params['booked_dates_times_arr']['dates_ymd_arr'], // [ '2023-10-20', '2023-10-25' ]
'times_his_arr' => $payment_params['booked_dates_times_arr']['times_his_arr'], // [ '16:00:01', '18:00:02' ]
'total_cost' => ( isset( $response__payment_form__arr['costs_arr'] ) && isset( $response__payment_form__arr['costs_arr']['total_cost'] ) )
? $response__payment_form__arr['costs_arr']['total_cost']
: 0,
'deposit_cost' => ( isset( $response__payment_form__arr['costs_arr'] ) && isset( $response__payment_form__arr['costs_arr']['deposit_cost'] ) )
? $response__payment_form__arr['costs_arr']['deposit_cost']
: 0,
'booking_summary' => ( ( ! empty( $response__payment_form__arr['gateways_output_arr'] ) ) && ( ! empty( $response__payment_form__arr['gateways_output_arr']['booking_summary'] ) ) )
? $response__payment_form__arr['gateways_output_arr']['booking_summary']
: '',
'gateway_rows' => ( ( ! empty( $response__payment_form__arr['gateways_output_arr'] ) ) && ( ! empty( $response__payment_form__arr['gateways_output_arr']['gateway_rows'] ) ) )
? $response__payment_form__arr['gateways_output_arr']['gateway_rows']
: ''
);
// It will not show payment form in Booking > Add booking page and if defined, do not make redirect
if ( $payment_params['is_from_admin_panel'] ) {
$confirmation_params_arr['ty_is_redirect'] = 'message'; // Do not make redirect, if it's in admin panel!
// But if we edit / duplicate the booking, then do redirection to Booking Listing page // FixIn: 9.9.0.3.
if (
( 0 !== $local_params['is_edit_booking'] )
// && ( empty( $local_params['is_duplicate_booking'] ) )
){
$confirmation_params_arr['ty_is_redirect'] = 'page';
$confirmation_params_arr['ty_url'] = wpbc_get_bookings_url() . '&tab=vm_booking_listing&wh_booking_id=' . $confirmation_params_arr['booking_id'];
}
}
$confirmation = wpbc_booking_confirmation( $confirmation_params_arr );
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
$php_performance = php_performance_END( 'confirmation' , $php_performance );
// </editor-fold>
make_bk_action( 'finish_check_multiuser_params_for_client_side', $create_params['resource_id'] ); // Deactivate working with specific user in WP MU
// <editor-fold defaultstate="collapsed" desc=" = PERFORMANCE = " >
$php_performance = php_performance_END( 'total' , $php_performance );
$php_performance['other_code'] = - 1 * array_reduce( $php_performance,
function ( $sum, $item ) {
$sum += $item;
return $sum;
}
, - 2 * $php_performance['total'] ); // PERFORMANCE OTHER - after TOTAL
// </editor-fold>
return array( 'ajx_data' => $ajx_data_arr, // [ 'status' => "ok", 'wpbc_payment_output' => "<p>Dear John<br..." ]
'booking_id' => $booking_new_arr['booking_id'], // 254
'booking_arr' => $payment_params,
'php_performance' => $php_performance, // [ ... ]
'confirmation' => $confirmation // [ ]
);
}
// ---------------------------------------------------------------------------------------------------------------------
// New booking creation sub functions
// ---------------------------------------------------------------------------------------------------------------------
/**
* Save booking in DB
*
* @param array $create_params = [
* 'resource_id' => 10,
* 'dates_only_sql_arr' => [ '2023-10-04', '2023-10-05', '2023-10-06'],
* 'time_as_his_arr' => [ '14:00:01', '16:00:02' ],
* 'is_from_admin_panel' => false,
* 'is_edit_booking' => 0,
* 'is_duplicate_booking' => 0,
* 'is_approve_booking' => 0,
* 'how_many_items_to_book' => 2,
* 'custom_form' => '',
* 'is_use_booking_recurrent_time' => false,
* 'all_booking_data_arr' => [ 'rangetime' => [
* 'type' => 'selectbox-multiple',
* 'original_name' => 'rangetime2[]', //NOTE: here RESOURCE ID (2) can be from Parent resource, but resource_id can rely on child (10) resource
* 'name' => 'rangetime',
* 'value' => '14:00 - 16:00',
* ],
* 'name' => [ 'type' => 'text', 'original_name' => 'name2', ... ],
* ...
* ]
* ]
*
* @param $where_to_save_booking = [
* 'result' = 'ok',
* 'main__resource_id' = 2
* 'resources_in_dates' = [ 2023-10-18 = [ 2, 12, 10, 11 ]
* 2023-10-19 = [ 2, 12, 10, 11 ]
* 2023-10-20 = [ 2, 12, 10, 11 ]
* ],
* 'time_to_book' = [ "14:00:01" , "16:00:02" ]
* ]
*
* @return [
* 'status' => 'ok' 'ok' | 'error' | 'warning'
* 'booking_id' => 100 int
* 'message' => '' 'If error, then here can be description of error'
* '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,
* ]
*/
function wpbc_db__booking_save( &$create_params, &$where_to_save_booking ) {
//FixIn: 10.11.5.4
/**
* Tip: $create_params['all_booking_data_arr'] - contain: [ 'field_name' => [ 'type' = "checkbox", 'original_name' = "fixed_fee2[]", 'name' = "fixed_fee", 'value' = "true" ] , ... ]
* $create_params['structured_booking_data_arr'] - contain: [ 'field_name' => 'field_value' , ... ]
*/
// <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- 'Wrong resource ID " >
if ( empty( $create_params['resource_id'] ) ) {
return array( 'status' => 'error', 'message' => 'Wrong ID of booking resource: ' . $create_params['resource_id'] );
}
// </editor-fold>
$defaults = array(
'is_use_booking_recurrent_time' => ( 'On' === get_bk_option( 'booking_recurrent_time' ) )
);
$create_params = wp_parse_args( $create_params, $defaults );
$sql_field_arr = array();
// Is it was used custom booking form ?
if ( ! empty( $create_params['custom_form'] ) ) {
$create_params['all_booking_data_arr']['wpbc_custom_booking_form'] = array(
'type' => 'text',
'original_name' => 'wpbc_custom_booking_form' . $create_params['resource_id'],
'name' => 'wpbc_custom_booking_form',
'value' => $create_params['custom_form']
);
}
if ( ! empty( $create_params['sync_gid'] ) ) {
/**
* Such fields are comming from '../wp-content/plugins/booking/core/sync/wpbc-gcal-class.php' in function run()
*/
// Escape any XSS injection from values in booking form
list( $create_params['sync_gid'] ) = wpbc_escape_any_xss_in_arr( array( $create_params['sync_gid'] ) );
$sql_field_arr[] = array( 'name' => 'sync_gid', 'type' => '%s', 'value' => $create_params['sync_gid'] );
}
// Escape any XSS injection from values in booking form
$create_params['all_booking_data_arr'] = wpbc_escape_any_xss_in_arr( $create_params['all_booking_data_arr'] );
//Set LAST check out day as AVAILABLE - remove it
if (
( 'On' === get_bk_option( 'booking_last_checkout_day_available' ) )
&& ( ! empty( $create_params['dates_only_sql_arr'] ) )
&& ( count( $create_params['dates_only_sql_arr'] ) > 1 )
) {
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.
// Delete last item // FixIn: 9.9.0.19.
$resources_in_dates_last_key = key( array_slice( $where_to_save_booking['resources_in_dates'], - 1, 1, true ) );
unset( $where_to_save_booking['resources_in_dates'][ $resources_in_dates_last_key ] );
}
// :: ERROR ::
if ( empty( $create_params['dates_only_sql_arr'] ) ) { // No dates :?
return array( 'status' => 'error', 'message' => 'Sent request with no dates.' );
}
// <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- CHECK_IN_DATE_OLDER_THAN_CHECK_OUT " >
if ( count( $create_params['dates_only_sql_arr'] ) == 1 ) { // Is it single selected date ?
// Is 'check in' date/time older than 'check out' date/time when SINGLE day for booking? Then show error.
/**
* If we are having "change over" days activated and selected only 1 day in calendar,
* then we can have error: "Warning! Number of check in != check out times.", because "check in" day older than "check out" date.
*/
$is_apply__check_in_out__10s = false;
$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 );
$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 );
if ( $datestamp_check_in > $datestamp_check_out ) {
$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.'
, '<strong>' . wpbc_convert__seconds__to_sql_date( $datestamp_check_in, $is_apply__check_in_out__10s ) . '</strong>'
, '<strong>' . wpbc_convert__seconds__to_sql_date( $datestamp_check_out, $is_apply__check_in_out__10s ) . '</strong>'
);
$error_message .= '<br>' . '<strong>Settings that can be reason of the issue:</strong> ';
if( 'On' === get_bk_option( 'booking_last_checkout_day_available' )){
$error_message .= '<br>' . 'You have enabled <strong>"Set check out date as available"</strong> option at Booking > Settings General page in "Calendar" section. ';
}
if ( 'On' === get_bk_option( 'booking_range_selection_time_is_active' ) ){
$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' );
}
if ( $create_params['is_use_booking_recurrent_time'] ) {
$error_message .= '<br>' . 'You have enabled <strong>"Use time selections as recurrent time slots"</strong> option at Booking > Settings General page in "Calendar" section. ';
}
return array( 'status' => 'error', 'message' => $error_message );
}
}
// </editor-fold>
// Compose form data for DB. Can be different resource: 'selectbox-multiple^rangetime10[]^14..' <-> 'selectbox-multiple^rangetime2[]^14..' -> 'rangetime10' - child res. Previously 'rangetime2' - parent
$form_data = wpbc_encode_booking_data_to_string( $create_params['all_booking_data_arr'], $create_params['resource_id'] );
// -----------------------------------------------------------------------------------------------------------------
// APPROVED / PENDING
// -----------------------------------------------------------------------------------------------------------------
// Is approve booking
$auto_approve_new_bookings_is_active = trim( get_bk_option( 'booking_auto_approve_new_bookings_is_active' ) );
$is_approved_dates = ( $auto_approve_new_bookings_is_active == 'On' ) ? 1 : intval( $create_params['is_approve_booking'] );
// Auto approve only for specific booking resources
/**
* How to use "Auto approve bookings only for specific booking resources" ?
* Add code similar to this in your functions.php file in your theme, or in some other php file:
*
function my_wpbc_get_booking_resources_arr_to_auto_approve( $resources_to_approve ) {
$resources_to_approve = array( 9, 12, 33 ); // Array of booking resources ID, which you need to auto approve
return $resources_to_approve;
}
add_filter( 'wpbc_get_booking_resources_arr_to_auto_approve', 'my_wpbc_get_booking_resources_arr_to_auto_approve' );
*/
$booking_resources_to_approve = array();
$booking_resources_to_approve = apply_filters( 'wpbc_get_booking_resources_arr_to_auto_approve', $booking_resources_to_approve ); // FixIn: 8.5.2.27.
if ( in_array( $create_params['resource_id'], $booking_resources_to_approve ) ) {
$is_approved_dates = 1;
}
if (
( $create_params['is_from_admin_panel'] ) // true | false
&& ( get_bk_option( 'booking_auto_approve_bookings_if_added_in_admin_panel' ) == 'On' )
){ // FixIn: 8.1.3.27.
$is_approved_dates = 1;
}
// If the booking auto-approved, then we need to mark it as "Read".
if ( $is_approved_dates ) {
$sql_field_arr[] = array(
'name' => 'is_new',
'type' => '%d',
'value' => 0,
);
}
// <editor-fold defaultstate="collapsed" desc=" == Save Booking == " >
// -----------------------------------------------------------------------------------------------------------------
// Save Booking
// -----------------------------------------------------------------------------------------------------------------
global $wpdb;
$sql_field_arr[] = array( 'name' => 'form', 'type' => '%s', 'value' => $form_data );
$sql_field_arr[] = array( 'name' => 'booking_type', 'type' => '%d', 'value' => $create_params['resource_id'] );
$sql_field_arr[] = array( 'name' => 'modification_date', 'type' => '%s', 'value' => gmdate( 'Y-m-d H:i:s' ) );
$sql_field_arr[] = array( 'name' => 'sort_date', 'type' => '%s', 'value' => $create_params['dates_only_sql_arr'][0] . ' ' . $create_params['time_as_his_arr'][0] );
$sql_field_arr[] = array( 'name' => 'hash', 'type' => 'MD5(%s)', 'value' => time() . '_' . wp_rand( 1000, 1000000 ) );
if (
( 0 == $create_params['is_edit_booking'] ) || // If not edit, then INSERT.
( 1 == $create_params['is_duplicate_booking'] ) // If duplicate, then INSERT.
) {
// Saved only for new booking creation.
$sql_field_arr[] = array(
'name' => 'creation_date',
'type' => '%s',
'value' => gmdate( 'Y-m-d H:i:s' ),
);
$sql_prepare_arr = array();
$sql_prepare_arr['name'] = array_map( function ( $value ) { return $value['name']; }, $sql_field_arr );
$sql_prepare_arr['type'] = array_map( function ( $value ) { return $value['type']; }, $sql_field_arr );
$sql_prepare_arr['value'] = array_map( function ( $value ) { return $value['value']; }, $sql_field_arr );
$sql_prepare_arr['name'] = implode( ', ', $sql_prepare_arr['name'] );
$sql_prepare_arr['type'] = implode( ', ', $sql_prepare_arr['type'] );
/* phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare */
$sql = $wpdb->prepare( "INSERT INTO {$wpdb->prefix}booking " . " ( {$sql_prepare_arr['name']} )" . " VALUES ( {$sql_prepare_arr['type']} )", $sql_prepare_arr['value'] );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
if ( false === $wpdb->query( $sql ) ) {
return array( 'status' => 'error', 'message' => 'Error. INSERT New Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql );
}
// Get ID of booking
$booking_id = (int) $wpdb->insert_id;
} else { // Edit - UPDATE
$booking_id = (int) $create_params['is_edit_booking'];
$sql_prepare_arr = array();
$sql_prepare_arr['set'] = array_map( function ( $value ) { return $value['name'] . '=' . $value['type']; }, $sql_field_arr );
$sql_prepare_arr['value'] = array_map( function ( $value ) { return $value['value']; }, $sql_field_arr );
$sql_prepare_arr['set'] = implode( ', ', $sql_prepare_arr['set'] );
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
$sql = $wpdb->prepare( "UPDATE {$wpdb->prefix}booking SET {$sql_prepare_arr['set']} WHERE booking_id={$booking_id};", $sql_prepare_arr['value'] );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
if ( false === $wpdb->query( $sql ) ) {
return array( 'status' => 'error',
'message' => 'Error. UPDATE Exist Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql,
);
}
// Check if dates previously was approved.
$slct_sql = "SELECT approved FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id}) LIMIT 0,1";
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
$slct_sql_results = $wpdb->get_results( $slct_sql );
$is_approved_dates = ( count( $slct_sql_results ) > 0 ) ? $slct_sql_results[0]->approved : $is_approved_dates;
$delete_sql = "DELETE FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id})";
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
if ( false === $wpdb->query( $delete_sql ) ) {
return array( 'status' => 'error', 'message' => 'Error. DELETE Old Dates in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $delete_sql );
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc=" == Compose D A T E S for DB == " >
// -----------------------------------------------------------------------------------------------------------------
// Compose D A T E S for DB
// -----------------------------------------------------------------------------------------------------------------
if ( class_exists( 'wpdev_bk_biz_l' ) ) {
$field_names_arr = array( 'booking_id', 'booking_date', 'approved', 'type_id' );
} else {
$field_names_arr = array( 'booking_id', 'booking_date', 'approved' );
}
$field_names = implode( ', ', $field_names_arr );
$dates_sql = "INSERT INTO {$wpdb->prefix}bookingdates ( {$field_names} ) VALUES ";
$insert_dates_arr = array();
$how_many_items_to_book = $create_params['how_many_items_to_book'];
// $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
for( $i = 0; $i < $how_many_items_to_book; $i++) {
$date_number = 0;
foreach ( $where_to_save_booking['resources_in_dates'] as $only_date_sql => $resources_in_date_arr ) {
$date_resource_id = $resources_in_date_arr[ $i ];
// $create_params['resource_id'] <- Main resource (saved in wp_booking )
// $only_date_sql <- '2023-09-12'
// $date_resource_id <- Child resource (need to save in wp_bookingdates) OR if ( $create_params['resource_id'] == $date_resource_id ) then NULL
// ---------------------------------------------------------------------------------------------------------
// Is full day booking:
if (
( '00:00' === substr( $where_to_save_booking['time_to_book'][0], 0, 5 ) )
&& (
( '24:00' === substr( $where_to_save_booking['time_to_book'][1], 0, 5 ) )
|| ( '00:00' === substr( $where_to_save_booking['time_to_book'][1], 0, 5 ) )
)
){
// Full days *******************************************************************************************
$full_date_sql = $only_date_sql . ' 00:00:00';
$insert_dates_arr[] = wpbc_prepare_date_row( $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id, $create_params['resource_id'] );
} else { // Times
if (
( $create_params['is_use_booking_recurrent_time'] ) // Activated option to book times as 'time-slots' OR
|| ( 1 === count( $where_to_save_booking['resources_in_dates'] ) ) // Selected only 1 date, so use time as time-slot
) {
// Time slots in each day **************************************************************************
// Start Time
$full_date_sql = $only_date_sql . ' ' . $where_to_save_booking['time_to_book'][0];
$insert_dates_arr[] = wpbc_prepare_date_row( $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id, $create_params['resource_id'] );
// End Time
$full_date_sql = $only_date_sql . ' ' . $where_to_save_booking['time_to_book'][1];
$insert_dates_arr[] = wpbc_prepare_date_row( $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id, $create_params['resource_id'] );
} else {
// Check in/out ************************************************************************************
if ( 0 == $date_number ) { // Is check in ?
$full_date_sql = $only_date_sql . ' ' . $where_to_save_booking['time_to_book'][0];
} else if ( ( count( $where_to_save_booking['resources_in_dates'] ) - 1 ) == $date_number ) { // Is check out ?
$full_date_sql = $only_date_sql . ' ' . $where_to_save_booking['time_to_book'][1];
} else { // Middle date - Full Date
$full_date_sql = $only_date_sql . ' 00:00:00';
}
$insert_dates_arr[] = wpbc_prepare_date_row( $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id, $create_params['resource_id'] );
}
}
// ---------------------------------------------------------------------------------------------------------
$date_number++;
}
}
$dates_sql .= implode( ', ', $insert_dates_arr );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
if ( false === $wpdb->query( $dates_sql ) ) {
return array( 'status' => 'error', 'message' => 'Error. INSERT "D A T E S" in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $dates_sql );
}
// -----------------------------------------------------------------------------------------------------------------
// End D A T E S
// -----------------------------------------------------------------------------------------------------------------
// </editor-fold>
return array(
'status' => 'ok'
, 'booking_id' => $booking_id
, 'form_data' => $form_data
, 'message' => ''
);
}
/**
* Get SQL ROWs of VALUES for INSERT to DB
*
* @param int $booking_id 101 ID of the booking
* @param string $full_date_sql '2023-09-12 10:00:01' SQL date
* @param int $is_approved_dates 1 1 - approved, 0 - pending
* @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
* @param int $main_resource_id ( >= biz_l ): 1 ID of main (parent booking resource)
*
* @return string - SQL for dates VALUES to insert
*/
function wpbc_prepare_date_row( $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id, $main_resource_id ) {
global $wpdb;
if ( class_exists( 'wpdev_bk_biz_l' ) ) {
$insert_dates_arr = ( $main_resource_id != $date_resource_id )
? $wpdb->prepare( "(%d, %s, %d, %d)", $booking_id, $full_date_sql, $is_approved_dates, $date_resource_id )
: $wpdb->prepare( "(%d, %s, %d, NULL)", $booking_id, $full_date_sql, $is_approved_dates );
} else {
$insert_dates_arr = $wpdb->prepare( "(%d, %s, %d)", $booking_id, $full_date_sql, $is_approved_dates );
}
return $insert_dates_arr;
}
// ---------------------------------------------------------------------------------------------------------------------
// Support
// ---------------------------------------------------------------------------------------------------------------------
/**
* Get booking_id and resource_id if we are editing the booking, by booking hash
*
* @param $booking_hash
* @param $server_request_url
*
* @return array | false false if not edit Otherwise [ 'booking_id': 100, 'resource_id': 3 ]
*/
function wpbc_get_data__if_edit_booking( $booking_hash, $server_request_url ) {
$is_edit_booking = false;
if ( ! empty( $booking_hash ) ) {
$my_booking_id_type = wpbc_hash__get_booking_id__resource_id( $booking_hash );
if ( $my_booking_id_type !== false ) {
$is_edit_booking = array();
$is_edit_booking['booking_id'] = intval( $my_booking_id_type[0] );
$is_edit_booking['resource_id'] = intval( $my_booking_id_type[1] );
//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.
// FixIn: 10.10.1.2
//if ( strpos( $server_request_url, 'resource_no_update' ) === false ) { // FixIn: 9.4.2.3.
if ( ( function_exists( 'wpbc_is_this_child_resource' ) ) && ( wpbc_is_this_child_resource( $is_edit_booking['resource_id'] ) ) ) {
$bk_parent_br_id = wpbc_get_parent_resource( $is_edit_booking['resource_id'] );
$is_edit_booking['resource_id'] = intval( $bk_parent_br_id );
}
//}
}
}
return $is_edit_booking;
}
/**
* Get how many items to book. Usually it's from [selectbox visitors "1" ... ] field.
*
* @param booking_form_data__arr = [
* selected_short_dates_hint = "September 27, 2023 - September 28, 2023"
* days_number_hint = "2"
* rangetime = "16:00 - 18:00"
* starttime = "21:00"
* durationtime = "00:30"
* name = "John"
* secondname = "Smith"
* email = "[email protected]"
* visitors = "1"
* children = "0"
* details = ""
* ]
*
* @return int
*/
function wpbc_get__how_many_items_to_book__in_booking_form( $booking_form_data__arr, $resource_id ){
$how_many_items_to_book = 1;
//TODO: Check about some URL parameter: '&resource_no_update' to book parent resource as single resource! // FixIn: 10.10.1.2
if (
( class_exists( 'wpdev_bk_biz_l' ) )
&& ( 0 !== wpbc_get_child_resources_number( $resource_id ) ) // Here several child booking resources
) {
$booking_capacity_field = wpbc_get__booking_capacity_field__name();
if (
( ! empty( $booking_capacity_field ) )
&& ( isset( $booking_form_data__arr[ $booking_capacity_field ] ) )
){
$how_many_items_to_book = intval( $booking_form_data__arr[ $booking_capacity_field ] ); // Get value of how many booking resources to book
$how_many_items_to_book = ( $how_many_items_to_book > 0 ) ? $how_many_items_to_book : 1;
}
}
return $how_many_items_to_book;
}
/**
* Get capacity field -- 'pure name'
*
* @return string - field name, or empty string - '' if not defined
*
* 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'
* or for standard form: 'select^visitors'
* and here we get only field name: 'visitors'
*/
function wpbc_get__booking_capacity_field__name() {
if ( class_exists( 'wpdev_bk_biz_l' ) ) {
if ( 'On' == get_bk_option( 'booking_quantity_control' ) ) {
$booking_capacity_field = get_bk_option( 'booking_capacity_field' );
if ( ! empty( $booking_capacity_field ) ) {
$booking_capacity_field = explode( '^', $booking_capacity_field );
if ( ! empty( $booking_capacity_field ) ) {
$booking_capacity_field_name = $booking_capacity_field[ count( $booking_capacity_field ) - 1 ];
return $booking_capacity_field_name;
}
}
}
}
return '';
}
/**
* 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.
* @param $booking_form_data__arr
*
* @return array [ 0, 24 * 60 * 60 ] OR [ 10*60*60, 14*60*60 ] <- start and end time in seconds
*
* Example:
*
* // Firstly, we need to Get parsed booking form: [ name = "John", secondname = "Smith", email = "[email protected]", visitors = "2",... ]
* $structured_booking_data_arr = wpbc_get_parsed_booking_data_arr( $params["form_data"], $params["resource_id"], array( 'get' => 'value' ) );
*
* // Now get start/end times as seconds: [ 64800, 72000 ]
* $time_as_seconds_arr = wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $structured_booking_data_arr );
*/
function wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $booking_form_data__arr ){
$selected_time_fields = wpbc_get__selected_time_fields__in_booking_form__as_arr( $booking_form_data__arr );
// 2.2 Get selected SECONDS to book ---------------------------------------------------------------------------
$time_as_seconds_arr = array( 0, 24 * 60 * 60 ); // Full day booking by default
foreach ( $selected_time_fields as $time_fields_obj ) { // { times_as_seconds: [ 21600, 23400 ], value_option_24h: '06:00 - 06:30', name: 'rangetime'}
if ( false !== strpos( $time_fields_obj['name'], 'rangetime' ) ) {
$time_as_seconds_arr[ 0 ] = $time_fields_obj['times_as_seconds'][ 0 ];
$time_as_seconds_arr[ 1 ] = $time_fields_obj['times_as_seconds'][ 1 ];
//break; // If we have range-time then skip this loop
}
if ( false !== strpos( $time_fields_obj['name'], 'starttime' ) ) {
$time_as_seconds_arr[ 0 ] = $time_fields_obj['times_as_seconds'][ 0 ];
}
if ( false !== strpos( $time_fields_obj['name'], 'endtime' ) ) {
$time_as_seconds_arr[ 1 ] = $time_fields_obj['times_as_seconds'][ 0 ];
}
}
// For duration time we need to make a new loop, because we need to be sure that was defined START_TIME before this,
// and end time was NOT defined, e.g. == (otherwise it's means that we already used END_TIME or RANGE_TIME)
if (
( ( 0 ) !== $time_as_seconds_arr[ 0 ] )
&& ( (24 * 60 * 60 ) === $time_as_seconds_arr[ 1 ] )
){
foreach ( $selected_time_fields as $time_fields_obj ) { // { times_as_seconds: [ 21600 ], value_option_24h: '06:00', name: 'durationtime'}
if ( false !== strpos( $time_fields_obj['name'], 'durationtime' ) ) {
$time_as_seconds_arr[ 1 ] = $time_as_seconds_arr[ 0 ] + $time_fields_obj['times_as_seconds'][ 0 ];
break;
}
}
}
return $time_as_seconds_arr;
}
/**
* Get all time fields in the booking form as array of objects
*
* @param booking_form_data__arr = [
* selected_short_dates_hint = "September 27, 2023 - September 28, 2023"
* days_number_hint = "2"
* rangetime = "16:00 - 18:00"
* starttime = "21:00"
* durationtime = "00:30"
* name = "John"
* secondname = "Smith"
* email = "[email protected]"
* visitors = "1"
* children = "0"
* details = ""
* ]
* @returns []
*
* Example:
* [
* [
* name = "rangetime"
* value_option_24h = "16:00 - 18:00"
* times_as_seconds = [ 57600, 64800 ]
* ]
* [
* name = "starttime"
* value_option_24h = "21:00"
* times_as_seconds = [ 75600 ]
* ]
* [
* name = "durationtime"
* value_option_24h = "00:30"
* times_as_seconds = [ 1800 ]
* ]
* ]
*/
function wpbc_get__selected_time_fields__in_booking_form__as_arr( $booking_form_data__arr ){
/**
* Fields with [] like this select[name="rangetime1[]"]
* it's when we have 'multiple' in shortcode: [select* rangetime multiple "06:00 - 06:30" ... ]
*/
$time_fields_arr = array( 'rangetime', 'starttime', 'endtime', 'durationtime' );
$time_fields_obj_arr = array();
// Loop all Time Fields
for ( $ctf = 0; $ctf < count( $time_fields_arr ); $ctf ++ ) {
$time_field = $time_fields_arr[ $ctf ];
if ( isset( $booking_form_data__arr[ $time_field ] ) ) {
$value_option_seconds_arr = explode( '-', $booking_form_data__arr[ $time_field ] );
$times_as_seconds_arr = array();
foreach ( $value_option_seconds_arr as $time_val ) {
$time_val = trim( $time_val );
if ( ! empty( $time_val ) ) {
$start_end_times_arr = explode( ':', $time_val );
$time_in_seconds = intval( $start_end_times_arr[0] ) * 60 * 60 + intval( $start_end_times_arr[1] ) * 60;
$times_as_seconds_arr[] = $time_in_seconds;
}
}
if (! empty($times_as_seconds_arr)) {
$time_fields_obj_arr[] = array(
'name' => $time_field,
'value_option_24h' => $booking_form_data__arr[ $time_field ],
'times_as_seconds' => $times_as_seconds_arr
);
}
}
}
return $time_fields_obj_arr;
}
//TODO: 2023-10-13 16:37
// - create new function for confirmation section
// - Update payment request
// - create redirection to the "Thank you." page and show there conformation, and payment request"
// - define in the settings new Stripe and PayPal button design as default !
// - Be sure that 'booking_log_booking_actions' active by default
// - test it in dark theme
// - test closing booked dates if all time slot was booked - it's relative to 'different time slots in dif dates'
// - add migrate support old dates selection variables
// - check and remove other global Js vars
// - Create wizard booking form style with steps ?
// - Check Booking > Availability page relative new functionality of calendar_load !
// - Update last func in wpbc-booking-new.php and remove it.
// - Test capacity in dates and time slots
// - Next 9.9 Customizer Wizard
// - Next 9.9 Update search admin UI
// - Next 9.9 - update functionality in Search functionality based on similar to where_to_save function.
// - Next 9.9 refactor Dates functions.
// Done. - Next 9.9 Update Booking > Settings General page to show tabs vertically in left column
// 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.
// Create the same navigation for Emails.
/**
* TODO: Performance improvement:
* "other_code": 0.003080129623413086
* "wpbc__where_to_save_booking": 0.060331106185913086
* "wpbc_db__booking_save": 0.023384809494018555
* "wpbc_maybe_get_payment_form": 1.3650128841400146 <-
* "emails_sending": 1.5024309158325195 <-
* "total": 2.9542438983917236
*/