## ✅ Step 1-3: Email System Core ### EmailManager.php - ✅ Disables WooCommerce emails (prevents duplicates) - ✅ Hooks into all WC order status changes - ✅ Hooks into customer, product events - ✅ Checks if events are enabled before sending - ✅ Sends via wp_mail() (SMTP plugin compatible) ### EmailRenderer.php - ✅ Renders emails with design templates - ✅ Variable replacement system - ✅ Gets recipient email (staff/customer) - ✅ Loads order/product/customer variables - ✅ Filter hook: `woonoow_email_template` - ✅ Supports HTML template designs ### Email Design Templates (3) **templates/emails/modern.html** - ✅ Clean, minimalist, Apple-inspired - ✅ Dark mode support - ✅ Mobile responsive - ✅ 2024 design trends **templates/emails/classic.html** - ✅ Professional, traditional - ✅ Gradient header - ✅ Table styling - ✅ Business-appropriate **templates/emails/minimal.html** - ✅ Ultra-clean, monospace font - ✅ Black & white aesthetic - ✅ Text-focused - ✅ Dark mode invert ### Architecture ``` Design Template (HTML) → Content Template (Text) → Final Email modern.html → order_processing → Beautiful HTML ``` --- **Next:** Rich text editor + Content templates 🎨
275 lines
7.4 KiB
PHP
275 lines
7.4 KiB
PHP
<?php
|
||
/**
|
||
* Email Renderer
|
||
*
|
||
* Renders email templates with content
|
||
*
|
||
* @package WooNooW\Core\Notifications
|
||
*/
|
||
|
||
namespace WooNooW\Core\Notifications;
|
||
|
||
class EmailRenderer {
|
||
|
||
/**
|
||
* Instance
|
||
*/
|
||
private static $instance = null;
|
||
|
||
/**
|
||
* Get instance
|
||
*/
|
||
public static function instance() {
|
||
if (null === self::$instance) {
|
||
self::$instance = new self();
|
||
}
|
||
return self::$instance;
|
||
}
|
||
|
||
/**
|
||
* Render email
|
||
*
|
||
* @param string $event_id Event ID (order_placed, order_processing, etc.)
|
||
* @param string $recipient_type Recipient type (staff, customer)
|
||
* @param mixed $data Order, Product, or Customer object
|
||
* @param array $extra_data Additional data
|
||
* @return array|null ['to', 'subject', 'body']
|
||
*/
|
||
public function render($event_id, $recipient_type, $data, $extra_data = []) {
|
||
// Get template settings
|
||
$template_settings = $this->get_template_settings($event_id, $recipient_type);
|
||
|
||
if (!$template_settings) {
|
||
return null;
|
||
}
|
||
|
||
// Get recipient email
|
||
$to = $this->get_recipient_email($recipient_type, $data);
|
||
|
||
if (!$to) {
|
||
return null;
|
||
}
|
||
|
||
// Get variables
|
||
$variables = $this->get_variables($event_id, $data, $extra_data);
|
||
|
||
// Replace variables in subject and content
|
||
$subject = $this->replace_variables($template_settings['subject'], $variables);
|
||
$content = $this->replace_variables($template_settings['body'], $variables);
|
||
|
||
// Get HTML template design
|
||
$design_template = $this->get_design_template($template_settings['design'] ?? 'modern');
|
||
|
||
// Render final HTML
|
||
$html = $this->render_html($design_template, $content, $subject, $variables);
|
||
|
||
return [
|
||
'to' => $to,
|
||
'subject' => $subject,
|
||
'body' => $html,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Get template settings
|
||
*
|
||
* @param string $event_id
|
||
* @param string $recipient_type
|
||
* @return array|null
|
||
*/
|
||
private function get_template_settings($event_id, $recipient_type) {
|
||
// Get saved template
|
||
$template = TemplateProvider::get_template($event_id, 'email');
|
||
|
||
if (!$template) {
|
||
return null;
|
||
}
|
||
|
||
// Get design template preference
|
||
$settings = get_option('woonoow_notification_settings', []);
|
||
$design = $settings['email_design_template'] ?? 'modern';
|
||
|
||
return [
|
||
'subject' => $template['subject'] ?? '',
|
||
'body' => $template['body'] ?? '',
|
||
'design' => $design,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Get recipient email
|
||
*
|
||
* @param string $recipient_type
|
||
* @param mixed $data
|
||
* @return string|null
|
||
*/
|
||
private function get_recipient_email($recipient_type, $data) {
|
||
if ($recipient_type === 'staff') {
|
||
return get_option('admin_email');
|
||
}
|
||
|
||
// Customer
|
||
if ($data instanceof \WC_Order) {
|
||
return $data->get_billing_email();
|
||
}
|
||
|
||
if ($data instanceof \WC_Customer) {
|
||
return $data->get_email();
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* Get variables for template
|
||
*
|
||
* @param string $event_id
|
||
* @param mixed $data
|
||
* @param array $extra_data
|
||
* @return array
|
||
*/
|
||
private function get_variables($event_id, $data, $extra_data = []) {
|
||
$variables = [
|
||
'store_name' => get_bloginfo('name'),
|
||
'store_url' => home_url(),
|
||
'site_title' => get_bloginfo('name'),
|
||
];
|
||
|
||
// Order variables
|
||
if ($data instanceof \WC_Order) {
|
||
$variables = array_merge($variables, [
|
||
'order_number' => $data->get_order_number(),
|
||
'order_id' => $data->get_id(),
|
||
'order_date' => $data->get_date_created()->date('F j, Y'),
|
||
'order_total' => $data->get_formatted_order_total(),
|
||
'order_subtotal' => wc_price($data->get_subtotal()),
|
||
'order_tax' => wc_price($data->get_total_tax()),
|
||
'order_shipping' => wc_price($data->get_shipping_total()),
|
||
'order_discount' => wc_price($data->get_discount_total()),
|
||
'order_status' => wc_get_order_status_name($data->get_status()),
|
||
'order_url' => $data->get_view_order_url(),
|
||
'payment_method' => $data->get_payment_method_title(),
|
||
'shipping_method' => $data->get_shipping_method(),
|
||
'customer_name' => $data->get_formatted_billing_full_name(),
|
||
'customer_first_name' => $data->get_billing_first_name(),
|
||
'customer_last_name' => $data->get_billing_last_name(),
|
||
'customer_email' => $data->get_billing_email(),
|
||
'customer_phone' => $data->get_billing_phone(),
|
||
'billing_address' => $data->get_formatted_billing_address(),
|
||
'shipping_address' => $data->get_formatted_shipping_address(),
|
||
]);
|
||
|
||
// Order items
|
||
$items_html = '';
|
||
foreach ($data->get_items() as $item) {
|
||
$product = $item->get_product();
|
||
$items_html .= sprintf(
|
||
'<tr><td>%s × %d</td><td>%s</td></tr>',
|
||
$item->get_name(),
|
||
$item->get_quantity(),
|
||
wc_price($item->get_total())
|
||
);
|
||
}
|
||
$variables['order_items'] = $items_html;
|
||
}
|
||
|
||
// Product variables
|
||
if ($data instanceof \WC_Product) {
|
||
$variables = array_merge($variables, [
|
||
'product_id' => $data->get_id(),
|
||
'product_name' => $data->get_name(),
|
||
'product_sku' => $data->get_sku(),
|
||
'product_price' => wc_price($data->get_price()),
|
||
'product_url' => get_permalink($data->get_id()),
|
||
'stock_quantity' => $data->get_stock_quantity(),
|
||
'stock_status' => $data->get_stock_status(),
|
||
]);
|
||
}
|
||
|
||
// Customer variables
|
||
if ($data instanceof \WC_Customer) {
|
||
$variables = array_merge($variables, [
|
||
'customer_id' => $data->get_id(),
|
||
'customer_name' => $data->get_display_name(),
|
||
'customer_first_name' => $data->get_first_name(),
|
||
'customer_last_name' => $data->get_last_name(),
|
||
'customer_email' => $data->get_email(),
|
||
'customer_username' => $data->get_username(),
|
||
]);
|
||
}
|
||
|
||
// Merge extra data
|
||
$variables = array_merge($variables, $extra_data);
|
||
|
||
return apply_filters('woonoow_email_variables', $variables, $event_id, $data);
|
||
}
|
||
|
||
/**
|
||
* Replace variables in text
|
||
*
|
||
* @param string $text
|
||
* @param array $variables
|
||
* @return string
|
||
*/
|
||
private function replace_variables($text, $variables) {
|
||
foreach ($variables as $key => $value) {
|
||
$text = str_replace('{' . $key . '}', $value, $text);
|
||
}
|
||
|
||
return $text;
|
||
}
|
||
|
||
/**
|
||
* Get design template path
|
||
*
|
||
* @param string $design Template name (modern, classic, minimal)
|
||
* @return string
|
||
*/
|
||
private function get_design_template($design) {
|
||
$template_path = WOONOOW_PATH . 'templates/emails/' . $design . '.html';
|
||
|
||
// Allow filtering template path
|
||
$template_path = apply_filters('woonoow_email_template', $template_path, $design);
|
||
|
||
// Fallback to modern if template doesn't exist
|
||
if (!file_exists($template_path)) {
|
||
$template_path = WOONOOW_PATH . 'templates/emails/modern.html';
|
||
}
|
||
|
||
return $template_path;
|
||
}
|
||
|
||
/**
|
||
* Render HTML email
|
||
*
|
||
* @param string $template_path Path to HTML template
|
||
* @param string $content Email content (HTML)
|
||
* @param string $subject Email subject
|
||
* @param array $variables All variables
|
||
* @return string
|
||
*/
|
||
private function render_html($template_path, $content, $subject, $variables) {
|
||
if (!file_exists($template_path)) {
|
||
// Fallback to plain HTML
|
||
return $content;
|
||
}
|
||
|
||
// Load template
|
||
$html = file_get_contents($template_path);
|
||
|
||
// Replace placeholders
|
||
$html = str_replace('{{email_heading}}', $subject, $html);
|
||
$html = str_replace('{{email_content}}', $content, $html);
|
||
$html = str_replace('{{store_name}}', $variables['store_name'], $html);
|
||
$html = str_replace('{{store_url}}', $variables['store_url'], $html);
|
||
$html = str_replace('{{current_year}}', date('Y'), $html);
|
||
|
||
// Replace all other variables
|
||
foreach ($variables as $key => $value) {
|
||
$html = str_replace('{{' . $key . '}}', $value, $html);
|
||
}
|
||
|
||
return $html;
|
||
}
|
||
}
|