Tribe__Tickets_Plus__QR

Class Tribe__Tickets_Plus__QR

Contents


Source

File: src/Tribe/QR.php

class Tribe__Tickets_Plus__QR {

	public function __construct() {
		add_filter( 'init', array( $this, 'handle_redirects' ), 10    );
		add_filter( 'admin_notices', array( $this, 'admin_notice' ), 10    );
		add_action( 'tribe_tickets_ticket_email_ticket_bottom', array( $this, 'inject_qr' ) );
	}

	/**
	 * Processes the links coming from QR codes and decides what to do:
	 *   - If the user is logged in and has proper permissions, it will redirect
	 *     to the attendees screen for the event, and will automatically check in the user.
	 *
	 *   - If the user is not logged in and/or does not have proper permissions, it will
	 *     redirect to the homepage of the event (front end single event view).
	 */
	public function handle_redirects() {

		// Check if it's our time to shine.
		// Not as fancy as a custom permalink handler, but way less likely to fail depending on setup and settings
		if ( ! isset( $_GET['event_qr_code'] ) ) {
			return;
		}

		// Check all the data we need is there
		if ( empty( $_GET['ticket_id'] ) || empty( $_GET['event_id'] ) ) {
			return;
		}

		// Make sure we don't fail too hard
		if ( ! class_exists( 'Tribe__Tickets__Tickets_Handler' ) ) {
			return;
		}


		$event_id      = (int) $_GET['event_id'];
		$ticket_id     = (int) $_GET['ticket_id'];
		$security_code = (string) isset( $_GET['security_code'] ) ? esc_attr( $_GET['security_code'] ) : '';

		// See if the user had access or not to the checkin process
		$checkin_arr = $this->authorized_checkin( $event_id, $ticket_id, $security_code );

		/**
		 * Filters the redirect URL if the user can access the QR checkin
		 *
		 * @param string $url             URL to redirect to, gets escaped upstream
		 * @param int    $event_id        Event Post ID
		 * @param int    $ticket_id       Ticket Post ID
		 * @param bool   $user_had_access Whether or not the logged-in user has permission to perform check ins
		 */
		$url = apply_filters( 'tribe_tickets_plus_qr_handle_redirects', $checkin_arr['url'], $event_id, $ticket_id, $checkin_arr['user_had_access'] );

		wp_redirect( esc_url_raw( $url ) );
		exit;
	}

	/**
	 * Check if user is authorized to Checkin Ticket
	 *
	 * @since 4.8.1
	 *
	 * @param $event_id      int event post ID
	 * @param $ticket_id     int ticket tost ID
	 * @param $security_code string ticket security code
	 *
	 * @return array
	 */
	public function authorized_checkin( $event_id, $ticket_id, $security_code ) {

		if ( ! is_user_logged_in() || ! current_user_can( 'edit_posts' ) ) {
			$checkin_arr = array(
				'url'             => get_permalink( $event_id ),
				'user_had_access' => false,
			);

			return $checkin_arr;
		}

		$post = get_post( $event_id );

		if ( empty( $post ) ) {
			return array( 'url' => '', 'user_had_access' => true );
		}

		/**
		 * Filters the check for security code when checking in a ticket
		 *
		 * @param false bool the default is not to check security code
		 */
		$check_security_code = apply_filters( 'tribe_tickets_plus_qr_check_security_code', false );

		$service_provider = tribe( 'tickets.data_api' )->get_ticket_provider( $ticket_id );

		//if check_security_code and security key does not match do not checkin and redirect with message
		if ( $check_security_code &&
			(
				empty( $service_provider->security_code ) ||
				$security_code !== get_post_meta( $ticket_id, $service_provider->security_code, true )
			)
		) {

			$url = add_query_arg(
				array(
					'post_type'              => $post->post_type,
					'page'                   => tribe( 'tickets.attendees' )->slug(),
					'event_id'               => $event_id,
					'qr_checked_in'          => $ticket_id,
					'no_security_code_match' => true,
				), admin_url( 'edit.php' )
			);

			$checkin_arr = array(
				'url'             => $url,
				'user_had_access' => true,
			);

			return $checkin_arr;

		}

		// If the user is the site owner (or similar), Check in the user to the event
		$this->_check_in( $ticket_id );

		$url = add_query_arg(
			array(
				'post_type'     => $post->post_type,
				'page'          => tribe( 'tickets.attendees' )->slug(),
				'event_id'      => $event_id,
				'qr_checked_in' => $ticket_id,
			), admin_url( 'edit.php' )
		);

		$checkin_arr = array(
			'url'             => $url,
			'user_had_access' => true,
		);

		return $checkin_arr;
	}


	/**
	 * Show a notice so the user knows the ticket was checked in.
	 */
	public function admin_notice() {

		if ( empty( $_GET['qr_checked_in'] ) ) {
			return;
		}

		// Use Human-readable ID Where Available for QR Check in Message.
		$ticket_id        = absint( $_GET['qr_checked_in'] );
		$no_match         = isset( $_GET['no_security_code_match'] ) ? absint( $_GET['no_security_code_match'] ) : false;
		$ticket_status    = get_post_status( $ticket_id );
		$checked_status   = get_post_meta( $ticket_id, '_tribe_qr_status', true );
		$ticket_unique_id = get_post_meta( $ticket_id, '_unique_id', true );
		$ticket_id        = $ticket_unique_id === '' ? $ticket_id : $ticket_unique_id;

		// If the attendee was deleted.
		if ( false === $ticket_status || 'trash' === $ticket_status ) {

			echo '<div class="error"><p>';
				printf( esc_html__( 'The ticket with ID %s was deleted and cannot be checked-in.', 'event-tickets-plus' ), esc_html( $ticket_id ) );
			echo '</p></div>';

		// If Security Code does not match
		} elseif ( $no_match ) {
			echo '<div class="error"><p>';
				printf( esc_html__( 'The security code for ticket with ID %s does not match.', 'event-tickets-plus' ), esc_html( $ticket_id ) );
			echo '</p></div>';

		// If status is QR then display already checked-in warning.
		} elseif ( $checked_status ) {

			echo '<div class="error"><p>';
				printf( esc_html__( 'The ticket with ID %s has already been checked in.', 'event-tickets-plus' ), esc_html( $ticket_id ) );
			echo '</p></div>';

		// Otherwise, just check-in like normal.
		} else {

			echo '<div class="updated"><p>';
				printf( esc_html__( 'The ticket with ID %s was checked in.', 'event-tickets-plus' ), esc_html( $ticket_id ) );
			echo '</p></div>';

			// Update the checked-in status when using the QR code here.
			update_post_meta( absint( $_GET['qr_checked_in'] ), '_tribe_qr_status', 1 );
		}
	}

	/**
	 * Generates the QR image, stores is locally and injects it into the tickets email
	 *
	 * @param $ticket array
	 *
	 * @return string
	 */
	public function inject_qr( $ticket ) {
		// if gzuncompress doesn't exist, we can't render QR codes
		if ( ! function_exists( 'gzuncompress' ) ) {
			Tribe__Main::instance()->log_warning( __( 'Could not render QR code because gzuncompress() is not available', 'event-tickets-plus' ), __CLASS__ );
			return;
		}

		$enabled = tribe_get_option( 'tickets-enable-qr-codes', true );

		/**
		 * Filters the QR enabled value
		 *
		 * @since 4.8.2
		 *
		 * @param bool   $enabled       The bool that comes from the options
		 * @param array  $ticket        The ticket
		 */
		$enabled = apply_filters( 'tribe_tickets_plus_qr_enabled', $enabled, $ticket );

		if ( empty( $enabled ) ) {
			return;
		}

		$link = $this->_get_link( $ticket['qr_ticket_id'], $ticket['event_id'], $ticket['security_code'] );
		$qr   = $this->_get_image( $link );

		if ( ! $qr ) {
			return;
		}

		// echo QR template for email
		tribe_tickets_get_template_part( 'tickets-plus/email-qr', null, array( 'qr' => $qr ), true );
	}


	/**
	 * Generates the link for the QR image
	 *
	 * @param $ticket_id
	 * @param $event_id
	 *
	 * @return string
	 */
	private function _get_link( $ticket_id, $event_id, $security_code ) {

		/**
		 * Allows filtering the base URL which QR code query args are appended to. Defaults to
		 * the site's home_url() with a trailing slash.
		 *
		 * @since 4.7.3
		 *
		 * @param string $url
		 * @param int $ticket_id
		 * @param int $event_id
		 */
		$base_url = apply_filters( 'tribe_tickets_qr_code_base_url', home_url( '/' ), $ticket_id, $event_id );

		$url = add_query_arg( 'event_qr_code', 1, $base_url );
		$url = add_query_arg( 'ticket_id', $ticket_id, $url );
		$url = add_query_arg( 'event_id', $event_id, $url );
		$url = add_query_arg( 'security_code', $security_code, $url );

		// add REST API QR Endpoint Path
		if ( function_exists( 'tribe_tickets_rest_url_prefix' ) ) {
			$url = add_query_arg( 'path', urlencode( tribe_tickets_rest_url_prefix() . '/qr' ), $url );
		}

		return $url;
	}

	/**
	 * Generates the QR image for a given link and stores it in /wp-content/uploads.
	 * Returns the link to the new image.
	 *
	 * @param $link
	 *
	 * @return string
	 */
	private function _get_image( $link ) {
		if ( ! function_exists( 'ImageCreate' ) ) {
			// The phpqrcode library requires GD but doesn't actually check if it is available
			return null;
		}
		if ( ! class_exists( 'QRencode' ) ) {
			include_once( EVENT_TICKETS_PLUS_DIR . '/vendor/phpqrcode/qrlib.php' );
		}

		$uploads   = wp_upload_dir();
		$file_name = 'qr_' . md5( $link ) . '.png';
		$path      = trailingslashit( $uploads['path'] ) . $file_name;
		$url       = trailingslashit( $uploads['url'] ) . $file_name;

		if ( ! file_exists( $path ) ) {
			QRcode::png( $link, $path, QR_ECLEVEL_L, 3 );
		}

		return $url;
	}

	/**
	 * Checks the user in, for all the *Tickets modules running.
	 *
	 * @param $ticket_id
	 */
	private function _check_in( $ticket_id ) {
		$modules = Tribe__Tickets__Tickets::modules();

		foreach ( $modules as $class => $module ) {
			if ( ! is_callable( array( $class, 'get_instance' ) ) ) {
				continue;
			}
			$obj = call_user_func( array( $class, 'get_instance' ) );
			$obj->checkin( $ticket_id, false );
		}
	}
}

Top ↑

Methods

  • __construct
  • admin_notice — Show a notice so the user knows the ticket was checked in.
  • authorized_checkin — Check if user is authorized to Checkin Ticket
  • get_qr_url — Get QR Code URL from ticket.
  • handle_redirects — Processes the links coming from QR codes and decides what to do: - If the user is logged in and has proper permissions, it will redirect to the attendees screen for the event, and will automatically check in the user.
  • inject_qr — Generates the QR image, stores is locally and injects it into the tickets email