', $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
$html = preg_replace('/(- .*?<\/li>\s*)+/s', '', $html);
// Ordered lists
$html = preg_replace('/^\d+\. (.*)$/m', '
- $1
', $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[] = '' . $line . '
';
}
$html = implode("\n", $processed_lines);
// Clean up extra newlines in HTML
$html = preg_replace('/\n{3,}/', "\n\n", $html);
return $html;
}
/**
* Convert newlines to
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(?!<)/', '
', $html);
return $html;
}
}