fix: backend email rendering for new button/card syntax

ROOT CAUSE:
Frontend blocksToMarkdown outputs NEW syntax:
- [card:type]...[/card]
- [button:style](url)Text[/button]

But backend EmailRenderer.php only had regex for OLD syntax:
- [card type="..."]...[/card]
- [button url="..."]Text[/button]

FIXES:
1. parse_cards() now handles BOTH syntaxes:
   - NEW [card:type] regex first (extracts type from :type)
   - OLD [card type="..."] regex for backward compatibility

2. render_card() now handles BOTH button syntaxes:
   - NEW [button:style](url)Text[/button] regex
   - OLD [button url="..."] regex for backward compatibility

3. Card types properly styled with inline CSS:
   - hero: gradient background
   - success: green background + border
   - info: blue background + border
   - warning: yellow background + orange border

4. Buttons rendered with full inline styles + table wrapper
   for Gmail/email client compatibility
This commit is contained in:
Dwindi Ramadhana
2026-01-01 22:27:20 +07:00
parent 1af1add5d4
commit 47a1e78eb7

View File

@@ -288,21 +288,43 @@ class EmailRenderer {
* @return string
*/
private function parse_cards($content) {
// Match [card ...] ... [/card] patterns
preg_match_all('/\[card([^\]]*)\](.*?)\[\/card\]/s', $content, $matches, PREG_SET_ORDER);
$html = '';
$has_cards = false;
if (empty($matches)) {
// No cards found, wrap entire content in a single card
return $this->render_card($content, []);
// Match NEW syntax [card:type]...[/card] first
preg_match_all('/\[card:(\w+)\](.*?)\[\/card\]/s', $content, $new_matches, PREG_SET_ORDER);
if (!empty($new_matches)) {
$has_cards = true;
foreach ($new_matches as $match) {
$attributes = ['type' => $match[1]];
$card_content = $match[2];
$html .= $this->render_card($card_content, $attributes);
$html .= $this->render_card_spacing();
// Remove matched content to avoid double processing
$content = str_replace($match[0], '', $content);
}
}
$html = '';
foreach ($matches as $match) {
$attributes = $this->parse_card_attributes($match[1]);
$card_content = $match[2];
$html .= $this->render_card($card_content, $attributes);
$html .= $this->render_card_spacing();
// Match OLD syntax [card type="..."]...[/card]
preg_match_all('/\[card([^\]]*)\](.*?)\[\/card\]/s', $content, $old_matches, PREG_SET_ORDER);
if (!empty($old_matches)) {
$has_cards = true;
foreach ($old_matches as $match) {
$attributes = $this->parse_card_attributes($match[1]);
$card_content = $match[2];
$html .= $this->render_card($card_content, $attributes);
$html .= $this->render_card_spacing();
}
}
if (!$has_cards) {
// No cards found, wrap entire content in a single card
return $this->render_card($content, []);
}
// Remove last spacing
@@ -360,37 +382,53 @@ class EmailRenderer {
$hero_text_color = $email_settings['hero_text_color'] ?? '#ffffff';
// Parse button shortcodes with FULL INLINE STYLES for Gmail compatibility
// Format: [button url="..." style="solid|outline"]Text[/button]
// Helper function to generate button HTML
$generateButtonHtml = function($url, $style, $text) use ($primary_color, $secondary_color, $button_text_color) {
if ($style === 'outline') {
// Outline button - transparent background with border
$button_style = sprintf(
'display: inline-block; background-color: transparent; color: %s; padding: 14px 28px; border: 2px solid %s; border-radius: 6px; text-decoration: none; font-weight: 600; font-family: "Inter", Arial, sans-serif; font-size: 16px; text-align: center; mso-padding-alt: 0;',
esc_attr($secondary_color),
esc_attr($secondary_color)
);
} else {
// Solid button - full background color
$button_style = sprintf(
'display: inline-block; background-color: %s; color: %s; padding: 14px 28px; border: none; border-radius: 6px; text-decoration: none; font-weight: 600; font-family: "Inter", Arial, sans-serif; font-size: 16px; text-align: center; mso-padding-alt: 0;',
esc_attr($primary_color),
esc_attr($button_text_color)
);
}
// Use table-based button for better email client compatibility
return sprintf(
'<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="margin: 16px auto;"><tr><td align="center"><a href="%s" style="%s">%s</a></td></tr></table>',
esc_url($url),
$button_style,
esc_html($text)
);
};
// NEW FORMAT: [button:style](url)Text[/button]
$content = preg_replace_callback(
'/\[button\s+url=["\']([^"\']+)["\'](?:\s+style=["\']?(solid|outline)["\']?)?\]([^\[]+)\[\/button\]/',
function($matches) use ($primary_color, $secondary_color, $button_text_color) {
'/\[button:(\w+)\]\(([^)]+)\)([^\[]+)\[\/button\]/',
function($matches) use ($generateButtonHtml) {
$style = $matches[1]; // solid or outline
$url = $matches[2];
$text = trim($matches[3]);
return $generateButtonHtml($url, $style, $text);
},
$content
);
// OLD FORMAT: [button url="..." style="solid|outline"]Text[/button]
$content = preg_replace_callback(
'/\[button\s+url=["\']([^"\']+)["\'](?:\s+style=["\'](solid|outline)["\'])?\]([^\[]+)\[\/button\]/',
function($matches) use ($generateButtonHtml) {
$url = $matches[1];
$style = $matches[2] ?? 'solid';
$text = trim($matches[3]);
if ($style === 'outline') {
// Outline button - transparent background with border
$button_style = sprintf(
'display: inline-block; background-color: transparent; color: %s; padding: 14px 28px; border: 2px solid %s; border-radius: 6px; text-decoration: none; font-weight: 600; font-family: "Inter", Arial, sans-serif; font-size: 16px; text-align: center; mso-padding-alt: 0;',
esc_attr($secondary_color),
esc_attr($secondary_color)
);
} else {
// Solid button - full background color
$button_style = sprintf(
'display: inline-block; background-color: %s; color: %s; padding: 14px 28px; border: none; border-radius: 6px; text-decoration: none; font-weight: 600; font-family: "Inter", Arial, sans-serif; font-size: 16px; text-align: center; mso-padding-alt: 0;',
esc_attr($primary_color),
esc_attr($button_text_color)
);
}
// Use table-based button for better email client compatibility
return sprintf(
'<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="margin: 16px auto;"><tr><td align="center"><a href="%s" style="%s">%s</a></td></tr></table>',
esc_url($url),
$button_style,
esc_html($text)
);
return $generateButtonHtml($url, $style, $text);
},
$content
);