Files
woocommerce-subscriptions/includes/core/emails/class-wc-subscriptions-email-preview.php
2025-05-21 10:18:23 +00:00

304 lines
9.7 KiB
PHP

<?php
/**
* Subscriptions Email Preview Class
*/
class WC_Subscriptions_Email_Preview {
/**
* The email being previewed
*
* @var string
*/
private $email_type;
/**
* Constructor.
*/
public function __construct() {
add_filter( 'woocommerce_prepare_email_for_preview', [ $this, 'prepare_email_for_preview' ] );
}
/**
* Prepare subscription email dummy data for preview.
*
* @param WC_Email $email The email object.
*
* @return WC_Email
*/
public function prepare_email_for_preview( $email ) {
$this->email_type = get_class( $email );
if ( ! $this->is_subscription_email() ) {
return $email;
}
$this->set_up_filters();
switch ( $this->email_type ) {
case 'WCS_Email_New_Switch_Order':
case 'WCS_Email_Completed_Switch_Order':
$email->subscriptions = [ $this->get_dummy_subscription() ];
break;
case 'WCS_Email_Cancelled_Subscription':
case 'WCS_Email_Expired_Subscription':
case 'WCS_Email_On_Hold_Subscription':
case 'WCS_Email_Customer_Notification_Auto_Trial_Expiration':
case 'WCS_Email_Customer_Notification_Manual_Trial_Expiration':
case 'WCS_Email_Customer_Notification_Subscription_Expiration':
case 'WCS_Email_Customer_Notification_Manual_Renewal':
case 'WCS_Email_Customer_Notification_Auto_Renewal':
$email->set_object( $this->get_dummy_subscription() );
break;
case 'WCS_Email_Customer_Payment_Retry':
case 'WCS_Email_Payment_Retry':
$email->retry = $this->get_dummy_retry( $email->object );
break;
}
$this->add_placeholders( $email );
add_filter( 'woocommerce_mail_content', [ $this, 'clean_up_filters' ] );
return $email;
}
/**
* Get a dummy subscription for use in preview emails.
*
* @return WC_Subscription
*/
private function get_dummy_subscription() {
$subscription = new WC_Subscription();
$product = $this->get_dummy_product();
$subscription->add_product( $product, 2 );
$subscription->set_id( 12346 );
$subscription->set_customer_id( 1 );
$subscription->set_date_created( gmdate( 'Y-m-d H:i:s', strtotime( '-1 month' ) ) );
$subscription->set_currency( 'USD' );
$subscription->set_total( 100 );
$subscription->set_billing_period( 'month' );
$subscription->set_billing_interval( 1 );
$subscription->set_start_date( gmdate( 'Y-m-d H:i:s', strtotime( '-1 month' ) ) );
$subscription->set_trial_end_date( gmdate( 'Y-m-d H:i:s', strtotime( '+1 week' ) ) );
$subscription->set_next_payment_date( gmdate( 'Y-m-d H:i:s', strtotime( '+1 week' ) ) );
$subscription->set_end_date( gmdate( 'Y-m-d H:i:s', strtotime( '+1 month' ) ) );
$address = self::get_dummy_address();
$subscription->set_billing_address( $address );
$subscription->set_shipping_address( $address );
/**
* Filter the dummy subscription object used in email previews.
*
* @param WC_Subscription $subscription The dummy subscription object.
* @param string $email_type The email type being previewed.
*/
return apply_filters( 'woocommerce_subscriptions_email_preview_dummy_subscription', $subscription, $this->email_type );
}
/**
* Get a dummy product for use when previewing subscription emails.
*
* @return WC_Product
*/
private function get_dummy_product() {
$product = new WC_Product();
$product->set_name( 'Dummy Subscription' );
$product->set_price( 25 );
/**
* Filter the dummy subscription product object used in email previews.
*
* @param WC_Product $product The dummy product object.
* @param string $email_type The email type being previewed.
*/
return apply_filters( 'woocommerce_subscriptions_email_preview_dummy_product', $product, $this->email_type );
}
/**
* Get a dummy address used when previewing subscription emails.
*
* @return array
*/
private function get_dummy_address() {
$address = [
'first_name' => 'John',
'last_name' => 'Doe',
'company' => 'Company',
'email' => 'john@company.com',
'phone' => '555-555-5555',
'address_1' => '123 Fake Street',
'city' => 'Faketown',
'postcode' => '12345',
'country' => 'US',
'state' => 'CA',
];
/**
* Filter the dummy address used in email previews.
*
* @param array $address The dummy address.
* @param string $email_type The email type being previewed.
*/
return apply_filters( 'woocommerce_subscriptions_email_preview_dummy_address', $address, $this->email_type );
}
/**
* Creates a dummy retry for use when previewing failed subscription payment retry emails.
*
* @param WC_Order $order The order object to create a dummy retry for.
* @return WCS_Retry The dummy retry object.
*/
private function get_dummy_retry( $order ) {
if ( ! class_exists( 'WCS_Retry_Manager' ) ) {
return null;
}
$order_id = is_a( $order, 'WC_Order' ) ? $order->get_id() : 12345;
$retry_rule = WCS_Retry_Manager::rules()->get_rule( 1, $order_id );
if ( is_a( $retry_rule, 'WCS_Retry_Rule' ) ) {
$interval = $retry_rule->get_retry_interval();
$raw_retry_rule = $retry_rule->get_raw_data();
} else {
// If the retry rule is not found, use a default interval of 12 hours and an empty raw rule.
$interval = 12 * HOUR_IN_SECONDS;
$raw_retry_rule = [];
}
return new WCS_Retry(
[
'status' => 'pending',
'order_id' => $order_id,
'date_gmt' => gmdate( 'Y-m-d H:i:s', time() + $interval ),
'rule_raw' => $raw_retry_rule,
]
);
}
/**
* Check if the email being previewed is a subscription email.
*
* Subscription emails include:
* - WC_Subscriptions_Email::$email_classes - core subscription emails.
* - WC_Subscriptions_Email_Notifications::$email_classes - subscription notification emails (pre-renewal emails).
* - WCS_Email_Customer_Payment_Retry - customer payment retry emails.
* - WCS_Email_Payment_Retry - admin payment retry emails.
*
* @return bool Whether the email being previewed is a subscription email.
*/
private function is_subscription_email() {
return isset( WC_Subscriptions_Email::$email_classes[ $this->email_type ] )
|| isset( WC_Subscriptions_Email_Notifications::$email_classes[ $this->email_type ] )
|| in_array( $this->email_type, [ 'WCS_Email_Customer_Payment_Retry', 'WCS_Email_Payment_Retry' ], true );
}
/**
* Set up filters for previewing emails.
*/
private function set_up_filters() {
// Filter the last order date created for a subscription to be displayed in the Cancelled Subscription email.
add_filter( 'woocommerce_subscription_get_last_order_date_created_date', [ $this, 'mock_last_order_date_created' ], 10, 2 );
// For the purpose of previewing an email, force the subscription to be early renewable if the feature is enabled.
add_filter( 'woocommerce_subscriptions_can_user_renew_early', [ $this, 'allow_early_renewals_during_preview' ], 10, 3 );
}
/**
* Clean up filters at the end of previewing emails.
*
* @param string $preview_content The email content.
*
* @return string
*/
public function clean_up_filters( $preview_content ) {
remove_filter( 'woocommerce_subscription_get_last_order_date_created_date', [ $this, 'mock_last_order_date_created' ] );
remove_filter( 'woocommerce_subscriptions_can_user_renew_early', [ $this, 'allow_early_renewals_during_preview' ] );
return $preview_content;
}
/**
* Mock the last order date created for a subscription to the current date.
*
* @param string $date The date.
*
* @return string
*/
public function mock_last_order_date_created( $date, $subscription ) {
if ( is_a( $subscription, 'WC_Subscription' ) && 12346 === $subscription->get_id() && 1 === $subscription->get_customer_id() ) {
return gmdate( 'Y-m-d H:i:s' );
}
return $date;
}
/**
* Allow early renewals for previewing emails.
*
* @param bool $can_renew_early Whether the subscription can be renewed early.
* @param WC_Subscription $subscription The subscription.
* @param int $user_id The user ID.
*
* @return bool
*/
public function allow_early_renewals_during_preview( $can_renew_early, $subscription, $user_id ) {
if ( 1 === $user_id ) {
return true;
}
return $can_renew_early;
}
/**
* Adds custom placeholders for subscription emails.
*
* @param WC_Email $email The email object.
*/
private function add_placeholders( $email ) {
if ( ! isset( $email->placeholders ) ) {
return;
}
$placeholders = [];
switch ( $this->email_type ) {
case 'WCS_Email_Customer_Notification_Subscription_Expiration':
case 'WCS_Email_Customer_Notification_Manual_Trial_Expiration':
case 'WCS_Email_Customer_Notification_Auto_Trial_Expiration':
case 'WCS_Email_Customer_Notification_Manual_Renewal':
case 'WCS_Email_Customer_Notification_Auto_Renewal':
// Pull the real values from the email object (Order or Subscription) if available.
if ( is_a( $email->object, 'WC_Subscription' ) ) {
$time_until_renewal = $email->get_time_until_date( $email->object, 'next_payment' );
$customer_first_name = $email->object->get_billing_first_name();
} else {
$time_until_renewal = human_time_diff( time(), time() + WEEK_IN_SECONDS );
$customer_first_name = 'John';
}
$placeholders['{time_until_renewal}'] = $time_until_renewal;
$placeholders['{customers_first_name}'] = $customer_first_name;
break;
case 'WCS_Email_Customer_Payment_Retry':
case 'WCS_Email_Payment_Retry':
$retry_time = is_a( $email->retry, 'WCS_Retry' )
? $email->retry->get_time()
: time() + ( 12 * HOUR_IN_SECONDS );
$placeholders['{retry_time}'] = wcs_get_human_time_diff( $retry_time );
break;
}
// Merge placeholders without overriding existing ones, and only adding those in the email.
$email->placeholders = wp_parse_args(
$placeholders,
$email->placeholders
);
}
}