['label' => 'Avakode', 'accent' => '#7C5CFC', 'subtle' => '#F5F3FF', 'on_accent' => '#FFFFFF'],
'bundle' => ['label' => 'Forge Suite', 'accent' => '#7C5CFC', 'subtle' => '#F5F3FF', 'on_accent' => '#FFFFFF'],
'support' => ['label' => 'Forge Support', 'accent' => '#7C5CFC', 'subtle' => '#F5F3FF', 'on_accent' => '#FFFFFF'],
'alerts' => ['label' => 'Forge Alerts', 'accent' => '#EF4444', 'subtle' => '#FEF2F2', 'on_accent' => '#FFFFFF'],
'langforge' => ['label' => 'Lang Forge', 'accent' => '#0EC49E', 'subtle' => '#F0FDF9', 'on_accent' => '#0C1A3A'],
'rankforge' => ['label' => 'Rank Forge', 'accent' => '#FF6B35', 'subtle' => '#FFF7ED', 'on_accent' => '#FFFFFF'],
'formforge' => ['label' => 'Form Forge', 'accent' => '#3B82F6', 'subtle' => '#EFF6FF', 'on_accent' => '#FFFFFF'],
'fieldforge' => ['label' => 'Field Forge', 'accent' => '#F43F5E', 'subtle' => '#FFF1F2', 'on_accent' => '#FFFFFF'],
// The four unpaused products, 2026-09-03. Without an entry here a
// product's mail silently falls back to the Avakode palette, which
// is how it reads as someone else's e-mail.
'guardforge' => ['label' => 'Guard Forge', 'accent' => '#2ABE55', 'subtle' => '#EBFCED', 'on_accent' => '#1A1A2E'],
'velocityforge' => ['label' => 'Velocity Forge', 'accent' => '#A2C700', 'subtle' => '#F3FAE5', 'on_accent' => '#1A1A2E'],
'draftforge' => ['label' => 'Draft Forge', 'accent' => '#0D9296', 'subtle' => '#EDFAFA', 'on_accent' => '#FFFFFF'],
'flowforge' => ['label' => 'Cascade Forge', 'accent' => '#DA76BB', 'subtle' => '#FFF3FA', 'on_accent' => '#1A1A2E'],
];
}
public static function render(array $args) {
$args = wp_parse_args($args, [
'product' => 'avakode',
'title' => '',
'preheader' => '',
'eyebrow' => '',
'intro' => '',
'blocks' => [],
'cta' => null,
'footer_note' => '',
]);
$palettes = self::palettes();
$palette = $palettes[$args['product']] ?? $palettes['avakode'];
$preheader = $args['preheader'] ?: ($args['intro'] ?: $args['title']);
$blocks = is_array($args['blocks']) ? $args['blocks'] : [];
$blocks_html = self::blocks_html($blocks);
$cta_html = '';
if (is_array($args['cta']) && !empty($args['cta']['label']) && !empty($args['cta']['url'])) {
$cta_html = '
';
}
$footer_note = $args['footer_note']
? '' . self::esc($args['footer_note']) . '
'
: '';
$intro_html = $args['intro']
? '' . self::esc($args['intro']) . '
'
: '';
$html = '
'
// wporg-advisory-ok: the head of an HTML email, not of a page: mail clients read the style from the message itself, there is no page to enqueue it into
. '
' . self::esc($preheader) . '
|
' . self::esc($args['eyebrow'] ?: $palette['label']) . '
|
|
' . self::esc($palette['label']) . '
' . self::esc($args['title']) . '
' . $intro_html . $blocks_html . $cta_html . '
|
|
|
' . esc_html__('Avakode ยท AI-powered WordPress ecosystem', 'guardforge') . '
' . $footer_note . '
|
|
';
return [
'html' => $html,
'text' => self::text_from_parts($args, $palette),
];
}
public static function send_wp($to, $subject, array $args, array $headers = []) {
$rendered = self::render($args);
$headers = self::html_headers($headers);
if (!empty($args['from'])) {
$headers[] = 'From: ' . preg_replace('/[\r\n]+/', ' ', (string) $args['from']);
}
$subject = preg_replace('/[\r\n]+/', ' ', wp_specialchars_decode((string) $subject, ENT_QUOTES));
self::prime_alt_body($rendered['text']);
add_filter('wp_mail_content_type', [__CLASS__, 'html_content_type']);
try {
return (bool) wp_mail($to, $subject, $rendered['html'], $headers);
} finally {
remove_filter('wp_mail_content_type', [__CLASS__, 'html_content_type']);
remove_action('phpmailer_init', [__CLASS__, 'set_alt_body']);
self::$alt_body = '';
}
}
public static function html_headers(array $headers = []) {
foreach ($headers as $header) {
if (stripos((string) $header, 'content-type:') === 0) {
return $headers;
}
}
$headers[] = 'Content-Type: text/html; charset=UTF-8';
return $headers;
}
public static function prime_alt_body($text) {
self::$alt_body = (string) $text;
add_action('phpmailer_init', [__CLASS__, 'set_alt_body']);
}
public static function html_content_type() {
return 'text/html';
}
public static function set_alt_body($phpmailer) {
if (self::$alt_body !== '' && is_object($phpmailer) && property_exists($phpmailer, 'AltBody')) {
$phpmailer->AltBody = self::$alt_body;
}
remove_action('phpmailer_init', [__CLASS__, 'set_alt_body']);
self::$alt_body = '';
}
private static function text_from_parts(array $args, array $palette) {
$parts = array_filter([
$args['eyebrow'] ?: $palette['label'],
$args['title'],
$args['intro'],
self::text_from_blocks(is_array($args['blocks']) ? $args['blocks'] : []),
is_array($args['cta'] ?? null) && !empty($args['cta']['label']) && !empty($args['cta']['url'])
? $args['cta']['label'] . ': ' . $args['cta']['url']
: '',
$args['footer_note'],
], function($part) {
return (string) $part !== '';
});
return implode("\n\n", array_map('wp_strip_all_tags', $parts));
}
private static function text_from_blocks(array $blocks) {
$parts = [];
foreach ($blocks as $block) {
if (is_array($block)) {
if (!empty($parts)) {
$last = end($parts);
if (strpos((string) $last, ':') === false) {
$parts[] = '';
}
}
$parts[] = ($block['label'] ?? '') . ': ' . self::format_value($block['value'] ?? '');
} else {
$parts[] = (string) $block;
}
}
return implode("\n", $parts);
}
private static function format_value($value) {
if (is_array($value)) {
return implode(', ', array_map('strval', $value));
}
return (string) $value;
}
private static function blocks_html(array $blocks) {
$html = '';
$rows = [];
foreach ($blocks as $block) {
if (is_array($block)) {
$rows[] = $block;
continue;
}
$html .= self::rows_table_html($rows);
$rows = [];
$html .= '' . nl2br(self::esc($block), false) . '
';
}
$html .= self::rows_table_html($rows);
return $html;
}
private static function rows_table_html(array $rows) {
if (empty($rows)) {
return '';
}
$row_html = '';
foreach ($rows as $row) {
$label = self::esc($row['label'] ?? '');
$value = self::esc(self::format_value($row['value'] ?? ''));
$row_html .= ''
. '| ' . $label . ' | '
. '' . $value . ' | '
. '
';
}
return '';
}
private static function esc($value) {
return esc_html(self::format_value($value));
}
}
}