__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

[email protected]: ~ $
<?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
 */

Filemanager

Name Type Size Permission Actions
_out Folder 0750
_src Folder 0750
aggregate.php File 6.35 KB 0640
booking_date_class.php File 10.52 KB 0640
calendar_load.php File 22.57 KB 0640
capacity.php File 101.96 KB 0640
captcha_simple_text.php File 6.36 KB 0640
confirmation.php File 21.37 KB 0640
confirmation_page.php File 16.81 KB 0640
create_booking.php File 83.19 KB 0640
dates_times_support.php File 69.99 KB 0640
get_times_fields.php File 16.76 KB 0640
resource_support.php File 10.47 KB 0640
where_to_save.php File 21.78 KB 0640
wpbc_cache.php File 4.48 KB 0640
Filemanager