Source: mt-add-to-cart.php

<?php
/**
 * Add to Cart form.
 *
 * @category Core
 * @package  My Tickets
 * @author   Joe Dolson
 * @license  GPLv2 or later
 * @link     https://www.joedolson.com/my-tickets/
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
} // Exit if accessed directly.

/**
 * Appends a registration form to post content for posts with defined event data.
 *
 * @uses function mt_add_to_cart_form();
 * @param string $content Post Content.
 *
 * @return string
 */
function mt_add_to_cart_form_post( $content ) {
	$options = mt_get_settings();
	global $post;
	$only_singular = $options['mt_singular'];
	if ( $only_singular && ! is_singular( $options['mt_post_types'] ) ) {
		return $content;
	}
	if ( in_array( get_post_type( $post ), $options['mt_post_types'], true ) ) {
		$event = $post->ID;
		if ( get_post_meta( $event, '_mc_event_data', true ) ) {
			$content = mt_add_to_cart_form( $content, $event );
		}
	}

	return $content;
}
add_filter( 'the_content', 'mt_add_to_cart_form_post', 50, 1 ); // after wpautop.

/**
 * Test whether a price set for an event has any tickets available for purchase.
 *
 * @param array $pricing Events pricing array.
 *
 * @return bool
 */
function mt_has_tickets( $pricing ) {
	$return = false;
	if ( is_array( $pricing ) ) {
		foreach ( $pricing as $options ) {
			$tickets = (int) $options['tickets'];
			if ( $tickets > 0 ) {
				$return = true;
			}
		}

		return $return;
	}

	return false;
}

/**
 * Check add to cart form exit conditions. Returns the event registration array if all conditions pass.
 *
 * @param int  $event_id Event post ID.
 * @param bool $override Is the add to cart form output being overridden.
 *
 * @return bool|array
 */
function mt_check_early_returns( $event_id, $override ) {
	$options       = mt_get_settings();
	$purchase_page = $options['mt_purchase_page'];
	$receipt_page  = $options['mt_receipt_page'];
	$tickets_page  = $options['mt_tickets_page'];
	if ( is_page( $purchase_page ) || is_page( $receipt_page ) || is_page( $tickets_page ) || ! $event_id ) {
		return array(
			'continue' => false,
			'reason'   => __( 'The required purchase, receipt, and ticket pages do not all exist.', 'my-tickets' ),
		);
	}
	$only_singular = $options['mt_singular'];
	if ( $only_singular && ! is_singular( $options['mt_post_types'] ) ) {
		return array(
			'continue' => false,
			'reason'   => __( 'Tickets are only sold on singular pages, and this is not an enabled singular post type.', 'my-tickets' ),
		);
	}
	if ( 'mc-events' === get_post_type( $event_id ) ) {
		$sell = get_post_meta( $event_id, '_mt_sell_tickets', true );
		if ( 'false' === $sell ) {
			return array(
				'continue' => false,
				'reason'   => __( 'This option to sell tickets is not enabled for this post.', 'my-tickets' ),
			);
		}
	}
	if ( 'true' === get_post_meta( $event_id, '_mt_hide_registration_form', true ) && false === $override ) {
		return array(
			'continue' => false,
			'reason'   => __( 'The option to hide the default registration form is enabled. Use the shortcode for display.', 'my-tickets' ),
		);
	}
	$registration = get_post_meta( $event_id, '_mt_registration_options', true );
	// if no 'total' is set at all, this is not an event with tickets.
	if ( empty( $registration['prices'] ) ) {
		return array(
			'continue' => false,
			'reason'   => __( 'This post does not have any pricing information.', 'my-tickets' ),
		);
	}
	// if total is set to inherit, but any ticket class has no defined number of tickets available, return. '0' is a valid number of tickets, '' is not.
	if ( ( isset( $registration['total'] ) && 'inherit' === $registration['total'] ) && 'general' !== $registration['counting_method'] && ! mt_has_tickets( $registration['prices'] ) ) {
		return array(
			'continue' => false,
			'reason'   => __( 'The total number of tickets is inherited from the child ticket types, but a ticket class does not have a number of tickets set.', 'my-tickets' ),
		);
	}

	// if total number of tickets is set but is an empty string or is not set; return.
	if ( ( isset( $registration['total'] ) && '' === trim( $registration['total'] ) ) || ! isset( $registration['total'] ) ) {
		return array(
			'continue' => false,
			'reason'   => __( 'The total number of tickets is either not set or is an empty value.', 'my-tickets' ),
		);
	}

	return $registration;
}

/**
 * Generates event registration form.
 *
 * @param string          $content Page content.
 * @param bool|int|object $event If boolean, exit.
 * @param string          $view Type of view for context.
 * @param string          $time Time view being displayed.
 * @param bool            $override Don't display.
 * @param bool|array      $group If grouped display, array with first and last IDs.
 *
 * @return string
 */
function mt_add_to_cart_form( $content, $event = false, $view = 'calendar', $time = 'month', $override = false, $group = false ) {
	$options  = mt_get_settings();
	$event_id = ( is_object( $event ) ) ? $event->event_post : $event;
	$continue = mt_check_early_returns( $event_id, $override );

	$continue_response = isset( $continue['continue'] ) ? false : true;
	if ( ! $continue_response ) {
		if ( MT_DEBUG ) {
			$content = $content . $continue['reason'];
		}

		return $content;
	} else {
		$registration = $continue;
	}

	$form        = '';
	$sold_out    = '';
	$has_tickets = '';
	$output      = '';
	$expired     = mt_event_expired( $event_id, true );
	$no_postal   = mt_no_postal( $event_id );
	if ( $no_postal && 1 === count( $options['mt_ticketing'] ) && in_array( 'postal', $options['mt_ticketing'], true ) && ! ( current_user_can( 'mt-order-expired' ) || current_user_can( 'manage_options' ) ) ) {
		$expired = true;
	}
	/**
	 * Filter to set whether the 'add to cart' form should be visible when the engagement is sold out.
	 *
	 * @hook mt_show_form_when_soldout
	 *
	 * @param {bool} $show Whether to show the form. Default false.
	 * @param {int}  $event_id Event ID for event displayed.
	 *
	 * @return {bool}
	 */
	$show_form_when_soldout = apply_filters( 'mt_show_form_when_soldout', true, $event_id );
	$handling_notice        = '';
	if ( ! $expired ) {
		if ( is_array( $registration ) ) {
			$pricing = $registration['prices'];
			$nonce   = wp_nonce_field( 'mt-cart-nonce', '_wpnonce', true, false );
			if ( isset( $_GET['mc_id'] ) ) {
				$mc_id     = (int) $_GET['mc_id'];
				$permalink = add_query_arg( 'mc_id', $mc_id, get_the_permalink() );
			} else {
				// This permalink is to the current page, not to the event permalink.
				$permalink = get_the_permalink();
			}
			if ( is_array( $pricing ) ) {
				/**
				 * Filter default number of tickets considered available under General Admission. General Admission uses a floating value of available tickets, so there's no real limit; this value does limit the number of tickets that can be purchased at once, however.
				 *
				 * @hook mt_default_available
				 *
				 * @param {int} $default_available Number of tickets available.
				 * @param {array} $registration Ticketing rules for this event.
				 *
				 * @return {int}
				 */
				$default_available = apply_filters( 'mt_default_available', 100, $registration );
				$available         = ( 'general' === $registration['counting_method'] ) ? $default_available : $registration['total'];
				$tickets_data      = mt_check_inventory( $event_id );
				$tickets_remaining = ( 'general' === $registration['counting_method'] ) ? $default_available : mt_check_inventory( $event_id )['available'];
				$tickets_sold      = $tickets_data['sold'];
				/**
				 * Filter when online ticket sales should close based on availability. Default 0; sales close when sold out.
				 *
				 * @hook mt_tickets_close_value
				 *
				 * @param {int}   $tickets_close_value Number of tickets remaining that triggers sold out condition.
				 * @param {int}   $event_id Event ID.
				 * @param {array} $tickets_data Data about sold and available tickets.
				 *
				 * @return {int}
				 */
				$close_value = apply_filters( 'mt_tickets_close_value', 0, $event_id, $tickets_data );
				if ( $tickets_remaining && $tickets_remaining > $close_value || $show_form_when_soldout ) {
					$sold_out    = false;
					$total_order = 0;
					$rows        = array();
					foreach ( $pricing as $type => $ticket_type ) {
						$row = mt_ticket_row( $event_id, $registration, $ticket_type, $type, $available, $tickets_remaining );
						if ( $row ) {
							$form_key          = $row['form_key'];
							$rows[ $form_key ] = $row;
						}
					}
					// Auto sort by date if event type.
					if ( 'event' === $registration['counting_method'] ) {
						ksort( $rows );
					}
					foreach ( $rows as $row ) {
						$form           .= $row['form'];
						$handling_notice = $row['handling'];
						$total_order    += (int) $row['value'];
						$has_tickets     = $row['has_tickets'];
					}
				} else {
					if ( 0 >= $tickets_remaining ) {
						$sold_out = true;
					} else {
						$output = '<p>' . mt_tickets_remaining( $tickets_data, $event_id ) . '</p>';
					}
				}
			}
			$remaining_notice = mt_remaining_tickets_notice( $event_id, $available, $tickets_remaining );
			// Translators: link to shopping cart/checkout.
			$in_cart = ( mt_in_cart( $event_id ) ) ? '<p class="my-tickets-in-cart">' . sprintf( __( 'Tickets for this event are in your cart. <a href="%s">Checkout</a>', 'my-tickets' ), mt_get_cart_url() ) . '</p>' : '';
			if ( true === $has_tickets || $show_form_when_soldout ) {
				$closing_time = ( 'event' !== $registration['counting_method'] ) ? mt_sales_close( $event_id, $registration['reg_expires'] ) : '';
				$no_post      = ( $no_postal && in_array( 'postal', array_keys( $options['mt_ticketing'] ), true ) ) ? "<p class='mt-no-post'>" . apply_filters( 'mt_cannot_send_by_email_text', __( 'Tickets for this event cannot be sent by mail.', 'my-tickets' ) ) . '</p>' : '';
				$legend       = ( 'registration' === $registration['sales_type'] ) ? __( 'Register', 'my-tickets' ) : __( 'Buy Tickets', 'my-tickets' );
				$legend       = apply_filters( 'mt_button_legend_text', $legend, $registration );
				$disabled     = ( $total_order > $tickets_remaining ) ? " disabled='disabled'" : '';
				/**
				 * Filter hidden fields added to My Tickets add to cart form.
				 *
				 * @hook mt_add_to_cart_hidden_fields
				 *
				 * @param {string} $hidden HTML output. Default empty.
				 * @param {int}    $event_id Event ID.
				 *
				 * @return {string}
				 */
				$hidden = apply_filters( 'mt_add_to_cart_hidden_fields', '', $event_id );
				/**
				 * Filter visible fields added to My Tickets add to cart form. Inserted between standard fields and submit button.
				 *
				 * @hook mt_add_to_cart_fields
				 *
				 * @param {string} $fields HTML output. Default empty.
				 * @param {int}    $event_id Event ID.
				 *
				 * @return {string}
				 */
				$fields = apply_filters( 'mt_add_to_cart_fields', '', $event_id );
				$output = "
			<div class='mt-order my-tickets'>
				<div class='mt-response' id='mt-response-$event_id' aria-live='assertive'></div>
				$no_post
				$closing_time
				$handling_notice
				<form action='" . esc_url( $permalink ) . "' method='POST' class='ticket-orders' id='order-tickets'>
					<div>
						$nonce
						<input type='hidden' name='mt_event_id' value='$event_id' />" . $hidden . "
					</div>
					<fieldset>
					<legend>$legend</legend>
						$in_cart
						$remaining_notice
						$form
						$fields" . "<p>
						<button type='submit' name='mt_add_to_cart'" . $disabled . '>' . apply_filters( 'mt_add_to_cart_text', __( 'Add to Cart', 'my-tickets' ) ) . "<span class='mt-processing'><img src='" . admin_url( 'images/spinner-2x.gif' ) . "' alt='" . __( 'Working', 'my-tickets' ) . "' /></span></button>
						<input type='hidden' name='my-tickets' value='true' />
						</p>
					</fieldset>
				</form>
			</div>";
			}
		}
	} else {
		$registration        = get_post_meta( $event_id, '_mt_registration_options', true );
		$tickets_remaining   = mt_check_inventory( $event_id );
		$tickets_remain_text = mt_tickets_remaining( $tickets_remaining, $event_id );
		$sales_closed        = ( 'registration' === $registration['sales_type'] ) ? __( 'Online registration for this event is closed', 'my-tickets' ) : __( 'Online ticket sales for this event are closed.', 'my-tickets' );
		$output              = "<div class='mt-order mt-closed'><p>" . apply_filters( 'mt_sales_closed', $sales_closed ) . "$tickets_remain_text</p></div>";
	}

	if ( true === $sold_out && $tickets_sold > 0 ) {
		$tickets_soldout = ( 'registration' === $registration['sales_type'] ) ? __( 'Registration for this event is full', 'my-tickets' ) : __( 'Tickets for this event are sold out.', 'my-tickets' );
		$soldout_banner  = "<div class='mt-order mt-soldout'><p>" . apply_filters( 'mt_tickets_soldout', $tickets_soldout ) . '</p></div>';
		if ( $show_form_when_soldout ) {
			$output = $soldout_banner . $output;
		} else {
			$output = $soldout_banner;
		}
		/**
		 * Append additional content to the tickets sold out notification.
		 *
		 * @hook mt_tickets_soldout_content
		 *
		 * @param {string} $output HTML output; default empty string.
		 * @param {int}    $event_id
		 * @param {array}  $registration Registration data for event.
		 *
		 * @return {string}
		 */
		$output .= apply_filters( 'mt_tickets_soldout_content', '', $event_id, $registration );
		$soldout = get_post_meta( $event_id, '_mt_event_soldout', true );
		if ( 'true' !== $soldout ) {
			update_post_meta( $event_id, '_mt_event_soldout', 'true' );
			// Check event types for individual expirations.
			mt_handle_expiration_status( $event_id );
			do_action( 'mt_event_sold_out', $event_id, $registration, 'soldout' );
		}
	}

	return $content . $output;
}
add_filter( 'mc_after_event', 'mt_add_to_cart_form', 5, 4 );

/**
 * Format a date for display.
 *
 * @param string $date Date string.
 *
 * @return string
 */
function mt_format_date( $date ) {
	/**
	 * Filter the date format used in add to cart and cart.
	 *
	 * @hook mt_cart_date_format
	 *
	 * @param {string} $date_format Default from WordPress settings.
	 *
	 * @return {string}
	 */
	$date_format = apply_filters( 'mt_cart_date_format', get_option( 'date_format' ) );
	/**
	 * Filter the time format used in add to cart and cart.
	 *
	 * @hook mt_cart_time_format
	 *
	 * @param {string} $time_format Default from WordPress settings.
	 *
	 * @return {string}
	 */
	$time_format = apply_filters( 'mt_cart_time_format', get_option( 'time_format' ) );
	$date_string = mt_date( $date_format, strtotime( $date ), false );
	$time_string = mt_date( $time_format, strtotime( $date ), false );

	return '<strong>' . $date_string . '</strong><span>, </span>' . $time_string;
}

/**
 * Get closing time for a ticket type.
 *
 * @param array $ticket_type Characteristics of this ticket type.
 * @param array $registration Registration characteristics for event.
 *
 * @return int
 */
function mt_get_ticket_type_close( $ticket_type, $registration ) {
	$stop   = (int) $registration['reg_expires'] * HOUR_IN_SECONDS;
	$method = $registration['counting_method'];
	if ( 'event' === $method ) {
		// Event occurrence date/time - amount of time.
		$close = strtotime( $ticket_type['label'] ) - $stop;
	} else {
		// The 'close' is a custom value, offset by the stop value.
		$close = ( isset( $ticket_type['close'] ) && ! empty( $ticket_type['close'] ) ) ? $ticket_type['close'] + $stop : '';
	}

	return (int) $close;
}

/**
 * Get original list price for an event.
 *
 * @param int    $event_id An event Post ID.
 * @param string $type A ticket type key.
 *
 * @return float
 */
function mt_get_original_ticket_price( $event_id, $type ) {
	$registration = get_post_meta( $event_id, '_mt_registration_options', true );
	$prices       = $registration['prices'];
	$ticket_type  = $prices[ $type ];
	$price        = $ticket_type['price'];

	return $price;
}

/**
 * Generate a single row in the add to cart form.
 *
 * @param int        $event_id Event post ID.
 * @param array      $registration Registration settings for this event.
 * @param array      $ticket_type Settings for this row.
 * @param string     $type Type of ticket.
 * @param int|string $available Number of tickets available or 'inherit'.
 * @param int        $tickets_remaining Number of tickets left for purchase.
 *
 * @return array|bool 'false' if ticket is not purchaseable.
 */
function mt_ticket_row( $event_id, $registration, $ticket_type, $type, $available, $tickets_remaining ) {
	$options   = mt_get_settings();
	$pricing   = $registration['prices'];
	$cart_data = mt_in_cart( $event_id );
	if ( ! is_array( $cart_data ) ) {
		$cart_data = '';
	}
	// if multiple != true, use checkboxes.
	$input_type = ( isset( $registration['multiple'] ) && 'true' === $registration['multiple'] ) ? 'number' : 'checkbox';
	$class      = 'mt-available';
	$form       = '';
	/**
	 * Filter value data about a specific type of ticket.
	 *
	 * @hook mt_ticket_settings
	 *
	 * @param {array}  $ticket_type Price and availability for a ticket type.
	 * @param {array}  $pricing Full pricing array being iterated.
	 * @param {int}    $event_id Event ID.
	 * @param {string} $type Current ticket type.
	 */
	$ticket_type = apply_filters( 'mt_ticket_settings', $ticket_type, $pricing, $event_id, $type );
	if ( ! mt_can_order( $type ) ) {
		return false;
	}
	$extra_label = '';
	if ( mt_admin_only( $type ) ) {
		$extra_label = ' <span class="mt-admin-only">(' . __( 'Administrators only', 'my-tickets' ) . ')</span>';
	}
	/**
	 * Add additional labeling appended to ticket type information.
	 *
	 * @hook mt_extra_label
	 *
	 * @param {string}     $extra_label Default empty string; 'Administrators only' for complimentary tickets.
	 * @param {int|object} $event Event post ID.
	 * @param {string}     $type Ticket type.
	 *
	 * @return {string}
	 */
	$extra_label = apply_filters( 'mt_extra_label', $extra_label, $event_id, $type );
	if ( $type ) {
		if ( ! isset( $ticket_type['price'] ) ) {
			return false;
		}
		$price = mt_calculate_discount( $ticket_type['price'], $event_id );
		$price = mt_handling_price( $price, $event_id, $type );
		/**
		 * Format a numeric string as money.
		 *
		 * @hook mt_money_format
		 *
		 * @param {string|float} $price Original numeric value to format using current selected currency and structure.
		 *
		 * @return {string}
		 */
		$price = apply_filters( 'mt_money_format', $price );
		/**
		 * Filter ticket handling price.
		 *
		 * @hook mt_ticket_handling_price
		 *
		 * @param {string}     $ticket_handling Handling price from settings.
		 * @param {int}object} $event Event post ID.
		 * @param {string}     $type Ticket type.
		 */
		$ticket_handling = apply_filters( 'mt_ticket_handling_price', $options['mt_ticket_handling'], $event_id, $type );
		$handling_notice = mt_handling_notice();
		/**
		 * Filter the displayed ticket price.
		 *
		 * @hook mt_ticket_price_label
		 *
		 * @param {string}       $price Formatted price.
		 * @param {string|float} $original_price Unformatted price.
		 * @param {string|float} $ticket_handling Ticket handling charge amount.
		 *
		 * @return {string}
		 */
		$ticket_price_label = '<span class="mt-ticket-price">' . apply_filters( 'mt_ticket_price_label', $price, $ticket_type['price'], $ticket_handling ) . '</span>';
		/**
		 * Filter the default ticket input value. Sets the default number of tickets to order.
		 *
		 * @hook mt_cart_default_value
		 *
		 * @param {int}    $value Default numeric value. Default '0'.
		 * @param {string} $type Ticket type displayed.
		 *
		 * @return {string}
		 */
		$default_cart_value = apply_filters( 'mt_cart_default_value', '0', $type );
		$value              = ( is_array( $cart_data ) && isset( $cart_data[ $type ] ) ) ? $cart_data[ $type ] : $default_cart_value;
		$value              = ( '' === $value ) ? 0 : (int) $value;
		$order_value        = $value;
		$attributes         = '';
		$close              = mt_get_ticket_type_close( $ticket_type, $registration );
		$type_sales_closed  = false;
		$closure            = '';

		if ( $close && ( $close - mt_date() ) < DAY_IN_SECONDS ) {
			$show_closed = ( 'true' === $options['mt_show_closed'] ) ? 'show' : 'hide';
			/**
			 * Filter whether a ticket type that has closed will be shown for an event.
			 *
			 * @hook mt_ticket_type_sales_closed
			 *
			 * @param {string} $show_closed 'hide' or 'show'.
			 * @param {int}    $event_id ID for currently displayed post.
			 *
			 * @return {string}
			 */
			$ticket_type_sales_closed_behavior = apply_filters( 'mt_ticket_type_sales_closed', $show_closed, $event_id );
			if ( 'hide' === $ticket_type_sales_closed_behavior && $close < mt_date() ) {
				// If this ticket type is no longer available, skip.
				return false;
			} else {
				if ( 'event' === $registration['counting_method'] ) {
					$type_sales_closed = ( $close < mt_date() ) ? true : false;
					if ( ( $close - mt_date() ) < DAY_IN_SECONDS ) {
						$window = human_time_diff( $close, mt_date() );
					}
					// translators: amount of time before sales close. Uses human_time_diff().
					$closure = ( $type_sales_closed ) ? __( 'Online sales are closed', 'my-tickets' ) : sprintf( __( 'Online sales close in %s', 'my-tickets' ), $window );
				} else {
					$type_sales_closed = true;
					// translators: Date ticket sales closed.
					$closure = sprintf( __( 'Online sales closed %s', 'my-tickets' ), date_i18n( get_option( 'date_format' ), $close ) );
				}
			}
		}
		if ( $type_sales_closed ) {
			mt_handle_ticket_type_expired( $event_id, $type );
		}
		if ( 'checkbox' === $input_type || 'radio' === $input_type ) {
			if ( 1 === $value ) {
				$attributes = " checked='checked'";
			}
			$value       = 1;
			$order_value = 0;
		}
		$button_up   = ( 'number' === $input_type ) ? '<button type="button" class="mt-increment"><span class="dashicons dashicons-plus" aria-hidden="true"></span><span class="screen-reader-text">' . __( 'Add one', 'my-tickets' ) . '</span></button>' : '';
		$button_down = ( 'number' === $input_type ) ? '<button type="button" class="mt-decrement"><span class="dashicons dashicons-minus" aria-hidden="true"></span><span class="screen-reader-text">' . __( 'Remove one', 'my-tickets' ) . '</span></button>' : '';

		if ( 'inherit' === $available ) {
			$inventory = mt_check_inventory( $event_id, $type );
			$tickets   = absint( $ticket_type['tickets'] );
			$label     = ( 'event' === $registration['counting_method'] ) ? mt_format_date( $ticket_type['label'] ) : $ticket_type['label'];
			$form_key  = ( 'event' === $registration['counting_method'] ) ? strtotime( strip_tags( $label ) ) : sanitize_key( strip_tags( $label ) );
			$remaining = $inventory['available'];
			if ( $remaining < 1 ) {
				$remaining = 0;
			}
			/**
			 * Filter maximum sale per event. Limits number of tickets that can be purchased at a time.
			 *
			 * @hook mt_max_sale_per_event
			 *
			 * @param {bool} $max_limit Default false.
			 *
			 * @return {bool|int} Number of tickets that can be purchased at once or false.
			 */
			$max_limit = apply_filters( 'mt_max_sale_per_event', false );
			if ( $max_limit ) {
				$max = ( $max_limit > $remaining ) ? $remaining : $max_limit;
			} else {
				$max = $remaining;
			}
			$disable = ( $remaining < 1 || $type_sales_closed ) ? ' disabled="disabled"' : '';
			if ( '' === $attributes ) {
				$attributes = " min='0' max='$max' inputmode='numeric' pattern='[0-9]*'";
				if ( 0 === $remaining || $type_sales_closed ) {
					$attributes .= ' readonly="readonly"';
					$button_up   = str_replace( 'type="button"', 'type="button" disabled', $button_up );
					$button_down = str_replace( 'type="button"', 'type="button" disabled', $button_down );
					$class       = ( $type_sales_closed ) ? 'mt-sales-closed' : 'mt-sold-out';
				} else {
					$class = 'mt-available';
				}
			}
			$form = "<div class='mt-ticket-field mt-ticket-$type $class'><label for='mt_tickets_$type" . '_' . "$event_id' id='mt_tickets_label_$type" . '_' . "$event_id'>" . $label . $extra_label . '</label>';
			/**
			 * Filter the add to cart input field.
			 *
			 * @hook mt_add_to_cart_input
			 *
			 * @param {string} $input      Default input form field.
			 * @param {string} $input_type Type of input requested.
			 * @param {int}    $value      Default value set.
			 * @param {string} $attributes String of field attributes provided for input.
			 * @param {string} $disable    Attribute to set field as disabled.
			 * @param {int}    $max        Max value allowed.
			 * @param {int}    $available  Number of tickets available.
			 * @param {int}    $event_id   Event ID.
			 *
			 * @return {string}
			 */
			$form .= '<div class="mt-ticket-input">' . apply_filters(
				'mt_add_to_cart_input',
				"<input type='$input_type' name='mt_tickets[$type]' id='mt_tickets_$type" . '_' . "$event_id' class='tickets_field' value='$value' $attributes aria-labelledby='mt_tickets_label_$type" . '_' . $event_id . " mt_tickets_data_$type'$disable />$button_up$button_down",
				$input_type,
				$type,
				$value,
				$attributes,
				$disable,
				$max,
				$available,
				$event_id
			) . '</div>';

			$hide_remaining = mt_hide_remaining( $remaining );
			$remaining_text = $ticket_price_label . sprintf(
				/**
				 * Filter the string describing the number of tickets remaining as a fraction.
				 *
				 * @hook mt_tickets_available_discrete_text
				 *
				 * @param {string} $default_text Original available tickets text.
				 * @param {int}    $remaining    Number of tickets remaining.
				 * @param {int}    $tickets      Number of tickets available.
				 *
				 * @return {string}
				 */
				apply_filters(
					'mt_tickets_remaining_discrete_text',
					// Translators: 1) Opening span tag; 2) number remaining as fraction e.g. 2/40, 3) closing span tag..
					__( '%1$s %2$s remaining%3$s', 'my-tickets' ),
					$remaining,
					$tickets
				),
				'<span class="tickets-remaining">',
				"<span class='value remaining-tickets'>" . $remaining . "</span><span class='separator'>/</span><span class='ticket-count'>" . $tickets . '</span>',
				'</span>'
			);
			$available_text = $ticket_price_label . sprintf(
				/**
				 * Filter the string describing the number of tickets available as a number.
				 *
				 * @hook mt_tickets_available_discrete_text
				 *
				 * @param {string} $default_text Original available tickets text.
				 * @param {int}    $remaining    Number of tickets remaining.
				 * @param {int}    $tickets      Number of tickets available.
				 *
				 * @return {string}
				 */
				apply_filters(
					'mt_tickets_available_discrete_text',
					// Translators: 1) opening span; 2) number remaining as integer, 3) closing span tag.
					__( '%1$s %2$s available%3$s', 'my-tickets' ),
					$remaining,
					$tickets
				),
				'<span class="tickets-remaining tickets-available">',
				"<span class='value available-tickets'>" . $remaining . '</span>',
				'</span>'
			);
			if ( 'proportion' === $options['mt_display_remaining'] ) {
				$display_text = $remaining_text;
			} else {
				$display_text = $available_text;
			}
			/**
			 * Append text after the 'remaining tickets' text in the cart form.
			 *
			 * @hook mt_after_remaining_text
			 *
			 * @param {string} $remaining Additional text. Default empty.
			 * @param {int}    $event_id Current event ID.
			 * @param {string} $type Ticket type displayed.
			 * @param {int}    $tickets_remaining. Number of tickets left.
			 */
			$display_text .= apply_filters( 'mt_after_remaining_text', '', $event_id, $type, $tickets_remaining );

			$form .= "<div id='mt_tickets_data_$type' class='ticket-pricing$hide_remaining'>" . $display_text . '<span class="mt-closure-date">' . $closure . '</span></div>';
			$form .= "<span class='mt-error-notice' aria-live='assertive'></span></div>";
		} else {
			$remaining = $tickets_remaining;
			if ( '' === $attributes ) {
				$attributes = " min='0' max='$remaining'";
				if ( 0 === $remaining ) {
					$attributes .= ' readonly="readonly"';
					$class       = 'mt-sold-out';
				}
			}
			/**
			 * Filter whether the price should be shown in the label or as an aria-described field after the input.
			 *
			 * @hook mt_price_in_label
			 *
			 * @param {false} $price_in_label Default false.
			 * @param {int}   $event_id Event ID.
			 *
			 * @return {bool}
			 */
			$price_in_label = apply_filters( 'mt_price_in_label', false, $event_id );
			$price_class    = ( $price_in_label ) ? 'mt-price-in-label' : '';
			$price          = "<span id='mt_tickets_data_$type'>$ticket_price_label</span>";
			$label_price    = ( $price_in_label ) ? ' <span class="mt-label-price">' . strip_tags( $price ) . '</span>' : '';
			$post_price     = ( ! $price_in_label ) ? $price : '';
			$form_key       = $type;
			$form          .= "<div class='mt-ticket-field mt-ticket-$type $class $price_class'><label for='mt_tickets_$type" . '_' . "$event_id' id='mt_tickets_label_$type" . '_' . "$event_id'>" . esc_attr( $ticket_type['label'] ) . $extra_label . $label_price . '</label>';
			$form          .= '<div class="mt-ticket-input">' . apply_filters(
				'mt_add_to_cart_input',
				"<input type='$input_type' name='mt_tickets[$type]' $attributes id='mt_tickets_$type" . '_' . "$event_id' class='tickets_field' value='$value' aria-labelledby='mt_tickets_label_$type" . '_' . $event_id . " mt_tickets_data_$type' />$button_up$button_down",
				$input_type,
				$type,
				$value,
				$attributes,
				'',
				$remaining,
				$available,
				$event_id
			) . '</div>';
			$form          .= $post_price . "<span class='mt-error-notice' aria-live='assertive'></span></div>";
		}
		$has_tickets = true;
	}
	return array(
		'form_key'    => $form_key,
		'form'        => $form,
		'value'       => $order_value,
		'has_tickets' => $has_tickets,
		'handling'    => $handling_notice,
	);
}

/**
 * Return the remaining tickets notice for an event.
 *
 * @param int        $event_id Event post ID.
 * @param string|int $available Number of tickets available or type of availability if inherited.
 * @param int        $tickets_remaining Number of tickets remaining.
 *
 * @return string
 */
function mt_remaining_tickets_notice( $event_id, $available, $tickets_remaining ) {
	$remaining_notice = '';
	if ( 'inherit' !== $available ) {
		// If this event is general admission, then never show number of tickets remaining or status.
		$data = get_post_meta( $event_id, '_mc_event_data', true );
		if ( isset( $data['general_admission'] ) && 'on' === $data['general_admission'] ) {
			$hide_remaining = ' hiding';
		} else {
			$hide_remaining = mt_hide_remaining( $tickets_remaining );
		}
		// Translators: tickets remaining.
		$remaining_continuous_text = __( '%s tickets remaining.', 'my-tickets' );
		/**
		 * Filter tickets remaining text for continuously sold events.
		 *
		 * @hook mt_tickets_remaining_continuous_text
		 *
		 * @param {string} $remaining_continuous_text Default text.
		 * @param {int}    $event_id Event ID.
		 *
		 * @return {string}
		 */
		$remaining_continuous_text = apply_filters( 'mt_tickets_remaining_continuous_text', $remaining_continuous_text, $event_id );
		$remaining_notice          = '<p class="tickets-remaining' . $hide_remaining . '">' . sprintf( $remaining_continuous_text, "<span class='value'>" . $tickets_remaining . '</span>' ) . '</p>';
	}

	return $remaining_notice;
}

/**
 * Get current status of an event.
 *
 * @param int $event_id Event ID.
 *
 * @return string
 */
function mt_event_status( $event_id = false ) {
	// Exit conditions.
	$options       = mt_get_settings();
	$purchase_page = $options['mt_purchase_page'];
	$receipt_page  = $options['mt_receipt_page'];
	$tickets_page  = $options['mt_tickets_page'];
	if ( is_page( $purchase_page ) || is_page( $receipt_page ) || is_page( $tickets_page ) ) {
		return '';
	}
	if ( ! $event_id ) {
		return '';
	}
	if ( 'mc-events' === get_post_type( $event_id ) ) {
		$sell = get_post_meta( $event_id, '_mt_sell_tickets', true );
		if ( 'false' === $sell ) {
			return '';
		}
	}

	if ( 'true' === get_post_meta( $event_id, '_mt_hide_registration_form', true ) ) {
		return '';
	}
	$registration = get_post_meta( $event_id, '_mt_registration_options', true );
	// if no 'total' is set at all, this is not an event with tickets.
	if ( empty( $registration['prices'] ) ) {
		return '';
	}

	// if total is set to inherit, but any ticket class has no defined number of tickets available, return. '0' is a valid number of tickets, '' is not.
	if ( ( isset( $registration['total'] ) && 'inherit' === $registration['total'] ) && ! mt_has_tickets( $registration['prices'] ) ) {
		return '';
	}

	// if total number of tickets is set but is an empty string or is not set; return.
	if ( ( isset( $registration['total'] ) && '' === trim( $registration['total'] ) ) || ! isset( $registration['total'] ) ) {
		return '';
	}

	// If this event is general admission, then never show number of tickets remaining or status.
	$data = get_post_meta( $event_id, '_mc_event_data', true );
	if ( isset( $data['general_admission'] ) && 'on' === $data['general_admission'] ) {
		return '';
	}

	$expired           = ( mt_event_expired( $event_id ) ) ? __( 'Sales closed', 'my-tickets' ) : '';
	$registration      = get_post_meta( $event_id, '_mt_registration_options', true );
	$tickets_remaining = mt_check_inventory( $event_id );
	// Translators: Number of tickets remaining.
	$remaining = ( 0 >= $tickets_remaining['available'] ) ? $expired : sprintf( __( '%s tickets remaining', 'my-tickets' ), '<strong>' . $tickets_remaining['available'] . '</strong>' );
	$sold_out  = ( 0 >= $tickets_remaining['available'] ) ? __( 'Sold out', 'my-tickets' ) : $remaining;

	return $sold_out;
}

/**
 * Figure out whether tickets remaining should be hidden.
 *
 * @param int $tickets_remaining Number of tickets remaining.
 *
 * @return string
 */
function mt_hide_remaining( $tickets_remaining ) {
	$options = mt_get_settings();
	// If hide remaining is enabled, set as hidden.
	$remaining_limit = isset( $options['mt_hide_remaining_limit'] ) ? absint( $options['mt_hide_remaining_limit'] ) : 0;
	$hide_remaining  = ( isset( $options['mt_hide_remaining'] ) && 'true' === $options['mt_hide_remaining'] ) ? true : false;
	// Hide this if 1) tickets remaining is hidden & limit is 0 or 2) tickets remaining is hidden and there are more tickets left than the limit.
	$hidden_class = ( ( $hide_remaining && ! $remaining_limit ) || ( $hide_remaining && $tickets_remaining > $remaining_limit ) ) ? ' hiding' : '';

	return $hidden_class;
}
/**
 * Test whether the current ticket type is admin-only
 *
 * @param string $type Type of ticket being used.
 *
 * @return boolean
 */
function mt_admin_only( $type ) {
	if ( ( 'complementary' === $type || 'complimentary' === $type ) ) {
		return true;
	}

	/**
	 * Set a ticket type as only available to administrators. Return true to hide ticket types from the public.
	 *
	 * @hook mt_admin_only_ticket
	 *
	 * @param {false} $admin_only False to indicate a ticket type is available to the public.
	 * @param {string} $type Ticket type key.
	 *
	 * @return {bool}
	 */
	return apply_filters( 'mt_admin_only_ticket', false, $type );
}

/**
 * Test whether the current user can order the current ticket type.
 *
 * @param string $type Type of ticket being handled.
 *
 * @return bool
 */
function mt_can_order( $type ) {
	if ( ! mt_admin_only( $type ) ) {
		return true;
	}
	$comps = ( current_user_can( 'mt-order-comps' ) || current_user_can( 'manage_options' ) ) ? true : false;
	if ( mt_admin_only( $type ) && $comps ) {
		return true;
	}

	return false;
}

/**
 * Produce notice about tickets remaining after sales are closed.
 *
 * @param array   $tickets_data array of ticket sales data.
 * @param integer $event_id Event ID.
 *
 * @return string Notice
 */
function mt_tickets_remaining( $tickets_data, $event_id ) {
	$tickets_remaining = $tickets_data['available'];
	/**
	 * Filter when online ticket sales should close based on availability. Default 0; sales close when sold out.
	 *
	 * @hook mt_tickets_close_value
	 *
	 * @param {int}   $tickets_close_value Number of tickets remaining that triggers sold out condition.
	 * @param {int}   $event_id Event ID.
	 * @param {array} $tickets_data Data about sold and available tickets.
	 *
	 * @return {int}
	 */
	$close_value = apply_filters( 'mt_tickets_close_value', 0, $event_id, $tickets_data );
	if ( $tickets_remaining && $tickets_remaining > $close_value ) {
		$tickets_remain_text = '';
	} else {
		if ( $tickets_remaining > 0 ) {
			// Translators: number of tickets available.
			$remaining_string = _n( 'Online sales are closed, but there is still %d ticket available at the box office!', 'Online sales are closed, but there are still %d tickets available at the box office!', $tickets_remaining, 'my-tickets' );
			/**
			 * Filter the text used to indicate that tickets are remaining after sales have closed.
			 *
			 * @hook mt_tickets_still_remaining_text
			 *
			 * @param {string} $remaining_string Default text.
			 * @param {int}    $tickets_remaining Number of tickets remaining.
			 * @param {int}    $event_id Event ID.
			 *
			 * @return {string}
			 */
			$remaining_string    = apply_filters( 'mt_tickets_still_remaining_text', $remaining_string );
			$tickets_remain_text = ' ' . sprintf( $remaining_string, $tickets_remaining, $event_id );
		} else {
			$tickets_remain_text = '';
		}
	}

	return $tickets_remain_text;
}

add_filter( 'mt_tickets_close_value', 'mt_close_ticket_sales', 10, 3 );
/**
 * Customize when events will close for ticket sales, to reserve some tickets for door sales.
 *
 * @param integer $limit Point where ticket sales will close. Default: 0.
 * @param integer $event_id Event ID, in case somebody wanted some further customization.
 * @param array   $remaining remaining, sold, and total tickets available.
 *
 * @return integer new value where ticket sales are closed for an event.
 */
function mt_close_ticket_sales( $limit, $event_id, $remaining ) {
	$options            = mt_get_settings();
	$tickets_close_at   = ( isset( $options['mt_tickets_close_value'] ) && is_numeric( $options['mt_tickets_close_value'] ) ) ? $options['mt_tickets_close_value'] : 0;
	$tickets_close_type = ( isset( $options['mt_tickets_close_type'] ) ) ? $options['mt_tickets_close_type'] : 'integer';
	switch ( $tickets_close_type ) {
		case 'integer':
			$limit = $tickets_close_at;
			break;
		case 'percent':
			$limit = round( ( $tickets_close_at / 100 ) * $remaining['total'] );
			break;
	}
	/**
	 * Filter the number of remaining tickets that trigger automatic closing of online ticket sales.
	 *
	 * @hook mt_custom_event_limit
	 *
	 * @param {int}   $limit Number of tickets that cause an event to be sold out.
	 * @param {int}   $event_id Event ID.
	 * @param {array} $remaining Array of information about the number of tickets remaining.
	 *
	 * @return {int}
	 */
	return apply_filters( 'mt_custom_event_limit', $limit, $event_id, $remaining );
}

/**
 * Produce price if a per-ticket handling charge is being applied.
 *
 * @param float   $price Original price without handling.
 * @param integer $event Event ID.
 * @param string  $type Public or admin ticket type.
 *
 * @return float new price
 */
function mt_handling_price( $price, $event, $type = 'standard' ) {
	// correction for an early mipselling.
	if ( mt_admin_only( $type ) ) {
		return $price; // no handling on complimentary tickets.
	}
	$options = mt_get_settings();
	if ( isset( $options['mt_ticket_handling'] ) && is_numeric( $options['mt_ticket_handling'] ) ) {
		/**
		 * Filter the amount charged for ticket handling on a particular event.
		 *
		 * @hook mt_ticket_handling_price
		 *
		 * @param {float} $mt_ticket_handling Amount saved in settings for event handling.
		 * @param {int}   $event Event ID.
		 *
		 * @return {float}
		 */
		$price = $price + apply_filters( 'mt_ticket_handling_price', $options['mt_ticket_handling'], $event );
	}

	return $price;
}

/**
 * Produce notice if a per-ticket handling charge is being applied.
 *
 * @return string handling notice
 */
function mt_handling_notice() {
	$options = mt_get_settings();
	if ( isset( $options['mt_ticket_handling'] ) && is_numeric( $options['mt_ticket_handling'] ) && $options['mt_ticket_handling'] > 0 ) {
		// Translators: amount of ticket handling charge.
		$handling_string = __( 'Tickets include a %s ticket handling charge.', 'my-tickets' );
		$money_formatted = apply_filters( 'mt_money_format', $options['mt_ticket_handling'] );
		/**
		 * Filter the string used to notify users about per-ticket handling charges.
		 *
		 * @hook mt_ticket_handling_notice
		 *
		 * @param {string} $handling_string Original notice.
		 * @param {string} $handling_charge Amount charged before formatting.
		 *
		 * @return {string}
		 */
		$handling_notice = "<div class='mt-ticket-handling'>" . apply_filters( 'mt_ticket_handling_notice', sprintf( $handling_string, $money_formatted ), $options['mt_ticket_handling'] ) . '</div>';
	} else {
		$handling_notice = '';
	}

	return $handling_notice;
}

/**
 * Get the handling costs for a cart purchase.
 *
 * @param array  $options Array of My Tickets options.
 * @param string $gateway Currently selected gateway.
 *
 * @return float|int
 */
function mt_get_cart_handling( $options, $gateway ) {
	$handling = ( isset( $options['mt_handling'] ) ) ? $options['mt_handling'] : 0;
	$original = $handling;
	$ignored  = ( isset( $options['mt_gateways'][ $gateway ]['mt_handling'] ) && 'true' === $options['mt_gateways'][ $gateway ]['mt_handling'] ) ? true : false;
	if ( $ignored ) {
		$handling = 0;
	}
	/**
	 * Filter the handling ticket based on gateway selected.
	 *
	 * @hook mt_handling_total
	 *
	 * @param {float|int} $handling Cart handling fee after gateway settings applied.
	 * @param {float|int} $original Cart handling fee from settings.
	 * @param {string}    $gateway Selected gateway.
	 *
	 * @return {float|int}
	 */
	$handling = apply_filters( 'mt_handling_total', $handling, $original, $gateway );
	$handling = ( is_numeric( $handling ) ) ? floatval( $handling ) : 0;

	return $handling;
}

/**
 * Get closing date/time for event
 *
 * @param integer $event_id Event ID.
 * @param string  $expires Expiration time.
 *
 * @return string
 */
function mt_sales_close( $event_id, $expires ) {
	$event = get_post_meta( $event_id, '_mc_event_data', true );
	if ( $event && is_array( $event ) ) {
		if ( isset( $event['general_admission'] ) && 'on' === $event['general_admission'] ) {
			// Don't display closing information on General Admission events.
			return '';
		}
		if ( isset( $event['event_begin'] ) && isset( $event['event_time'] ) ) {
			$expiration = $expires * 60 * 60;
			$begin      = strtotime( $event['event_begin'] . ' ' . $event['event_time'] ) - $expiration;
			if ( mt_date( 'Y-m-d', $begin ) === mt_date( 'Y-m-d', mt_current_time() ) ) {
				// Translators: time that ticket sales close today.
				$sales_close_text = __( 'Ticket sales close at %s today', 'my-tickets' );
				/**
				 * Filter the notification for when ticket sales will close.
				 *
				 * @hook mt_ticket_sales_close_text
				 *
				 * @param {string} $sales_close_text Original notice.
				 * @param {int}    $event Event ID.
				 *
				 * @return {string}
				 */
				$sales_close_text = apply_filters( 'mt_ticket_sales_close_text', $sales_close_text, $event );
				return '<p>' . sprintf( $sales_close_text, '<strong>' . date_i18n( get_option( 'time_format' ), $begin ) . '</strong>' ) . '</p>';
			}
		}
	}

	return '';
}

/**
 * Test whether event can currently allow tickets to be shipped, given provided time frame for shipping in relation to event date/time.
 *
 * @param integer $event_id Event ID.
 *
 * @return bool
 */
function mt_no_postal( $event_id ) {
	$options       = mt_get_settings();
	$shipping_time = $options['mt_shipping_time'];
	$event         = get_post_meta( $event_id, '_mc_event_data', true );
	$no_postal     = false;
	if ( $event && is_array( $event ) ) {
		$date = ( isset( $event['event_begin'] ) ) ? $event['event_begin'] : false;
		$time = ( isset( $event['event_time'] ) ) ? $event['event_time'] : false;
		if ( is_numeric( $date ) && is_numeric( $time ) ) {
			$event_date = strtotime( absint( $date . ' ' . $time ) );
			$no_postal  = ( $event_date <= ( mt_current_time() + ( 60 * 60 * 24 * $shipping_time ) ) ) ? true : false;

			return $no_postal;
		}
	}

	return $no_postal;
}

add_action( 'init', 'mt_add_to_cart' );
/**
 * Add event tickets to cart. (Non AJAX).
 *
 * @uses function mt_register_message.
 */
function mt_add_to_cart() {
	if ( ! isset( $_REQUEST['mt_add_to_cart'] ) ) {
		return;
	} else {
		if ( isset( $_POST['mt_add_to_cart'] ) ) {
			$nonce = $_POST['_wpnonce'];
			if ( ! wp_verify_nonce( $nonce, 'mt-cart-nonce' ) ) {
				return;
			}
		}
		if ( isset( $_GET['mt_add_to_cart'] ) ) {
			$data     = mt_get_data( 'cart' );
			$event_id = intval( $_GET['event_id'] );
			// todo: figure out how to set this up for mt_admin_only ticket types.
			$type    = ( isset( $_GET['ticket_type'] ) && ( 'complementary' !== $_GET['ticket_type'] && 'complimentary' !== $_GET['ticket_type'] ) ) ? sanitize_key( $_GET['ticket_type'] ) : false;
			$count   = isset( $_GET['count'] ) ? intval( $_GET['count'] ) : 1;
			$options = ( $type ) ? array( $type => $count ) : false;
			// Record into virtual inventory.
			$cur_count = ( isset( $data[ $event_id ] ) ) ? $data[ $event_id ][ $type ] : 0;
			$new_count = $count;
			if ( $new_count > $cur_count ) {
				$increment = ( $cur_count - $new_count );
			} else {
				$increment = ( $cur_count - $new_count ) * -1;
			}
			mt_update_inventory( $event_id, $type, $increment );
			if ( $data ) {
				$event_options = isset( $data[ $event_id ] ) && is_array( $data[ $event_id ] ) ? $data[ $event_id ] : array();
				$options       = array_merge( $event_options, $options );
			}
		} else {
			$event_id = ( isset( $_POST['mt_event_id'] ) ) ? intval( $_POST['mt_event_id'] ) : false;
			$options  = ( isset( $_POST['mt_tickets'] ) ) ? array_map( 'absint', $_POST['mt_tickets'] ) : false;
		}
		$saved = ( false !== $options ) ? mt_save_data(
			array(
				'event_id' => $event_id,
				'options'  => $options,
			)
		) : false;
	}
	if ( $saved ) {
		mt_register_message( 'add_to_cart', 'success' );
	} else {
		mt_register_message( 'add_to_cart', 'error' );
	}
}

/**
 * Register a message to be displayed following a cart or button action.
 *
 * @param string                $context Current message context.
 * @param string                $type Message type.
 * @param mixed boolean|integer $payment_id Payment ID.
 *
 * @return void
 */
function mt_register_message( $context, $type, $payment_id = false ) {
	global $mt_message;
	$mt_message = mt_get_message( $context, $type, $payment_id );
}

/**
 * Fetch a message to be displayed following a cart or button action.
 *
 * @param string  $context Current message context.
 * @param string  $type Message type.
 * @param integer $payment_id Payment ID.
 *
 * @return string
 */
function mt_get_message( $context, $type, $payment_id ) {
	$context = esc_attr( $context );
	$type    = esc_attr( $type );
	if ( 'add_to_cart' === $context ) {
		$cart_url = mt_get_cart_url();
		switch ( $type ) {
			case 'success':
				// Translators: cart URL.
				$return = sprintf( __( 'Event successfully added to <a href="%s">your cart</a>.', 'my-tickets' ), $cart_url );
				break;
			case 'error':
				$return = __( 'That event could not be added to your cart.', 'my-tickets' );
				break;
			case 'error-expired':
				$return = __( 'Online ticket sales are no longer available for this event.', 'my-tickets' );
				break;
			default:
				$return = '';
		}
	} elseif ( 'update_cart' === $context ) {
		switch ( $type ) {
			case 'success':
				$return = __( 'Cart Updated.', 'my-tickets' );
				break;
			case 'error':
				$return = __( 'Could not update your cart.', 'my-tickets' );
				break;
			case 'error-expired':
				$return = __( 'The event is no longer available for online ticket sales.', 'my-tickets' );
				break;
			default:
				$return = '';
		}
	} elseif ( 'payment_due' === $context ) {
		switch ( $type ) {
			case 'success':
				if ( $payment_id ) {
					$gateway = get_post_meta( $payment_id, '_gateway', true );
					if ( 'offline' === $gateway ) {
						wp_publish_post( $payment_id );
					}
				}
				$return = __( 'Your ticket order has been submitted! Any payment due will be collected when you arrive at the event.', 'my-tickets' );
				break;
			default:
				$return = '';
		}
	} elseif ( 'cart_submitted' === $context ) {
		switch ( $type ) {
			case 'success':
				$return = __( 'Cart Submitted.', 'my-tickets' );
				break;
			case 'error':
				$return = __( 'Could not submit your cart.', 'my-tickets' );
				break;
			default:
				$return = '';
		}
	}

	return apply_filters( 'mt_get_message_text', "<div class='$context $type mt-message'><p>$return</p></div>", $context, $type );
}

add_filter( 'the_content', 'mt_display_message' );
/**
 * Get registered message and display at top of content.
 *
 * @param string $content Post content.
 *
 * @return string
 */
function mt_display_message( $content ) {
	if ( isset( $_POST['my-tickets'] ) && is_main_query() ) {
		global $mt_message;

		return $mt_message . $content;
	}

	return $content;
}

/**
 * Update cart data. Special case application of mt_save_data.
 *
 * @param array $post Posted data.
 *
 * @return array
 */
function mt_update_cart( $post = array() ) {
	$cart = mt_get_cart();
	if ( ! $cart ) {
		$event_id = ( isset( $post['mt_event_id'] ) ) ? $post['mt_event_id'] : false;
		$options  = ( isset( $post['mt_tickets'] ) ) ? $post['mt_tickets'] : false;
		$cart     = array(
			'event_id' => $event_id,
			'options'  => $options,
		);
		$updated  = mt_save_data( $cart );
	} else {
		foreach ( $post as $id => $item ) {
			if ( is_numeric( $id ) ) {
				$cart_item = isset( $cart[ $id ] ) ? $cart[ $id ] : array();
				$post_item = array();
				if ( ! is_array( $item ) ) {
					continue;
				}
				foreach ( $item as $type => $count ) {
					$post_item[ $type ] = absint( $count['count'] );
				}
				$post_item = array_merge( $cart_item, $post_item );
				if ( ! isset( $cart[ $id ] ) || ( $cart[ $id ] !== $post_item ) ) {
					$cart[ $id ] = $post_item;
				}
			}
		}
		$has_contents = false;
		// If any ticket type has a count, keep event in cart.
		foreach ( $cart as $id => $type ) {
			if ( is_array( $type ) ) {
				foreach ( $type as $counted ) {
					if ( 0 < (int) $counted ) {
						$has_contents = true;
					}
				}
			}
			if ( ! $has_contents ) {
				unset( $cart[ $id ] );
			}
		}

		$updated = mt_save_data( $cart, 'cart', true );
	}

	return array(
		'success' => $updated,
		'cart'    => $cart,
	);
}

/**
 * Checks whether a given event is currently represented in user's cart.
 *
 * @param int $event_id event ID.
 * @param int $user_ID user ID.
 *
 * @return bool|int
 */
function mt_in_cart( $event_id, $user_ID = false ) {
	$cart = mt_get_cart( $user_ID );
	if ( isset( $cart[ $event_id ] ) ) {
		return $cart[ $event_id ];
	}

	return false;
}