From 129eb2507876a559ecec4560f54419e1bc0d07f5 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 13 Sep 2026 20:02:56 +0800 Subject: [PATCH] fix(email): lock templates to built-in catalog --- inc/modules/user-parts/account-ajax.php | 6 +- src/Admin/SchemaProvider.php | 12 +-- .../UserControllerAuthPhoneOperations.php | 4 +- src/Auth/ServicePart1a.php | 2 +- src/Settings/DisplayPageGroups.php | 1 - src/Settings/DomainGroups.php | 27 +----- src/Settings/Mail.php | 93 ++++++------------- src/Settings/MailNotifications.php | 2 +- src/Settings/MailTemplates.php | 4 - .../email-system-contract.test.cjs | 21 ++++- 10 files changed, 61 insertions(+), 111 deletions(-) diff --git a/inc/modules/user-parts/account-ajax.php b/inc/modules/user-parts/account-ajax.php index ba3ef586..7d576222 100644 --- a/inc/modules/user-parts/account-ajax.php +++ b/inc/modules/user-parts/account-ajax.php @@ -152,7 +152,8 @@ add_action('wp_ajax_aksrt_send_email_code', function () { $site_name = get_bloginfo( 'name' ); $sent = \AKSRT\Settings\Settings::send_event_email( - 'change_code', $target_email, '', '', + 'change_code', + $target_email, [ '{username}' => $current_user->display_name, '{site_name}' => $site_name, '{code}' => $code ] ); @@ -294,7 +295,8 @@ add_action('wp_ajax_aksrt_change_email', function () { if ( is_email( $old_email ) ) { $site_name = get_bloginfo( 'name' ); \AKSRT\Settings\Settings::send_event_email( - 'change_notice', $old_email, '', '', + 'change_notice', + $old_email, [ '{username}' => $current_user->display_name, '{site_name}' => $site_name, '{new_email}' => $new_email ] ); } diff --git a/src/Admin/SchemaProvider.php b/src/Admin/SchemaProvider.php index c75d29c3..2d6f94df 100644 --- a/src/Admin/SchemaProvider.php +++ b/src/Admin/SchemaProvider.php @@ -540,16 +540,12 @@ class SchemaProvider { $data = $this->collect_defaults($schema); foreach ($this->get_all_fields($schema) as $field) { - if (array_key_exists($field['key'], $stored)) { - $stored_value = $stored[$field['key']]; - $is_mail_template = $option_key === 'aksrt_settings' - && preg_match('/^email_.+_(subject|content)$/', (string) $field['key']); - if (!$is_mail_template || trim((string) $stored_value) !== '') { - $data[$field['key']] = $stored_value; - } - } + if (array_key_exists($field['key'], $stored)) { + $data[$field['key']] = $stored[$field['key']]; + } $stored_value = array_key_exists($field['key'], $stored) ? $stored[$field['key']] : ($data[$field['key']] ?? ''); + if (!empty($field['secret'])) { $data[$field['key']] = trim((string) $stored_value) !== '' ? '***' : ''; } diff --git a/src/Admin/UserControllerAuthPhoneOperations.php b/src/Admin/UserControllerAuthPhoneOperations.php index a0fc8fab..4ab7aa84 100644 --- a/src/Admin/UserControllerAuthPhoneOperations.php +++ b/src/Admin/UserControllerAuthPhoneOperations.php @@ -205,9 +205,9 @@ trait UserControllerAuthPhoneOperations { '{code}' => $code, ]; $sent = $scene_event !== '' && class_exists('\\AKSRT\\Settings\\Settings') - ? \AKSRT\Settings\Settings::send_event_email($scene_event, $target, '', '', $placeholders) + ? \AKSRT\Settings\Settings::send_event_email($scene_event, $target, $placeholders) + : false; - : false; if (!$sent) { delete_transient($this->auth_code_key($scene, $target)); if ($scene !== 'register') { diff --git a/src/Auth/ServicePart1a.php b/src/Auth/ServicePart1a.php index 5a48215d..aa8e2215 100644 --- a/src/Auth/ServicePart1a.php +++ b/src/Auth/ServicePart1a.php @@ -136,7 +136,7 @@ trait ServicePart1a { return ['success' => false, 'error' => __('注册成功,但读取用户信息失败,请联系管理员', 'aksrt')]; } if ($user instanceof \WP_User && is_email($user->user_email)) { - \AKSRT\Settings\Mail::send_event_email('reg_success', $user->user_email, '', '', [ + \AKSRT\Settings\Mail::send_event_email('reg_success', $user->user_email, [ '{username}' => $user->display_name, '{site_name}' => get_bloginfo('name'), '{url}' => function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : home_url('/'), diff --git a/src/Settings/DisplayPageGroups.php b/src/Settings/DisplayPageGroups.php index 354a3735..525423a0 100644 --- a/src/Settings/DisplayPageGroups.php +++ b/src/Settings/DisplayPageGroups.php @@ -193,7 +193,6 @@ trait DisplayPageGroups { 'trackcode' => self::trackcode(), 'seo' => self::get_seo_settings(), 'mail' => self::get_smtp(), - 'email_events' => self::get_email_event_config(), 'footer' => self::get_footer_settings(), 'tabbar' => self::get_footer_tabbar_settings(), 'floating_buttons' => self::get_floating_buttons_settings(), diff --git a/src/Settings/DomainGroups.php b/src/Settings/DomainGroups.php index 2b75ee78..e1861c86 100644 --- a/src/Settings/DomainGroups.php +++ b/src/Settings/DomainGroups.php @@ -40,34 +40,11 @@ trait DomainGroups { return 'template_1'; } - /** - * 获取邮件事件配置 - * - * @param string $event 事件类型,留空返回全部 - * @return array 单个事件配置或全部事件数组 - */ - public static function get_email_event_config(string $event = ''): array { - return Mail::get_email_event_config($event); - } - - /** - * 邮件事件是否启用 - */ - public static function is_email_event_enabled(string $event): bool { - return Mail::is_email_event_enabled($event); - } - /** * 通过邮件事件体系发送邮件 */ - public static function send_event_email( - string $event, - string $to, - string $default_subject, - string $default_content, - array $placeholders = [] - ): bool { - return Mail::send_event_email($event, $to, $default_subject, $default_content, $placeholders); + public static function send_event_email(string $event, string $to, array $placeholders = []): bool { + return Mail::send_event_email($event, $to, $placeholders); } /** 禁封设置 */ diff --git a/src/Settings/Mail.php b/src/Settings/Mail.php index 27bfb740..792a4684 100644 --- a/src/Settings/Mail.php +++ b/src/Settings/Mail.php @@ -28,66 +28,17 @@ final class Mail { ]; } - /** @return array */ - public static function get_email_event_config(string $event = ''): array { + /** Send a fixed catalog event. */ + public static function send_event_email(string $event, string $to, array $placeholders = []): bool { $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 (array_keys($definitions) as $key) { - $events[$key] = self::event_config($key, $settings, $defaults); - } - return ['global' => $global, 'events' => $events]; - } - if (!isset($definitions[$event])) { - return ['enabled' => false, 'subject' => '', 'content' => '', 'placeholders' => [], 'global' => $global]; - } - return array_merge(self::event_config($event, $settings, $defaults), ['global' => $global]); - } - - public static function is_email_event_enabled(string $event): bool { - $definitions = MailTemplates::definitions(); - if (!isset($definitions[$event])) { - return false; - } - $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 = '', - array $placeholders = [] - ): bool { $to = sanitize_email($to); - if ($to === '' || !is_email($to) || !isset(MailTemplates::definitions()[$event]) || !self::is_email_event_enabled($event)) { + if ($to === '' || !is_email($to) || !isset($definitions[$event]) || !self::is_email_event_enabled($event)) { return false; } - $cfg = self::get_email_event_config($event); - $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); - $content = self::replace_placeholders($content, $placeholders); - $content = self::wrap_html($content, $cfg['global'] ?? []); + $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); } @@ -129,17 +80,27 @@ final class Mail { return $error; } - /** @return array */ - 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' => !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 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 */ + 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 { diff --git a/src/Settings/MailNotifications.php b/src/Settings/MailNotifications.php index 9c47d221..391c073d 100644 --- a/src/Settings/MailNotifications.php +++ b/src/Settings/MailNotifications.php @@ -192,7 +192,7 @@ final class MailNotifications { private static function send(string $event, string $to, array $vars): void { $placeholders = []; foreach ($vars as $key => $value) $placeholders['{' . $key . '}'] = (string) $value; - Mail::send_event_email($event, $to, '', '', $placeholders); + Mail::send_event_email($event, $to, $placeholders); } /** @param array $vars */ diff --git a/src/Settings/MailTemplates.php b/src/Settings/MailTemplates.php index 90279b2a..8de7f699 100644 --- a/src/Settings/MailTemplates.php +++ b/src/Settings/MailTemplates.php @@ -70,8 +70,6 @@ final class MailTemplates { ]; foreach ($definitions as $event => $definition) { $defaults["email_{$event}_enabled"] = (bool) $definition['enabled']; - $defaults["email_{$event}_subject"] = $definition['title']; - $defaults["email_{$event}_content"] = $definition['content']; } return $defaults; } @@ -114,8 +112,6 @@ final class MailTemplates { foreach ($events as $event) { $definition = $definitions[$event]; $fields[] = ['key' => "email_{$event}_enabled", 'label' => "{$definition['title']}:启用", 'type' => 'switch', 'default' => $defaults["email_{$event}_enabled"]]; - $fields[] = ['key' => "email_{$event}_subject", 'label' => "{$definition['title']}:主题模板", 'type' => 'input', 'default' => $defaults["email_{$event}_subject"]]; - $fields[] = ['key' => "email_{$event}_content", 'label' => "{$definition['title']}:正文模板", 'type' => 'textarea', 'rows' => 6, 'sanitize_type' => 'html', 'default' => $defaults["email_{$event}_content"], 'help' => '可用占位符:' . implode('、', array_map(static fn(string $p): string => '{' . $p . '}', $definition['placeholders']))]; } return ['key' => $key, 'title' => $title, 'fields' => $fields]; } diff --git a/tests/widget-system/email-system-contract.test.cjs b/tests/widget-system/email-system-contract.test.cjs index 9ee2de97..fa4e1c93 100644 --- a/tests/widget-system/email-system-contract.test.cjs +++ b/tests/widget-system/email-system-contract.test.cjs @@ -8,6 +8,7 @@ const read = (file) => fs.readFileSync(path.join(root, file), 'utf8') const templates = read('src/Settings/MailTemplates.php') const mail = read('src/Settings/Mail.php') +const schemaProvider = read('src/Admin/SchemaProvider.php') const notifications = read('src/Settings/MailNotifications.php') const coreHooks = read('inc/core-hooks.php') const globalController = read('src/Admin/GlobalController.php') @@ -34,9 +35,10 @@ test('forum-only events stay out of the email catalog', () => { } }) -test('email templates and SMTP use one aksrt_settings schema', () => { +test('email settings keep one canonical SMTP schema', () => { assert.match(templates, /'option_key'\s*=>\s*'aksrt_settings'/) assert.match(templates, /'mail_passwd'.*'secret'\s*=>\s*true/) + assert.match(templates, /'email_test_send'.*'ui_only'\s*=>\s*true/) assert.match(mail, /Settings::get_all\(\)/) assert.match(mail, /'auth'\s*=>\s*\$username\s*!==\s*''\s*&&\s*\$password\s*!==\s*''/) assert.doesNotMatch(templates, /mail_smtpauth/) @@ -46,6 +48,23 @@ test('email templates and SMTP use one aksrt_settings schema', () => { assert.doesNotMatch(userController, /aksrt_mail_templates|mail-templates/) }) +test('email event schema exposes only notification switches', () => { + assert.match(templates, /private static function event_group\([^)]*\): array/) + assert.match(templates, /\$fields\[\] = \['key' => "email_\{\$event\}_enabled"/) + assert.doesNotMatch(templates, /email_\{\$event\}_subject/) + assert.doesNotMatch(templates, /email_\{\$event\}_content/) + assert.doesNotMatch(templates, /email_[A-Za-z0-9_]+_(subject|content)/) + assert.doesNotMatch(templates, /主题模板|正文模板|可用占位符/) + assert.doesNotMatch(schemaProvider, /is_mail_template|email_.+_\(subject\|content\)/) +}) + +test('mail runtime always uses the built-in event catalog', () => { + assert.match(mail, /replace_placeholders\(\(string\) \$definition\['title'\], \$placeholders\)/) + assert.match(mail, /replace_placeholders\(\(string\) \$definition\['content'\], \$placeholders\)/) + assert.doesNotMatch(mail, /\$subject_key|\$content_key|default_subject|default_content/) + assert.doesNotMatch(mail, /get_email_event_config\(\)/) +}) + test('SMTP authentication and failures use the canonical mail service', () => { assert.match(coreHooks, /add_filter\('wp_mail_from',/) assert.match(coreHooks, /add_filter\('wp_mail_from_name',/) -- 2.54.0