1. API Route Fix (NotificationsController.php): - Changed PUT to POST for /templates/:eventId/:channelId - Frontend was using api.post() but backend only accepted PUT - Templates can now be saved 2. Contextual Variables (EventRegistry.php): - Added get_variables_for_event() method - Returns category-based variables (order, customer, product, etc.) - Merges event-specific variables from event definition - Sorted alphabetically for easy browsing 3. API Response (NotificationsController.php): - Template API now returns available_variables for the event - Frontend can show only relevant variables 4. Frontend (EditTemplate.tsx): - Removed hardcoded 50+ variable list - Now uses template.available_variables from API - Variables update based on selected event type
521 lines
19 KiB
PHP
521 lines
19 KiB
PHP
<?php
|
|
/**
|
|
* Event Registry - Single Source of Truth for Notification Events
|
|
*
|
|
* Defines all notification events in the system with their metadata.
|
|
* Other components query this registry instead of hardcoding event lists.
|
|
*
|
|
* @package WooNooW\Core\Notifications
|
|
*/
|
|
|
|
namespace WooNooW\Core\Notifications;
|
|
|
|
class EventRegistry {
|
|
|
|
/**
|
|
* Get all registered notification events
|
|
*
|
|
* This is the SINGLE SOURCE OF TRUTH for all events in the system.
|
|
* All other components (API, TemplateProvider, etc.) must use this.
|
|
*
|
|
* @return array Event definitions with structure:
|
|
* [
|
|
* 'event_id' => [
|
|
* 'id' => 'event_id',
|
|
* 'label' => 'Human readable label',
|
|
* 'description' => 'What triggers this event',
|
|
* 'category' => 'orders|products|customers',
|
|
* 'recipient_type' => 'staff|customer',
|
|
* 'wc_email' => 'woocommerce_email_id',
|
|
* 'enabled' => true|false,
|
|
* ]
|
|
* ]
|
|
*/
|
|
public static function get_all_events() {
|
|
$events = [
|
|
// ===== CUSTOMER ACCOUNT EVENTS =====
|
|
'new_customer' => [
|
|
'id' => 'new_customer',
|
|
'label' => __('New Customer', 'woonoow'),
|
|
'description' => __('When a new customer registers', 'woonoow'),
|
|
'category' => 'customers',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => 'customer_new_account',
|
|
'enabled' => true,
|
|
],
|
|
'password_reset' => [
|
|
'id' => 'password_reset',
|
|
'label' => __('Password Reset', 'woonoow'),
|
|
'description' => __('When a customer requests a password reset', 'woonoow'),
|
|
'category' => 'customers',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
'variables' => [
|
|
'{reset_link}' => __('Password reset link', 'woonoow'),
|
|
'{reset_key}' => __('Password reset key', 'woonoow'),
|
|
'{user_login}' => __('Username', 'woonoow'),
|
|
'{user_email}' => __('User email', 'woonoow'),
|
|
'{site_name}' => __('Site name', 'woonoow'),
|
|
],
|
|
],
|
|
|
|
// ===== NEWSLETTER EVENTS =====
|
|
'newsletter_welcome' => [
|
|
'id' => 'newsletter_welcome',
|
|
'label' => __('Newsletter Welcome', 'woonoow'),
|
|
'description' => __('Welcome email sent when someone subscribes to newsletter', 'woonoow'),
|
|
'category' => 'marketing',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
],
|
|
'newsletter_subscribed_admin' => [
|
|
'id' => 'newsletter_subscribed_admin',
|
|
'label' => __('New Newsletter Subscriber', 'woonoow'),
|
|
'description' => __('Admin notification when someone subscribes to newsletter', 'woonoow'),
|
|
'category' => 'marketing',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
],
|
|
'newsletter_campaign' => [
|
|
'id' => 'newsletter_campaign',
|
|
'label' => __('Newsletter Campaign', 'woonoow'),
|
|
'description' => __('Master email design template for newsletter campaigns', 'woonoow'),
|
|
'category' => 'marketing',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
'variables' => [
|
|
'{content}' => __('Campaign content', 'woonoow'),
|
|
'{campaign_title}' => __('Campaign title', 'woonoow'),
|
|
'{subscriber_email}' => __('Subscriber email', 'woonoow'),
|
|
'{unsubscribe_url}' => __('Unsubscribe link', 'woonoow'),
|
|
],
|
|
],
|
|
|
|
// ===== ORDER INITIATION =====
|
|
'order_placed' => [
|
|
'id' => 'order_placed',
|
|
'label' => __('Order Placed', 'woonoow'),
|
|
'description' => __('When a new order is placed', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => 'new_order',
|
|
'enabled' => true,
|
|
],
|
|
'order_placed_customer' => [
|
|
'id' => 'order_placed',
|
|
'label' => __('Order Placed', 'woonoow'),
|
|
'description' => __('When customer places an order', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => 'customer_on_hold_order',
|
|
'enabled' => true,
|
|
],
|
|
'order_pending' => [
|
|
'id' => 'order_pending',
|
|
'label' => __('Order Pending', 'woonoow'),
|
|
'description' => __('When order is created (pending payment)', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
],
|
|
'order_pending_customer' => [
|
|
'id' => 'order_pending',
|
|
'label' => __('Order Pending', 'woonoow'),
|
|
'description' => __('When order is created (pending payment)', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
],
|
|
|
|
// ===== PAYMENT PROCESSING =====
|
|
'order_on_hold' => [
|
|
'id' => 'order_on_hold',
|
|
'label' => __('Order On-Hold', 'woonoow'),
|
|
'description' => __('When order is awaiting payment', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
],
|
|
'order_on_hold_customer' => [
|
|
'id' => 'order_on_hold',
|
|
'label' => __('Order On-Hold', 'woonoow'),
|
|
'description' => __('When order is awaiting payment', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => 'customer_on_hold_order',
|
|
'enabled' => true,
|
|
],
|
|
'payment_received' => [
|
|
'id' => 'payment_received',
|
|
'label' => __('Payment Received', 'woonoow'),
|
|
'description' => __('When payment is successfully received', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
],
|
|
'payment_received_customer' => [
|
|
'id' => 'payment_received',
|
|
'label' => __('Payment Received', 'woonoow'),
|
|
'description' => __('When payment is confirmed', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
],
|
|
'payment_failed' => [
|
|
'id' => 'payment_failed',
|
|
'label' => __('Payment Failed', 'woonoow'),
|
|
'description' => __('When payment processing fails', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
],
|
|
'payment_failed_customer' => [
|
|
'id' => 'payment_failed',
|
|
'label' => __('Payment Failed', 'woonoow'),
|
|
'description' => __('When payment fails - prompt retry', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => 'customer_failed_order',
|
|
'enabled' => true,
|
|
],
|
|
|
|
// ===== ORDER PROCESSING =====
|
|
'order_processing' => [
|
|
'id' => 'order_processing',
|
|
'label' => __('Order Processing', 'woonoow'),
|
|
'description' => __('When order is confirmed and being processed', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => 'customer_processing_order',
|
|
'enabled' => true,
|
|
],
|
|
'order_processing_customer' => [
|
|
'id' => 'order_processing',
|
|
'label' => __('Order Processing', 'woonoow'),
|
|
'description' => __('When order status changes to processing', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => 'customer_processing_order',
|
|
'enabled' => true,
|
|
],
|
|
|
|
// ===== FULFILLMENT =====
|
|
'order_shipped' => [
|
|
'id' => 'order_shipped',
|
|
'label' => __('Order Shipped', 'woonoow'),
|
|
'description' => __('When order is shipped', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
],
|
|
'order_shipped_customer' => [
|
|
'id' => 'order_shipped',
|
|
'label' => __('Order Shipped', 'woonoow'),
|
|
'description' => __('When order is shipped with tracking', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
],
|
|
'order_completed' => [
|
|
'id' => 'order_completed',
|
|
'label' => __('Order Completed', 'woonoow'),
|
|
'description' => __('When order is marked as completed', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => 'customer_completed_order',
|
|
'enabled' => true,
|
|
],
|
|
'order_completed_customer' => [
|
|
'id' => 'order_completed',
|
|
'label' => __('Order Completed', 'woonoow'),
|
|
'description' => __('When order is delivered/completed', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => 'customer_completed_order',
|
|
'enabled' => true,
|
|
],
|
|
|
|
// ===== ORDER ISSUES / EXCEPTIONS =====
|
|
'order_failed' => [
|
|
'id' => 'order_failed',
|
|
'label' => __('Order Failed', 'woonoow'),
|
|
'description' => __('When order fails', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => 'failed_order',
|
|
'enabled' => true,
|
|
],
|
|
'order_failed_customer' => [
|
|
'id' => 'order_failed',
|
|
'label' => __('Order Failed', 'woonoow'),
|
|
'description' => __('When order fails', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => 'customer_failed_order',
|
|
'enabled' => true,
|
|
],
|
|
'order_cancelled' => [
|
|
'id' => 'order_cancelled',
|
|
'label' => __('Order Cancelled', 'woonoow'),
|
|
'description' => __('When order is cancelled', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => 'cancelled_order',
|
|
'enabled' => true,
|
|
],
|
|
'order_cancelled_customer' => [
|
|
'id' => 'order_cancelled',
|
|
'label' => __('Order Cancelled', 'woonoow'),
|
|
'description' => __('When order is cancelled', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => 'customer_refunded_order',
|
|
'enabled' => true,
|
|
],
|
|
'order_refunded' => [
|
|
'id' => 'order_refunded',
|
|
'label' => __('Order Refunded', 'woonoow'),
|
|
'description' => __('When order is refunded', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'staff',
|
|
'wc_email' => '',
|
|
'enabled' => true,
|
|
],
|
|
'order_refunded_customer' => [
|
|
'id' => 'order_refunded',
|
|
'label' => __('Order Refunded', 'woonoow'),
|
|
'description' => __('When order is refunded', 'woonoow'),
|
|
'category' => 'orders',
|
|
'recipient_type' => 'customer',
|
|
'wc_email' => 'customer_refunded_order',
|
|
'enabled' => true,
|
|
],
|
|
];
|
|
|
|
/**
|
|
* Filter: woonoow_notification_events_registry
|
|
*
|
|
* Allows plugins/themes to add custom notification events
|
|
*
|
|
* @param array $events Event definitions
|
|
*/
|
|
return apply_filters('woonoow_notification_events_registry', $events);
|
|
}
|
|
|
|
/**
|
|
* Get events by recipient type
|
|
*
|
|
* @param string $recipient_type 'staff' or 'customer'
|
|
* @return array Filtered events
|
|
*/
|
|
public static function get_events_by_recipient($recipient_type) {
|
|
$all_events = self::get_all_events();
|
|
|
|
return array_filter($all_events, function($event) use ($recipient_type) {
|
|
return $event['recipient_type'] === $recipient_type;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get events by category
|
|
*
|
|
* @param string $category 'orders', 'products', 'customers'
|
|
* @return array Filtered events
|
|
*/
|
|
public static function get_events_by_category($category) {
|
|
$all_events = self::get_all_events();
|
|
|
|
return array_filter($all_events, function($event) use ($category) {
|
|
return $event['category'] === $category;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get single event definition
|
|
*
|
|
* @param string $event_id Event ID
|
|
* @param string $recipient_type Recipient type
|
|
* @return array|null Event definition or null if not found
|
|
*/
|
|
public static function get_event($event_id, $recipient_type) {
|
|
$all_events = self::get_all_events();
|
|
|
|
foreach ($all_events as $event) {
|
|
if ($event['id'] === $event_id && $event['recipient_type'] === $recipient_type) {
|
|
return $event;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Check if event exists
|
|
*
|
|
* @param string $event_id Event ID
|
|
* @param string $recipient_type Recipient type
|
|
* @return bool
|
|
*/
|
|
public static function event_exists($event_id, $recipient_type) {
|
|
return self::get_event($event_id, $recipient_type) !== null;
|
|
}
|
|
|
|
/**
|
|
* Get variables available for a specific event
|
|
*
|
|
* Returns both common variables and event-specific variables
|
|
*
|
|
* @param string $event_id Event ID
|
|
* @param string $recipient_type Recipient type
|
|
* @return array Array of variable definitions with key => description
|
|
*/
|
|
public static function get_variables_for_event($event_id, $recipient_type = 'customer') {
|
|
// Common variables available for ALL events
|
|
$common = [
|
|
'{site_name}' => __('Store/Site name', 'woonoow'),
|
|
'{site_title}' => __('Site title', 'woonoow'),
|
|
'{store_url}' => __('Store URL', 'woonoow'),
|
|
'{shop_url}' => __('Shop page URL', 'woonoow'),
|
|
'{my_account_url}' => __('My Account page URL', 'woonoow'),
|
|
'{login_url}' => __('Login page URL', 'woonoow'),
|
|
'{support_email}' => __('Support email address', 'woonoow'),
|
|
'{current_year}' => __('Current year', 'woonoow'),
|
|
'{current_date}' => __('Current date', 'woonoow'),
|
|
];
|
|
|
|
// Customer variables (for customer-facing events)
|
|
$customer_vars = [
|
|
'{customer_name}' => __('Customer full name', 'woonoow'),
|
|
'{customer_first_name}' => __('Customer first name', 'woonoow'),
|
|
'{customer_last_name}' => __('Customer last name', 'woonoow'),
|
|
'{customer_email}' => __('Customer email', 'woonoow'),
|
|
'{customer_phone}' => __('Customer phone', 'woonoow'),
|
|
];
|
|
|
|
// Order variables (for order-related events)
|
|
$order_vars = [
|
|
'{order_id}' => __('Order ID/number', 'woonoow'),
|
|
'{order_number}' => __('Order number', 'woonoow'),
|
|
'{order_date}' => __('Order date', 'woonoow'),
|
|
'{order_total}' => __('Order total', 'woonoow'),
|
|
'{order_subtotal}' => __('Order subtotal', 'woonoow'),
|
|
'{order_tax}' => __('Order tax', 'woonoow'),
|
|
'{order_shipping}' => __('Shipping cost', 'woonoow'),
|
|
'{order_discount}' => __('Discount amount', 'woonoow'),
|
|
'{order_status}' => __('Order status', 'woonoow'),
|
|
'{order_url}' => __('Order details URL', 'woonoow'),
|
|
'{order_items_table}' => __('Order items table (HTML)', 'woonoow'),
|
|
'{billing_address}' => __('Billing address', 'woonoow'),
|
|
'{shipping_address}' => __('Shipping address', 'woonoow'),
|
|
'{payment_method}' => __('Payment method', 'woonoow'),
|
|
'{payment_status}' => __('Payment status', 'woonoow'),
|
|
'{shipping_method}' => __('Shipping method', 'woonoow'),
|
|
];
|
|
|
|
// Shipping/tracking variables (for shipped/delivered events)
|
|
$shipping_vars = [
|
|
'{tracking_number}' => __('Tracking number', 'woonoow'),
|
|
'{tracking_url}' => __('Tracking URL', 'woonoow'),
|
|
'{shipping_carrier}' => __('Shipping carrier', 'woonoow'),
|
|
'{estimated_delivery}' => __('Estimated delivery date', 'woonoow'),
|
|
];
|
|
|
|
// Product variables (for stock alerts)
|
|
$product_vars = [
|
|
'{product_name}' => __('Product name', 'woonoow'),
|
|
'{product_sku}' => __('Product SKU', 'woonoow'),
|
|
'{product_url}' => __('Product URL', 'woonoow'),
|
|
'{product_price}' => __('Product price', 'woonoow'),
|
|
'{stock_quantity}' => __('Stock quantity', 'woonoow'),
|
|
];
|
|
|
|
// Newsletter variables
|
|
$newsletter_vars = [
|
|
'{subscriber_email}' => __('Subscriber email', 'woonoow'),
|
|
'{subscriber_name}' => __('Subscriber name', 'woonoow'),
|
|
'{unsubscribe_url}' => __('Unsubscribe link', 'woonoow'),
|
|
];
|
|
|
|
// Build variables based on event ID and category
|
|
$event = self::get_event($event_id, $recipient_type);
|
|
|
|
// If event not found, try to match by just event_id
|
|
if (!$event) {
|
|
$all_events = self::get_all_events();
|
|
foreach ($all_events as $e) {
|
|
if ($e['id'] === $event_id) {
|
|
$event = $e;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start with common vars
|
|
$variables = $common;
|
|
|
|
// Add category-specific vars
|
|
if ($event) {
|
|
$category = $event['category'] ?? '';
|
|
|
|
// Add customer vars for customer-facing events
|
|
if (($event['recipient_type'] ?? '') === 'customer') {
|
|
$variables = array_merge($variables, $customer_vars);
|
|
}
|
|
|
|
// Add based on category
|
|
switch ($category) {
|
|
case 'orders':
|
|
$variables = array_merge($variables, $order_vars);
|
|
// Add tracking for completed/shipped events
|
|
if (in_array($event_id, ['order_completed', 'order_shipped', 'order_delivered'])) {
|
|
$variables = array_merge($variables, $shipping_vars);
|
|
}
|
|
break;
|
|
|
|
case 'products':
|
|
$variables = array_merge($variables, $product_vars);
|
|
break;
|
|
|
|
case 'marketing':
|
|
$variables = array_merge($variables, $newsletter_vars);
|
|
// Add campaign-specific for newsletter_campaign
|
|
if ($event_id === 'newsletter_campaign') {
|
|
$variables['{content}'] = __('Campaign content', 'woonoow');
|
|
$variables['{campaign_title}'] = __('Campaign title', 'woonoow');
|
|
}
|
|
break;
|
|
|
|
case 'customers':
|
|
$variables = array_merge($variables, $customer_vars);
|
|
// Add account-specific vars
|
|
if ($event_id === 'new_customer') {
|
|
$variables['{user_temp_password}'] = __('Temporary password', 'woonoow');
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Add event-specific variables if defined
|
|
if (!empty($event['variables'])) {
|
|
$variables = array_merge($variables, $event['variables']);
|
|
}
|
|
}
|
|
|
|
// Sort alphabetically for easier browsing
|
|
ksort($variables);
|
|
|
|
return $variables;
|
|
}
|
|
}
|