Source: my-calendar-default-fields.php

<?php
/**
 * Generate output for standard My Calendar submission fields.
 *
 * @category Submissions
 * @package  My Calendar Pro
 * @author   Joe Dolson
 * @license  GPLv2 or later
 * @link     https://www.joedolson.com/my-calendar-pro/
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Array of default fields for use in My Calendar submissions.
 *
 * @return array
 */
function mcs_default_fields() {
	// Can users change their names?
	$disallow_user_changes = apply_filters( 'mcs_disallow_user_changes', true );
	// Are image uploads allowed?
	$input_type = mcs_image_input_type();
	// Is there a maxlength on event descriptions?
	$maxlength         = apply_filters( 'mcs_event_description_maxlength', '' );
	$description_notes = '';
	if ( $maxlength ) {
		$description_attr = array(
			'data-maxlength' => absint( $maxlength ),
		);
		// Translators: maximum number of words allowed for this field.
		$description_notes = sprintf( __( '(maximum %s words)', 'my-calendar-pro' ), $maxlength );
	}
	// Custom name value on main description field.
	$description_attr['name'] = 'content';

	$short_description_attrs = array(
		'name' => 'event_short',
	);

	$event_link_attrs = array(
		'placeholder' => 'https://example.com',
	);

	$default_fields = array(
		'submitter'         => array(
			'field_label'       => __( 'Submitter', 'my-calendar-pro' ),
			'field_type'        => 'section',
			'section_contains'  => array(
				'mcs_name'  => array(
					'field_label'       => __( 'Your Name', 'my-calendar-pro' ),
					'field_type'        => ( $disallow_user_changes && is_user_logged_in() ) ? 'hidden' : 'text',
					'field_notes'       => '',
					'field_options'     => ( is_user_logged_in() ) ? wp_get_current_user()->display_name : '',
					'id'                => '',
					'field_required'    => true,
					'sanitize_callback' => 'sanitize_text_field',
					'escape_callback'   => 'esc_attr',
					'attrs'             => '',
					'unselectable'      => true, // Unselectable fields are not available to choose in widget setup.
				),
				'mcs_email' => array(
					'field_label'       => __( 'Your Email', 'my-calendar-pro' ),
					'field_type'        => ( $disallow_user_changes && is_user_logged_in() ) ? 'hidden' : 'email',
					'field_notes'       => '',
					'field_options'     => ( is_user_logged_in() ) ? wp_get_current_user()->user_email : '',
					'id'                => '',
					'field_required'    => true,
					'sanitize_callback' => 'sanitize_text_field',
					'escape_callback'   => 'esc_attr',
					'attrs'             => '',
					'unselectable'      => true,
				),
			),
			'field_notes'       => '',
			'field_options'     => '',
			'id'                => '',
			'field_required'    => '',
			'sanitize_callback' => 'sanitize_text_field',
			'escape_callback'   => 'esc_attr',
			'attrs'             => '',
			'unselectable'      => true,
		),
		'event_title'       => array(
			'field_label'       => __( 'Event Title', 'my-calendar-pro' ),
			'field_type'        => 'text',
			'field_notes'       => '',
			'field_options'     => '',
			'id'                => '',
			'field_required'    => true,
			'sanitize_callback' => 'sanitize_text_field',
			'escape_callback'   => 'esc_attr',
			'attrs'             => '',
			'unselectable'      => true,
		),
		'event_date'        => array(
			'field_label'       => __( 'Date', 'my-calendar-pro' ),
			'field_type'        => 'date',
			'section_contains'  => array(
				'event_begin' => array(
					'field_label'       => __( 'Event Date', 'my-calendar-pro' ),
					'field_type'        => 'date',
					'field_notes'       => '',
					'field_options'     => '',
					'id'                => '',
					'field_required'    => true,
					'sanitize_callback' => 'sanitize_text_field',
					'escape_callback'   => 'esc_attr',
					'attrs'             => '',
					'unselectable'      => true,
				),
				'event_time'  => array(
					'field_label'       => __( 'Time', 'my-calendar-pro' ),
					'field_type'        => 'time',
					'field_notes'       => '',
					'field_options'     => '',
					'id'                => '',
					'field_required'    => '',
					'sanitize_callback' => 'sanitize_text_field',
					'escape_callback'   => 'esc_attr',
					'attrs'             => '',
					'unselectable'      => true,
				),
			),
			'field_notes'       => '',
			'field_options'     => '',
			'id'                => '',
			'field_required'    => true,
			'sanitize_callback' => 'sanitize_text_field',
			'escape_callback'   => 'esc_attr',
			'attrs'             => '',
			'unselectable'      => true,
		),
		'event_allday'      => array(
			'field_label'       => __( 'All day event', 'my-calendar-pro' ),
			'field_type'        => 'checkbox',
			'field_notes'       => '',
			'field_options'     => 'true',
			'id'                => '',
			'field_required'    => '',
			'sanitize_callback' => 'sanitize_text_field',
			'escape_callback'   => 'esc_attr',
			'attrs'             => '',
		),
		'event_hide_end'    => array(
			'field_label'       => __( 'Hide end time', 'my-calendar-pro' ),
			'field_type'        => 'checkbox',
			'field_notes'       => '',
			'field_options'     => 'true',
			'id'                => '',
			'field_required'    => '',
			'sanitize_callback' => 'sanitize_text_field',
			'escape_callback'   => 'esc_attr',
			'attrs'             => '',
		),
		'event_end'         => array(
			'field_label'       => __( 'End date', 'my-calendar-pro' ),
			'field_type'        => 'date',
			'field_notes'       => '',
			'field_options'     => '',
			'id'                => '',
			'field_required'    => '',
			'sanitize_callback' => 'sanitize_text_field',
			'escape_callback'   => 'esc_attr',
			'attrs'             => '',
		),
		'event_endtime'     => array(
			'field_label'       => __( 'End time', 'my-calendar-pro' ),
			'field_type'        => 'time',
			'field_notes'       => '',
			'field_options'     => '',
			'id'                => '',
			'field_required'    => '',
			'sanitize_callback' => 'sanitize_text_field',
			'escape_callback'   => 'esc_attr',
			'attrs'             => '',
		),
		'description'       => array(
			'field_label'       => __( 'Description', 'my-calendar-pro' ),
			'field_type'        => 'textarea',
			'field_notes'       => $description_notes,
			'field_options'     => '',
			'id'                => '',
			'field_required'    => '',
			'sanitize_callback' => 'wp_kses_post',
			'escape_callback'   => 'esc_attr',
			'attrs'             => $description_attr,
		),
		'short_description' => array(
			'field_label'       => __( 'Excerpt', 'my-calendar-pro' ),
			'field_type'        => 'textarea',
			'field_notes'       => '',
			'field_options'     => '',
			'id'                => '',
			'field_required'    => '',
			'sanitize_callback' => 'sanitize_textarea_field',
			'escape_callback'   => 'esc_textarea',
			'attrs'             => $short_description_attrs,
		),
		'event_image'       => array(
			'field_label'       => ( 'file' === $input_type ) ? __( 'Image', 'my-calendar-pro' ) : __( 'Image (URL)', 'my-calendar-pro' ),
			'field_type'        => $input_type,
			'field_notes'       => '',
			'field_options'     => '',
			'id'                => '',
			'field_required'    => '',
			'sanitize_callback' => 'sanitize_textarea_field',
			'escape_callback'   => 'esc_textarea',
			'attrs'             => '',
		),
		'event_host'        => array(
			'field_label'       => __( 'Event Host', 'my-calendar-pro' ),
			'field_type'        => ( count_users() <= 1 ) ? 'hidden' : 'select',
			'field_notes'       => '',
			'field_options'     => mc_selected_users( '', 'hosts', 'array' ),
			'id'                => '',
			'field_required'    => '',
			'validate_callback' => 'mc_validate_host',
			'sanitize_callback' => 'sanitize_text_field',
			'escape_callback'   => ( count_users() <= 1 ) ? 'esc_attr' : 'mc_kses_callback',
			'attrs'             => '',
		),
		'access'            => array(
			'field_label'       => __( 'Event Access', 'my-calendar-pro' ),
			'field_type'        => 'checkbox-multiple',
			'field_notes'       => '',
			'field_options'     => '',
			'id'                => '',
			'field_required'    => '',
			'sanitize_callback' => 'sanitize_text_field',
			'escape_callback'   => 'esc_attr',
			'attrs'             => '',
		),
		'event_link'        => array(
			'field_label'       => __( 'Link', 'my-calendar-pro' ),
			'field_type'        => 'url',
			'field_notes'       => '',
			'field_options'     => '',
			'id'                => '',
			'field_required'    => '',
			'sanitize_callback' => 'sanitize_text_field',
			'escape_callback'   => 'esc_attr',
			'attrs'             => $event_link_attrs,
		),
		'registration'      => array(
			'field_label'      => __( 'Ticketing Information', 'my-calendar-pro' ),
			'field_type'       => 'section',
			'section_contains' => array(
				'event_tickets'      => array(
					'field_label'       => __( 'Tickets URL', 'my-calendar-pro' ),
					'field_type'        => 'url',
					'field_notes'       => '',
					'field_options'     => '',
					'id'                => '',
					'field_required'    => '',
					'sanitize_callback' => 'sanitize_text_field',
					'escape_callback'   => 'esc_url',
					'unselectable'      => true,
				),
				'event_registration' => array(
					'field_label'       => __( 'Registration Information', 'my-calendar-pro' ),
					'field_type'        => 'textarea',
					'field_notes'       => '',
					'field_options'     => '',
					'id'                => '',
					'field_required'    => '',
					'sanitize_callback' => 'sanitize_textarea_field',
					'escape_callback'   => 'mc_kses_post',
					'attrs'             => array(
						'cols' => 40,
						'rows' => 4,
					),
					'unselectable'      => true,
				),
			),
		),
		'event_recurring'   => array(
			'field_label'       => __( 'Recurring Events', 'my-calendar-pro' ),
			'field_type'        => 'section',
			'section_contains'  => array(
				'event_every'   => array(
					'field_label'       => __( 'Frequency', 'my-calendar-pro' ),
					'field_type'        => 'number',
					'field_notes'       => '',
					'field_options'     => '',
					'id'                => '',
					'field_required'    => '',
					'sanitize_callback' => 'sanitize_text_field',
					'escape_callback'   => 'esc_attr',
					'attrs'             => array(
						'size'      => '4',
						'min'       => '1',
						'max'       => '99',
						'maxlength' => '2',
					),
					'unselectable'      => true,
				),
				'event_recur'   => array(
					'field_label'       => __( 'Period', 'my-calendar-pro' ),
					'field_type'        => 'select',
					'field_notes'       => '',
					'field_options'     => mc_recur_options( '', 'array' ),
					'id'                => '',
					'field_required'    => '',
					'sanitize_callback' => 'sanitize_text_field',
					'escape_callback'   => 'mc_kses_post',
					'attrs'             => '',
					'unselectable'      => true,
				),
				'event_repeats' => array(
					'field_label'       => __( 'Repeat Until', 'my-calendar-pro' ),
					'field_type'        => 'date',
					'field_notes'       => '',
					'field_options'     => '',
					'id'                => '',
					'field_required'    => '',
					'sanitize_callback' => 'sanitize_text_field',
					'escape_callback'   => 'esc_attr',
					'attrs'             => '',
					'unselectable'      => true,
				),
			),
			'field_notes'       => '',
			'field_options'     => '',
			'id'                => '',
			'field_required'    => '',
			'sanitize_callback' => 'sanitize_text_field',
			'escape_callback'   => 'esc_attr',
			'attrs'             => '',
		),
		'custom_fields'     => '', // Container for all custom fields, if legacy in use.
	);

	/**
	 * Filter the default fields available.
	 *
	 * @hook mcs_default_fields
	 *
	 * @param {array} $default_fields Array of fields used as defaults.
	 *
	 * @return {array}
	 */
	return apply_filters( 'mcs_default_fields', $default_fields );
}

/**
 * Validate host select field. Checks whether the supplied data is a valid user ID, and returns false if not.
 *
 * @param string|int $value Value supplied by user.
 * @param array      $schema Field schema used.
 *
 * @return bool
 */
function mc_validate_host( $value, $schema ) {
	if ( true === (bool) $schema['field_required'] ) {
		if ( ! get_userdata( $value ) ) {
			return false;
		}
	}

	return true;
}

/**
 * Get default location fields and merge with default fields.
 *
 * @param string $key array key.
 * @param string $value Array value.
 *
 * @return Needed value.
 */
function mcs_get_field_name( $key, $value ) {
	$values = array_merge( mcs_default_fields(), mcs_default_location_fields() );

	return ( in_array( $key, array_keys( $values ), true ) ) ? $values[ $key ] : $value;
}

/**
 * Default location field label.
 *
 * @return array
 */
function mcs_default_location_fields() {
	return array(
		'event_label' => array(
			'field_label' => __( 'Location Name', 'my-calendar-pro' ),
			'required'    => false,
		),
		'street'      => array(
			'field_label' => __( 'Street Address', 'my-calendar-pro' ),
			'required'    => false,
		),
		'street2'     => array(
			'field_label' => __( 'Street Address (2)', 'my-calendar-pro' ),
			'required'    => false,
		),
		'phone'       => array(
			'field_label' => __( 'Phone', 'my-calendar-pro' ),
			'required'    => false,
		),
		'city'        => array(
			'field_label' => __( 'City', 'my-calendar-pro' ),
			'required'    => false,
		),
		'state'       => array(
			'field_label' => __( 'State/Province', 'my-calendar-pro' ),
			'required'    => false,
		),
		'postcode'    => array(
			'field_label' => __( 'Postal Code', 'my-calendar-pro' ),
			'required'    => false,
		),
		'region'      => array(
			'field_label' => __( 'Region', 'my-calendar-pro' ),
			'required'    => false,
		),
		'country'     => array(
			'field_label' => __( 'Country', 'my-calendar-pro' ),
			'required'    => false,
		),
		'url'         => array(
			'field_label' => __( 'Location URL', 'my-calendar-pro' ),
			'required'    => false,
		),
		'gps'         => array(
			'field_label' => __( 'GPS Coordinates', 'my-calendar-pro' ),
			'required'    => false,
		),
	);
}

/**
 * Create a set of options for a select from an array and a selected value.
 *
 * @param int|string $selected Selected value.
 * @param array      $options Array of possible values.
 *
 * @return string
 */
function mcs_create_select_options( $selected, $options ) {
	$output = '';
	if ( ! is_array( $options ) ) {
		return '';
	}
	foreach ( $options as $option ) {
		if ( is_array( $option ) ) {
			$value = $option['value'];
			$label = $option['label'];
		} else {
			$value = $option;
			$label = $option;
		}
		$output .= '<option value="' . esc_attr( $value ) . '"' . selected( $selected, $value, false ) . '>' . esc_html( $label ) . '</option>';
	}

	return $output;
}

/**
 * Get data from an event object in the editing form.
 *
 * @param string $field Field ID.
 * @param object $data Event data.
 *
 * @return mixed
 */
function mcs_get_data( $field, $data ) {
	if ( ! is_object( $data ) ) {
		return '';
	}
	if ( property_exists( $data, $field ) && 'event_recur' !== $field ) {
		$value = map_deep( $data->{$field}, 'stripslashes' );
	} else {
		$map = array(
			'event_allday'      => array( 'mc_is_all_day' ),
			'mcs_name'          => array( 'mcs_get_name' ),
			'mcs_email'         => array( 'mcs_get_email' ),
			'event_every'       => array( 'mcs_get_event_every' ),
			'event_recur'       => array( 'mcs_get_event_recur' ),
			'description'       => 'event_desc',
			'short_description' => 'event_short',
		);
		// Handle the fact the submissions field names don't always match main file names.
		$key = ( isset( $map[ $field ] ) ) ? $map[ $field ] : false;
		if ( ! $key ) {
			return '';
		} else {
			if ( is_string( $key ) ) {
				$value = ( property_exists( $data, $key ) ) ? stripslashes( $data->{$key} ) : '';
			} else {
				$value = ( function_exists( $key[0] ) ) ? call_user_func( $key[0], $data ) : '';
			}
		}
	}

	return $value;
}

/**
 * Get event every value.
 *
 * @param object $event Event object.
 *
 * @return string
 */
function mcs_get_event_every( $event ) {
	$recur = $event->event_recur;
	$data  = str_split( $recur, 1 );
	$every = $data[1];

	return $every;
}

/**
 * Get event recur value.
 *
 * @param object $event Event object.
 *
 * @return string
 */
function mcs_get_event_recur( $event ) {
	$recur  = $event->event_recur;
	$data   = str_split( $recur, 1 );
	$recurs = $data[0];

	return $recurs;
}

/**
 * Get submitter name for event.
 *
 * @param object $event Event data.
 *
 * @return string
 */
function mcs_get_name( $event ) {
	$name    = '';
	$post_id = $event->event_post;
	$author  = get_post_meta( $post_id, '_submitter_details', true );
	if ( is_array( $author ) && ! empty( $author ) ) {
		$name = $author['first_name'] . ' ' . $author['last_name'];
	}

	return $name;
}

/**
 * Get submitter email for event.
 *
 * @param object $event Event data.
 *
 * @return string
 */
function mcs_get_email( $event ) {
	$email   = '';
	$post_id = $event->event_post;
	$author  = get_post_meta( $post_id, '_submitter_details', true );
	if ( is_array( $author ) && ! empty( $author ) ) {
		$email = $author['email'];
	}

	return $email;
}

/**
 * Create My Calendar input field for submissions.
 *
 * @param string $field Field reference.
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 * @param array  $attrs Array of attributes passed from source function.
 *
 * @return string
 */
function mcs_basic_input( $field, $data, $fields, $attrs = array() ) {
	$defaults = mcs_default_fields();
	$label    = ( isset( $fields[ $field ] ) && 'true' !== $fields[ $field ] ) ? $fields[ $field ] : $defaults[ $field ]['field_label'];
	$value    = mcs_get_data( $field, $data );
	if ( 'mcs_name' === $field ) {
		$value = ( is_user_logged_in() ) ? wp_get_current_user()->user_email : $value;
	}
	if ( 'mcs_name' === $field ) {
		$value = ( is_user_logged_in() ) ? wp_get_current_user()->display_name : $value;
	}
	$schema          = $defaults[ $field ];
	$type            = $schema['field_type'];
	$values          = $schema['field_options'];
	$classes         = array( 'mcs-' . sanitize_html_class( $field ), 'mcs-' . sanitize_html_class( $type ), 'mcs-input' );
	$schema_attrs    = ( is_array( $schema['attrs'] ) ) ? $schema['attrs'] : array();
	$attrs           = array_merge( $attrs, $schema_attrs );
	$required_fields = apply_filters( 'mcs_required_fields', get_option( 'mcs_required_fields', array() ) );
	$required        = ( in_array( $field, $required_fields, true ) || true === (bool) $schema['field_required'] );
	$valid_value     = isset( $schema['validate_callback'] ) && function_exists( $schema['validate_callback'] ) ? call_user_func( $schema['validate_callback'], $value, $schema ) : true;
	$required_text   = '';
	if ( ! empty( $data ) && $required && ( ! $value || ! $valid_value ) ) {
		$classes[] = 'invalid';
	}
	if ( $required ) {
		$attrs['required'] = 'required';
		$required_text     = mcs_required_text();
	}
	$attributes = '';
	foreach ( $attrs as $attr => $val ) {
		$attributes .= ' ' . esc_attr( $attr ) . '="' . esc_attr( $val ) . '"';
	}
	$name = $field;
	// If field attributes pass a custom name, use that.
	if ( isset( $attrs['name'] ) ) {
		$name = $attrs['name'];
		unset( $attrs['name'] );
	}
	if ( 'checkbox' === $type || 'radio' === $type ) {
		$structure = '<p class="%1$s"><input type="%7$s" value="1" id="mcs_%4$s" ' . checked( $value, '1', false ) . ' name="%8$s" %4$s /> <label for="mcs_%4$s">%2$s</label>
	</p>';
	} elseif ( 'textarea' === $type ) {
		$structure = '<p class="%1$s"><label for="mcs_%4$s">%2$s %3$s</label> <textarea name="%8$s" id="mcs_%4$s" %6$s>%5$s</textarea></p>';
	} elseif ( 'date' === $type ) {
		$start_of_week = absint( get_option( 'start_of_week' ) );
		$firstday      = ( 1 === $start_of_week || 0 === $start_of_week ) ? $start_of_week : 0;
		$attributes   .= 'first-day-of-week="' . $firstday . '"';
		$structure     = '<p class="%1$s"><label for="mcs_%4$s">%2$s %3$s</label> <duet-date-picker name="%8$s[]" identifier="mcs_%4$s" value="%5$s" %6$s></duet-date-picker></p>';
	} elseif ( 'select' === $type ) {
		// translators: Label for this field.
		$value     = '<option value="">' . sprintf( __( 'Select %s', 'my-calendar-pro' ), $label ) . '</option>';
		$value    .= mcs_create_select_options( $value, $values );
		$structure = '<p class="%1$s"><label for="mcs_%4$s">%2$s %3$s</label> <select type="%7$s" name="%8$s" id="mcs_%4$s" %6$s>%5$s</select></p>';
	} elseif ( 'hidden' === $type ) {
		$structure = '<input type="%7$s" name="%4$s" value="%5$s" />';
	} else {
		$is_array = '';
		if ( 'time' === $type ) {
			$is_array = '[]';
		}
		$structure = '<p class="%1$s"><label for="mcs_%4$s">%2$s %3$s</label> <input type="%7$s" name="%8$s' . $is_array . '" id="mcs_%4$s" value="%5$s" %6$s /></p>';
	}
	$escape_callback = ( isset( $schema['escape_callback'] ) && function_exists( $schema['escape_callback'] ) ) ? $schema['escape_callback'] : ( ( 'textarea' === $type ) ? 'esc_textarea' : 'esc_attr' );

	return sprintf(
		$structure,
		implode( ' ', array_map( 'sanitize_html_class', $classes ) ),
		$label,
		$required_text,
		$field,
		call_user_func( $escape_callback, $value ),
		$attributes,
		$type,
		$name
	);
}


/**
 * Create My Calendar input field inside groups for submissions.
 *
 * @param string $field Field reference.
 * @param string $parent_field Parent field.
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 * @param array  $attrs Array of attributes passed from source function.
 *
 * @return string
 */
function mcs_grouped_input( $field, $parent_field, $data, $fields, $attrs = array() ) {
	$defaults = mcs_default_fields();
	$label    = ( isset( $fields[ $parent_field ][ $field ] ) && ( 'true' !== $fields[ $parent_field ][ $field ] && ! empty( $fields[ $parent_field ][ $field ] ) ) ) ? $fields[ $parent_field ][ $field ] : $defaults[ $parent_field ]['section_contains'][ $field ]['field_label'];
	$value    = mcs_get_data( $field, $data );
	if ( 'mcs_email' === $field ) {
		$value = ( is_user_logged_in() ) ? wp_get_current_user()->user_email : $value;
	}
	if ( 'mcs_name' === $field ) {
		$value = ( is_user_logged_in() ) ? wp_get_current_user()->display_name : $value;
	}

	$schema          = $defaults[ $parent_field ]['section_contains'][ $field ];
	$type            = $schema['field_type'];
	$values          = $schema['field_options'];
	$classes         = array( 'mcs-' . sanitize_html_class( $field ), 'mcs-' . sanitize_html_class( $type ), 'mcs-input' );
	$schema_attrs    = ( isset( $schema['attrs'] ) && is_array( $schema['attrs'] ) ) ? $schema['attrs'] : array();
	$attrs           = array_merge( $attrs, $schema_attrs );
	$required_fields = apply_filters( 'mcs_required_fields', get_option( 'mcs_required_fields', array() ) );
	$required        = ( in_array( $field, $required_fields, true ) || true === (bool) $schema['field_required'] );
	$required_text   = '';
	if ( ! empty( $data ) && $required && ! $value ) {
		$classes[] = 'invalid';
	}
	if ( $required ) {
		$attrs['required'] = 'required';
		$required_text     = mcs_required_text();
	}
	$attributes = '';
	foreach ( $attrs as $attr => $val ) {
		$attributes .= ' ' . esc_attr( $attr ) . '="' . esc_attr( $val ) . '"';
	}
	$name = $field;
	// If field attributes pass a custom name, use that.
	if ( isset( $attrs['name'] ) ) {
		$name = $attrs['name'];
	}
	if ( 'checkbox' === $type || 'radio' === $type ) {
		$structure = '<p class="%1$s"><input type="%7$s" value="1" id="mcs_%4$s" name="%8$s" %4$s /> <label for="mcs_%4$s">%2$s</label>
	</p>';
	} elseif ( 'textarea' === $type ) {
		$structure = '<p class="%1$s"><label for="mcs_%4$s">%2$s %3$s</label> <textarea name="%8$s" id="mcs_%4$s" %6$s>%5$s</textarea></p>';
	} elseif ( 'date' === $type ) {
		$is_array      = ( 'event_repeats' !== $field ) ? '[]' : '';
		$start_of_week = absint( get_option( 'start_of_week' ) );
		$firstday      = ( 1 === $start_of_week || 0 === $start_of_week ) ? $start_of_week : 0;
		$attributes   .= 'first-day-of-week="' . $firstday . '"';
		$structure     = '<p class="%1$s"><label for="mcs_%4$s">%2$s %3$s</label> <duet-date-picker name="%8$s' . $is_array . '" identifier="mcs_%4$s" value="%5$s" %6$s></duet-date-picker></p>';
	} elseif ( 'select' === $type ) {
		$value     = mcs_create_select_options( $value, $values );
		$structure = '<p class="%1$s"><label for="mcs_%4$s">%2$s %3$s</label> <select type="%7$s" name="%8$s" id="mcs_%4$s" %6$s>%5$s</select></p>';
	} elseif ( 'hidden' === $type ) {
		$structure = '<input type="%7$s" name="%4$s" value="%5$s" />';
	} else {
		$is_array = '';
		if ( 'time' === $type ) {
			$is_array = '[]';
		}
		$structure = '<p class="%1$s"><label for="mcs_%4$s">%2$s %3$s</label> <input type="%7$s" name="%8$s' . $is_array . '" id="mcs_%4$s" value="%5$s" %6$s /></p>';
	}
	$escape_callback = ( isset( $schema['escape_callback'] ) && function_exists( $schema['escape_callback'] ) ) ? $schema['escape_callback'] : ( ( 'textarea' === $type ) ? 'esc_textarea' : 'esc_attr' );

	if ( 'select' !== $type ) {
		$value = call_user_func( $escape_callback, $value );
	}

	return sprintf(
		$structure,
		implode( ' ', array_map( 'sanitize_html_class', $classes ) ),
		$label,
		$required_text,
		$field,
		$value,
		$attributes,
		$type,
		$name
	);
}

/**
 * Create My Calendar title field for submissions.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_title_input( $data, $fields ) {
	return mcs_basic_input( 'event_title', $data, $fields );
}

/**
 * Create My Calendar all day event field for submissions.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_allday_input( $data, $fields ) {
	return mcs_basic_input( 'event_allday', $data, $fields );
}

/**
 * Create My Calendar hide end dat event field for submissions.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_hide_end_input( $data, $fields ) {
	return mcs_basic_input( 'event_hide_end', $data, $fields );
}

/**
 * Create My Calendar event date & time fields for submissions.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_event_date_input( $data, $fields ) {
	// Set up min/max range if enabled.
	$max    = apply_filters( 'mcs_time_max', '00:00' );
	$min    = apply_filters( 'mcs_time_min', '00:00' );
	$attrs  = array();
	$append = '';
	$range  = '';
	if ( '00:00' !== $max || '00:00' !== $min ) {
		// Translators: starting time, ending time.
		$range  = '<p id="mc_time_range_allowed">' . sprintf( __( 'Times must be between %1$s and %2$s', 'my-calendar-pro' ), mc_date( get_option( 'mc_time_format' ), strtotime( $min ) ), mc_date( get_option( 'mc_time_format' ), strtotime( $max ) ) ) . '</p>';
		$append = '<span class="validity"><span class="dashicons dashicons-no" aria-hidden="true"></span>' . __( 'Invalid time', 'my-calendar-pro' ) . '</span>';
	}
	// Add secondary fields.
	$allday_field   = '';
	$hide_end_field = '';
	if ( isset( $fields['event_allday'] ) ) {
		$allday_field = mcs_allday_input( $data, $fields );
	}
	if ( isset( $fields['event_hide_end'] ) ) {
		$hide_end_field = mcs_hide_end_input( $data, $fields );
	}
	$extras = ( '' !== $allday_field . $hide_end_field ) ? '<div class="mc_date_extras">' . $allday_field . $hide_end_field . '</div>' : '';

	if ( '00:00' !== $min || '00:00' !== $max ) {
		$attrs['min']              = $min;
		$attrs['max']              = $max;
		$attrs['aria-describedby'] = 'mc_time_range_allowed';
	}

	$date_input = mcs_date_input( $data, $fields );
	$time_input = mcs_time_input( $data, $fields, $attrs );

	$output = "
	<div class='mc_begin_container mcs-date-time-container'>
		$range
		$date_input
		$time_input
		$append
	</div>
	$extras";

	return $output;
}



/**
 * Create My Calendar event end date & time fields for submissions.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_end_date_input( $data, $fields ) {
	$output     = '';
	$date_input = ( isset( $fields['end_date'] ) || isset( $fields['event_end'] ) ) ? mcs_event_end_date_input( $data, $fields ) : '';
	$time_input = ( isset( $fields['end_time'] ) || isset( $fields['event_end'] ) || isset( $fields['event_endtime'] ) ) ? mcs_end_time_input( $data, $fields ) : '';

	if ( $date_input || $time_input ) {
		$output = "
		<div class='mc_end_container mcs-date-time-container'>
			$date_input
			$time_input
		</div>";
	}

	return $output;
}

/**
 * Build begin date input field.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_date_input( $data, $fields ) {
	return mcs_grouped_input( 'event_begin', 'event_date', $data, $fields );
}


/**
 * Build begin time input field.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_time_input( $data, $fields ) {
	return mcs_grouped_input( 'event_time', 'event_date', $data, $fields );
}

/**
 * Build end date input field.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_event_end_date_input( $data, $fields ) {
	return mcs_basic_input( 'event_end', $data, $fields );
}

/**
 * Build end time input field.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_end_time_input( $data, $fields ) {
	return mcs_basic_input( 'event_endtime', $data, $fields );
}

/**
 * Name & email input fields.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_name_and_email_input( $data, $fields ) {
	$name_input  = mcs_name_input( $data, $fields );
	$email_input = mcs_email_input( $data, $fields );
	if ( is_user_logged_in() ) {
		$before = '';
		$after  = '';
	} else {
		$before = '<div class="mcs-name-and-email">';
		$after  = '</div>';
	}
	return $before . $name_input . $email_input . $after;
}

/**
 * Name input fields.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_name_input( $data, $fields ) {
	return mcs_grouped_input( 'mcs_name', 'submitter', $data, $fields );
}

/**
 * Email input fields.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_email_input( $data, $fields ) {
	return mcs_grouped_input( 'mcs_email', 'submitter', $data, $fields );
}

/**
 * Event host input field.
 *
 * @param object $data Event data.
 * @param array  $fields Field list passed from shortcode.
 *
 * @return string
 */
function mcs_event_host_input( $data, $fields ) {
	return mcs_basic_input( 'event_host', $data, $fields );
}

/**
 * Event recurring input fields.
 *
 * @param object $data Event data.
 * @param array  $fields Fields passed from shortcode.
 *
 * @return string
 */
function mcs_event_recurring_input( $data, $fields ) {
	if ( ! isset( $fields['event_recurring'] ) ) {
		return "<div>
			<input type='hidden' name='event_repeats' value='0' />
			<input type='hidden' name='event_recur' value='S' />
			<input type='hidden' name='event_every' value='1' />
		</div>";
	}
	$every   = mcs_grouped_input( 'event_every', 'event_recurring', $data, $fields );
	$recur   = mcs_grouped_input( 'event_recur', 'event_recurring', $data, $fields );
	$repeats = mcs_grouped_input( 'event_repeats', 'event_recurring', $data, $fields );

	return '<fieldset class="recurring"><legend class="screen-reader-text">' . __( 'Recurring Events', 'my-calendar-pro' ) . '</legend><div class="columns">' . $every . $recur . $repeats . '</div></fieldset>';
}

/**
 * Event registration input fields.
 *
 * @param object $data Event data.
 * @param array  $fields Fields passed from shortcode.
 *
 * @return string
 */
function mcs_event_registration_input( $data, $fields ) {
	$tickets      = mcs_grouped_input( 'event_tickets', 'registration', $data, $fields );
	$registration = mcs_grouped_input( 'event_registration', 'registration', $data, $fields );
	$has_data     = ( ! empty( $data ) ) ? true : false;
	/**
	 * Append HTML to end of event registration fields.
	 *
	 * @hook mc_event_registration
	 *
	 * @param {string} $html HTML to place inside registration fieldset.
	 * @param {bool}   $has_data Whether current event object is populated.
	 * @param {object} $data Event object.
	 * @param {string} $public Rendering in public context.
	 *
	 * @return {string}
	 */
	$registration .= apply_filters( 'mc_event_registration', '', $has_data, $data, 'public' );

	return '<fieldset class="registration"><legend class="screen-reader-text">' . __( 'Ticketing Information', 'my-calendar-pro' ) . '</legend><div class="rows">' . $tickets . $registration . '</div></fieldset>';
}

/**
 * Event description input field.
 *
 * @param object $data Event data.
 * @param array  $fields Fields passed from shortcode.
 *
 * @return string
 */
function mcs_description_input( $data, $fields ) {
	return mcs_basic_input( 'description', $data, $fields );
}

/**
 * Event short description input field.
 *
 * @param object $data Event data.
 * @param array  $fields Fields passed from shortcode.
 *
 * @return string
 */
function mcs_short_description_input( $data, $fields ) {
	return mcs_basic_input( 'short_description', $data, $fields );
}

/**
 * Event link input field.
 *
 * @param object $data Event data.
 * @param array  $fields Fields passed from shortcode.
 *
 * @return string
 */
function mcs_event_link_input( $data, $fields ) {
	return mcs_basic_input( 'event_link', $data, $fields );
}

/**
 * Event image input field.
 *
 * @param object $data Event data.
 * @param array  $fields Fields passed from shortcode.
 *
 * @return string
 */
function mcs_event_image_input( $data, $fields ) {
	$required_fields = apply_filters( 'mcs_required_fields', get_option( 'mcs_required_fields', array() ) );
	$image_url       = ( ! empty( $data ) ) ? esc_attr( $data->event_image ) : '';
	$image_id        = ( ! empty( $data ) ) ? get_post_thumbnail_id( $data->event_post ) : '';
	$image           = ( $image_id ) ? $image_id : $image_url;
	$alt             = ( isset( $_POST['event_image_alt'] ) ) ? esc_attr( sanitize_text_field( $_POST['event_image_alt'] ) ) : '';
	$alt             = ( $image_id && is_int( $image_id ) ) ? get_post_meta( $image_id, '_wp_attachment_image_alt', true ) : $alt;
	if ( ! empty( $data ) ) {
		$alt = ( '' === $alt ) ? get_post_meta( $data->event_post, '_mcs_submitted_alt', true ) : $alt;
	}
	$defaults = mcs_default_fields();
	if ( isset( $fields['event_image'] ) || in_array( 'event_image', $required_fields, true ) ) {
		$required      = ( in_array( 'event_image', $required_fields, true ) ) ? ' required="required"' : '';
		$invalid_class = '';
		if ( ! empty( $data ) && $required && ! $image ) {
			$invalid_class = ' class="invalid"';
		}
		$input_type = mcs_image_input_type( $image );
		if ( isset( $fields['event_image'] ) ) {
			$flabel = ( 'true' !== $fields['event_image'] ) ? $fields['event_image'] : $defaults['event_image']['field_label'];
		} else {
			$flabel = $defaults['event_image']['field_label'];
		}
		$flabel       .= ( '' !== $required ) ? ' ' . mcs_required_text() : '';
		$progress      = ( 'file' === $input_type && 'true' === get_option( 'mcs_ajax', 'true' ) ) ? '<div class="mcs-upload-process"><button type="button" class="mcs-file-upload-submit mcs-hidden">' . __( 'Upload', 'my-calendar-pro' ) . '</button><div class="mcs-upload-progress" role="alert"></div></div><input type="hidden" name="event_image_id" value="' . esc_attr( $image_id ) . '">' : '';
		$current_image = ( $image_url ) ? '<img src="' . esc_url( $image_url ) . '" alt="' . esc_attr( $alt ) . '" />' : '';
		$class         = ( $image_url ) ? ' has-image' : '';
		$current_image = '<p class="mcs-selected-image' . $class . '">' . $current_image . '</p>';

		$link = 'https://www.w3.org/WAI/tutorials/images/decision-tree';
		/**
		 * Filter the link for users to learn about alt text.
		 *
		 * @hook mc_event_image_alt_link
		 *
		 * @param {string} $link Default link to the W3C's alt text decision tree.
		 *
		 * @return {string}
		 */
		$event_image_alt_link = apply_filters( 'mc_event_image_alt_link', $link );
		$event_image_alt_link = ( ! $event_image_alt_link ) ? $link : $event_image_alt_link;
		$text                 = __( 'Learn how to describe the purpose of the image.', 'my-calendar-pro' );
		/**
		 * Filter the link text telling users how to learn about alt text.
		 *
		 * @hook mc_event_image_alt_info_text
		 *
		 * @param {string} $text Default visible text.
		 *
		 * @return {string}
		 */
		$event_image_alt_text = apply_filters( 'mc_event_image_alt_info_text', $text );
		$event_image_alt_text = ( ! $event_image_alt_text ) ? $text : $event_image_alt_text;

		return "
		<div class='mc-image-fields'>
			<p$invalid_class>
				<label for='mc_event_image'>$flabel</label>
				<span>
				<input type='$input_type' name='event_image' id='mc_event_image' value='$image' $required />
				<button type='button' id='mc_event_image_clear'>" . __( 'Clear', 'my-calendar-pro' ) . "</button>
				</span>
			</p>
			<p class='mc_event_image_alt'>
				<label for='event_image_alt'>" . __( 'Alternative text', 'my-calendar-pro' ) . ' ' . mcs_required_text() . "</label>
				<input type='text' name='event_image_alt' id='event_image_alt' aria-labelledby='event_image_alt_info' value='$alt' required />
				<span id='event_image_alt_info'><a href='" . esc_url( $event_image_alt_link ) . "' class='wpa-allow-target' target='_blank' rel='noopener'>" . $event_image_alt_text . " (new tab <i class='dashicons dashicons-external' aria-hidden='true'></i>)</a></span>" . '
			</p>' . $progress . $current_image . '
		</div>';
	}

	return '';
}