✅ New cleaner syntax implemented: - [card:type] instead of [card type='type'] - [button:style](url)Text[/button] instead of [button url='...' style='...'] - Standard markdown images:  ✅ Variable protection from markdown parsing: - Variables with underscores (e.g., {order_items_table}) now protected - HTML comment placeholders prevent italic/bold parsing - All variables render correctly in preview ✅ Button rendering fixes: - Buttons work in Visual mode inside cards - Buttons work in Preview mode - Button clicks prevented in visual editor - Proper styling for solid and outline buttons ✅ Backward compatibility: - Old syntax still supported - No breaking changes ✅ Bug fixes: - Fixed order_item_table → order_items_table naming - Fixed button regex to match across newlines - Added button/image parsing to parseMarkdownBasics - Prevented button clicks on .button and .button-outline classes 📚 Documentation: - NEW_MARKDOWN_SYNTAX.md - Complete user guide - MARKDOWN_SYNTAX_AND_VARIABLES.md - Technical analysis
348 lines
10 KiB
PHP
348 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* Notification Template Provider
|
|
*
|
|
* Manages notification templates for all channels.
|
|
*
|
|
* @package WooNooW\Core\Notifications
|
|
*/
|
|
|
|
namespace WooNooW\Core\Notifications;
|
|
|
|
use WooNooW\Email\DefaultTemplates as EmailDefaultTemplates;
|
|
|
|
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
|
|
* @param string $recipient_type Recipient type ('customer' or 'staff')
|
|
* @return array|null
|
|
*/
|
|
public static function get_template($event_id, $channel_id, $recipient_type = 'customer') {
|
|
$templates = self::get_templates();
|
|
|
|
$key = "{$recipient_type}_{$event_id}_{$channel_id}";
|
|
|
|
if (isset($templates[$key])) {
|
|
return $templates[$key];
|
|
}
|
|
|
|
// Return default if exists
|
|
$defaults = self::get_default_templates();
|
|
|
|
if (isset($defaults[$key])) {
|
|
return $defaults[$key];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Save template
|
|
*
|
|
* @param string $event_id Event ID
|
|
* @param string $channel_id Channel ID
|
|
* @param array $template Template data
|
|
* @param string $recipient_type Recipient type ('customer' or 'staff')
|
|
* @return bool
|
|
*/
|
|
public static function save_template($event_id, $channel_id, $template, $recipient_type = 'customer') {
|
|
$templates = get_option(self::OPTION_KEY, []);
|
|
|
|
$key = "{$recipient_type}_{$event_id}_{$channel_id}";
|
|
|
|
$templates[$key] = [
|
|
'event_id' => $event_id,
|
|
'channel_id' => $channel_id,
|
|
'recipient_type' => $recipient_type,
|
|
'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
|
|
* @param string $recipient_type Recipient type ('customer' or 'staff')
|
|
* @return bool
|
|
*/
|
|
public static function delete_template($event_id, $channel_id, $recipient_type = 'customer') {
|
|
$templates = get_option(self::OPTION_KEY, []);
|
|
|
|
$key = "{$recipient_type}_{$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() {
|
|
$templates = [];
|
|
|
|
// Get all events from EventRegistry (single source of truth)
|
|
$all_events = EventRegistry::get_all_events();
|
|
|
|
// Get email templates from DefaultTemplates
|
|
$allEmailTemplates = EmailDefaultTemplates::get_all_templates();
|
|
|
|
foreach ($all_events as $event) {
|
|
$event_id = $event['id'];
|
|
$recipient_type = $event['recipient_type'];
|
|
// Get template body from the new clean markdown source
|
|
$body = $allEmailTemplates[$recipient_type][$event_id] ?? '';
|
|
$subject = EmailDefaultTemplates::get_default_subject($recipient_type, $event_id);
|
|
|
|
// If template doesn't exist, create a simple fallback
|
|
if (empty($body)) {
|
|
$body = "[card]\n\n## Notification\n\nYou have a new notification about {$event_id}.\n\n[/card]";
|
|
$subject = __('Notification from {store_name}', 'woonoow');
|
|
}
|
|
|
|
$templates["{$recipient_type}_{$event_id}_email"] = [
|
|
'event_id' => $event_id,
|
|
'channel_id' => 'email',
|
|
'recipient_type' => $recipient_type,
|
|
'subject' => $subject,
|
|
'body' => $body,
|
|
'variables' => self::get_variables_for_event($event_id),
|
|
];
|
|
}
|
|
|
|
// Add push notification templates
|
|
$templates['staff_order_placed_push'] = [
|
|
'event_id' => 'order_placed',
|
|
'channel_id' => 'push',
|
|
'recipient_type' => 'staff',
|
|
'subject' => __('New Order #{order_number}', 'woonoow'),
|
|
'body' => __('New order from {customer_name} - {order_total}', 'woonoow'),
|
|
'variables' => self::get_order_variables(),
|
|
];
|
|
$templates['customer_order_processing_push'] = [
|
|
'event_id' => 'order_processing',
|
|
'channel_id' => 'push',
|
|
'recipient_type' => 'customer',
|
|
'subject' => __('Order Processing', 'woonoow'),
|
|
'body' => __('Your order #{order_number} is being processed', 'woonoow'),
|
|
'variables' => self::get_order_variables(),
|
|
];
|
|
$templates['customer_order_completed_push'] = [
|
|
'event_id' => 'order_completed',
|
|
'channel_id' => 'push',
|
|
'recipient_type' => 'customer',
|
|
'subject' => __('Order Completed', 'woonoow'),
|
|
'body' => __('Your order #{order_number} has been completed!', 'woonoow'),
|
|
'variables' => self::get_order_variables(),
|
|
];
|
|
$templates['staff_order_cancelled_push'] = [
|
|
'event_id' => 'order_cancelled',
|
|
'channel_id' => 'push',
|
|
'recipient_type' => 'staff',
|
|
'subject' => __('Order Cancelled', 'woonoow'),
|
|
'body' => __('Order #{order_number} has been cancelled', 'woonoow'),
|
|
'variables' => self::get_order_variables(),
|
|
];
|
|
$templates['customer_order_refunded_push'] = [
|
|
'event_id' => 'order_refunded',
|
|
'channel_id' => 'push',
|
|
'recipient_type' => 'customer',
|
|
'subject' => __('Order Refunded', 'woonoow'),
|
|
'body' => __('Your order #{order_number} has been refunded', 'woonoow'),
|
|
'variables' => self::get_order_variables(),
|
|
];
|
|
$templates['staff_low_stock_push'] = [
|
|
'event_id' => 'low_stock',
|
|
'channel_id' => 'push',
|
|
'recipient_type' => 'staff',
|
|
'subject' => __('Low Stock Alert', 'woonoow'),
|
|
'body' => __('{product_name} is running low on stock', 'woonoow'),
|
|
'variables' => self::get_product_variables(),
|
|
];
|
|
$templates['staff_out_of_stock_push'] = [
|
|
'event_id' => 'out_of_stock',
|
|
'channel_id' => 'push',
|
|
'recipient_type' => 'staff',
|
|
'subject' => __('Out of Stock Alert', 'woonoow'),
|
|
'body' => __('{product_name} is now out of stock', 'woonoow'),
|
|
'variables' => self::get_product_variables(),
|
|
];
|
|
$templates['customer_new_customer_push'] = [
|
|
'event_id' => 'new_customer',
|
|
'channel_id' => 'push',
|
|
'recipient_type' => 'customer',
|
|
'subject' => __('Welcome!', 'woonoow'),
|
|
'body' => __('Welcome to {store_name}, {customer_name}!', 'woonoow'),
|
|
'variables' => self::get_customer_variables(),
|
|
];
|
|
$templates['customer_customer_note_push'] = [
|
|
'event_id' => 'customer_note',
|
|
'channel_id' => 'push',
|
|
'recipient_type' => 'customer',
|
|
'subject' => __('Order Note Added', 'woonoow'),
|
|
'body' => __('A note has been added to order #{order_number}', 'woonoow'),
|
|
'variables' => self::get_order_variables(),
|
|
];
|
|
|
|
return $templates;
|
|
}
|
|
|
|
/**
|
|
* Get variables for a specific event
|
|
*
|
|
* @param string $event_id Event ID
|
|
* @return array
|
|
*/
|
|
private static function get_variables_for_event($event_id) {
|
|
// Product events
|
|
if (in_array($event_id, ['low_stock', 'out_of_stock'])) {
|
|
return self::get_product_variables();
|
|
}
|
|
|
|
// Customer events (but not order-related)
|
|
if ($event_id === 'new_customer') {
|
|
return self::get_customer_variables();
|
|
}
|
|
|
|
// All other events are order-related
|
|
return self::get_order_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_list' => __('Order Items (formatted list)', 'woonoow'),
|
|
'order_items_table' => __('Order Items (formatted table)', 'woonoow'),
|
|
'payment_method' => __('Payment Method', 'woonoow'),
|
|
'payment_url' => __('Payment URL (for pending payments)', '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;
|
|
}
|
|
}
|