!array_key_exists('avatar_upload_s', $settings) || !empty($settings['avatar_upload_s']), 'avatar_external' => $source, 'avatar_default_img' => $avatar !== '' ? $avatar : get_theme_file_uri('assets/img/avatar-default.png'), 'user_cover_upload_s' => !array_key_exists('user_cover_upload_s', $settings) || !empty($settings['user_cover_upload_s']), 'user_cover_img' => $cover !== '' ? $cover : get_theme_file_uri('assets/img/user_t.jpg'), 'user_desc_std' => sanitize_text_field((string) ($settings['user_desc_std'] ?? '这家伙很懒,什么都没有写...')), ]; } /** * Public user-home display settings. * * @return array */ public static function get_user_home_settings(): array { $settings = Settings::read_domain('page_display', 'user-home'); $tab_options = is_array($settings['author_main_tab_opt'] ?? null) ? $settings['author_main_tab_opt'] : [ 'post' => ['pc_s' => true, 'm_s' => true], 'favorite' => ['pc_s' => true, 'm_s' => true], 'comment' => ['pc_s' => true, 'm_s' => true], 'follow' => ['pc_s' => true, 'm_s' => true], ]; $map = ['post' => 'posts', 'favorite' => 'favorites', 'comment' => 'comments', 'follow' => 'follow']; $ordered = []; $pc_hide = []; $mobile_hide = []; foreach ($tab_options as $key => $option) { $key = sanitize_key((string) $key); if (!isset($map[$key]) || !is_array($option)) { continue; } $tab = $map[$key]; $ordered[] = $tab; if (empty($option['pc_s'])) { $pc_hide[] = $tab; } if (empty($option['m_s'])) { $mobile_hide[] = $tab; } } return [ 'route_identifier' => 'nicename', 'tabs' => $ordered, 'tabs_order' => $ordered, 'tabs_pc_hide' => $pc_hide, 'tabs_mobile_hide' => $mobile_hide, 'sidebar' => false, 'cover' => true, 'follow' => true, 'posts_per_page' => max(1, (int) get_option('posts_per_page', 10)), 'avatar' => true, 'name' => true, 'desc' => true, 'stats' => false, 'social' => true, 'tab_swiper' => !empty($settings['author_main_tab_swiper']), 'message' => true, 'private' => true, 'paging_ajax' => true, ]; } /** * Private user-center display settings. * * @return array{tabs:string[]} */ public static function get_user_center_settings(): array { return [ 'tabs' => ['profile', 'level', 'comments', 'credits', 'revenue', 'favorites', 'auth'], ]; } /** * User identity, level, medal, and report governance settings. * * @return array{ * verify:array{enabled:bool,public_display:bool,allow_apply:bool,disable_desc:string,desc:string,types:array}, * level:array{enabled:bool,public_display:bool,source:string,label_prefix:string,thresholds:array}, * medal:array{enabled:bool,public_display:bool,limit:int,categories:array}>,items_by_slug:array}, * badge:array{enabled:bool,public_display:bool,limit:int,dictionary:array}, * report:array{enabled:bool,public_entry:bool,public_notice:bool,ban_notice_text:string,categories:array} * } */ public static function get_user_identity_settings(): array { $settings = Settings::get_all(); $ban_notice_text = trim((string) ($settings['user_report_ban_notice_text'] ?? '')); $verify_types = [ 'personal' => [ 'slug' => 'personal', 'label' => __('个人认证', 'aksrt'), 'badge_text' => trim((string) ($settings['user_verify_personal_badge_text'] ?? '')) ?: __('个人认证', 'aksrt'), 'fee' => self::identity_fee_setting($settings, 'user_verify_personal_fee'), 'free_applications' => max(0, (int) ($settings['user_verify_personal_free_applications'] ?? 0)), 'require_real_name' => true, ], 'enterprise' => [ 'slug' => 'enterprise', 'label' => __('企业认证', 'aksrt'), 'badge_text' => trim((string) ($settings['user_verify_enterprise_badge_text'] ?? '')) ?: __('企业认证', 'aksrt'), 'fee' => self::identity_fee_setting($settings, 'user_verify_enterprise_fee'), 'free_applications' => max(0, (int) ($settings['user_verify_enterprise_free_applications'] ?? 0)), 'require_real_name' => true, ], ]; $medal = self::parse_medal_catalog($settings); $provider = sanitize_key((string) ($settings['user_verify_provider'] ?? 'aliyun')); if (!in_array($provider, ['aliyun', 'tencent'], true)) { $provider = 'aliyun'; } return [ 'verify' => [ 'enabled' => !isset($settings['user_verify_enabled']) || !empty($settings['user_verify_enabled']), 'public_display' => !isset($settings['user_verify_public_display']) || !empty($settings['user_verify_public_display']), 'allow_apply' => !isset($settings['user_verify_allow_apply']) || !empty($settings['user_verify_allow_apply']), 'disable_desc' => (string) ($settings['user_verify_disable_desc'] ?? ''), 'desc' => (string) ($settings['user_verify_desc'] ?? ''), 'provider' => $provider, 'allow_manual_fallback' => !isset($settings['user_verify_allow_manual_fallback']) || !empty($settings['user_verify_allow_manual_fallback']), 'poll_interval_seconds' => max(3, min(60, (int) ($settings['user_verify_poll_interval_seconds'] ?? 5))), 'types' => $verify_types, ], 'level' => [ 'enabled' => !isset($settings['user_level_s']) || !empty($settings['user_level_s']), 'public_display' => !isset($settings['user_level_public_display']) || !empty($settings['user_level_public_display']), 'max' => max(3, min(30, (int) ($settings['user_level_max'] ?? 10))), ], 'medal' => $medal, // Alias for existing call sites that still read badge.*; dictionary is derived from medal catalog. 'badge' => [ 'enabled' => $medal['enabled'], 'public_display' => $medal['public_display'], 'limit' => $medal['limit'], 'dictionary' => array_values(array_map(static function (array $item): array { return [ 'slug' => $item['slug'], 'label' => $item['name'], 'tone' => 'info', 'desc' => (string) ($item['desc'] ?? ''), 'icon_url' => $item['icon'], ]; }, $medal['items_by_slug'])), ], 'report' => [ // 默认开启:对齐后台 schema default true 与参考主题 _pz('user_report_s', true)。 'enabled' => (bool) ($settings['user_report_s'] ?? $settings['user_report_enabled'] ?? true), 'public_entry' => !isset($settings['user_report_public_entry']) || !empty($settings['user_report_public_entry']), 'public_notice' => !isset($settings['user_report_public_notice']) || !empty($settings['user_report_public_notice']), // 原样返回配置值:缺省时由 Home::get_profile_notice() 按治理展示态 // 取分级默认文案(封号=全面限制/小黑屋=部分限制/禁言=评论域), // 不再用「部分互动能力已关闭」一刀切描述封号账号。 'ban_notice_text' => $ban_notice_text, ], ]; } /** @param array $settings */ private static function identity_fee_setting(array $settings, string $key): string { $value = array_key_exists($key, $settings) ? $settings[$key] : '0.00'; if (!is_string($value)) { return ''; } try { return \AKSRT\Ledger\AssetAmount::balance(trim($value))->decimal(); } catch (\InvalidArgumentException $e) { return ''; } } /** * 禁封/举报分区的运行时读取。 * * get_all() 是裸 option:用户从未保存过“禁封/举报”分区时, * ban_preset_reason / user_report_img_s / user_report_desc 等键不存在, * schema 默认值只活在后台 UI 里。参考主题 _pz($key, $default) 的语义是 * 缺键即用默认值,因此这里改走 SchemaProvider 的带默认读取。 */ private static function read_report_section(): array { $settings = class_exists('\AKSRT\Admin\SchemaProvider') ? Settings::read_domain('users', 'report') : []; return is_array($settings) && $settings ? $settings : Settings::get_all(); } /** * 禁封设置 */ public static function ban_settings(): array { $settings = self::read_report_section(); // 预置原因:group 数组 [{t:...}](对齐参考主题)或旧版每行一条文本。 // slug 不能用 sanitize_key(标签) 派生:中文标签会被清洗成空串, // 导致前端按 slug 非空过滤后整列表被丢弃。改用标签内容的稳定短哈希 // (跨请求一致、可通过服务端 sanitize_key、ASCII 标签同样稳定)。 $reasons_raw = $settings['ban_preset_reason'] ?? null; $reasons = []; $reason_slug = static function (string $text): string { $key = sanitize_key($text); return $key !== '' ? $key : substr(md5($text), 0, 12); }; if (is_array($reasons_raw)) { foreach ($reasons_raw as $row) { $text = trim((string) ($row['t'] ?? '')); if ($text !== '') { $reasons[] = ['slug' => $reason_slug($text), 'name' => sanitize_text_field($text)]; } } } else { foreach (preg_split('/\r\n|\r|\n/', (string) $reasons_raw) as $line) { $line = trim((string) $line); if ($line === '') { continue; } $parts = explode('|', $line, 2); $reasons[] = [ 'slug' => $reason_slug((string) ($parts[1] ?? $parts[0])), 'name' => sanitize_text_field($parts[1] ?? $parts[0]), ]; } } // 申诉资料:group 数组 [{t:...}] 或旧版逗号分隔文本 $keys_raw = $settings['ban_appeal_keys'] ?? null; $appeal_keys = []; if (is_array($keys_raw)) { foreach ($keys_raw as $row) { $text = trim((string) ($row['t'] ?? '')); if ($text !== '') { $appeal_keys[] = sanitize_text_field($text); } } } else { $appeal_keys = array_filter(array_map('trim', explode(',', (string) $keys_raw))); } return [ 'reasons' => $reasons, 'appeal_desc' => wp_kses_post((string) ($settings['ban_appeal_desc'] ?? '')), 'appeal_keys' => array_values($appeal_keys), ]; } /** * 举报设置 */ public static function report_settings(): array { $settings = self::read_report_section(); return [ // 默认开启:对齐后台 schema default true 与参考主题 _pz('user_report_s', true)。 'enabled' => (bool) ($settings['user_report_s'] ?? $settings['user_report_enabled'] ?? true), 'public_entry' => !isset($settings['user_report_public_entry']) || !empty($settings['user_report_public_entry']), 'public_notice' => !isset($settings['user_report_public_notice']) || !empty($settings['user_report_public_notice']), 'ban_notice_text' => sanitize_text_field((string) ($settings['user_report_ban_notice_text'] ?? '')), 'img_enabled' => !empty($settings['user_report_img_s']), 'desc' => wp_kses_post((string) ($settings['user_report_desc'] ?? '')), ]; } /** * Parse structured user_medal_args catalog (rewrite; no badge dictionary dual-read). * * @param array $settings * @return array{enabled:bool,public_display:bool,limit:int,categories:array,items_by_slug:array} */ public static function parse_medal_catalog(array $settings): array { $enabled = !empty($settings['user_medal_s']); $public = !isset($settings['user_medal_public_display']) || !empty($settings['user_medal_public_display']); $limit = max(1, min(8, (int) ($settings['user_medal_limit'] ?? 3))); $allowed_types = [ 'registration_time', 'checkin_all_day', 'new_post', 'comment', 'followed', 'like', 'comment_like', 'views', 'creator_income', 'affiliate_commission', 'manually_add', ]; $raw = $settings['user_medal_args'] ?? null; if (is_string($raw) && $raw !== '') { $decoded = json_decode($raw, true); $raw = is_array($decoded) ? $decoded : []; } if (!is_array($raw)) { $raw = []; } $categories = []; $items_by_slug = []; foreach ($raw as $cat) { if (!is_array($cat)) { continue; } $cat_name = sanitize_text_field((string) ($cat['cat_name'] ?? $cat['name'] ?? '')); if ($cat_name === '') { $cat_name = __('未命名', 'aksrt'); } $items_out = []; $items = is_array($cat['items'] ?? null) ? $cat['items'] : []; foreach ($items as $item) { if (!is_array($item)) { continue; } $slug = sanitize_key((string) ($item['slug'] ?? '')); $name = sanitize_text_field((string) ($item['name'] ?? '')); if ($slug === '' || $name === '') { continue; } $get_type = sanitize_key((string) ($item['get_type'] ?? 'manually_add')); if (!in_array($get_type, $allowed_types, true)) { $get_type = 'manually_add'; } $icon = esc_url_raw((string) ($item['icon'] ?? $item['icon_url'] ?? '')); $row = [ 'slug' => $slug, 'name' => $name, 'desc' => sanitize_text_field((string) ($item['desc'] ?? '')), 'icon' => $icon, 'get_type' => $get_type, 'get_val' => max(0, (int) ($item['get_val'] ?? 0)), 'cat_name' => $cat_name, ]; $items_out[] = $row; $items_by_slug[$slug] = $row; } $categories[] = [ 'cat_name' => $cat_name, 'items' => $items_out, ]; } return [ 'enabled' => $enabled, 'public_display' => $public, 'limit' => $limit, 'categories' => $categories, 'items_by_slug' => $items_by_slug, ]; } /** * @return array */ public static function parse_level_thresholds(string $raw): array { $lines = preg_split('/[\r\n]+/', $raw, -1, PREG_SPLIT_NO_EMPTY); $thresholds = []; foreach ((array) $lines as $line) { $parts = preg_split('/[|:,]/', trim($line), 2); if (!is_array($parts) || count($parts) < 2) { continue; } $level = (int) trim((string) $parts[0]); $value = (int) trim((string) $parts[1]); if ($level <= 0 || $value < 0) { continue; } $thresholds[$level] = $value; } if (empty($thresholds)) { $thresholds = [1 => 0, 2 => 30, 3 => 100, 4 => 300]; } ksort($thresholds, SORT_NUMERIC); return $thresholds; } /** * @deprecated Removed — medal catalog is structured user_medal_args. Method kept empty stub intentionally deleted. * @return array */ public static function parse_badge_dictionary(string $raw): array { // Rewrite policy: no dual-read of pipe dictionary. Callers must use parse_medal_catalog / get_user_identity_settings()['medal']. unset($raw); return []; } }