fix: Add markdown parsing, variable replacement, and logo fallback
🐛 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: <h2>Great news, Dwindi Ramadhana!</h2> Before: {support_email} After: admin@example.com Before: Broken image After: Site icon or store name
This commit is contained in:
142
includes/Core/Notifications/MarkdownParser.php
Normal file
142
includes/Core/Notifications/MarkdownParser.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
/**
|
||||
* Markdown to Email HTML Parser
|
||||
*
|
||||
* PHP port of the TypeScript markdown parser from admin-spa
|
||||
*
|
||||
* Supports:
|
||||
* - Standard Markdown (headings, bold, italic, lists, links, horizontal rules)
|
||||
* - Card blocks with ::: syntax
|
||||
* - Button blocks with [button url="..."]Text[/button] syntax
|
||||
* - Variables with {variable_name}
|
||||
* - Checkmarks (✓) and bullet points (•)
|
||||
* - Proper newline handling
|
||||
*
|
||||
* @package WooNooW\Core\Notifications
|
||||
*/
|
||||
|
||||
namespace WooNooW\Core\Notifications;
|
||||
|
||||
class MarkdownParser {
|
||||
|
||||
/**
|
||||
* Parse markdown to email HTML
|
||||
*
|
||||
* @param string $markdown
|
||||
* @return string
|
||||
*/
|
||||
public static function parse($markdown) {
|
||||
$html = $markdown;
|
||||
|
||||
// Parse card blocks first (:::card or :::card[type])
|
||||
$html = preg_replace_callback(
|
||||
'/:::card(?:\[(\w+)\])?\n([\s\S]*?):::/s',
|
||||
function($matches) {
|
||||
$type = $matches[1] ?? '';
|
||||
$content = trim($matches[2]);
|
||||
$parsed_content = self::parse_basics($content);
|
||||
return '[card' . ($type ? ' type="' . $type . '"' : '') . "]\n" . $parsed_content . "\n[/card]";
|
||||
},
|
||||
$html
|
||||
);
|
||||
|
||||
// Parse button blocks [button url="..."]Text[/button] - already in correct format
|
||||
// Also support legacy [button](url){text} syntax
|
||||
$html = preg_replace_callback(
|
||||
'/\[button(?:\s+style="(solid|outline)")?\]\((.*?)\)\s*\{([^}]+)\}/',
|
||||
function($matches) {
|
||||
$style = $matches[1] ?? '';
|
||||
$url = $matches[2];
|
||||
$text = $matches[3];
|
||||
return '[button url="' . $url . '"' . ($style ? ' style="' . $style . '"' : '') . ']' . $text . '[/button]';
|
||||
},
|
||||
$html
|
||||
);
|
||||
|
||||
// Horizontal rules
|
||||
$html = preg_replace('/^---$/m', '<hr>', $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', '<h4>$1</h4>', $html);
|
||||
$html = preg_replace('/^### (.*)$/m', '<h3>$1</h3>', $html);
|
||||
$html = preg_replace('/^## (.*)$/m', '<h2>$1</h2>', $html);
|
||||
$html = preg_replace('/^# (.*)$/m', '<h1>$1</h1>', $html);
|
||||
|
||||
// Bold
|
||||
$html = preg_replace('/\*\*(.*?)\*\*/s', '<strong>$1</strong>', $html);
|
||||
$html = preg_replace('/__(.*?)__/s', '<strong>$1</strong>', $html);
|
||||
|
||||
// Italic
|
||||
$html = preg_replace('/\*([^\*]+?)\*/', '<em>$1</em>', $html);
|
||||
$html = preg_replace('/_([^_]+?)_/', '<em>$1</em>', $html);
|
||||
|
||||
// Links (but not button syntax)
|
||||
$html = preg_replace('/\[(?!button)([^\]]+)\]\(([^)]+)\)/', '<a href="$2">$1</a>', $html);
|
||||
|
||||
// Unordered lists (including checkmarks and bullets)
|
||||
$html = preg_replace('/^[\*\-•✓✔] (.*)$/m', '<li>$1</li>', $html);
|
||||
|
||||
// Wrap consecutive <li> in <ul>
|
||||
$html = preg_replace('/(<li>.*?<\/li>\s*)+/s', '<ul>$0</ul>', $html);
|
||||
|
||||
// Ordered lists
|
||||
$html = preg_replace('/^\d+\. (.*)$/m', '<li>$1</li>', $html);
|
||||
|
||||
// Paragraphs (lines not already in tags)
|
||||
$lines = explode("\n", $html);
|
||||
$processed_lines = [];
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$trimmed = trim($line);
|
||||
|
||||
// Skip empty lines
|
||||
if (empty($trimmed)) {
|
||||
$processed_lines[] = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip lines that are already HTML tags or shortcodes
|
||||
if (preg_match('/^</', $trimmed) || preg_match('/^\[/', $trimmed)) {
|
||||
$processed_lines[] = $line;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Wrap in paragraph
|
||||
$processed_lines[] = '<p>' . $line . '</p>';
|
||||
}
|
||||
|
||||
$html = implode("\n", $processed_lines);
|
||||
|
||||
// Clean up extra newlines in HTML
|
||||
$html = preg_replace('/\n{3,}/', "\n\n", $html);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert newlines to <br> tags for email rendering
|
||||
*
|
||||
* @param string $html
|
||||
* @return string
|
||||
*/
|
||||
public static function nl2br_email($html) {
|
||||
// Don't convert newlines inside HTML tags
|
||||
$html = preg_replace('/(?<!>)\n(?!<)/', '<br>', $html);
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user