From c599bce71a241a24e119d35b80fea351c7d781b1 Mon Sep 17 00:00:00 2001 From: dwindown Date: Tue, 18 Nov 2025 18:36:28 +0700 Subject: [PATCH] fix: Add markdown parsing, variable replacement, and logo fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 Email Rendering Issues Fixed: 1. Markdown Not Parsed ❌ Raw markdown showing: ## Great news... ✅ Created MarkdownParser.php (PHP port from TypeScript) ✅ Parses headings, bold, italic, lists, links ✅ Supports card blocks and button syntax ✅ Proper newline handling 2. Variables Not Replaced ❌ {support_email} showing literally ✅ Added support_email variable ✅ Added current_year variable ✅ Added estimated_delivery variable (3-5 business days) 3. Broken Logo Image ❌ Broken image placeholder ✅ Fallback to site icon if no logo set ✅ Fallback to text header if no icon ✅ Proper URL handling 4. Newline Issues ❌ Variables on same line ✅ Markdown parser handles newlines correctly ✅ Proper paragraph wrapping 📦 New File: - includes/Core/Notifications/MarkdownParser.php - parse() - Convert markdown to HTML - parse_basics() - Parse standard markdown - nl2br_email() - Convert newlines for email 🔧 Updated Files: - EmailRenderer.php - Use MarkdownParser in render_card() - Add support_email, current_year variables - Add estimated_delivery calculation - Logo fallback to site icon - Text header fallback if no logo 🎯 Result: - ✅ Markdown properly rendered - ✅ All variables replaced - ✅ Logo displays (or text fallback) - ✅ Proper line breaks - ✅ Professional email appearance 📝 Example: Before: ## Great news, {customer_name}! After:

Great news, Dwindi Ramadhana!

Before: {support_email} After: admin@example.com Before: Broken image After: Site icon or store name --- includes/Core/Notifications/EmailRenderer.php | 21 ++- .../Core/Notifications/MarkdownParser.php | 142 ++++++++++++++++++ 2 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 includes/Core/Notifications/MarkdownParser.php diff --git a/includes/Core/Notifications/EmailRenderer.php b/includes/Core/Notifications/EmailRenderer.php index e8383d3..95a5452 100644 --- a/includes/Core/Notifications/EmailRenderer.php +++ b/includes/Core/Notifications/EmailRenderer.php @@ -143,10 +143,15 @@ class EmailRenderer { 'store_name' => get_bloginfo('name'), 'store_url' => home_url(), 'site_title' => get_bloginfo('name'), + 'support_email' => get_option('admin_email'), + 'current_year' => date('Y'), ]; // Order variables if ($data instanceof \WC_Order) { + // Calculate estimated delivery (3-5 business days from now) + $estimated_delivery = date('F j', strtotime('+3 days')) . '-' . date('j', strtotime('+5 days')); + $variables = array_merge($variables, [ 'order_number' => $data->get_order_number(), 'order_id' => $data->get_id(), @@ -160,6 +165,7 @@ class EmailRenderer { 'order_url' => $data->get_view_order_url(), 'payment_method' => $data->get_payment_method_title(), 'shipping_method' => $data->get_shipping_method(), + 'estimated_delivery' => $estimated_delivery, 'customer_name' => $data->get_formatted_billing_full_name(), 'customer_first_name' => $data->get_billing_first_name(), 'customer_last_name' => $data->get_billing_last_name(), @@ -280,6 +286,9 @@ class EmailRenderer { $type = $attributes['type'] ?? 'default'; $bg = $attributes['bg'] ?? null; + // Parse markdown in content + $content = MarkdownParser::parse($content); + // Get email customization settings for colors $email_settings = get_option('woonoow_email_settings', []); $hero_gradient_start = $email_settings['hero_gradient_start'] ?? '#667eea'; @@ -397,14 +406,22 @@ class EmailRenderer { $body_bg = '#f8f8f8'; // Email header (logo or text) - if (!empty($email_settings['logo_url'])) { + $logo_url = $email_settings['logo_url'] ?? ''; + + // Fallback to site icon if no logo set + if (empty($logo_url) && has_site_icon()) { + $logo_url = get_site_icon_url(200); + } + + if (!empty($logo_url)) { $header = sprintf( '%s', esc_url($variables['store_url']), - esc_url($email_settings['logo_url']), + esc_url($logo_url), esc_attr($variables['store_name']) ); } else { + // No logo, use text header $header_text = !empty($email_settings['header_text']) ? $email_settings['header_text'] : $variables['store_name']; $header = sprintf( '%s', diff --git a/includes/Core/Notifications/MarkdownParser.php b/includes/Core/Notifications/MarkdownParser.php new file mode 100644 index 0000000..a53e62b --- /dev/null +++ b/includes/Core/Notifications/MarkdownParser.php @@ -0,0 +1,142 @@ +', $html); + + // Parse remaining markdown (outside cards) + $html = self::parse_basics($html); + + return $html; + } + + /** + * Parse basic markdown syntax + * + * @param string $text + * @return string + */ + private static function parse_basics($text) { + $html = $text; + + // Headings (must be done in order from h4 to h1 to avoid conflicts) + $html = preg_replace('/^#### (.*)$/m', '

$1

', $html); + $html = preg_replace('/^### (.*)$/m', '

$1

', $html); + $html = preg_replace('/^## (.*)$/m', '

$1

', $html); + $html = preg_replace('/^# (.*)$/m', '

$1

', $html); + + // Bold + $html = preg_replace('/\*\*(.*?)\*\*/s', '$1', $html); + $html = preg_replace('/__(.*?)__/s', '$1', $html); + + // Italic + $html = preg_replace('/\*([^\*]+?)\*/', '$1', $html); + $html = preg_replace('/_([^_]+?)_/', '$1', $html); + + // Links (but not button syntax) + $html = preg_replace('/\[(?!button)([^\]]+)\]\(([^)]+)\)/', '$1', $html); + + // Unordered lists (including checkmarks and bullets) + $html = preg_replace('/^[\*\-•✓✔] (.*)$/m', '
  • $1
  • ', $html); + + // Wrap consecutive
  • in