<?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) { exit; }

// FORM

/**
 * Create a new form
 * @since 1.5.0
 * @version 1.14.0
 * @param string $title
 * @param boolean $insert_default_fields
 * @return int|false
 */
function bookacti_create_form( $title = '', $insert_default_fields = false ) {
	// Insert form
	$form_id = bookacti_insert_form( $title );
	if( $form_id === false ) { return false; }
	
	// Insert default form fields
	if( $insert_default_fields ) {
		$inserted = bookacti_insert_default_form_fields( $form_id );
		if( $inserted ) {
			// Save initial field order
			$field_order = bookacti_sanitize_form_field_order( $form_id, array() );
			if( $field_order ) { bookacti_update_metadata( 'form', $form_id, array( 'field_order' => $field_order ) ); }
		}
	}
	
	// Insert default form managers
	$form_managers = bookacti_format_form_managers();
	bookacti_update_managers( 'form', $form_id, $form_managers );
	
	do_action( 'bookacti_form_inserted', $form_id );
	
	return $form_id;
}


/**
 * Get form data and metadata
 * @since 1.5.0
 * @version 1.14.0
 * @param int $form_id
 * @param boolean $raw
 * @return array
 */
function bookacti_get_form_data( $form_id, $raw = false ) {
	$form = bookacti_get_form( $form_id );
	if( ! $form ) { return array(); }
	
	// Add form metadata
	$form_meta = bookacti_get_metadata( 'form', $form_id );
	if( is_array( $form_meta ) ) { 
		$form = array_merge( $form, $form_meta );
	}

	// Format data
	if( ! $raw ) { $form = bookacti_format_form_data( $form ); }
	
	return apply_filters( 'bookacti_form_data', $form, $form_id, $raw );
}


/**
 * Get default form data
 * @since 1.5.0
 * @return array
 */
function bookacti_get_default_form_data() {
	return apply_filters( 'bookacti_default_form_data', array( 
		'form_id'       => 0,  // Form ID
		'title'         => '', // Form title displayed in form list and form editor
		'user_id'       => -1, // Author user ID
		'creation_date' => '', // Datetime when the form was created
		'status'        => '', // Form status
		'active'        => -1  // If the form is active
	));
}


/**
 * Get default form meta
 * @since 1.5.0
 * @return array
 */
function bookacti_get_default_form_meta() {
	return apply_filters( 'bookacti_default_form_meta', array(
		'id' => '',           // Form's id
		'class' => '',        // Form's classes
		'redirect_url' => '', // URL to redirect to when the form is submitted
	));
}


/**
 * Format form data
 * @since 1.5.0
 * @version 1.14.0
 * @param array|string $raw_form_data
 * @param string $context "view" or "edit"
 * @return array
 */
function bookacti_format_form_data( $raw_form_data = array(), $context = 'view' ) {
	if( ! is_array( $raw_form_data ) ) { return array(); }
	
	$default_data = bookacti_get_default_form_data();
	$default_meta = bookacti_get_default_form_meta();
	if( ! $default_data ) { return array(); }
	
	// Empty default strings in edit mode
	if( $context === 'edit' ) {
		$default_data[ 'title' ] = $default_meta[ 'redirect_url' ] = '';
	}
	
	// Format meta values
	$keys_by_type = array( 
		'str_id' => array( 'id' ),
		'str'    => array( 'class', 'redirect_url' )
	);
	$form_meta = bookacti_sanitize_values( $default_meta, $raw_form_data, $keys_by_type );
	
	// Exception: Keep field_order and format it
	$form_meta[ 'field_order' ] = isset( $raw_form_data[ 'field_order' ] ) ? maybe_unserialize( $raw_form_data[ 'field_order' ] ) : array();
	
	// Format common values
	$keys_by_type = array( 
		'int'      => array( 'form_id', 'user_id' ),
		'str_id'   => array( 'status' ),
		'str'      => array( 'title' ),
		'datetime' => array( 'creation_date' ),
		'bool'     => array( 'active' )
	);
	$form_data = bookacti_sanitize_values( $default_data, $raw_form_data, $keys_by_type );
	
	// Merge common data and metadata
	$form_data = array_merge( $form_data, $form_meta );
	
	// Translate texts
	if( $context !== 'edit' ) { 
		if( ! empty( $form_data[ 'title' ] ) )        { $form_data[ 'title' ]        = apply_filters( 'bookacti_translate_text', $form_data[ 'title' ] ); }
		if( ! empty( $form_data[ 'redirect_url' ] ) ) { $form_data[ 'redirect_url' ] = apply_filters( 'bookacti_translate_text', $form_data[ 'redirect_url' ] ); }
	}
	
	$form_data = apply_filters( 'bookacti_formatted_form_data', $form_data, $raw_form_data, $context );
	
	return $form_data;
}


/**
 * Sanitize form data
 * @since 1.5.0
 * @version 1.14.0
 * @param array|string $raw_form_data
 * @return array|false
 */
function bookacti_sanitize_form_data( $raw_form_data ) {
	if( ! is_array( $raw_form_data ) ) { return array(); }
	
	$default_data = bookacti_get_default_form_data();
	$default_meta = bookacti_get_default_form_meta();
	if( ! $default_data ) { return array(); }
	
	// Empty default translatable strings
	if( ! empty( $default_data[ 'title' ] ) )        { $default_data[ 'title' ] = ''; }
	if( ! empty( $default_meta[ 'redirect_url' ] ) ) { $default_meta[ 'redirect_url' ] = ''; }
	
	// Sanitize meta values
	$keys_by_type = array( 
		'str_id' => array( 'id' ),
		'str'    => array( 'class', 'redirect_url' )
	);
	$form_meta = bookacti_sanitize_values( $default_meta, $raw_form_data, $keys_by_type );
	
	// Sanitize common values
	$keys_by_type = array( 
		'int'      => array( 'form_id', 'user_id' ),
		'str_id'   => array( 'status' ),
		'str'      => array( 'title' ),
		'datetime' => array( 'creation_date' ),
		'bool'     => array( 'active' )
	);
	$form_data = bookacti_sanitize_values( $default_data, $raw_form_data, $keys_by_type );
	
	// Merge common data and metadata
	$form_data = array_merge( $form_data, $form_meta );
	
	return apply_filters( 'bookacti_sanitized_form_data', $form_data, $raw_form_data );
}


/**
 * Display a booking form
 * @version 1.14.0
 * @param int $form_id
 * @param string $instance_id
 * @param string $context
 * @param boolean $echo
 * @return void|string
 */
function bookacti_display_form( $form_id, $instance_id = '', $context = 'display', $echo = true ) {
	if( ! $form_id ) { return ''; }
	
	$form = bookacti_get_form_data( $form_id );
	if( ! $form ) { return ''; }
	
	$form_action = 'bookactiSubmitBookingForm';
	
	// Set the form unique CSS selector
	$form_css_id = ! empty( $form[ 'id' ] ) ? esc_attr( $form[ 'id' ] ) : ( $instance_id ? esc_attr( $instance_id ) : esc_attr( ( $context === 'login_form' ? 'login-form-' : 'form-' ) . $form[ 'form_id' ] . '-' . rand() ) );
	if( ! $instance_id ) { $instance_id = $form_css_id; }
	
	$fields = bookacti_get_form_fields_data( $form_id );
	$ordered_form_fields = bookacti_sort_form_fields_array( $form_id, $fields );
	
	$displayed_form_fields = apply_filters( 'bookacti_displayed_form_fields', $ordered_form_fields, $form, $instance_id, $context );
	
	// Build array of field types
	$is_form = 0;
	$login_field = array();
	$calendar_field = array();
	foreach( $displayed_form_fields as $field ) { 
		if( empty( $field[ 'type' ] ) ) { continue; }
			 if( $field[ 'type' ] === 'calendar' ) { $calendar_field = $field; }
		else if( $field[ 'type' ] === 'login' )    { $login_field = $field; }
		else if( $field[ 'type' ] === 'submit' )   { $is_form = 1; }
	}
	
	// Show only the login field if the user is not logged in
	if( ! is_user_logged_in() && ( ! empty( $login_field[ 'login_first' ] ) || $context === 'login_form' ) ) {
		// Force some values
		$login_field[ 'login_first' ] = 1;
		$login_field[ 'login_button' ] = 1;
		$login_field[ 'displayed_fields' ][ 'no_account' ] = 0;
		$displayed_form_fields = array( $login_field );
		$form_action = 'bookactiSubmitLoginForm';
		$is_form = 1;
	}
	
	// Change the form action according to the form_action option of calendar field
	else if( ! empty( $calendar_field[ 'form_action' ] ) ) {
		if( $calendar_field[ 'form_action' ] === 'redirect_to_url' ) { $form_action = ''; }
	}
	
	// Set form action
	$form_redirect_url = $form[ 'redirect_url' ];
	if( $context === 'login_form' ) {
		$form_redirect_url = ! empty( $GLOBALS[ 'bookacti_login_redirect_url' ] ) ? $GLOBALS[ 'bookacti_login_redirect_url' ] : '';
	}
	
	// Set form attributes
	$form_attributes = apply_filters( 'bookacti_form_attributes', array(
		'action'       => $form_redirect_url,
		'id'           => empty( $form[ 'id' ] ) ? 'bookacti-' . $form_css_id : $form_css_id,
		'class'        => 'bookacti-booking-form-' . $form_id . ' ' . $form[ 'class' ],
		'autocomplete' => 'off'
	), $form, $instance_id, $context, $displayed_form_fields );
	
	// Add compulsory class
	$compulsory_class = $is_form ? 'bookacti-booking-form' : 'bookacti-form-fields';
	$form_attributes[ 'class' ] = $compulsory_class . ( ! empty( $form_attributes[ 'class' ] ) ? ' ' . $form_attributes[ 'class' ] : '' );
	
	// Convert $form_attributes array to inline attributes
	$form_attributes_str = '';
	foreach( $form_attributes as $form_attribute_key => $form_attribute_value ) {
		if( $form_attribute_value !== '' ) { $form_attributes_str .= $form_attribute_key . '="' . $form_attribute_value . '" '; }
	}
	
	ob_start();
	
	// Add form container only if there is a "submit" button
	if( $is_form ) { ?>
		<form <?php echo $form_attributes_str; ?>>
	<?php } else { ?>
		<div <?php echo $form_attributes_str; ?>>
	<?php } ?>
			<input type='hidden' name='form_id' value='<?php echo $form_id; ?>'/>
			<input type='hidden' name='action' value='<?php echo apply_filters( 'bookacti_form_action_field_value', $form_action, $form, $instance_id, $context, $displayed_form_fields ); ?>'/>
		<?php
			do_action( 'bookacti_form_before', $form, $instance_id, $context, $displayed_form_fields );

			foreach( $displayed_form_fields as $field ) {
				if( ! $field ) { continue; }
				bookacti_display_form_field( $field, $instance_id, $context, true );
			}

			do_action( 'bookacti_form_after', $form, $instance_id, $context, $displayed_form_fields );
		?>
			<div class='bookacti-notices' style='display:none;'></div>
	<?php
	if( ! $is_form ) { ?>
		</div>
	<?php } else { ?>
		</form>
	<?php }
	
	$html = apply_filters( 'bookacti_form_html', ob_get_clean(), $form, $instance_id, $context, $displayed_form_fields );
	if( ! $echo ) { return $html; }
	echo $html;
}




// FORM FIELDS

/**
 * Get form fields array
 * @since 1.5.4
 * @version 1.15.0
 * @param int $form_id
 * @param boolean $active_only   Whether to fetch only active fields. Default "true".
 * @param boolean $index_by_name Whether to index by name. Else, indexed by field id.
 * @param boolean $raw           Whether to format the data.
 * @return array
 */
function bookacti_get_form_fields_data( $form_id, $active_only = true, $index_by_name = false, $raw = false ) {
	$fields_data_by_id = wp_cache_get( 'form_fields_data_' . $form_id, 'bookacti' );
	
	if( $fields_data_by_id === false ) {
		// Retrieve inactive fields too, for a better cache efficiency
		$fields = bookacti_get_form_fields( $form_id, $raw ? $active_only : false );
		if( $fields ) {
			// Get fields meta
			$fields_meta = bookacti_get_metadata( 'form_field', array_keys( $fields ) );

			// Add form field metadata
			$fields_data_by_id = array();
			foreach( $fields as $field_id => $field ) {
				// Add field-specific data
				$field_metadata = isset( $fields_meta[ $field_id ] ) ? $fields_meta[ $field_id ] : array();
				if( is_array( $field_metadata ) ) { 
					$field = array_merge( $field, $field_metadata );
				}
				
				$fields_data_by_id[ $field_id ] = $field;
			}
		}
		
		$fields_data_by_id = apply_filters( 'bookacti_form_fields_data_raw', $fields_data_by_id ? $fields_data_by_id : array(), $form_id, $active_only );
		
		// Cache data
		wp_cache_set( 'form_fields_data_' . $form_id, $fields_data_by_id, 'bookacti' );
	}
	
	// Format data
	if( ! $raw && $fields_data_by_id ) { $fields_data_by_id = array_filter( array_map( 'bookacti_format_form_field_data', $fields_data_by_id ) ); }
	
	$fields_data = ! $index_by_name && $fields_data_by_id ? $fields_data_by_id : array();
	if( $fields_data_by_id ) {
		foreach( $fields_data_by_id as $field_id => $field_data ) {
			// Remove inactive fields
			if( ! $index_by_name && $active_only && empty( $field_data[ 'active' ] ) ) { unset( $fields_data[ $field_id ] ); continue; }
			// Index by name
			if( $index_by_name && ( ! $active_only || ( $active_only && ! empty( $field_data[ 'active' ] ) ) ) ) { $fields_data[ $field_data[ 'name' ] ] = $field_data; }
		}
	}
	
	return $fields_data;
}


/**
 * Get the desired field data as an array
 * @since 1.5.0
 * @version 1.14.0
 * @param int $field_id
 * @param boolean $raw
 * @return array
 */
function bookacti_get_form_field_data( $field_id, $raw = false ) {
	$field_data = wp_cache_get( 'form_field_data_' . $field_id, 'bookacti' );
	
	if( $field_data === false ) {
		$field = bookacti_get_form_field( $field_id );
		if( $field ) {
			// Add form field metadata
			$field_metadata = bookacti_get_metadata( 'form_field', $field[ 'field_id' ] );
			if( is_array( $field_metadata ) ) { $field = array_merge( $field, $field_metadata ); }
		}
		
		$field_data = apply_filters( 'bookacti_form_field', $field );
		
		// Cache data
		wp_cache_set( 'form_field_data_' . $field_id, $field_data, 'bookacti' );
		if( isset( $field[ 'name' ] ) && isset( $field[ 'form_id' ] ) ) {
			wp_cache_set( 'form_field_data_' . $field[ 'name' ] . '_' . $field[ 'form_id' ], $field_data, 'bookacti' );
		}
	}
	
	// Format data
	if( ! $raw && $field_data ) { $field_data = bookacti_format_form_field_data( $field_data ); }
	
	return $field_data;
}


/**
 * Get the desired field data as an array. The field name must be unique, only the first will be retrieved.
 * @since 1.5.0
 * @version 1.14.0
 * @param int $form_id
 * @param string $field_name
 * @param boolean $raw
 * @return array
 */
function bookacti_get_form_field_data_by_name( $form_id, $field_name, $raw = false ) {
	$field_data = wp_cache_get( 'form_field_data_' . $field_name . '_' . $form_id, 'bookacti' );
	
	if( $field_data === false ) {
		$field = bookacti_get_form_field_by_name( $form_id, $field_name );
		if( $field ) {
			// Add form field metadata 
			$field_metadata = bookacti_get_metadata( 'form_field', $field[ 'field_id' ] );
			if( is_array( $field_metadata ) ) { $field = array_merge( $field, $field_metadata ); }
		}

		$field_data = apply_filters( 'bookacti_form_field', $field );

		// Cache data
		wp_cache_set( 'form_field_data_' . $field_name . '_' . $form_id, $field_data, 'bookacti' );
		if( ! empty( $field[ 'field_id' ] ) ) {
			wp_cache_set( 'form_field_data_' . $field[ 'field_id' ], $field_data, 'bookacti' );
		}
	}
	
	// Format data
	if( ! $raw && $field_data ) { $field_data = bookacti_format_form_field_data( $field_data ); }
	
	return $field_data;
}


/**
 * Get the desired forms field data as an array. The field name must be unique, only the first will be retrieved.
 * @since 1.16.2
 * @param array $form_ids
 * @param string $field_name
 * @param boolean $raw
 * @return array
 */
function bookacti_get_forms_field_data_by_name( $form_ids, $field_name, $raw = false ) {
	$forms_field_data = array();
	
	$forms_field = bookacti_get_forms_field_by_name( $form_ids, $field_name );
	if( $forms_field ) {
		$form_id_per_field = array();
		foreach( $forms_field as $field ) {
			$form_id_per_field[ intval( $field[ 'field_id' ] ) ] = intval( $field[ 'form_id' ] );
		}
		$field_ids = bookacti_ids_to_array( array_keys( $form_id_per_field ) );
		
		// Add form field metadata 
		$fields_meta = $field_ids ? bookacti_get_metadata( 'form_field', $field_ids ) : array();
		if( $fields_meta ) {
			foreach( $fields_meta as $field_id => $field_meta ) {
				if( ! is_array( $field_meta ) || ! isset( $form_id_per_field[ $field_id ] ) ) { continue; }
				$form_id = $form_id_per_field[ $field_id ];
				if( ! isset( $forms_field[ $form_id ] ) ) { continue; }
				$forms_field[ $form_id ] = array_merge( $forms_field[ $form_id ], $field_meta );
			}
		}
	}
	
	foreach( $form_ids as $form_id ) {
		$field      = isset( $forms_field[ $form_id ] ) ? $forms_field[ $form_id ] : array();
		$field_data = apply_filters( 'bookacti_form_field', $field );

		// Cache data
		wp_cache_set( 'form_field_data_' . $field_name . '_' . $form_id, $field_data, 'bookacti' );
		if( ! empty( $field[ 'field_id' ] ) ) {
			wp_cache_set( 'form_field_data_' . $field[ 'field_id' ], $field_data, 'bookacti' );
		}

		// Format data
		if( ! $raw && $field_data ) { $field_data = bookacti_format_form_field_data( $field_data ); }

		$forms_field_data[ $form_id ] = $field_data;
	}
	
	return $forms_field_data;
}


/**
 * Get the default common for field data
 * @since 1.5.3
 * @version 1.14.0
 * @param string $context 'view' or 'edit'
 * @return array
 */
function bookacti_get_default_form_field_common_data( $context = 'view' ) {
	return apply_filters( 'bookacti_default_common_form_field_data', array( 
		'field_id'    => 0,       // Field ID
		'form_id'     => 0,       // Form ID
		'name'        => '',      // Text identifier of the field
		'type'        => '',      // Field type [calendar, quantity, submit, login, free_text, or your custom types]
		'title'       => '',      // Field title displayed in form editor
		'label'       => '',      // Text displayed for the field on the frontend
		'id'          => '',      // Field CSS id
		'class'       => '',      // Field CSS classes
		'options'     => array(), // Array of allowed values
		'value'       => '',      // Default value among the allowed values
		'placeholder' => '',      // Text displayed in transparency when the field is empty
		'tip'         => '',      // Help text displayed in a tooltip next to the field
		'required'    => 0,       // Whether the customer is forced to fill this field when it is displayed
		'compulsory'  => 0,       // Whether the field can be deleted
		'default'     => 0,       // Whether the field is part of the form by default (if compulsory, it is by default too)
		'unique'      => 1,       // Whether the user can add multiple occurrence of this field in the form
		'active'      => 1        // Set to 0 if the field has been removed
	), $context );
}


/**
 * Get fields data
 * @see bookacti_format_form_field_data to properly format your array
 * @since 1.5.0
 * @version 1.14.0
 * @param string $field_name
 * @param string $context 'edit' or 'view'
 * @return array
 */
function bookacti_get_default_form_fields_data( $field_name = '', $context = 'view' ) {
	// Set the common default data
	$default_data = bookacti_get_default_form_field_common_data( $context );
	
	// Add register fields default
	$register_fields   = bookacti_get_register_fields_default_data( $context );
	$register_defaults = array( 'label' => array(), 'placeholder' => array(), 'tip' => array() );
	foreach( $register_fields as $register_field_name => $register_field ) {
		$register_defaults[ 'label' ][ $register_field_name ]       = isset( $register_field[ 'label' ] )       ? $register_field[ 'label' ] : '';
		$register_defaults[ 'placeholder' ][ $register_field_name ] = isset( $register_field[ 'placeholder' ] ) ? $register_field[ 'placeholder' ] : '';
		$register_defaults[ 'tip' ][ $register_field_name ]         = isset( $register_field[ 'tip' ] )         ? $register_field[ 'tip' ] : '';
	}
	
	// Add log in fields default
	$log_in_fields   = bookacti_get_log_in_fields_default_data( $context );
	$log_in_defaults = array( 'label' => array(), 'placeholder' => array(), 'tip' => array() );
	foreach( $log_in_fields as $log_in_field_name => $log_in_field ) {
		$log_in_defaults[ 'label' ][ $log_in_field_name ]       = isset( $log_in_field[ 'label' ] )       ? $log_in_field[ 'label' ] : '';
		$log_in_defaults[ 'placeholder' ][ $log_in_field_name ] = isset( $log_in_field[ 'placeholder' ] ) ? $log_in_field[ 'placeholder' ] : '';
		$log_in_defaults[ 'tip' ][ $log_in_field_name ]         = isset( $log_in_field[ 'tip' ] )         ? $log_in_field[ 'tip' ] : '';
	}
	
	// Add login type fields default
	$login_types         = bookacti_get_login_type_field_default_options( array(), $context );
	$login_type_defaults = array( 'label' => array(), 'placeholder' => array(), 'tip' => array() );
	foreach( $login_types as $login_type_name => $login_type ) {
		$login_type_defaults[ 'label' ][ $login_type_name ]       = isset( $login_type[ 'label' ] )       ? $login_type[ 'label' ] : '';
		$login_type_defaults[ 'placeholder' ][ $login_type_name ] = isset( $login_type[ 'placeholder' ] ) ? $login_type[ 'placeholder' ] : '';
		$login_type_defaults[ 'tip' ][ $login_type_name ]         = isset( $login_type[ 'tip' ] )         ? $login_type[ 'tip' ] : '';
	}
	
	// Set field-speicific default data
	$fields_data = array(
		'calendar' => array( 
			'name'       => 'calendar',
			'type'       => 'calendar',
			'title'      => esc_html__( 'Calendar', 'booking-activities' ),
			'compulsory' => 1,
			'default'    => 1
		),
		'login' => array( 
			'name'        => 'login',
			'type'        => 'login',
			'title'       => esc_html__( 'User Data (Login / Registration)', 'booking-activities' ),
			'default'     => 1,
			'label'       => array_merge( $log_in_defaults[ 'label' ], $login_type_defaults[ 'label' ], $register_defaults[ 'label' ] ),
			'placeholder' => array_merge( $log_in_defaults[ 'placeholder' ], $login_type_defaults[ 'placeholder' ], $register_defaults[ 'placeholder' ] ),
			'tip'         => array_merge( $log_in_defaults[ 'tip' ], $login_type_defaults[ 'tip' ], $register_defaults[ 'tip' ] )
		),
		'free_text' => array( 
			'name'   => 'free_text',
			'type'   => 'free_text',
			'title'  => esc_html__( 'Free text', 'booking-activities' ),
			'unique' => 0
		),
		'quantity' => array( 
			'name'     => 'quantity',
			'type'     => 'quantity',
			'title'    => esc_html__( 'Quantity', 'booking-activities' ),
			'label'    => esc_html__( 'Quantity', 'booking-activities' ),
			'required' => 1,
			'default'  => 1
		),
		'terms' => array( 
			'name'     => 'terms',
			'type'     => 'checkbox',
			'title'    => esc_html__( 'Terms', 'booking-activities' ),
			'label'    => esc_html__( 'I have read and agree to the terms and conditions', 'booking-activities' ),
			'required' => 1
		),
		'total_price' => array( 
			'name'  => 'total_price',
			'type'  => 'total_price',
			'title' => esc_html__( 'Total price', 'booking-activities' ),
			'label' => esc_html__( 'Total price', 'booking-activities' )
		),
		'submit' => array( 
			'name'    => 'submit',
			'type'    => 'submit',
			'title'   => esc_html__( 'Submit button', 'booking-activities' ),
			'value'   => esc_html__( 'Book', 'booking-activities' ),
			'default' => 1
		)
	);
	
	$fields_data = apply_filters( 'bookacti_default_form_fields_data', $fields_data, $field_name, $context );
	
	// Merge field-specific data to common data
	foreach( $fields_data as $i => $field_data ) {
		$fields_data[ $i ] = array_merge( $default_data, $field_data );
	}
		
	if( $field_name ) {
		return isset( $fields_data[ $field_name ] ) ? $fields_data[ $field_name ] : array();
	}
	
	return $fields_data;
}


/**
 * Get fields metadata
 * @see bookacti_format_form_field_data to properly format your array
 * @since 1.5.0
 * @version 1.17.0
 * @param string $field_name
 * @param string $context 'view' or 'edit'
 * @return array
 */
function bookacti_get_default_form_fields_meta( $field_name = '', $context = 'view' ) {
	// Add register fields default meta to login field meta
	$register_fields   = bookacti_get_register_fields_default_data( $context );
	$register_defaults = array( 'displayed' => array(), 'required' => array() );
	foreach( $register_fields as $register_field_name => $register_field ) {
		$register_defaults[ 'displayed' ][ $register_field_name ] = isset( $register_field[ 'displayed' ] ) ? $register_field[ 'displayed' ] : 0;
		$register_defaults[ 'required' ][ $register_field_name ]  = isset( $register_field[ 'required' ] )  ? $register_field[ 'required' ] : 0;
	}
	
	// Add log in fields default
	$log_in_fields   = bookacti_get_log_in_fields_default_data( $context );
	$log_in_defaults = array( 'label' => array(), 'placeholder' => array(), 'tip' => array() );
	foreach( $log_in_fields as $log_in_field_name => $log_in_field ) {
		$log_in_defaults[ 'displayed' ][ $log_in_field_name ] = isset( $log_in_field[ 'displayed' ] ) ? $log_in_field[ 'displayed' ] : 0;
		$log_in_defaults[ 'required' ][ $log_in_field_name ]  = isset( $log_in_field[ 'required' ] )  ? $log_in_field[ 'required' ] : 0;
	}
	
	// Add login type fields default
	$login_types         = bookacti_get_login_type_field_default_options( array(), $context );
	$login_type_defaults = array( 'displayed' => array(), 'required' => array() );
	foreach( $login_types as $login_type_name => $login_type ) {
		$login_type_defaults[ 'displayed' ][ $login_type_name ] = isset( $login_type[ 'displayed' ] ) ? $login_type[ 'displayed' ] : 0;
		$login_type_defaults[ 'required' ][ $login_type_name ]  = isset( $login_type[ 'required' ] )  ? $login_type[ 'required' ] : 0;
	}
	
	$fields_meta = array(
		'calendar' => array(
			'id'                             => '',
			'class'                          => '',
			'method'                         => 'calendar',
			'hide_availability'              => 100,
			'hide_calendar'                  => array( 'none' ),
			'calendars'                      => array(),
			'activities'                     => array(),
			'group_categories'               => array( 'none' ),
			'groups_only'                    => 0,
			'groups_single_events'           => 0,
			'groups_first_event_only'        => 0,
			'multiple_bookings'              => 0,
			'bookings_only'                  => 0,
			'status'                         => array(),
			'user_id'                        => 0,
			'start'                          => '',
			'end'                            => '',
			'availability_period_start'      => 0,
			'availability_period_end'        => 0,
			'trim'                           => 1,
			'out_of_period_events'           => 0,
			'past_events'                    => 0,
			'past_events_bookable'           => 0,
			'use_global_days_off'            => -1,
			'days_off'                       => array(),
			'form_action'                    => 'default',
			'when_perform_form_action'       => 'on_submit',
			'select_first_event'             => 0,
			'redirect_url_by_activity'       => array(),
			'redirect_url_by_group_category' => array(),
			'slotMinTime'                    => '00:00',
			'slotMaxTime'                    => '00:00'
		),
		'login' => array(
			'automatic_login'        => 1,
			'login_button'           => 1,
			'login_first'            => 0,
			'login_button_label'     => esc_html__( 'Log in', 'booking-activities' ),
			'register_button_label'  => esc_html__( 'Register', 'booking-activities' ),
			'min_password_strength'  => 4,
			'generate_password'      => 0,
			'remember'               => 0,
			'send_new_account_email' => 1,
			'new_user_role'          => 'default',
			'displayed_fields'       => array_merge( $log_in_defaults[ 'displayed' ], $login_type_defaults[ 'displayed' ], $register_defaults[ 'displayed' ] ),
			'required_fields'        => array_merge( $log_in_defaults[ 'required' ], $login_type_defaults[ 'required' ], $register_defaults[ 'required' ] )
		),
		'free_text'   => array(),
		'quantity'    => array(),
		'terms'       => array(),
		'total_price' => array( 'price_breakdown' => 1 ),
		'submit'      => array()
	);
	
	$fields_meta = apply_filters( 'bookacti_default_form_fields_meta', $fields_meta, $field_name, $context );
	
	if( $field_name ) {
		return isset( $fields_meta[ $field_name ] ) ? $fields_meta[ $field_name ] : array();
	}
	
	return $fields_meta;
}


/**
 * Get available form actions
 * @since 1.7.17
 * @version 1.17.0
 * @return array
 */
function bookacti_get_available_form_actions() {
	$fields = bookacti_get_booking_system_fields_default_data( array( 'form_action' ) );
	return ! empty( $fields[ 'form_action' ][ 'options' ] ) ? $fields[ 'form_action' ][ 'options' ] : array();
}


/**
 * Get available form submit triggers
 * @since 1.7.17
 * @version 1.17.0
 * @return array
 */
function bookacti_get_available_form_action_triggers() {
	$fields = bookacti_get_booking_system_fields_default_data( array( 'when_perform_form_action' ) );
	return ! empty( $fields[ 'when_perform_form_action' ][ 'options' ] ) ? $fields[ 'when_perform_form_action' ][ 'options' ] : array();
}


/**
 * Get available hide calendar options
 * @since 1.17.0
 * @return array
 */
function bookacti_get_available_hide_calendar_options() {
	$fields = bookacti_get_booking_system_fields_default_data( array( 'hide_calendar' ) );
	return ! empty( $fields[ 'hide_calendar' ][ 'options' ] ) ? $fields[ 'hide_calendar' ][ 'options' ] : array();
}


/**
 * Format field data according to its type
 * @since 1.5.0
 * @version 1.17.0
 * @param array|string $raw_field_data
 * @param $context "view" or "edit"
 * @return array
 */
function bookacti_format_form_field_data( $raw_field_data, $context = 'view' ) {
	// Check if name and type are set
	if( ! is_array( $raw_field_data ) || empty( $raw_field_data[ 'name' ] ) || empty( $raw_field_data[ 'type' ] ) ) { return array(); }
	
	$default_data = bookacti_get_default_form_fields_data( $raw_field_data[ 'name' ], $context );
	$default_meta = bookacti_get_default_form_fields_meta( $raw_field_data[ 'name' ], $context );
	if( ! $default_data ) { return array(); }
	
	$field_data = array();
	$field_meta = array();
	
	// Format field-specific data and metadata
	if( $raw_field_data[ 'name' ] === 'calendar' ) {
		$booleans_to_check = array( 'groups_only', 'groups_single_events', 'groups_first_event_only', 'multiple_bookings', 'bookings_only', 'trim', 'out_of_period_events', 'past_events', 'past_events_bookable', 'select_first_event' );
		foreach( $booleans_to_check as $key ) {
			if( ! isset( $raw_field_data[ $key ] ) ) { continue; }
			$field_meta[ $key ] = in_array( $raw_field_data[ $key ], array( 1, '1', true, 'true', 'yes', 'ok' ), true ) ? 1 : 0;
		}
		
		$field_meta[ 'id' ]    = isset( $raw_field_data[ 'id' ] ) ? sanitize_title_with_dashes( $raw_field_data[ 'id' ] ) : $default_meta[ 'id' ];
		$field_meta[ 'class' ] = isset( $raw_field_data[ 'class' ] ) ? sanitize_text_field( $raw_field_data[ 'class' ] ) : $default_meta[ 'class' ];
		
		$field_meta[ 'method' ]            = isset( $raw_field_data[ 'method' ] ) && in_array( $raw_field_data[ 'method' ], array_keys( bookacti_get_available_booking_methods() ), true ) ? $raw_field_data[ 'method' ] : $default_meta[ 'method' ];
		$field_meta[ 'hide_availability' ] = isset( $raw_field_data[ 'hide_availability' ] ) && is_numeric( $raw_field_data[ 'hide_availability' ] ) ? max( min( intval( $raw_field_data[ 'hide_availability' ] ), 100 ), 0 ) : $default_meta[ 'hide_availability' ];
		$field_meta[ 'hide_calendar' ]     = isset( $raw_field_data[ 'hide_calendar' ] ) ? array_values( array_diff( array_intersect( array_keys( bookacti_get_available_hide_calendar_options() ), bookacti_str_ids_to_array( $raw_field_data[ 'hide_calendar' ] ) ), array( false, 'none', 'false', 'no' ) ) ) : $default_meta[ 'hide_calendar' ];
		if( ! $field_meta[ 'hide_calendar' ] || array_intersect( $field_meta[ 'hide_calendar' ], array( false, 'none', 'false', 'no' ) ) ) { $field_meta[ 'hide_calendar' ] = array( 'none' ); }
		
		$field_meta[ 'calendars' ] = isset( $raw_field_data[ 'calendars' ] ) ? bookacti_ids_to_array( $raw_field_data[ 'calendars' ] ) : $default_meta[ 'calendars' ];
		
		$had_activities             = ! empty( $raw_field_data[ 'activities' ] );
		$activities                 = isset( $raw_field_data[ 'activities' ] ) ? bookacti_ids_to_array( $raw_field_data[ 'activities' ] ) : $default_meta[ 'activities' ];
		$field_meta[ 'activities' ] = $activities && is_array( $activities ) ? $activities : ( $had_activities ? array( 'none' ) : array() );
		
		$had_group_categories             = ! empty( $raw_field_data[ 'group_categories' ] );
		$group_categories                 = isset( $raw_field_data[ 'group_categories' ] ) ? bookacti_ids_to_array( $raw_field_data[ 'group_categories' ] ) : $default_meta[ 'group_categories' ];
		$field_meta[ 'group_categories' ] = $group_categories && is_array( $group_categories ) ? $group_categories : ( $had_group_categories ? array( 'none' ) : array() );
		
		$status                  = isset( $raw_field_data[ 'status' ] ) ? ( is_string( $raw_field_data[ 'status' ] ) ? array( $raw_field_data[ 'status' ] ) : $raw_field_data[ 'status' ] ) : $default_meta[ 'status' ];
		$field_meta[ 'status' ]  = is_array( $status ) ? array_intersect( $status, array_keys( bookacti_get_booking_statuses() ) ) : $default_meta[ 'status' ];
		$field_meta[ 'user_id' ] = isset( $raw_field_data[ 'user_id' ] ) && is_numeric( $raw_field_data[ 'user_id' ] ) ? intval( $raw_field_data[ 'user_id' ] ) : ( isset( $raw_field_data[ 'user_id' ] ) && in_array( $raw_field_data[ 'user_id' ], array( 0, '0', 'current' ), true ) ? $raw_field_data[ 'user_id' ] : $default_meta[ 'user_id' ] );
		
		$field_meta[ 'start' ]    = isset( $raw_field_data[ 'start' ] ) && bookacti_sanitize_date( $raw_field_data[ 'start' ] ) ? bookacti_sanitize_date( $raw_field_data[ 'start' ] ) : $default_meta[ 'start' ];
		$field_meta[ 'end' ]      = isset( $raw_field_data[ 'end' ] ) && bookacti_sanitize_date( $raw_field_data[ 'end' ] ) ? bookacti_sanitize_date( $raw_field_data[ 'end' ] ) : $default_meta[ 'end' ];
		$field_meta[ 'availability_period_start' ] = isset( $raw_field_data[ 'availability_period_start' ] ) && is_numeric( $raw_field_data[ 'availability_period_start' ] ) ? intval( $raw_field_data[ 'availability_period_start' ] ) : $default_meta[ 'availability_period_start' ];
		$field_meta[ 'availability_period_end' ]   = isset( $raw_field_data[ 'availability_period_end' ] ) && is_numeric( $raw_field_data[ 'availability_period_end' ] ) ? intval( $raw_field_data[ 'availability_period_end' ] ) : $default_meta[ 'availability_period_end' ];
		$field_meta[ 'use_global_days_off' ] = isset( $raw_field_data[ 'use_global_days_off' ] ) && is_numeric( $raw_field_data[ 'use_global_days_off' ] ) ? max( min( intval( $raw_field_data[ 'use_global_days_off' ] ), 1 ), -1 ) : $default_meta[ 'use_global_days_off' ];
		
		$days_off = isset( $raw_field_data[ 'days_off' ] ) && is_array( $raw_field_data[ 'days_off' ] ) ? $raw_field_data[ 'days_off' ] : $default_meta[ 'days_off' ];
		if( $context !== 'edit' ) {
			if( $field_meta[ 'use_global_days_off' ] > 0 ) {
				$days_off = bookacti_get_setting_value( 'bookacti_general_settings', 'days_off' );
			} else if( $field_meta[ 'use_global_days_off' ] < 0 ) {
				$days_off = array_merge( bookacti_get_setting_value( 'bookacti_general_settings', 'days_off' ), $days_off );
			}
		}
		$field_meta[ 'days_off' ] = bookacti_sanitize_days_off( $days_off );
		
		$field_meta[ 'form_action' ]                    = isset( $raw_field_data[ 'form_action' ] ) && in_array( $raw_field_data[ 'form_action' ], array_keys( bookacti_get_available_form_actions() ), true ) ? $raw_field_data[ 'form_action' ] : $default_meta[ 'form_action' ];
		$field_meta[ 'when_perform_form_action' ]       = isset( $raw_field_data[ 'when_perform_form_action' ] ) && in_array( $raw_field_data[ 'when_perform_form_action' ], array_keys( bookacti_get_available_form_action_triggers() ), true ) ? $raw_field_data[ 'when_perform_form_action' ] : $default_meta[ 'when_perform_form_action' ];
		$field_meta[ 'redirect_url_by_activity' ]       = isset( $raw_field_data[ 'redirect_url_by_activity' ] ) && is_array( $raw_field_data[ 'redirect_url_by_activity' ] ) ? array_filter( array_map( 'esc_url', $raw_field_data[ 'redirect_url_by_activity' ] ) ) : $default_meta[ 'redirect_url_by_activity' ];
		$field_meta[ 'redirect_url_by_group_category' ] = isset( $raw_field_data[ 'redirect_url_by_group_category' ] ) && is_array( $raw_field_data[ 'redirect_url_by_group_category' ] ) ? array_filter( array_map( 'esc_url', $raw_field_data[ 'redirect_url_by_group_category' ] ) ) : $default_meta[ 'redirect_url_by_group_category' ];
		
		// The Calendar field display data are the same as the booking system's, we can safely use bookacti_format_booking_system_display_data
		$display_data = bookacti_format_booking_system_display_data( $raw_field_data );
		foreach( $default_meta as $default_meta_key => $default_meta_value ) {
			if( isset( $display_data[ $default_meta_key ] ) ) {
				$field_meta[ $default_meta_key ] = $display_data[ $default_meta_key ];
			}
		}
		
	} else if( $raw_field_data[ 'name' ] === 'login' ) {
		// Format meta values
		$keys_by_type = array( 
			'bool'   => array( 'automatic_login', 'generate_password', 'send_new_account_email', 'login_first', 'login_button', 'remember' ),
			'int'    => array( 'min_password_strength' ),
			'str_id' => array( 'new_user_role' ),
			'str'    => array( 'login_button_label', 'register_button_label' )
		);
		$field_meta = bookacti_sanitize_values( $default_meta, $raw_field_data, $keys_by_type );
		
		// Translate texts
		$field_meta[ 'login_button_label' ]    = isset( $raw_field_data[ 'login_button_label' ] ) ? ( $context !== 'edit' && ! empty( $raw_field_data[ 'login_button_label' ] ) ? apply_filters( 'bookacti_translate_text', $raw_field_data[ 'login_button_label' ] ) : $raw_field_data[ 'login_button_label' ] ) : $default_meta[ 'login_button_label' ];
		$field_meta[ 'register_button_label' ] = isset( $raw_field_data[ 'register_button_label' ] ) ? ( $context !== 'edit' && ! empty( $raw_field_data[ 'register_button_label' ] ) ? apply_filters( 'bookacti_translate_text', $raw_field_data[ 'register_button_label' ] ) : $raw_field_data[ 'register_button_label' ] ) : $default_meta[ 'register_button_label' ];
		
		// Treat 'required_fields' and 'displayed_fields' field meta as a common field data
		$default_data[ 'displayed_fields' ] = $default_meta[ 'displayed_fields' ]; unset( $default_meta[ 'displayed_fields' ] );
		$default_data[ 'required_fields' ]  = $default_meta[ 'required_fields' ]; unset( $default_meta[ 'required_fields' ] );
		
		// Format common values (specific cases)
		// Format label, placeholder and tip
		$register_defaults   = bookacti_get_register_fields_default_data( $context );
		$log_in_defaults     = bookacti_get_log_in_fields_default_data( $context );
		$login_type_defaults = bookacti_get_login_type_field_default_options( array(), $context );
		
		$keys = array( 'label', 'placeholder', 'tip', 'displayed_fields', 'required_fields' );
		foreach( $keys as $key ) {
			$raw_login_field_data = isset( $raw_field_data[ $key ] ) ? maybe_unserialize( $raw_field_data[ $key ] ) : array();
			$is_translatable = in_array( $key, array( 'label', 'placeholder', 'tip' ), true );
			
			if( is_array( $raw_login_field_data ) && $raw_login_field_data ) {
				// Format booleans
				if( in_array( $key, array( 'displayed_fields', 'required_fields' ), true ) ) { $raw_login_field_data = array_map( 'intval', $raw_login_field_data ); }
				
				// Format register
				$register_fields = array();
				foreach( $register_defaults as $register_field_name => $register_default ) {
					$register_fields[ $register_field_name ] = $default_data[ $key ][ $register_field_name ];
					if( isset( $raw_login_field_data[ $register_field_name ] ) ) {
						$register_fields[ $register_field_name ] = $is_translatable && $context !== 'edit' && ! empty( $raw_login_field_data[ $register_field_name ] ) ? apply_filters( 'bookacti_translate_text', $raw_login_field_data[ $register_field_name ] ) : $raw_login_field_data[ $register_field_name ];
					}
				}
				
				// Format log in fields
				$log_in_fields = array();
				foreach( $log_in_defaults as $log_in_field_name => $log_in_field_default ) {
					$log_in_fields[ $log_in_field_name ] = $default_data[ $key ][ $log_in_field_name ];
					
					// Keep default "required" attribute for login fields
					if( $key === 'required_fields' ) { continue; }
					
					if( isset( $raw_login_field_data[ $log_in_field_name ] ) ) {
						$log_in_fields[ $log_in_field_name ] = $is_translatable && $context !== 'edit' && ! empty( $raw_login_field_data[ $log_in_field_name ] ) ? apply_filters( 'bookacti_translate_text', $raw_login_field_data[ $log_in_field_name ] ) : $raw_login_field_data[ $log_in_field_name ];
					}
				}
				
				// Format login type
				$login_types = array();
				foreach( $login_type_defaults as $login_type_name => $login_type_default ) {
					$login_types[ $login_type_name ] = $default_data[ $key ][ $login_type_name ];
					if( isset( $raw_login_field_data[ $login_type_name ] ) ) {
						$login_types[ $login_type_name ] = $is_translatable && $context !== 'edit' && ! empty( $raw_login_field_data[ $login_type_name ] ) ? apply_filters( 'bookacti_translate_text', $raw_login_field_data[ $login_type_name ] ) : $raw_login_field_data[ $login_type_name ];
					}
				}
				
				// Merge register fields
				$field_data[ $key ] = array_merge( $log_in_fields, $login_types, $register_fields );

			} else {
				$field_data[ $key ] = $default_data[ $key ];
			}
		}
		
	} else if( $raw_field_data[ 'name' ] === 'quantity' ) {
	} else if( $raw_field_data[ 'name' ] === 'terms' ) {
		// Format common values (specific cases)
		$field_data[ 'label' ] = isset( $raw_field_data[ 'label' ] ) && is_string( $raw_field_data[ 'label' ] ) ? sanitize_text_field( stripslashes( $raw_field_data[ 'label' ] ) ) : $default_data[ 'label' ];
		
	} else if( $raw_field_data[ 'name' ] === 'submit' ) {
		// Format Submit button label
		$keys_by_type = array( 'str' => array( 'value' ) );
		$field_data = bookacti_sanitize_values( $default_data, $raw_field_data, $keys_by_type );
		// Translate texts
		if( $context !== 'edit' ) {
			if( ! empty( $raw_field_data[ 'value' ] ) ) { $field_data[ 'value' ] = apply_filters( 'bookacti_translate_text', $raw_field_data[ 'value' ] ); }
		}
		
	} else if( $raw_field_data[ 'name' ] === 'free_text' ) {
		// Format common values (specific cases)
		if( isset( $raw_field_data[ 'value' ] ) ) {
			// Translate texts
			if( $context !== 'edit' ) {
				$field_data[ 'value' ] = $raw_field_data[ 'value' ] ? wpautop( apply_filters( 'bookacti_translate_text', $raw_field_data[ 'value' ], '', true, array( 'string_name' => ! empty( $raw_field_data[ 'field_id' ] ) ? 'Form field #' . $raw_field_data[ 'field_id' ] . ' - value' : '' ) ) ) : $raw_field_data[ 'value' ];
			} else {
				$field_data[ 'value' ] = $raw_field_data[ 'value' ];
			}
		}
	} else if( $raw_field_data[ 'name' ] === 'total_price' ) {
		// Format meta values
		$keys_by_type = array( 'bool' => array( 'price_breakdown' ) );
		$field_meta = bookacti_sanitize_values( $default_meta, $raw_field_data, $keys_by_type );
	}
	
	// Format common values
	$keys_by_type = array( 
		'int'    => array( 'field_id', 'form_id' ),
		'str_id' => array( 'name', 'type', 'id' ),
		'str'    => array( 'title', 'label', 'class', 'value', 'placeholder', 'tip' ),
		'array'  => array( 'options' ),
		'bool'   => array( 'compulsory', 'default', 'unique', 'required', 'active' )
	);
	$formatted_field_data = bookacti_sanitize_values( $default_data, $raw_field_data, $keys_by_type, $field_data );
	
	// Translate texts
	$translatable_keys = array( 'title', 'label', 'placeholder', 'tip' );
	foreach( $translatable_keys as $key ) {
		if( is_string( $formatted_field_data[ $key ] ) ) { 
			$formatted_field_data[ $key ] = isset( $raw_field_data[ $key ] ) ? ( $context !== 'edit' && ! empty( $raw_field_data[ $key ] ) ? apply_filters( 'bookacti_translate_text', $raw_field_data[ $key ] ) : $raw_field_data[ $key ] ) : ( isset( $field_data[ $key ] ) ? $field_data[ $key ] : $default_data[ $key ] );
		}
	}
	
	// Keep only meta declared in default meta
	foreach( $default_meta as $name => $default ) {
		if( ! isset( $field_meta[ $name ] ) ) { $field_meta[ $name ] = $default; }
	}
	$formatted_field_meta = array_intersect_key( $field_meta, $default_meta );
	
	$formatted_field_data = apply_filters( 'bookacti_formatted_field_data', array_merge( $formatted_field_data, $formatted_field_meta ), $raw_field_data, $context );
	
	return $formatted_field_data;
}


/**
 * Sanitize field data according to its type
 * @since 1.5.0
 * @version 1.17.0
 * @param array|string $raw_field_data
 * @return array
 */
function bookacti_sanitize_form_field_data( $raw_field_data ) {
	// Check if name and type are set
	if( ! is_array( $raw_field_data ) || empty( $raw_field_data[ 'name' ] ) || empty( $raw_field_data[ 'type' ] ) ) { return array(); }
	
	$default_data = bookacti_get_default_form_fields_data( $raw_field_data[ 'name' ], 'edit' );
	$default_meta = bookacti_get_default_form_fields_meta( $raw_field_data[ 'name' ], 'edit' );
	
	if( ! $default_data ) { return array(); }
	
	$field_data	= array();
	$field_meta	= array();
	
	// Sanitize field-specific data and metadata
	if( $raw_field_data[ 'name' ] === 'calendar' ) {
		$booleans_to_check = array( 'groups_only', 'groups_single_events', 'groups_first_event_only', 'multiple_bookings', 'bookings_only', 'trim', 'out_of_period_events', 'past_events', 'past_events_bookable', 'select_first_event' );
		foreach( $booleans_to_check as $key ) {
			if( ! isset( $raw_field_data[ $key ] ) ) { $field_meta[ $key ] = $default_meta[ $key ]; continue; }
			$field_meta[ $key ] = in_array( $raw_field_data[ $key ], array( 1, '1', true, 'true', 'yes', 'ok' ), true ) ? 1 : 0;
		}
		
		$field_meta[ 'id' ]    = isset( $raw_field_data[ 'id' ] ) && $raw_field_data[ 'id' ] !== '' ? sanitize_title_with_dashes( $raw_field_data[ 'id' ] ) : $default_meta[ 'id' ];
		$field_meta[ 'class' ] = isset( $raw_field_data[ 'class' ] ) && $raw_field_data[ 'class' ] !== '' ? sanitize_text_field( $raw_field_data[ 'class' ] ) : $default_meta[ 'class' ];
		
		$field_meta[ 'method' ]            = isset( $raw_field_data[ 'method' ] ) && in_array( $raw_field_data[ 'method' ], array_keys( bookacti_get_available_booking_methods() ), true ) ? $raw_field_data[ 'method' ] : $default_meta[ 'method' ];
		$field_meta[ 'hide_availability' ] = isset( $raw_field_data[ 'hide_availability' ] ) && is_numeric( $raw_field_data[ 'hide_availability' ] ) ? max( min( intval( $raw_field_data[ 'hide_availability' ] ), 100 ), 0 ) : $default_meta[ 'hide_availability' ];
		$field_meta[ 'hide_calendar' ]     = isset( $raw_field_data[ 'hide_calendar' ] ) ? array_values( array_intersect( array_keys( bookacti_get_available_hide_calendar_options() ), bookacti_str_ids_to_array( $raw_field_data[ 'hide_calendar' ] ) ) ) : $default_meta[ 'hide_calendar' ];
		if( ! $field_meta[ 'hide_calendar' ] || array_intersect( $field_meta[ 'hide_calendar' ], array( false, 'none', 'false', 'no' ) ) ) { $field_meta[ 'hide_calendar' ] = array( 'none' ); }
		
		$field_meta[ 'calendars' ] = isset( $raw_field_data[ 'calendars' ] ) ? bookacti_ids_to_array( $raw_field_data[ 'calendars' ] ) : $default_meta[ 'calendars' ];
		
		$had_activities             = ! empty( $raw_field_data[ 'activities' ] );
		if( isset( $raw_field_data[ 'activities' ] ) && ( $raw_field_data[ 'activities' ] === 'all' || ( is_array( $raw_field_data[ 'activities' ] ) && in_array( 'all', $raw_field_data[ 'activities' ], true ) ) ) ) { $had_activities = false; }
		$activities                 = isset( $raw_field_data[ 'activities' ] ) ? bookacti_ids_to_array( $raw_field_data[ 'activities' ] ) : $default_meta[ 'activities' ];
		$field_meta[ 'activities' ] = $activities && is_array( $activities ) ? $activities : ( $had_activities ? array( 'none' ) : array() );
		
		$had_group_categories             = ! empty( $raw_field_data[ 'group_categories' ] );
		if( isset( $raw_field_data[ 'group_categories' ] ) && ( $raw_field_data[ 'group_categories' ] === 'all' || ( is_array( $raw_field_data[ 'group_categories' ] ) && in_array( 'all', $raw_field_data[ 'group_categories' ], true ) ) ) ) { $had_group_categories = false; }
		$group_categories                 = isset( $raw_field_data[ 'group_categories' ] ) ? bookacti_ids_to_array( $raw_field_data[ 'group_categories' ] ) : $default_meta[ 'group_categories' ];
		$field_meta[ 'group_categories' ] = $group_categories && is_array( $group_categories ) ? $group_categories : ( $had_group_categories ? array( 'none' ) : array() );
		
		$status                  = isset( $raw_field_data[ 'status' ] ) ? ( is_string( $raw_field_data[ 'status' ] ) ? array( $raw_field_data[ 'status' ] ) : $raw_field_data[ 'status' ] ) : $default_meta[ 'status' ];
		$field_meta[ 'status' ]  = is_array( $status ) ? array_intersect( $status, array_keys( bookacti_get_booking_statuses() ) ) : $default_meta[ 'status' ];
		$field_meta[ 'user_id' ] = isset( $raw_field_data[ 'user_id' ] ) && is_numeric( $raw_field_data[ 'user_id' ] ) ? intval( $raw_field_data[ 'user_id' ] ) : ( isset( $raw_field_data[ 'user_id' ] ) && in_array( $raw_field_data[ 'user_id' ], array( 0, '0', 'current' ), true ) ? $raw_field_data[ 'user_id' ] : $default_meta[ 'user_id' ] );
		
		$field_meta[ 'start' ]    = isset( $raw_field_data[ 'start' ] ) && bookacti_sanitize_date( $raw_field_data[ 'start' ] ) ? bookacti_sanitize_date( $raw_field_data[ 'start' ] ) : $default_meta[ 'start' ];
		$field_meta[ 'end' ]      = isset( $raw_field_data[ 'end' ] ) && bookacti_sanitize_date( $raw_field_data[ 'end' ] ) ? bookacti_sanitize_date( $raw_field_data[ 'end' ] ) : $default_meta[ 'end' ];
		$field_meta[ 'use_global_days_off' ] = isset( $raw_field_data[ 'use_global_days_off' ] ) && is_numeric( $raw_field_data[ 'use_global_days_off' ] ) ? max( min( intval( $raw_field_data[ 'use_global_days_off' ] ), 1 ), -1 ) : $default_meta[ 'use_global_days_off' ];
		$field_meta[ 'days_off' ]            = isset( $raw_field_data[ 'days_off' ] ) && is_array( $raw_field_data[ 'days_off' ] ) && $field_meta[ 'use_global_days_off' ] < 1 ? bookacti_sanitize_days_off( $raw_field_data[ 'days_off' ] ) : $default_meta[ 'days_off' ];
		$field_meta[ 'availability_period_start' ] = isset( $raw_field_data[ 'availability_period_start' ] ) && is_numeric( $raw_field_data[ 'availability_period_start' ] ) ? intval( $raw_field_data[ 'availability_period_start' ] ) : $default_meta[ 'availability_period_start' ];
		$field_meta[ 'availability_period_end' ]   = isset( $raw_field_data[ 'availability_period_end' ] ) && is_numeric( $raw_field_data[ 'availability_period_end' ] ) ? intval( $raw_field_data[ 'availability_period_end' ] ) : $default_meta[ 'availability_period_end' ];
		
		// Switch start and end if start > end
		if( $field_meta[ 'start' ] && $field_meta[ 'end' ] ) {
			$start_dt = new DateTime( $field_meta[ 'start' ] );
			$end_dt = new DateTime( $field_meta[ 'end' ] );
			if( $start_dt > $end_dt ) {
				$start = $field_meta[ 'start' ];
				$field_meta[ 'start' ] = $field_meta[ 'end' ];
				$field_meta[ 'end' ]   = $start;
			}
		}
		
		// Switch availability_period_start and availability_period_end if availability_period_start > availability_period_end
		if( $field_meta[ 'availability_period_start' ] && $field_meta[ 'availability_period_end' ] 
		&&  $field_meta[ 'availability_period_start' ] > $field_meta[ 'availability_period_end' ] ) {
			$availability_period_start = $field_meta[ 'availability_period_start' ];
			$field_meta[ 'availability_period_start' ] = $field_meta[ 'availability_period_end' ];
			$field_meta[ 'availability_period_end' ]   = $availability_period_start;
		}
		
		$field_meta[ 'form_action' ]                    = isset( $raw_field_data[ 'form_action' ] ) && in_array( $raw_field_data[ 'form_action' ], array_keys( bookacti_get_available_form_actions() ), true ) ? $raw_field_data[ 'form_action' ] : $default_meta[ 'form_action' ];
		$field_meta[ 'when_perform_form_action' ]       = isset( $raw_field_data[ 'when_perform_form_action' ] ) && in_array( $raw_field_data[ 'when_perform_form_action' ], array_keys( bookacti_get_available_form_action_triggers() ), true ) ? $raw_field_data[ 'when_perform_form_action' ] : $default_meta[ 'when_perform_form_action' ];
		$field_meta[ 'redirect_url_by_activity' ]       = isset( $raw_field_data[ 'redirect_url_by_activity' ] ) && is_array( $raw_field_data[ 'redirect_url_by_activity' ] ) ? array_filter( array_map( 'esc_url_raw', array_map( 'wp_unslash', $raw_field_data[ 'redirect_url_by_activity' ] ) ) ) : $default_meta[ 'redirect_url_by_activity' ];
		$field_meta[ 'redirect_url_by_group_category' ] = isset( $raw_field_data[ 'redirect_url_by_group_category' ] ) && is_array( $raw_field_data[ 'redirect_url_by_group_category' ] ) ? array_filter( array_map( 'esc_url_raw', array_map( 'wp_unslash', $raw_field_data[ 'redirect_url_by_group_category' ] ) ) ) : $default_meta[ 'redirect_url_by_group_category' ];
		
		// The Calendar field display data are the same as the booking system's, we can safely use bookacti_sanitize_booking_system_display_data
		$display_data = bookacti_sanitize_booking_system_display_data( $raw_field_data );
		foreach( $default_meta as $default_meta_key => $default_meta_value ) {
			if( isset( $display_data[ $default_meta_key ] ) ) {
				$field_meta[ $default_meta_key ] = $display_data[ $default_meta_key ];
			}
		}
		
	} else if( $raw_field_data[ 'name' ] === 'login' ) {
		// Sanitize meta values
		$keys_by_type = array( 
			'bool'   => array( 'automatic_login', 'generate_password', 'send_new_account_email', 'login_first', 'login_button', 'remember' ),
			'int'    => array( 'min_password_strength' ),
			'str_id' => array( 'new_user_role' ),
			'str'    => array( 'login_button_label', 'register_button_label' )
		);
		$field_meta = bookacti_sanitize_values( $default_meta, $raw_field_data, $keys_by_type );
		
		// Treat 'required_fields' and 'displayed_fields' field meta as a common field data
		$default_data[ 'displayed_fields' ] = $default_meta[ 'displayed_fields' ]; unset( $default_meta[ 'displayed_fields' ] );
		$default_data[ 'required_fields' ]  = $default_meta[ 'required_fields' ]; unset( $default_meta[ 'required_fields' ] );
		
		// Sanitize common values (specific cases)
		// Sanitize label, placeholder and tip
		$register_defaults   = bookacti_get_register_fields_default_data( 'edit' );
		$log_in_defaults     = bookacti_get_log_in_fields_default_data( 'edit' );
		$login_type_defaults = bookacti_get_login_type_field_default_options( array(), 'edit' );
		
		$keys = array( 'label', 'placeholder', 'tip', 'displayed_fields', 'required_fields' );
		foreach( $keys as $key ) {
			$raw_login_field_data = isset( $raw_field_data[ $key ] ) ? maybe_unserialize( $raw_field_data[ $key ] ) : array();
			$is_translatable = in_array( $key, array( 'label', 'placeholder', 'tip' ), true );
			
			if( is_array( $raw_login_field_data ) && $raw_login_field_data ) {
				// Sanitize booleans
				if( in_array( $key, array( 'displayed_fields', 'required_fields' ), true ) ) { $raw_login_field_data = array_map( 'intval', $raw_login_field_data ); }
				
				// Sanitize strings
				else { $raw_login_field_data = array_map( 'stripslashes', $raw_login_field_data ); }
				
				// Sanitize register fields
				$register_fields = array();
				foreach( $register_defaults as $register_field_name => $register_default ) {
					$register_fields[ $register_field_name ] = isset( $raw_login_field_data[ $register_field_name ] ) ? $raw_login_field_data[ $register_field_name ] : ( $is_translatable ? '' : $default_data[ $key ][ $register_field_name ] );
				}
				
				// Sanitize login fields
				$log_in_fields = array();
				foreach( $log_in_defaults as $log_in_field_name => $log_in_field_default ) {
					// Keep default "required" attribute for login fields
					if( $key === 'required_fields' ) {
						$log_in_fields[ $log_in_field_name ] = $default_data[ $key ][ $log_in_field_name ];
						continue;
					}
					
					$log_in_fields[ $log_in_field_name ] = isset( $raw_login_field_data[ $log_in_field_name ] ) ? $raw_login_field_data[ $log_in_field_name ] : ( $is_translatable ? '' : $default_data[ $key ][ $log_in_field_name ] );
				}
				
				// Sanitize login type
				$login_types = array();
				foreach( $login_type_defaults as $login_type_name => $login_type_default ) {
					$login_types[ $login_type_name ] = isset( $raw_login_field_data[ $login_type_name ] ) ? $raw_login_field_data[ $login_type_name ] : ( $is_translatable ? '' : $default_data[ $key ][ $login_type_name ] );
				}
				
				// Merge register fields
				$field_data[ $key ] = array_merge( $log_in_fields, $login_types, $register_fields );
			} else {
				$field_data[ $key ] = array();
			}
		}
		
		// Exception: $field_data[ 'placeholder' ][ 'remember' ] must be a integer
		if(     isset( $field_data[ 'placeholder' ][ 'remember' ] ) 
		&& is_numeric( $field_data[ 'placeholder' ][ 'remember' ] ) ) { $field_data[ 'placeholder' ][ 'remember' ] = intval( $field_data[ 'placeholder' ][ 'remember' ] ); }
		
	} else if( $raw_field_data[ 'name' ] === 'quantity' ) {
	} else if( $raw_field_data[ 'name' ] === 'terms' ) {
		if( isset( $raw_field_data[ 'label' ] ) ) { $field_data[ 'label' ] = bookacti_sanitize_form_field_free_text( $raw_field_data[ 'label' ] ); }
		
	} else if( $raw_field_data[ 'name' ] === 'submit' ) {
		$field_data[ 'value' ] = isset( $raw_field_data[ 'value' ] ) ? sanitize_text_field( $raw_field_data[ 'value' ] ) : '';
	
	} else if( $raw_field_data[ 'name' ] === 'free_text' ) {
		$field_data[ 'value' ] = isset( $raw_field_data[ 'value' ] ) ? bookacti_sanitize_form_field_free_text( $raw_field_data[ 'value' ] ) : '';
		
	} else if( $raw_field_data[ 'name' ] === 'total_price' ) {
		// Sanitize meta values
		$keys_by_type = array( 'bool' => array( 'price_breakdown' ) );
		$field_meta = bookacti_sanitize_values( $default_meta, $raw_field_data, $keys_by_type );
	}
	
	// Sanitize common values
	$keys_by_type = array( 
		'int'    => array( 'field_id', 'form_id' ),
		'str_id' => array( 'name', 'type', 'id' ),
		'str'    => array( 'title', 'label', 'class', 'value', 'placeholder', 'tip' ),
		'array'  => array( 'options' ),
		'bool'   => array( 'compulsory', 'default', 'unique', 'required', 'active' )
	);
	$sanitized_raw_field_data = bookacti_sanitize_values( $default_data, $raw_field_data, $keys_by_type, $field_data );
	
	// Keep only allowed data and metadata
	$sanitized_field_data = array_intersect_key( $sanitized_raw_field_data, $default_data );
	$sanitized_field_meta = array_intersect_key( $field_meta, $default_meta );
	
	$sanitized_data = apply_filters( 'bookacti_sanitized_field_data', array_merge( $sanitized_field_data, $sanitized_field_meta ), $raw_field_data );
	
	// We must serialize arrays in addition to sanitizing them
	foreach( $sanitized_data as $key => $value ) {
		if( is_array( $value ) ) { $sanitized_data[ $key ] = maybe_serialize( $value ); }
	}
	
	return $sanitized_data;
}


/**
 * Sanitize the values entered by the user in the form fields
 * @since 1.5.0
 * @version 1.16.42
 * @param array $values
 * @param string $field_type
 * @return array
 */
function bookacti_sanitize_form_field_values( $values, $field_type = '' ) {
	if( ! $field_type && empty( $values[ 'type' ] ) ) { return $values; }
	if( ! $field_type && ! empty( $values[ 'type' ] ) ) { $field_type = $values[ 'type' ]; }
	
	$sanitized_values = array();
	
	// Login fields
	if( $field_type === 'login' ) {
		$sanitized_values[ 'email' ]             = ! empty( $values[ 'email' ] ) ? ( strpos( $values[ 'email' ], '@', 1 ) !== false ? sanitize_email( wp_unslash( $values[ 'email' ] ) ) : sanitize_user( wp_unslash( $values[ 'email' ] ) ) ) : '';
		$sanitized_values[ 'password' ]          = ! empty( $values[ 'password' ] ) ? trim( sanitize_text_field( wp_unslash( $values[ 'password' ] ) ) ) : '';
		$sanitized_values[ 'password_strength' ] = ! empty( $values[ 'password_strength' ] ) ? intval( $values[ 'password_strength' ] ) : 1;
		$sanitized_values[ 'remember' ]          = ! empty( $values[ 'remember' ] ) ? 1 : 0;
		
		$login_types = bookacti_get_login_type_field_default_options( array(), 'edit' );
		$sanitized_values[ 'login_type' ] = ! empty( $values[ 'login_type' ] ) && in_array( $values[ 'login_type' ], array_keys( $login_types ), true ) ? $values[ 'login_type' ] : '';
		
		$default_register_data = bookacti_get_register_fields_default_data( 'edit' );
		$default_register_values = array();
		foreach( $default_register_data as $field_name => $register_field ) {
			$default_register_values[ $field_name ] = $register_field[ 'value' ];
		}
		$register_keys_by_type = array( 'str' => array( 'first_name', 'last_name', 'phone' ) );
		$sanitized_register_values = bookacti_sanitize_values( $default_register_values, $values, $register_keys_by_type );
		
		$sanitized_values = array_merge( $sanitized_values, $sanitized_register_values );
	}
	
	return apply_filters( 'bookacti_sanitize_form_field_values', $sanitized_values, $values, $field_type );
}


/**
 * Transform form field sanitized data to actual update data (mainly to avoid saving default values)
 * @since 1.16.42
 * @version 1.16.45
 * @param array $field
 * @param array $sanitized_data
 * @param boolean $switch_locale
 * @return array
 */
function bookacti_get_form_field_update_data( $field, $sanitized_data, $switch_locale = true ) {
	// Get edit data in the default language
	$lang_switched = $switch_locale ? bookacti_switch_locale( bookacti_get_site_default_locale() ) : false;
	
	$default_data   = bookacti_get_default_form_fields_data( $field[ 'name' ], 'edit' );
	$default_meta   = bookacti_get_default_form_fields_meta( $field[ 'name' ], 'edit' );
	$sanitized_data = array_map( 'maybe_unserialize', $sanitized_data );
	$field_meta     = array_intersect_key( $sanitized_data, $default_meta );
	
	// Do not save default data, but allow empty data
	$null_data  = array();
	$empty_data = array();
	$null_keys  = array( 'title', 'label', 'placeholder', 'tip', 'options', 'value' );
	foreach( $null_keys as $key ) { 
		if( $sanitized_data[ $key ] === $default_data[ $key ] ) {
			$null_data[ $key ] = 'null';
		}
		else if( in_array( $sanitized_data[ $key ], array( '', 'a:0:{}', array() ), true ) ) {
			$empty_data[ $key ] = 'empty';
		}
	}
	
	// Do not save default meta
	foreach( $field_meta as $meta_key => $meta_value ) {
		$default_value = isset( $default_meta[ $meta_key ] ) ? $default_meta[ $meta_key ] : '';
		if( $meta_value === $default_value ) {
			unset( $field_meta[ $meta_key ] );
		}
	}
	
	$update_data = array_map( 'maybe_serialize', array_merge( array_diff_key( $sanitized_data, $null_data, $empty_data, $default_meta ), $field_meta ) );
	
	foreach( $null_keys as $key ) { 
		if( ! empty( $empty_data[ $key ] ) ) { 
			$update_data[ $key ] = 'empty';
		}
		if( ! isset( $update_data[ $key ] ) || ! empty( $null_data[ $key ] ) ) { 
			$update_data[ $key ] = 'null';
		}
	}
	
	$update_data = apply_filters( 'bookacti_form_field_update_data', $update_data, $field, $sanitized_data );
	
	if( $lang_switched ) { bookacti_restore_locale(); }
	
	return $update_data;
}


/**
 * Display a form field
 * @since 1.5.0
 * @version 1.14.0
 * @param array $field
 * @param string $instance_id
 * @param string $context
 * @param boolean $echo
 * @return string|void
 */
function bookacti_display_form_field( $field, $instance_id = '', $context = 'display', $echo = true ) {
	if( empty( $field[ 'name' ] ) ) { return ''; }
	
	if( ! $instance_id ) { $instance_id = rand(); }
	$field_id       = ! empty( $field[ 'id' ] ) ? esc_attr( $field[ 'id' ] ) : esc_attr( 'bookacti-form-field-' . $field[ 'type' ] . '-' . $field[ 'field_id' ] . '-' . $instance_id );
	$field_class    = 'bookacti-form-field-container';
	$field_css_data = '';
	if( ! empty( $field[ 'name' ] ) )     { $field_class .= ' bookacti-form-field-name-' . sanitize_title_with_dashes( esc_attr( $field[ 'name' ] ) ); $field_css_data .= ' data-field-name="' . esc_attr( $field[ 'name' ] ) . '"'; } 
	if( ! empty( $field[ 'type' ] ) )     { $field_class .= ' bookacti-form-field-type-' . sanitize_title_with_dashes( esc_attr( $field[ 'type' ] ) ); $field_css_data .= ' data-field-type="' . esc_attr( $field[ 'type' ] ) . '"'; } 
	if( ! empty( $field[ 'field_id' ] ) ) { $field_class .= ' bookacti-form-field-id-' . esc_attr( $field[ 'field_id' ] ); $field_css_data .= ' data-field-id="' . esc_attr( $field[ 'field_id' ] ) . '"'; }
	if( ! empty( $field[ 'class' ] ) )    { $field_class .= ' ' . esc_attr( $field[ 'class' ] ); }
	ob_start();
?>
	<div class='<?php echo $field_class; ?>' id='<?php echo $field_id; ?>' <?php echo trim( $field_css_data ); ?>>
	<?php if( ! empty( $field[ 'label' ] ) && is_string( $field[ 'label' ] ) ) { ?>
		<div class='bookacti-form-field-label' >
			<label>
			<?php
				echo $field[ 'label' ];
				if( $field[ 'required' ] ) {
					echo '<span class="bookacti-required-field-indicator" title="' . esc_attr__( 'Required field', 'booking-activities' ) . '"></span>';
				}
			?>
			</label>
		<?php if( ! empty( $field[ 'tip' ] ) && is_string( $field[ 'tip' ] )) { bookacti_help_tip( esc_html( $field[ 'tip' ] ) ); } ?>
		</div>
	<?php } ?>
		<div class='bookacti-form-field-content' >
		<?php 
			do_action( 'bookacti_display_form_field_' . $field[ 'type' ], $field, $instance_id, $context ); 
		?>
		</div>
	</div>
<?php
	$html = apply_filters( 'bookacti_html_form_field_' . $field[ 'type' ], ob_get_clean(), $field, $instance_id, $context );
	if( ! $echo ) { return $html; }
	echo $html;
}


/**
 * Display a form field for the form editor
 * @since 1.5.0
 * @version 1.15.0
 * @param array $field
 * @param boolean $echo
 * @return string|void
 */
function bookacti_display_form_field_for_editor( $field, $echo = true ) {
	$field_name = esc_attr( $field[ 'name' ] );
	$field_class= 'bookacti-form-editor-field';
	if( ! empty( $field[ 'name' ] ) )     { $field_class .= ' bookacti-form-field-name-' . sanitize_title_with_dashes( esc_attr( $field[ 'name' ] ) ); } 
	if( ! empty( $field[ 'type' ] ) )     { $field_class .= ' bookacti-form-field-type-' . sanitize_title_with_dashes( esc_attr( $field[ 'type' ] ) ); } 
	if( ! empty( $field[ 'field_id' ] ) ) { $field_class .= ' bookacti-form-field-id-' . esc_attr( $field[ 'field_id' ] ); }
	if( ! empty( $field[ 'class' ] ) )    { $field_class .= ' ' . esc_attr( $field[ 'class' ] ); }
	$field[ 'id' ] = esc_attr( 'bookacti-form-editor-' . $field_name );
	if( ! $field[ 'unique' ] ) { $field[ 'id' ] .= '-' . esc_attr( $field[ 'field_id' ] ); }
	if( ! $echo ) { ob_start(); }
	?>
	<div id='bookacti-form-editor-field-<?php echo esc_attr( $field[ 'field_id' ] ); ?>' class='<?php echo $field_class; ?>' data-field-id='<?php echo esc_attr( $field[ 'field_id' ] ); ?>' data-field-name='<?php echo esc_attr( $field[ 'name' ] ); ?>' >
		<div class='bookacti-form-editor-field-header' >
			<div class='bookacti-form-editor-field-title' >
				<h3><?php echo apply_filters( 'bookacti_form_editor_field_title', wp_kses_post( $field[ 'title' ] ), $field ); ?></h3>
			</div>
			<div class='bookacti-form-editor-field-actions' >
			<?php do_action( 'bookacti_form_editor_field_actions_before', $field ); ?>
				<div id='bookacti-edit-form-field-<?php echo esc_attr( $field[ 'field_id' ] ); ?>' class='bookacti-form-editor-field-action bookacti-edit-form-field <?php echo $field_name === 'calendar' ? 'button button-secondary' : 'dashicons dashicons-admin-generic'; ?>' title='<?php esc_attr_e( 'Change field settings', 'booking-activities' ); ?>'><?php if( $field_name === 'calendar' ) { esc_attr_e( 'Calendar settings', 'booking-activities' ); } ?></div>
			<?php if( ! $field[ 'compulsory' ] ) { ?>
				<div id='bookacti-remove-form-field-<?php echo esc_attr( $field[ 'field_id' ] ); ?>' class='bookacti-form-editor-field-action bookacti-remove-form-field dashicons dashicons-trash' title='<?php esc_attr_e( 'Remove this field', 'booking-activities' ); ?>'></div>
			<?php }
				do_action( 'bookacti_form_editor_field_actions_after', $field ); 
			?>
				<div id='bookacti-toggle-form-field-<?php echo esc_attr( $field[ 'field_id' ] ); ?>' class='bookacti-field-toggle dashicons <?php echo $field_name === 'calendar' ? 'dashicons-arrow-up' :'dashicons-arrow-down'; ?>' title='<?php esc_attr_e( 'Show / Hide', 'booking-activities' ); ?>'></div>
			</div>
		</div>
		<div class='bookacti-form-editor-field-body' style='<?php echo $field_name === 'calendar' ? '' : 'display:none;'; ?>' >
		<?php
			bookacti_display_form_field( $field, 'form-editor', 'edit' );
		?>
		</div>
	</div>
<?php
	if( ! $echo ) { return ob_get_clean(); }
}


/**
 * Validate register fields
 * @since 1.5.0
 * @version 1.16.45
 * @param array $login_values
 * @param array $login_data
 * @return array
 */
function bookacti_validate_registration( $login_values, $login_data ) {
	$return_array = array( 'status' => 'success', 'error' => '', 'messages' => array() );

	// Check email
	if( ! is_email( $login_values[ 'email' ] ) || strlen( $login_values[ 'email' ] ) > 64 ) {
		$return_array[ 'error' ] = 'invalid_email';
		$return_array[ 'messages' ][ 'invalid_email' ] = esc_html__( 'Invalid email address.', 'booking-activities' );
	}
	
	// Check if password exists
	if( ! $login_values[ 'password' ] && ! $login_data[ 'generate_password' ] ) {
		$return_array[ 'error' ] = 'invalid_password';
		$return_array[ 'messages' ][ 'invalid_password' ] = esc_html__( 'Invalid password.', 'booking-activities' );
	}
	
	// Check password strength
	if( $login_values[ 'password_strength' ] < $login_data[ 'min_password_strength' ] && ! $login_data[ 'generate_password' ] ) {
		$return_array[ 'error' ] = 'invalid_password_strength';
		$return_array[ 'messages' ][ 'invalid_password_strength' ] = esc_html__( 'Your password is not strong enough.', 'booking-activities' );
	}
	
	// Check that required register fields are filled
	foreach( $login_data[ 'required_fields' ] as $field_name => $is_required ) {
		if( $is_required && empty( $login_values[ $field_name ] ) ) {
			if( $field_name === 'password' && $login_data[ 'generate_password' ] ) { continue; }
			$field_label = ! empty( $login_data[ 'label' ][ $field_name ] ) ? $login_data[ 'label' ][ $field_name ] : $field_name;
			/* translators: %s is the field name. */
			$return_array[ 'messages' ][ 'missing_' . $field_name ] = sprintf( esc_html__( 'The field "%s" is required.', 'booking-activities' ), $field_label );
			$return_array[ 'error' ] = 'missing_' . $field_name;
		}
	}
	
	if( ! empty( $return_array[ 'error' ] ) ) {
		$return_array[ 'status' ] = 'failed';
	}
	
	return apply_filters( 'bookacti_validate_registration_form', $return_array, $login_values );
}


/**
 * Register a new user through a booking form
 * @since 1.5.0
 * @version 1.16.42
 * @param array $login_values
 * @param array $login_data
 * @return WP_User|false
 */
function bookacti_register_a_new_user( $login_values, $login_data ) {
	$return_array = array( 'status' => 'failed', 'error' => '', 'messages' => array() );
	$user         = null;
	
	// Validate registration fields
	$resgister_response = bookacti_validate_registration( $login_values, $login_data );
	if( $resgister_response[ 'status' ] !== 'success' ) {
		$return_array[ 'error' ]    = 'invalid_register_field';
		$return_array[ 'messages' ] = $resgister_response[ 'messages' ];
		
	} else {
		// Generate username
		$username  = ! empty( $login_values[ 'first_name' ] ) ? $login_values[ 'first_name' ] . ' ' : '';
		$username .= ! empty( $login_values[ 'last_name' ] ) ? $login_values[ 'last_name' ] : '';
		if( ! trim( $username ) ) {
			$username = substr( $login_values[ 'email' ], 0, strpos( $login_values[ 'email' ], '@' ) );
		}
		$username = sanitize_title_with_dashes( $username );
		$username_base = strlen( $username ) > 60 ? substr( $username, 0, 50 ) : $username;
		$i = 2;
		while( username_exists( $username ) ) {
			$username = $username_base . '-' . $i;
			++$i;
		}

		// Let third party modify initial user data
		$new_user_data = apply_filters( 'bookacti_register_user_data', array(
			'user_login' => $username,
			'user_pass'  => ! empty( $login_data[ 'generate_password' ] ) ? wp_generate_password( 24 ) : $login_values[ 'password' ],
			'user_email' => $login_values[ 'email' ],
			'first_name' => ! empty( $login_values[ 'first_name' ] ) ? $login_values[ 'first_name' ] : '',
			'last_name'  => ! empty( $login_values[ 'last_name' ] ) ? $login_values[ 'last_name' ] : '',
			'role'       => $login_data[ 'new_user_role' ] === 'default' ? get_option( 'default_role' ) : $login_data[ 'new_user_role' ]
		), $login_values );

		// Create the user
		$user_id = wp_insert_user( $new_user_data );
		if( is_wp_error( $user_id ) ) { 
			$return_array[ 'error' ] = $user_id->get_error_code();
			$return_array[ 'messages' ][ $return_array[ 'error' ] ] = $user_id->get_error_message();
			
		} else {
			$user = get_user_by( 'id', $user_id );

			// Insert user metadata
			$user_meta = array();
			if( ! empty( $login_values[ 'phone' ] ) ) { $user_meta[ 'phone' ] = $login_values[ 'phone' ]; }
			$user_meta = apply_filters( 'bookacti_register_user_metadata', $user_meta, $user, $login_values );
			foreach( $user_meta as $meta_key => $meta_value ) {
				update_user_meta( $user_id, $meta_key, $meta_value );
			}

			// Send the welcome email
			$user_registered_notify = apply_filters( 'bookacti_new_registered_user_notify', empty( $login_data[ 'send_new_account_email' ] ) ? 'admin' : 'both', $user, $login_values );
			bookacti_send_new_user_notification( $user_id, $user_registered_notify, 1 );
		}
	}
	
	return apply_filters( 'bookacti_new_registered_user', is_a( $user, 'WP_User' ) ? $user : $return_array, $login_values, $login_data );
}


/**
 * Validate login fields
 * @since 1.5.0
 * @version 1.16.45
 * @param array $login_values
 * @return WP_User|array
 */
function bookacti_validate_login( $login_values ) {
	// Check if email is correct
	$user_login = ! empty( $login_values[ 'email' ] ) ? $login_values[ 'email' ] : '';
	$user       = $user_login ? get_user_by( is_email( $user_login ) ? 'email' : 'login', $user_login ) : null;
	
	$return_array = array( 'status' => 'failed', 'error' => '', 'messages' => array() );
	
	if( ! $user ) { 
		$return_array[ 'error' ] = 'user_not_found';
		$return_array[ 'messages' ][ 'user_not_found' ] = esc_html__( 'The email address/username provided does not match any account.', 'booking-activities' );
	}

	// Check if password is correct
	else {
		$password = ! empty( $login_values[ 'password' ] ) ? $login_values[ 'password' ] : '';
		$user     = wp_authenticate( $user->user_email, $password );
		
		if( ! is_a( $user, 'WP_User' ) ) {
			if( is_wp_error( $user ) ) {
				$return_array[ 'error' ] = $user->get_error_code();
				$return_array[ 'messages' ][ $return_array[ 'error' ] ] = $user->get_error_message();
			}
			
			if( empty( $return_array[ 'error' ] ) )    { $return_array[ 'error' ]   = 'authentication_failed'; }
			if( empty( $return_array[ 'messages' ] ) ) { $return_array[ 'messages' ][ $return_array[ 'error' ] ] = __( '<strong>Error</strong>: Invalid username, email address or incorrect password.' ); }
		}
	}
	
	return apply_filters( 'bookacti_validate_login_form', is_a( $user, 'WP_User' ) ? $user : $return_array, $login_values );
}


/**
 * Process login form: log in / register / book without account the user
 * @since 1.16.42
 * @version 1.16.45
 * @param int $form_id
 * @param array $login_values
 * @return array
 */
function bookacti_process_login_form( $form_id, $login_values ) {
	$return_array = array( 'status' => 'success', 'error' => '', 'messages' => array(), 'user_id' => '' );
	
	if( is_user_logged_in() ) {
		$return_array[ 'user_id' ] = apply_filters( 'bookacti_current_user_id', get_current_user_id() );
	}
	else if( ! apply_filters( 'bookacti_bypass_login_process', false ) ) {
		// Retrieve login data
		$login_field = bookacti_get_form_field_data_by_name( $form_id, 'login' );
		$user        = null;
		
		// Check login data and input values
		if( ! $login_field || empty( $login_values[ 'login_type' ] ) ) {
			$return_array[ 'error' ] = 'not_logged_in';
			$return_array[ 'messages' ][ 'not_logged_in' ] = esc_html__( 'You are not logged in. Please create an account and log in first.', 'booking-activities' );
		}
		
		// Check email address
		else if( $login_field[ 'required_fields' ][ 'email' ]
		&&  ( empty( $login_values[ 'email' ] ) || strlen( $login_values[ 'email' ] ) > 64 ) ) {
			$return_array[ 'error' ] = 'invalid_email';
			$return_array[ 'messages' ][ 'invalid_email' ] = esc_html__( 'Your email address is not valid.', 'booking-activities' );
		}
		
		// Register
		else if( $login_values[ 'login_type' ] === 'new_account' ) {
			// Register the new user
			$user = bookacti_register_a_new_user( $login_values, $login_field );
			
			if( ! is_a( $user, 'WP_User' ) ) {
				$return_array[ 'error' ]    = 'user_registration_failed';
				$return_array[ 'messages' ]	= $user[ 'messages' ];
				
			} else {
				do_action( 'bookacti_booking_form_user_registered', $user, $form_id, $login_values );

				$return_array[ 'user_id' ] = $user->ID;
				$return_array[ 'has_registered' ] = true;
				$return_array[ 'messages' ][ 'registered' ] = esc_html__( 'Your account has been successfully created.', 'booking-activities' );
			}
			
		// Login
		} else if( $login_values[ 'login_type' ] === 'my_account' ) {
			// Validate login fields
			$user = bookacti_validate_login( $login_values );
			if( ! is_a( $user, 'WP_User' ) ) {
				$return_array[ 'error' ]    = 'user_login_failed';
				$return_array[ 'messages' ] = $user[ 'messages' ];
			} else {
				$return_array[ 'user_id' ] = $user->ID;
			}
			
		// Book without account
		} else if( $login_values[ 'login_type' ] === 'no_account' ) {
			$return_array[ 'user_id' ] = ! empty( $login_values[ 'email' ] ) ? $login_values[ 'email' ] : '';
			
			// Check if the user exists
			if( is_email( $return_array[ 'user_id' ] ) ) {
				$user = get_user_by( 'email', $return_array[ 'user_id' ] );
				if( is_a( $user, 'WP_User' ) ) {
					$current_url    = ! empty( $_POST[ 'current_url' ] ) ? sanitize_url( $_POST[ 'current_url' ] ) : ( ! empty( $_SERVER[ 'HTTP_REFERER' ] ) ? sanitize_url( $_SERVER[ 'HTTP_REFERER' ] ) : '' );
					$login_url      = bookacti_get_login_link( $current_url, true );
					$login_field_id = ! empty( $login_field[ 'field_id' ] ) ? $login_field[ 'field_id' ] : '';
					
					$return_array[ 'error' ] = 'user_already_exists';
					$return_array[ 'messages' ][ 'user_already_exists' ] = sprintf(
						/* translators: %s = "Log in" link. */
						esc_html__( 'This email address is associated with an account (%s). In order to make a booking without account, you need to choose an email address that is not associated with any account.', 'booking-activities' ),
						'<a href="' . $login_url . '" class="bookacti-login-link" data-field-id="' . $login_field_id . '">' . esc_html__( 'Log in', 'booking-activities' ) . '</a>'
					);
				}
			} else {
				$return_array[ 'error' ] = 'invalid_email';
				$return_array[ 'messages' ][ 'invalid_email' ] = esc_html__( 'Your email address is not valid.', 'booking-activities' );
			}
			
			// Check that required register fields are filled
			if( empty( $return_array[ 'error' ] ) ) {
				$register_fields_errors = array();
				foreach( $login_field[ 'required_fields' ] as $field_name => $is_required ) {
					if( $is_required && empty( $login_values[ $field_name ] ) ) {
						if( $field_name === 'password' ) { continue; }
						$field_label = ! empty( $login_field[ 'label' ][ $field_name ] ) ? $login_field[ 'label' ][ $field_name ] : $field_name;
						/* translators: %s is the field name. */
						$register_fields_errors[ 'required_' . $field_name ] = sprintf( esc_html__( 'The field "%s" is required.', 'booking-activities' ), $field_label );
					}
				}

				if( ! empty( $register_fields_errors ) ) {
					$return_array[ 'error' ] = 'invalid_register_field';
					$return_array[ 'messages' ]	= array_merge( $return_array[ 'messages' ], $register_fields_errors );
				}
			}
		}
		
		// Log the user in programmatically
		if( $login_field[ 'automatic_login' ] && is_a( $user, 'WP_User' ) && empty( $return_array[ 'error' ] ) ) {
			$is_logged_in = bookacti_log_user_in( $user->user_login, $login_values[ 'remember' ] );
			if( ! $is_logged_in ) { 
				$return_array[ 'error' ] = 'cannot_log_in';
				$return_array[ 'messages' ][ 'cannot_log_in' ] = esc_html__( 'An error occurred while trying to log you in.', 'booking-activities' );
			} else {
				do_action( 'bookacti_booking_form_user_logged_in', $user, $form_id, $login_values );

				$return_array[ 'has_logged_in' ] = true;
				$return_array[ 'messages' ][ 'logged_in' ] = esc_html__( 'You are now logged into your account.', 'booking-activities' );
			}
		}
	}
	
	// If the user ID or email could not be found
	if( empty( $return_array[ 'user_id' ] ) && empty( $return_array[ 'error' ] ) ) {
		$return_array[ 'error' ] = 'invalid_user';
		$return_array[ 'messages' ][ 'invalid_user' ] = esc_html__( 'Invalid user data.', 'booking-activities' );
	}
	
	if( ! empty( $return_array[ 'error' ] ) ) {
		$return_array[ 'status' ] = 'failed';
	}
	
	return apply_filters( 'bookacti_process_login_form', $return_array, $form_id, $login_values );
}


/**
 * Validate form fields according to values received with $_POST
 * @since 1.7.0
 * @version 1.16.42
 * @param int $form_id
 * @param array $fields_data
 * @return array
 */
function bookacti_validate_form_fields( $form_id = 0, $fields_data = array() ) {
	// Get form data
	if( $form_id && ! $fields_data ) { 
		$fields_data = bookacti_get_form_fields_data( $form_id );
	}
	
	$validated = array( 
		'status'   => 'success',
		'messages' => array()
	);
	
	// Make sure that form data exist
	if( ! $fields_data ) { 
		$validated[ 'status' ] = 'failed';
		$validated[ 'messages' ][ 'invalid_form_id' ] = esc_html__( 'Invalid form ID.', 'booking-activities' );
		
	} else {
		// Validate terms
		$has_terms = false;
		foreach( $fields_data as $field_data ) {
			if( $field_data[ 'name' ] === 'terms' ) { 
				$has_terms = true;
				break;
			}
		}
		if( $has_terms && empty( $_POST[ 'terms' ] ) ) {
			$validated[ 'status' ] = 'failed';
			$validated[ 'messages' ][ 'terms_not_agreed' ] = esc_html__( 'You must agree to the terms and conditions.', 'booking-activities' );
		}
	}
	
	return apply_filters( 'bookacti_validate_form_fields', $validated, $form_id, $fields_data );
}


/**
 * Validate reschedule form fields according to values received with $_POST
 * @since 1.15.6
 * @version 1.16.0
 * @param array $bookings
 * @return array
 */
function bookacti_validate_reschedule_form_fields( $bookings = array() ) {
	$validated = array( 
		'status'   => 'success',
		'messages' => array()
	);
	return apply_filters( 'bookacti_validate_reschedule_form_fields', $validated, $bookings );
}




/* FIELD ORDER */

/**
 * Sanitize a field order array
 * @since 1.5.0
 * @version 1.5.2
 * @param int $form_id
 * @param array $field_order
 * @return array
 */
function bookacti_sanitize_form_field_order( $form_id, $field_order ) {
	
	$custom_fields = bookacti_get_form_fields( $form_id );
	
	if( ! $custom_fields ) { return array(); }
	
	// Get existing field ids for the desired form
	$existing_field_ids = array();
	foreach( $custom_fields as $custom_field ) {
		$existing_field_ids[] = $custom_field[ 'field_id' ];
	}
	
	// Keep only existing field ids
	$intersect = array_intersect( $field_order, $existing_field_ids );
	
	// Add existing missing field ids to field order
	$diff  = array_diff( $existing_field_ids, $intersect );
	$merge = array_merge( $intersect, $diff );
	
	// Sanitize strings to integers
	$map    = array_map( 'intval', $merge );
	$filter = array_filter( $map );
	
	return array_values( $filter );
}


/**
 * Get form fields order
 * @since 1.12.0
 * @version 1.14.0
 * @param int $form_id
 * @return array
 */
function bookacti_get_form_fields_order( $form_id ) {
	$fields_order = wp_cache_get( 'form_fields_order_' . $form_id, 'bookacti' );
	if( $form_id && $fields_order === false ) {
		$fields_order_raw = bookacti_get_metadata( 'form', $form_id, 'field_order', true );
		$fields_order     = $fields_order_raw ? bookacti_ids_to_array( $fields_order_raw ) : array();
		wp_cache_set( 'form_fields_order_' . $form_id, $fields_order, 'bookacti' );
	}
	return $fields_order ? $fields_order : array();
}


/**
 * Sort fields array by field order
 * @since 1.5.0
 * @version 1.16.33
 * @param int $form_id
 * @param array $fields
 * @param boolean $remove_unordered_fields Whether to remove the fields that are not in the field order array 
 * @param boolean $keep_keys Whether to keep the keys
 * @return array
 */
function bookacti_sort_form_fields_array( $form_id, $fields, $remove_unordered_fields = false, $keep_keys = false ) {
	// Sort form fields by customer custom order
	$field_order = bookacti_get_form_fields_order( $form_id );
	
	if( $field_order ) { 
		$ordered_fields = array();
		$remaining_fields = $fields;
		foreach( $field_order as $field_id ) {
			foreach( $fields as $i => $field ) {
				if( empty( $field[ 'field_id' ] ) ) { continue; }
				if( intval( $field[ 'field_id' ] ) !== intval( $field_id ) ) { continue; }
				if( $keep_keys ) { $ordered_fields[ $i ] = $fields[ $i ]; }
				else { $ordered_fields[] = $fields[ $i ]; }
				unset( $remaining_fields[ $i ] );
				break;
			}
		}

		// Add the remaining unordered fields (if any)
		if( ! $remove_unordered_fields && $remaining_fields ) {
			foreach( $remaining_fields as $i => $remaining_field ) {
				if( $keep_keys ) { $ordered_fields[ $i ] = $remaining_field; }
				else { $ordered_fields[] = $remaining_field; }
			}
		}
	} else {
		$ordered_fields = $keep_keys ? $fields : array_values( $fields );
	}
	
	return apply_filters( 'bookacti_ordered_form_fields', $ordered_fields, $form_id, $fields, $remove_unordered_fields, $keep_keys );
}




// LOGIN / REGISTRATION

/**
 * Get log in fields default data
 * @since 1.14.0 (was bookacti_get_login_fields_default_data)
 * @param string $context
 * @return array
 */
function bookacti_get_log_in_fields_default_data( $context = 'view' ) {
	$defaults = array(
		'email' => array( 
			'name'        => 'email', 
			'type'        => 'text', 
			'label'       => esc_html__( 'Email', 'booking-activities' ), 
			'placeholder' => '', 
			'tip'         => '', 
			'value'       => '', 
			'required'    => 1, 
			'displayed'   => 1
		),
		'password' => array( 
			'name'        => 'password', 
			'type'        => 'password', 
			'label'       => esc_html__( 'Password', 'booking-activities' ), 
			'placeholder' => '', 
			'tip'         => '', 
			'value'       => '', 
			'required'    => 1, 
			'displayed'   => 1
		),
		'forgotten_password' => array( 
			'name'        => 'forgotten_password', 
			'type'        => 'link', 
			'label'       => esc_html__( 'Forgot your password?', 'booking-activities' ), 
			'placeholder' => '', 
			'tip'         => '', 
			'value'       => '', 
			'required'    => 0, 
			'displayed'   => 1
		),
		'remember' => array( 
			'name'        => 'remember', 
			'type'        => 'checkbox', 
			'label'       => esc_html__( 'Remember me', 'booking-activities' ), 
			'placeholder' => 0, 
			'tip'         => '', 
			'required'    => 0, 
			'displayed'   => 0
		)
	);
	
	return apply_filters( 'bookacti_log_in_fields_default_data', $defaults, $context );
}


/**
 * Get user meta fields default data
 * @since 1.6.0
 * @version 1.14.0
 * @param array $keys
 * @param string $context
 * @return array
 */
function bookacti_get_login_type_field_default_options( $keys = array(), $context = 'view' ) {
	$login_types = apply_filters( 'bookacti_login_type_field_default_options', array(
		'my_account' => array( 
			'value'       => 'my_account', 
			'title'       => esc_html__( 'Log in', 'booking-activities' ), 
			'label'       => esc_html__( 'Log in', 'booking-activities' ), 
			'placeholder' => '', 
			'tip'         => '', 
			'displayed'   => 1
		),
		'new_account' => array( 
			'value'       => 'new_account', 
			'title'       => esc_html__( 'Create an account', 'booking-activities' ), 
			'label'       => esc_html__( 'Create an account', 'booking-activities' ), 
			'placeholder' => '', 
			'tip'         => '', 
			'displayed'   => 1
		),
		'no_account' => array( 
			'value'       => 'no_account', 
			'title'       => esc_html__( 'Book without account', 'booking-activities' ), 
			'label'       => esc_html__( 'Book without account', 'booking-activities' ), 
			'placeholder' => '', 
			'tip'         => '',  
			'displayed'   => 1
		)
	), $keys, $context );
	
	// Filter by tab
	if( $keys ) {
		foreach( $login_types as $login_type_name => $login_type ) {
			if( ! in_array( $login_type_name, $keys, true ) ) {
				unset( $login_types[ $login_type_name ] );
			}
		}
	}
	
	return $login_types;
}


/**
 * Get user meta fields default data
 * @since 1.5.0
 * @version 1.14.0
 * @param string $context 'view' or 'edit'
 * @return array
 */
function bookacti_get_register_fields_default_data( $context = 'view' ) {
	$defaults = array(
		'first_name' => array( 
			'name'        => 'first_name', 
			'type'        => 'text', 
			'label'       => esc_html__( 'First name', 'booking-activities' ), 
			'placeholder' => '', 
			'tip'         => '', 
			'value'       => '', 
			'required'    => 0, 
			'displayed'   => 1
		),
		'last_name' => array( 
			'name'        => 'last_name', 
			'type'        => 'text', 
			'label'       => esc_html__( 'Last name', 'booking-activities' ), 
			'placeholder' => '', 
			'tip'         => '', 
			'value'       => '', 
			'required'    => 0, 
			'displayed'   => 1
		),
		'phone' => array( 
			'name'        => 'phone', 
			'type'        => 'tel', 
			'label'       => esc_html__( 'Phone number', 'booking-activities' ), 
			'placeholder' => '', 
			'tip'         => '', 
			'value'       => '', 
			'required'    => 0, 
			'displayed'   => 1
		)
	);
	
	return apply_filters( 'bookacti_register_fields_default_data', $defaults, $context );
}




// FORM LIST

/**
 * Get Default form filters
 * @since 1.16.0
 * @return array
 */
function bookacti_get_default_form_filters() {
	return apply_filters( 'bookacti_default_form_filters', array(
		'id'       => array(), 
		'title'    => '', 
		'status'   => array(), 
		'user_id'  => 0, 
		'active'   => false,
		'order_by' => array( 'id' ), 
		'order'    => 'desc',
		'offset'   => 0,
		'per_page' => 0
	));
}


/**
 * Format form filters
 * @since 1.5.0
 * @version 1.16.0
 * @param array $filters 
 * @return array
 */
function bookacti_format_form_filters( $filters = array() ) {
	$default_filters = bookacti_get_default_form_filters();

	$formatted_filters = array();
	foreach( $default_filters as $filter => $default_value ) {
		// If a filter isn't set, use the default value
		if( ! isset( $filters[ $filter ] ) ) {
			$formatted_filters[ $filter ] = $default_value;
			continue;
		}

		$current_value = $filters[ $filter ];

		// Else, check if its value is correct, or use default
		if( in_array( $filter, array( 'id' ), true ) ) {
			if( is_numeric( $current_value ) ) { $current_value = array( $current_value ); }
			if( ! is_array( $current_value ) ) { $current_value = $default_value; }
			else if( ( $i = array_search( 'all', $current_value, true ) ) !== false ) { unset( $current_value[ $i ] ); }
			$current_value = array_values( bookacti_ids_to_array( $current_value, false ) );
		
		} else if( in_array( $filter, array( 'title' ), true ) ) {
			if( ! is_string( $current_value ) ) { $current_value = $default_value; }
			$current_value = sanitize_text_field( $current_value );
		
		} else if( in_array( $filter, array( 'status' ), true ) ) {
			if( is_string( $current_value ) )  { $current_value = array( $current_value ); }
			if( ! is_array( $current_value ) ) { $current_value = $default_value; }
			else if( ( $i = array_search( 'all', $current_value, true ) ) !== false ) { unset( $current_value[ $i ] ); }
			$current_value = array_values( bookacti_str_ids_to_array( $current_value ) );
			
		} else if( in_array( $filter, array( 'active' ), true ) ) {
				 if( in_array( $current_value, array( true, 'true', 1, '1' ), true ) ) { $current_value = 1; }
			else if( in_array( $current_value, array( 0, '0' ), true ) ) { $current_value = 0; }
			if( ! in_array( $current_value, array( 0, 1 ), true ) ) { $current_value = $default_value; }
		
		} else if( $filter === 'order_by' ) {
			$sortable_columns = array( 
				'id', 
				'title'
			);
			if( is_string( $current_value ) ) { 
				if( ! in_array( $current_value, $sortable_columns, true ) ) { $current_value = $default_value; }
				else { $current_value = array( $current_value ); }
			}
			if( ! is_array( $current_value ) || ! $current_value ) { $current_value = $default_value; }
			$current_value = array_values( bookacti_str_ids_to_array( $current_value ) );
			
		} else if( $filter === 'order' ) {
			if( ! in_array( $current_value, array( 'asc', 'desc' ), true ) ) { $current_value = $default_value; }

		} else if( in_array( $filter, array( 'user_id', 'offset', 'per_page' ), true ) ) {
			if( ! is_numeric( $current_value ) ) { $current_value = $default_value; }
			$current_value = intval( $current_value );
		}
		
		$formatted_filters[ $filter ] = $current_value;
	}
	
	return $formatted_filters;
}




// FORM EDITOR METABOXES

/**
 * Display 'managers' metabox content for forms
 * @since 1.5.0
 * @version 1.16.2
 * @param array $form_raw
 */
function bookacti_display_form_managers_meta_box( $form_raw ) {
	$manager_ids  = bookacti_get_form_managers( $form_raw[ 'form_id' ] );
	$capabilities = array( 'bookacti_edit_forms' );
	$role_in      = apply_filters( 'bookacti_managers_roles', array_merge( bookacti_get_roles_by_capabilities( $capabilities ), $capabilities ), 'form' );
	$role_not_in  = apply_filters( 'bookacti_managers_roles_exceptions', array( 'administrator' ), 'form' );
	
	$user_id             = intval( $form_raw[ 'user_id' ] );
	$author_capabilities = array( 'bookacti_create_forms', 'bookacti_edit_forms' );
	$author_role_in      = apply_filters( 'bookacti_author_roles', array_merge( bookacti_get_roles_by_capabilities( $author_capabilities ), $author_capabilities ), 'form' );
	
	$fields = array(
		'author' => array(
			'type'      => 'user_id',
			'name'      => 'author',
			'id'        => 'bookacti-form-author',
			'fullwidth' => 1,
			'options'   => array(
				'option_label' => array( 'display_name', ' (#', 'ID', ' - ', 'user_login', ') - ', 'roles' ),
				'selected'     => array( $user_id ),
				'role__in'     => $author_role_in ? $author_role_in : array( 'none' ),
				'meta'         => false,
				'multiple'     => 0,
				'ajax'         => 0
			),
			'title'     => esc_html__( 'Author', 'booking-activities' ),
			'tip'       => esc_html__( 'Select the form author. The author must be allowed to manage all the calendars displayed in this form, otherwise, their events won\'t be displayed. If you are not sure, set the form author to an administrator.', 'booking-activities' )
		),
		'form-managers' => array( 
			'type'      => 'user_id',
			'name'      => 'form-managers',
			'id'        => 'bookacti-form-managers',
			'fullwidth' => 1,
			'options'   => array(
				'option_label' => array( 'display_name', ' (', 'user_login', ')' ),
				'selected'     => $manager_ids,
				'role__in'     => $role_in ? $role_in : array( 'none' ),
				'role__not_in' => $role_not_in,
				'meta'         => false,
				'multiple'     => 1,
				'ajax'         => 0
			),
			'title'     => esc_html__( 'Who can manage this form?', 'booking-activities' ),
			'tip'       => esc_html__( 'Choose who is allowed to access this form.', 'booking-activities' )
						. '<br/>' . sprintf( esc_html__( 'These roles already have this privilege: %s.', 'booking-activities' ), '<code>' . implode( '</code>, <code>', array_intersect_key( bookacti_get_roles(), array_flip( $role_not_in ) ) ) . '</code>' )
						. '<br/>' . sprintf( esc_html__( 'If the selectbox is empty, it means that no other users have these capabilities: %s.', 'booking-activities' ), '<code>' . implode( '</code>, <code>', $capabilities ) . '</code>' )
						. ' ' . sprintf( esc_html__( 'If you want to grant a user these capabilities, use a plugin such as %1$s.', 'booking-activities' ), '<a href="https://wordpress.org/plugins/user-role-editor/" target="_blank" >User Role Editor</a>' )
		)
	);
	
	if( ! current_user_can( 'administrator' ) && ! is_super_admin() ) {
		unset( $fields[ 'author' ] );
	}
	
	bookacti_display_fields( $fields );
}


/**
 * Display 'publish' metabox content for forms
 * @since 1.5.0
 * @version 1.15.4
 * @param array $form_raw
 */
function bookacti_display_form_publish_meta_box( $form_raw ) {
?>
	<div class='submitbox' id='submitpost'>
		<div id='major-publishing-actions' data-popup='<?php echo ! $form_raw[ 'active' ] && $form_raw[ 'status' ] !== 'trash' ? 1 : 0; ?>' >
			<div id='delete-action'>
			<?php
				if ( current_user_can( 'bookacti_delete_forms' ) ) {
					if( ! $form_raw[ 'active' ] ) {
						echo '<a href="' . esc_url( wp_nonce_url( admin_url( 'admin.php?page=bookacti_forms&action=delete&form_id=' . $form_raw[ 'form_id' ] ), 'delete-form_' . $form_raw[ 'form_id' ] ) ) . '" class="submitdelete deletion" >'
								. esc_html__( 'Delete Permanently' )
							. '</a>';
					} else {
						echo '<a href="' . esc_url( wp_nonce_url( admin_url( 'admin.php?page=bookacti_forms&status=trash&action=trash&form_id=' . $form_raw[ 'form_id' ] ), 'trash-form_' . $form_raw[ 'form_id' ] ) ) . '" class="submitdelete deletion" >'
								. esc_html_x( 'Trash', 'verb' )
							. '</a>';
					}
				}
			?>
			</div>

			<div id='publishing-action'>
				<span class='spinner'></span>
				<input id='bookacti-save-form-button' 
					   name='save' 
					   type='submit' 
					   class='button button-primary button-large' 
					   id='publish' 
					   value='<?php echo $form_raw[ 'active' ] ? esc_attr__( 'Update' ) : ( $form_raw[ 'status' ] === 'auto-draft' ? esc_attr__( 'Publish' ) : esc_attr__( 'Restore' ) ); ?>' 
				/>
			</div>
			<div class='clear'></div>
		</div>
	</div>
<?php
}


/**
 * Display 'integration tuto' metabox content for forms
 * @since 1.5.0
 * @version 1.14.0
 * @param array $form_raw
 */
function bookacti_display_form_integration_tuto_meta_box( $form_raw ) {
	$shortcode = '[bookingactivities_form form="' . $form_raw[ 'form_id' ] . '"]';
?>
	<h4><?php _e( 'Integrate in a post, page, or text widget', 'booking-activities' ) ?></h4>
	<div>
		<p><em><label for='bookacti-form-shortcode'><?php esc_html_e( 'Copy this shortcode and paste it into your post, page, or text widget content:', 'booking-activities' ); ?></label></em></p>
		<p class='shortcode wp-ui-highlight'>
			<input type='text' id='bookacti-form-shortcode' onfocus='this.select();' readonly='readonly' class='large-text code' value='<?php echo esc_attr( $shortcode ); ?>' />
		</p>
	</div>
<?php
	do_action( 'bookacti_after_form_integration_tuto', $form_raw );
}


/**
 * Display the calendar field help content
 * @since 1.8.0
 * @return string
 */
function bookacti_display_calendar_field_help() {
	ob_start();
	esc_html_e( 'Click on the problem you are experiencing to try to fix it:', 'booking-activities' );
	?>
	<ul class='bookacti-help-list'>
		<li><a href='https://booking-activities.fr/en/faq/events-do-not-appear-on-the-calendar/' target='_blank'><?php esc_html_e( 'The events do not appear on the calendar', 'booking-activities' ); ?></a><br/>
		<li><a href='https://booking-activities.fr/en/faq/calendar-not-showing/' target='_blank'><?php esc_html_e( 'The calendar doesn\'t show up', 'booking-activities' ); ?></a><br/>
		<li><a href='https://booking-activities.fr/en/faq/booking-activities-doesnt-work-as-it-should/' target='_blank'><?php esc_html_e( 'Booking Activities doesn\'t work as it should', 'booking-activities' ); ?></a>
		<?php do_action( 'bookacti_calendar_field_help_after' ); ?>
	</ul>
	<?php esc_html_e( 'This documentation can help you too:', 'booking-activities' ); ?>
	<ul class='bookacti-help-list'>
		<li><a href='https://booking-activities.fr/en/documentation/faq/' target='_blank'><?php esc_html_e( 'See the FAQ', 'booking-activities' ); ?></a>
		<li><a href='https://booking-activities.fr/en/documentation/user-documentation/' target='_blank'><?php esc_html_e( 'See the documentation', 'booking-activities' ); ?></a>
		<?php do_action( 'bookacti_global_help_after' ); ?>
	</ul>
	<?php
	return ob_get_clean();
}




// PERMISSIONS

/**
 * Check if user is allowed to manage form
 * @since 1.5.0
 * @version 1.16.0
 * @param int $form_id
 * @param int $user_id
 * @return boolean
 */
function bookacti_user_can_manage_form( $form_id, $user_id = false ) {
	$user_can_manage_form = false;
	if( ! $user_id ) { $user_id = get_current_user_id(); }
	$bypass_form_managers_check = apply_filters( 'bookacti_bypass_form_managers_check', false, $user_id );
	if( is_super_admin( $user_id ) || $bypass_form_managers_check ) { $user_can_manage_form = true; }
	else {
		$admins = bookacti_get_form_managers( $form_id );
		if( $admins ) {
			if( in_array( $user_id, $admins, true ) ) { $user_can_manage_form = true; }
		}
	}

	return apply_filters( 'bookacti_user_can_manage_form', $user_can_manage_form, $form_id, $user_id );
}


/**
 * Get form managers
 * @since 1.5.0
 * @version 1.9.2
 * @param int|array $form_ids
 * @return array
 */
function bookacti_get_form_managers( $form_ids ) {
	$managers = bookacti_get_managers( 'form', $form_ids );
	
	$merged_managers = array();
	foreach( $managers as $user_ids ) {
		$merged_managers = array_merge( $merged_managers, bookacti_ids_to_array( $user_ids ) );
	}
	
	return array_unique( $merged_managers );
}


/**
 * Format form managers
 * @since 1.5.0
 * @version 1.16.0
 * @param array $form_managers
 * @return array
 */
function bookacti_format_form_managers( $form_managers = array() ) {
	$form_managers = bookacti_ids_to_array( $form_managers );
	
	// If user is not super admin, add the user automatically in the form managers list
	$user_id = get_current_user_id();
	$bypass_form_managers_check = apply_filters( 'bookacti_bypass_form_managers_check', false, $user_id );
	if( ! is_super_admin( $user_id ) && ! $bypass_form_managers_check ) {
		if( ! in_array( $user_id, $form_managers, true ) ) {
			$form_managers[] = $user_id;
		}
	}
	
	// Make sure all users have permission to manage forms
	$form_managers_caps = array( 'bookacti_edit_forms' );
	foreach( $form_managers as $i => $form_manager ) {
		if( $form_manager ) {
			$user_can = false;
			foreach( $form_managers_caps as $form_managers_cap ) {
				if( user_can( $form_manager, $form_managers_cap ) ) { $user_can = true; break; }
			}
			if( $user_can ) { continue; }
		}
		unset( $form_managers[ $i ] );
	}
	
	return apply_filters( 'bookacti_form_managers', $form_managers );
}