Files
WooNooW/includes/Core/Notifications/TemplateProvider.php
dwindown aa9ca24988 fix: All 7 User Feedback Issues Resolved! 🎯
##  Issue 1: WordPress Media Not Loading
**Problem:** WP media library not loaded error
**Solution:**
- Added fallback to URL prompt
- Better error handling
- User can still insert images if WP media fails

##  Issue 2: Button Variables Filter
**Problem:** All variables shown in button link field
**Solution:**
- Filter to only show URL variables
- Applied to both RichTextEditor and EmailBuilder
- Only `*_url` variables displayed

**Before:** {order_number} {customer_name} {order_total} ...
**After:** {order_url} {store_url} only

##  Issue 3: Color Customization Note
**Noted for future:**
- Hero card gradient colors
- Button primary color
- Button secondary border color
- Will be added to email customization form later

##  Issue 4 & 5: Heading Display in Editor & Builder
**Problem:** Headings looked like paragraphs
**Solution:**
- Added Tailwind heading styles to RichTextEditor
- Added heading styles to BlockRenderer
- Now headings are visually distinct:
  - H1: 3xl, bold
  - H2: 2xl, bold
  - H3: xl, bold
  - H4: lg, bold

**Files Modified:**
- `components/ui/rich-text-editor.tsx`
- `components/EmailBuilder/BlockRenderer.tsx`

##  Issue 6: Order Items Variable
**Problem:** No variable for product list/table
**Solution:**
- Added `order_items` variable
- Description: "Order Items (formatted table)"
- Will render formatted product list in emails

**File Modified:**
- `includes/Core/Notifications/TemplateProvider.php`

##  Issue 7: Remove Edit Icon from Spacer/Divider
**Problem:** Edit button shown but no options to edit
**Solution:**
- Conditional rendering of edit button
- Only show for `card` and `button` blocks
- Spacer and divider only show: ↑ ↓ ×

**File Modified:**
- `components/EmailBuilder/BlockRenderer.tsx`

---

## 📋 Summary

All user feedback addressed:
1.  WP Media fallback
2.  Button variables filtered
3.  Color customization noted
4.  Headings visible in editor
5.  Headings visible in builder
6.  Order items variable added
7.  Edit icon removed from spacer/divider

Ready for testing! ��
2025-11-13 10:12:03 +07:00

358 lines
12 KiB
PHP

<?php
/**
* Notification Template Provider
*
* Manages notification templates for all channels.
*
* @package WooNooW\Core\Notifications
*/
namespace WooNooW\Core\Notifications;
class TemplateProvider {
/**
* Option key for storing templates
*/
const OPTION_KEY = 'woonoow_notification_templates';
/**
* Get all templates
*
* @return array
*/
public static function get_templates() {
$templates = get_option(self::OPTION_KEY, []);
// Merge with defaults
$defaults = self::get_default_templates();
return array_merge($defaults, $templates);
}
/**
* Get template for specific event and channel
*
* @param string $event_id Event ID
* @param string $channel_id Channel ID
* @return array|null
*/
public static function get_template($event_id, $channel_id) {
$templates = self::get_templates();
$key = "{$event_id}_{$channel_id}";
if (isset($templates[$key])) {
return $templates[$key];
}
// Return default if exists
$defaults = self::get_default_templates();
return $defaults[$key] ?? null;
}
/**
* Save template
*
* @param string $event_id Event ID
* @param string $channel_id Channel ID
* @param array $template Template data
* @return bool
*/
public static function save_template($event_id, $channel_id, $template) {
$templates = get_option(self::OPTION_KEY, []);
$key = "{$event_id}_{$channel_id}";
$templates[$key] = [
'event_id' => $event_id,
'channel_id' => $channel_id,
'subject' => $template['subject'] ?? '',
'body' => $template['body'] ?? '',
'variables' => $template['variables'] ?? [],
'updated_at' => current_time('mysql'),
];
return update_option(self::OPTION_KEY, $templates);
}
/**
* Delete template (revert to default)
*
* @param string $event_id Event ID
* @param string $channel_id Channel ID
* @return bool
*/
public static function delete_template($event_id, $channel_id) {
$templates = get_option(self::OPTION_KEY, []);
$key = "{$event_id}_{$channel_id}";
if (isset($templates[$key])) {
unset($templates[$key]);
return update_option(self::OPTION_KEY, $templates);
}
return false;
}
/**
* Get WooCommerce email template content
*
* @param string $email_id WooCommerce email ID
* @return array|null
*/
private static function get_wc_email_template($email_id) {
if (!function_exists('WC')) {
return null;
}
$mailer = WC()->mailer();
$emails = $mailer->get_emails();
if (isset($emails[$email_id])) {
$email = $emails[$email_id];
return [
'subject' => $email->get_subject(),
'heading' => $email->get_heading(),
'enabled' => $email->is_enabled(),
];
}
return null;
}
/**
* Get default templates
*
* @return array
*/
public static function get_default_templates() {
// Try to load WooCommerce email templates
$wc_new_order = self::get_wc_email_template('WC_Email_New_Order');
$wc_processing = self::get_wc_email_template('WC_Email_Customer_Processing_Order');
$wc_completed = self::get_wc_email_template('WC_Email_Customer_Completed_Order');
$wc_refunded = self::get_wc_email_template('WC_Email_Customer_Refunded_Order');
$wc_cancelled = self::get_wc_email_template('WC_Email_Cancelled_Order');
$wc_new_account = self::get_wc_email_template('WC_Email_Customer_New_Account');
$wc_customer_note = self::get_wc_email_template('WC_Email_Customer_Note');
return [
// Email templates - Staff
'order_placed_email' => [
'event_id' => 'order_placed',
'channel_id' => 'email',
'subject' => $wc_new_order['subject'] ?? __('New Order #{order_number}', 'woonoow'),
'body' => __('[card]
<h1>New Order Received</h1>
<p>Hi Admin,</p>
<p>You have received a new order from <strong>{customer_name}</strong>.</p>
<div class="info-box">
<p style="margin: 0;"><strong>Order #{order_number}</strong></p>
<p style="margin: 0;">Total: {order_total}</p>
</div>
[/card]
[card]
<h2>Customer Details</h2>
<p><strong>Name:</strong> {customer_name}<br>
<strong>Email:</strong> {customer_email}<br>
<strong>Phone:</strong> {customer_phone}</p>
[/card]
[card]
<p style="text-align: center;"><a href="{order_url}" class="button">View Order Details</a></p>
[/card]', 'woonoow'),
'variables' => self::get_order_variables(),
'wc_email_id' => 'WC_Email_New_Order',
],
'order_processing_email' => [
'event_id' => 'order_processing',
'channel_id' => 'email',
'subject' => $wc_processing['subject'] ?? __('Your order #{order_number} is being processed', 'woonoow'),
'body' => __('[card type="success"]
<h1>✅ Order Confirmed!</h1>
<p>Hi {customer_name},</p>
<p>Thank you for your order! We\'re now processing it and will notify you once it ships.</p>
[/card]
[card]
<h2>Order Summary</h2>
<div class="info-box">
<p style="margin: 0;"><strong>Order #{order_number}</strong></p>
<p style="margin: 0;">Total: {order_total}</p>
<p style="margin: 0;">Payment: {payment_method}</p>
</div>
{order_items}
[/card]
[card]
<p style="text-align: center;"><a href="{order_url}" class="button">Track Your Order</a></p>
<p style="text-align: center; font-size: 14px; color: #888;">Questions? Reply to this email or contact us.</p>
[/card]', 'woonoow'),
'variables' => self::get_order_variables(),
'wc_email_id' => 'WC_Email_Customer_Processing_Order',
],
'order_completed_email' => [
'event_id' => 'order_completed',
'channel_id' => 'email',
'subject' => $wc_completed['subject'] ?? __('Your order #{order_number} is complete', 'woonoow'),
'body' => __("Hi {customer_name},\n\nYour order has been completed and shipped!\n\nOrder Number: {order_number}\nOrder Total: {order_total}\nTracking Number: {tracking_number}\n\nThank you for shopping with us!\n\nBest regards,\n{store_name}", 'woonoow'),
'variables' => self::get_order_variables(),
'wc_email_id' => 'WC_Email_Customer_Completed_Order',
],
'order_cancelled_email' => [
'event_id' => 'order_cancelled',
'channel_id' => 'email',
'subject' => $wc_cancelled['subject'] ?? __('Order #{order_number} has been cancelled', 'woonoow'),
'body' => __("Hi Admin,\n\nOrder #{order_number} has been cancelled.\n\nOrder Number: {order_number}\nOrder Total: {order_total}\nCustomer: {customer_name}\n\nView order: {order_url}", 'woonoow'),
'variables' => self::get_order_variables(),
'wc_email_id' => 'WC_Email_Cancelled_Order',
],
'order_refunded_email' => [
'event_id' => 'order_refunded',
'channel_id' => 'email',
'subject' => $wc_refunded['subject'] ?? __('Your order #{order_number} has been refunded', 'woonoow'),
'body' => __("Hi {customer_name},\n\nYour order has been refunded.\n\nOrder Number: {order_number}\nRefund Amount: {refund_amount}\n\nThe refund will be processed within 5-7 business days.\n\nBest regards,\n{store_name}", 'woonoow'),
'variables' => self::get_order_variables(),
'wc_email_id' => 'WC_Email_Customer_Refunded_Order',
],
'low_stock_email' => [
'event_id' => 'low_stock',
'channel_id' => 'email',
'subject' => __('Low Stock Alert: {product_name}', 'woonoow'),
'body' => __("Hi Admin,\n\nThe following product is running low on stock:\n\nProduct: {product_name}\nSKU: {product_sku}\nCurrent Stock: {stock_quantity}\n\nPlease restock soon.\n\nView product: {product_url}", 'woonoow'),
'variables' => self::get_product_variables(),
],
'out_of_stock_email' => [
'event_id' => 'out_of_stock',
'channel_id' => 'email',
'subject' => __('Out of Stock Alert: {product_name}', 'woonoow'),
'body' => __("Hi Admin,\n\nThe following product is now out of stock:\n\nProduct: {product_name}\nSKU: {product_sku}\n\nPlease restock immediately.\n\nView product: {product_url}", 'woonoow'),
'variables' => self::get_product_variables(),
],
'new_customer_email' => [
'event_id' => 'new_customer',
'channel_id' => 'email',
'subject' => $wc_new_account['subject'] ?? __('Welcome to {store_name}!', 'woonoow'),
'body' => __("Hi {customer_name},\n\nWelcome to {store_name}!\n\nYour account has been created successfully.\n\nEmail: {customer_email}\n\nYou can now browse our products and place orders.\n\nVisit our store: {store_url}\n\nBest regards,\n{store_name}", 'woonoow'),
'variables' => self::get_customer_variables(),
'wc_email_id' => 'WC_Email_Customer_New_Account',
],
'customer_note_email' => [
'event_id' => 'customer_note',
'channel_id' => 'email',
'subject' => $wc_customer_note['subject'] ?? __('Note added to your order #{order_number}', 'woonoow'),
'body' => __("Hi {customer_name},\n\nA note has been added to your order:\n\nOrder Number: {order_number}\nNote: {note_content}\n\nView order: {order_url}\n\nBest regards,\n{store_name}", 'woonoow'),
'variables' => self::get_order_variables(),
'wc_email_id' => 'WC_Email_Customer_Note',
],
// Push notification templates
'order_placed_push' => [
'event_id' => 'order_placed',
'channel_id' => 'push',
'subject' => __('New Order #{order_number}', 'woonoow'),
'body' => __('New order from {customer_name} - {order_total}', 'woonoow'),
'variables' => self::get_order_variables(),
],
'order_processing_push' => [
'event_id' => 'order_processing',
'channel_id' => 'push',
'subject' => __('Order Processing', 'woonoow'),
'body' => __('Your order #{order_number} is being processed', 'woonoow'),
'variables' => self::get_order_variables(),
],
'order_completed_push' => [
'event_id' => 'order_completed',
'channel_id' => 'push',
'subject' => __('Order Completed', 'woonoow'),
'body' => __('Your order #{order_number} has been completed!', 'woonoow'),
'variables' => self::get_order_variables(),
],
'low_stock_push' => [
'event_id' => 'low_stock',
'channel_id' => 'push',
'subject' => __('Low Stock Alert', 'woonoow'),
'body' => __('{product_name} is running low on stock', 'woonoow'),
'variables' => self::get_product_variables(),
],
];
}
/**
* Get available order variables
*
* @return array
*/
public static function get_order_variables() {
return [
'order_number' => __('Order Number', 'woonoow'),
'order_total' => __('Order Total', 'woonoow'),
'order_status' => __('Order Status', 'woonoow'),
'order_date' => __('Order Date', 'woonoow'),
'order_url' => __('Order URL', 'woonoow'),
'order_items' => __('Order Items (formatted table)', 'woonoow'),
'payment_method' => __('Payment Method', 'woonoow'),
'shipping_method' => __('Shipping Method', 'woonoow'),
'tracking_number' => __('Tracking Number', 'woonoow'),
'refund_amount' => __('Refund Amount', 'woonoow'),
'customer_name' => __('Customer Name', 'woonoow'),
'customer_email' => __('Customer Email', 'woonoow'),
'customer_phone' => __('Customer Phone', 'woonoow'),
'billing_address' => __('Billing Address', 'woonoow'),
'shipping_address' => __('Shipping Address', 'woonoow'),
'store_name' => __('Store Name', 'woonoow'),
'store_url' => __('Store URL', 'woonoow'),
'store_email' => __('Store Email', 'woonoow'),
];
}
/**
* Get available product variables
*
* @return array
*/
public static function get_product_variables() {
return [
'product_name' => __('Product Name', 'woonoow'),
'product_sku' => __('Product SKU', 'woonoow'),
'product_url' => __('Product URL', 'woonoow'),
'stock_quantity' => __('Stock Quantity', 'woonoow'),
'store_name' => __('Store Name', 'woonoow'),
'store_url' => __('Store URL', 'woonoow'),
];
}
/**
* Get available customer variables
*
* @return array
*/
public static function get_customer_variables() {
return [
'customer_name' => __('Customer Name', 'woonoow'),
'customer_email' => __('Customer Email', 'woonoow'),
'customer_phone' => __('Customer Phone', 'woonoow'),
'store_name' => __('Store Name', 'woonoow'),
'store_url' => __('Store URL', 'woonoow'),
'store_email' => __('Store Email', 'woonoow'),
];
}
/**
* Replace variables in template
*
* @param string $content Content with variables
* @param array $data Data to replace variables
* @return string
*/
public static function replace_variables($content, $data) {
foreach ($data as $key => $value) {
$content = str_replace('{' . $key . '}', $value, $content);
}
return $content;
}
}