<?php
/**
* Create and send notifications.
*
* @category Core
* @package My Tickets
* @author Joe Dolson
* @license GPLv2 or later
* @link https://www.joedolson.com/my-tickets/
*/
// if a post is trashed, return the tickets to pool.
// Trashing a payment is *not* a refund; no notifications are sent.
add_action( 'wp_trash_post', 'mt_return_tickets_action' );
/**
* Return reserved tickets to pool if a payment is trashed.
*
* @param int $id Payment ID.
*/
function mt_return_tickets_action( $id ) {
$type = get_post_type( $id );
if ( 'mt-payments' === $type && ( false !== get_post_status( $id ) ) ) {
mt_return_tickets( $id );
}
}
add_action( 'save_post', 'mt_generate_notifications', 15 );
/**
* Send payment notifications to admin and purchaser when a payment is transitioned to published.
*
* @param int $id Payment ID.
*/
function mt_generate_notifications( $id ) {
$type = get_post_type( $id );
$status = 'quo';
if ( 'mt-payments' === $type ) {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE || wp_is_post_revision( $id ) ) {
return;
}
$post = get_post( $id );
if ( 'publish' !== $post->post_status ) {
return;
}
$email_sent = get_post_meta( $id, '_notified', true );
$last_status = get_post_meta( $id, '_last_status', true );
$paid = get_post_meta( $id, '_is_paid', true );
if ( ! $email_sent || isset( $_POST['_send_email'] ) || ( 'Pending' === $last_status && 'Completed' === $paid ) ) {
$resend = ( isset( $_POST['_send_email'] ) ) ? true : false;
$details['email'] = get_post_meta( $id, '_email', true );
$details['name'] = get_the_title( $id );
$details['id'] = $id;
// only send this if email is provided; otherwise send notice to admin.
if ( ! ( is_email( $details['email'] ) ) ) {
$details['email'] = get_option( 'admin_email' );
$status = 'invalid_email';
}
mt_send_notifications( $paid, $details, $status, $resend );
}
}
return;
}
add_filter( 'mt_format_array', 'mt_format_array', 10, 5 );
/**
* Format array data for use in email notifications.
*
* @param string $output text output.
* @param string $type Type of display.
* @param array $data Data to display.
* @param int $purchase_id Payment ID.
* @param string $context Admin or email.
*
* @return string
*/
function mt_format_array( $output, $type, $data, $purchase_id, $context = 'admin' ) {
if ( is_array( $data ) ) {
switch ( $type ) {
case 'purchase':
$output = mt_format_purchase( $data, false, $purchase_id );
break;
case 'address':
$output = mt_format_address( $data );
break;
case 'tickets':
$output = mt_format_tickets( $data, 'text', $purchase_id, $context );
break;
case 'ticket_ids':
$output = mt_format_tickets( $data, 'ids', $purchase_id, $context );
break;
}
}
return $output;
}
/**
* Get the permalink to an event.
*
* @param int $event_id Post ID for the ticketed event.
*
* @return string URL to event.
*/
function mt_get_event_link( $event_id ) {
$url = get_the_permalink( $event_id );
/**
* Filter the link to a ticketed event.
*
* @hook mt_get_event_link
*
* @param {string} $url Event permalink.
* @param {int} $event_id Event Post ID.
*
* @return {string}
*/
$url = apply_filters( 'mt_get_event_link', $url, $event_id );
return $url;
}
/**
* Format purchase data for use in email notifications. (Basically, simplified version of cart output.)
*
* @param array $purchase Purchase data.
* @param bool $format Text or HTML.
* @param mixed integer/boolean $purchase_id Payment ID.
*
* @return string
*/
function mt_format_purchase( $purchase, $format = false, $purchase_id = false ) {
$output = '';
$options = mt_get_settings();
// format purchase.
$is_html = ( 'true' === $options['mt_html_email'] || 'html' === $format ) ? true : false;
$sep = ( $is_html ) ? '<br />' : "\n";
if ( ! $purchase ) {
$output = __( 'Your ticket information will be available once your payment is completed.', 'my-tickets' );
} else {
$total = 0;
$ids = array();
foreach ( $purchase as $event ) {
foreach ( $event as $event_id => $tickets ) {
if ( in_array( $event_id, $ids, true ) ) {
continue;
}
$ids[] = $event_id;
if ( false === get_post_status( $event_id ) ) {
// This event does not exist.
return __( 'This event has been deleted.', 'my-tickets' );
}
$handling = get_post_meta( $purchase_id, '_ticket_handling', true );
if ( ! ( $handling && is_numeric( $handling ) ) ) {
$handling = 0;
}
$handling = (float) $handling;
$title = ( $is_html ) ? '<strong>' . get_the_title( $event_id ) . '</strong>' : get_the_title( $event_id );
$title = ( $is_html ) ? "<a href='" . mt_get_event_link( $event_id ) . "'>" . $title . '</a>' : $title;
$event = get_post_meta( $event_id, '_mc_event_data', true );
$registration = get_post_meta( $event_id, '_mt_registration_options', true );
$counting = $registration['counting_method'];
if ( ! is_array( $event ) ) {
continue; // This event may no longer have event data on it, and needs to be skipped.
}
$date = date_i18n( get_option( 'date_format' ), strtotime( $event['event_begin'] ) );
$time = date_i18n( get_option( 'time_format' ), strtotime( $event['event_time'] ) );
$general = ( isset( $event['general_admission'] ) && 'on' === $event['general_admission'] ) ? true : false;
$validity = ( isset( $event['event_valid'] ) ) ? $event['event_valid'] : 0;
if ( 'expire' === $validity && isset( $event['expire_date'] ) && ! empty( $event['expire_date'] ) ) {
$valid_dt = $event['expire_date'];
} else {
$purchase_date = get_the_date( 'Y-m-d', $purchase_id );
$valid_dt = ( 'infinite' !== $validity ) ? strtotime( $purchase_date . ' + ' . $validity ) : '';
}
if ( 'infinite' === $validity ) {
$valid_til = __( 'Ticket does not expire', 'my-tickets' );
} else {
$valid_til = mt_date( get_option( 'date_format' ), $valid_dt );
// Translators: Date ticket valid until.
$valid_til = ( $general ) ? sprintf( __( 'Tickets valid until %s', 'my-tickets' ), $valid_til ) : $event['event_begin'] . ' ' . $event['event_time'];
}
$handling_notice = '';
$tickets_list = '';
foreach ( $tickets as $type => $ticket ) {
if ( $ticket['count'] > 0 ) {
$price = (float) $ticket['price'];
$orig = mt_get_original_ticket_price( $event_id, $type );
if ( 'event' === $counting ) {
// At this stage, the event date is parsed by sanitize_title, and contains an extra hyphen.
$type = substr_replace( $type, ' ', 10, 1 );
$type = date_i18n( get_option( 'date_format' ) . ' @ ' . get_option( 'time_format' ), strtotime( $type ) );
} else {
$type = apply_filters( 'mt_ticket_type_label', ucfirst( str_replace( '-', ' ', $type ) ) );
}
$price = $price - $handling;
$discount = ( $price !== $orig ) ? $price : mt_calculate_discount( $price, $event_id, $purchase_id );
$total = ( $discount !== $price ) ? $total + $discount * $ticket['count'] : $total + $price * $ticket['count'];
$display_app = '';
// Match formats so comparison is valid.
if ( sprintf( '%01.2f', $orig ) !== sprintf( '%01.2f', $price ) ) {
// Translators: original ticket price, before discounts.
$discount = strip_tags( apply_filters( 'mt_money_format', $orig ) );
// Translators: discounted cost of ticket.
$display_app = ' (' . sprintf( __( 'Discounted from %s', 'my-tickets' ), $discount ) . ')';
}
$display_price = strip_tags( apply_filters( 'mt_money_format', $price ) ) . $display_app;
if ( $handling ) {
// Translators: price of ticket handling charge.
$handling_notice = ' ' . apply_filters( 'mt_handling_charge_of', sprintf( __( '(Per-ticket handling charge of %s)', 'my-tickets' ), apply_filters( 'mt_money_format', $handling ) ) );
}
if ( $is_html ) {
// translators: Type of tickets, cost of tickets, price of tickets.
$tickets_list .= sprintf( _n( '%1$s: %3$d ticket at %2$s', '%1$s: %3$d tickets at %2$s', $ticket['count'], 'my-tickets' ), '<strong>' . $type . '</strong>', $display_price, $ticket['count'] ) . $handling_notice . $sep;
} else {
// translators: type of tickets, cost of tickets, price of tickets.
$tickets_list .= sprintf( _n( '%1$s: %3$d ticket at %2$s', '%1$s: %3$d tickets at %2$s', $ticket['count'], 'my-tickets' ), $type, $display_price, $ticket['count'] ) . $handling_notice . $sep;
}
}
}
if ( '' !== trim( $tickets_list ) ) {
if ( $general ) {
$output .= sprintf( apply_filters( 'mt_purchased_ga_tickets_format', '%1$s - %2$s', $is_html, $event ), $title, $valid_til ) . $sep;
} else {
if ( 'event' === $counting ) {
$output .= sprintf( apply_filters( 'mt_purchased_tickets_format', '%1$s', $is_html, $event ), $title ) . $sep;
} else {
$output .= sprintf( apply_filters( 'mt_purchased_tickets_format', '%1$s - %2$s @ %3$s', $is_html, $event ), $title, $date, $time ) . $sep;
}
}
$output .= apply_filters( 'mt_custom_tickets_fields', '', $event_id, $purchase_id, $sep );
$output .= $sep . $tickets_list;
}
}
}
$output .= $sep;
$total = apply_filters( 'mt_apply_total_discount', $total, $purchase_id );
if ( $is_html ) {
$output = wpautop( $output . '<hr><strong>' . __( 'Ticket Total', 'my-tickets' ) . '</strong>: ' . strip_tags( apply_filters( 'mt_money_format', $total ) ) );
} else {
$output .= $sep . __( 'Ticket Total', 'my-tickets' ) . ': ' . strip_tags( apply_filters( 'mt_money_format', $total ) ) . $sep;
}
}
return $output;
}
/**
* Format shipping address data for use in email notifications.
*
* @param array $address Address data.
*
* @return string
*/
function mt_format_address( $address ) {
// format address.
$output = '';
if ( $address ) {
$options = mt_get_settings();
$sep = ( 'true' === $options['mt_html_email'] ) ? '<br />' : PHP_EOL;
foreach ( $address as $value ) {
$separator = ( '' === trim( $value ) ) ? '' : $sep;
$output .= $value . $separator;
}
return ( 'true' === $options['mt_html_email'] ) ? wpautop( $output ) : PHP_EOL . $output . PHP_EOL;
}
return $output;
}
/**
* Format ticket data for use in email notifications.
*
* @param array $tickets Tickets to format.
* @param string $type Type of display.
* @param int $purchase_id Payment ID.
* @param string $context Admin or email.
*
* @return string
*/
function mt_format_tickets( $tickets, $type = 'text', $purchase_id = false, $context = 'admin' ) {
if ( ! $purchase_id ) {
return '';
}
$options = mt_get_settings();
$output = '';
$show = '';
$move = '';
$is_html = ( 'true' === $options['mt_html_email'] || 'html' === $type ) ? true : false;
$sep = ( $is_html ) ? '<br />' . "\r\n" : "\n";
$total = count( $tickets );
$i = 1;
$test_use = false;
if ( is_admin() && ( current_user_can( 'mt-verify-ticket' ) || current_user_can( 'manage_options' ) ) ) {
if ( isset( $_GET['ticket-action'] ) && isset( $_GET['ticket'] ) ) {
$ticket_id = sanitize_text_field( $_GET['ticket'] );
if ( 'checkin' === $_GET['ticket-action'] ) {
add_post_meta( $purchase_id, '_tickets_used', $ticket_id );
} else {
delete_post_meta( $purchase_id, '_tickets_used', $ticket_id );
}
}
$used = get_post_meta( $purchase_id, '_tickets_used' );
$test_use = true;
}
$options = mt_get_settings();
$ticket_url = get_permalink( $options['mt_tickets_page'] );
foreach ( $tickets as $ticket ) {
if ( $test_use ) {
$ticket_id = str_replace( array( $ticket_url . '&ticket_id=', $ticket_url . '?ticket_id=' ), '', $ticket );
if ( is_array( $used ) ) {
$is_used = in_array( $ticket_id, $used, true );
$action = add_query_arg(
array(
'action' => 'edit',
'post' => $purchase_id,
'ticket' => $ticket_id,
),
admin_url( 'post.php' )
);
$checkin = add_query_arg( 'ticket-action', 'checkin', $action );
$undo = add_query_arg( 'ticket-action', 'undo', $action );
if ( 'admin' === $context ) {
$show = ( $is_used ) ? " <span class='dashicons dashicons-yes' aria-hidden='true'></span><a href='" . esc_url( $undo ) . "'>" . __( 'Checked in', 'my-tickets' ) . '</a> ' : " <span class='dashicons dashicons-edit' aria-hidden='true'></span><a href='" . esc_url( $checkin ) . "'>" . __( 'Check-in', 'my-tickets' ) . '</a>';
$show = '<div>' . $show . '</div>';
}
}
if ( 'admin' === $context ) {
$event_id = mt_get_ticket( $ticket_id )->ID;
$prices = mt_get_prices( $event_id );
$type_options = '';
$ticket_data = get_post_meta( $event_id, '_' . $ticket_id, true );
$ticket_type = isset( $ticket_data['type'] ) ? $ticket_data['type'] : '';
foreach ( $prices as $key => $type ) {
if ( $ticket_type === $key ) {
continue;
}
$type_options .= '<option value="' . $key . '">' . $type['label'] . '</option>';
}
// Translators: 1) type of ticket, 2) event ticket sold for, 3) Event time.
$status = sprintf( __( 'Move %1$s ticket (%2$s, %3$s) to a different event', 'my-tickets' ), mt_get_ticket_type( $ticket_id ), get_the_title( $event_id ), mt_get_event_time( $ticket_id ) );
$move = "<button type='button' class='edit-ticket button-secondary' aria-expanded='false' aria-controls='mt-edit-tickets-$i'>" . __( 'Edit', 'my-tickets' ) . '</button>';
$move_form = '<div class="mt-move-tickets-wrapper">
<div class="mt-move-tickets" id="mt-edit-tickets-' . $i . '">
<div class="mt-ticket-moved-response" aria-live="polite">' . $status . '</div>
<div class="mt-move-tickets-inner">
<label for="mt-move-tickets-choose-' . $i . '">Move to Event <i><span aria-live="assertive"></span></i></label>
<input type="text" placeholder="Search term" id="mt-move-tickets-choose-' . $i . '" class="suggest widefat mt-move-tickets-target" name="mt-event-target" value="" />
</div>
<div class="mt-move-tickets-inner">
<label for="mt-switch-ticket-type">' . __( 'Change ticket group', 'my-tickets' ) . '</label>
<select id="mt-switch-ticket-type" class="widefat mt-switch-ticket-type">
<option value="none">' . __( 'No change', 'my-tickets' ) . '</option>
' . $type_options . '
</select>
</div>
<button type="button" data-payment="' . $purchase_id . '" data-event="' . $event_id . '" data-ticket="' . $ticket_id . '" class="mt-move-tickets-button button-secondary">' . __( 'Update Ticket', 'my-tickets' ) . '</button>
<button type="button" data-payment="' . $purchase_id . '" data-event="' . $event_id . '" data-ticket="' . $ticket_id . '" class="mt-delete-ticket-button button-secondary"><span class="dashicons dashicons-no" aria-hidden="true"></span> ' . __( 'Delete Ticket', 'my-tickets' ) . '</button>
</div>
</div>';
}
}
if ( 'ids' === $type ) {
$ticket_output = "$i/$total: $ticket" . $show . $move . $sep;
} else {
$ticket = ( $is_html ) ? "<a href='$ticket'>" . __( 'View Ticket', 'my-tickets' ) . " ($i/$total)</a>" : $ticket;
$ticket_output = "$i/$total: " . $ticket . $show . $move . $sep;
$ticket_output = ( 'admin' === $context ) ? '<li><div class="controls">' . $ticket . $show . $move . '</div>' . $move_form . '</li>' : $ticket_output;
}
$output .= apply_filters( 'mt_custom_ticket_output', $ticket_output, $purchase_id, $sep );
++$i;
}
$output = ( 'admin' === $context ) ? '<ul class="admin-tickets">' . $output . '</ul>' : $output;
return $output;
}
add_filter( 'mt_format_receipt', 'mt_format_receipt' );
/**
* Generate link to receipt for email notifications.
*
* @param string $receipt Receipt URL.
*
* @return string
*/
function mt_format_receipt( $receipt ) {
$options = mt_get_settings();
if ( 'true' === $options['mt_html_email'] ) {
$receipt = "<a href='$receipt'>" . __( 'View your receipt for this purchase', 'my-tickets' ) . '</a>';
}
return $receipt;
}
/**
* Send notifications to purchaser and admin.
*
* @param string $status Payment status.
* @param array $details Payment information.
* @param bool $error Error thrown.
* @param bool $resending Resending this notification.
*/
function mt_send_notifications( $status = 'Completed', $details = array(), $error = false, $resending = false ) {
$options = mt_get_settings();
$blogname = get_option( 'blogname' );
$subject = '';
$body = '';
$subject2 = '';
$body2 = '';
$send = true;
$id = $details['id']; // Purchase id.
$gateway = get_post_meta( $id, '_gateway', true );
$notes = ( ! empty( $options['mt_gateways'][ $gateway ]['notes'] ) ) ? $options['mt_gateways'][ $gateway ]['notes'] : '';
$phone = get_post_meta( $id, '_phone', true );
$vat = get_post_meta( $id, '_vat', true );
// Restructure post meta array to match cart array.
if ( ( 'Completed' === $status || ( 'Pending' === $status && 'offline' === $gateway ) ) && ! $resending ) {
mt_create_tickets( $id );
}
$purchased = get_post_meta( $id, '_purchased' );
$purchase_data = get_post_meta( $id, '_purchase_data', true );
$ticket_array = mt_setup_tickets( $purchased, $id );
$handling = mt_get_cart_handling( $options, $gateway );
$shipping = ( isset( $options['mt_shipping'] ) ) ? floatval( $options['mt_shipping'] ) : 0;
$total = mt_calculate_cart_cost( $purchase_data, $id ) + $handling + $shipping;
$hash = md5(
add_query_arg(
array(
'post_type' => 'mt-payments',
'p' => $id,
),
home_url()
)
);
$receipt = add_query_arg( 'receipt_id', $hash, get_permalink( $options['mt_receipt_page'] ) );
$transaction_id = get_post_meta( $id, '_transaction_id', true );
if ( 'Completed' === $status ) {
$amount_due = '0.00';
} else {
$amount_due = $total;
}
$amount_due = strip_tags( apply_filters( 'mt_money_format', $amount_due ) );
$total = strip_tags( apply_filters( 'mt_money_format', $total ) );
$transaction_data = get_post_meta( $id, '_transaction_data', true );
$address = ( isset( $transaction_data['shipping'] ) ) ? $transaction_data['shipping'] : false;
$ticketing_method = get_post_meta( $id, '_ticketing_method', true );
$email = $details['email'];
if ( 'eticket' === $ticketing_method || 'printable' === $ticketing_method ) {
$tickets = apply_filters( 'mt_format_array', '', 'tickets', $ticket_array, $id, 'email' );
$ticket_ids = apply_filters( 'mt_format_array', '', 'ticket_ids', array_keys( $ticket_array ), $id, 'email' );
update_post_meta( $id, '_is_delivered', 'true' );
} else {
$tickets = ( 'willcall' === $ticketing_method ) ? __( 'Your tickets will be available at the box office.', 'my-tickets' ) : __( 'Your tickets will be mailed to you at the address provided.', 'my-tickets' );
$tickets = ( 'true' === $options['mt_html_email'] ) ? '<p>' . $tickets . '</p>' : $tickets;
$ticket_ids = '';
}
$bulk_tickets = ( 'printable' === $ticketing_method ) ? add_query_arg(
array(
'receipt_id' => $hash,
'multiple' => true,
),
get_permalink( $options['mt_tickets_page'] )
) : '';
// Get translatable name of ticketing method.
$method_options = mt_default_fields()['ticketing_method']['choices'];
$friendly_method = ( isset( $method_options[ $ticketing_method ] ) ) ? $method_options[ $ticketing_method ] : $ticketing_method;
$purchases = apply_filters( 'mt_format_array', '', 'purchase', $purchased, $id, 'email' );
$data = array(
'receipt' => apply_filters( 'mt_format_receipt', $receipt ),
'tickets' => $tickets,
'ticket_ids' => $ticket_ids,
'name' => $details['name'],
'blogname' => $blogname,
'total' => $total,
'key' => $hash,
'purchase' => $purchases,
'address' => apply_filters( 'mt_format_array', '', 'address', $address, $id, 'email' ),
'transaction' => apply_filters( 'mt_format_array', '', 'transaction', $transaction_data, $id, 'email' ),
'transaction_id' => $transaction_id,
'amount_due' => $amount_due,
'handling' => apply_filters( 'mt_money_format', $handling ),
'shipping' => apply_filters( 'mt_money_format', $shipping ),
'method' => $friendly_method,
'phone' => $phone,
'vat' => $vat,
'purchase_ID' => $id,
'purchase_edit' => get_edit_post_link( $id, 'email' ),
'gateway_notes' => $notes,
'buyer_email' => $email,
'event_notes' => apply_filters( 'mt_format_notes', '', $purchased, $id ),
'bulk_tickets' => $bulk_tickets,
);
$custom_fields = mt_get_custom_fields( 'notify' );
foreach ( $custom_fields as $name => $field ) {
$info = get_post_meta( $id, $name, true );
$event = isset( $info['event_id'] ) ? $info['event_id'] : false;
if ( ! $event ) {
continue;
}
$value = $info[ $name ];
$data[ $name ] = call_user_func( $field['display_callback'], $value, $event, $field );
}
$data = apply_filters( 'mt_notifications_data', $data, $details );
$headers[] = "From: $blogname Events <" . $options['mt_from'] . '>';
$headers[] = "Reply-to: $options[mt_from]";
if ( 'Completed' === $status || ( 'Pending' === $status && 'offline' === $gateway ) ) {
$append = '';
if ( 'invalid_email' === $error ) {
$append = __( 'Purchaser did not provide valid email', 'my-tickets' );
}
if ( ! empty( $options['messages']['interim']['purchaser']['subject'] ) && ! empty( $options['messages']['interim']['purchaser']['body'] ) && 'Pending' === $status && 'offline' === $gateway ) {
$purchaser_subject = $options['messages']['interim']['purchaser']['subject'];
$purchaser_body = $options['messages']['interim']['purchaser']['body'];
$admin_subject = $options['messages']['interim']['admin']['subject'];
$admin_body = $options['messages']['interim']['admin']['body'];
} else {
$purchaser_subject = $options['messages']['completed']['purchaser']['subject'];
$purchaser_body = $options['messages']['completed']['purchaser']['body'];
$admin_subject = $options['messages']['completed']['admin']['subject'];
$admin_body = $options['messages']['completed']['admin']['body'];
}
$subject = mt_draw_template( $data, $purchaser_subject );
$subject2 = mt_draw_template( $data, $admin_subject );
$body = mt_draw_template( $data, $append . $purchaser_body );
$body2 = mt_draw_template( $data, $admin_body );
}
if ( 'Refunded' === $status ) {
$subject = mt_draw_template( $data, $options['messages']['refunded']['purchaser']['subject'] );
$subject2 = mt_draw_template( $data, $options['messages']['refunded']['admin']['subject'] );
$body = mt_draw_template( $data, $options['messages']['refunded']['purchaser']['body'] );
$body2 = mt_draw_template( $data, $options['messages']['refunded']['admin']['body'] );
// put tickets purchased on this registration back on event.
mt_return_tickets( $id );
}
if ( 'Turned Back' === $status ) {
// No notifications, just cancelled.
mt_return_tickets( $id );
}
if ( 'Failed' === $status ) {
$subject = mt_draw_template( $data, $options['messages']['failed']['purchaser']['subject'] );
$subject2 = mt_draw_template( $data, $options['messages']['failed']['admin']['subject'] );
$body = mt_draw_template( $data, $options['messages']['failed']['purchaser']['body'] );
$body2 = mt_draw_template( $data, $options['messages']['failed']['admin']['body'] );
}
if ( 'Pending' === $status || ( false !== strpos( $status, 'Other' ) ) ) {
if ( 'Pending' === $status && 'offline' === $gateway ) {
// For offline payments, we do send notifications.
$send = true;
} else {
// No messages sent while status is pending or for 'Other' statuses.
$send = false;
}
}
if ( $send ) {
if ( 'true' === $options['mt_html_email'] ) {
add_filter( 'wp_mail_content_type', 'mt_html_type' );
$body = mt_html_email_header( $data ) . $body . mt_html_email_footer( $data );
}
// message to purchaser.
$body = apply_filters( 'mt_modify_email_body', $body, 'purchaser' );
// Log this message.
add_post_meta(
$id,
'_mt_send_email',
array(
'body' => $body,
'subject' => $subject,
'date' => mt_current_time(),
)
);
$sent = wp_mail( $email, $subject, $body, $headers );
if ( ! $sent ) {
// If mail sends, try without custom headers.
wp_mail( $email, $subject, $body );
}
// message to admin.
$body2 = apply_filters( 'mt_modify_email_body', $body2, 'admin' );
$admin_sent = wp_mail( $options['mt_to'], $subject2, $body2, $headers );
if ( ! $admin_sent ) {
// If mail send fails, try without custom headers.
wp_mail( $options['mt_to'], $subject2, $body2 );
}
if ( 'true' === $options['mt_html_email'] ) {
remove_filter( 'wp_mail_content_type', 'mt_html_type' );
}
update_post_meta( $id, '_notified', 'true' );
}
}
/**
* Write HTML email header for email body.
*
* @param array $data Email data array.
*
* @return string
*/
function mt_html_email_header( $data ) {
$margin = is_rtl() ? 'rightmargin' : 'leftmargin';
$header = '<!DOCTYPE html>
<html ' . get_language_attributes() . '>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=' . get_bloginfo( 'charset' ) . '" />
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>' . get_bloginfo( 'name', 'display' ) . '</title>
</head>
<body ' . $margin . '="0" marginwidth="0" topmargin="0" marginheight="0" offset="0" style="padding: 0">
<table width="100%" id="outer_wrapper">
<tr>
<td><!-- Deliberately empty to support consistent sizing and layout across multiple email clients. --></td>
<td width="600" id="wrapper">
<div id="wrapper" style="' . mt_html_email_wrapper_styles() . '">
<div id="header"><h1 style="' . mt_html_email_h1_styles() . '">' . $data['blogname'] . '</h1></div>
<div id="body_content" style="' . mt_html_email_body_styles() . '">';
/**
* Filter the header used for HTML email messages.
*
* @hook mt_html_email_header
*
* @param {string} $header HTML header content.
* @param {array} $data Email data array.
*
* @return {string}
*/
return apply_filters( 'mt_html_email_header', $header, $data );
}
/**
* Write HTML email footer for email body.
*
* @param array $data Email data array.
*
* @return string
*/
function mt_html_email_footer( $data ) {
$footer = '
</div>
</div>
</td>
<td></td>
</tr>
</table>
</body>
</html>';
/**
* Filter the footer used for HTML email messages.
*
* @hook mt_html_email_footer
*
* @param {string} $header HTML footer content.
* @param {array} $data Email data array.
*
* @return {string}
*/
return apply_filters( 'mt_html_email_footer', $footer, $data );
}
/**
* Write Inline HTML email styles for email main wrapper.
*/
function mt_html_email_wrapper_styles() {
$styles = 'background-color: #fff;margin: 0;padding: 40px 0;-webkit-text-size-adjust: none !important;width: 100%;max-width: 600px;';
/**
* Filter the wrapper styles used for HTML email messages.
*
* @hook mt_html_email_wrapper_styles
*
* @param {string} $styles HTML styles.
*
* @return {string}
*/
return apply_filters( 'mt_html_email_wrapper_styles', $styles );
}
/**
* Write Inline HTML email styles for email h1.
*/
function mt_html_email_h1_styles() {
$styles = 'color:#000;font-size:32px;font-weight:700;text-align:center;';
/**
* Filter the wrapper styles used for HTML email messages.
*
* @hook mt_html_email_h1_styles
*
* @param {string} $styles HTML styles.
*
* @return {string}
*/
return apply_filters( 'mt_html_email_h1_styles', $styles );
}
/**
* Write Inline HTML email styles for email body.
*/
function mt_html_email_body_styles() {
$styles = 'padding:1rem; background: #f3f3f3; color: #111;';
/**
* Filter the wrapper styles used for HTML email messages.
*
* @hook mt_html_email_h1_styles
*
* @param {string} $styles HTML styles.
*
* @return {string}
*/
return apply_filters( 'mt_html_email_body_styles', $styles );
}
add_filter( 'mt_format_notes', 'mt_create_event_notes', 10, 3 );
/**
* Add event specific notes.
*
* @param string $event_notes Notes to send with notifications.
* @param array $purchased Purchased tickets data.
* @param int $payment_id Payment ID.
*
* @return string
*/
function mt_create_event_notes( $event_notes, $purchased, $payment_id ) {
$options = mt_get_settings();
if ( is_array( $purchased ) ) {
foreach ( $purchased as $event ) {
foreach ( $event as $event_id => $tickets ) {
if ( 'true' === $options['mt_html_email'] ) {
$notes = wpautop( get_post_meta( $event_id, '_mt_event_notes', true ) );
} else {
$notes = get_post_meta( $event_id, '_mt_event_notes', true ) . PHP_EOL . PHP_EOL;
}
$event_notes .= apply_filters( 'mt_event_notes', $notes, $payment_id, $event_id );
}
}
}
return $event_notes;
}
/**
* Draw template from event data. If My Calendar is installed, use the My Calendar template engine.
*
* @param array $data Data to use in a template.
* @param string $template Format structure for template.
*
* @return string
*/
function mt_draw_template( $data, $template ) {
if ( function_exists( 'mc_draw_template' ) ) {
return mc_draw_template( $data, $template );
} else {
$template = stripcslashes( $template );
foreach ( $data as $key => $value ) {
if ( is_object( $value ) && ! empty( $value ) ) {
// null values return false.
} else {
if ( false !== strpos( $template, '{' . $key ) ) {
if ( false !== strpos( $template, '{' . $key . ' ' ) ) { // only do preg_match if appropriate.
preg_match_all( '/{' . $key . '\b(?>\s+(?:before="([^"]*)"|after="([^"]*)"|format="([^"]*)")|[^\s]+|\s+){0,2}}/', $template, $matches, PREG_PATTERN_ORDER );
if ( $matches ) {
$number = count( $matches[0] );
for ( $i = 0; $i < $number; $i++ ) {
$orig = $value;
$before = $matches[1][ $i ];
$after = $matches[2][ $i ];
$format = $matches[3][ $i ];
if ( '' !== $format ) {
$value = date_i18n( stripslashes( $format ), strtotime( stripslashes( $value ) ) );
}
$value = ( '' === $value ) ? '' : $before . $value . $after;
$search = $matches[0][ $i ];
$template = str_replace( $search, $value, $template );
$value = $orig;
}
}
} else { // don't do preg match (never required for RSS).
$template = stripcslashes( str_replace( '{' . $key . '}', $value, $template ) );
}
} // end {$key check.
}
}
return stripslashes( trim( $template ) );
}
}
/**
* Return tickets to available ticket pool if Payment is refunded or trashed.
*
* @param int $payment_id Payment ID.
*/
function mt_return_tickets( $payment_id ) {
$purchases = get_post_meta( $payment_id, '_purchased' );
if ( is_array( $purchases ) ) {
foreach ( $purchases as $key => $value ) {
foreach ( $value as $event_id => $purchase ) {
if ( false !== get_post_status( $event_id ) ) {
$registration = get_post_meta( $event_id, '_mt_registration_options', true );
foreach ( $purchase as $type => $ticket ) {
if ( ! isset( $registration['prices'][ $type ] ) ) {
// If this ticket type was removed, it could trigger a fatal error.
continue;
}
// add ticket hash for each ticket.
$count = $ticket['count'];
$price = $ticket['price'];
$sold = $registration['prices'][ $type ]['sold'];
$new_sold = $sold - $count;
$registration['prices'][ $type ]['sold'] = $new_sold;
update_post_meta( $event_id, '_mt_registration_options', $registration );
for ( $i = 0; $i < $count; $i++ ) {
// delete tickets from system.
$ticket_id = mt_generate_ticket_id( $payment_id, $event_id, $type, $i, $price );
delete_post_meta( $event_id, '_ticket', $ticket_id );
delete_post_meta( $event_id, '_' . $ticket_id );
}
}
}
}
}
update_post_meta( $payment_id, '_returned', 'true' );
}
}
/**
* Return a ticket to purchase pool if refunded or deleted.
*
* @param string $ticket_id - ID for a single ticket.
* @param int $event_id - ID for event ticket was sold for.
* @param int $purchase_id Payment ID.
* @param string $type - type of ticket sold.
*/
function mt_return_ticket( $ticket_id, $event_id, $purchase_id, $type ) {
delete_post_meta( $event_id, '_ticket', $ticket_id );
delete_post_meta( $event_id, '_' . $ticket_id );
$registration = get_post_meta( '_mt_registration_options', true );
$sold = $registration['prices'][ $type ]['sold'];
$new_sold = $sold + 1;
$registration['prices'][ $type ]['sold'] = $new_sold;
update_post_meta( $event_id, '_mt_registration_options', $registration );
}
add_action( 'mt_ticket_type_close_sales', 'mt_notify_admin', 10, 3 );
add_action( 'mt_ticket_sales_closed', 'mt_notify_admin', 10, 3 );
add_action( 'mt_event_sold_out', 'mt_notify_admin', 10, 3 );
/**
* Send notification to admin when ticket sales are closed.
*
* @param int $event Event ID.
* @param array|string $ticket_info Event registration data or ticket type.
* @param string $context 'closed' or 'soldout'.
*/
function mt_notify_admin( $event, $ticket_info, $context ) {
$event = (int) $event;
$event_url = get_the_permalink( $event );
$options = mt_get_settings();
$email = $options['mt_to'];
$blogname = get_option( 'blogname' );
$headers[] = "From: $blogname Events <" . $options['mt_from'] . '>';
$headers[] = "Reply-to: $options[mt_from]";
apply_filters( 'mt_filter_email_headers', $headers, $event );
$title = get_the_title( $event );
$inventory = mt_check_inventory( $event, '', false );
$download = admin_url( "admin.php?page=mt-reports&event_id=$event&format=csv&mt-event-report=purchases" );
$tickets = admin_url( "admin.php?page=mt-reports&event_id=$event&format=csv&mt-event-report=tickets" );
if ( 'type' === $context ) {
$download = add_query_arg( 'mt_select_ticket_type', $ticket_info, $download );
$tickets = add_query_arg( 'mt_select_ticket_type', $ticket_info, $tickets );
}
if ( 'closed' === $context || 'type' === $context ) {
if ( 'type' === $context ) {
$title = $title . ' (' . $ticket_info . ')';
}
/**
* Filter the "ticket sales closed" email subject sent to administrators.
*
* @hook mt_closure_subject
*
* @param {string} $subject Email subject line.
* @param {int} $event Event Post ID.
*
* @return {string}
*/
$subject = apply_filters(
'mt_closure_subject',
sprintf(
// Translators: Name of event being closed.
__( 'Ticket sales for %s are now closed', 'my-tickets' ),
$title
),
$event
);
$subject = mb_encode_mimeheader( $subject );
if ( $inventory && 0 === (int) $inventory['sold'] ) {
// Translators: Name of event closed.
$body = apply_filters( 'mt_closure_body', sprintf( __( 'Online ticket sales for %1$s are now closed with no online sales.', 'my-tickets' ), $title ), $event, 'no-sales' );
} else {
/**
* Filter the "ticket sales closed" email body sent to administrators.
*
* @hook mt_closure_body
*
* @param {string} $body Email body text.
* @param {int} $event Event Post ID.
* @param {string} $type Message type. 'has-sales' or 'no-sales'.
*
* @return {string}
*/
$body = apply_filters(
'mt_closure_body',
sprintf(
// Translators: Name of event closed; link to download list of purchase; link to download list of tickets.
'<p>' . __( 'Online ticket sales for %1$s are now closed. <a href="%2$s">Download the purchases list</a> <a href="%3$s">Download the tickets list</a>', 'my-tickets' ) . '</p><p>' . $event_url . '</p>',
$title,
$download,
$tickets
),
$event,
'has-sales'
);
}
}
if ( 'soldout' === $context ) {
/**
* Filter the "ticket sales sold out" email subject sent to administrators.
*
* @hook mt_soldout_subject
*
* @param {string} $subject Email subject line.
* @param {int} $event Event Post ID.
*
* @return {string}
*/
$subject = apply_filters(
'mt_soldout_subject',
sprintf(
// Translators: Name of event soldout.
__( '%s has sold out. Ticket sales are now closed.', 'my-tickets' ),
strip_tags( $title )
),
$event
);
$subject = mb_encode_mimeheader( $subject );
/**
* Filter the "ticket sales sold out" email body sent to administrators.
*
* @hook mt_soldout_body
*
* @param {string} $body Email body text.
* @param {int} $event Event Post ID.
*
* @return {string}
*/
$body = apply_filters(
'mt_soldout_body',
sprintf(
// Translators: Name of event closed; link to download list of purchase; link to download list of tickets.
'<p>' . __( '%1$s has sold out, and ticket sales are now closed. <a href="%2$s">Download the purchases list</a> <a href="%3$s">Download the tickets list</a>', 'my-tickets' ) . '</p><p>' . $event_url . '</p>',
strip_tags( $title ),
$download,
$tickets
),
$event
);
}
/**
* Filter the recipient of admin closure and sold out email notifications.
*
* @hook mt_closure_recipient
*
* @param {string} $email Email address of recipient.
* @param {id} $event Event Post ID.
*
* @return {string}
*/
$to = apply_filters( 'mt_closure_recipient', $email, $event );
add_filter( 'wp_mail_content_type', 'mt_html_type' );
$body = apply_filters( 'mt_modify_email_body', $body, 'admin' );
wp_mail( $to, $subject, $body, $headers );
remove_filter( 'wp_mail_content_type', 'mt_html_type' );
}
/**
* Return string for HTML email types
*/
function mt_html_type() {
return 'text/html';
}
// Use Codemirror for email fields when enabled.
add_action(
'admin_enqueue_scripts',
function () {
if ( ! function_exists( 'wp_enqueue_code_editor' ) ) {
return;
}
if ( 'toplevel_page_my-tickets' === get_current_screen()->id || 'my-tickets_page_mt-reports' === get_current_screen()->id ) {
// Enqueue code editor and settings for manipulating HTML.
$settings = wp_enqueue_code_editor(
array(
'type' => 'text/html',
'codemirror' => array(
'autoRefresh' => true,
),
)
);
// Bail if user disabled CodeMirror or using default styles.
$options = mt_get_settings();
if ( false === $settings || 'true' !== $options['mt_html_email'] ) {
return;
}
if ( 'toplevel_page_my-tickets' === get_current_screen()->id ) {
wp_add_inline_script(
'code-editor',
sprintf(
'jQuery( function() { wp.codeEditor.initialize( "mt_messages_completed_admin_body", %s ); } );',
wp_json_encode( $settings )
)
);
wp_add_inline_script(
'code-editor',
sprintf(
'jQuery( function() { wp.codeEditor.initialize( "mt_messages_completed_purchaser_body", %s ); } );',
wp_json_encode( $settings )
)
);
wp_add_inline_script(
'code-editor',
sprintf(
'jQuery( function() { wp.codeEditor.initialize( "mt_messages_failed_admin_body", %s ); } );',
wp_json_encode( $settings )
)
);
wp_add_inline_script(
'code-editor',
sprintf(
'jQuery( function() { wp.codeEditor.initialize( "mt_messages_failed_purchaser_body", %s ); } );',
wp_json_encode( $settings )
)
);
wp_add_inline_script(
'code-editor',
sprintf(
'jQuery( function() { wp.codeEditor.initialize( "mt_messages_refunded_admin_body", %s ); } );',
wp_json_encode( $settings )
)
);
wp_add_inline_script(
'code-editor',
sprintf(
'jQuery( function() { wp.codeEditor.initialize( "mt_messages_refunded_purchaser_body", %s ); } );',
wp_json_encode( $settings )
)
);
wp_add_inline_script(
'code-editor',
sprintf(
'jQuery( function() { wp.codeEditor.initialize( "mt_messages_interim_admin_body", %s ); } );',
wp_json_encode( $settings )
)
);
wp_add_inline_script(
'code-editor',
sprintf(
'jQuery( function() { wp.codeEditor.initialize( "mt_messages_interim_purchaser_body", %s ); } );',
wp_json_encode( $settings )
)
);
} else {
wp_add_inline_script(
'code-editor',
sprintf(
'jQuery( function() { wp.codeEditor.initialize( "mt_body", %s ); } );',
wp_json_encode( $settings )
)
);
}
}
}
);