Files
wordpress-theme-aksrt/src/Settings/Mail.php
T

143 lines
6.8 KiB
PHP

<?php
/**
* Canonical email settings and delivery service.
*
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
final class Mail {
/** @return array<string, mixed> */
public static function get_smtp(): array {
$s = Settings::get_all();
$username = trim((string) ($s['mail_smtp_name'] ?? '')) ?: sanitize_email((string) ($s['mail_name'] ?? ''));
$password = (string) ($s['mail_passwd'] ?? '');
return [
'enabled' => !empty($s['mail_smtps']),
'host' => trim((string) ($s['mail_host'] ?? '')),
'port' => max(1, min(65535, (int) ($s['mail_port'] ?? 465))),
'username' => $username,
'password' => $password,
'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' => $username !== '' && $password !== '',
];
}
/** Send a fixed catalog event. */
public static function send_event_email(string $event, string $to, array $placeholders = []): bool {
$definitions = MailTemplates::definitions();
$to = sanitize_email($to);
if ($to === '' || !is_email($to) || !isset($definitions[$event]) || !self::is_email_event_enabled($event)) {
return false;
}
$definition = $definitions[$event];
$subject = self::replace_placeholders((string) $definition['title'], $placeholders);
$content = self::replace_placeholders((string) $definition['content'], $placeholders);
$content = self::wrap_html($content, self::get_global_config());
$headers = ['Content-Type: text/html; charset=UTF-8'];
return (bool) wp_mail($to, $subject, $content, $headers);
}
public static function send_test_email(string $to): bool|\WP_Error {
$to = sanitize_email($to);
if ($to === '' || !is_email($to)) {
return new \WP_Error('aksrt_invalid_test_email', __('请输入有效的邮箱地址', 'aksrt'));
}
$failure = null;
$capture_failure = static function (\WP_Error $error) use (&$failure): void {
$failure = $error;
};
add_action('wp_mail_failed', $capture_failure);
$site = esc_html((string) get_bloginfo('name'));
$subject = '邮件配置测试';
$content = "您好,\n\n这是一封来自 {$site} 的邮件配置测试。";
$content = self::wrap_html($content, [
'description' => '此信为系统邮件,请不要直接回复。',
'more_content' => '',
]);
try {
$sent = (bool) wp_mail($to, $subject, $content, ['Content-Type: text/html; charset=UTF-8']);
} finally {
remove_action('wp_mail_failed', $capture_failure);
}
if ($sent) {
return true;
}
$error = $failure instanceof \WP_Error
? $failure
: new \WP_Error('aksrt_mail_failed', __('wp_mail 返回失败,但未提供详细错误。', 'aksrt'));
self::log_test_failure($error);
return $error;
}
private static function is_email_event_enabled(string $event): bool {
$settings = Settings::get_all();
return !array_key_exists("email_{$event}_enabled", $settings) || !empty($settings["email_{$event}_enabled"]);
}
/** @return array<string, string> */
private static function get_global_config(): array {
$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]);
}
}
return $global;
}
private static function replace_placeholders(string $value, array $placeholders): string {
$replace = [];
foreach ($placeholders as $key => $replacement) {
$replace[$key] = wp_strip_all_tags((string) $replacement);
}
return strtr($value, $replace);
}
private static function log_test_failure(\WP_Error $error): void {
$message = sanitize_text_field($error->get_error_message());
$message = preg_replace('/[A-Z0-9._%+\-]+@[A-Z0-9.\-]+\.[A-Z]{2,}/i', '[email]', $message);
$message = preg_replace('/\b(?:[a-z0-9-]+\.)+[a-z]{2,}\b/i', '[host]', (string) $message);
$message = preg_replace('/\b(?:\d{1,3}\.){3}\d{1,3}\b/', '[ip]', (string) $message);
$message = preg_replace('/\b(?:password|passwd|username|user|login|credential)\s*[:=]\s*[^\s,;]+/i', '$1=[redacted]', (string) $message);
error_log('[AKSRT] SMTP 测试失败:' . substr((string) $message, 0, 500));
}
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>';
}
}