525 lines
15 KiB
PHP
525 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Email Manager
|
|
*
|
|
* Manages custom email notifications and disables WooCommerce default emails
|
|
*
|
|
* @package WooNooW\Core\Notifications
|
|
*/
|
|
|
|
namespace WooNooW\Core\Notifications;
|
|
|
|
class EmailManager {
|
|
|
|
/**
|
|
* Instance
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Get instance
|
|
*/
|
|
public static function instance() {
|
|
if (null === self::$instance) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
private function __construct() {
|
|
$this->init_hooks();
|
|
}
|
|
|
|
/**
|
|
* Initialize hooks
|
|
*/
|
|
private function init_hooks() {
|
|
// Disable WooCommerce emails to prevent duplicates
|
|
add_action('woocommerce_email', [$this, 'disable_wc_emails'], 1);
|
|
|
|
// Hook into WooCommerce order status changes
|
|
add_action('woocommerce_order_status_pending_to_processing', [$this, 'send_order_processing_email'], 10, 2);
|
|
add_action('woocommerce_order_status_pending_to_completed', [$this, 'send_order_completed_email'], 10, 2);
|
|
add_action('woocommerce_order_status_processing_to_completed', [$this, 'send_order_completed_email'], 10, 2);
|
|
add_action('woocommerce_order_status_completed', [$this, 'send_order_completed_email'], 10, 2);
|
|
add_action('woocommerce_order_status_pending_to_on-hold', [$this, 'send_order_on_hold_email'], 10, 2);
|
|
add_action('woocommerce_order_status_failed_to_processing', [$this, 'send_order_processing_email'], 10, 2);
|
|
add_action('woocommerce_order_status_cancelled', [$this, 'send_order_cancelled_email'], 10, 2);
|
|
add_action('woocommerce_order_status_refunded', [$this, 'send_order_refunded_email'], 10, 2);
|
|
add_action('woocommerce_order_fully_refunded', [$this, 'send_order_refunded_email'], 10, 2);
|
|
|
|
// New order notification for admin
|
|
add_action('woocommerce_new_order', [$this, 'send_new_order_admin_email'], 10, 1);
|
|
|
|
// Customer note
|
|
add_action('woocommerce_new_customer_note', [$this, 'send_customer_note_email'], 10, 1);
|
|
|
|
// New customer account
|
|
add_action('woocommerce_created_customer', [$this, 'send_new_customer_email'], 10, 3);
|
|
|
|
// Password reset - intercept WordPress default email and use our template
|
|
add_filter('retrieve_password_message', [$this, 'handle_password_reset_email'], 10, 4);
|
|
|
|
// Low stock / Out of stock
|
|
add_action('woocommerce_low_stock', [$this, 'send_low_stock_email'], 10, 1);
|
|
add_action('woocommerce_no_stock', [$this, 'send_out_of_stock_email'], 10, 1);
|
|
add_action('woocommerce_product_set_stock', [$this, 'check_stock_levels'], 10, 1);
|
|
}
|
|
|
|
/**
|
|
* Check if WooNooW notification system is enabled
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function is_enabled() {
|
|
// Check global notification system mode
|
|
$system_mode = get_option('woonoow_notification_system_mode', 'woonoow');
|
|
return $system_mode === 'woonoow';
|
|
}
|
|
|
|
/**
|
|
* Disable WooCommerce default emails
|
|
*
|
|
* @param WC_Emails $email_class
|
|
*/
|
|
public function disable_wc_emails($email_class) {
|
|
// Only disable WC emails if WooNooW system is enabled
|
|
if (!self::is_enabled()) {
|
|
return; // Keep WC emails if WooNooW system disabled
|
|
}
|
|
|
|
// Disable all WooCommerce transactional emails
|
|
$emails_to_disable = [
|
|
'WC_Email_New_Order', // Admin: New order
|
|
'WC_Email_Cancelled_Order', // Admin: Cancelled order
|
|
'WC_Email_Failed_Order', // Admin: Failed order
|
|
'WC_Email_Customer_On_Hold_Order', // Customer: Order on-hold
|
|
'WC_Email_Customer_Processing_Order', // Customer: Processing order
|
|
'WC_Email_Customer_Completed_Order', // Customer: Completed order
|
|
'WC_Email_Customer_Refunded_Order', // Customer: Refunded order
|
|
'WC_Email_Customer_Invoice', // Customer: Invoice
|
|
'WC_Email_Customer_Note', // Customer: Note added
|
|
'WC_Email_Customer_Reset_Password', // Customer: Reset password
|
|
'WC_Email_Customer_New_Account', // Customer: New account
|
|
];
|
|
|
|
foreach ($emails_to_disable as $email_id) {
|
|
add_filter('woocommerce_email_enabled_' . strtolower(str_replace('WC_Email_', '', $email_id)), '__return_false');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send order processing email
|
|
*
|
|
* @param int $order_id
|
|
* @param WC_Order $order
|
|
*/
|
|
public function send_order_processing_email($order_id, $order = null) {
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('[EmailManager] send_order_processing_email triggered for order #' . $order_id);
|
|
}
|
|
|
|
if (!$order) {
|
|
$order = wc_get_order($order_id);
|
|
}
|
|
|
|
if (!$order) {
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('[EmailManager] Order not found for ID: ' . $order_id);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Check if event is enabled
|
|
if (!$this->is_event_enabled('order_processing', 'email', 'customer')) {
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('[EmailManager] order_processing email is disabled in settings');
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('[EmailManager] Sending order_processing email for order #' . $order_id);
|
|
}
|
|
|
|
// Send email
|
|
$this->send_email('order_processing', 'customer', $order);
|
|
}
|
|
|
|
/**
|
|
* Send order completed email
|
|
*
|
|
* @param int $order_id
|
|
* @param WC_Order $order
|
|
*/
|
|
public function send_order_completed_email($order_id, $order = null) {
|
|
if (!$order) {
|
|
$order = wc_get_order($order_id);
|
|
}
|
|
|
|
if (!$order) {
|
|
return;
|
|
}
|
|
|
|
// Check if event is enabled
|
|
if (!$this->is_event_enabled('order_completed', 'email', 'customer')) {
|
|
return;
|
|
}
|
|
|
|
// Send email
|
|
$this->send_email('order_completed', 'customer', $order);
|
|
}
|
|
|
|
/**
|
|
* Send order on-hold email
|
|
*
|
|
* @param int $order_id
|
|
* @param WC_Order $order
|
|
*/
|
|
public function send_order_on_hold_email($order_id, $order = null) {
|
|
if (!$order) {
|
|
$order = wc_get_order($order_id);
|
|
}
|
|
|
|
if (!$order) {
|
|
return;
|
|
}
|
|
|
|
// Check if event is enabled
|
|
if (!$this->is_event_enabled('order_processing', 'email', 'customer')) {
|
|
return;
|
|
}
|
|
|
|
// Send email (use processing template for on-hold)
|
|
$this->send_email('order_processing', 'customer', $order);
|
|
}
|
|
|
|
/**
|
|
* Send order cancelled email
|
|
*
|
|
* @param int $order_id
|
|
* @param WC_Order $order
|
|
*/
|
|
public function send_order_cancelled_email($order_id, $order = null) {
|
|
if (!$order) {
|
|
$order = wc_get_order($order_id);
|
|
}
|
|
|
|
if (!$order) {
|
|
return;
|
|
}
|
|
|
|
// Send to admin
|
|
if ($this->is_event_enabled('order_cancelled', 'email', 'staff')) {
|
|
$this->send_email('order_cancelled', 'staff', $order);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send order refunded email
|
|
*
|
|
* @param int $order_id
|
|
* @param WC_Order $order
|
|
*/
|
|
public function send_order_refunded_email($order_id, $order = null) {
|
|
if (!$order) {
|
|
$order = wc_get_order($order_id);
|
|
}
|
|
|
|
if (!$order) {
|
|
return;
|
|
}
|
|
|
|
// Check if event is enabled
|
|
if (!$this->is_event_enabled('order_refunded', 'email', 'customer')) {
|
|
return;
|
|
}
|
|
|
|
// Send email
|
|
$this->send_email('order_refunded', 'customer', $order);
|
|
}
|
|
|
|
/**
|
|
* Send new order admin email
|
|
*
|
|
* @param int $order_id
|
|
*/
|
|
public function send_new_order_admin_email($order_id) {
|
|
$order = wc_get_order($order_id);
|
|
|
|
if (!$order) {
|
|
return;
|
|
}
|
|
|
|
// Check if event is enabled
|
|
if (!$this->is_event_enabled('order_placed', 'email', 'staff')) {
|
|
return;
|
|
}
|
|
|
|
// Send email
|
|
$this->send_email('order_placed', 'staff', $order);
|
|
}
|
|
|
|
/**
|
|
* Send customer note email
|
|
*
|
|
* @param array $args
|
|
*/
|
|
public function send_customer_note_email($args) {
|
|
$order = wc_get_order($args['order_id']);
|
|
|
|
if (!$order) {
|
|
return;
|
|
}
|
|
|
|
// Check if event is enabled
|
|
if (!$this->is_event_enabled('customer_note', 'email', 'customer')) {
|
|
return;
|
|
}
|
|
|
|
// Send email with note data
|
|
$this->send_email('customer_note', 'customer', $order, ['note' => $args['customer_note']]);
|
|
}
|
|
|
|
/**
|
|
* Send new customer email
|
|
*
|
|
* @param int $customer_id
|
|
* @param array $new_customer_data
|
|
* @param bool $password_generated
|
|
*/
|
|
public function send_new_customer_email($customer_id, $new_customer_data = [], $password_generated = false) {
|
|
// Check if event is enabled
|
|
if (!$this->is_event_enabled('new_customer', 'email', 'customer')) {
|
|
return;
|
|
}
|
|
|
|
$customer = new \WC_Customer($customer_id);
|
|
|
|
// Send email
|
|
$this->send_email('new_customer', 'customer', $customer, [
|
|
'password_generated' => $password_generated,
|
|
'user_login' => $new_customer_data['user_login'] ?? '',
|
|
'user_pass' => $new_customer_data['user_pass'] ?? '',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Handle password reset email - intercept WordPress default and use our template
|
|
*
|
|
* @param string $message Email message (we replace this)
|
|
* @param string $key Reset key
|
|
* @param string $user_login User login
|
|
* @param WP_User $user_data User object
|
|
* @return string Empty string to prevent WordPress sending default email
|
|
*/
|
|
public function handle_password_reset_email($message, $key, $user_login, $user_data) {
|
|
// Check if WooNooW notification system is enabled
|
|
if (!self::is_enabled()) {
|
|
return $message; // Use WordPress default
|
|
}
|
|
|
|
// Check if event is enabled
|
|
if (!$this->is_event_enabled('password_reset', 'email', 'customer')) {
|
|
return $message; // Use WordPress default
|
|
}
|
|
|
|
// Build reset URL - use SPA route if available, otherwise WordPress default
|
|
$site_url = get_site_url();
|
|
|
|
// Check if this is a WooCommerce customer - use SPA reset page
|
|
// Otherwise fall back to WordPress default reset page
|
|
$reset_link = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
|
|
|
|
// Create a pseudo WC_Customer for template rendering
|
|
$customer = null;
|
|
if (class_exists('WC_Customer')) {
|
|
try {
|
|
$customer = new \WC_Customer($user_data->ID);
|
|
} catch (\Exception $e) {
|
|
$customer = null;
|
|
}
|
|
}
|
|
|
|
// Send our custom email
|
|
$this->send_password_reset_email($user_data, $key, $reset_link, $customer);
|
|
|
|
// Return empty string to prevent WordPress from sending its default plain-text email
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Send password reset email using our template
|
|
*
|
|
* @param WP_User $user User object
|
|
* @param string $key Reset key
|
|
* @param string $reset_link Full reset link URL
|
|
* @param WC_Customer|null $customer WooCommerce customer object if available
|
|
*/
|
|
private function send_password_reset_email($user, $key, $reset_link, $customer = null) {
|
|
// Get email renderer
|
|
$renderer = EmailRenderer::instance();
|
|
|
|
// Build extra data for template variables
|
|
$extra_data = [
|
|
'reset_key' => $key,
|
|
'reset_link' => $reset_link,
|
|
'user_login' => $user->user_login,
|
|
'user_email' => $user->user_email,
|
|
'customer_name' => $user->display_name ?: $user->user_login,
|
|
'customer_email' => $user->user_email,
|
|
];
|
|
|
|
// Use WC_Customer if available for better template rendering
|
|
$data = $customer ?: $user;
|
|
|
|
// Render email
|
|
$email = $renderer->render('password_reset', 'customer', $data, $extra_data);
|
|
|
|
if (!$email) {
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('[EmailManager] Password reset email rendering failed for user: ' . $user->user_login);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Send email via wp_mail
|
|
$headers = [
|
|
'Content-Type: text/html; charset=UTF-8',
|
|
'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>',
|
|
];
|
|
|
|
$sent = wp_mail($email['to'], $email['subject'], $email['body'], $headers);
|
|
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('[EmailManager] Password reset email sent to ' . $email['to'] . ' - Result: ' . ($sent ? 'success' : 'failed'));
|
|
}
|
|
|
|
// Log email sent
|
|
do_action('woonoow_email_sent', 'password_reset', 'customer', $email);
|
|
}
|
|
|
|
/**
|
|
* Send low stock email
|
|
*
|
|
* @param WC_Product $product
|
|
*/
|
|
public function send_low_stock_email($product) {
|
|
// Check if event is enabled
|
|
if (!$this->is_event_enabled('low_stock', 'email', 'staff')) {
|
|
return;
|
|
}
|
|
|
|
// Send email
|
|
$this->send_email('low_stock', 'staff', $product);
|
|
}
|
|
|
|
/**
|
|
* Send out of stock email
|
|
*
|
|
* @param WC_Product $product
|
|
*/
|
|
public function send_out_of_stock_email($product) {
|
|
// Check if event is enabled
|
|
if (!$this->is_event_enabled('out_of_stock', 'email', 'staff')) {
|
|
return;
|
|
}
|
|
|
|
// Send email
|
|
$this->send_email('out_of_stock', 'staff', $product);
|
|
}
|
|
|
|
/**
|
|
* Check stock levels when product stock is updated
|
|
*
|
|
* @param WC_Product $product
|
|
*/
|
|
public function check_stock_levels($product) {
|
|
$stock = $product->get_stock_quantity();
|
|
$low_stock_threshold = get_option('woocommerce_notify_low_stock_amount', 2);
|
|
|
|
if ($stock <= 0) {
|
|
$this->send_out_of_stock_email($product);
|
|
} elseif ($stock <= $low_stock_threshold) {
|
|
$this->send_low_stock_email($product);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if event is enabled
|
|
*
|
|
* @param string $event_id
|
|
* @param string $channel_id
|
|
* @param string $recipient_type
|
|
* @return bool
|
|
*/
|
|
private function is_event_enabled($event_id, $channel_id, $recipient_type) {
|
|
$settings = get_option('woonoow_notification_settings', []);
|
|
|
|
// Check if event exists and channel is configured
|
|
if (isset($settings['events'][$event_id]['channels'][$channel_id])) {
|
|
return $settings['events'][$event_id]['channels'][$channel_id]['enabled'] ?? false;
|
|
}
|
|
|
|
// Default: enable email notifications if not explicitly configured
|
|
// This allows the plugin to work out-of-the-box with default templates
|
|
if ($channel_id === 'email') {
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('[EmailManager] Event not configured, using default: enabled');
|
|
}
|
|
return true; // Enable by default
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Send email
|
|
*
|
|
* @param string $event_id
|
|
* @param string $recipient_type
|
|
* @param mixed $data
|
|
* @param array $extra_data
|
|
*/
|
|
private function send_email($event_id, $recipient_type, $data, $extra_data = []) {
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('[EmailManager] send_email called - Event: ' . $event_id . ', Recipient: ' . $recipient_type);
|
|
}
|
|
|
|
// Get email renderer
|
|
$renderer = EmailRenderer::instance();
|
|
|
|
// Render email
|
|
$email = $renderer->render($event_id, $recipient_type, $data, $extra_data);
|
|
|
|
if (!$email) {
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('[EmailManager] Email rendering failed for event: ' . $event_id);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('[EmailManager] Email rendered successfully - To: ' . $email['to'] . ', Subject: ' . $email['subject']);
|
|
}
|
|
|
|
// Send email via wp_mail
|
|
$headers = [
|
|
'Content-Type: text/html; charset=UTF-8',
|
|
'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>',
|
|
];
|
|
|
|
$sent = wp_mail($email['to'], $email['subject'], $email['body'], $headers);
|
|
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('[EmailManager] wp_mail called - Result: ' . ($sent ? 'success' : 'failed'));
|
|
}
|
|
|
|
// Log email sent
|
|
do_action('woonoow_email_sent', $event_id, $recipient_type, $email);
|
|
}
|
|
}
|