fix(email): complete mail settings and templates
This commit is contained in:
+116
-77
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Canonical email settings and delivery service.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
@@ -7,112 +9,149 @@ namespace AKSRT\Settings;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class Mail {
|
||||
final class Mail {
|
||||
/** @return list<string> */
|
||||
public static function events(): array {
|
||||
return array_keys(MailTemplates::definitions());
|
||||
}
|
||||
|
||||
/** 所有邮件事件类型常量 */
|
||||
public const EMAIL_EVENTS = [
|
||||
'reg_verify_link',
|
||||
'reg_verify_code',
|
||||
'reg_success',
|
||||
'password_reset',
|
||||
'change_code',
|
||||
'change_notice',
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取邮件事件配置
|
||||
*
|
||||
* @param string $event 事件类型,留空返回全部
|
||||
* @return array
|
||||
*/
|
||||
public static function get_email_event_config(string $event = ''): array {
|
||||
/** @return array<string, mixed> */
|
||||
public static function get_smtp(): array {
|
||||
$s = Settings::get_all();
|
||||
|
||||
$global = [
|
||||
'footer_text' => sanitize_text_field((string) ($s['email_footer_text'] ?? '')),
|
||||
'site_branding' => !isset($s['email_site_branding']) || !empty($s['email_site_branding']),
|
||||
'template_mode' => in_array($s['email_template_mode'] ?? 'plain', ['plain', 'html'], true)
|
||||
? ($s['email_template_mode'] ?? 'plain') : 'plain',
|
||||
return [
|
||||
'enabled' => !empty($s['mail_smtps']),
|
||||
'host' => trim((string) ($s['mail_host'] ?? '')),
|
||||
'port' => max(1, min(65535, (int) ($s['mail_port'] ?? 465))),
|
||||
'username' => trim((string) ($s['mail_smtp_name'] ?? '')) ?: sanitize_email((string) ($s['mail_name'] ?? '')),
|
||||
'password' => (string) ($s['mail_passwd'] ?? ''),
|
||||
'encryption' => in_array(($s['mail_smtpsecure'] ?? 'ssl'), ['ssl', 'tls', ''], true) ? (string) ($s['mail_smtpsecure'] ?? 'ssl') : 'ssl',
|
||||
'from_name' => trim((string) ($s['mail_showname'] ?? '')) ?: get_bloginfo('name'),
|
||||
'from_address' => sanitize_email((string) ($s['mail_name'] ?? '')),
|
||||
'auth' => !empty($s['mail_smtpauth']),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public static function get_email_event_config(string $event = ''): array {
|
||||
$definitions = MailTemplates::definitions();
|
||||
$defaults = MailTemplates::defaults();
|
||||
$settings = Settings::get_all();
|
||||
$global = [
|
||||
'description' => (string) ($defaults['mail_description'] ?? ''),
|
||||
'more_content' => (string) ($defaults['mail_more_content'] ?? ''),
|
||||
];
|
||||
foreach (['mail_description', 'mail_more_content'] as $key) {
|
||||
if (array_key_exists($key, $settings) && trim((string) $settings[$key]) !== '') {
|
||||
$global[str_replace('mail_', '', $key)] = $key === 'mail_more_content'
|
||||
? wp_kses_post((string) $settings[$key])
|
||||
: sanitize_textarea_field((string) $settings[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($event === '') {
|
||||
$events = [];
|
||||
foreach (self::EMAIL_EVENTS as $ev) {
|
||||
$events[$ev] = self::build_email_event_entry($s, $ev);
|
||||
foreach (array_keys($definitions) as $key) {
|
||||
$events[$key] = self::event_config($key, $settings, $defaults);
|
||||
}
|
||||
return ['global' => $global, 'events' => $events];
|
||||
}
|
||||
|
||||
if (!in_array($event, self::EMAIL_EVENTS, true)) {
|
||||
return ['enabled' => true, 'subject' => '', 'content' => '', 'html' => false, 'global' => $global];
|
||||
if (!isset($definitions[$event])) {
|
||||
return ['enabled' => false, 'subject' => '', 'content' => '', 'placeholders' => [], 'global' => $global];
|
||||
}
|
||||
|
||||
return array_merge(self::build_email_event_entry($s, $event), ['global' => $global]);
|
||||
return array_merge(self::event_config($event, $settings, $defaults), ['global' => $global]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮件事件是否启用
|
||||
*/
|
||||
public static function is_email_event_enabled(string $event): bool {
|
||||
if (!in_array($event, self::EMAIL_EVENTS, true)) {
|
||||
return true;
|
||||
$definitions = MailTemplates::definitions();
|
||||
if (!isset($definitions[$event])) {
|
||||
return false;
|
||||
}
|
||||
$s = Settings::get_all();
|
||||
return !isset($s["email_{$event}_enabled"]) || !empty($s["email_{$event}_enabled"]);
|
||||
$settings = Settings::get_all();
|
||||
return !array_key_exists("email_{$event}_enabled", $settings) || !empty($settings["email_{$event}_enabled"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过邮件事件体系发送邮件
|
||||
* Send a configured event. The optional defaults keep existing callers source-compatible.
|
||||
*/
|
||||
public static function send_event_email(
|
||||
string $event,
|
||||
string $to,
|
||||
string $default_subject,
|
||||
string $default_content,
|
||||
string $default_subject = '',
|
||||
string $default_content = '',
|
||||
array $placeholders = []
|
||||
): bool {
|
||||
if (!self::is_email_event_enabled($event)) {
|
||||
$to = sanitize_email($to);
|
||||
if ($to === '' || !is_email($to) || !isset(MailTemplates::definitions()[$event]) || !self::is_email_event_enabled($event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cfg = self::get_email_event_config($event);
|
||||
|
||||
$subject = ($cfg['subject'] !== '') ? $cfg['subject'] : $default_subject;
|
||||
$subject = strtr($subject, $placeholders);
|
||||
|
||||
$content = ($cfg['content'] !== '') ? $cfg['content'] : $default_content;
|
||||
$content = strtr($content, $placeholders);
|
||||
|
||||
$branding = Settings::get_mail_branding();
|
||||
$footer = $cfg['global']['footer_text'];
|
||||
if ($footer === '' && $branding['description'] !== '') {
|
||||
$footer = $branding['description'];
|
||||
}
|
||||
if ($footer === '') {
|
||||
$footer = get_bloginfo('name');
|
||||
}
|
||||
if ($footer !== '') {
|
||||
$content .= "\n\n— " . $footer;
|
||||
if ($branding['description'] !== '' && $footer !== $branding['description']) {
|
||||
$content .= "\n" . $branding['description'];
|
||||
}
|
||||
}
|
||||
|
||||
$is_html = $cfg['html'] || ($cfg['global']['template_mode'] === 'html');
|
||||
$headers = [$is_html ? 'Content-Type: text/html; charset=UTF-8' : 'Content-Type: text/plain; charset=UTF-8'];
|
||||
|
||||
return wp_mail($to, $subject, $content, $headers);
|
||||
$definition = MailTemplates::definitions()[$event];
|
||||
$subject = (string) ($cfg['subject'] ?? $default_subject ?: $definition['title']);
|
||||
$content = (string) ($cfg['content'] ?? $default_content ?: $definition['content']);
|
||||
$subject = self::replace_placeholders($subject, $placeholders, false);
|
||||
$content = self::replace_placeholders($content, $placeholders, false);
|
||||
$content = self::wrap_html($content, $cfg['global'] ?? []);
|
||||
$headers = ['Content-Type: text/html; charset=UTF-8'];
|
||||
return (bool) wp_mail($to, $subject, $content, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建单个邮件事件条目
|
||||
*/
|
||||
private static function build_email_event_entry(array $s, string $event): array {
|
||||
public static function send_test_email(string $to): bool {
|
||||
$to = sanitize_email($to);
|
||||
if ($to === '' || !is_email($to)) return false;
|
||||
$site = esc_html((string) get_bloginfo('name'));
|
||||
$subject = '邮件配置测试';
|
||||
$content = "您好,\n\n这是一封来自 {$site} 的邮件配置测试。";
|
||||
$content = self::wrap_html($content, [
|
||||
'description' => '此信为系统邮件,请不要直接回复。',
|
||||
'more_content' => '',
|
||||
]);
|
||||
return (bool) wp_mail($to, $subject, $content, ['Content-Type: text/html; charset=UTF-8']);
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
private static function event_config(string $event, array $settings, array $defaults): array {
|
||||
$definition = MailTemplates::definitions()[$event];
|
||||
$subject_key = "email_{$event}_subject";
|
||||
$content_key = "email_{$event}_content";
|
||||
return [
|
||||
'enabled' => !isset($s["email_{$event}_enabled"]) || !empty($s["email_{$event}_enabled"]),
|
||||
'subject' => sanitize_text_field((string) ($s["email_{$event}_subject"] ?? '')),
|
||||
'content' => sanitize_textarea_field((string) ($s["email_{$event}_content"] ?? '')),
|
||||
'html' => !empty($s["email_{$event}_html"]),
|
||||
'enabled' => !array_key_exists("email_{$event}_enabled", $settings) || !empty($settings["email_{$event}_enabled"]),
|
||||
'subject' => trim((string) ($settings[$subject_key] ?? '')) ?: (string) ($defaults[$subject_key] ?? $definition['title']),
|
||||
'content' => (string) ($settings[$content_key] ?? '') !== '' ? (string) $settings[$content_key] : (string) ($defaults[$content_key] ?? $definition['content']),
|
||||
'placeholders' => $definition['placeholders'],
|
||||
];
|
||||
}
|
||||
|
||||
private static function replace_placeholders(string $value, array $placeholders, bool $html): string {
|
||||
$replace = [];
|
||||
foreach ($placeholders as $key => $replacement) {
|
||||
$replacement = (string) $replacement;
|
||||
if ($html) {
|
||||
$replacement = str_contains($key, 'url') ? esc_url($replacement) : esc_html($replacement);
|
||||
} else {
|
||||
$replacement = wp_strip_all_tags($replacement);
|
||||
}
|
||||
$replace[$key] = $replacement;
|
||||
}
|
||||
return strtr($value, $replace);
|
||||
}
|
||||
|
||||
private static function wrap_html(string $content, array $global): string {
|
||||
$logo = method_exists(Settings::class, 'logo_url') ? Settings::logo_url() : '';
|
||||
$background = esc_url(AKSRT_URI . '/assets/img/mail-bg.png');
|
||||
$body = preg_match('/<[^>]+>/', $content) ? wp_kses_post($content) : nl2br(esc_html($content));
|
||||
$description = esc_html((string) ($global['description'] ?? ''));
|
||||
$more = wp_kses_post((string) ($global['more_content'] ?? ''));
|
||||
$site = esc_html((string) get_bloginfo('name'));
|
||||
$logo_html = $logo !== '' ? '<img src="' . esc_url($logo) . '" alt="' . $site . '" style="max-width:220px;height:auto;">' : '<strong style="font-size:24px;">' . $site . '</strong>';
|
||||
return '<div style="background:#ecf1f3;padding:2%;font-family:Arial,sans-serif;color:#55798d;line-height:1.7;">'
|
||||
. '<div style="max-width:701px;margin:0 auto;background:#fff;box-shadow:0 3px 12px rgba(0,0,0,.08);">'
|
||||
. '<div style="padding:42px 10%;text-align:center;">' . $logo_html . '</div>'
|
||||
. '<div style="padding:0 10% 42px;overflow-wrap:break-word;">' . $body . '</div>'
|
||||
. '<div style="border-top:2px dashed rgba(125,136,140,.15);padding:34px 10% 0;">'
|
||||
. '<strong style="font-size:20px;">' . $site . '</strong>'
|
||||
. '<div style="margin:16px 0;white-space:pre-wrap;">' . $description . '</div>'
|
||||
. '<div>' . $more . '</div></div>'
|
||||
. '<img src="' . $background . '" alt="" style="display:block;width:100%;height:auto;margin-top:26px;">'
|
||||
. '</div></div>';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user