WP maple cart and page subtitle plugin upload
This commit is contained in:
parent
b3e87772ec
commit
c85895d306
18 changed files with 5741 additions and 0 deletions
|
|
@ -0,0 +1,633 @@
|
|||
<?php
|
||||
/**
|
||||
* Email handling for Maple Carts.
|
||||
*
|
||||
* @package MapleCarts
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Emails class.
|
||||
*/
|
||||
class Maple_Carts_Emails {
|
||||
|
||||
/**
|
||||
* Single instance.
|
||||
*
|
||||
* @var Maple_Carts_Emails
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Get instance.
|
||||
*
|
||||
* @return Maple_Carts_Emails
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( is_null( self::$instance ) ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
private function __construct() {
|
||||
// Cron hook for sending emails.
|
||||
add_action( 'maple_carts_send_emails', [ $this, 'process_pending_emails' ] );
|
||||
|
||||
// AJAX for preview email.
|
||||
add_action( 'wp_ajax_maple_carts_preview_email', [ $this, 'ajax_preview_email' ] );
|
||||
add_action( 'wp_ajax_maple_carts_send_test_email', [ $this, 'ajax_send_test_email' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process pending emails.
|
||||
* Includes lock to prevent overlapping cron runs.
|
||||
*/
|
||||
public function process_pending_emails() {
|
||||
// Skip if Mailjet mode is enabled - emails are handled by Mailjet automations.
|
||||
if ( 'mailjet' === Maple_Carts::get_option( 'email_mode', 'builtin' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent overlapping cron runs.
|
||||
if ( get_transient( 'maple_carts_sending_emails' ) ) {
|
||||
return;
|
||||
}
|
||||
set_transient( 'maple_carts_sending_emails', 1, 300 ); // 5 minute lock.
|
||||
|
||||
$pending = Maple_Carts_DB::get_pending_emails();
|
||||
|
||||
foreach ( $pending as $email_data ) {
|
||||
$this->send_email( $email_data );
|
||||
}
|
||||
|
||||
delete_transient( 'maple_carts_sending_emails' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a recovery email.
|
||||
*
|
||||
* @param object $email_data Email data from database.
|
||||
* @return bool
|
||||
*/
|
||||
public function send_email( $email_data ) {
|
||||
// Validate email address.
|
||||
if ( ! is_email( $email_data->email ) ) {
|
||||
Maple_Carts_DB::update_email_log( $email_data->id, 'failed', [ 'error_message' => 'Invalid email address' ] );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if cart still qualifies.
|
||||
$cart = Maple_Carts_DB::get_cart_by_session( $email_data->session_id );
|
||||
if ( ! $cart || 'abandoned' !== $cart->status || $cart->unsubscribed ) {
|
||||
Maple_Carts_DB::update_email_log( $email_data->id, 'cancelled' );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for out-of-stock products.
|
||||
$cart_contents = maybe_unserialize( $email_data->cart_contents );
|
||||
if ( $this->cart_has_out_of_stock( $cart_contents ) ) {
|
||||
Maple_Carts_DB::update_email_log( $email_data->id, 'cancelled', [ 'error_message' => 'Products out of stock' ] );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate coupon if needed.
|
||||
$coupon_code = '';
|
||||
if ( $email_data->include_coupon ) {
|
||||
$coupon_code = $this->generate_coupon(
|
||||
$email_data->coupon_type,
|
||||
$email_data->coupon_amount,
|
||||
$email_data->coupon_expires_days,
|
||||
$email_data->cart_id
|
||||
);
|
||||
|
||||
// Store coupon code in log.
|
||||
global $wpdb;
|
||||
$wpdb->update(
|
||||
$wpdb->prefix . MAPLE_CARTS_EMAIL_LOG_TABLE,
|
||||
[ 'coupon_code' => $coupon_code ],
|
||||
[ 'id' => $email_data->id ]
|
||||
);
|
||||
}
|
||||
|
||||
// Generate recovery URL with coupon.
|
||||
$recovery_url = Maple_Carts_Tracking::instance()->generate_recovery_url(
|
||||
$email_data->session_id,
|
||||
$coupon_code
|
||||
);
|
||||
|
||||
// Add UTM parameters for analytics attribution.
|
||||
$recovery_url = add_query_arg( [
|
||||
'utm_source' => 'maple_carts',
|
||||
'utm_medium' => 'email',
|
||||
'utm_campaign' => 'cart_recovery',
|
||||
'utm_content' => 'email_' . $email_data->email_template_id,
|
||||
], $recovery_url );
|
||||
|
||||
// Parse email content.
|
||||
$subject = $this->parse_template( $email_data->subject, $email_data, $coupon_code, $recovery_url );
|
||||
$body = $this->parse_template( $email_data->body, $email_data, $coupon_code, $recovery_url );
|
||||
|
||||
// Send email.
|
||||
$sent = $this->dispatch_email( $email_data->email, $subject, $body );
|
||||
|
||||
if ( $sent ) {
|
||||
Maple_Carts_DB::update_email_log( $email_data->id, 'sent' );
|
||||
|
||||
/**
|
||||
* Fires after a recovery email is sent.
|
||||
*
|
||||
* @param object $email_data Email data.
|
||||
* @param string $subject Email subject.
|
||||
*/
|
||||
do_action( 'maple_carts_email_sent', $email_data, $subject );
|
||||
|
||||
return true;
|
||||
} else {
|
||||
Maple_Carts_DB::update_email_log( $email_data->id, 'failed', [ 'error_message' => 'Failed to send email' ] );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse email template with placeholders.
|
||||
*
|
||||
* @param string $template Template string.
|
||||
* @param object $email_data Email data.
|
||||
* @param string $coupon_code Coupon code.
|
||||
* @param string $recovery_url Recovery URL.
|
||||
* @return string
|
||||
*/
|
||||
private function parse_template( $template, $email_data, $coupon_code, $recovery_url ) {
|
||||
$cart_contents = maybe_unserialize( $email_data->cart_contents );
|
||||
|
||||
// Customer name fallback.
|
||||
$customer_name = $email_data->customer_name;
|
||||
if ( empty( $customer_name ) ) {
|
||||
$customer_name = __( 'there', 'maple-carts' );
|
||||
}
|
||||
|
||||
// Generate cart contents HTML.
|
||||
$cart_html = $this->generate_cart_html( $cart_contents, $email_data->currency );
|
||||
|
||||
// Generate product names list.
|
||||
$product_names = [];
|
||||
if ( is_array( $cart_contents ) ) {
|
||||
foreach ( $cart_contents as $item ) {
|
||||
$product_names[] = $item['name'];
|
||||
}
|
||||
}
|
||||
|
||||
// Coupon display amount.
|
||||
$coupon_display = '';
|
||||
if ( ! empty( $coupon_code ) ) {
|
||||
if ( 'percent' === $email_data->coupon_type ) {
|
||||
$coupon_display = $email_data->coupon_amount . '%';
|
||||
} else {
|
||||
$coupon_display = wc_price( $email_data->coupon_amount );
|
||||
}
|
||||
}
|
||||
|
||||
// Unsubscribe link (using signed URL from Tracking class).
|
||||
$unsubscribe_url = Maple_Carts_Tracking::instance()->generate_unsubscribe_url( $email_data->session_id );
|
||||
$unsubscribe_link = sprintf(
|
||||
'<a href="%s" style="color:#999;">%s</a>',
|
||||
esc_url( $unsubscribe_url ),
|
||||
__( 'Unsubscribe from these emails', 'maple-carts' )
|
||||
);
|
||||
|
||||
// Delete data link for GDPR compliance (optional).
|
||||
$delete_data_link = '';
|
||||
if ( 'yes' === Maple_Carts::get_option( 'show_delete_link', 'no' ) ) {
|
||||
$delete_data_url = Maple_Carts_Privacy::generate_deletion_url( $email_data->session_id );
|
||||
$delete_data_link = sprintf(
|
||||
' | <a href="%s" style="color:#999;">%s</a>',
|
||||
esc_url( $delete_data_url ),
|
||||
__( 'Delete my data', 'maple-carts' )
|
||||
);
|
||||
}
|
||||
|
||||
// Replacements.
|
||||
$replacements = [
|
||||
'{{customer_name}}' => esc_html( $customer_name ),
|
||||
'{{customer_email}}' => esc_html( $email_data->email ),
|
||||
'{{cart_contents}}' => $cart_html,
|
||||
'{{cart_total}}' => wc_price( $email_data->cart_total, [ 'currency' => $email_data->currency ] ),
|
||||
'{{product_names}}' => esc_html( implode( ', ', $product_names ) ),
|
||||
'{{recovery_url}}' => esc_url( $recovery_url ),
|
||||
'{{coupon_code}}' => esc_html( $coupon_code ),
|
||||
'{{coupon_amount}}' => $coupon_display,
|
||||
'{{coupon_expires}}' => $email_data->coupon_expires_days,
|
||||
'{{site_name}}' => esc_html( get_bloginfo( 'name' ) ),
|
||||
'{{site_url}}' => esc_url( home_url() ),
|
||||
'{{unsubscribe_link}}' => $unsubscribe_link,
|
||||
'{{delete_data_link}}' => $delete_data_link,
|
||||
'{{current_date}}' => date_i18n( get_option( 'date_format' ) ),
|
||||
];
|
||||
|
||||
/**
|
||||
* Filter email template replacements.
|
||||
*
|
||||
* @param array $replacements Placeholder replacements.
|
||||
* @param object $email_data Email data.
|
||||
*/
|
||||
$replacements = apply_filters( 'maple_carts_email_replacements', $replacements, $email_data );
|
||||
|
||||
return str_replace( array_keys( $replacements ), array_values( $replacements ), $template );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate cart contents HTML.
|
||||
*
|
||||
* @param array $cart_contents Cart contents.
|
||||
* @param string $currency Currency code.
|
||||
* @return string
|
||||
*/
|
||||
private function generate_cart_html( $cart_contents, $currency = 'USD' ) {
|
||||
if ( empty( $cart_contents ) || ! is_array( $cart_contents ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$html = '<table style="width:100%;border-collapse:collapse;margin:20px 0;">';
|
||||
$html .= '<thead><tr style="background:#f7f7f7;">';
|
||||
$html .= '<th style="padding:12px;text-align:left;border-bottom:2px solid #ddd;">' . __( 'Product', 'maple-carts' ) . '</th>';
|
||||
$html .= '<th style="padding:12px;text-align:center;border-bottom:2px solid #ddd;">' . __( 'Qty', 'maple-carts' ) . '</th>';
|
||||
$html .= '<th style="padding:12px;text-align:right;border-bottom:2px solid #ddd;">' . __( 'Price', 'maple-carts' ) . '</th>';
|
||||
$html .= '</tr></thead><tbody>';
|
||||
|
||||
$total = 0;
|
||||
foreach ( $cart_contents as $item ) {
|
||||
$line_total = $item['price'] * $item['quantity'];
|
||||
$total += $line_total;
|
||||
|
||||
$html .= '<tr>';
|
||||
$html .= '<td style="padding:12px;border-bottom:1px solid #eee;">';
|
||||
|
||||
if ( ! empty( $item['image'] ) ) {
|
||||
$html .= '<img src="' . esc_url( $item['image'] ) . '" alt="" style="width:50px;height:50px;object-fit:cover;margin-right:10px;vertical-align:middle;">';
|
||||
}
|
||||
|
||||
$html .= '<span style="vertical-align:middle;">' . esc_html( $item['name'] ) . '</span>';
|
||||
$html .= '</td>';
|
||||
$html .= '<td style="padding:12px;text-align:center;border-bottom:1px solid #eee;">' . esc_html( $item['quantity'] ) . '</td>';
|
||||
$html .= '<td style="padding:12px;text-align:right;border-bottom:1px solid #eee;">' . wc_price( $line_total, [ 'currency' => $currency ] ) . '</td>';
|
||||
$html .= '</tr>';
|
||||
}
|
||||
|
||||
$html .= '</tbody></table>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if cart has out of stock products.
|
||||
*
|
||||
* @param array $cart_contents Cart contents.
|
||||
* @return bool
|
||||
*/
|
||||
private function cart_has_out_of_stock( $cart_contents ) {
|
||||
if ( empty( $cart_contents ) || ! is_array( $cart_contents ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $cart_contents as $item ) {
|
||||
$product_id = ! empty( $item['variation_id'] ) ? $item['variation_id'] : $item['product_id'];
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
if ( ! $product || ! $product->is_in_stock() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a coupon code.
|
||||
*
|
||||
* @param string $type Coupon type (percent, fixed_cart).
|
||||
* @param float $amount Discount amount.
|
||||
* @param int $expires_days Days until expiry.
|
||||
* @param int $cart_id Cart ID for reference.
|
||||
* @return string
|
||||
*/
|
||||
public function generate_coupon( $type, $amount, $expires_days, $cart_id ) {
|
||||
// Check if cart already has a coupon (prevents double creation on cron misfire).
|
||||
$existing_coupon = $this->get_cart_coupon( $cart_id );
|
||||
if ( $existing_coupon ) {
|
||||
return $existing_coupon;
|
||||
}
|
||||
|
||||
$coupon_code = 'MAPLE-' . strtoupper( wp_generate_password( 8, false, false ) );
|
||||
|
||||
$coupon = new WC_Coupon();
|
||||
$coupon->set_code( $coupon_code );
|
||||
$coupon->set_discount_type( $type );
|
||||
$coupon->set_amount( $amount );
|
||||
$coupon->set_individual_use( true );
|
||||
$coupon->set_usage_limit( 1 );
|
||||
$coupon->set_usage_limit_per_user( 1 );
|
||||
|
||||
if ( $expires_days > 0 ) {
|
||||
$expiry = strtotime( "+{$expires_days} days" );
|
||||
$coupon->set_date_expires( $expiry );
|
||||
}
|
||||
|
||||
$coupon->save();
|
||||
|
||||
// Mark as Maple Carts coupon for cleanup with creation timestamp.
|
||||
update_post_meta( $coupon->get_id(), '_maple_carts_coupon', '1' );
|
||||
update_post_meta( $coupon->get_id(), '_maple_carts_cart_id', $cart_id );
|
||||
update_post_meta( $coupon->get_id(), '_maple_carts_created_at', time() );
|
||||
|
||||
return $coupon_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing coupon for a cart (prevents double creation).
|
||||
*
|
||||
* @param int $cart_id Cart ID.
|
||||
* @return string|null Coupon code or null.
|
||||
*/
|
||||
private function get_cart_coupon( $cart_id ) {
|
||||
global $wpdb;
|
||||
|
||||
// Check email log for existing coupon.
|
||||
$log_table = $wpdb->prefix . MAPLE_CARTS_EMAIL_LOG_TABLE;
|
||||
$coupon = $wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT coupon_code FROM {$log_table}
|
||||
WHERE cart_id = %d AND coupon_code IS NOT NULL AND coupon_code != ''
|
||||
LIMIT 1",
|
||||
$cart_id
|
||||
) );
|
||||
|
||||
if ( $coupon ) {
|
||||
// Verify coupon still exists in WooCommerce.
|
||||
$coupon_id = wc_get_coupon_id_by_code( $coupon );
|
||||
if ( $coupon_id ) {
|
||||
return $coupon;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch email using wp_mail (works with any SMTP plugin).
|
||||
*
|
||||
* @param string $to Recipient email.
|
||||
* @param string $subject Email subject.
|
||||
* @param string $body Email body (HTML).
|
||||
* @return bool
|
||||
*/
|
||||
private function dispatch_email( $to, $subject, $body ) {
|
||||
// Sanitize header values to prevent header injection.
|
||||
$from_name = $this->sanitize_header_value( Maple_Carts::get_option( 'from_name', get_bloginfo( 'name' ) ) );
|
||||
$from_email = $this->sanitize_header_value( Maple_Carts::get_option( 'from_email', get_option( 'admin_email' ) ) );
|
||||
$reply_to = $this->sanitize_header_value( Maple_Carts::get_option( 'reply_to', get_option( 'admin_email' ) ) );
|
||||
|
||||
// Validate email addresses.
|
||||
if ( ! is_email( $from_email ) ) {
|
||||
$from_email = get_option( 'admin_email' );
|
||||
}
|
||||
if ( ! is_email( $reply_to ) ) {
|
||||
$reply_to = get_option( 'admin_email' );
|
||||
}
|
||||
|
||||
// Wrap in basic HTML template.
|
||||
$html_body = $this->wrap_email_html( $body );
|
||||
|
||||
// Generate plain text alternative for better spam scores.
|
||||
$plain_body = $this->generate_plain_text( $body );
|
||||
|
||||
// Create multipart boundary.
|
||||
$boundary = 'maple_carts_' . md5( uniqid( time() ) );
|
||||
|
||||
$headers = [
|
||||
"From: {$from_name} <{$from_email}>",
|
||||
"Reply-To: {$reply_to}",
|
||||
"Content-Type: multipart/alternative; boundary=\"{$boundary}\"",
|
||||
];
|
||||
|
||||
// Build multipart message.
|
||||
$message = "--{$boundary}\r\n";
|
||||
$message .= "Content-Type: text/plain; charset=UTF-8\r\n";
|
||||
$message .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
|
||||
$message .= $plain_body . "\r\n\r\n";
|
||||
$message .= "--{$boundary}\r\n";
|
||||
$message .= "Content-Type: text/html; charset=UTF-8\r\n";
|
||||
$message .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
|
||||
$message .= $html_body . "\r\n\r\n";
|
||||
$message .= "--{$boundary}--";
|
||||
|
||||
/**
|
||||
* Filter the email before sending.
|
||||
*
|
||||
* @param array $email Email data.
|
||||
*/
|
||||
$email = apply_filters( 'maple_carts_before_send_email', [
|
||||
'to' => $to,
|
||||
'subject' => $subject,
|
||||
'body' => $message,
|
||||
'headers' => $headers,
|
||||
] );
|
||||
|
||||
// Use wp_mail which integrates with SMTP plugins.
|
||||
$sent = wp_mail( $email['to'], $email['subject'], $email['body'], $email['headers'] );
|
||||
|
||||
// Retry once on failure.
|
||||
if ( ! $sent ) {
|
||||
sleep( 1 );
|
||||
$sent = wp_mail( $email['to'], $email['subject'], $email['body'], $email['headers'] );
|
||||
}
|
||||
|
||||
return $sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate plain text version of HTML email.
|
||||
*
|
||||
* @param string $html HTML content.
|
||||
* @return string Plain text content.
|
||||
*/
|
||||
private function generate_plain_text( $html ) {
|
||||
// Convert common HTML elements.
|
||||
$text = $html;
|
||||
|
||||
// Convert links to text format.
|
||||
$text = preg_replace( '/<a[^>]+href=["\']([^"\']+)["\'][^>]*>([^<]+)<\/a>/i', '$2 ($1)', $text );
|
||||
|
||||
// Convert line breaks and paragraphs.
|
||||
$text = preg_replace( '/<br\s*\/?>/i', "\n", $text );
|
||||
$text = preg_replace( '/<\/p>/i', "\n\n", $text );
|
||||
$text = preg_replace( '/<\/div>/i', "\n", $text );
|
||||
$text = preg_replace( '/<\/h[1-6]>/i', "\n\n", $text );
|
||||
|
||||
// Convert list items.
|
||||
$text = preg_replace( '/<li[^>]*>/i', "• ", $text );
|
||||
$text = preg_replace( '/<\/li>/i', "\n", $text );
|
||||
|
||||
// Strip remaining tags.
|
||||
$text = wp_strip_all_tags( $text );
|
||||
|
||||
// Decode HTML entities.
|
||||
$text = html_entity_decode( $text, ENT_QUOTES, 'UTF-8' );
|
||||
|
||||
// Clean up whitespace.
|
||||
$text = preg_replace( '/[ \t]+/', ' ', $text );
|
||||
$text = preg_replace( '/\n{3,}/', "\n\n", $text );
|
||||
$text = trim( $text );
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a value for use in email headers.
|
||||
* Prevents header injection attacks by removing newlines and control characters.
|
||||
*
|
||||
* @param string $value The value to sanitize.
|
||||
* @return string
|
||||
*/
|
||||
private function sanitize_header_value( $value ) {
|
||||
// Remove any newlines, carriage returns, and null bytes.
|
||||
$value = preg_replace( '/[\r\n\0]/', '', $value );
|
||||
// Remove any remaining control characters.
|
||||
$value = preg_replace( '/[\x00-\x1F\x7F]/', '', $value );
|
||||
return trim( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap email content in HTML template.
|
||||
*
|
||||
* @param string $content Email content.
|
||||
* @return string
|
||||
*/
|
||||
private function wrap_email_html( $content ) {
|
||||
$template = '<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body style="margin:0;padding:0;background-color:#f4f4f4;font-family:-apple-system,BlinkMacSystemFont,\'Segoe UI\',Roboto,Oxygen-Sans,Ubuntu,Cantarell,\'Helvetica Neue\',sans-serif;">
|
||||
<table role="presentation" style="width:100%;border-collapse:collapse;">
|
||||
<tr>
|
||||
<td style="padding:40px 20px;">
|
||||
<table role="presentation" style="max-width:600px;margin:0 auto;background:#ffffff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.05);">
|
||||
<tr>
|
||||
<td style="padding:40px;">
|
||||
{{content}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>';
|
||||
|
||||
return str_replace( '{{content}}', wpautop( $content ), $template );
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for email preview.
|
||||
*/
|
||||
public function ajax_preview_email() {
|
||||
check_ajax_referer( 'maple_carts_admin', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( 'Permission denied' );
|
||||
}
|
||||
|
||||
$template_id = isset( $_POST['template_id'] ) ? absint( $_POST['template_id'] ) : 0;
|
||||
$subject = isset( $_POST['subject'] ) ? sanitize_text_field( wp_unslash( $_POST['subject'] ) ) : '';
|
||||
$body = isset( $_POST['body'] ) ? wp_kses_post( wp_unslash( $_POST['body'] ) ) : '';
|
||||
|
||||
// Create dummy data for preview.
|
||||
$dummy_data = (object) [
|
||||
'email' => 'customer@example.com',
|
||||
'customer_name' => 'John Doe',
|
||||
'cart_contents' => maybe_serialize( [
|
||||
[
|
||||
'product_id' => 1,
|
||||
'name' => 'Sample Product',
|
||||
'price' => 29.99,
|
||||
'quantity' => 2,
|
||||
'image' => '',
|
||||
],
|
||||
] ),
|
||||
'cart_total' => 59.98,
|
||||
'currency' => get_woocommerce_currency(),
|
||||
'session_id' => 'preview-session',
|
||||
'include_coupon' => true,
|
||||
'coupon_type' => 'percent',
|
||||
'coupon_amount' => 10,
|
||||
'coupon_expires_days' => 7,
|
||||
];
|
||||
|
||||
$parsed_subject = $this->parse_template( $subject, $dummy_data, 'PREVIEW10', '#' );
|
||||
$parsed_body = $this->parse_template( $body, $dummy_data, 'PREVIEW10', '#' );
|
||||
$html_body = $this->wrap_email_html( $parsed_body );
|
||||
|
||||
wp_send_json_success( [
|
||||
'subject' => $parsed_subject,
|
||||
'body' => $html_body,
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for sending test email.
|
||||
*/
|
||||
public function ajax_send_test_email() {
|
||||
check_ajax_referer( 'maple_carts_admin', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( 'Permission denied' );
|
||||
}
|
||||
|
||||
$email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
|
||||
$subject = isset( $_POST['subject'] ) ? sanitize_text_field( wp_unslash( $_POST['subject'] ) ) : '';
|
||||
$body = isset( $_POST['body'] ) ? wp_kses_post( wp_unslash( $_POST['body'] ) ) : '';
|
||||
|
||||
if ( ! is_email( $email ) ) {
|
||||
wp_send_json_error( __( 'Invalid email address', 'maple-carts' ) );
|
||||
}
|
||||
|
||||
// Create dummy data.
|
||||
$dummy_data = (object) [
|
||||
'email' => $email,
|
||||
'customer_name' => 'Test Customer',
|
||||
'cart_contents' => maybe_serialize( [
|
||||
[
|
||||
'product_id' => 1,
|
||||
'name' => 'Test Product',
|
||||
'price' => 49.99,
|
||||
'quantity' => 1,
|
||||
'image' => '',
|
||||
],
|
||||
] ),
|
||||
'cart_total' => 49.99,
|
||||
'currency' => get_woocommerce_currency(),
|
||||
'session_id' => 'test-session',
|
||||
'include_coupon' => true,
|
||||
'coupon_type' => 'percent',
|
||||
'coupon_amount' => 10,
|
||||
'coupon_expires_days' => 7,
|
||||
];
|
||||
|
||||
$parsed_subject = $this->parse_template( $subject, $dummy_data, 'TEST10OFF', '#' );
|
||||
$parsed_body = $this->parse_template( $body, $dummy_data, 'TEST10OFF', '#' );
|
||||
|
||||
$sent = $this->dispatch_email( $email, '[TEST] ' . $parsed_subject, $parsed_body );
|
||||
|
||||
if ( $sent ) {
|
||||
wp_send_json_success( __( 'Test email sent successfully!', 'maple-carts' ) );
|
||||
} else {
|
||||
wp_send_json_error( __( 'Failed to send test email. Check your email configuration.', 'maple-carts' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue