refactor(autoload): migrate theme classes to Composer PSR-4

Move 288 types from global AKSRT_* + inc/classes manual requires into
namespace AKSRT\{Domain} under src/AKSRT/, wire composer.json psr-4,
and slim bootstrap to helpers/modules/hooks only. Split multi-class
widget/gateway files, update admin contract audit paths, and document
the layout in ADR/README.
This commit is contained in:
kate522 committed 2026-07-17 13:29:22 +08:00
1 parent 8f349b40fb
commit aaf458af1c
404 files changed
+13602 -7705

No files matched your search

+226
View File
@@ -0,0 +1,226 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
trait AuthReader {
/** 评论是否启用 */
public static function comment_enabled(): bool {
$s = self::get_all();
return !isset($s['comment_enabled']) || !empty($s['comment_enabled']);
}
/** 是否要求注册用户才能评论 */
public static function comment_requires_registration(): bool {
return !empty(self::get_value('comment_require_login', false)) && self::comment_enabled();
}
/** 是否允许注册 */
public static function registration_enabled(): bool {
return (bool) get_option('users_can_register');
}
/** 注册是否要求邮箱验证 */
public static function registration_requires_email_verification(): bool {
$s = self::get_all();
return self::registration_enabled() && !empty($s['registration_email_verification_enabled']);
}
/** 注册邮箱验证方式 */
public static function registration_verification_method(): string {
$s = self::get_all();
$method = $s['registration_email_verification_method'] ?? 'link';
return in_array($method, ['link', 'code'], true) ? $method : 'link';
}
/** 注册邮箱验证有效期(分钟) */
public static function registration_verification_ttl_minutes(): int {
$s = self::get_all();
$ttl = (int) ($s['registration_verification_ttl_minutes'] ?? 30);
return max(5, min(1440, $ttl));
}
/** 验证成功后自动登录 */
public static function registration_auto_login_after_verify(): bool {
$s = self::get_all();
return !isset($s['registration_auto_login_after_verify']) || !empty($s['registration_auto_login_after_verify']);
}
/** 注册邮箱验证功能首次启用时间戳(用于历史账号判定) */
public static function registration_verification_enabled_at(): int {
$s = self::get_all();
return (int) ($s['registration_verification_enabled_at'] ?? 0);
}
/** 主题内置默认图片 URL */
private static function default_asset_url(string $filename): string {
return get_template_directory_uri() . '/assets/img/' . $filename;
}
/** 获取Logo URL(留空时使用内置默认;zibll别名 logo_src 回退) */
public static function logo_url(): string {
$url = trim((string) self::get_value('site_logo', ''));
if ($url === '') {
$url = trim((string) get_option('logo_src', ''));
}
return $url !== '' ? $url : self::default_asset_url('default-logo.svg');
}
/** 获取暗色模式 Logo URL(留空时跟随浅色模式;zibll别名 logo_src_dark 回退) */
public static function logo_dark_url(): string {
$url = trim((string) self::get_value('site_logo_dark', ''));
if ($url === '') {
$url = trim((string) get_option('logo_src_dark', ''));
}
return $url !== '' ? $url : self::logo_url();
}
/** 获取站点品牌文字 */
public static function logo_text(): string {
$text = trim((string) self::get_value('logo_text', ''));
return $text !== '' ? $text : get_bloginfo('name');
}
// ---- 注册登录 auth 段 getter ----
/** 一键禁用登录注册总开关(默认 false;含社交登录,不影响后台) */
public static function close_sign(): bool {
return (bool) self::get_value('close_sign', false);
}
/** 是否关闭注册(受 close_sign 总开关影响;兼容 user_close_signup 别名) */
public static function close_signup(): bool {
if (self::close_sign()) return true;
return (bool) self::get_value('user_close_signup', false);
}
/** 是否关闭账密登录(仅允许社交登录) */
public static function close_signin(): bool {
if (self::close_sign()) return true;
return (bool) self::get_value('close_signin', false);
}
/** 是否关闭登录(受 close_sign 总开关影响;兼容 user_close_login 别名) */
public static function close_login(): bool {
if (self::close_sign()) return true;
return (bool) self::get_value('user_close_login', false);
}
/** 登录注册样式: 'modal' | 'page'(默认 page;modal 运行时未就绪时自动回退 page) */
public static function user_sign_type(): string {
$type = (string) self::get_value('user_sign_type', 'page');
if (!in_array($type, ['modal', 'page'], true)) $type = 'page';
return $type;
}
/** 应急登录 Key(空=禁用;管理员通过 ?aksrt_bypass=<key> 绕过 wp-login 替换) */
public static function admin_sign_key(): string {
return trim((string) self::get_value('admin_sign_key', ''));
}
/** 登录页 UI 配置数组 */
public static function sign_page_ui(): array {
return [
'background' => self::get_value('sign_page_background', []),
'theme_mode' => in_array((string) self::get_value('sign_theme_mode', 'default'), ['default', 'light', 'dark'], true)
? (string) self::get_value('sign_theme_mode', 'default') : 'default',
'show_header' => (bool) self::get_value('sign_show_header', true),
'card_position' => in_array((string) self::get_value('sign_card_position', 'center'), ['left', 'center', 'right'], true)
? (string) self::get_value('sign_card_position', 'center') : 'center',
'show_logo' => (bool) self::get_value('sign_show_logo', true),
'footer' => (string) self::get_value('sign_page_footer', ''),
];
}
/** 弹窗 UI 配置数组 */
public static function modal_ui(): array {
return [
'background' => self::get_value('sign_modal_background', []),
'show_logo' => (bool) self::get_value('sign_modal_show_logo', true),
];
}
/** 登录框日间 LOGO URL(空时回退全局 logo_url) */
public static function user_logo_url(): string {
$ids = self::get_value('user_logo', []);
if (is_array($ids) && !empty($ids)) {
$url = wp_get_attachment_url((int) $ids[0]);
if ($url) return $url;
}
return self::logo_url();
}
/** 登录框夜间 LOGO URL(空时回退全局 logo_dark_url) */
public static function user_logo_dark_url(): string {
$ids = self::get_value('user_logo_dark', []);
if (is_array($ids) && !empty($ids)) {
$url = wp_get_attachment_url((int) $ids[0]);
if ($url) return $url;
}
return self::logo_dark_url();
}
/** 是否允许手机号登录 */
public static function signin_phone_enabled(): bool {
return (bool) self::get_value('user_signin_phone_s', false);
}
/** 免密登录显示模式 */
public static function nopas_active_mode(): string {
$mode = (string) self::get_value('user_signin_nopas_active', 'nopas_first');
return in_array($mode, ['nopas_first', 'password_first', 'nopas_only'], true) ? $mode : 'nopas_first';
}
/** 绑定提醒类型(兼容旧 bool 值:true→'phone',false→'close') */
public static function bind_reminder_type(): string {
$val = self::get_value('bind_reminder', 'close');
if (is_bool($val)) return $val ? 'phone' : 'close';
$val = (string) $val;
return in_array($val, ['email', 'phone', 'email_phone', 'close'], true) ? $val : 'close';
}
/** 强制绑定类型(兼容旧 bool 值:true→'phone',false→'close') */
public static function mandatory_bind_type(): string {
$val = self::get_value('mandatory_bind', 'close');
if (is_bool($val)) return $val ? 'phone' : 'close';
$val = (string) $val;
return in_array($val, ['email', 'phone', 'email_phone', 'close'], true) ? $val : 'close';
}
/** 昵称黑名单关键词数组 */
public static function nickname_blacklist(): array {
$raw = (string) self::get_value('user_nickname_out', '');
if ($raw === '') return [];
// 支持逗号分隔和换行分隔
$items = preg_split('/[,,\r\n]+/', $raw);
return array_values(array_filter(array_map('trim', $items), 'strlen'));
}
/** 邮箱限制配置:['type' => 'black'|'white', 'domains' => [...]] */
public static function email_limit(): array {
$type = (string) self::get_value('user_email_limit_type', 'black');
if (!in_array($type, ['black', 'white'], true)) $type = 'black';
$raw = (string) self::get_value('user_email_out', '');
$domains = [];
if ($raw !== '') {
$items = preg_split('/[\r\n]+/', $raw);
$domains = array_values(array_filter(array_map('trim', $items), 'strlen'));
}
return ['type' => $type, 'domains' => $domains];
}
/** 用户协议前台配置(enabled / required / page) */
public static function user_agreement_settings(): array {
return [
'enabled' => !isset(self::get_all()['user_agreement_s']) || !empty(self::get_value('user_agreement_s', true)),
'required' => !isset(self::get_all()['user_agreement_check']) || !empty(self::get_value('user_agreement_check', true)),
'page' => trim((string) self::get_value('user_agreement_page', '')),
];
}
}
+259
View File
@@ -0,0 +1,259 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class Capabilities {
/**
* Capability definitions registry.
*
* @return array<string, array{label:string, roles?:string[], exclude_roles?:string[], default?:array<string,mixed>}>
*/
public static function cap_definitions(): array {
return [
'user_report' => ['label' => __('举报其它用户', 'aksrt'), 'default' => ['logged' => true]],
'balance_transfer' => ['label' => __('余额转账给其他用户', 'aksrt'), 'exclude_roles' => ['all']],
'points_transfer' => ['label' => __('积分转账给其他用户', 'aksrt'), 'exclude_roles' => ['all']],
'balance_withdraw' => ['label' => __('余额提现', 'aksrt'), 'exclude_roles' => ['all']],
'set_user_ban' => ['label' => __('设置用户封禁状态', 'aksrt'), 'roles' => ['cat_moderator'], 'default' => ['cat_moderator' => true]],
'medal_manually_set' => ['label' => __('为用户授予徽章', 'aksrt'), 'roles' => ['cat_moderator'], 'default' => ['cat_moderator' => true]],
'new_post_add' => ['label' => __('发布新文章', 'aksrt'), 'default' => ['logged' => true]],
'new_post_audit_no' => ['label' => __('投稿无需审核直接发布', 'aksrt'), 'exclude_roles' => ['all']],
'new_post_audit_no_manual' => ['label' => __('无需人工审核直接发布', 'aksrt'), 'exclude_roles' => ['all']],
'new_post_upload_img' => ['label' => __('投稿允许上传图片', 'aksrt'), 'exclude_roles' => ['all']],
'new_post_upload_video' => ['label' => __('投稿允许上传视频', 'aksrt'), 'exclude_roles' => ['all']],
'new_post_iframe_video' => ['label' => __('投稿允许插入 iframe 视频', 'aksrt'), 'exclude_roles' => ['all'], 'default' => ['logged' => true]],
'new_post_upload_file' => ['label' => __('投稿允许上传文件', 'aksrt'), 'exclude_roles' => ['all']],
'new_post_hide' => ['label' => __('投稿允许发布隐藏内容', 'aksrt'), 'default' => ['logged' => true]],
'new_post_image_cover' => ['label' => __('发帖允许设置图片封面', 'aksrt'), 'exclude_roles' => ['all']],
'new_post_slide_cover' => ['label' => __('允许设置幻灯片封面', 'aksrt'), 'exclude_roles' => ['all']],
'new_post_video_cover' => ['label' => __('允许设置视频封面', 'aksrt'), 'exclude_roles' => ['all']],
'new_post_pay' => ['label' => __('投稿允许设置付费内容', 'aksrt'), 'exclude_roles' => ['all']],
'new_post_pay_download' => ['label' => __('投稿允许设置付费下载', 'aksrt'), 'exclude_roles' => ['all']],
'new_post_edit' => ['label' => __('修改自己发布的投稿', 'aksrt'), 'exclude_roles' => ['all'], 'default' => ['logged' => true]],
'new_post_delete' => ['label' => __('删除自己发布的投稿', 'aksrt'), 'exclude_roles' => ['all']],
'comment_view' => ['label' => __('查看评论', 'aksrt'), 'default' => ['all' => true]],
'comment_edit' => ['label' => __('修改自己发布的评论', 'aksrt'), 'exclude_roles' => ['all'], 'default' => ['logged' => true]],
'comment_delete' => ['label' => __('删除自己发布的评论', 'aksrt'), 'exclude_roles' => ['all']],
'comment_audit_no' => ['label' => __('评论无需审核直接发布', 'aksrt'), 'exclude_roles' => ['all']],
'comment_audit_no_manual' => ['label' => __('评论无需人工审核', 'aksrt'), 'exclude_roles' => ['all']],
'comment_set_topping_my_post' => ['label' => __('置顶自己文章下的评论', 'aksrt'), 'exclude_roles' => ['all'], 'default' => ['moderator' => true, 'plate_author' => true, 'cat_moderator' => true]],
'comment_set_topping_other' => ['label' => __('置顶管理帖子下的评论', 'aksrt'), 'roles' => ['moderator', 'plate_author', 'cat_moderator'], 'default' => ['cat_moderator' => true]],
'comment_edit_my_post' => ['label' => __('修改自己文章下的评论', 'aksrt'), 'exclude_roles' => ['all']],
'comment_edit_other' => ['label' => __('修改管理帖子下的评论', 'aksrt'), 'roles' => ['moderator', 'plate_author', 'cat_moderator'], 'default' => ['cat_moderator' => true]],
'comment_audit_my_post' => ['label' => __('审核自己文章下的评论', 'aksrt'), 'exclude_roles' => ['all'], 'default' => ['moderator' => true, 'plate_author' => true, 'cat_moderator' => true]],
'comment_audit_other' => ['label' => __('审核管理帖子下的评论', 'aksrt'), 'roles' => ['moderator', 'plate_author', 'cat_moderator'], 'default' => ['moderator' => true, 'plate_author' => true, 'cat_moderator' => true]],
'comment_delete_my_post' => ['label' => __('删除自己文章下的评论', 'aksrt'), 'exclude_roles' => ['all']],
'comment_delete_other' => ['label' => __('删除管理帖子下的评论', 'aksrt'), 'roles' => ['moderator', 'plate_author', 'cat_moderator'], 'default' => ['cat_moderator' => true]],
];
}
/**
* All role columns in the capability matrix.
*/
public static function cap_role_columns(): array {
return ['all', 'logged', 'level', 'vip', 'auth', 'moderator', 'plate_author', 'cat_moderator'];
}
/**
* Read the user_caps matrix from settings.
*
* @return array<string, array<string, mixed>>
*/
public static function get_user_capabilities(): array {
$s = Settings::get_all();
$stored = is_array($s['user_caps'] ?? null) ? $s['user_caps'] : [];
$defs = self::cap_definitions();
$cols = self::cap_role_columns();
$result = [];
foreach ($defs as $cap_id => $def) {
$defaults = $def['default'] ?? [];
$excludes = $def['exclude_roles'] ?? [];
$only_roles = $def['roles'] ?? null;
$row = [];
foreach ($cols as $col) {
if ($only_roles !== null && !in_array($col, $only_roles, true) && !in_array($col, ['all', 'logged', 'level', 'vip', 'auth'], true)) {
$row[$col] = false;
continue;
}
if (in_array($col, $excludes, true)) {
$row[$col] = false;
continue;
}
if (isset($stored[$cap_id][$col])) {
$val = $stored[$cap_id][$col];
$row[$col] = in_array($col, ['level', 'vip'], true) ? (int) $val : (bool) $val;
} else {
$row[$col] = $defaults[$col] ?? (in_array($col, ['level', 'vip'], true) ? -1 : false);
}
}
$result[$cap_id] = $row;
}
return $result;
}
/**
* Resolve whether a user has a specific capability.
*/
public static function user_can(string $cap, int $user_id = 0, ?int $context = null): bool {
if ($user_id > 0 && user_can($user_id, 'manage_options')) {
return true;
}
$caps = self::get_user_capabilities();
if (!isset($caps[$cap])) {
return false;
}
$row = $caps[$cap];
if (!empty($row['all'])) {
return true;
}
if ($user_id <= 0) {
return false;
}
if (!empty($row['logged'])) {
return true;
}
$level_threshold = (int) ($row['level'] ?? -1);
if ($level_threshold >= 0) {
$user_level = (int) get_user_meta($user_id, 'aksrt_user_level', true);
if ($user_level >= $level_threshold) {
return true;
}
}
$vip_threshold = (int) ($row['vip'] ?? -1);
if ($vip_threshold >= 0) {
$user_vip = (int) get_user_meta($user_id, 'aksrt_vip_level', true);
if ($user_vip >= $vip_threshold) {
return true;
}
}
if (!empty($row['auth'])) {
$verify_status = get_user_meta($user_id, 'aksrt_user_verify_status', true);
if ($verify_status === 'verified') {
return true;
}
}
$forum = class_exists('\AKSRT\Forum\Forum') ? \AKSRT\Forum\Forum::get_instance() : null;
if (!empty($row['moderator']) && $forum && $context > 0) {
if ($forum->get_user_plate_tier($user_id, $context) >= \AKSRT\Forum\Forum::TIER_MODERATOR) {
return true;
}
}
if (!empty($row['plate_author']) && $forum && $context > 0) {
if ($forum->get_user_plate_tier($user_id, $context) >= \AKSRT\Forum\Forum::TIER_PLATE_AUTHOR) {
return true;
}
}
if (!empty($row['cat_moderator']) && $forum) {
if ($context > 0) {
if ($forum->get_user_plate_tier($user_id, $context) >= \AKSRT\Forum\Forum::TIER_CAT_MOD) {
return true;
}
} else {
if (method_exists($forum, 'is_cat_moderator_anywhere') && $forum->is_cat_moderator_anywhere($user_id)) {
return true;
}
}
}
return false;
}
/**
* 获取"网站安全"设置(人机验证见 aksrt_get_captcha_status_summary)。
*/
public static function get_security_settings(): array {
$s = Settings::get_all();
$captcha = function_exists('aksrt_get_captcha_status_summary')
? aksrt_get_captcha_status_summary()
: ['enabled' => false, 'provider' => '', 'configured' => false, 'scenes' => []];
return [
'captcha' => $captcha,
'brush_limit_post' => [
'enabled' => !empty($s['brush_limit_post_enabled']),
'minutes_10' => (int) ($s['brush_limit_post_minutes_10'] ?? 20),
'day_1' => (int) ($s['brush_limit_post_day_1'] ?? 50),
],
'brush_limit_comment' => [
'enabled' => !empty($s['brush_limit_comment_enabled']),
'minutes_10' => (int) ($s['brush_limit_comment_minutes_10'] ?? 20),
'day_1' => (int) ($s['brush_limit_comment_day_1'] ?? 100),
],
'brush_limit_upload' => [
'enabled' => !empty($s['brush_limit_upload_enabled']),
'minutes_10' => (int) ($s['brush_limit_upload_minutes_10'] ?? 50),
'day_1' => (int) ($s['brush_limit_upload_day_1'] ?? 200),
],
'brush_limit_register' => [
'enabled' => !empty($s['brush_limit_register_enabled']),
'minutes_10' => (int) ($s['brush_limit_register_minutes_10'] ?? 20),
'day_1' => (int) ($s['brush_limit_register_day_1'] ?? 100),
],
'brush_limit_order' => [
'enabled' => !empty($s['brush_limit_order_enabled']),
'minutes_10' => (int) ($s['brush_limit_order_minutes_10'] ?? 30),
'day_1' => (int) ($s['brush_limit_order_day_1'] ?? 100),
],
'error_frequency_limit' => !empty($s['error_frequency_limit'] ?? true),
];
}
/**
* 获取"系统工具"设置
*/
public static function get_system_tools_settings(): array {
$s = Settings::get_all();
$time_format = sanitize_text_field((string) ($s['time_format'] ?? 'n月j日 H:i'));
return [
'remove_emoji' => !empty($s['remove_emoji'] ?? true),
'remove_open_sans' => !empty($s['remove_open_sans'] ?? true),
'remove_more_wp_head' => !empty($s['remove_more_wp_head'] ?? true),
'hide_admin_bar' => !empty($s['hide_admin_bar'] ?? true),
'remove_dashboard_widgets' => !empty($s['remove_dashboard_widgets'] ?? true),
'admin_frontend_set' => !empty($s['admin_frontend_set'] ?? true),
'display_wp_update' => !empty($s['display_wp_update'] ?? true),
'close_gutenberg' => !empty($s['close_gutenberg']),
'admin_user_del_fields' => !empty($s['admin_user_del_fields'] ?? true),
'split_upload_s' => !empty($s['split_upload_s'] ?? true),
'split_minimum_size' => max(4, min(100, (int) ($s['split_minimum_size'] ?? 20))),
'newfilename' => !empty($s['newfilename']),
'newfilename_type' => in_array($s['newfilename_type'] ?? 'time', ['random', 'time'], true)
? $s['newfilename_type'] : 'time',
'disabled_large_thumbnail' => !empty($s['disabled_large_thumbnail'] ?? true),
'disabled_autoembed' => !empty($s['disabled_autoembed'] ?? true),
'mce_float_toolbar' => [
'pc_s' => !empty($s['mce_float_toolbar_pc_s'] ?? true),
'm_s' => !empty($s['mce_float_toolbar_m_s'] ?? true),
],
'time_ago_s' => !empty($s['time_ago_s'] ?? true),
'time_format' => $time_format,
];
}
}
@@ -0,0 +1,152 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
trait CategoryIntegrationGroups {
/**
* 获取分类推荐配置(含默认值)
* @return array
*/
public static function get_category_recommend_settings(): array {
return CategoryRuntime::get_category_recommend_settings();
}
/**
* 获取分类封面图 URL(带优先级回退)
*
* 优先级:
* 1. aksrt_category_cover_id term meta
* 2. 该分类最新一篇有缩略图文章的缩略图
* 3. 默认占位图
*/
public static function get_category_cover_url(int $term_id): string {
return CategoryRuntime::get_category_cover_url($term_id);
}
/**
* 获取分类推荐描述(带优先级回退)
*
* 优先级:
* 1. aksrt_category_recommend_desc term meta
* 2. 原生 category description
* 3. 空字符串
*/
public static function get_category_recommend_desc(int $term_id): string {
return CategoryRuntime::get_category_recommend_desc($term_id);
}
/**
* 清理分类推荐缓存
*/
public static function flush_category_recommend_cache(): void {
CategoryRuntime::flush_category_recommend_cache();
}
/**
* 获取分类推荐渲染数据(带缓存)
*
* @param array $settings 分类推荐配置
* @return array 每项包含 term_id, name, description, count, link, cover, posts
*/
public static function get_category_recommend_items(array $settings): array {
return CategoryRuntime::get_category_recommend_items($settings);
}
/**
* 获取底部Tab完整配置
* @return array {enabled, scroll_hide, scroll_down_hide, items}
*/
public static function get_footer_tabbar_settings(): array {
return Integration::get_footer_tabbar_settings();
}
/**
* 获取底部Tab项(仅返回启用的,按排序)
* @return array
*/
public static function get_footer_tabbar_items(): array {
return Integration::get_footer_tabbar_items();
}
/**
* 获取文章页专属Tab模式
* @return string 'default' | 'extend'
*/
public static function get_footer_tabbar_single(): string {
return Integration::get_footer_tabbar_single();
}
/**
* 获取通知系统设置
* @return array {read_retention_days, unread_retention_days, max_per_user}
*/
public static function get_notification_settings(): array {
return Integration::get_notification_settings();
}
/** 单项通知是否启用 */
public static function notification_enabled(string $type): bool {
return Integration::notification_enabled($type);
}
/** user 徽标是否启用 */
public static function notification_user_badge_enabled(): bool {
return Integration::notification_user_badge_enabled();
}
/** user 徽标是否包含即时提醒 */
public static function notification_badge_include_stateful(): bool {
return Integration::notification_badge_include_stateful();
}
/**
* 获取右侧浮动按钮完整配置
* @return array {enabled, style, position, scroll_hide, scroll_down_hide, filter_blur, items}
*/
public static function get_floating_buttons_settings(): array {
return Integration::get_floating_buttons_settings();
}
/**
* 获取右侧浮动按钮项(仅返回启用的,按排序)
* @return array
*/
public static function get_floating_buttons_items(): array {
return Integration::get_floating_buttons_items();
}
/**
* Customizer 注册(可选)
*/
public function customizer(WP_Customize_Manager $wp_customize): void {
$wp_customize->add_setting('blogname', ['default' => get_bloginfo('name'), 'type' => 'option']);
$wp_customize->add_setting('aksrt_logo', [
'default' => '',
'sanitize_callback' => 'esc_url_raw',
'transport' => 'postMessage',
]);
$wp_customize->add_control(new WP_Customize_Image_Control(
$wp_customize,
'aksrt_logo',
['label' => __('Logo', 'aksrt'), 'section' => 'title_tagline']
));
$wp_customize->add_setting('aksrt_favicon', [
'default' => '',
'sanitize_callback' => 'esc_url_raw',
'transport' => 'postMessage',
]);
$wp_customize->add_control(new WP_Customize_Upload_Control(
$wp_customize,
'aksrt_favicon',
['label' => __('Favicon', 'aksrt'), 'section' => 'title_tagline']
));
}
}
+186
View File
@@ -0,0 +1,186 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class CategoryRuntime {
/**
* 获取分类推荐配置(含默认值)
* @return array
*/
public static function get_category_recommend_settings(): array {
$defaults = [
'enable_category_recommend' => false,
'category_recommend_title' => __('分类推荐', 'aksrt'),
'category_recommend_category_ids' => [],
'category_recommend_posts_per_category' => 5,
'category_recommend_columns' => 2,
'category_recommend_show_description' => true,
'category_recommend_show_dates' => true,
'category_recommend_show_more_link' => true,
];
$saved = get_option('aksrt_category_recommend', []);
return array_merge($defaults, $saved);
}
/**
* 获取分类封面图 URL(带优先级回退)
*
* 优先级:
* 1. aksrt_category_cover_id term meta
* 2. 该分类最新一篇有缩略图文章的缩略图
* 3. 默认占位图
*
* @param int $term_id
* @return string
*/
public static function get_category_cover_url(int $term_id): string {
$cover_id = (int) get_term_meta($term_id, 'aksrt_category_cover_id', true);
if ($cover_id > 0) {
$url = wp_get_attachment_url($cover_id);
if ($url) {
return $url;
}
}
$posts = get_posts([
'category__in' => [$term_id],
'posts_per_page' => 1,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
'meta_query' => [
[
'key' => '_thumbnail_id',
'compare' => 'EXISTS',
],
],
'fields' => 'ids',
]);
if (!empty($posts)) {
$thumb_url = get_the_post_thumbnail_url($posts[0], 'medium_large');
if ($thumb_url) {
return $thumb_url;
}
}
return get_template_directory_uri() . '/assets/img/placeholder.svg';
}
/**
* 获取分类推荐描述(带优先级回退)
*
* 优先级:
* 1. aksrt_category_recommend_desc term meta
* 2. 原生 category description
* 3. 空字符串
*
* @param int $term_id
* @return string
*/
public static function get_category_recommend_desc(int $term_id): string {
$description = (string) get_term_meta($term_id, 'aksrt_category_recommend_desc', true);
if ($description !== '') {
return $description;
}
$term = get_term($term_id, 'category');
if ($term && !is_wp_error($term) && $term->description) {
return $term->description;
}
return '';
}
/**
* 清理分类推荐缓存
*/
public static function flush_category_recommend_cache(): void {
$keys = get_option('aksrt_category_recommend_cache_keys', []);
if (is_array($keys)) {
foreach ($keys as $key) {
delete_transient($key);
}
}
delete_option('aksrt_category_recommend_cache_keys');
}
/**
* 获取分类推荐渲染数据(带缓存)
*
* @param array $settings 分类推荐配置
* @return array 每项包含 term_id, name, description, count, link, cover, posts
*/
public static function get_category_recommend_items(array $settings): array {
$category_ids = array_map('absint', (array) ($settings['category_recommend_category_ids'] ?? []));
$category_ids = array_filter($category_ids, fn($id) => $id > 0);
if (empty($category_ids)) {
return [];
}
$per_page = max(1, min(10, (int) ($settings['category_recommend_posts_per_category'] ?? 5)));
$show_desc = !empty($settings['category_recommend_show_description']);
$show_dates = !empty($settings['category_recommend_show_dates']);
$show_more = !empty($settings['category_recommend_show_more_link']);
$cache_key = 'aksrt_cr_' . md5(implode(',', $category_ids) . '|' . $per_page . '|' . (int) $show_desc . '|' . (int) $show_dates . '|' . (int) $show_more);
$cached = get_transient($cache_key);
if ($cached !== false) {
return $cached;
}
$items = [];
foreach ($category_ids as $term_id) {
$term = get_term($term_id, 'category');
if (!$term || is_wp_error($term)) {
continue;
}
$description = $show_desc ? self::get_category_recommend_desc($term_id) : '';
$recent_posts = get_posts([
'category__in' => [$term_id],
'posts_per_page' => $per_page,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
]);
$posts = [];
foreach ($recent_posts as $post) {
$posts[] = [
'title' => get_the_title($post->ID),
'link' => get_permalink($post->ID),
'date' => get_the_date('Y-m-d', $post->ID),
];
}
$items[] = [
'term_id' => (int) $term_id,
'name' => $term->name,
'description' => $description,
'count' => (int) $term->count,
'link' => get_category_link($term_id),
'cover' => self::get_category_cover_url($term_id),
'posts' => $posts,
];
}
set_transient($cache_key, $items, 12 * HOUR_IN_SECONDS);
$keys = get_option('aksrt_category_recommend_cache_keys', []);
if (!is_array($keys)) {
$keys = [];
}
$keys[] = $cache_key;
update_option('aksrt_category_recommend_cache_keys', array_unique($keys));
return $items;
}
}
+209
View File
@@ -0,0 +1,209 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
trait CreditsPaymentGroups {
/**
* 积分配置(合并默认值)。
* @return array
*/
public static function get_credit_settings(): array {
return CreditsRuntime::get_credit_settings();
}
/**
* Normalized runtime settings for daily sign-in rewards.
*
* @return array{enabled:bool,base:int,streak:array<int,int>,currency_name:string}
*/
public static function get_signin_reward_settings(): array {
return CreditsRuntime::get_signin_reward_settings();
}
/**
* Normalized runtime settings for invite rewards and registration gating.
*
* @return array{
* enabled:bool,
* inviter_amount:int,
* invitee_amount:int,
* require_verify:bool,
* ip_daily_cap:int,
* register_field:string,
* reject_invalid:bool,
* registration_enabled:bool
* }
*/
public static function get_invite_reward_settings(): array {
return CreditsRuntime::get_invite_reward_settings();
}
/**
* Canonical invite registration config derived from users.invite fields.
*
* @return array{
* enabled:bool,
* mode:string,
* register_field:string,
* reject_invalid:bool,
* code_length:int,
* open_desc:string,
* must_desc:string
* }
*/
public static function get_invite_registration_config(): array {
return CreditsRuntime::get_invite_registration_config();
}
/**
* 积分功能是否启用(仅积分系统开关)。
*/
public static function credits_enabled(): bool {
return CreditsRuntime::credits_enabled();
}
/**
* 自定义货币名称,为空时回退"积分"。
*/
public static function get_currency_name(): string {
return CreditsRuntime::get_currency_name();
}
/**
* 邀请系统是否启用(=积分系统启用 && invite_reward.enabled)。
* 用于 UI 入口(个人中心邀请 Tab)的展示门禁。
*/
public static function invite_enabled(): bool {
return CreditsRuntime::invite_enabled();
}
/**
* 邀请注册功能总开关(独立于积分系统)。
* 控制邀请链接、注册页邀请码字段、用户中心邀请卡片的显示。
* 存储在 aksrt_settings(invit_code_s / invite_registration_enabled)。
*/
public static function invite_registration_enabled(): bool {
return CreditsRuntime::invite_registration_enabled();
}
/**
* 注册页邀请码字段模式:'hidden' | 'optional' | 'required'
*/
public static function invite_register_field_mode(): string {
return CreditsRuntime::invite_register_field_mode();
}
/**
* 必填模式下邀请码无效是否拒绝注册。
*/
public static function invite_reject_invalid(): bool {
return CreditsRuntime::invite_reject_invalid();
}
public static function invite_code_length(): int {
return CreditsRuntime::invite_code_length();
}
/**
* 资源下载默认积分价(当资源未单独设价时回退使用)。
*/
public static function download_default_cost(): int {
return CreditsRuntime::download_default_cost();
}
/**
* Unified paid-content and download policy settings.
*
* @return array{
* download_login:bool,
* download_credit:bool,
* download_captcha:bool,
* default_cost:int,
* paid_post_types:string[],
* vip_free_download:bool,
* vip_exempt_roles:string[],
* catalog_note:string,
* manual_delivery_note:string
* }
*/
public static function get_paid_content_settings(): array {
return PaymentRuntime::get_paid_content_settings();
}
/**
* Content coupon global switches.
*
* @return array{enabled:bool,desc:string}
*/
public static function get_content_coupon_settings(): array {
return PaymentRuntime::get_content_coupon_settings();
}
/**
* @return array{enabled:bool,mode:string,packages_raw:string,package_count:int,help_text:string}
*/
public static function get_payment_recharge_settings(): array {
return PaymentRuntime::get_payment_recharge_settings();
}
/**
* @return array{enabled:bool,name:string,badge_text:string,packages_raw:string,package_count:int,help_text:string}
*/
public static function get_payment_vip_settings(): array {
return PaymentRuntime::get_payment_vip_settings();
}
/**
* @return array{enabled:bool,share_ratio:int,settlement_threshold:int,cycle_days:int,note:string}
*/
public static function get_payment_revenue_settings(): array {
return PaymentRuntime::get_payment_revenue_settings();
}
/**
* @return array{enabled:bool,ratio:int,window_days:int,require_paid_order:bool,note:string}
*/
public static function get_payment_affiliate_settings(): array {
return PaymentRuntime::get_payment_affiliate_settings();
}
/**
* @return array{mode:string,test_enabled:bool,manual_review:bool,callback_secret:string,help_text:string}
*/
public static function get_payment_gateway_settings(): array {
return PaymentRuntime::get_payment_gateway_settings();
}
/**
* 获取订单设置
*
* @return array{prefix:string,expiry:int,show_user:bool,show_paycount:bool,no_logged_in:bool,cookie_day:int,no_logged_remind:bool,wait_float:bool,order_max_minutes:int,mark:string,box_position:string,free_logged_show:bool,show_ip:bool,show_device:bool,show_order_no:bool,show_create_time:bool,show_pay_time:bool,status_texts:array,service:array}
*/
public static function get_order_settings(): array {
return PaymentRuntime::get_order_settings();
}
/**
* 获取充值卡设置
*
* @return array{enabled:bool,desc:string,type:string,prefix:string,length:int,batch_size:int,max_redeem:int,expiry_days:int,amount:int,vip_days:int,credits:int,redeem_page:string,success_msg:string,fail_msg:string}
*/
public static function get_card_pass_settings(): array {
return PaymentRuntime::get_card_pass_settings();
}
/**
* 获取提现管理设置
*
* @return array{min_amount:int,service_charge_rate:int,text:string}
*/
public static function get_withdrawal_settings(): array {
return PaymentRuntime::get_withdrawal_settings();
}
}
+268
View File
@@ -0,0 +1,268 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class CreditsRuntime {
/**
* 积分配置(合并默认值)。
* @return array
*/
public static function get_credit_settings(): array {
$defaults = [
'enabled' => true,
'currency_name' => '',
'register_bonus' => 10,
'daily_signin' => [
'enabled' => true,
'base' => 1,
'streak' => [
3 => 2,
7 => 5,
30 => 20,
],
],
'comment_reward' => [
'enabled' => true,
'amount' => 1,
'daily_cap' => 5,
'min_length' => 10,
'on_approved' => true,
],
'invite_reward' => [
'enabled' => true,
'inviter_amount' => 5,
'invitee_amount' => 2,
'require_verify' => true,
'ip_daily_cap' => 3,
'register_field' => 'optional',
'reject_invalid' => true,
],
'download_default_cost' => 0,
];
$saved = get_option('aksrt_credit_settings', []);
if (!is_array($saved)) {
$saved = [];
}
return array_replace_recursive($defaults, $saved);
}
/**
* Normalized runtime settings for daily sign-in rewards.
*
* @return array{enabled:bool,base:int,streak:array<int,int>,currency_name:string}
*/
public static function get_signin_reward_settings(): array {
$config = self::get_credit_settings();
$signin = is_array($config['daily_signin'] ?? null) ? $config['daily_signin'] : [];
$streak = [];
foreach ((array) ($signin['streak'] ?? []) as $day => $bonus) {
$day = (int) $day;
$bonus = (int) $bonus;
if ($day <= 0 || $bonus < 0) {
continue;
}
$streak[$day] = $bonus;
}
ksort($streak, SORT_NUMERIC);
return [
'enabled' => !empty($config['enabled']) && !empty($signin['enabled']),
'base' => max(0, (int) ($signin['base'] ?? 0)),
'streak' => $streak,
'currency_name' => self::get_currency_name(),
];
}
/**
* Normalized runtime settings for invite rewards and registration gating.
*
* @return array{
* enabled:bool,
* inviter_amount:int,
* invitee_amount:int,
* require_verify:bool,
* ip_daily_cap:int,
* register_field:string,
* reject_invalid:bool,
* registration_enabled:bool
* }
*/
public static function get_invite_reward_settings(): array {
$config = self::get_credit_settings();
$invite = is_array($config['invite_reward'] ?? null) ? $config['invite_reward'] : [];
$all = Settings::get_all();
$register_field = (string) ($invite['register_field'] ?? 'optional');
if (!in_array($register_field, ['hidden', 'optional', 'required'], true)) {
$register_field = 'optional';
}
$inviter_amount = array_key_exists('invit_reward_points', $all)
? max(0, (int) $all['invit_reward_points'])
: max(0, (int) ($invite['inviter_amount'] ?? 0));
$invitee_enabled = array_key_exists('invit_invitee_reward', $all)
? !empty($all['invit_invitee_reward'])
: max(0, (int) ($invite['invitee_amount'] ?? 0)) > 0;
$invitee_amount = $invitee_enabled
? max(0, (int) ($invite['invitee_amount'] ?? $inviter_amount))
: 0;
return [
'enabled' => !empty($config['enabled']) && !empty($invite['enabled']),
'inviter_amount' => $inviter_amount,
'invitee_amount' => $invitee_amount,
'require_verify' => !empty($invite['require_verify']),
'ip_daily_cap' => max(0, (int) ($invite['ip_daily_cap'] ?? 0)),
'register_field' => $register_field,
'reject_invalid' => !empty($invite['reject_invalid']),
'registration_enabled' => self::invite_registration_enabled(),
'invit_reward_points' => $inviter_amount,
'invit_reward_level' => max(0, (int) ($all['invit_reward_level'] ?? 0)),
'invit_reward_vip_days' => max(0, (int) ($all['invit_reward_vip_days'] ?? 0)),
'invit_invitee_reward' => $invitee_enabled,
];
}
/**
* Canonical invite registration config derived from users.invite fields.
*
* @return array{
* enabled:bool,
* mode:string,
* register_field:string,
* reject_invalid:bool,
* code_length:int,
* open_desc:string,
* must_desc:string
* }
*/
public static function get_invite_registration_config(): array {
$all = Settings::get_all();
$credit_config = self::get_credit_settings();
$enabled = array_key_exists('invit_code_s', $all)
? !empty($all['invit_code_s'])
: (!isset($all['invite_registration_enabled']) || !empty($all['invite_registration_enabled']));
$mode = isset($all['invit_code_mode']) ? (string) $all['invit_code_mode'] : '';
if (!in_array($mode, ['open', 'must', 'close'], true)) {
$legacy_mode = (string) ($credit_config['invite_reward']['register_field'] ?? 'optional');
if ($legacy_mode === 'required') {
$mode = 'must';
} elseif ($legacy_mode === 'hidden') {
$mode = 'close';
} else {
$mode = 'open';
}
}
$register_field = 'optional';
if (!$enabled || $mode === 'close') {
$register_field = 'hidden';
} elseif ($mode === 'must') {
$register_field = 'required';
}
$open_desc = trim((string) ($all['invit_code_open_desc'] ?? ''));
if ($open_desc === '') {
$open_desc = __('邀请码为选填,没有邀请码也可以注册', 'aksrt');
}
$must_desc = trim((string) ($all['invit_code_must_desc'] ?? ''));
if ($must_desc === '') {
$must_desc = __('请输入邀请码', 'aksrt');
}
$code_length = isset($all['invit_code_length']) ? (int) $all['invit_code_length'] : 0;
if ($code_length <= 0) {
$code_length = (int) ($all['invite_code_length'] ?? 6);
}
$code_length = max(4, min(12, $code_length));
return [
'enabled' => $enabled && $mode !== 'close',
'mode' => $mode,
'register_field' => $register_field,
'reject_invalid' => !empty($credit_config['invite_reward']['reject_invalid']),
'code_length' => $code_length,
'open_desc' => $open_desc,
'must_desc' => $must_desc,
];
}
/**
* 积分功能是否启用。
* 仅看积分系统开关;不再与 cms_features.download_credit 联合(该开关表示积分购买模式,非按次扣费)。
*/
public static function credits_enabled(): bool {
$config = self::get_credit_settings();
return !empty($config['enabled']);
}
/**
* 自定义货币名称,为空时回退"积分"。
*/
public static function get_currency_name(): string {
$config = self::get_credit_settings();
$name = trim((string) ($config['currency_name'] ?? ''));
return $name !== '' ? $name : __('积分', 'aksrt');
}
/**
* 邀请系统是否启用(=积分系统启用 && invite_reward.enabled)。
* 用于 UI 入口(个人中心邀请 Tab)的展示门禁。
*/
public static function invite_enabled(): bool {
$config = self::get_credit_settings();
return !empty($config['enabled']) && !empty($config['invite_reward']['enabled']);
}
/**
* 邀请注册功能总开关(独立于积分系统)。
* 控制邀请链接、注册页邀请码字段、用户中心邀请卡片的显示。
* 存储在 aksrt_settings(invit_code_s / invite_registration_enabled)。
*/
public static function invite_registration_enabled(): bool {
return !empty(self::get_invite_registration_config()['enabled']);
}
/**
* 注册页邀请码字段模式:'hidden' | 'optional' | 'required'
*/
public static function invite_register_field_mode(): string {
$mode = (string) (self::get_invite_registration_config()['register_field'] ?? 'hidden');
return in_array($mode, ['hidden', 'optional', 'required'], true) ? $mode : 'hidden';
}
/**
* 必填模式下邀请码无效是否拒绝注册。
*/
public static function invite_reject_invalid(): bool {
return !empty(self::get_invite_registration_config()['reject_invalid']);
}
public static function invite_code_length(): int {
return max(4, min(12, (int) (self::get_invite_registration_config()['code_length'] ?? 6)));
}
/**
* 资源下载默认积分价(当资源未单独设价时回退使用)。
*/
public static function download_default_cost(): int {
$config = self::get_credit_settings();
return max(0, (int) ($config['download_default_cost'] ?? 0));
}
}
+305
View File
@@ -0,0 +1,305 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
trait DisplayPageGroups {
/**
* 获取 SEO 设置(从 aksrt_settings 读取)
*/
public static function get_seo_settings(): array {
$s = self::get_all();
return [
'seo_enabled' => !isset($s['seo_enabled']) || !empty($s['seo_enabled']),
'seo_connector' => trim((string) ($s['seo_connector'] ?? '-')),
'seo_title' => $s['seo_title'] ?? '',
'seo_description' => $s['seo_description'] ?? '',
'seo_keywords' => self::seo_keywords(),
'seo_ai_enabled' => !empty($s['seo_ai_enabled']),
'ai_seo_title_prompt' => trim((string) ($s['ai_seo_title_prompt'] ?? '')),
'ai_seo_keywords_prompt' => trim((string) ($s['ai_seo_keywords_prompt'] ?? '')),
'ai_seo_description_prompt' => trim((string) ($s['ai_seo_description_prompt'] ?? '')),
'seo_baidu_push' => !empty($s['seo_baidu_push']) || !empty($s['baidu_push_enabled']),
'seo_baidu_normal' => !empty($s['seo_baidu_normal']),
'seo_baidu_fast' => !empty($s['seo_baidu_fast']),
'seo_baidu_token' => (string) (($s['seo_baidu_token'] ?? '') ?: ($s['baidu_push_token'] ?? '')),
'seo_image_optimize' => !isset($s['seo_image_optimize']) || !empty($s['seo_image_optimize']),
'seo_image_default' => esc_url_raw((string) ($s['seo_image_default'] ?? '')),
'seo_image_always_default'=> !empty($s['seo_image_always_default']),
'seo_auto_alt' => !isset($s['seo_auto_alt']) || !empty($s['seo_auto_alt']),
'seo_go_link' => !isset($s['seo_go_link']) || !empty($s['seo_go_link']),
'seo_go_link_nonce' => !isset($s['seo_go_link_nonce']) || !empty($s['seo_go_link_nonce']),
'seo_go_link_post' => !isset($s['seo_go_link_post']) || !empty($s['seo_go_link_post']),
'seo_go_link_exclude' => trim((string) ($s['seo_go_link_exclude'] ?? '')),
'seo_no_category' => !empty($s['seo_no_category']),
'seo_jsonld' => !isset($s['seo_jsonld']) || !empty($s['seo_jsonld']),
'seo_jsonld_single' => !isset($s['seo_jsonld_single']) || !empty($s['seo_jsonld_single']),
'seo_jsonld_page' => !isset($s['seo_jsonld_page']) || !empty($s['seo_jsonld_page']),
'head_code' => self::custom_head_code(),
'foot_code' => self::custom_footer_code(),
'trackcode' => self::trackcode(),
];
}
/**
* 获取顶部多功能组件设置
*/
public static function get_header_components_settings(): array {
$s = self::get_all();
$allowed_page_types = [
'home',
'topics',
'category',
'tag',
'author',
'single',
'search',
'forum_home',
'forum_plate',
'forum_post',
'page',
];
$show_on = isset($s['header_components_show_on']) ? (array) $s['header_components_show_on'] : ['home'];
$show_on = array_values(array_intersect(
array_map('sanitize_key', $show_on),
$allowed_page_types
));
$show_type = $s['header_components_device_visibility'] ?? '';
if (!in_array($show_type, ['', 'only_pc', 'only_sm'], true)) {
$show_type = '';
}
return [
'search_enabled' => self::header_components_search_enabled(),
'search_config' => self::get_header_components_search_config(),
'cards_enabled' => self::header_components_cards_enabled(),
'show_on_pages' => $show_on,
'show_type' => $show_type,
'filter_blur' => isset($s['header_components_filter_blur_devices']) ? array_values(array_intersect(array_map('sanitize_key', (array) $s['header_components_filter_blur_devices']), ['pc', 'mobile'])) : [],
'card_show' => isset($s['header_components_card_show_devices']) ? array_values(array_intersect(array_map('sanitize_key', (array) $s['header_components_card_show_devices']), ['pc', 'mobile'])) : ['pc', 'mobile'],
'slider_config' => self::get_slider_config(),
'icon_cards' => self::get_header_components_icon_cards(),
'card_columns' => self::get_header_components_card_columns(),
'card_bg_enabled' => self::header_components_card_bg_enabled(),
];
}
/**
* 获取"全局&功能"域的合并设置
*/
public static function get_global_settings(): array {
return [
'site_logo' => self::logo_url(),
'site_logo_dark' => self::logo_dark_url(),
'logo_text' => self::logo_text(),
'logo_width' => self::logo_width(),
'site_favicon' => self::favicon_url(),
'avatar_source' => self::avatar_source(),
'icp_filing' => self::icp_filing(),
'footer_text' => self::footer_text(),
'theme_mode' => self::theme_mode(),
'theme_mode_button' => self::get_theme_mode_button_positions(),
'primary_color' => self::primary_color(),
'border_radius' => self::border_radius(),
'theme_main_radius' => self::card_radius(),
'qj_loading' => self::is_qj_loading_enabled(),
'qj_dh_xs' => self::get_qj_loading_style(),
'container_max_width' => self::container_max_width(),
'font_family' => self::font_family(),
'search_type' => self::search_mode(),
'search_placeholder' => self::search_placeholder(),
'search_history_enabled' => self::search_history_enabled(),
'hot_search_enabled' => self::hot_search_enabled(),
'hot_search_keywords' => self::hot_search_keywords(),
'search_suggestions_enabled' => self::search_suggestions_enabled(),
'search_posts' => self::search_posts_enabled(),
'search_no_page' => self::search_no_page(),
'search_exclude_cats' => self::search_exclude_cats(),
'search_cat' => self::search_cat_enabled(),
'search_cat_in' => self::search_cat_in(),
'search_more_cat_obj' => self::search_more_cat_obj(),
'search_custom_key_s' => self::search_custom_key_enabled(),
'search_custom_key' => self::search_custom_key(),
'search_facets' => self::search_facets_enabled(),
'search_facets_order' => self::search_facets_order(),
'custom_css' => self::custom_css(),
'head_code' => self::custom_head_code(),
'foot_code' => self::custom_footer_code(),
'trackcode' => self::trackcode(),
'seo' => self::get_seo_settings(),
'mail' => self::get_smtp(),
'email_events' => self::get_email_event_config(),
'email_private_receive_limit' => self::get_email_private_receive_limit(),
'footer' => self::get_footer_settings(),
'tabbar' => self::get_footer_tabbar_settings(),
'floating_buttons' => self::get_floating_buttons_settings(),
'header_nav' => self::get_header_nav_settings(),
'header_newposts' => self::get_header_newposts_config(),
'header_theme' => self::get_header_theme_config(),
'friend_links' => self::get_friend_links(),
'comment_enabled' => self::comment_enabled(),
'cms_features' => self::get_cms_features(),
'page_display' => self::get_page_display_settings(),
'footer_theme' => self::get_footer_theme_config(),
];
}
/**
* 获取"页面&显示"域的合并设置
*/
public static function get_page_display_settings(): array {
return [
'header_components' => self::get_header_components_settings(),
'homepage' => self::get_homepage_fixed_settings(),
'category_recommend' => self::get_category_recommend_settings(),
'category' => self::get_category_page_settings(),
'tag' => self::get_tag_page_settings(),
'special' => self::get_special_page_settings(),
'user_home' => self::get_user_home_settings(),
'other' => self::get_other_page_settings(),
'background' => self::get_global_background_settings(),
'menu_decoration' => function_exists('aksrt_get_menu_decoration_settings')
? aksrt_get_menu_decoration_settings()
: [],
];
}
/**
* 获取"文章&列表"域的合并设置
*/
public static function get_post_list_settings(): array {
$list = self::get_post_list_style_settings();
$thumb = self::get_post_thumb_settings();
$filter = self::get_post_filter_settings();
return [
'list' => [
'post_card_style' => $list['list_type'] ?? $list['card_style'] ?? 'thumb',
'post_list_author' => $list['show_author'],
'post_show_date' => $list['show_date'],
'post_show_views' => $list['show_views'],
'post_show_category' => $list['show_category'],
'post_show_comments' => $list['show_comments'],
'post_show_likes' => $list['show_likes'],
'post_excerpt_length' => $list['excerpt_length'],
'paging_ajax_s' => PostDisplay::is_list_paging_ajax() ? '1' : '0',
'paging_ajax_ias_s' => PostDisplay::is_list_paging_ajax_ias(),
'post_pagination_mode' => self::get_post_pagination_mode(),
'render_mode' => $list['render_mode'],
'sticky_s' => $list['sticky_pages'],
'list_badge_show' => $list['badge_types'],
'target_blank' => $list['target_blank'],
'item_heading_bold' => $list['heading_bold'],
'list_meta_show' => $list['mobile_meta'],
'list_show_type' => $list['list_show_type'],
'ias_max' => $list['ias_max'],
'ajax_trigger' => $list['ajax_trigger_text'],
'ajax_nomore' => $list['ajax_nomore_text'],
'ajax_nomore_text' => $list['ajax_nomore_text'],
'post_list_columns' => $list['columns'],
'post_list_per_page' => $list['per_page'],
'list_list_style' => $list['list_style'],
'list_list_img_position' => $list['list_img_position'],
'list_list_scale' => $list['list_scale'],
'list_card_style' => $list['card_variant'] ?? '',
'list_card_scale' => $list['card_scale'],
'list_card_home' => $list['card_home'],
'list_card_cat' => $list['card_cat_ids'] ?? [],
'list_card_tag' => $list['card_tag'],
'list_card_topics' => $list['card_topics'],
'list_card_author' => $list['card_author'],
],
'thumb' => [
'thumb_priority' => implode(',', $thumb['priority']),
'thumb_fallback' => $thumb['fallback'],
'thumb_spare_images' => $thumb['spare_images'],
'thumb_lazy' => $thumb['lazy'],
'thumb_aspect_ratio' => $thumb['aspect_ratio'],
'thumb_multi_enabled' => $thumb['multi_enabled'],
'thumb_multi_count' => $thumb['multi_count'],
'list_thumb_fit' => $thumb['fit'],
'list_thumb_slides_s' => $thumb['slides_s'],
'list_thumb_video_s' => $thumb['video_s'],
'list_thumb_video_mute_s' => $thumb['video_mute_s'],
'thumb_postfirstimg_s' => $thumb['postfirstimg_s'],
'thumb_catimg_s' => $thumb['catimg_s'],
],
'filter' => [
'filter_enabled' => $filter['enabled'],
'filter_categories' => $filter['categories'],
'filter_tags' => $filter['tags'],
'filter_sort' => $filter['sort'],
],
];
}
/**
* 获取"用户&互动"域的合并设置
*/
public static function get_user_interaction_settings(): array {
return [
'oauth' => self::get_oauth(),
'notifications' => self::get_notification_settings(),
'credits' => self::get_credit_settings(),
];
}
/**
* 获取"功能&权限"域的合并设置
*/
public static function get_permission_settings(): array {
return [
'moderation' => self::get_moderation(),
'capabilities' => self::get_user_capabilities(),
];
}
/**
* 获取"支付&付费"域的合并设置
*/
public static function get_payment_settings(): array {
return [
'paid' => self::get_paid_content_settings(),
'recharge' => self::get_payment_recharge_settings(),
'vip' => self::get_payment_vip_settings(),
'revenue' => self::get_payment_revenue_settings(),
'affiliate' => self::get_payment_affiliate_settings(),
'gateway' => self::get_payment_gateway_settings(),
'order' => self::get_order_settings(),
'card_pass' => self::get_card_pass_settings(),
'withdrawal' => self::get_withdrawal_settings(),
];
}
/**
* 获取 IP 归属地设置
*/
public static function get_ip_geo_settings(): array {
return ExtensionRuntime::get_ip_geo_settings();
}
/**
* 获取内容审核设置
*/
public static function get_content_audit_settings(): array {
return ExtensionRuntime::get_content_audit_settings();
}
/**
* 获取搜索引擎推送设置
*/
public static function get_search_push_settings(): array {
return ExtensionRuntime::get_search_push_settings();
}
/**
* 获取短信接口设置
*/
public static function get_sms_settings(): array {
return ExtensionRuntime::get_sms_settings();
}
}
+235
View File
@@ -0,0 +1,235 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class DisplayRuntime {
/** 获取Banner列表 */
public static function get_banners(string $position = ''): array {
$banners = get_option('aksrt_banners', []) ?: [];
if ($position) {
$banners = array_filter($banners, fn($banner) => ($banner['position'] ?? '') === $position);
}
usort($banners, fn($a, $b) => ($a['sort_order'] ?? 0) - ($b['sort_order'] ?? 0));
return array_filter($banners, fn($banner) => ($banner['status'] ?? 'enabled') === 'enabled');
}
/** 保存Banner */
public static function save_banner(array $data): int {
$banners = get_option('aksrt_banners', []);
if (!empty($data['id'])) {
$banners[$data['id']] = $data;
} else {
$data['id'] = uniqid('banner_');
$banners[] = $data;
}
update_option('aksrt_banners', $banners);
return $data['id'];
}
/** 删除Banner */
public static function delete_banner(string $id): void {
$banners = get_option('aksrt_banners', []);
unset($banners[$id]);
update_option('aksrt_banners', array_values($banners));
}
/** 获取友链列表 */
public static function get_friend_links(): array {
$links = get_option('aksrt_friend_links', []) ?: [];
return array_values(array_filter($links, static function ($link) {
return !empty($link['active']);
}));
}
/** 保存友链 */
public static function save_friend_links(array $links): void {
update_option('aksrt_friend_links', $links);
}
/**
* 是否显示顶部组件搜索覆盖框
*/
public static function header_components_search_enabled(): bool {
$settings = Settings::get_all();
return !empty($settings['header_components_search_enabled']);
}
/**
* Hero 搜索组件详细配置(PC/移动分别 + 子选项)
*
* @return array{pc:bool,mobile:bool,show_keywords:bool,popular_limit:int,show_input_cat:bool,show_type:bool,keywords:string[],categories:array,type_options:array}
*/
public static function get_header_components_search_config(): array {
$settings = Settings::get_all();
$master = self::header_components_search_enabled();
$is_mobile = function_exists('wp_is_mobile') && wp_is_mobile();
$pc_on = $master && (!isset($settings['header_components_search_pc']) || !empty($settings['header_components_search_pc']));
$mobile_on = $master && (!isset($settings['header_components_search_mobile']) || !empty($settings['header_components_search_mobile']));
$show = $is_mobile ? $mobile_on : $pc_on;
$show_kw = !empty($settings['header_components_search_show_keywords']);
$kw_limit = max(1, min(20, (int) ($settings['header_components_search_popular_limit'] ?? 5)));
$show_cat = !empty($settings['header_components_search_show_input_cat']);
$show_type = !empty($settings['header_components_search_show_type']);
$keywords = [];
if ($show && $show_kw) {
$raw = $settings['hot_search_keywords'] ?? '';
$keywords = is_array($raw) ? $raw : preg_split('/[\s,]+/', (string) $raw, -1, PREG_SPLIT_NO_EMPTY);
$keywords = array_slice(array_map('sanitize_text_field', $keywords), 0, $kw_limit);
}
$categories = [];
if ($show && $show_cat) {
$cats = get_categories(['hide_empty' => false, 'number' => 50]);
foreach ($cats as $cat) {
$categories[] = ['id' => $cat->term_id, 'name' => $cat->name];
}
}
$type_options = [];
if ($show && $show_type) {
$types = Settings::get_search_content_types();
foreach (($types['enabled'] ?? []) as $type_key => $type_value) {
if ($type_value) {
$type_options[] = ['key' => $type_key, 'label' => $type_key];
}
}
}
return [
'pc' => $pc_on,
'mobile' => $mobile_on,
'show' => $show,
'show_keywords' => $show_kw,
'popular_limit' => $kw_limit,
'show_input_cat' => $show_cat,
'show_type' => $show_type,
'keywords' => $keywords,
'categories' => $categories,
'type_options' => $type_options,
];
}
/**
* 是否显示顶部组件图标卡片覆盖
*/
public static function header_components_cards_enabled(): bool {
$settings = Settings::get_all();
return !empty($settings['header_components_cards_enabled']);
}
public static function homepage_module_hero_enabled(): bool {
$settings = Settings::get_all();
return !isset($settings['homepage_module_hero_enabled']) || !empty($settings['homepage_module_hero_enabled']);
}
public static function homepage_module_banner_enabled(): bool {
$settings = Settings::get_all();
return !isset($settings['homepage_module_banner_enabled']) || !empty($settings['homepage_module_banner_enabled']);
}
/**
* Global slider configuration shared by Hero and Banner.
*
* @return array<string,mixed>
*/
public static function get_slider_config(): array {
$settings = Settings::get_all();
$config = $settings['slider_config'] ?? [];
return [
'effect' => in_array($config['effect'] ?? '', ['fade', 'slide', 'cube', 'coverflow', 'flip'], true) ? $config['effect'] : 'fade',
'direction' => in_array($settings['slider_config_direction'] ?? ($config['direction'] ?? ''), ['horizontal', 'vertical'], true)
? ($settings['slider_config_direction'] ?? ($config['direction'] ?? 'horizontal')) : 'horizontal',
'loop' => !isset($config['loop']) || !empty($config['loop']),
'autoplay_interval' => max(1000, min(30000, (int) ($config['autoplay_interval'] ?? 5000))),
'pagination' => !isset($config['pagination']) || !empty($config['pagination']),
'navigation' => !isset($config['navigation']) || !empty($config['navigation']),
'button' => isset($settings['slider_config_button']) ? !empty($settings['slider_config_button']) : (!isset($config['button']) || !empty($config['button'])),
'auto_height' => !empty($config['auto_height']),
'pc_height' => isset($config['pc_height']) && $config['pc_height'] !== '' ? sanitize_text_field($config['pc_height']) : '',
'mobile_height' => isset($config['mobile_height']) && $config['mobile_height'] !== '' ? sanitize_text_field($config['mobile_height']) : '',
'max_height' => isset($settings['slider_config_max_height']) && $settings['slider_config_max_height'] !== '' ? sanitize_text_field($settings['slider_config_max_height']) : (isset($config['max_height']) && $config['max_height'] !== '' ? sanitize_text_field($config['max_height']) : ''),
'min_height' => isset($settings['slider_config_min_height']) && $settings['slider_config_min_height'] !== '' ? sanitize_text_field($settings['slider_config_min_height']) : (isset($config['min_height']) && $config['min_height'] !== '' ? sanitize_text_field($config['min_height']) : ''),
'scale_height' => !empty($settings['slider_config_scale_height'] ?? ($config['scale_height'] ?? false)),
'scale' => !empty($settings['slider_config_scale'] ?? ($config['scale'] ?? false)),
'speed' => max(100, min(3000, (int) ($settings['slider_config_speed'] ?? ($config['speed'] ?? 300)))),
'space_between' => max(0, min(100, (int) ($config['space_between'] ?? 0))),
'nav_color' => sanitize_hex_color((string) ($settings['header_components_nav_color'] ?? '')) ?: '',
];
}
public static function get_header_components_icon_cards(): array {
$settings = Settings::get_all();
$cards = $settings['header_components_icon_cards'] ?? [];
return is_array($cards) ? array_values($cards) : [];
}
public static function get_header_components_card_columns(): int {
$settings = Settings::get_all();
$columns = (int) ($settings['header_components_card_columns'] ?? 4);
return max(2, min(6, $columns));
}
public static function header_components_card_bg_enabled(): bool {
$settings = Settings::get_all();
return !empty($settings['header_components_card_bg']);
}
/**
* 获取全局背景设置
*/
public static function get_global_background_settings(): array {
$settings = Settings::get_all();
$valid_sizes = ['cover', 'contain', 'auto', '100%', '100% 100%'];
$size = sanitize_text_field((string) ($settings['bg_size'] ?? '100%'));
if (!in_array($size, $valid_sizes, true)) {
$size = '100%';
}
$valid_positions = ['center center', 'top center', 'bottom center'];
$position = sanitize_text_field((string) ($settings['bg_position'] ?? 'center center'));
if (!in_array($position, $valid_positions, true)) {
$position = 'center center';
}
$valid_repeats = ['no-repeat', 'repeat', 'repeat-x', 'repeat-y'];
$repeat = sanitize_key((string) ($settings['bg_repeat'] ?? 'no-repeat'));
if (!in_array($repeat, $valid_repeats, true)) {
$repeat = 'no-repeat';
}
$attachment = sanitize_key((string) ($settings['bg_attachment'] ?? 'fixed'));
if (!in_array($attachment, ['fixed', 'scroll'], true)) {
$attachment = 'fixed';
}
return [
'bg_color' => sanitize_hex_color((string) ($settings['bg_color'] ?? '')) ?: '',
'bg_image' => esc_url_raw((string) ($settings['bg_image'] ?? '')),
'bg_size' => $size,
'bg_position' => $position,
'bg_repeat' => $repeat,
'bg_attachment' => $attachment,
'bg_dark_color' => sanitize_hex_color((string) ($settings['bg_dark_color'] ?? '')) ?: '',
'bg_dark_image' => esc_url_raw((string) ($settings['bg_dark_image'] ?? '')),
'bg_overlay' => !empty($settings['bg_overlay']),
'bg_overlay_color' => sanitize_text_field((string) ($settings['bg_overlay_color'] ?? 'rgba(0, 0, 0, 0.3)')),
'page_bg_color' => sanitize_hex_color((string) ($settings['page_bg_color'] ?? '')) ?: '',
'page_bg_image' => esc_url_raw((string) ($settings['page_bg_image'] ?? '')),
];
}
}
+200
View File
@@ -0,0 +1,200 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
trait DomainGroups {
/**
* 获取微信通知设置
*
* @return array{enabled:bool,app_id:string,app_secret:string,template_id:string}
*/
public static function get_wechat_settings(): array {
return Integration::get_wechat_settings();
}
/**
* 更新微信通知设置
*
* @param array $settings 合并后的完整微信配置
*/
public static function update_wechat_settings(array $settings): void {
Integration::update_wechat_settings($settings);
}
/**
* 获取商城设置
*
* @return array
*/
public static function get_shop_settings(): array {
return ForumShop::get_shop_settings();
}
/**
* 获取论坛设置
*
* 对标 zibll 论坛后台 12 section,提供一站式读取。
* 所有 key 与 schema_forum() 一一对应。
*/
public static function get_forum_settings(): array {
return ForumShop::get_forum_settings();
}
/**
* 获取文档模式设置
*/
public static function get_docs_mode_settings(): array {
return Integration::get_docs_mode_settings();
}
/**
* 获取全局背景设置
*/
public static function get_global_background_settings(): array {
return DisplayRuntime::get_global_background_settings();
}
/**
* 获取页脚布局模板
*/
public static function get_footer_layout_template(): string {
$footer = self::get_footer_settings();
$template = sanitize_key((string) ($footer['layout_template'] ?? 'template_1'));
// Legacy aliases map onto Zibll template_1 shell.
if (in_array($template, ['default', 'compact', 'centered', 'template_1'], true)) {
return 'template_1';
}
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 ban_settings(): array {
return UserRuntime::ban_settings();
}
/** 举报设置 */
public static function report_settings(): array {
return UserRuntime::report_settings();
}
/** 站内通知总开关 */
public static function message_enabled(): bool {
return Integration::message_enabled();
}
/** 通知基础设置(basic group) */
public static function notification_settings(): array {
return Integration::notification_settings();
}
/** 已关闭的通知推送类型列表 */
public static function notification_closed_types(): array {
return Integration::notification_closed_types();
}
/** 自动清理已读消息配置 */
public static function auto_clear_settings(): array {
return Integration::auto_clear_settings();
}
/** 私信功能配置 */
public static function private_message_settings(): array {
return Integration::private_message_settings();
}
/**
* 获取菜单项扩展 meta 字段列表
*/
public static function get_menu_item_meta_fields(): array {
return [
'icon_class',
'icon_svg',
'badge_text',
'badge_color',
'menu_card_image',
'menu_card_desc',
];
}
/**
* Capability definitions registry.
*
* @return array<string, array{label:string, roles?:string[], exclude_roles?:string[], default?:array<string,mixed>}>
*/
public static function cap_definitions(): array {
return Capabilities::cap_definitions();
}
/**
* All role columns in the capability matrix.
*/
public static function cap_role_columns(): array {
return Capabilities::cap_role_columns();
}
/**
* Read the user_caps matrix from settings.
*
* @return array<string, array<string, mixed>>
*/
public static function get_user_capabilities(): array {
return Capabilities::get_user_capabilities();
}
/**
* Resolve whether a user has a specific capability.
*/
public static function user_can(string $cap, int $user_id = 0, ?int $context = null): bool {
return Capabilities::user_can($cap, $user_id, $context);
}
/**
* 获取"网站安全"设置
*/
public static function get_security_settings(): array {
return Capabilities::get_security_settings();
}
/**
* 获取"系统工具"设置
*/
public static function get_system_tools_settings(): array {
return Capabilities::get_system_tools_settings();
}
}
+215
View File
@@ -0,0 +1,215 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class ExtensionRuntime {
/** 获取审核配置 */
public static function get_moderation(): array {
return get_option('aksrt_moderation_config', [
'enabled' => false,
'akismet_enabled' => false,
'akismet_site_url' => '',
'akismet_blog_lang' => 'zh-CN',
'akismet_api_key' => '',
'ai_enabled' => false,
'ai_provider' => 'baidu',
'baidu_api_key' => '',
'baidu_secret_key' => '',
'baidu_token_url' => 'https://aip.baidubce.com/oauth/2.0/token',
'baidu_censor_url' => 'https://aip.baidubce.com/rest/2.0/solution/v1/text_censor/v2/user_defined',
'baidu_img_censor_url' => 'https://aip.baidubce.com/rest/2.0/solution/v1/img_censor/v2/user_defined',
'ai_moderate_comments' => true,
'ai_moderate_background' => true,
'auto_approve_low_risk' => true,
'auto_reject_high_risk' => true,
'low_risk_max_score' => 35,
'high_risk_min_score' => 80,
'blocked_keywords' => '',
'rate_limit_enabled' => false,
'rate_limit_min_interval' => 8,
'rate_limit_max_per_hour' => 15,
'geoip_enabled' => false,
'geoip_provider' => 'ipapi',
'geoip_api_key' => '',
'geoip_block_countries' => [],
]);
}
/**
* 获取 IP 归属地设置
*/
public static function get_ip_geo_settings(): array {
$settings = Settings::get_all();
$provider = sanitize_key((string) ($settings['ip_addr_sdk'] ?? ($settings['ip_geo_provider'] ?? 'null')));
$allowed = ['polling', 'qq', 'amap', 'null'];
if (!in_array($provider, $allowed, true)) {
$provider = 'null';
}
$display_raw = $settings['ip_geo_display'] ?? ['comment'];
$display = is_array($display_raw) ? $display_raw : [$display_raw];
$format = sanitize_key((string) ($settings['ip_geo_format'] ?? 'province_city'));
if (!in_array($format, ['province_city', 'province', 'country_province'], true)) {
$format = 'province_city';
}
return [
'ip_geo_enabled' => !empty($settings['ip_geo_enabled']),
'ip_addr_sdk' => $provider,
'ip_geo_provider' => $provider,
'ip_geo_cache_ttl' => max(3600, (int) ($settings['ip_geo_cache_ttl'] ?? 86400)),
'ip_geo_display' => $display,
'ip_geo_format' => $format,
'ip_geo_fallback_text' => (string) ($settings['ip_geo_fallback_text'] ?? ''),
'ip_addr_sdk_qq_appkey' => (string) ($settings['ip_addr_sdk_qq_appkey'] ?? ''),
'ip_addr_sdk_qq_secretkey' => (string) ($settings['ip_addr_sdk_qq_secretkey'] ?? ''),
'ip_addr_sdk_amap_appkey' => (string) ($settings['ip_addr_sdk_amap_appkey'] ?? ''),
'ip_addr_sdk_amap_secretkey' => (string) ($settings['ip_addr_sdk_amap_secretkey'] ?? ''),
'ip_geo_verified' => !empty($settings['ip_geo_verified']),
];
}
/**
* 获取内容审核设置
*/
public static function get_content_audit_settings(): array {
$settings = Settings::get_all();
$provider = sanitize_key((string) ($settings['content_audit_provider'] ?? 'baidu'));
$allowed = ['baidu', 'null'];
if (!in_array($provider, $allowed, true)) {
$provider = 'baidu';
}
$on_failure = sanitize_key((string) ($settings['content_audit_on_failure'] ?? 'allow'));
if (!in_array($on_failure, ['allow', 'block'], true)) {
$on_failure = 'allow';
}
return [
'content_audit_enabled' => !empty($settings['content_audit_enabled']),
'content_audit_provider' => $provider,
'content_audit_api_key' => (string) ($settings['content_audit_api_key'] ?? ''),
'content_audit_api_secret' => (string) ($settings['content_audit_api_secret'] ?? ''),
'content_audit_text_enabled' => !isset($settings['content_audit_text_enabled']) || !empty($settings['content_audit_text_enabled']),
'content_audit_image_enabled' => !empty($settings['content_audit_image_enabled']),
'content_audit_on_failure' => $on_failure,
'content_audit_log_enabled' => !isset($settings['content_audit_log_enabled']) || !empty($settings['content_audit_log_enabled']),
'content_audit_baidu_token_url' => (string) ($settings['content_audit_baidu_token_url'] ?? ''),
'content_audit_baidu_text_url' => (string) ($settings['content_audit_baidu_text_url'] ?? ''),
'content_audit_baidu_img_url' => (string) ($settings['content_audit_baidu_img_url'] ?? ''),
'content_audit_verified' => !empty($settings['content_audit_verified']),
];
}
/**
* 获取搜索引擎推送设置
*/
public static function get_search_push_settings(): array {
$settings = Settings::get_all();
$enabled = !empty($settings['baidu_push_enabled']) || !empty($settings['seo_baidu_push']);
$token = (string) ($settings['baidu_push_token'] ?? '');
if ($token === '' && !empty($settings['seo_baidu_token'])) {
$token = (string) $settings['seo_baidu_token'];
}
return [
'baidu_push_enabled' => $enabled,
'baidu_push_token' => $token,
'baidu_push_auto' => !isset($settings['baidu_push_auto']) || !empty($settings['baidu_push_auto']),
'baidu_push_on_publish' => !isset($settings['baidu_push_on_publish']) || !empty($settings['baidu_push_on_publish']),
'baidu_push_on_update' => !empty($settings['baidu_push_on_update']),
'baidu_push_batch_size' => max(1, min(500, (int) ($settings['baidu_push_batch_size'] ?? 50))),
'baidu_push_log_enabled' => !isset($settings['baidu_push_log_enabled']) || !empty($settings['baidu_push_log_enabled']),
'baidu_push_verified' => !empty($settings['baidu_push_verified']),
'xzh_on' => !empty($settings['xzh_on']),
'xzh_appid' => (string) ($settings['xzh_appid'] ?? ''),
'xzh_render_tail' => !isset($settings['xzh_render_tail']) || !empty($settings['xzh_render_tail']),
'xzh_jsonld_single' => !isset($settings['xzh_jsonld_single']) || !empty($settings['xzh_jsonld_single']),
'xzh_jsonld_page' => !empty($settings['xzh_jsonld_page']),
'xzh_jsonld_img' => !empty($settings['xzh_jsonld_img']),
'seo_baidu_normal' => !empty($settings['seo_baidu_normal']),
'seo_baidu_fast' => !empty($settings['seo_baidu_fast']),
];
}
/**
* 获取短信接口设置
*
* 注意:所有供应商共用 sms_access_key / sms_access_secret(通用凭证字段)。
* 各供应商特有的额外字段使用各自的命名空间(sms_tencent_*, sms_huawei_*, sms_custom_*)。
* sms_sign_name / sms_template_code 为所有供应商共用。
*/
public static function get_sms_settings(): array {
$settings = Settings::get_all();
$provider = sanitize_key((string) ($settings['sms_provider'] ?? 'aliyun'));
$allowed_providers = ['aliyun', 'tencent', 'huawei', 'custom'];
if (!in_array($provider, $allowed_providers, true)) {
$provider = 'aliyun';
}
$ali_api_type = sanitize_key((string) ($settings['sms_ali_api_type'] ?? 'sms'));
if (!in_array($ali_api_type, ['sms', 'verify'], true)) {
$ali_api_type = 'sms';
}
$code_length = max(4, min(8, (int) ($settings['sms_code_length'] ?? 6)));
$code_expire = max(60, min(600, (int) ($settings['sms_code_expire'] ?? 300)));
$rate_limit = max(10, (int) ($settings['sms_rate_limit'] ?? 60));
$daily_limit = max(1, (int) ($settings['sms_daily_limit'] ?? 10));
return [
'sms_enabled' => !empty($settings['sms_enabled']),
'sms_provider' => $provider,
'sms_number_limit_ch' => !isset($settings['sms_number_limit_ch']) || !empty($settings['sms_number_limit_ch']),
'sms_access_key' => (string) ($settings['sms_access_key'] ?? ''),
'sms_access_secret' => (string) ($settings['sms_access_secret'] ?? ''),
'sms_sign_name' => (string) ($settings['sms_sign_name'] ?? ''),
'sms_template_code' => (string) ($settings['sms_template_code'] ?? ''),
'sms_code_length' => $code_length,
'sms_code_expire' => $code_expire,
'sms_rate_limit' => $rate_limit,
'sms_daily_limit' => $daily_limit,
'sms_notify_bind' => !isset($settings['sms_notify_bind']) || !empty($settings['sms_notify_bind']),
'sms_notify_login' => !empty($settings['sms_notify_login']),
'sms_notify_register' => !isset($settings['sms_notify_register']) || !empty($settings['sms_notify_register']),
'sms_notify_reset_password' => !isset($settings['sms_notify_reset_password']) || !empty($settings['sms_notify_reset_password']),
// 阿里云特有
'sms_ali_api_type' => $ali_api_type,
'sms_region_id' => (string) ($settings['sms_region_id'] ?? 'cn-hangzhou'),
'sms_aliyun_verified' => !empty($settings['sms_aliyun_verified']),
// 腾讯云特有
'sms_tencent_app_id' => (string) ($settings['sms_tencent_app_id'] ?? ''),
'sms_tencent_app_key' => (string) ($settings['sms_tencent_app_key'] ?? ''),
'sms_tencent_secret_id' => (string) ($settings['sms_tencent_secret_id'] ?? ''),
'sms_tencent_secret_key' => (string) ($settings['sms_tencent_secret_key'] ?? ''),
'sms_tencent_region' => (string) ($settings['sms_tencent_region'] ?? 'ap-guangzhou'),
'sms_tencent_verified' => !empty($settings['sms_tencent_verified']),
// 华为云特有
'sms_huawei_sender' => (string) ($settings['sms_huawei_sender'] ?? ''),
'sms_huawei_signature' => (string) ($settings['sms_huawei_signature'] ?? ''),
'sms_huawei_endpoint' => (string) ($settings['sms_huawei_endpoint'] ?? ''),
'sms_huawei_verified' => !empty($settings['sms_huawei_verified']),
// 自定义接口特有
'sms_custom_url' => (string) ($settings['sms_custom_url'] ?? ''),
'sms_custom_method' => (string) ($settings['sms_custom_method'] ?? 'POST'),
'sms_custom_auth_type' => (string) ($settings['sms_custom_auth_type'] ?? 'header'),
'sms_custom_auth_key' => (string) ($settings['sms_custom_auth_key'] ?? ''),
'sms_custom_auth_value' => (string) ($settings['sms_custom_auth_value'] ?? ''),
'sms_custom_headers' => (string) ($settings['sms_custom_headers'] ?? ''),
'sms_custom_body' => (string) ($settings['sms_custom_body'] ?? ''),
'sms_custom_success_rule' => (string) ($settings['sms_custom_success_rule'] ?? ''),
'sms_custom_verified' => !empty($settings['sms_custom_verified']),
];
}
}
+559
View File
@@ -0,0 +1,559 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
trait FacadeBulk {
/**
* Header navigation display settings.
*
* @return array{fixed:bool,search_enabled:bool,user_menu:bool,publish_btn:bool,mobile_drawer:bool,nav_style:string,max_width:int,scroll_down_hide:bool,layout:string,mobile_layout:string,mobile_align:string}
*/
public static function get_header_nav_settings(): array {
$settings = self::get_all();
$nav_style = sanitize_key((string) ($settings['header_nav_style'] ?? 'default'));
$max_width = (int) ($settings['header_max_width'] ?? 1200);
$layout = sanitize_key((string) ($settings['header_layout'] ?? '2'));
$ml = sanitize_key((string) ($settings['mobile_header_layout'] ?? 'center'));
$ma = sanitize_key((string) ($settings['mobile_navbar_align'] ?? 'left'));
if (!in_array($nav_style, ['default', 'transparent', 'blur'], true)) {
$nav_style = 'default';
}
if (!in_array($max_width, [0, 1080, 1200, 1320], true)) {
$max_width = 1200;
}
return [
'fixed' => !isset($settings['header_fixed']) || !empty($settings['header_fixed']),
'search_enabled' => !isset($settings['header_search_enabled']) || !empty($settings['header_search_enabled']),
'user_menu' => !isset($settings['header_user_menu']) || !empty($settings['header_user_menu']),
'publish_btn' => !isset($settings['header_publish_btn']) || !empty($settings['header_publish_btn']),
'mobile_drawer' => !isset($settings['header_mobile_drawer']) || !empty($settings['header_mobile_drawer']),
'nav_style' => $nav_style,
'max_width' => $max_width,
'scroll_down_hide'=> !empty($settings['header_scroll_down_hide']),
'layout' => in_array($layout, ['1', '2', '3'], true) ? $layout : '2',
'mobile_layout' => in_array($ml, ['center', 'left'], true) ? $ml : 'center',
'mobile_align' => in_array($ma, ['top', 'left', 'right'], true) ? $ma : 'left',
];
}
/**
* Header publish button configuration.
*
* @return array{enabled:bool,text:string,radius:int,class:string}
*/
public static function get_header_newposts_config(): array {
$s = self::get_all();
return [
'enabled' => !empty($s['header_newposts_enabled']),
'text' => trim((string) ($s['header_newposts_text'] ?? '<i class="fa fa-pencil"></i> 发布')),
'radius' => max(0, min(30, (int) ($s['header_newposts_radius'] ?? 20))),
'class' => sanitize_html_class((string) ($s['header_newposts_class'] ?? '')),
];
}
/**
* Header theme color configuration.
*
* @return array{custom:bool,bg_custom:string,bg:string,color_custom:string}
*/
public static function get_header_theme_config(): array {
$s = self::get_all();
return [
'custom' => !empty($s['header_theme_custom']),
'bg_custom' => sanitize_hex_color((string) ($s['header_theme_bg_custom'] ?? '')) ?: '',
'bg' => sanitize_hex_color((string) ($s['header_theme_bg'] ?? '')) ?: '',
'color_custom' => sanitize_hex_color((string) ($s['header_theme_color_custom'] ?? '')) ?: '',
];
}
/**
* Category archive display settings.
*
* @return array
*/
public static function get_category_page_settings(): array {
$settings = self::get_all();
$header_style = sanitize_key((string) ($settings['category_header_style'] ?? 'cover'));
$list_mode = sanitize_key((string) ($settings['category_list_mode'] ?? 'grid'));
$per_page = (int) ($settings['category_per_page'] ?? 12);
$orderby = sanitize_key((string) ($settings['category_orderby'] ?? 'modified'));
if (!in_array($header_style, ['cover', 'simple', 'none'], true)) {
$header_style = 'cover';
}
if (!in_array($list_mode, ['grid', 'list', 'compact'], true)) {
$list_mode = 'grid';
}
if (!in_array($orderby, ['modified', 'date', 'views', 'comments', 'likes'], true)) {
$orderby = 'modified';
}
return [
'header_style' => $header_style,
'cover_enabled' => !isset($settings['category_cover_enabled']) || !empty($settings['category_cover_enabled']),
'desc_enabled' => !isset($settings['category_desc_enabled']) || !empty($settings['category_desc_enabled']),
'default_cover' => esc_url_raw((string) ($settings['category_default_cover'] ?? '')),
'list_mode' => $list_mode,
'filter_bar' => !isset($settings['category_filter_bar']) || !empty($settings['category_filter_bar']),
'sidebar' => !isset($settings['category_sidebar']) || !empty($settings['category_sidebar']),
'per_page' => max(4, min(48, $per_page)),
'post_count' => !empty($settings['category_post_count']),
'orderby' => $orderby,
];
}
/**
* Tag archive display settings.
*
* @return array
*/
public static function get_tag_page_settings(): array {
$settings = self::get_all();
$header_style = sanitize_key((string) ($settings['tag_header_style'] ?? 'simple'));
$list_mode = sanitize_key((string) ($settings['tag_list_mode'] ?? 'grid'));
$per_page = (int) ($settings['tag_per_page'] ?? 12);
$orderby = sanitize_key((string) ($settings['tag_orderby'] ?? 'count'));
if (!in_array($header_style, ['cover', 'simple', 'none'], true)) {
$header_style = 'simple';
}
if (!in_array($list_mode, ['grid', 'list'], true)) {
$list_mode = 'grid';
}
if (!in_array($orderby, ['count', 'name', 'modified'], true)) {
$orderby = 'count';
}
return [
'header_style' => $header_style,
'default_cover' => esc_url_raw((string) ($settings['tag_default_cover'] ?? '')),
'list_mode' => $list_mode,
'sidebar' => !isset($settings['tag_sidebar']) || !empty($settings['tag_sidebar']),
'per_page' => max(4, min(48, $per_page)),
'post_count' => !empty($settings['tag_post_count']),
'orderby' => $orderby,
];
}
/**
* Misc page display settings (404, search, author).
*
* @return array
*/
public static function get_other_page_settings(): array {
$settings = self::get_all();
$message = trim((string) ($settings['page_404_message'] ?? ''));
$search_per_page = max(4, min(48, (int) ($settings['page_search_per_page'] ?? 12)));
$author_per_page = max(4, min(48, (int) ($settings['page_author_posts_per_page'] ?? 12)));
$header_style = sanitize_key((string) ($settings['page_header_style'] ?? '1'));
if (!in_array($header_style, ['', '1', '2', '3'], true)) {
$header_style = '1';
}
return [
'header_style' => $header_style,
'header_cover_img' => esc_url_raw((string) ($settings['page_header_cover_img'] ?? '')),
'page_404_message' => $message !== '' ? $message : __('页面未找到', 'aksrt'),
'page_404_search' => !isset($settings['page_404_search']) || !empty($settings['page_404_search']),
'page_404_popular' => !isset($settings['page_404_popular']) || !empty($settings['page_404_popular']),
'page_404_back' => !isset($settings['page_404_back']) || !empty($settings['page_404_back']),
'page_search_sidebar' => !isset($settings['page_search_sidebar']) || !empty($settings['page_search_sidebar']),
'page_search_per_page' => $search_per_page,
'page_author_cover' => !isset($settings['page_author_cover']) || !empty($settings['page_author_cover']),
'page_author_sidebar' => !isset($settings['page_author_sidebar']) || !empty($settings['page_author_sidebar']),
'page_author_posts_per_page' => $author_per_page,
];
}
/**
* Special/topic page display settings.
*
* @return array
*/
public static function get_special_page_settings(): array {
$s = self::get_all();
$layout = sanitize_key((string) ($s['special_layout'] ?? 'grid'));
$source_type = sanitize_key((string) ($s['special_source_type'] ?? 'category'));
$subpage_display = sanitize_key((string) ($s['special_subpage_display'] ?? 'cards'));
$pagination = sanitize_key((string) ($s['special_pagination'] ?? 'numeric'));
$orderby = sanitize_key((string) ($s['special_orderby'] ?? 'date'));
if (!in_array($layout, ['grid', 'list', 'masonry'], true)) {
$layout = 'grid';
}
if (!in_array($source_type, ['category', 'tag', 'custom'], true)) {
$source_type = 'category';
}
if (!in_array($subpage_display, ['cards', 'list', 'grid'], true)) {
$subpage_display = 'cards';
}
if (!in_array($pagination, ['numeric', 'load_more', 'infinite'], true)) {
$pagination = 'numeric';
}
if (!in_array($orderby, ['date', 'modified', 'views', 'comments'], true)) {
$orderby = 'date';
}
return [
'cover_enabled' => !empty($s['special_cover_enabled']),
'summary_enabled' => !empty($s['special_summary_enabled']),
'summary_length' => max(50, min(500, (int) ($s['special_summary_length'] ?? 200))),
'default_cover' => esc_url_raw((string) ($s['special_default_cover'] ?? '')),
'layout' => $layout,
'columns' => max(1, min(4, (int) ($s['special_columns'] ?? 2))),
'source_type' => $source_type,
'source_ids' => trim((string) ($s['special_source_ids'] ?? '')),
'subpage_display' => $subpage_display,
'subpage_count' => max(1, min(20, (int) ($s['special_subpage_count'] ?? 6))),
'sidebar' => !empty($s['special_sidebar']),
'pagination' => $pagination,
'per_page' => max(4, min(48, (int) ($s['special_per_page'] ?? 12))),
'post_count' => !empty($s['special_post_count']),
'orderby' => $orderby,
];
}
/**
* Share and poster settings.
*
* @return array{wechat_enabled:bool,wechat_appid:string,wechat_app_secret:string,only_logo:bool,compatible_s:bool,poster_img:string,poster_logo:string,share_img:string,share_desc:string}
*/
public static function get_share_settings(): array {
$settings = self::get_all();
return [
'wechat_enabled' => !empty($settings['share_wechat_s']),
'wechat_appid' => sanitize_text_field((string) ($settings['share_wechat_appid'] ?? '')),
'wechat_app_secret' => sanitize_text_field((string) ($settings['share_wechat_app_secret'] ?? '')),
'only_logo' => !empty($settings['share_only_logo']),
'compatible_s' => !empty($settings['share_img_compatible_s']),
'poster_img' => esc_url_raw((string) ($settings['share_img_byimg'] ?? '')),
'poster_logo' => esc_url_raw((string) ($settings['share_logo'] ?? '')),
'share_img' => esc_url_raw((string) ($settings['share_img'] ?? '')),
'share_desc' => sanitize_textarea_field((string) ($settings['share_desc'] ?? '')) ?: __('扫一扫 立即查看', 'aksrt'),
];
}
/**
* Single post/page display settings.
*
* @return array{toc_enabled:bool,toc_depth:int,reading_progress:bool,lightbox:bool,related_enabled:bool,related_count:int,like_enabled:bool,favorite_enabled:bool,share_enabled:bool,copyright_enabled:bool,print_enabled:bool,pdf_export_enabled:bool,word_count_enabled:bool,reading_time_enabled:bool}
*/
public static function get_post_page_settings(): array {
// Canonical assembly lives in the post-display helper (related style/source split, aliases).
if (class_exists('PostDisplay')) {
return PostDisplay::get_post_page_settings();
}
$settings = self::get_all();
return [
'toc_enabled' => !isset($settings['post_toc_enabled']) || !empty($settings['post_toc_enabled']),
'toc_depth' => max(1, min(6, (int) ($settings['post_toc_depth'] ?? 3))),
'reading_progress' => !isset($settings['post_reading_progress']) || !empty($settings['post_reading_progress']),
'breadcrumbs' => !isset($settings['breadcrumbs_single_s']) || !empty($settings['breadcrumbs_single_s']),
'breadcrumbs_home' => trim((string) ($settings['breadcrumbs_home_text'] ?? '')) ?: __('首页', 'aksrt'),
'related_enabled' => !isset($settings['post_related_enabled']) || !empty($settings['post_related_enabled']),
'related_count' => max(1, min(20, (int) ($settings['post_related_count'] ?? 6))),
'related_style' => 'img',
'related_source' => 'category',
'related_type' => 'category',
'related_orderby' => 'views',
'related_title' => sanitize_text_field((string) ($settings['post_related_title'] ?? '相关推荐')),
];
}
/**
* 获取懒加载配置
*
* 返回 schema 定义的全部字段:全局开关、动画、占位图、模糊预览、
* 以及 8 项粒度范围开关。
*
* @return array {enabled, animation, placeholder_url, blur, avatar, content, comment, private, slider, cover, other, widget}
*/
public static function get_lazyload_config(): array {
$s = self::get_all();
// placeholder 兼容:schema 存 lazyload_placeholder,REST/Vue 存 lazyload_placeholder_url
$placeholder = '';
if (!empty($s['lazyload_placeholder'])) {
$placeholder = $s['lazyload_placeholder'];
} elseif (!empty($s['lazyload_placeholder_url'])) {
$placeholder = $s['lazyload_placeholder_url'];
}
return [
'enabled' => !isset($s['lazyload_enabled']) || !empty($s['lazyload_enabled']),
'animation' => $s['lazyload_animation'] ?? 'blur',
'placeholder_url' => (string) $placeholder,
'blur' => !isset($s['lazyload_blur']) || !empty($s['lazyload_blur']),
'avatar' => !isset($s['lazyload_avatar']) || !empty($s['lazyload_avatar']),
'content' => !empty($s['lazyload_content']),
'comment' => !isset($s['lazyload_comment']) || !empty($s['lazyload_comment']),
'private' => !isset($s['lazyload_private']) || !empty($s['lazyload_private']),
'slider' => !isset($s['lazyload_slider']) || !empty($s['lazyload_slider']),
'cover' => !isset($s['lazyload_cover']) || !empty($s['lazyload_cover']),
'other' => !isset($s['lazyload_other']) || !empty($s['lazyload_other']),
'widget' => !isset($s['lazyload_widget']) || !empty($s['lazyload_widget']),
];
}
/**
* 获取备案配置(统一 page-display footer schema 和全局设置)
* @return array {icp_enabled, icp_number, police_enabled, police_number}
*/
public static function get_filing_settings(): array {
$s = self::get_all();
$icp_enabled = !isset($s['footer_icp']) || !empty($s['footer_icp']);
$police_enabled = !isset($s['footer_police']) || !empty($s['footer_police']);
// 优先读 footer schema 的 icp_number,回退到全局 icp_filing
$icp_number = trim((string) ($s['icp_number'] ?? $s['icp_filing'] ?? ''));
$police_number = trim((string) ($s['police_number'] ?? ''));
return [
'icp_enabled' => $icp_enabled,
'icp_number' => $icp_number,
'police_enabled' => $police_enabled,
'police_number' => $police_number,
];
}
/** 获取页脚附加文字 */
public static function footer_text(): string {
return trim((string) self::get_value('footer_text', ''));
}
/** 获取 ICP 备案号 */
public static function icp_filing(): string {
return trim((string) self::get_value('icp_filing', ''));
}
/** 获取公安备案号 */
public static function police_filing(): string {
return trim((string) self::get_value('police_number', ''));
}
/** 获取导航 Logo 宽度 */
public static function logo_width(): int {
$width = (int) self::get_value('logo_width', 150);
return max(50, min(400, $width));
}
/** 获取Favicon URL */
public static function custom_favicon_url(): string {
$favicon = (string) self::get_value('site_favicon', '');
return $favicon !== '' ? $favicon : '';
}
public static function favicon_url(): string {
$custom = self::custom_favicon_url();
if ($custom !== '') {
return $custom;
}
$site_icon = get_site_icon_url(512);
return $site_icon ? (string) $site_icon : self::default_asset_url('default-favicon.svg');
}
/** 获取About显示名 */
/**
* 让 SEO 设置中的 Favicon 覆盖 WordPress 原生 site_icon 输出链路。
*
* @param string $url
* @param int $size
* @param int $blog_id
* @return string
*/
public static function filter_site_icon_url($url, $size, $blog_id): string {
$custom = self::custom_favicon_url();
return $custom !== '' ? $custom : (string) $url;
}
/**
* 尝试把自定义 favicon 同步到 WordPress 原生 site_icon 选项。
*
* 仅当 URL 能映射到媒体库附件时才同步;外链仍通过 get_site_icon_url 过滤覆盖。
*/
public static function sync_native_site_icon(string $favicon_url): void {
$favicon_url = trim(esc_url_raw($favicon_url));
if ($favicon_url === '') {
return;
}
$attachment_id = attachment_url_to_postid($favicon_url);
if ($attachment_id > 0 && (int) get_option('site_icon', 0) !== $attachment_id) {
update_option('site_icon', $attachment_id);
}
}
/** 启动时对齐原生 site_icon,同步自定义 site_favicon 到原生 site_icon。 */
public static function maybe_sync_native_site_icon(): void {
$custom = self::custom_favicon_url();
if ($custom === '') {
return;
}
self::sync_native_site_icon($custom);
}
public static function about_display_name(): string {
$s = self::get_all();
return !empty($s['about_display_name']) ? $s['about_display_name'] : get_bloginfo('name');
}
/** 获取About简介 */
public static function about_bio(): string {
$s = self::get_all();
return !empty($s['about_bio']) ? $s['about_bio'] : get_bloginfo('description');
}
/** 获取About头像URL */
public static function about_avatar(): string {
$s = self::get_all();
return $s['about_avatar'] ?? ($s['admin_avatar_url'] ?? '');
}
/** 获取头像镜像源 */
public static function avatar_source(): string {
$source = sanitize_text_field((string) self::get_value('avatar_source', 'cn.cravatar.com'));
$allowed = ['cn.cravatar.com', 'cn.gravatar.com', 'gravatar.com'];
return in_array($source, $allowed, true) ? $source : 'cn.cravatar.com';
}
/** 获取SEO关键词 */
public static function seo_keywords(): string {
$s = self::get_all();
return $s['seo_keywords'] ?? '';
}
/** 获取自定义头部代码 */
public static function custom_head_code(): string {
$code = (string) self::get_value('head_code', '');
if ($code === '') {
$code = (string) self::get_value('custom_js_header', '');
}
return $code;
}
/** 获取自定义 CSS */
public static function custom_css(): string {
return (string) self::get_value('custom_css', '');
}
/** 获取自定义底部代码 */
public static function custom_footer_code(): string {
$code = (string) self::get_value('foot_code', '');
if ($code === '') {
$code = (string) self::get_value('custom_js_footer', '');
}
return $code;
}
/** 获取统计代码 */
public static function trackcode(): string {
return trim((string) self::get_value('trackcode', ''));
}
/** 获取SMTP配置 */
public static function get_smtp(): array {
return get_option('aksrt_mail_smtp', [
'enabled' => false,
'host' => '',
'port' => 465,
'username' => '',
'password' => '',
'encryption' => 'ssl',
'from_name' => '',
'from_address' => '',
]);
}
/** 获取邮件品牌设置(发件人昵称/标题前缀/底部内容) */
public static function get_mail_branding(): array {
$s = self::get_all();
return [
'showname' => trim((string) ($s['mail_showname'] ?? '')),
'title_prefix' => trim((string) ($s['mail_title_prefix'] ?? '')),
'description' => trim((string) ($s['mail_description'] ?? '')),
'more_content' => trim((string) ($s['mail_more_content'] ?? '')),
];
}
}
+347
View File
@@ -0,0 +1,347 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class ForumShop {
/**
* 获取商城设置
*
* @return array
*/
public static function get_shop_settings(): array {
$s = Settings::get_all();
// -- basic / general --
$enabled = !empty($s['shop_s']);
$home_url = esc_url_raw((string) ($s['shop_home_url'] ?? ''));
// breadcrumbs
$breadcrumbs_s = !isset($s['shop_breadcrumbs_s']) || !empty($s['shop_breadcrumbs_s']);
$breadcrumbs_home = !isset($s['shop_breadcrumbs_home']) || !empty($s['shop_breadcrumbs_home']);
$breadcrumbs_shop_home = !isset($s['shop_breadcrumbs_shop_home']) || !empty($s['shop_breadcrumbs_shop_home']);
$breadcrumbs_shop_home_name = sanitize_text_field((string) ($s['shop_breadcrumbs_shop_home_name'] ?? '商城首页'));
// rewrite
$product_rewrite_slug = sanitize_key((string) ($s['shop_product_rewrite_slug'] ?? 'product'));
$cart_rewrite_slug = sanitize_key((string) ($s['shop_cart_rewrite_slug'] ?? 'cart'));
$rewrite_suffix_html = !empty($s['shop_rewrite_suffix_html_s']);
// -- list --
$list_orderby = sanitize_key((string) ($s['shop_list_orderby'] ?? 'date'));
if (!in_array($list_orderby, ['date', 'sales', 'price_asc', 'price_desc', 'comments'], true)) {
$list_orderby = 'date';
}
$list_count = max(4, min(100, (int) ($s['shop_list_count'] ?? 12)));
$list_paging = sanitize_key((string) ($s['shop_list_paging'] ?? 'number'));
if (!in_array($list_paging, ['number', 'ajax'], true)) {
$list_paging = 'number';
}
$list_ias_s = !empty($s['shop_list_ias_s']);
$list_ias_max = max(1, min(50, (int) ($s['shop_list_ias_max'] ?? 5)));
$list_style = sanitize_key((string) ($s['shop_list_style'] ?? 'default'));
if (!in_array($list_style, ['default', 'card-lg', 'card-sm'], true)) {
$list_style = 'default';
}
$sales_show = sanitize_key((string) ($s['shop_sales_show'] ?? 'show'));
if (!in_array($sales_show, ['show', 'hide', 'threshold'], true)) {
$sales_show = 'show';
}
$sales_show_min = max(0, min(99999, (int) ($s['shop_sales_show_min'] ?? 10)));
// -- contact --
$contact_msg_s = !isset($s['shop_contact_msg_s']) || !empty($s['shop_contact_msg_s']);
$contact_name = sanitize_text_field((string) ($s['shop_contact_name'] ?? '联系客服'));
$contact_note = sanitize_textarea_field((string) ($s['shop_contact_note'] ?? ''));
$contact_more = sanitize_textarea_field((string) ($s['shop_contact_more'] ?? ''));
// -- single --
$content_layout = sanitize_key((string) ($s['shop_content_layout'] ?? 'adapt'));
if (!in_array($content_layout, ['adapt', 'full', 'sidebar'], true)) {
$content_layout = 'adapt';
}
$content_show_bg = !empty($s['shop_content_show_bg']);
$single_tab = sanitize_textarea_field((string) ($s['shop_single_tab'] ?? ''));
$content_after = wp_kses_post((string) ($s['shop_content_after'] ?? ''));
$related_s = !isset($s['shop_single_related_s']) || !empty($s['shop_single_related_s']);
$related_opt = sanitize_textarea_field((string) ($s['shop_single_related_opt'] ?? ''));
$main_image_default = esc_url_raw((string) ($s['shop_main_image_default'] ?? ''));
// -- product defaults --
$limit_buy = max(-1, min(9999, (int) ($s['shop_limit_buy'] ?? -1)));
$guest_buy = !empty($s['shop_guest_buy']);
$email_fill = !empty($s['shop_email_fill']);
$rebate = !isset($s['shop_rebate']) || !empty($s['shop_rebate']);
$shipping_fee = sanitize_key((string) ($s['shop_shipping_fee'] ?? 'free'));
if (!in_array($shipping_fee, ['free', 'fixed', 'amount'], true)) {
$shipping_fee = 'free';
}
$after_sale = sanitize_key((string) ($s['shop_after_sale'] ?? 'refund_return'));
if (!in_array($after_sale, ['refund', 'refund_return', 'none'], true)) {
$after_sale = 'refund_return';
}
$service = sanitize_textarea_field((string) ($s['shop_service'] ?? ''));
$params_default = sanitize_textarea_field((string) ($s['shop_product_params_default'] ?? ''));
// -- shipping config --
$manual_address = !empty($s['shop_manual_address']);
$express_api_sdk = sanitize_key((string) ($s['shop_express_api_sdk'] ?? 'kuaidi100'));
if (!in_array($express_api_sdk, ['kuaidi100', 'aliyun', 'kdniao'], true)) {
$express_api_sdk = 'kuaidi100';
}
$express_query_interval = max(30, min(240, (int) ($s['shop_express_query_interval'] ?? 60)));
$express_kdniao_opt = sanitize_textarea_field((string) ($s['shop_express_kdniao_opt'] ?? ''));
$express_kuaidi100_opt = sanitize_textarea_field((string) ($s['shop_express_kuaidi100_opt'] ?? ''));
$express_aliyun_opt = sanitize_textarea_field((string) ($s['shop_express_aliyun_opt'] ?? ''));
$shipping_company = sanitize_textarea_field((string) ($s['shop_shipping_company'] ?? ''));
// -- timing --
$receipt_max_day = max(1, min(60, (int) ($s['shop_order_receipt_max_day'] ?? 15)));
$comment_max_day = max(1, min(60, (int) ($s['shop_comment_max_day'] ?? 7)));
$comment_placeholder = sanitize_text_field((string) ($s['shop_comment_placeholder'] ?? ''));
$comment_img_num = max(0, min(9, (int) ($s['shop_comment_img_num'] ?? 3)));
$after_sale_return_max_day = max(1, min(30, (int) ($s['shop_after_sale_return_express_max_day'] ?? 7)));
$after_sale_reason_refund = sanitize_textarea_field((string) ($s['shop_after_sale_reason_refund'] ?? ''));
$after_sale_reason_return = sanitize_textarea_field((string) ($s['shop_after_sale_reason_return'] ?? ''));
$author_show = !isset($s['shop_author_show']) || !empty($s['shop_author_show']);
return [
'enabled' => $enabled,
'home_url' => $home_url,
'breadcrumbs' => [
'enabled' => $breadcrumbs_s,
'home' => $breadcrumbs_home,
'shop_home' => $breadcrumbs_shop_home,
'shop_home_name' => $breadcrumbs_shop_home_name,
],
'rewrite' => [
'product_slug' => $product_rewrite_slug,
'cart_slug' => $cart_rewrite_slug,
'suffix_html' => $rewrite_suffix_html,
],
'list' => [
'orderby' => $list_orderby,
'count' => $list_count,
'paging' => $list_paging,
'ias_s' => $list_ias_s,
'ias_max' => $list_ias_max,
'style' => $list_style,
],
'sales' => [
'show' => $sales_show,
'show_min' => $sales_show_min,
],
'contact' => [
'msg_s' => $contact_msg_s,
'name' => $contact_name,
'note' => $contact_note,
'more' => $contact_more,
],
'single' => [
'content_layout' => $content_layout,
'content_show_bg' => $content_show_bg,
'tab' => $single_tab,
'content_after' => $content_after,
'related_s' => $related_s,
'related_opt' => $related_opt,
'main_image_default' => $main_image_default,
],
'product_defaults' => [
'limit_buy' => $limit_buy,
'guest_buy' => $guest_buy,
'email_fill' => $email_fill,
'rebate' => $rebate,
'shipping_fee' => $shipping_fee,
'after_sale' => $after_sale,
'service' => $service,
'params_default' => $params_default,
],
'shipping' => [
'manual_address' => $manual_address,
'express_api_sdk' => $express_api_sdk,
'express_query_interval' => $express_query_interval,
'express_kdniao_opt' => $express_kdniao_opt,
'express_kuaidi100_opt' => $express_kuaidi100_opt,
'express_aliyun_opt' => $express_aliyun_opt,
'shipping_company' => $shipping_company,
],
'timing' => [
'receipt_max_day' => $receipt_max_day,
'comment_max_day' => $comment_max_day,
'comment_placeholder' => $comment_placeholder,
'comment_img_num' => $comment_img_num,
'after_sale_return_max_day' => $after_sale_return_max_day,
'after_sale_reason_refund' => $after_sale_reason_refund,
'after_sale_reason_return' => $after_sale_reason_return,
],
'author_show' => $author_show,
];
}
/**
* 获取论坛设置
*
* 对标 zibll 论坛后台 12 section,提供一站式读取。
* 所有 key 与 schema_forum() 一一对应。
*/
public static function get_forum_settings(): array {
$s = Settings::get_all();
// helper: bool with default true
$bool = static fn(string $k, bool $d = false): bool =>
isset($s[$k]) ? !empty($s[$k]) : $d;
// helper: int
$int = static fn(string $k, int $d): int =>
(int) ($s[$k] ?? $d);
// helper: string
$str = static fn(string $k, string $d): string =>
sanitize_text_field((string) ($s[$k] ?? $d));
// helper: select with allowlist
$sel = static fn(string $k, string $d, array $allow): string =>
in_array($s[$k] ?? '', $allow, true) ? $s[$k] : $d;
return [
// ── basic ──
'enabled' => $bool('bbs_s', true),
'admin_users' => array_map('absint', (array) ($s['bbs_admin_users'] ?? [])),
// ── names ──
'names' => [
'forum' => $str('bbs_forum_display_name', '论坛'),
'plate' => $str('bbs_plate_display_name', '版块'),
'posts' => $str('bbs_posts_display_name', '帖子'),
'topic' => $str('bbs_topic_display_name', '话题'),
'tag' => $str('bbs_tag_display_name', '标签'),
'comment' => $str('bbs_comment_display_name', '回复'),
'question_badge' => $str('bbs_question_badge_name', '提问'),
'question_ok_badge' => $str('bbs_question_ok_badge_name', '已解决'),
'cat_moderator' => $str('bbs_cat_moderator_name', '分区版主'),
'plate_author' => $str('bbs_plate_author_name', '超级版主'),
'plate_moderator' => $str('bbs_plate_moderator_name', '版主'),
'plate_follow' => $str('bbs_plate_follow_name', '关注'),
],
// ── homepage ──
'homepage' => [
'tabs' => (array) ($s['bbs_home_tab'] ?? [
['type' => 'follow', 'label' => '关注'],
['type' => 'synthesis', 'label' => '综合'],
['type' => 'plate', 'label' => '版块'],
]),
'tab_active_index' => $int('bbs_home_tab_active_index', 1),
'quick_posts' => $bool('bbs_home_tab_quick_posts_s', true),
'tab_swiper' => $bool('bbs_home_tab_swiper', true),
],
// ── board_page ──
'board_page' => [
'top_info' => $bool('bbs_plate_top_info_s', true),
'tabs' => (array) ($s['bbs_plate_tab'] ?? [
['label' => '全部', 'sort' => 'date'],
['label' => '推荐', 'sort' => 'featured'],
['label' => '最新回复', 'sort' => 'last_reply'],
['label' => '热门', 'sort' => 'hot'],
['label' => '精华', 'sort' => 'essence'],
]),
'tab_active_index' => $int('bbs_plate_tab_active_index', 0),
'tab_swiper' => $bool('bbs_plate_tab_swiper', true),
],
// ── post_page ──
'post_page' => [
'breadcrumbs' => $bool('bbs_breadcrumbs_s', true),
'breadcrumbs_home' => $bool('bbs_breadcrumbs_home', true),
'breadcrumbs_bbs_home' => $bool('bbs_breadcrumbs_bbs_home', true),
'breadcrumbs_bbs_home_name' => $str('bbs_breadcrumbs_bbs_home_name', '论坛'),
'breadcrumbs_plate_cat' => $bool('bbs_breadcrumbs_plate_cat', true),
'score_extra_max' => $int('bbs_score_extra_max', 5),
'hot_recommend_threshold' => $int('bbs_hot_recommend_threshold', 500),
'score_deduct_max' => $int('bbs_score_deduct_max', 3),
'maxheight_enabled' => $bool('bbs_posts_maxheight_s'),
'maxheight' => $int('bbs_posts_maxheight', 800),
'cover_ratio' => $sel('bbs_posts_cover_ratio', '16:9', ['16:9', '4:3', '1:1', '3:4']),
'cover_video_mute' => $bool('bbs_posts_cover_video_mute', true),
],
// ── reply ──
'reply' => [
'paginate_type' => $sel('bbs_reply_paginate_type', 'ajax', ['ajax', 'page']),
'paginate_ias' => $bool('bbs_reply_paginate_ias_s'),
'paginate_ias_max' => $int('bbs_reply_paginate_ias_max', 3),
'smilie' => $bool('bbs_comment_smilie', true),
'code' => $bool('bbs_comment_code', true),
'img' => $bool('bbs_comment_img', true),
'upload_img' => $bool('bbs_comment_upload_img'),
'quick_reply' => $bool('bbs_comment_quick_s', true),
'quick_phrases' => $str('bbs_comment_quick_often', "不错,学习了\n感谢分享\n期待更新"),
'placeholder' => $str('bbs_comment_placeholder', '写下你的回复…'),
'hot_threshold' => $int('bbs_comment_hot_threshold', 10),
],
// ── permissions ──
'permissions' => [
'post_add_limit' => $int('bbs_posts_add_limit_opt_max', 10),
'plate_add_limit' => $int('bbs_plate_add_limit_opt_max', 3),
'show_new_plate' => $bool('bbs_show_new_plate'),
'show_new_topic' => $bool('bbs_show_new_topic', true),
'show_apply_moderator' => $bool('bbs_show_apply_moderator', true),
'paid_content' => $bool('bbs_post_pay_hide_type_s'),
'vip_price' => $bool('bbs_post_pay_vip_price_s'),
'vip_1_discount' => $int('bbs_post_pay_vip_1_discount', 80),
'vip_2_discount' => $int('bbs_post_pay_vip_2_discount', 50),
'pay_price_limit' => $int('bbs_post_pay_price_limit', 1000),
'plate_follow_pay' => $bool('bbs_plate_follow_pay_s'),
'plate_follow_price' => $int('bbs_plate_follow_pay_price', 10),
'plate_follow_pay_type' => $sel('bbs_plate_follow_pay_type', 'money', ['money', 'points']),
],
// ── other ──
'other' => [
'vote_number_type' => $sel('bbs_vote_number_type', 'percent', ['percent', 'number']),
'vote_max' => $int('bbs_vote_max', 8),
'apply_moderator_desc' => $str('bbs_apply_moderator_desc', '欢迎有热情的用户申请成为版主,协助管理社区秩序。'),
'term_thumb' => esc_url_raw((string) ($s['bbs_term_thumb'] ?? '')),
'hide_pay_in_list' => $bool('bbs_post_lists_hide_pay_s'),
'title_strlen_limit' => $int('bbs_post_title_strlen_limit', 30),
't_placeholder' => $str('bbs_t_placeholder', '请输入标题'),
'c_placeholder' => $str('bbs_c_placeholder', '请输入内容'),
'html_suffix' => $bool('bbs_rewrite_suffix_html_s'),
'plate_slug' => $str('bbs_plate_rewrite_slug', 'board'),
'posts_slug' => $str('bbs_posts_rewrite_slug', 'topic'),
'edit_slug' => $str('bbs_posts_edit_rewrite_slug', 'topic-edit'),
'gutenberg' => $bool('bbs_posts_show_in_rest_s'),
],
// ── upload ──
'upload' => [
'img_size' => $int('bbs_upload_img_size', 2048),
'img_multiple' => $int('bbs_image_upload_multiple', 9),
'video_size' => $int('bbs_upload_video_size', 20480),
'file_size' => $int('bbs_upload_file_size', 10240),
'file_mimes' => $str('bbs_upload_file_mimes', 'jpg,jpeg,png,gif,mp4,zip,rar,pdf'),
],
// ── credits ──
'credits' => [
'post_new' => $int('bbs_credits_post_new', 5),
'plate_new' => $int('bbs_credits_plate_new', 10),
'comment_new' => $int('bbs_credits_comment_new', 2),
'comment_like' => $int('bbs_credits_comment_like', 1),
'score_extra' => $int('bbs_credits_score_extra', 3),
'essence' => $int('bbs_credits_essence', 10),
'posts_hot' => $int('bbs_credits_posts_hot', 5),
'hot_recommend_threshold' => $int('bbs_hot_recommend_threshold', 500),
'adopted' => $int('bbs_credits_adopted', 20),
'daily_max' => $int('bbs_credits_daily_max', 50),
],
];
}
}
+505
View File
@@ -0,0 +1,505 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class HomeCms {
public static function get_footer_settings(): array {
$defaults = [
'contacts' => [],
'quick_links' => [],
'more_links' => [],
'hot_categories' => 5,
'copyright_text' => '',
'layout_template' => 'template_1',
];
$footer = get_option('aksrt_footer_settings', []);
$merged = array_merge($defaults, is_array($footer) ? $footer : []);
$s = Settings::get_all();
$bridge_keys = [
'footer_columns', 'footer_copyright', 'footer_icp', 'icp_number',
'footer_police', 'police_number',
'footer_theme_custom', 'footer_theme_bg_custom', 'footer_theme_bg', 'footer_theme_color_custom',
'footer_contact_wechat_img', 'footer_contact_qq', 'footer_contact_weibo', 'footer_contact_email',
];
foreach ($bridge_keys as $key) {
if (isset($s[$key])) {
$merged[$key] = $s[$key];
}
}
return $merged;
}
public static function get_footer_contacts(): array {
$footer = self::get_footer_settings();
return array_filter($footer['contacts'], fn($c) => !empty($c['enabled']));
}
public static function get_quick_links(): array {
$footer = self::get_footer_settings();
$links = array_filter($footer['quick_links'], fn($l) => !empty($l['enabled']));
usort($links, fn($a, $b) => ($a['sort_order'] ?? 0) <=> ($b['sort_order'] ?? 0));
return array_values($links);
}
public static function get_more_links(): array {
$footer = self::get_footer_settings();
$links = array_filter($footer['more_links'], fn($l) => !empty($l['enabled']));
usort($links, fn($a, $b) => ($a['sort_order'] ?? 0) <=> ($b['sort_order'] ?? 0));
return array_values($links);
}
public static function get_hot_categories_count(): int {
$footer = self::get_footer_settings();
return max(1, min(20, (int) ($footer['hot_categories'] ?? 5)));
}
public static function get_copyright_text(): string {
$footer = self::get_footer_settings();
return $footer['copyright_text'] ?: '';
}
public static function get_footer_brand_settings(): array {
$f = self::get_footer_settings();
return [
'm_s' => is_array($f['footer_t1_m_s'] ?? null) ? $f['footer_t1_m_s'] : ['pc', 'mobile'],
'img' => (string) ($f['footer_t1_img'] ?? ''),
'img_dark' => (string) ($f['footer_t1_img_dark'] ?? ''),
'title' => (string) ($f['footer_t1_t'] ?? ''),
'custom_code' => (string) ($f['fcode_t1_code'] ?? ''),
];
}
public static function get_footer_contact_extended_settings(): array {
$f = self::get_footer_settings();
return [
'm_s' => is_array($f['footer_contact_m_s'] ?? null) ? $f['footer_contact_m_s'] : ['pc', 'mobile'],
'wechat_img' => (string) ($f['footer_contact_wechat_img'] ?? ''),
'qq' => (string) ($f['footer_contact_qq'] ?? ''),
'weibo' => (string) ($f['footer_contact_weibo'] ?? ''),
'email' => (string) ($f['footer_contact_email'] ?? ''),
'more' => is_array($f['footer_contact_more'] ?? null) ? $f['footer_contact_more'] : [],
'code_1' => (string) ($f['fcode_t2_code_1'] ?? ''),
'code_2' => (string) ($f['fcode_t2_code_2'] ?? ''),
];
}
public static function get_footer_mini_img_settings(): array {
$f = self::get_footer_settings();
$raw_items = is_array($f['footer_mini_img'] ?? null) ? $f['footer_mini_img'] : [];
$items = [];
foreach ($raw_items as $item) {
if (!is_array($item)) {
continue;
}
$img = (string) ($item['img'] ?? $item['image'] ?? '');
if ($img === '') {
continue;
}
$items[] = [
'img' => $img,
'image' => $img,
'text' => (string) ($item['text'] ?? $item['label'] ?? ''),
'label' => (string) ($item['label'] ?? $item['text'] ?? ''),
];
}
return [
'm_s' => is_array($f['footer_mini_img_m_s'] ?? null) ? $f['footer_mini_img_m_s'] : ['pc', 'mobile'],
'items' => $items,
];
}
public static function get_footer_customize_code(): string {
$f = self::get_footer_settings();
return (string) ($f['fcode_customize_code'] ?? '');
}
public static function get_footer_theme_config(): array {
$s = Settings::get_all();
return [
'custom' => !empty($s['footer_theme_custom']),
'bg_custom' => sanitize_hex_color((string) ($s['footer_theme_bg_custom'] ?? '')) ?: '',
'bg' => sanitize_hex_color((string) ($s['footer_theme_bg'] ?? '')) ?: '',
'color_custom' => sanitize_hex_color((string) ($s['footer_theme_color_custom'] ?? '')) ?: '',
];
}
public static function get_site_profile(): string {
return 'cms';
}
public static function get_cms_features(): array {
$defaults = [
'resource_cpt_enabled' => true,
'download_login' => true,
'download_credit' => true,
'download_captcha' => false,
'search_include_resource' => true,
];
$s = Settings::get_all();
$saved = isset($s['cms_features']) && is_array($s['cms_features']) ? $s['cms_features'] : [];
return array_merge($defaults, $saved);
}
public static function cms_feature(string $key, $default = null) {
$f = self::get_cms_features();
return array_key_exists($key, $f) ? $f[$key] : $default;
}
public static function homepage_allowed_sorts(): array {
return ['modified', 'date', 'views', 'likes', 'comments', 'favorites', 'downloads', 'rand'];
}
public static function homepage_sort_labels(): array {
return [
'modified' => __('更新', 'aksrt'),
'date' => __('发布', 'aksrt'),
'views' => __('浏览', 'aksrt'),
'likes' => __('点赞', 'aksrt'),
'comments' => __('评论', 'aksrt'),
'favorites' => __('收藏', 'aksrt'),
'downloads' => __('下载', 'aksrt'),
'rand' => __('随机', 'aksrt'),
];
}
public static function get_homepage_fixed_settings(): array {
$s = Settings::get_all();
$allowed_sorts = self::homepage_allowed_sorts();
// Zibll-aligned primary list contract.
$main_list_enabled = array_key_exists('home_posts_list_s', $s)
? !empty($s['home_posts_list_s'])
: (!isset($s['home_posts_list_enabled']) || !empty($s['home_posts_list_enabled']));
$sort_bar_enabled = array_key_exists('home_list1_orderby_s', $s)
? !empty($s['home_list1_orderby_s'])
: (!isset($s['home_list_sort_enabled']) || !empty($s['home_list_sort_enabled']));
$orderby_option = [];
if (isset($s['home_list1_orderby_option']) && is_array($s['home_list1_orderby_option'])) {
$orderby_option = $s['home_list1_orderby_option'];
}
$default_sort = sanitize_key((string) ($orderby_option['orderby'] ?? $s['homepage_default_sort'] ?? 'date'));
if (!in_array($default_sort, $allowed_sorts, true)) {
$default_sort = 'date';
}
$sort_options_raw = $orderby_option['options'] ?? $s['home_list_sort_options'] ?? [];
if (!is_array($sort_options_raw)) {
$sort_options_raw = [];
}
$sort_options = self::normalize_sort_options($sort_options_raw);
if (!in_array($default_sort, $sort_options, true)) {
array_unshift($sort_options, $default_sort);
$sort_options = array_values(array_unique($sort_options));
}
$list_title = '';
if (array_key_exists('index_list_title', $s)) {
$list_title = sanitize_text_field((string) $s['index_list_title']);
} elseif (array_key_exists('home_list_title', $s)) {
$list_title = sanitize_text_field((string) $s['home_list_title']);
}
if ($list_title === '') {
$list_title = __('最新发布', 'aksrt');
}
$exclude_posts = self::normalize_int_array(
$s['home_exclude_posts'] ?? $s['home_exclude_post_ids'] ?? []
);
$exclude_cats = self::normalize_int_array(
$s['home_exclude_cats'] ?? $s['home_exclude_category_ids'] ?? []
);
$tabs_raw = [];
if (isset($s['home_lists']) && is_array($s['home_lists'])) {
$tabs_raw = $s['home_lists'];
} elseif (isset($s['homepage_tabs']) && is_array($s['homepage_tabs'])) {
$tabs_raw = $s['homepage_tabs'];
}
$settings = [
// Canonical Zibll keys.
'home_posts_list_s' => $main_list_enabled,
'home_list1_orderby_s' => $sort_bar_enabled,
'home_list1_orderby_option' => [
'orderby' => $default_sort,
'options' => $sort_options,
],
'index_list_title' => $list_title,
'home_exclude_posts' => $exclude_posts,
'home_exclude_cats' => $exclude_cats,
'home_lists' => self::normalize_homepage_tabs($tabs_raw),
// Convenience aliases for existing PHP/Vue callers during cutover of this slice.
'home_posts_list_enabled' => $main_list_enabled,
'home_list_sort_enabled' => $sort_bar_enabled,
'homepage_default_sort' => $default_sort,
'home_list_sort_options' => $sort_options,
'home_list_title' => $list_title,
'home_exclude_post_ids' => $exclude_posts,
'home_exclude_category_ids' => $exclude_cats,
'homepage_tabs' => self::normalize_homepage_tabs($tabs_raw),
// AKSRT presentation extras.
'homepage_card_style' => isset($s['homepage_card_style']) ? sanitize_key((string) $s['homepage_card_style']) : 'default',
'homepage_multi_thumb_enabled' => !empty($s['homepage_multi_thumb_enabled']),
'homepage_multi_thumb_min_images' => (int) ($s['homepage_multi_thumb_min_images'] ?? 3),
'homepage_sidebar_mini_enabled' => !isset($s['homepage_sidebar_mini_enabled']) || !empty($s['homepage_sidebar_mini_enabled']),
'homepage_default_view' => isset($s['homepage_default_view']) ? sanitize_key((string) $s['homepage_default_view']) : 'list',
'homepage_sort_ajax_enabled' => !isset($s['homepage_sort_ajax_enabled']) || !empty($s['homepage_sort_ajax_enabled']),
'homepage_sticky_tabs_enabled' => !isset($s['homepage_sticky_tabs_enabled']) || !empty($s['homepage_sticky_tabs_enabled']),
'home_list_sort_dropdown' => !empty($s['home_list_sort_dropdown']),
'homepage_tab_posts_per_page' => max(1, min(20, (int) ($s['homepage_tab_posts_per_page'] ?? 6))),
'homepage_tab_limit' => max(1, min(12, (int) ($s['homepage_tab_limit'] ?? 4))),
'homepage_module_hero_enabled' => !isset($s['homepage_module_hero_enabled']) || !empty($s['homepage_module_hero_enabled']),
'homepage_module_banner_enabled' => !isset($s['homepage_module_banner_enabled']) || !empty($s['homepage_module_banner_enabled']),
];
if (!in_array($settings['homepage_card_style'], ['default', 'style2', 'style3'], true)) {
$settings['homepage_card_style'] = 'default';
}
$settings['homepage_multi_thumb_min_images'] = max(2, min(4, $settings['homepage_multi_thumb_min_images']));
if (!in_array($settings['homepage_default_view'], ['list', 'card'], true)) {
$settings['homepage_default_view'] = 'list';
}
return $settings;
}
/**
* Homepage always uses ordinary posts. Kept for API stability.
*
* @return string Always 'post'.
*/
public static function homepage_content_model(): string {
return 'post';
}
public static function homepage_tabs(): array {
$settings = self::get_homepage_fixed_settings();
return $settings['home_lists'];
}
/** @return array<int, array{term_id:int,title:string,orderby:string,enabled:bool}> */
public static function home_lists(): array {
return self::homepage_tabs();
}
public static function homepage_card_style(): string {
$settings = self::get_homepage_fixed_settings();
return $settings['homepage_card_style'];
}
public static function homepage_multi_thumb_enabled(): bool {
$settings = self::get_homepage_fixed_settings();
return !empty($settings['homepage_multi_thumb_enabled']);
}
public static function homepage_multi_thumb_min_images(): int {
$settings = self::get_homepage_fixed_settings();
return (int) $settings['homepage_multi_thumb_min_images'];
}
public static function homepage_sidebar_mini_enabled(): bool {
$settings = self::get_homepage_fixed_settings();
return !empty($settings['homepage_sidebar_mini_enabled']);
}
public static function homepage_default_sort(): string {
$settings = self::get_homepage_fixed_settings();
return $settings['home_list1_orderby_option']['orderby'] ?? 'date';
}
public static function homepage_default_view(): string {
$settings = self::get_homepage_fixed_settings();
return $settings['homepage_default_view'];
}
public static function homepage_sort_ajax_enabled(): bool {
$settings = self::get_homepage_fixed_settings();
return !empty($settings['homepage_sort_ajax_enabled']);
}
public static function homepage_sticky_tabs_enabled(): bool {
$settings = self::get_homepage_fixed_settings();
return !empty($settings['homepage_sticky_tabs_enabled']);
}
public static function home_posts_list_enabled(): bool {
$settings = self::get_homepage_fixed_settings();
return !empty($settings['home_posts_list_s']);
}
public static function home_list_title(): string {
$settings = self::get_homepage_fixed_settings();
return (string) $settings['index_list_title'];
}
public static function home_list_sort_enabled(): bool {
$settings = self::get_homepage_fixed_settings();
return !empty($settings['home_list1_orderby_s']);
}
public static function home_list_sort_options(): array {
$settings = self::get_homepage_fixed_settings();
return $settings['home_list1_orderby_option']['options'] ?? ['modified', 'date', 'views', 'likes', 'comments'];
}
public static function home_list_sort_dropdown(): bool {
$settings = self::get_homepage_fixed_settings();
return !empty($settings['home_list_sort_dropdown']);
}
public static function home_exclude_post_ids(): array {
$settings = self::get_homepage_fixed_settings();
return $settings['home_exclude_posts'];
}
public static function home_exclude_category_ids(): array {
$settings = self::get_homepage_fixed_settings();
return $settings['home_exclude_cats'];
}
public static function homepage_tab_posts_per_page(): int {
$settings = self::get_homepage_fixed_settings();
return (int) $settings['homepage_tab_posts_per_page'];
}
public static function homepage_tab_limit(): int {
$settings = self::get_homepage_fixed_settings();
return (int) $settings['homepage_tab_limit'];
}
public static function get_paid_content_settings(): array {
return PaymentRuntime::get_paid_content_settings();
}
public static function get_content_coupon_settings(): array {
return PaymentRuntime::get_content_coupon_settings();
}
public static function get_payment_recharge_settings(): array {
return PaymentRuntime::get_payment_recharge_settings();
}
public static function get_payment_vip_settings(): array {
return PaymentRuntime::get_payment_vip_settings();
}
public static function get_payment_revenue_settings(): array {
return PaymentRuntime::get_payment_revenue_settings();
}
public static function get_payment_affiliate_settings(): array {
return PaymentRuntime::get_payment_affiliate_settings();
}
public static function get_payment_gateway_settings(): array {
return PaymentRuntime::get_payment_gateway_settings();
}
public static function get_order_settings(): array {
return PaymentRuntime::get_order_settings();
}
public static function get_card_pass_settings(): array {
return PaymentRuntime::get_card_pass_settings();
}
public static function get_withdrawal_settings(): array {
return PaymentRuntime::get_withdrawal_settings();
}
private static function normalize_sort_options(array $raw): array {
$allowed = self::homepage_allowed_sorts();
$result = [];
$seen = [];
foreach ($raw as $key) {
$key = sanitize_key((string) $key);
if (in_array($key, $allowed, true) && !isset($seen[$key])) {
$result[] = $key;
$seen[$key] = true;
}
}
if (empty($result)) {
$result = ['modified', 'date', 'views', 'likes', 'comments'];
}
return $result;
}
private static function normalize_int_array($raw): array {
if (!is_array($raw)) {
return [];
}
return array_values(array_filter(array_map('absint', $raw), static fn($v) => $v > 0));
}
private static function normalize_homepage_tabs(array $raw_tabs): array {
$allowed_orderby = self::homepage_allowed_sorts();
$tabs = [];
foreach ($raw_tabs as $tab) {
if (!is_array($tab)) {
continue;
}
$term_id = absint($tab['term_id'] ?? 0);
if ($term_id <= 0) {
continue;
}
$orderby = sanitize_key($tab['orderby'] ?? 'date');
if (!in_array($orderby, $allowed_orderby, true)) {
$orderby = 'date';
}
$tabs[] = [
'term_id' => $term_id,
'title' => sanitize_text_field($tab['title'] ?? ''),
'orderby' => $orderby,
'enabled' => !empty($tab['enabled']),
];
}
return $tabs;
}
private static function parse_slug_list(string $raw, array $fallback = []): array {
$items = preg_split('/[\s,]+/', $raw, -1, PREG_SPLIT_NO_EMPTY);
$normalized = [];
foreach ((array) $items as $item) {
$slug = sanitize_key((string) $item);
if ($slug !== '' && !in_array($slug, $normalized, true)) {
$normalized[] = $slug;
}
}
return !empty($normalized) ? $normalized : $fallback;
}
private static function count_structured_lines(string $raw): int {
$lines = preg_split('/[\r\n]+/', $raw, -1, PREG_SPLIT_NO_EMPTY);
$count = 0;
foreach ((array) $lines as $line) {
if (trim((string) $line) !== '') {
$count++;
}
}
return $count;
}
}
+257
View File
@@ -0,0 +1,257 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
trait HomepageReader {
/**
* Allowed sort/orderby values for the homepage.
*
* @return string[]
*/
public static function homepage_allowed_sorts(): array {
return HomeCms::homepage_allowed_sorts();
}
/**
* Full sort label map for admin UI and front-end rendering.
*
* @return array<string,string>
*/
public static function homepage_sort_labels(): array {
return HomeCms::homepage_sort_labels();
}
/**
* Fixed homepage visual settings.
*
* @return array<string,mixed>
*/
public static function get_homepage_fixed_settings(): array {
return HomeCms::get_homepage_fixed_settings();
}
/**
* Homepage always uses ordinary posts.
*
* @return string Always 'post'.
*/
public static function homepage_content_model(): string {
return HomeCms::homepage_content_model();
}
/**
* Structured homepage tabs.
*
* @return array<int, array{term_id:int, title:string, orderby:string, enabled:bool}>
*/
public static function homepage_tabs(): array {
return HomeCms::homepage_tabs();
}
/**
* Homepage card view variant.
*
* @return string default|style2|style3
*/
public static function homepage_card_style(): string {
return HomeCms::homepage_card_style();
}
/**
* Whether homepage list cards should switch to the multi-thumb layout.
*/
public static function homepage_multi_thumb_enabled(): bool {
return HomeCms::homepage_multi_thumb_enabled();
}
/**
* Minimum image count required to trigger the multi-thumb layout.
*/
public static function homepage_multi_thumb_min_images(): int {
return HomeCms::homepage_multi_thumb_min_images();
}
/**
* Whether homepage sidebar widgets should use the compact mini list style.
*/
public static function homepage_sidebar_mini_enabled(): bool {
return HomeCms::homepage_sidebar_mini_enabled();
}
/**
* Default homepage sort mode when no query override is present.
*/
public static function homepage_default_sort(): string {
return HomeCms::homepage_default_sort();
}
/**
* Default homepage view mode.
*/
public static function homepage_default_view(): string {
return HomeCms::homepage_default_view();
}
/**
* Whether the homepage sortbar should refresh through AJAX.
*/
public static function homepage_sort_ajax_enabled(): bool {
return HomeCms::homepage_sort_ajax_enabled();
}
/**
* Whether the homepage tabs should use sticky/affix behaviour.
*/
public static function homepage_sticky_tabs_enabled(): bool {
return HomeCms::homepage_sticky_tabs_enabled();
}
/**
* Whether the main "latest posts" list is enabled on the homepage.
*/
public static function home_posts_list_enabled(): bool {
return HomeCms::home_posts_list_enabled();
}
/**
* Custom title for the main homepage list section.
*/
public static function home_list_title(): string {
return HomeCms::home_list_title();
}
/**
* Whether the sort bar is enabled for the main stream.
*/
public static function home_list_sort_enabled(): bool {
return HomeCms::home_list_sort_enabled();
}
/**
* Configured sort option keys for the main stream sort bar.
*
* @return string[]
*/
public static function home_list_sort_options(): array {
return HomeCms::home_list_sort_options();
}
/**
* Whether the sort bar uses a dropdown instead of inline links.
*/
public static function home_list_sort_dropdown(): bool {
return HomeCms::home_list_sort_dropdown();
}
/**
* Post IDs excluded from both main stream and tab queries.
*
* @return int[]
*/
public static function home_exclude_post_ids(): array {
return HomeCms::home_exclude_post_ids();
}
/**
* Category IDs excluded from the main stream.
*
* @return int[]
*/
public static function home_exclude_category_ids(): array {
return HomeCms::home_exclude_category_ids();
}
/**
* Posts per page for each category tab.
*/
public static function homepage_tab_posts_per_page(): int {
return HomeCms::homepage_tab_posts_per_page();
}
/**
* Maximum number of category tabs to display.
*/
public static function homepage_tab_limit(): int {
return HomeCms::homepage_tab_limit();
}
// ============================================
// 顶部多功能组件设置(图标卡片 + 搜索覆盖)
// ============================================
/**
* 是否显示顶部组件搜索覆盖框
*/
public static function header_components_search_enabled(): bool {
return DisplayRuntime::header_components_search_enabled();
}
/**
* 顶部组件搜索详细配置(PC/移动分别 + 子选项)
*
* @return array{pc:bool,mobile:bool,show_keywords:bool,popular_limit:int,show_input_cat:bool,show_type:bool,keywords:string[],categories:array,type_options:array}
*/
public static function get_header_components_search_config(): array {
return DisplayRuntime::get_header_components_search_config();
}
/**
* 是否显示顶部组件图标卡片覆盖
*/
public static function header_components_cards_enabled(): bool {
return DisplayRuntime::header_components_cards_enabled();
}
/**
* 获取顶部组件图标卡片配置
* @return array
*/
public static function homepage_module_hero_enabled(): bool {
return DisplayRuntime::homepage_module_hero_enabled();
}
public static function homepage_module_banner_enabled(): bool {
return DisplayRuntime::homepage_module_banner_enabled();
}
/**
* Global slider configuration shared by Hero and Banner.
*
* @return array<string,mixed>
*/
public static function get_slider_config(): array {
return DisplayRuntime::get_slider_config();
}
public static function get_header_components_icon_cards(): array {
return DisplayRuntime::get_header_components_icon_cards();
}
/**
* 获取顶部组件卡片列数
*/
public static function get_header_components_card_columns(): int {
return DisplayRuntime::get_header_components_card_columns();
}
/**
* 是否显示顶部组件卡片背景色
*/
public static function header_components_card_bg_enabled(): bool {
return DisplayRuntime::header_components_card_bg_enabled();
}
/**
* 获取页脚配色配置
*/
public static function get_footer_theme_config(): array {
return HomeCms::get_footer_theme_config();
}
}
+621
View File
@@ -0,0 +1,621 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class Integration {
/** 私信邮件接收组限制 (all/vip/vip2/admin) */
public static function get_email_private_receive_limit(): string {
$limit = (string) Settings::get_value('email_private_receive_limit', 'all');
$valid = ['all', 'vip', 'vip2', 'admin'];
return in_array($limit, $valid, true) ? $limit : 'all';
}
/** 获取OAuth配置 */
public static function get_oauth(): array {
return get_option('aksrt_oauth', []);
}
/** 检查OAuth平台是否已配置 */
public static function oauth_configured(string $provider): bool {
$oauth = self::get_oauth();
return !empty($oauth[$provider]['client_id']) && !empty($oauth[$provider]['client_secret']);
}
/** OAuth 代理登录模式: 'off' | 'server' | 'client' (schema 存 'close'→映射为 'off') */
public static function oauth_proxy_mode(): string {
$settings = Settings::get_all();
$mode = sanitize_key((string) ($settings['oauth_proxy_mode'] ?? ''));
if ($mode === '') {
$oauth = self::get_oauth();
$mode = $oauth['proxy']['mode'] ?? (!empty($settings['oauth_agent']) ? 'client' : 'off');
}
if ($mode === 'close') {
$mode = 'off';
}
return in_array($mode, ['off', 'server', 'client'], true) ? $mode : 'off';
}
/** OAuth 代理登录 HMAC 密钥 */
public static function oauth_proxy_secret(): string {
$settings = Settings::get_all();
if (!empty($settings['oauth_proxy_secret'])) {
return (string) $settings['oauth_proxy_secret'];
}
$oauth = self::get_oauth();
return (string) ($oauth['proxy']['secret'] ?? '');
}
/** OAuth 代理登录服务端地址(客户端模式使用) */
public static function oauth_proxy_server_url(): string {
$settings = Settings::get_all();
if (!empty($settings['oauth_proxy_server_url'])) {
return esc_url_raw((string) $settings['oauth_proxy_server_url']);
}
$oauth = self::get_oauth();
return esc_url_raw((string) ($oauth['proxy']['server_url'] ?? ''));
}
/** OAuth 代理登录允许的平台列表(服务端模式使用) */
public static function oauth_proxy_allowed_providers(): array {
$settings = Settings::get_all();
$valid = ['github', 'gitee', 'qq', 'google', 'weibo', 'weixin', 'baidu', 'alipay'];
if (isset($settings['oauth_proxy_server_providers']) && is_array($settings['oauth_proxy_server_providers'])) {
return array_values(array_intersect($settings['oauth_proxy_server_providers'], $valid));
}
$oauth = self::get_oauth();
$allowed = $oauth['proxy']['allowed_providers'] ?? ['github', 'gitee', 'qq', 'google'];
return array_values(array_intersect($allowed, $valid));
}
/** OAuth 代理登录启用的平台列表(客户端模式使用) */
public static function oauth_proxy_client_providers(): array {
$settings = Settings::get_all();
$valid = ['github', 'gitee', 'qq', 'google', 'weibo', 'weixin', 'baidu', 'alipay'];
if (isset($settings['oauth_proxy_client_providers']) && is_array($settings['oauth_proxy_client_providers'])) {
return array_values(array_intersect($settings['oauth_proxy_client_providers'], $valid));
}
$oauth = self::get_oauth();
$enabled = $oauth['proxy']['client_providers'] ?? ['github', 'gitee', 'qq', 'google'];
return array_values(array_intersect($enabled, $valid));
}
/**
* 获取微信公众号登录完整配置
*
* @return array{gzh_type:string,token:string,token_nocheck:bool,encoding_aes_key:string,appid:string,appsecret:string,code_qrcode:string,code_keyword:string,code_reply:string,code_subscribe_reply:string,subscribe_msg:string,new_subscribe_msg:string,scan_msg:string,auto_reply_text:string,auto_reply_search:string,auto_reply_image:string,auto_reply_voice:string,auto_reply_default:string,gzh_menu:string,priority:bool,auto:bool,auto_expires:int}
*/
public static function get_weixingzh_settings(): array {
$settings = Settings::get_all();
$gzh_type = sanitize_key((string) ($settings['weixingzh_gzh_type'] ?? 'service'));
if (!in_array($gzh_type, ['service', 'subscribe'], true)) {
$gzh_type = 'service';
}
$auto_expires = max(0, min(86400, (int) ($settings['weixingzh_auto_expires'] ?? 3600)));
return [
'gzh_type' => $gzh_type,
'token' => (string) ($settings['weixingzh_token'] ?? ''),
'token_nocheck' => !empty($settings['weixingzh_token_nocheck']),
'encoding_aes_key' => (string) ($settings['weixingzh_encoding_aes_key'] ?? ''),
'appid' => (string) ($settings['weixingzh_appid'] ?? ''),
'appsecret' => (string) ($settings['weixingzh_appsecret'] ?? ''),
'code_qrcode' => (string) ($settings['weixingzh_code_qrcode'] ?? ''),
'code_keyword' => (string) ($settings['weixingzh_code_keyword'] ?? '登录'),
'code_reply' => (string) ($settings['weixingzh_code_reply'] ?? ''),
'code_subscribe_reply' => (string) ($settings['weixingzh_code_subscribe_reply'] ?? ''),
'subscribe_msg' => (string) ($settings['weixingzh_subscribe_msg'] ?? ''),
'new_subscribe_msg' => (string) ($settings['weixingzh_new_subscribe_msg'] ?? ''),
'scan_msg' => (string) ($settings['weixingzh_scan_msg'] ?? ''),
'auto_reply_text' => (string) ($settings['weixingzh_auto_reply_text'] ?? ''),
'auto_reply_search' => (string) ($settings['weixingzh_auto_reply_search'] ?? ''),
'auto_reply_image' => (string) ($settings['weixingzh_auto_reply_image'] ?? ''),
'auto_reply_voice' => (string) ($settings['weixingzh_auto_reply_voice'] ?? ''),
'auto_reply_default' => (string) ($settings['weixingzh_auto_reply_default'] ?? ''),
'gzh_menu' => (string) ($settings['weixingzh_gzh_menu'] ?? ''),
'priority' => !empty($settings['weixingzh_priority']),
'auto' => !empty($settings['weixingzh_auto']),
'auto_expires' => $auto_expires,
];
}
/**
* 获取 OAuth 代理登录完整配置(aksrt_settings 优先,BC fallback aksrt_oauth)
*
* @return array{mode:string,secret:string,server_url:string,server_providers:array,client_providers:array,gzh_type:string}
*/
public static function get_oauth_proxy_settings(): array {
$settings = Settings::get_all();
$oauth = self::get_oauth();
$mode = sanitize_key((string) ($settings['oauth_proxy_mode'] ?? ''));
if ($mode === '') {
$mode = $oauth['proxy']['mode'] ?? (!empty($settings['oauth_agent']) ? 'client' : 'close');
}
if (!in_array($mode, ['close', 'server', 'client'], true)) {
$mode = 'close';
}
$secret = (string) ($settings['oauth_proxy_secret'] ?? ($oauth['proxy']['secret'] ?? ''));
$server_url = esc_url_raw((string) ($settings['oauth_proxy_server_url'] ?? ($oauth['proxy']['server_url'] ?? '')));
$valid_providers = ['github', 'gitee', 'qq', 'google', 'weibo', 'weixin', 'baidu', 'alipay'];
$server_providers = array_values(array_intersect(
(array) ($settings['oauth_proxy_server_providers'] ?? ($oauth['proxy']['allowed_providers'] ?? ['github', 'gitee', 'qq', 'google'])),
$valid_providers
));
$client_providers = array_values(array_intersect(
(array) ($settings['oauth_proxy_client_providers'] ?? ($oauth['proxy']['client_providers'] ?? ['github', 'gitee', 'qq', 'google'])),
$valid_providers
));
$gzh_type = sanitize_key((string) ($settings['oauth_proxy_gzh_type'] ?? 'service'));
if (!in_array($gzh_type, ['service', 'subscribe'], true)) {
$gzh_type = 'service';
}
return [
'mode' => $mode,
'secret' => $secret,
'server_url' => $server_url,
'server_providers' => $server_providers,
'client_providers' => $client_providers,
'gzh_type' => $gzh_type,
];
}
/**
* 获取底部Tab完整配置
* @return array {enabled, scroll_hide, scroll_down_hide, items}
*/
public static function get_footer_tabbar_settings(): array {
$defaults = [
'enabled' => true,
'scroll_hide' => false,
'scroll_down_hide' => true,
'max_items' => 5,
'icon_size' => 22,
'text_size' => 10,
'show_text' => true,
'active_color' => '#f04494',
'bg_color' => '',
'border' => true,
'shadow' => true,
'animation' => true,
'double_click_home' => true,
'badge_dot' => false,
'footer_tabbar_single' => 'default',
'items' => [
[
'id' => 'tab_home',
'type' => 'home',
'icon' => 'fas fa-home',
'text' => __('首页', 'aksrt'),
'link' => '',
'badge' => '',
'badge_mode' => 'static',
'category_id' => 0,
'new_window' => false,
'enabled' => true,
'sort_order' => 0,
'ontop' => true,
],
[
'id' => 'tab_category',
'type' => 'category',
'icon' => 'fas fa-th-large',
'text' => __('分类', 'aksrt'),
'link' => '',
'badge' => '',
'badge_mode' => 'static',
'category_id' => 0,
'new_window' => false,
'enabled' => true,
'sort_order' => 1,
],
[
'id' => 'tab_search',
'type' => 'search',
'icon' => 'fas fa-search',
'text' => __('搜索', 'aksrt'),
'link' => '',
'badge' => '',
'badge_mode' => 'dynamic',
'category_id' => 0,
'search_default_type' => 'post',
'new_window' => false,
'enabled' => true,
'sort_order' => 2,
],
[
'id' => 'tab_auth',
'type' => 'auth',
'icon' => 'fas fa-user',
'text' => __('我的', 'aksrt'),
'link' => '',
'badge' => '',
'badge_mode' => 'dynamic',
'category_id' => 0,
'search_default_type' => 'post',
'new_window' => false,
'enabled' => true,
'sort_order' => 3,
],
],
];
$saved = get_option('aksrt_footer_tabbar', []);
if (!is_array($saved)) {
$saved = [];
}
return array_merge($defaults, $saved);
}
/**
* 获取底部Tab项(仅返回启用的,按排序)
* @return array
*/
public static function get_footer_tabbar_items(): array {
$settings = self::get_footer_tabbar_settings();
if (empty($settings['enabled'])) {
return [];
}
$items = array_filter($settings['items'], fn($item) => !empty($item['enabled']));
usort($items, fn($a, $b) => ($a['sort_order'] ?? 0) <=> ($b['sort_order'] ?? 0));
return array_values($items);
}
/**
* 获取文章页专属Tab模式
* @return string 'default' | 'extend'
*/
public static function get_footer_tabbar_single(): string {
$settings = self::get_footer_tabbar_settings();
$mode = (string) ($settings['footer_tabbar_single'] ?? 'default');
$valid = ['default', 'extend'];
return in_array($mode, $valid, true) ? $mode : 'default';
}
/**
* 获取通知系统设置
* @return array {read_retention_days, unread_retention_days, max_per_user}
*/
public static function get_notification_settings(): array {
$defaults = [
'enabled_types' => [
'comment_reply' => true,
'comment_approved' => true,
'download_ready' => true,
'credits_added' => true,
'credits_deducted' => true,
'verify_email' => true,
'pending_comments' => true,
'upload_moderation' => true,
],
'user_badge_enabled' => true,
'badge_include_stateful' => true,
'read_retention_days' => 30,
'unread_retention_days' => 180,
'max_per_user' => 200,
];
$saved = get_option('aksrt_notification_settings', []);
if (!is_array($saved)) {
$saved = [];
}
$merged = array_merge($defaults, $saved);
$merged['read_retention_days'] = max(1, min(365, (int) $merged['read_retention_days']));
$merged['unread_retention_days'] = max(7, min(3650, (int) $merged['unread_retention_days']));
$merged['max_per_user'] = max(20, min(5000, (int) $merged['max_per_user']));
return $merged;
}
/** 单项通知是否启用 */
public static function notification_enabled(string $type): bool {
$settings = self::get_notification_settings();
$enabled = is_array($settings['enabled_types'] ?? null) ? $settings['enabled_types'] : [];
return !isset($enabled[$type]) || !empty($enabled[$type]);
}
/** user 徽标是否启用 */
public static function notification_user_badge_enabled(): bool {
$settings = self::get_notification_settings();
return !isset($settings['user_badge_enabled']) || !empty($settings['user_badge_enabled']);
}
/** user 徽标是否包含即时提醒 */
public static function notification_badge_include_stateful(): bool {
$settings = self::get_notification_settings();
return !empty($settings['badge_include_stateful']);
}
/** 站内通知总开关 */
public static function message_enabled(): bool {
return !empty(Settings::get_value('message_s', true));
}
/** 通知基础设置(basic group) */
public static function notification_settings(): array {
$settings = Settings::get_all();
$slug = sanitize_key((string) ($settings['msg_center_rewrite_slug'] ?? 'message'));
if ($slug === '') {
$slug = 'message';
}
$paginate = sanitize_key((string) ($settings['message_paginate_type'] ?? 'ajax_lists'));
if (!in_array($paginate, ['ajax_lists', 'default'], true)) {
$paginate = 'ajax_lists';
}
$icon_show = $settings['message_icon_show'] ?? ['nav_menu'];
if (!is_array($icon_show)) {
$icon_show = ['nav_menu'];
}
$allowed_positions = ['nav_menu', 'avatar', 'mobile_bar'];
$icon_show = array_values(array_intersect($icon_show, $allowed_positions));
return [
'enabled' => !empty($settings['message_s']),
'slug' => $slug,
'icon_show' => $icon_show,
'paginate_type' => $paginate,
'user_set' => !empty($settings['message_user_set']),
];
}
/** 已关闭的通知推送类型列表 */
public static function notification_closed_types(): array {
$types = Settings::get_value('message_close_msg_type', []);
if (!is_array($types)) {
return [];
}
$allowed = ['posts', 'comment', 'favorite', 'like', 'hot', 'followed', 'system', 'pay', 'withdraw_reply', 'medal', 'auth_apply_reply'];
return array_values(array_intersect($types, $allowed));
}
/** 自动清理已读消息配置 */
public static function auto_clear_settings(): array {
$settings = Settings::get_all();
$types = $settings['message_auto_clear_types'] ?? ['posts', 'comment', 'favorite', 'like', 'hot', 'followed', 'system'];
if (!is_array($types)) {
$types = [];
}
$allowed = ['posts', 'comment', 'favorite', 'like', 'hot', 'followed', 'system', 'pay', 'withdraw_reply', 'medal', 'auth_apply_reply', 'download', 'credits', 'private', 'report'];
return [
'enabled' => !empty($settings['message_auto_clear_s']),
'days' => max(1, min(3650, (int) ($settings['message_auto_clear_time'] ?? 365))),
'types' => array_values(array_intersect($types, $allowed)),
];
}
/** 私信功能配置 */
public static function private_message_settings(): array {
$settings = Settings::get_all();
$quick_often = (string) ($settings['private_quick_often'] ?? "你好,请问有什么可以帮你的?\n感谢你的反馈\n已处理,请查收");
$quick_lines = array_filter(array_map('trim', preg_split('/\r\n|\r|\n/', $quick_often)));
return [
'enabled' => !array_key_exists('private_s', $settings) || !empty($settings['private_s']),
'smilie' => !empty($settings['private_smilie_s']),
'code' => !empty($settings['private_code_s']),
'image' => !empty($settings['private_image_s']),
'upload_img' => !empty($settings['private_upload_img']),
'quick_reply' => !empty($settings['private_quick_s']),
'quick_often' => array_values($quick_lines),
'submit_text' => sanitize_text_field((string) ($settings['private_submit_text'] ?? '发送')),
'placeholder' => sanitize_text_field((string) ($settings['private_placeholder'] ?? '请输入消息...')),
];
}
/**
* 获取右侧浮动按钮完整配置
* @return array {enabled, style, position, scroll_hide, scroll_down_hide, filter_blur, items}
*/
public static function get_floating_buttons_settings(): array {
$defaults = [
'enabled' => true,
'style' => 'round',
'position' => 'bottom',
'scroll_hide' => false,
'scroll_down_hide' => true,
'max' => 5,
'size' => 44,
'spacing' => 10,
'shadow' => true,
'animation' => true,
'opacity' => 100,
'active_color' => '',
'bg_color' => '',
'pc_show' => true,
'm_show' => true,
'filter_blur' => ['mobile'],
'items' => [
[
'id' => 'btn_wechat',
'type' => 'service_wechat',
'icon' => 'fab fa-weixin',
'icon_dark' => '',
'icon_light' => '',
'text' => __('微信', 'aksrt'),
'desc' => '',
'action_mode'=> 'qrcode',
'qq' => '',
'qrcode_img' => '',
'wechat_img' => '',
'link' => '',
'add_items' => [],
'hover' => '',
'qr_size' => 120,
'hover_width' => 200,
'pc_s' => true,
'm_s' => true,
'color' => '',
'bg' => '',
'enabled' => true,
'sort_order' => 0,
],
[
'id' => 'btn_theme',
'type' => 'theme_mode',
'icon' => 'fas fa-sun',
'icon_dark' => '',
'icon_light' => '',
'text' => __('主题', 'aksrt'),
'desc' => __('切换主题', 'aksrt'),
'action_mode'=> 'link',
'qq' => '',
'qrcode_img' => '',
'wechat_img' => '',
'link' => '',
'add_items' => [],
'hover' => '',
'qr_size' => 120,
'hover_width' => 200,
'pc_s' => true,
'm_s' => false,
'color' => '',
'bg' => '',
'enabled' => true,
'sort_order' => 1,
],
[
'id' => 'btn_top',
'type' => 'back_top',
'icon' => 'fas fa-chevron-up',
'icon_dark' => '',
'icon_light' => '',
'text' => __('顶部', 'aksrt'),
'desc' => __('返回顶部', 'aksrt'),
'action_mode'=> 'link',
'qq' => '',
'qrcode_img' => '',
'wechat_img' => '',
'link' => '',
'add_items' => [],
'hover' => '',
'qr_size' => 120,
'hover_width' => 200,
'pc_s' => true,
'm_s' => true,
'color' => '',
'bg' => '',
'enabled' => true,
'sort_order' => 2,
],
],
];
$saved = get_option('aksrt_floating_buttons', []);
if (!is_array($saved)) {
$saved = [];
}
return array_merge($defaults, $saved);
}
/**
* 获取右侧浮动按钮项(仅返回启用的,按排序)
* @return array
*/
public static function get_floating_buttons_items(): array {
$settings = self::get_floating_buttons_settings();
if (empty($settings['enabled'])) {
return [];
}
$items = array_filter($settings['items'], fn($item) => !empty($item['enabled']));
usort($items, fn($a, $b) => ($a['sort_order'] ?? 0) <=> ($b['sort_order'] ?? 0));
return array_values($items);
}
/**
* 获取微信通知设置
*
* @return array{enabled:bool,app_id:string,app_secret:string,template_id:string}
*/
public static function get_wechat_settings(): array {
$stored = get_option('aksrt_wechat_settings', []);
if (!is_array($stored)) {
$stored = [];
}
return [
'enabled' => !empty($stored['enabled']),
'app_id' => sanitize_text_field((string) ($stored['app_id'] ?? '')),
'app_secret' => (string) ($stored['app_secret'] ?? ''),
'template_id' => sanitize_text_field((string) ($stored['template_id'] ?? '')),
];
}
/**
* 更新微信通知设置
*
* @param array $settings 合并后的完整微信配置
*/
public static function update_wechat_settings(array $settings): void {
$allowed = ['enabled', 'app_id', 'app_secret', 'template_id'];
$clean = [];
foreach ($allowed as $key) {
if (array_key_exists($key, $settings)) {
$clean[$key] = $settings[$key];
}
}
update_option('aksrt_wechat_settings', $clean);
}
/**
* 获取文档模式设置
*/
public static function get_docs_mode_settings(): array {
$all = Settings::get_all();
$template = in_array($all['docs_mode_default_template'] ?? '', ['documentnav', 'special'], true)
? $all['docs_mode_default_template']
: 'documentnav';
$order = in_array($all['docs_mode_default_order'] ?? '', ['menu_order', 'date', 'title'], true)
? $all['docs_mode_default_order']
: 'menu_order';
$style = in_array($all['docs_mode_style'] ?? '', ['default', 'simple', 'card'], true)
? $all['docs_mode_style']
: 'default';
$raw_cats = $all['docs_mode_cats'] ?? [];
if (!is_array($raw_cats)) {
$raw_cats = array_filter(array_map('trim', explode(',', (string) $raw_cats)));
}
$cats = array_values(array_filter(array_map('absint', $raw_cats)));
return [
'enabled' => !empty($all['docs_mode_enabled']),
'default_template' => $template,
'toc_enabled' => !isset($all['docs_mode_toc_enabled']) || !empty($all['docs_mode_toc_enabled']),
'chapter_nav_enabled' => !isset($all['docs_mode_chapter_nav_enabled']) || !empty($all['docs_mode_chapter_nav_enabled']),
'default_order' => $order,
'cats' => $cats,
'exclude' => !isset($all['docs_mode_exclude']) || !empty($all['docs_mode_exclude']),
'toc_depth' => max(1, min(6, (int) ($all['docs_mode_toc_depth'] ?? 3))),
'chapter_title' => sanitize_text_field((string) ($all['docs_mode_chapter_title'] ?? '')),
'style' => $style,
'icon' => !isset($all['docs_mode_icon']) || !empty($all['docs_mode_icon']),
'count' => !isset($all['docs_mode_count']) || !empty($all['docs_mode_count']),
'docs_mode_enabled' => !empty($all['docs_mode_enabled']),
'docs_mode_default_template' => $template,
'docs_mode_toc_enabled' => !isset($all['docs_mode_toc_enabled']) || !empty($all['docs_mode_toc_enabled']),
'docs_mode_chapter_nav_enabled' => !isset($all['docs_mode_chapter_nav_enabled']) || !empty($all['docs_mode_chapter_nav_enabled']),
'docs_mode_default_order' => $order,
'docs_mode_cats' => $cats,
'docs_mode_exclude' => !isset($all['docs_mode_exclude']) || !empty($all['docs_mode_exclude']),
'docs_mode_toc_depth' => max(1, min(6, (int) ($all['docs_mode_toc_depth'] ?? 3))),
'docs_mode_chapter_title' => sanitize_text_field((string) ($all['docs_mode_chapter_title'] ?? '')),
'docs_mode_style' => $style,
'docs_mode_icon' => !isset($all['docs_mode_icon']) || !empty($all['docs_mode_icon']),
'docs_mode_count' => !isset($all['docs_mode_count']) || !empty($all['docs_mode_count']),
];
}
}
+118
View File
@@ -0,0 +1,118 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class Mail {
/** 所有邮件事件类型常量 */
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 {
$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',
];
if ($event === '') {
$events = [];
foreach (self::EMAIL_EVENTS as $ev) {
$events[$ev] = self::build_email_event_entry($s, $ev);
}
return ['global' => $global, 'events' => $events];
}
if (!in_array($event, self::EMAIL_EVENTS, true)) {
return ['enabled' => true, 'subject' => '', 'content' => '', 'html' => false, 'global' => $global];
}
return array_merge(self::build_email_event_entry($s, $event), ['global' => $global]);
}
/**
* 邮件事件是否启用
*/
public static function is_email_event_enabled(string $event): bool {
if (!in_array($event, self::EMAIL_EVENTS, true)) {
return true;
}
$s = Settings::get_all();
return !isset($s["email_{$event}_enabled"]) || !empty($s["email_{$event}_enabled"]);
}
/**
* 通过邮件事件体系发送邮件
*/
public static function send_event_email(
string $event,
string $to,
string $default_subject,
string $default_content,
array $placeholders = []
): bool {
if (!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);
}
/**
* 构建单个邮件事件条目
*/
private static function build_email_event_entry(array $s, string $event): array {
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"]),
];
}
}
+484
View File
@@ -0,0 +1,484 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class PaymentRuntime {
/**
* Paid content / download policy.
*
* @return array<string,mixed>
*/
public static function get_paid_content_settings(): array {
$settings = Settings::get_all();
$features = HomeCms::get_cms_features();
$catalog_note = trim((string) ($settings['payment_paid_catalog_note'] ?? ''));
$manual_note = trim((string) ($settings['payment_manual_delivery_note'] ?? ''));
$box_position = sanitize_key((string) ($settings['pay_box_position'] ?? 'top'));
// Zibll: box_top | top | bottom | box_bottom
if (!in_array($box_position, ['box_top', 'top', 'bottom', 'box_bottom'], true)) {
$box_position = 'top';
}
return [
'download_login' => !empty($features['download_login']),
'download_credit' => !empty($features['download_credit']) && Settings::credits_enabled(),
'download_captcha' => !empty($features['download_captcha']),
'default_cost' => Settings::download_default_cost(),
'paid_post_types' => self::parse_slug_list((string) ($settings['payment_paid_post_types'] ?? 'post'), ['post']),
'vip_free_download' => !empty($settings['payment_vip_free_download']),
'vip_exempt_roles' => self::parse_slug_list((string) ($settings['payment_vip_exempt_roles'] ?? '')),
'catalog_note' => $catalog_note,
'manual_delivery_note' => $manual_note !== '' ? $manual_note : __('付费或人工确认内容请在订单备注中记录交付方式。', 'aksrt'),
'no_logged_in' => !isset($settings['pay_no_logged_in']) || !empty($settings['pay_no_logged_in']),
'free_logged_show' => !empty($settings['pay_free_logged_show']),
'down_alone_page' => !empty($settings['payment_down_alone_page']),
'display' => [
'mark' => sanitize_text_field((string) ($settings['pay_mark'] ?? '付费')),
'currency_symbol' => sanitize_text_field((string) ($settings['payment_currency_symbol'] ?? '¥')),
'show_user' => !isset($settings['pay_show_user']) || !empty($settings['pay_show_user']),
'show_paycount' => !isset($settings['pay_show_paycount']) || !empty($settings['pay_show_paycount']),
'box_position' => $box_position,
],
'download' => [
'enabled' => !empty($settings['payment_download_enable']),
'remote' => !isset($settings['payment_download_remote_s']) || !empty($settings['payment_download_remote_s']),
'verify_nonce' => !isset($settings['payment_download_verify_nonce']) || !empty($settings['payment_download_verify_nonce']),
'user_log' => max(0, min(9999, (int) ($settings['payment_download_user_log'] ?? 50))),
'post_log' => max(0, min(9999, (int) ($settings['payment_download_post_log'] ?? 50))),
'remote_size' => max(1, (int) ($settings['payment_download_remote_size'] ?? 2048)),
'remote_ext' => sanitize_text_field((string) ($settings['payment_download_remote_ext'] ?? 'zip,rar,7z')),
'alone_page' => !empty($settings['payment_down_alone_page']),
],
// Content cashier coupon (zibll coupon_post_s / coupon_desc).
'coupon_post_s' => !empty($settings['coupon_post_s']),
'coupon_desc' => sanitize_textarea_field((string) ($settings['coupon_desc'] ?? '')),
];
}
/**
* Content coupon global switches (zibll coupon_post_s).
*
* @return array{enabled:bool,desc:string}
*/
public static function get_content_coupon_settings(): array {
$settings = self::get_paid_content_settings();
return [
'enabled' => !empty($settings['coupon_post_s']),
'desc' => (string) ($settings['coupon_desc'] ?? ''),
];
}
/**
* Balance recharge settings (Zibll pay_balance_* contract).
*
* @return array<string,mixed>
*/
public static function get_payment_recharge_settings(): array {
$settings = Settings::get_all();
$packages = (string) ($settings['pay_balance_product'] ?? "30|30|入门包\n98|108|推荐包");
if (is_array($settings['pay_balance_product'] ?? null)) {
$packages = self::format_product_lines((array) $settings['pay_balance_product']);
}
$desc = sanitize_textarea_field((string) ($settings['pay_balance_desc'] ?? ''));
return [
'enabled' => !empty($settings['pay_balance_s']),
'desc' => $desc,
'auto_to_balance' => !isset($settings['pay_auto_to_balance']) || !empty($settings['pay_auto_to_balance']),
'packages_raw' => $packages,
'package_count' => self::count_structured_lines($packages),
'help_text' => $desc !== '' ? $desc : __('当前支持人工确认与测试联调;接入真实网关时复用同一组字段。', 'aksrt'),
'product' => [
'name' => sanitize_text_field((string) ($settings['pay_balance_product_name'] ?? '')),
'price' => max(0, (float) ($settings['pay_balance_price'] ?? 0)),
'tag' => sanitize_text_field((string) ($settings['pay_balance_tag'] ?? '元')),
'tag_class' => sanitize_text_field((string) ($settings['pay_balance_tag_class'] ?? '')),
'custom_s' => !isset($settings['pay_balance_product_custom_s']) || !empty($settings['pay_balance_product_custom_s']),
'custom_limit' => max(1, min(99999, (int) ($settings['pay_balance_product_custom_limit'] ?? 9999))),
],
'transfer' => [
'enabled' => !empty($settings['pay_balance_transfer_s']),
'service_charge' => max(0, min(50, (int) ($settings['pay_balance_transfer_service_charge'] ?? 0))),
'desc' => sanitize_textarea_field((string) ($settings['pay_balance_transfer_desc'] ?? '')),
],
'pass_charge' => [
'enabled' => !empty($settings['pay_balance_pass_charge_s']),
'desc' => sanitize_textarea_field((string) ($settings['pay_balance_pass_charge_desc'] ?? '')),
'only_password' => !empty($settings['pay_balance_pass_charge_only_password']),
],
'exchange' => [
'enabled' => !empty($settings['pay_balance_exchange_s']),
'rate' => max(1, min(10000, (int) ($settings['pay_balance_exchange_rate'] ?? 100))),
'desc' => sanitize_textarea_field((string) ($settings['pay_balance_exchange_desc'] ?? '')),
],
];
}
/**
* Dual-level VIP settings (Zibll pay_user_vip_1 / pay_user_vip_2).
*
* @return array<string,mixed>
*/
public static function get_payment_vip_settings(): array {
$settings = Settings::get_all();
$vip1_enabled = !empty($settings['pay_user_vip_1_s']);
$vip2_enabled = !empty($settings['pay_user_vip_2_s']);
$enabled = $vip1_enabled || $vip2_enabled;
$name1 = trim((string) ($settings['pay_user_vip_1_name'] ?? ''));
$name2 = trim((string) ($settings['pay_user_vip_2_name'] ?? ''));
if ($name1 === '') {
$name1 = __('一级会员', 'aksrt');
}
if ($name2 === '') {
$name2 = __('二级会员', 'aksrt');
}
$badge = $vip2_enabled ? $name2 : ($vip1_enabled ? $name1 : __('VIP', 'aksrt'));
$renew_price_type = sanitize_key((string) ($settings['vip_renew_price_type'] ?? 'discount'));
if (!in_array($renew_price_type, ['same', 'discount', 'reduce'], true)) {
$renew_price_type = 'discount';
}
$packages = (string) ($settings['payment_vip_packages'] ?? "30|30|月卡\n365|365|年卡");
$desc = sanitize_textarea_field((string) ($settings['pay_user_vip_desc'] ?? ''));
return [
'enabled' => $enabled,
'name' => $vip1_enabled ? $name1 : $badge,
'badge_text' => $badge,
'desc' => $desc,
'packages_raw' => $packages,
'package_count' => self::count_structured_lines($packages),
'help_text' => $desc !== '' ? $desc : __('会员权益以免扣下载和身份展示为主,开通后立即生效。', 'aksrt'),
'levels' => [
1 => [
'enabled' => $vip1_enabled,
'name' => $name1,
'download_limit' => max(0, min(9999, (int) ($settings['pay_download_limit_vip_1'] ?? 0))),
'download_speed' => max(0, min(99999, (int) ($settings['pay_download_speed_vip_1'] ?? 0))),
],
2 => [
'enabled' => $vip2_enabled,
'name' => $name2,
'download_limit' => max(0, min(9999, (int) ($settings['pay_download_limit_vip_2'] ?? 0))),
'download_speed' => max(0, min(99999, (int) ($settings['pay_download_speed_vip_2'] ?? 0))),
],
],
'benefit' => [
'download_limit' => max(0, min(9999, (int) ($settings['pay_download_limit'] ?? 0))),
'download_speed' => max(0, min(99999, (int) ($settings['pay_download_speed'] ?? 0))),
'pass_charge' => !empty($settings['pay_vip_pass_charge_s']),
'pass_charge_desc' => sanitize_textarea_field((string) ($settings['pay_vip_pass_charge_desc'] ?? '')),
'pass_charge_only_password' => !isset($settings['pay_vip_pass_charge_only_password']) || !empty($settings['pay_vip_pass_charge_only_password']),
],
'renew' => [
'enabled' => !isset($settings['vip_renew']) || !empty($settings['vip_renew']),
'desc' => sanitize_textarea_field((string) ($settings['vip_renew_desc'] ?? '')),
'price_type' => $renew_price_type,
'discount' => max(1, min(100, (int) ($settings['vip_renew_discount'] ?? 8))),
'reduce' => max(0, min(9999, (int) ($settings['vip_renew_reduce'] ?? 20))),
],
'upgrade' => [
'enabled' => !isset($settings['vip_upgrade']) || !empty($settings['vip_upgrade']),
'desc' => sanitize_textarea_field((string) ($settings['vip_upgrade_desc'] ?? '')),
'product' => sanitize_text_field((string) ($settings['vip_upgrade_product'] ?? '')),
],
'points_exchange' => [
'enabled' => !empty($settings['pay_vip_points_exchange_s']),
'desc' => sanitize_textarea_field((string) ($settings['pay_vip_points_exchange_desc'] ?? '')),
'packages_raw' => (string) ($settings['payment_vip_points_packages'] ?? "1|500|30|积分月卡\n1|5000|0|积分永久"),
],
];
}
/**
* @return array<string,mixed>
*/
public static function get_payment_revenue_settings(): array {
$settings = Settings::get_all();
$note = trim((string) ($settings['payment_revenue_note'] ?? ''));
return [
'enabled' => !empty($settings['pay_income_s']),
'share_ratio' => max(0, min(100, (int) ($settings['payment_revenue_share_ratio'] ?? 70))),
'ratio_vip1' => max(0, min(100, (int) ($settings['ratio_vip_1'] ?? 0))),
'ratio_vip2' => max(0, min(100, (int) ($settings['ratio_vip_2'] ?? 0))),
'settlement_threshold' => max(0, (int) ($settings['payment_revenue_settlement_threshold'] ?? 100)),
'cycle_days' => max(1, min(365, (int) ($settings['payment_revenue_cycle_days'] ?? 30))),
'points_rule' => sanitize_textarea_field((string) ($settings['payment_revenue_points_rule'] ?? '')),
'note' => $note !== '' ? $note : __('分成策略和台账结构已就绪,订单入账后自动按比例结算。', 'aksrt'),
'display' => [
'desc_details' => sanitize_textarea_field((string) ($settings['pay_income_desc_details'] ?? '')),
'desc_page' => esc_url_raw((string) ($settings['pay_income_desc_page'] ?? '')),
],
];
}
/**
* @return array<string,mixed>
*/
public static function get_payment_affiliate_settings(): array {
$settings = Settings::get_all();
$note = trim((string) ($settings['pay_rebate_text_desc'] ?? ''));
return [
'enabled' => !empty($settings['pay_rebate_s']),
'ratio' => max(0, min(100, (int) ($settings['pay_rebate_ratio'] ?? 10))),
'window_days' => max(1, min(365, (int) ($settings['payment_affiliate_window_days'] ?? 30))),
'require_paid_order' => !isset($settings['payment_affiliate_require_paid_order']) || !empty($settings['payment_affiliate_require_paid_order']),
'self_s' => !isset($settings['pay_rebate_self_s']) || !empty($settings['pay_rebate_self_s']),
'show_users' => !isset($settings['pay_rebate_show_users']) || !empty($settings['pay_rebate_show_users']),
'note' => $note !== '' ? $note : __('返佣链路复用邀请体系,订单归因和结算自动衔接当前策略字段。', 'aksrt'),
'user_ratio' => [
'user' => max(0, min(100, (int) ($settings['payment_affiliate_user_ratio'] ?? $settings['pay_rebate_ratio'] ?? 10))),
'vip1' => max(0, min(100, (int) ($settings['pay_rebate_ratio_vip_1'] ?? 0))),
'vip2' => max(0, min(100, (int) ($settings['pay_rebate_ratio_vip_2'] ?? 0))),
],
'poster' => [
'enabled' => !isset($settings['pay_rebate_poster_s']) || !empty($settings['pay_rebate_poster_s']),
'img' => esc_url_raw((string) ($settings['pay_rebate_poster_img'] ?? '')),
'title' => sanitize_text_field((string) ($settings['pay_rebate_poster_title'] ?? '')),
'desc' => sanitize_text_field((string) ($settings['pay_rebate_poster_desc'] ?? '')),
],
'text' => [
'desc' => sanitize_textarea_field((string) ($settings['pay_rebate_text_desc'] ?? '')),
'details_title' => sanitize_text_field((string) ($settings['pay_rebate_text_details_title'] ?? '')),
'details' => sanitize_textarea_field((string) ($settings['pay_rebate_text_details'] ?? '')),
'discount' => sanitize_text_field((string) ($settings['pay_rebate_text_discount'] ?? '')),
],
];
}
/**
* @return array<string,mixed>
*/
public static function get_payment_gateway_settings(): array {
$settings = Settings::get_all();
$mode = sanitize_key((string) ($settings['payment_gateway_mode'] ?? 'manual_review'));
if (!in_array($mode, ['manual_review', 'test_gateway', 'adapter_ready'], true)) {
$mode = 'manual_review';
}
$help_text = trim((string) ($settings['payment_gateway_help'] ?? ''));
$gateway_keys = ['wechat', 'alipay', 'paypal', 'stripe', 'balance', 'card_pass', 'xunhupay', 'xhpay', 'payjs', 'epay', 'vmq', 'codepay'];
$gateways = [];
foreach ($gateway_keys as $gw) {
$gateways[$gw] = !empty($settings["payment_{$gw}_enabled"]);
}
return [
'mode' => $mode,
'test_enabled' => !isset($settings['payment_gateway_test_enabled']) || !empty($settings['payment_gateway_test_enabled']),
'manual_review' => !isset($settings['payment_gateway_manual_review']) || !empty($settings['payment_gateway_manual_review']),
'callback_secret' => sanitize_text_field((string) ($settings['payment_gateway_callback_secret'] ?? '')),
'help_text' => $help_text !== '' ? $help_text : __('未接真实支付网关时,建议使用测试网关或人工确认模式完成联调。', 'aksrt'),
'gateways' => $gateways,
'xunhupay' => [
'appid' => sanitize_text_field((string) ($settings['xunhupay_appid'] ?? '')),
'appsecret' => sanitize_text_field((string) ($settings['xunhupay_appsecret'] ?? '')),
],
'xhpay' => [
'mchid' => sanitize_text_field((string) ($settings['xhpay_mchid'] ?? '')),
'alipay_v2' => sanitize_text_field((string) ($settings['xhpay_alipay_v2'] ?? '')),
'wx_h5' => sanitize_text_field((string) ($settings['xhpay_wx_h5'] ?? '')),
],
'payjs' => [
'mchid' => sanitize_text_field((string) ($settings['payjs_mchid'] ?? '')),
'key' => sanitize_text_field((string) ($settings['payjs_key'] ?? '')),
],
'epay' => [
'apiurl' => esc_url_raw((string) ($settings['epay_apiurl'] ?? '')),
'pid' => sanitize_text_field((string) ($settings['epay_pid'] ?? '')),
'key' => sanitize_text_field((string) ($settings['epay_key'] ?? '')),
],
'alipay_extra' => [
'return_url' => esc_url_raw((string) ($settings['alipay_return_url'] ?? '')),
'notify_url' => esc_url_raw((string) ($settings['alipay_notify_url'] ?? '')),
],
];
}
/**
* Order display / checkout policy (Zibll pay_* + AKSRT order ops).
*
* @return array<string,mixed>
*/
public static function get_order_settings(): array {
$s = Settings::get_all();
$expiry = max(5, min(1440, (int) ($s['payment_order_expiry'] ?? 30)));
$cookie_day = max(1, min(31, (int) ($s['pay_cookie_day'] ?? 15)));
$max_minutes = max(5, min(1440, (int) ($s['payment_order_max_minutes'] ?? 30)));
$box_position = sanitize_key((string) ($s['pay_box_position'] ?? 'top'));
if (!in_array($box_position, ['box_top', 'top', 'bottom', 'box_bottom'], true)) {
$box_position = 'top';
}
$status_keys = ['pending', 'paid', 'completed', 'cancelled', 'refunded', 'refunding', 'processing'];
$status_defaults = ['待支付', '已支付', '已完成', '已取消', '已退款', '退款中', '处理中'];
$status_texts = [];
foreach ($status_keys as $i => $key) {
$val = trim((string) ($s["payment_order_status_{$key}"] ?? ''));
$status_texts[$key] = $val !== '' ? $val : $status_defaults[$i];
}
return [
'prefix' => sanitize_text_field((string) ($s['payment_order_prefix'] ?? 'AKSRT')),
'expiry' => $expiry,
'show_user' => !isset($s['pay_show_user']) || !empty($s['pay_show_user']),
'show_paycount' => !isset($s['pay_show_paycount']) || !empty($s['pay_show_paycount']),
'no_logged_in' => !isset($s['pay_no_logged_in']) || !empty($s['pay_no_logged_in']),
'cookie_day' => $cookie_day,
'no_logged_remind' => sanitize_textarea_field((string) ($s['pay_no_logged_remind'] ?? '')),
'wait_float' => !isset($s['pay_wait_float_s']) || !empty($s['pay_wait_float_s']),
'order_max_minutes' => $max_minutes,
'mark' => sanitize_text_field((string) ($s['pay_mark'] ?? '付费')),
'box_position' => $box_position,
'free_logged_show' => !empty($s['pay_free_logged_show']),
'show_ip' => !empty($s['payment_show_ip']),
'show_device' => !empty($s['payment_show_device']),
'show_order_no' => !isset($s['payment_show_order_no']) || !empty($s['payment_show_order_no']),
'show_create_time' => !isset($s['payment_show_create_time']) || !empty($s['payment_show_create_time']),
'show_pay_time' => !isset($s['payment_show_pay_time']) || !empty($s['payment_show_pay_time']),
'status_texts' => $status_texts,
'service' => [
'text' => sanitize_textarea_field((string) ($s['pay_service'] ?? '')),
'icon' => sanitize_text_field((string) ($s['pay_service_icon'] ?? '')),
'value' => sanitize_text_field((string) ($s['pay_service_value'] ?? '')),
'qq' => sanitize_text_field((string) ($s['pay_service_qq'] ?? '')),
'wechat' => esc_url_raw((string) ($s['pay_service_wechat'] ?? '')),
],
];
}
/**
* @return array<string,mixed>
*/
public static function get_card_pass_settings(): array {
$s = Settings::get_all();
$type = sanitize_key((string) ($s['card_pass_type'] ?? 'balance'));
if (!in_array($type, ['balance', 'vip', 'credits'], true)) {
$type = 'balance';
}
$success_msg = trim((string) ($s['card_pass_success_msg'] ?? ''));
$fail_msg = trim((string) ($s['card_pass_fail_msg'] ?? ''));
return [
'enabled' => !empty($s['card_pass_enabled']),
'desc' => sanitize_textarea_field((string) ($s['card_pass_desc'] ?? '')),
'type' => $type,
'prefix' => sanitize_text_field((string) ($s['card_pass_prefix'] ?? 'AK')),
'length' => max(8, min(32, (int) ($s['card_pass_length'] ?? 16))),
'batch_size' => max(1, min(500, (int) ($s['card_pass_batch_size'] ?? 50))),
'max_redeem' => max(1, min(100, (int) ($s['card_pass_max_redeem'] ?? 1))),
'expiry_days' => max(0, (int) ($s['card_pass_expiry'] ?? 0)),
'amount' => max(1, min(9999, (int) ($s['card_pass_amount'] ?? 10))),
'vip_days' => max(1, min(3650, (int) ($s['card_pass_vip_days'] ?? 30))),
'credits' => max(0, min(99999, (int) ($s['card_pass_credits'] ?? 0))),
'redeem_page' => esc_url_raw((string) ($s['card_pass_redeem_page'] ?? '')),
'success_msg' => $success_msg !== '' ? $success_msg : __('兑换成功!', 'aksrt'),
'fail_msg' => $fail_msg !== '' ? $fail_msg : __('卡号无效或已过期', 'aksrt'),
];
}
/**
* @return array{min_amount:int,service_charge_rate:int,text:string}
*/
public static function get_withdrawal_settings(): array {
$s = Settings::get_all();
return [
'min_amount' => max(1, min(99999, (int) ($s['pay_rebate_withdraw_lowest_money'] ?? 50))),
'service_charge_rate' => max(0, min(50, (int) ($s['withdraw_service_charge'] ?? 0))),
'text' => sanitize_textarea_field((string) ($s['pay_rebate_withdraw_text_details'] ?? '')),
];
}
/**
* Whether a registered adapter is admin-enabled.
* Maps gateway ids (wechatpay) to schema toggles (payment_wechat_enabled).
*/
public static function is_gateway_enabled(string $gateway_id): bool {
$settings = Settings::get_all();
$map = [
'alipay' => 'payment_alipay_enabled',
'wechatpay' => 'payment_wechat_enabled',
'paypal' => 'payment_paypal_enabled',
'stripe' => 'payment_stripe_enabled',
'balance' => 'payment_balance_enabled',
'card_pass' => 'payment_card_pass_enabled',
'xunhupay' => 'payment_xunhupay_enabled',
'xhpay' => 'payment_xhpay_enabled',
'payjs' => 'payment_payjs_enabled',
'epay' => 'payment_epay_enabled',
'vmq' => 'payment_vmq_enabled',
'codepay' => 'payment_codepay_enabled',
];
$key = $map[$gateway_id] ?? '';
if ($key === '') {
return false;
}
if ($gateway_id === 'balance') {
return !empty($settings['payment_balance_enabled']) && !empty($settings['pay_balance_s']);
}
return !empty($settings[$key]);
}
/**
* @param string[] $fallback
* @return string[]
*/
private static function parse_slug_list(string $raw, array $fallback = []): array {
$items = preg_split('/[\s,]+/', $raw, -1, PREG_SPLIT_NO_EMPTY);
$normalized = [];
foreach ((array) $items as $item) {
$slug = sanitize_key((string) $item);
if ($slug !== '' && !in_array($slug, $normalized, true)) {
$normalized[] = $slug;
}
}
return !empty($normalized) ? $normalized : $fallback;
}
private static function count_structured_lines(string $raw): int {
$lines = preg_split('/[\r\n]+/', $raw, -1, PREG_SPLIT_NO_EMPTY);
$count = 0;
foreach ((array) $lines as $line) {
if (trim((string) $line) !== '') {
$count++;
}
}
return $count;
}
/**
* @param array<int,mixed> $products
*/
private static function format_product_lines(array $products): string {
$lines = [];
foreach ($products as $row) {
if (!is_array($row)) {
continue;
}
$price = $row['pay_price'] ?? $row['price'] ?? '';
$value = $row['value'] ?? $row['balance'] ?? $price;
$title = $row['title'] ?? $row['name'] ?? '';
$lines[] = $price . '|' . $value . '|' . $title;
}
return implode("\n", $lines);
}
}
+590
View File
@@ -0,0 +1,590 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class PostDisplay {
public static function get_post_list_style_settings(): array {
$settings = Settings::get_all();
// post_card_style storage key mirrors Zibll list_type.
$list_type = sanitize_key((string) ($settings['post_card_style'] ?? 'thumb'));
$allowed_list_types = ['thumb', 'compact', 'large_thumb', 'multi_thumb', 'text', 'thumb_if_has', 'card', 'standard'];
if (!in_array($list_type, $allowed_list_types, true)) {
$list_type = 'thumb';
}
// Historical aliases → Zibll list_type tokens.
if ($list_type === 'standard') {
$list_type = 'thumb';
}
// Non-Zibll extras stay list-mode variants (never force card grid).
if (in_array($list_type, ['compact', 'large_thumb', 'multi_thumb'], true)) {
// Keep token for CSS hooks; render as list.
}
// Zibll: only list_type=card is card mode; thumb/text/thumb_if_has are list.
$render_mode_map = [
'thumb' => 'list',
'compact' => 'list',
'large_thumb' => 'list',
'multi_thumb' => 'list',
'text' => 'list',
'thumb_if_has' => 'list',
'card' => 'card',
];
$sticky_pages_raw = $settings['sticky_s'] ?? null;
if (is_array($sticky_pages_raw)) {
$allowed_pages = ['home', 'cat', 'tag', 'topics'];
$sticky_pages = array_values(array_intersect(array_map('sanitize_key', $sticky_pages_raw), $allowed_pages));
} else {
$sticky_pages = ['home', 'cat'];
}
$badge_types_raw = $settings['list_badge_show'] ?? null;
if (is_array($badge_types_raw)) {
$allowed_types = ['pay', 'cat', 'topics', 'tag'];
$badge_types = array_values(array_intersect(array_map('sanitize_key', $badge_types_raw), $allowed_types));
} else {
$badge_types = ['pay', 'cat', 'topics', 'tag'];
}
$show_author = array_key_exists('post_list_author', $settings)
? !empty($settings['post_list_author'])
: (!isset($settings['post_show_author']) || !empty($settings['post_show_author']));
$show_date = array_key_exists('post_list_time', $settings)
? !empty($settings['post_list_time'])
: (!isset($settings['post_show_date']) || !empty($settings['post_show_date']));
$target_blank = array_key_exists('target_blank', $settings)
? !empty($settings['target_blank'])
: !empty($settings['post_target_blank']);
$ajax_trigger = '';
if (array_key_exists('ajax_trigger', $settings)) {
$ajax_trigger = sanitize_text_field((string) $settings['ajax_trigger']);
} elseif (array_key_exists('ajax_trigger_text', $settings)) {
$ajax_trigger = sanitize_text_field((string) $settings['ajax_trigger_text']);
}
$ajax_nomore = '';
if (array_key_exists('ajax_nomore', $settings)) {
$ajax_nomore = sanitize_text_field((string) $settings['ajax_nomore']);
} elseif (array_key_exists('ajax_nomore_text', $settings)) {
$ajax_nomore = sanitize_text_field((string) $settings['ajax_nomore_text']);
}
$list_style = sanitize_key((string) ($settings['list_list_style'] ?? ''));
if (!in_array($list_style, ['', 'style2'], true)) {
$list_style = '';
}
$card_variant = sanitize_key((string) ($settings['list_card_style'] ?? ''));
if (!in_array($card_variant, ['', 'style3'], true)) {
$card_variant = '';
}
$card_cat_ids = [];
if (is_array($settings['list_card_cat'] ?? null)) {
$card_cat_ids = array_values(array_filter(array_map('intval', $settings['list_card_cat'])));
}
return [
// Zibll list_type (storage: post_card_style).
'list_type' => $list_type,
// Base mode from list_type only (surface card overrides applied by resolver).
'render_mode' => $render_mode_map[$list_type] ?? 'list',
// List item subtype token for text/thumb_if_has class hooks (not card variant).
'list_type_token' => $list_type,
// List mode visual variant (Zibll list_list_option.style).
'list_style' => $list_style,
// Card mode visual variant (Zibll list_card_option.style).
'card_variant' => $card_variant,
// Back-compat aliases used by older callers (prefer list_type / card_variant).
'card_style' => $list_type,
'show_author' => $show_author,
'show_date' => $show_date,
'show_views' => !isset($settings['post_show_views']) || !empty($settings['post_show_views']),
'show_category' => !isset($settings['post_show_category']) || !empty($settings['post_show_category']),
'show_comments' => !isset($settings['post_meta_comments']) || !empty($settings['post_meta_comments']),
'show_likes' => !isset($settings['post_meta_likes']) || !empty($settings['post_meta_likes']),
'badge_types' => $badge_types,
'badge_show' => !empty($badge_types),
'sticky_pages' => $sticky_pages,
'sticky_enabled' => !empty($sticky_pages),
'target_blank' => $target_blank,
'heading_bold' => !empty($settings['item_heading_bold']),
'mobile_meta' => in_array(sanitize_key((string) ($settings['list_meta_show'] ?? 'like')), ['view', 'like', 'comm'], true) ? sanitize_key((string) ($settings['list_meta_show'] ?? 'like')) : 'like',
'list_show_type' => in_array(sanitize_key((string) ($settings['list_show_type'] ?? 'separate')), ['separate', 'no_margin'], true) ? sanitize_key((string) ($settings['list_show_type'] ?? 'separate')) : 'separate',
'excerpt_length' => max(20, min(500, (int) ($settings['post_excerpt_length'] ?? 120))),
'ias_max' => max(0, min(10, (int) ($settings['ias_max'] ?? 3))),
'paging_ajax' => self::is_list_paging_ajax($settings),
'paging_ajax_ias' => self::is_list_paging_ajax_ias($settings),
'ajax_trigger_text' => $ajax_trigger,
'ajax_nomore_text' => $ajax_nomore,
'list_orderby' => in_array(sanitize_key((string) ($settings['list_orderby'] ?? 'modified')), ['modified', 'date', 'default'], true) ? sanitize_key((string) ($settings['list_orderby'] ?? 'modified')) : 'modified',
'columns' => max(1, min(3, (int) ($settings['post_list_columns'] ?? 1))),
'per_page' => max(4, min(48, (int) ($settings['post_list_per_page'] ?? 12))),
'list_img_position' => in_array(sanitize_key((string) ($settings['list_list_img_position'] ?? 'left')), ['left', 'right'], true) ? sanitize_key((string) ($settings['list_list_img_position'] ?? 'left')) : 'left',
'list_scale' => max(50, min(200, (int) ($settings['list_list_scale'] ?? 70))),
'card_scale' => max(20, min(200, (int) ($settings['list_card_scale'] ?? 70))),
'card_home' => !empty($settings['list_card_home']),
'card_cat' => !empty($card_cat_ids),
'card_cat_ids' => $card_cat_ids,
'card_tag' => !empty($settings['list_card_tag']),
'card_topics' => !empty($settings['list_card_topics']),
'card_author' => !empty($settings['list_card_author']),
];
}
public static function get_post_thumb_settings(): array {
$settings = Settings::get_all();
$priority_raw = (string) ($settings['thumb_priority'] ?? 'featured,attachment,resource,custom');
$priority = preg_split('/[\s,]+/', strtolower($priority_raw)) ?: [];
$priority = array_values(array_unique(array_filter(array_map('sanitize_key', $priority))));
$allowed_sources = ['featured', 'attachment', 'resource', 'custom', 'category_cover', 'post_first_img'];
$priority = array_values(array_filter($priority, static function (string $source) use ($allowed_sources): bool {
return in_array($source, $allowed_sources, true);
}));
if (empty($priority)) {
$priority = ['featured', 'attachment', 'resource', 'custom'];
}
$fallback = sanitize_key((string) ($settings['thumb_fallback'] ?? 'placeholder'));
if (!in_array($fallback, ['placeholder', 'hide', 'random'], true)) {
$fallback = 'placeholder';
}
$aspect_ratio = (string) ($settings['thumb_aspect_ratio'] ?? '16:9');
if (!in_array($aspect_ratio, ['16:9', '4:3', '1:1', 'auto'], true)) {
$aspect_ratio = '16:9';
}
return [
'priority' => $priority,
'fallback' => $fallback,
'spare_images' => is_array($settings['thumb_spare_images'] ?? null) ? $settings['thumb_spare_images'] : [],
'aspect_ratio' => $aspect_ratio,
'multi_enabled' => !empty($settings['thumb_multi_enabled']),
'multi_count' => max(2, min(6, (int) ($settings['thumb_multi_count'] ?? 3))),
'lazy' => !isset($settings['thumb_lazy']) || !empty($settings['thumb_lazy']),
'fit' => in_array(sanitize_key((string) ($settings['list_thumb_fit'] ?? 'cover')), ['cover', 'contain'], true) ? sanitize_key((string) ($settings['list_thumb_fit'] ?? 'cover')) : 'cover',
'slides_s' => !isset($settings['list_thumb_slides_s']) || !empty($settings['list_thumb_slides_s']),
'video_s' => !isset($settings['list_thumb_video_s']) || !empty($settings['list_thumb_video_s']),
'video_mute_s' => !isset($settings['list_thumb_video_mute_s']) || !empty($settings['list_thumb_video_mute_s']),
'postfirstimg_s' => !isset($settings['thumb_postfirstimg_s']) || !empty($settings['thumb_postfirstimg_s']),
'catimg_s' => !isset($settings['thumb_catimg_s']) || !empty($settings['thumb_catimg_s']),
'postfirstimg_size'=> in_array(sanitize_key((string) ($settings['thumb_postfirstimg_size'] ?? 'medium')), ['thumbnail', 'medium', 'large', 'full'], true) ? sanitize_key((string) ($settings['thumb_postfirstimg_size'] ?? 'medium')) : 'medium',
'mult_thumb_cat_ids' => is_array($settings['mult_thumb_cat'] ?? null) ? array_map('intval', $settings['mult_thumb_cat']) : [],
];
}
public static function get_post_filter_settings(): array {
$settings = Settings::get_all();
return [
'enabled' => !empty($settings['filter_enabled']),
'categories' => !isset($settings['filter_categories']) || !empty($settings['filter_categories']),
'tags' => !empty($settings['filter_tags']),
'sort' => !isset($settings['filter_sort']) || !empty($settings['filter_sort']),
'ajax_enabled' => !empty($settings['filter_ajax_enabled']),
'cat_show_cat' => !isset($settings['filter_cat_show_cat']) || !empty($settings['filter_cat_show_cat']),
'cat_show_tag' => !empty($settings['filter_cat_show_tag']),
'tag_show_cat' => !isset($settings['filter_tag_show_cat']) || !empty($settings['filter_tag_show_cat']),
'tag_show_tag' => !empty($settings['filter_tag_show_tag']),
'topics_show_cat' => !isset($settings['filter_topics_show_cat']) || !empty($settings['filter_topics_show_cat']),
'topics_show_tag' => !empty($settings['filter_topics_show_tag']),
'custom_filter_show' => is_array($settings['custom_filter_show'] ?? null) ? array_map('sanitize_key', $settings['custom_filter_show']) : ['cat', 'tag'],
'custom_filter' => self::sanitize_custom_filter_defs($settings['custom_filter'] ?? []),
'cat_custom_filter' => self::sanitize_cat_custom_filter($settings['cat_custom_filter'] ?? []),
];
}
public static function get_filter_config_for_category(int $term_id): array {
$cfg = self::get_post_filter_settings();
foreach ($cfg['cat_custom_filter'] as $rule) {
if (in_array($term_id, $rule['cats'], true)) {
return $rule;
}
}
return [
'cats' => [],
'cat_s' => $cfg['cat_show_cat'],
'tag_s' => $cfg['cat_show_tag'],
'topics_s' => false,
'orderby_s' => $cfg['sort'],
'custom_filters' => [],
];
}
public static function get_post_page_settings(): array {
$settings = Settings::get_all();
// Canonical render keys (used by aksrt_single_*). Legacy *_s aliases kept for older callers.
$breadcrumbs = !isset($settings['breadcrumbs_single_s']) || !empty($settings['breadcrumbs_single_s']);
$breadcrumbs_home = sanitize_text_field((string) ($settings['breadcrumbs_home_text'] ?? '首页'));
// Zibll breadcrumbs_single_text: true → last crumb is "正文"; false → full title.
$breadcrumbs_single_text = !isset($settings['breadcrumbs_single_text']) || !empty($settings['breadcrumbs_single_text']);
$prevnext = !isset($settings['post_prevnext_s']) || !empty($settings['post_prevnext_s']);
$authordesc = !isset($settings['post_authordesc_s']) || !empty($settings['post_authordesc_s']);
$hide_time = !empty($settings['post_single_hide_time_s']);
$share_enabled = !isset($settings['share_s']) || !empty($settings['share_s']);
$rewards_enabled = !empty($settings['post_rewards_s']) || !empty($settings['post_rewards_enabled']);
$rewards_text = sanitize_text_field((string) ($settings['post_rewards_text'] ?? ''));
if ($rewards_text === '') {
$rewards_text = '赞赏';
}
$author_card_limit = max(0, min(12, (int) ($settings['post_author_card_limit'] ?? 6)));
$author_card_orderby = sanitize_key((string) ($settings['post_author_card_orderby'] ?? 'views'));
if (!in_array($author_card_orderby, ['views', 'date', 'modified', 'comment_count'], true)) {
$author_card_orderby = 'views';
}
$author_card_style = sanitize_key((string) ($settings['post_author_card_style'] ?? 'card'));
if (!in_array($author_card_style, ['card', 'list'], true)) {
$author_card_style = 'card';
}
$maxheight_enabled = !empty($settings['article_maxheight_kg'])
|| !empty($settings['post_article_maxheight_kg'])
|| !empty($settings['post_maxheight_kg']);
$maxheight = max(0, (int) ($settings['article_maxheight'] ?? $settings['post_article_maxheight'] ?? $settings['post_maxheight'] ?? 1000));
if ($maxheight <= 0) {
$maxheight = 1000;
}
// Related: split Zibll display style (img/list/text) from query source (category/tag/random).
// Legacy post_related_type held either style (img|list|text) or source (category|tag|random).
$raw_related_type = sanitize_key((string) ($settings['post_related_type'] ?? ''));
$raw_related_style = sanitize_key((string) ($settings['post_related_style'] ?? ''));
$raw_related_source = sanitize_key((string) ($settings['post_related_source'] ?? ''));
$related_style = 'img';
if (in_array($raw_related_style, ['img', 'list', 'text'], true)) {
$related_style = $raw_related_style;
} elseif (in_array($raw_related_type, ['img', 'list', 'text'], true)) {
$related_style = $raw_related_type;
}
$related_source = 'category';
if (in_array($raw_related_source, ['category', 'tag', 'random'], true)) {
$related_source = $raw_related_source;
} elseif (in_array($raw_related_type, ['category', 'tag', 'random'], true)) {
$related_source = $raw_related_type;
}
$related_orderby = sanitize_key((string) ($settings['post_related_orderby'] ?? 'views'));
if (!in_array($related_orderby, ['rand', 'relevance', 'date', 'views', 'modified'], true)) {
$related_orderby = 'views';
}
return [
'toc_enabled' => !isset($settings['post_toc_enabled']) || !empty($settings['post_toc_enabled']),
'toc_depth' => max(1, min(6, (int) ($settings['post_toc_depth'] ?? 3))),
'article_nav_mobile_nav_s' => !isset($settings['article_nav_mobile_nav_s']) || !empty($settings['article_nav_mobile_nav_s']),
'mobile_nav_s' => !isset($settings['article_nav_mobile_nav_s']) || !empty($settings['article_nav_mobile_nav_s']),
'reading_progress' => !isset($settings['post_reading_progress']) || !empty($settings['post_reading_progress']),
// Canonical + legacy breadcrumbs.
'breadcrumbs' => $breadcrumbs,
'breadcrumbs_home' => $breadcrumbs_home,
'breadcrumbs_single_s' => $breadcrumbs,
'breadcrumbs_home_text' => $breadcrumbs_home,
'breadcrumbs_single_text' => $breadcrumbs_single_text,
// Canonical + legacy prev/next / author / time.
'prevnext' => $prevnext,
'prevnext_s' => $prevnext,
'authordesc' => $authordesc,
'authordesc_s' => $authordesc,
'hide_time' => $hide_time,
'single_hide_time_s' => $hide_time,
'article_image_cover' => !isset($settings['post_article_image_cover']) || !empty($settings['post_article_image_cover']),
'article_image_cover_scale'=> max(10, min(100, (int) ($settings['post_article_image_cover_scale'] ?? 35))),
'article_slide_cover' => !isset($settings['post_article_slide_cover']) || !empty($settings['post_article_slide_cover']),
'article_video_cover' => !isset($settings['post_article_video_cover']) || !empty($settings['post_article_video_cover']),
'article_video_cover_scale'=> max(20, min(100, (int) ($settings['post_article_video_cover_scale'] ?? 56))),
'lightbox' => !isset($settings['post_lightbox']) || !empty($settings['post_lightbox']),
'lightbox_thumbs' => !isset($settings['post_lightbox_thumbs']) || !empty($settings['post_lightbox_thumbs']),
'lightbox_zoom' => !isset($settings['post_lightbox_zoom']) || !empty($settings['post_lightbox_zoom']),
'lightbox_full' => !isset($settings['post_lightbox_full']) || !empty($settings['post_lightbox_full']),
'lightbox_down' => !empty($settings['post_lightbox_down']),
'lightbox_play' => !empty($settings['post_lightbox_play']),
'related_enabled' => !isset($settings['post_related_enabled']) || !empty($settings['post_related_enabled']),
'related_count' => max(1, min(20, (int) ($settings['post_related_count'] ?? 6))),
// Display style (Zibll img/list/text) + query source (category/tag/random).
'related_style' => $related_style,
'related_source' => $related_source,
// Legacy alias: older code treated related_type as source.
'related_type' => $related_source,
'related_orderby' => $related_orderby,
'related_title' => sanitize_text_field((string) ($settings['post_related_title'] ?? '相关推荐')),
'like_enabled' => !isset($settings['post_like_enabled']) || !empty($settings['post_like_enabled']),
'favorite_enabled' => !isset($settings['post_favorite_enabled']) || !empty($settings['post_favorite_enabled']),
// Canonical + legacy share.
'share_enabled' => $share_enabled,
'share_s' => $share_enabled,
// Author card posts (Zibll user_card_box defaults).
'author_card_limit' => $author_card_limit,
'author_card_orderby' => $author_card_orderby,
'author_card_style' => $author_card_style,
// Rewards (Zibll post_rewards_s) — per-user QR only.
'rewards_enabled' => $rewards_enabled,
'post_rewards_s' => $rewards_enabled,
'rewards_text' => $rewards_text,
'rewards_btn_text' => $rewards_text,
// Content max height (Zibll article_maxheight_kg / article_maxheight).
'maxheight_enabled' => $maxheight_enabled,
'maxheight' => $maxheight,
'copyright_enabled' => !isset($settings['post_copyright_enabled']) || !empty($settings['post_copyright_enabled']),
'copyright_text' => (string) ($settings['post_copyright_text'] ?? ''),
'button_toptext' => sanitize_text_field((string) ($settings['post_button_toptext'] ?? '喜欢就支持一下吧')),
'front_content' => (string) ($settings['post_front_content'] ?? ''),
'after_content' => (string) ($settings['post_after_content'] ?? ''),
'yiyan_header' => !empty($settings['post_yiyan_header']),
'yiyan_footer' => !empty($settings['post_yiyan_footer']),
'yiyan_box' => !empty($settings['post_yiyan_box']),
'default_views_min' => max(0, min(9999, (int) ($settings['post_default_views_min'] ?? 20))),
'default_views_max' => max(1, min(9999, (int) ($settings['post_default_views_max'] ?? 50))),
'default_likes_min' => max(0, min(9999, (int) ($settings['post_default_likes_min'] ?? 5))),
'default_likes_max' => max(1, min(9999, (int) ($settings['post_default_likes_max'] ?? 15))),
'word_count_enabled' => !isset($settings['post_word_count_enabled']) || !empty($settings['post_word_count_enabled']),
'reading_time_enabled' => !isset($settings['post_reading_time_enabled']) || !empty($settings['post_reading_time_enabled']),
'print_enabled' => !isset($settings['feature_print']) || !empty($settings['feature_print']),
'pdf_export_enabled' => !empty($settings['feature_pdf_export']),
];
}
public static function get_share_settings(): array {
$settings = Settings::get_all();
return [
'enabled' => !isset($settings['share_s']) || !empty($settings['share_s']),
'wechat_appid' => sanitize_text_field((string) ($settings['wechat_share_appid'] ?? '')),
'wechat_timestamp' => sanitize_text_field((string) ($settings['wechat_share_timestamp'] ?? '')),
'wechat_nonce' => sanitize_text_field((string) ($settings['wechat_share_nonce'] ?? '')),
'wechat_signature' => sanitize_text_field((string) ($settings['wechat_share_signature'] ?? '')),
'poster_enabled' => !empty($settings['post_share_poster_s']),
'poster_qr_enabled' => !empty($settings['post_share_poster_qr_s']),
'poster_bg' => (string) ($settings['post_share_poster_bg'] ?? ''),
'social_items' => is_array($settings['social_share_items'] ?? null) ? $settings['social_share_items'] : [],
'copy_link_enabled' => !isset($settings['post_share_copylink_s']) || !empty($settings['post_share_copylink_s']),
];
}
public static function get_submit_settings(): array {
$settings = Settings::get_all();
return [
'enabled' => !isset($settings['front_submit_enabled']) || !empty($settings['front_submit_enabled']),
'btn_text' => sanitize_text_field((string) ($settings['post_article_btn_txte'] ?? '')) ?: __('发布文章', 'aksrt'),
'title_min' => max(1, min(100, (int) ($settings['post_article_title_strlen_limit_min'] ?? 5))),
'title_max' => max(5, min(200, (int) ($settings['post_article_title_strlen_limit_max'] ?? 30))),
'title_placeholder' => sanitize_text_field((string) ($settings['post_t_placeholder'] ?? '')) ?: __('请输入标题', 'aksrt'),
'content_placeholder' => sanitize_textarea_field((string) ($settings['post_c_placeholder'] ?? '')) ?: __('请输入内容', 'aksrt'),
'allowed_cat_ids' => is_array($settings['post_article_cat'] ?? null) ? array_map('intval', $settings['post_article_cat']) : [],
'default_user_id' => max(0, (int) ($settings['post_article_user'] ?? 0)),
'pay_free_s' => !isset($settings['post_article_pay_free_s']) || !empty($settings['post_article_pay_free_s']),
'pay_price_min' => max(0, (int) ($settings['post_article_pay_price_min'] ?? 0)),
'pay_price_max' => max(0, (int) ($settings['post_article_pay_price_max'] ?? 100)),
'pay_vip_price_s' => !isset($settings['post_article_pay_vip_price_s']) || !empty($settings['post_article_pay_vip_price_s']),
'pay_vip_1_discount' => max(0, min(100, (int) ($settings['post_article_pay_vip_1_discount'] ?? 80))),
'pay_vip_2_discount' => max(0, min(100, (int) ($settings['post_article_pay_vip_2_discount'] ?? 50))),
'topics_s' => !empty($settings['post_article_topics_s']),
'custom_filter_s' => !isset($settings['post_article_custom_filter_s']) || !empty($settings['post_article_custom_filter_s']),
];
}
public static function get_comment_display_settings(): array {
$settings = Settings::get_all();
$order = sanitize_key((string) ($settings['comment_order'] ?? 'desc'));
if (!in_array($order, ['asc', 'desc'], true)) {
$order = 'desc';
}
$empty_text = trim((string) ($settings['comment_empty_text'] ?? ''));
$submit_text = trim((string) ($settings['comment_submit_text'] ?? ''));
$box_placeholder = trim((string) ($settings['comment_text'] ?? ''));
$title = trim((string) ($settings['comment_title'] ?? ''));
return [
'order' => $order,
'pagination_mode' => self::get_comment_pagination_mode(),
'paging_ajax_ias' => !isset($settings['comment_paging_ajax_ias_s']) || !empty($settings['comment_paging_ajax_ias_s']),
'paging_ajax_ias_max'=> max(0, min(10, (int) ($settings['comment_paging_ajax_ias_max'] ?? 3))),
'per_page' => max(5, min(100, (int) ($settings['comment_per_page'] ?? 20))),
'nested_depth' => max(1, min(10, (int) ($settings['comment_nested_depth'] ?? 3))),
'require_login' => Settings::comment_requires_registration(),
'allow_reply' => !isset($settings['comment_allow_reply']) || !empty($settings['comment_allow_reply']),
'avatar_size' => max(24, min(96, (int) ($settings['comment_avatar_size'] ?? 48))),
'empty_text' => $empty_text !== '' ? $empty_text : __('暂无评论,来做第一个评论的人吧~', 'aksrt'),
'toolbar' => [
'smilie' => !isset($settings['comment_smilie']) || !empty($settings['comment_smilie']),
'code' => !isset($settings['comment_code']) || !empty($settings['comment_code']),
'image' => !isset($settings['comment_img']) || !empty($settings['comment_img']),
'upload_img' => !empty($settings['comment_upload_img']),
],
'like_enabled' => !isset($settings['comment_like_s']) || !empty($settings['comment_like_s']),
'author_tag' => !isset($settings['comment_author_tag']) || !empty($settings['comment_author_tag']),
'corderby' => !isset($settings['comment_corderby']) || !empty($settings['comment_corderby']),
'edit_window' => max(0, min(1440, (int) ($settings['comment_edit_window'] ?? 10))),
'submit_text' => $submit_text !== '' ? $submit_text : __('提交评论', 'aksrt'),
'box_placeholder' => $box_placeholder !== '' ? $box_placeholder : __('说点什么吧...', 'aksrt'),
'title' => $title !== '' ? $title : __('评论', 'aksrt'),
];
}
public static function apply_list_orderby($query): void {
if (is_admin() || !($query instanceof \WP_Query) || !$query->is_main_query()) {
return;
}
if (!$query->is_home() && !$query->is_archive() && !$query->is_search()) {
return;
}
if ($query->get('orderby')) {
return;
}
$orderby = sanitize_key((string) (self::get_post_list_style_settings()['list_orderby'] ?? 'modified'));
if ($orderby === '' || $orderby === 'default') {
return;
}
if (in_array($orderby, ['date', 'modified'], true)) {
$query->set('orderby', $orderby);
$query->set('order', 'DESC');
}
}
public static function apply_list_per_page($query): void {
if (is_admin() || !$query->is_main_query()) {
return;
}
if (!$query->is_archive() && !$query->is_search() && !$query->is_home()) {
return;
}
$s = Settings::get_all();
$per_page = max(4, min(48, (int) ($s['post_list_per_page'] ?? 12)));
if ($per_page !== 12) {
$query->set('posts_per_page', $per_page);
}
}
public static function get_archive_layout(): string {
$settings = self::get_post_list_style_settings();
return $settings['render_mode'];
}
public static function get_post_pagination_mode(): string {
$settings = Settings::get_all();
// Zibll: paging_ajax_s '1' = ajax append, '0' = numeric buttons.
if (array_key_exists('paging_ajax_s', $settings)) {
$ajax = self::is_list_paging_ajax($settings);
if (!$ajax) {
return 'numbered';
}
return self::is_list_paging_ajax_ias($settings) ? 'infinite' : 'load_more';
}
// Temporary bridge for installs still holding the previous schema key only.
$mode = (string) ($settings['post_pagination_mode'] ?? 'numbered');
if ($mode === 'numeric') {
$mode = 'numbered';
}
return in_array($mode, ['numbered', 'load_more', 'infinite'], true) ? $mode : 'numbered';
}
/**
* Whether list pagination uses AJAX append (Zibll paging_ajax_s).
*
* @param array<string,mixed>|null $settings
*/
public static function is_list_paging_ajax(?array $settings = null): bool {
$settings = $settings ?? Settings::get_all();
if (!array_key_exists('paging_ajax_s', $settings)) {
// Fallback from previous 3-mode key.
$mode = (string) ($settings['post_pagination_mode'] ?? 'numbered');
return in_array($mode, ['load_more', 'infinite', 'ajax'], true);
}
$raw = $settings['paging_ajax_s'];
if (is_bool($raw)) {
return $raw;
}
return (string) $raw === '1' || (string) $raw === 'true';
}
/**
* Whether AJAX list paging auto-loads on scroll (Zibll paging_ajax_ias_s).
*
* @param array<string,mixed>|null $settings
*/
public static function is_list_paging_ajax_ias(?array $settings = null): bool {
$settings = $settings ?? Settings::get_all();
if (!self::is_list_paging_ajax($settings)) {
return false;
}
if (!array_key_exists('paging_ajax_ias_s', $settings)) {
$mode = (string) ($settings['post_pagination_mode'] ?? '');
return $mode === 'infinite';
}
return !empty($settings['paging_ajax_ias_s']);
}
public static function get_comment_pagination_mode(): string {
$mode = (string) Settings::get_value('comment_pagination_type', 'numbered');
if ($mode === 'page') {
$mode = 'numbered';
}
return in_array($mode, ['numbered', 'load_more', 'infinite'], true) ? $mode : 'numbered';
}
private static function sanitize_custom_filter_defs($raw): array {
if (!is_array($raw)) {
return [];
}
$result = [];
foreach ($raw as $item) {
if (!is_array($item)) continue;
$name = sanitize_text_field((string) ($item['name'] ?? ''));
$key = sanitize_key((string) ($item['key'] ?? ''));
if ($name === '' || $key === '') continue;
$vals = [];
foreach ((array) ($item['vals'] ?? []) as $val) {
if (!is_array($val)) continue;
$vkey = sanitize_key((string) ($val['key'] ?? ''));
$vname = sanitize_text_field((string) ($val['name'] ?? ''));
if ($vkey === '' || $vname === '') continue;
$vals[] = ['key' => $vkey, 'name' => $vname];
}
$result[] = ['name' => $name, 'key' => $key, 'vals' => $vals];
}
return $result;
}
private static function sanitize_cat_custom_filter($raw): array {
if (!is_array($raw)) {
return [];
}
$result = [];
foreach ($raw as $item) {
if (!is_array($item)) continue;
$cats = is_array($item['cats'] ?? null) ? array_values(array_filter(array_map('absint', $item['cats']), static fn($id) => $id > 0)) : [];
if (empty($cats)) continue;
$filters_raw = trim((string) ($item['custom_filters'] ?? ''));
$filters = $filters_raw === '' ? [] : array_values(array_filter(array_map('sanitize_key', preg_split('/[,,\s]+/', $filters_raw) ?: [])));
$result[] = [
'cats' => $cats,
'cat_s' => !isset($item['cat_s']) || !empty($item['cat_s']),
'tag_s' => !empty($item['tag_s']),
'topics_s' => !empty($item['topics_s']),
'orderby_s' => !isset($item['orderby_s']) || !empty($item['orderby_s']),
'custom_filters' => $filters,
];
}
return $result;
}
}
+922
View File
@@ -0,0 +1,922 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class Settings {
use DomainGroups;
use DisplayPageGroups;
use CreditsPaymentGroups;
use CategoryIntegrationGroups;
use AuthReader;
use HomepageReader;
private static $instance = null;
use FacadeBulk;
private function __construct() {}
public static function get_instance(): self {
if (null === self::$instance) self::$instance = new self();
return self::$instance;
}
public function init(): void {
// 注册设置API(可选)
add_action('customize_register', [$this, 'customizer']);
add_filter('get_site_icon_url', [self::class, 'filter_site_icon_url'], 10, 3);
add_action('init', [self::class, 'maybe_sync_native_site_icon'], 20);
// 搜索结果排除(search_no_page / search_exclude_cats)
add_action('pre_get_posts', [self::class, 'filter_search_query']);
// 全局列表默认排序(list_orderby)
add_action('pre_get_posts', [self::class, 'apply_list_orderby']);
// 全局列表每页数量(post_list_per_page)
add_action('pre_get_posts', [self::class, 'apply_list_per_page']);
// 文章发布/更新/删除时清理分类推荐缓存
add_action('save_post', [self::class, 'on_post_changed']);
add_action('delete_post', [self::class, 'on_post_changed']);
// 设置变更时清理 per-request memo
add_action('updated_option', [self::class, 'on_option_updated'], 10, 1);
add_action('added_option', [self::class, 'on_option_updated'], 10, 1);
add_action('deleted_option', [self::class, 'on_option_updated'], 10, 1);
}
/**
* Option 变更时失效缓存(响应所有主题相关 option key)。
*/
public static function on_option_updated(string $option_name): void {
$tracked = array_merge(
['aksrt_settings'],
self::STANDALONE_OPTION_KEYS
);
if (in_array($option_name, $tracked, true)) {
self::$cached_all = null;
}
}
/**
* 文章变更时清理分类推荐缓存
*/
public static function on_post_changed(int $post_id): void {
if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) return;
$post = get_post($post_id);
if (!$post || $post->post_type !== 'post') return;
self::flush_category_recommend_cache();
}
// ============================================
// 设置读取方法
// ============================================
/** 获取全部设置(per-request memo) */
public static function get_all(): array {
if (self::$cached_all !== null) {
return self::$cached_all;
}
// Canonical theme settings live only in aksrt_settings.
// Operational configs (captcha/moderation/oauth/footer/etc.) stay standalone.
$settings = get_option('aksrt_settings', []);
if (!is_array($settings)) {
$settings = [];
}
self::$cached_all = $settings;
return $settings;
}
/**
* 显式刷新缓存(外部直接修改 option 后可调用)。
*/
public static function flush_settings_cache(): void {
self::$cached_all = null;
}
/**
* 读取指定 domain/section 的配置(委托 Schema Provider)。
*
* @param string $domain 域名 (global, page_display, post_list, ...)
* @param string $section 分区名 (可为空,读取整个 domain)
* @return array
*/
public static function read_domain(string $domain, string $section = ''): array {
$provider = \AKSRT\Admin\SchemaProvider::get_instance();
return $provider->read_settings($domain, $section);
}
/**
* 写入指定 domain/section 的配置(委托 Schema Provider)。
*
* 仅写入当前 section allowlist 中的字段,不会覆盖同 option 中其它 section 的字段。
* 写入后自动刷新 memo cache 并触发 action。
*
* @param string $domain 域名
* @param string $section 分区名
* @param array $data 要写入的数据
* @return bool
*/
public static function write_domain(string $domain, string $section, array $data): bool {
$provider = \AKSRT\Admin\SchemaProvider::get_instance();
$result = $provider->write_settings($domain, $data, $section);
if ($result) {
self::flush_settings_cache();
$option_key = $provider->get_option_key($domain, $section);
do_action('aksrt_settings_domain_saved', $domain, $section, $option_key);
}
return $result;
}
/**
* 获取指定 domain/section 的默认值。
*/
public static function get_domain_defaults(string $domain, string $section = ''): array {
$provider = \AKSRT\Admin\SchemaProvider::get_instance();
return $provider->get_defaults($domain, $section);
}
/**
* 重置指定 domain/section 到默认值。
*/
public static function reset_domain(string $domain, string $section = ''): bool {
$provider = \AKSRT\Admin\SchemaProvider::get_instance();
$result = $provider->reset_to_defaults($domain, $section);
if ($result) {
self::flush_settings_cache();
do_action('aksrt_settings_domain_reset', $domain, $section);
}
return $result;
}
public static function get_value(string $key, $default = null) {
$settings = self::get_all();
return array_key_exists($key, $settings) ? $settings[$key] : $default;
}
/** 获取搜索框占位文案 */
public static function search_placeholder(): string {
return ThemeSearch::search_placeholder();
}
/** 获取搜索模式 */
public static function search_mode(): string {
return ThemeSearch::search_mode();
}
/** 是否启用搜索历史 */
public static function search_history_enabled(): bool {
return ThemeSearch::search_history_enabled();
}
/** 是否启用热门搜索 */
public static function hot_search_enabled(): bool {
return ThemeSearch::hot_search_enabled();
}
/** 获取热门搜索关键词 */
public static function hot_search_keywords(): array {
return ThemeSearch::hot_search_keywords();
}
/** 热门搜索标题(留空使用默认) */
public static function search_popular_title(): string {
return ThemeSearch::search_popular_title();
}
/** 置顶搜索词(始终显示在最前面) */
public static function search_popular_sticky(): array {
return ThemeSearch::search_popular_sticky();
}
/** 是否启用搜索建议 */
public static function search_suggestions_enabled(): bool {
return ThemeSearch::search_suggestions_enabled();
}
/** 是否在搜索面板显示热门文章 */
public static function search_posts_enabled(): bool {
return ThemeSearch::search_posts_enabled();
}
/** 搜索结果是否排除页面 */
public static function search_no_page(): bool {
return ThemeSearch::search_no_page();
}
/** 获取搜索排除分类 ID 列表 */
public static function search_exclude_cats(): array {
return ThemeSearch::search_exclude_cats();
}
/** 是否启用分类搜索选择器 */
public static function search_cat_enabled(): bool {
return ThemeSearch::search_cat_enabled();
}
/** 获取默认搜索分类 */
public static function search_cat_in(): string {
return ThemeSearch::search_cat_in();
}
/** 是否显示更多分类 */
public static function search_more_cat_obj(): bool {
return ThemeSearch::search_more_cat_obj();
}
/** 是否启用自定义搜索匹配范围 */
public static function search_custom_key_enabled(): bool {
return ThemeSearch::search_custom_key_enabled();
}
/** 获取自定义搜索匹配字段列表 */
public static function search_custom_key(): array {
return ThemeSearch::search_custom_key();
}
/** 是否启用搜索结果 taxonomy 筛选 */
public static function search_facets_enabled(): bool {
return ThemeSearch::search_facets_enabled();
}
/** 获取搜索结果筛选 taxonomy 排序列表 */
public static function search_facets_order(): array {
return ThemeSearch::search_facets_order();
}
/**
* Meilisearch integration configuration.
*
* @return array{enabled:bool,api_url:string,api_key:string,index_prefix:string,synonyms:array}
*/
public static function get_meilisearch_config(): array {
return ThemeSearch::get_meilisearch_config();
}
/**
* pre_get_posts 过滤:应用搜索排除配置
* - search_no_page: 排除页面
* - search_exclude_cats: 排除指定分类
*/
public static function filter_search_query($query): void {
ThemeSearch::filter_search_query($query);
}
/**
* pre_get_posts: apply global list_orderby on archive/tag/category/search pages.
*
* Only fires when no explicit orderby is set by URL params or other hooks.
*/
public static function apply_list_orderby($query): void {
PostDisplay::apply_list_orderby($query);
}
/**
* 获取搜索内容类型配置
*
* @return array{types: string[], default: string}
*/
public static function get_search_content_types(): array {
return ThemeSearch::get_search_content_types();
}
/** 默认主题模式 (white-theme / dark-theme / time-auto) */
public static function theme_mode(): string {
return ThemeSearch::theme_mode();
}
/** 主题模式 → JS 可消费的简化值 (light / dark / time-auto) */
public static function theme_mode_js_value(): string {
return ThemeSearch::theme_mode_js_value();
}
/** time-auto:对齐 zibll 早晚 8 点自动切换语义。 */
public static function time_auto_theme_mode(): string {
return ThemeSearch::time_auto_theme_mode();
}
/** 首屏主题模式:优先消费 zibll 兼容 cookie,回退后台默认值。 */
public static function initial_theme_mode(): string {
return ThemeSearch::initial_theme_mode();
}
/** 获取主题色 */
public static function primary_color(): string {
return ThemeSearch::primary_color();
}
/** 获取主题皮肤预设 hex(解析 palette 选项为 #hex 格式) */
public static function theme_skin(): string {
return ThemeSearch::theme_skin();
}
/** 获取暗色模式独立主题色 */
public static function dark_primary_color(): string {
return ThemeSearch::dark_primary_color();
}
/** 获取主题切换按钮显示位置列表 */
public static function get_theme_mode_button_positions(): array {
return ThemeSearch::get_theme_mode_button_positions();
}
/** 获取最终生效的主题色(skin 优先于 custom primary_color) */
public static function resolved_primary_color(): string {
return ThemeSearch::resolved_primary_color();
}
/** 获取全局圆角 */
public static function border_radius(): int {
return ThemeSearch::border_radius();
}
/** 获取卡片圆角值(独立设置优先,否则 border_radius + 4) */
public static function card_radius(): int {
return ThemeSearch::card_radius();
}
/** 全站加载动画开关 */
public static function is_qj_loading_enabled(): bool {
return ThemeSearch::is_qj_loading_enabled();
}
/** 加载动画样式 (no1–no10) */
public static function get_qj_loading_style(): string {
return ThemeSearch::get_qj_loading_style();
}
/** 获取内容最大宽度 */
public static function container_max_width(): int {
return ThemeSearch::container_max_width();
}
/** 获取全局字体方案 */
public static function font_family(): string {
return ThemeSearch::font_family();
}
/**
* 获取代码高亮配置
*
* @return array
*/
public static function get_code_highlight_config(): array {
return ThemeSearch::get_code_highlight_config();
}
/**
* 获取系统公告配置
*
* @return array
*/
public static function get_system_notice_config(): array {
return ThemeSearch::get_system_notice_config();
}
/**
* 获取侧边栏配置
*
* @return array
*/
public static function get_sidebar_config(): array {
return ThemeSearch::get_sidebar_config();
}
/**
* 获取多语言翻译配置
*
* @return array
*/
public static function get_i18n_config(): array {
return ThemeSearch::get_i18n_config();
}
/**
* Public user-home display settings.
*
* @return array
*/
public static function get_user_home_settings(): array {
return UserRuntime::get_user_home_settings();
}
/**
* Private user-center display settings.
*
* @return array{tabs:string[],avatar_upload:bool,cover_upload:bool,social_links:bool}
*/
public static function get_user_center_settings(): array {
return UserRuntime::get_user_center_settings();
}
/**
* User identity, level, badge, and report governance settings.
*
* @return array{
* verify:array{enabled:bool,public_display:bool,badge_text:string,accept_email_verified:bool,require_manual_review:bool},
* level:array{enabled:bool,public_display:bool,source:string,label_prefix:string,thresholds:array<int,int>},
* badge:array{enabled:bool,public_display:bool,limit:int,dictionary:array<int,array{slug:string,label:string,tone:string}>},
* report:array{enabled:bool,public_entry:bool,public_notice:bool,ban_notice_text:string,categories:array<int,array{slug:string,label:string}>}
* }
*/
public static function get_user_identity_settings(): array {
return UserRuntime::get_user_identity_settings();
}
/**
* Post-list card style and meta display settings.
*
* @return array{card_style:string,render_mode:string,show_author:bool,show_date:bool,show_views:bool,show_category:bool,show_comments:bool,show_likes:bool,excerpt_length:int}
*/
public static function get_post_list_style_settings(): array {
return PostDisplay::get_post_list_style_settings();
}
/**
* Post-list thumbnail policy settings.
*
* @return array{priority:string[],fallback:string,aspect_ratio:string,multi_enabled:bool,multi_count:int,lazy:bool}
*/
public static function get_post_thumb_settings(): array {
return PostDisplay::get_post_thumb_settings();
}
/**
* Post-list filter toolbar settings.
*
* @return array{enabled:bool,categories:bool,tags:bool,sort:bool}
*/
public static function get_post_filter_settings(): array {
return PostDisplay::get_post_filter_settings();
}
/**
* Get custom filter definitions for a specific category.
* Finds the matching cat_custom_filter binding, falls back to global custom_filter.
*
* @param int $cat_id Category term ID.
* @return array{dimensions: array, show_cat: bool, show_tag: bool, show_topics: bool, show_orderby: bool}
*/
public static function get_filter_config_for_category(int $cat_id): array {
return PostDisplay::get_filter_config_for_category($cat_id);
}
/**
* Front submit settings.
*
* @return array{enabled:bool,btn_text:string,title_min:int,title_max:int}
*/
public static function get_submit_settings(): array {
return PostDisplay::get_submit_settings();
}
/**
* Comment display and behavior settings.
*
* @return array{order:string,pagination_mode:string,nested_depth:int,require_login:bool,allow_reply:bool,avatar_size:int,empty_text:string}
*/
public static function get_comment_display_settings(): array {
return PostDisplay::get_comment_display_settings();
}
public static function get_withdrawal_settings(): array {
return PaymentRuntime::get_withdrawal_settings();
}
/**
* pre_get_posts: apply post_list_per_page on archive/tag/category/search/home pages.
*
* Overrides WP Settings > Reading "posts per page" for list views.
*/
public static function apply_list_per_page($query): void {
PostDisplay::apply_list_per_page($query);
}
/** 获取Banner列表 */
public static function get_banners(string $position = ''): array {
return DisplayRuntime::get_banners($position);
}
/** 保存Banner */
public static function save_banner(array $data): int {
return DisplayRuntime::save_banner($data);
}
/** 删除Banner */
public static function delete_banner(string $id): void {
DisplayRuntime::delete_banner($id);
}
/** 获取友链列表 */
public static function get_friend_links(): array {
return DisplayRuntime::get_friend_links();
}
/** 保存友链 */
public static function save_friend_links(array $links): void {
DisplayRuntime::save_friend_links($links);
}
/** 私信邮件接收组限制 (all/vip/vip2/admin) */
public static function get_email_private_receive_limit(): string {
return Integration::get_email_private_receive_limit();
}
/** 获取OAuth配置 */
public static function get_oauth(): array {
return Integration::get_oauth();
}
/** 检查OAuth平台是否已配置 */
public static function oauth_configured(string $provider): bool {
return Integration::oauth_configured($provider);
}
// ============================================
// OAuth 代理登录 & 自动创建用户
// ============================================
/** OAuth 代理登录模式: 'off' | 'server' | 'client' (schema 存 'close'→映射为 'off') */
public static function oauth_proxy_mode(): string {
return Integration::oauth_proxy_mode();
}
/** OAuth 代理登录 HMAC 密钥 */
public static function oauth_proxy_secret(): string {
return Integration::oauth_proxy_secret();
}
/** OAuth 代理登录服务端地址(客户端模式使用) */
public static function oauth_proxy_server_url(): string {
return Integration::oauth_proxy_server_url();
}
/** OAuth 代理登录允许的平台列表(服务端模式使用) */
public static function oauth_proxy_allowed_providers(): array {
return Integration::oauth_proxy_allowed_providers();
}
/** OAuth 代理登录启用的平台列表(客户端模式使用) */
public static function oauth_proxy_client_providers(): array {
return Integration::oauth_proxy_client_providers();
}
/**
* 获取微信公众号登录完整配置
*
* @return array{gzh_type:string,token:string,token_nocheck:bool,encoding_aes_key:string,appid:string,appsecret:string,code_qrcode:string,code_keyword:string,code_reply:string,code_subscribe_reply:string,subscribe_msg:string,new_subscribe_msg:string,scan_msg:string,auto_reply_text:string,auto_reply_search:string,auto_reply_image:string,auto_reply_voice:string,auto_reply_default:string,gzh_menu:string,priority:bool,auto:bool,auto_expires:int}
*/
public static function get_weixingzh_settings(): array {
return Integration::get_weixingzh_settings();
}
/**
* 获取 OAuth 代理登录完整配置(aksrt_settings 优先,BC fallback aksrt_oauth)
*
* @return array{mode:string,secret:string,server_url:string,server_providers:array,client_providers:array,gzh_type:string}
*/
public static function get_oauth_proxy_settings(): array {
return Integration::get_oauth_proxy_settings();
}
// ============================================
// 底部信息配置
// ============================================
/**
* 获取底部信息完整配置(统一 aksrt_footer_settings + aksrt_settings page-display footer 字段)
* @return array
*/
public static function get_footer_settings(): array {
return HomeCms::get_footer_settings();
}
/** 获取联系图标列表(仅返回启用的) */
public static function get_footer_contacts(): array {
return HomeCms::get_footer_contacts();
}
/** 获取快速链接列表(仅返回启用的,按排序) */
public static function get_quick_links(): array {
return HomeCms::get_quick_links();
}
/** 获取更多链接列表(仅返回启用的,按排序) */
public static function get_more_links(): array {
return HomeCms::get_more_links();
}
/** 获取热门分类显示数量 */
public static function get_hot_categories_count(): int {
return HomeCms::get_hot_categories_count();
}
/** 获取版权文字 */
public static function get_copyright_text(): string {
return HomeCms::get_copyright_text();
}
/**
* 获取页脚品牌列(第一列)配置
* @return array {m_s, img, img_dark, title, custom_code}
*/
public static function get_footer_brand_settings(): array {
return HomeCms::get_footer_brand_settings();
}
/**
* 获取页脚扩展联系方式配置
* @return array {m_s, wechat_img, qq, weibo, email, more, code_1, code_2}
*/
public static function get_footer_contact_extended_settings(): array {
return HomeCms::get_footer_contact_extended_settings();
}
/**
* 获取页脚二维码图片组(第三列)
* @return array {m_s, items: [{text, img}]}
*/
public static function get_footer_mini_img_settings(): array {
return HomeCms::get_footer_mini_img_settings();
}
/**
* 获取页脚底部自定义代码
*/
public static function get_footer_customize_code(): string {
return HomeCms::get_footer_customize_code();
}
/** 获取审核配置 */
public static function get_moderation(): array {
return ExtensionRuntime::get_moderation();
}
// ============================================
// 文章布局配置
// ============================================
/**
* 获取归档布局模式。
*
* 从 post_list.list schema 的 render_mode 字段读取。
* 后台可在 /post-list/list 页面配置。
*
* @return string grid-cms|list|compact
*/
public static function get_archive_layout(): string {
return PostDisplay::get_archive_layout();
}
/**
* 获取文章列表分页模式
* @return string numbered|load_more|infinite
*/
public static function get_post_pagination_mode(): string {
return PostDisplay::get_post_pagination_mode();
}
/**
* 获取评论列表分页模式
* @return string numbered|load_more|infinite
*/
public static function get_comment_pagination_mode(): string {
return PostDisplay::get_comment_pagination_mode();
}
// ============================================
// CMS 配置
// ============================================
/**
* 获取站点全局风格。
*
* v4 起主题统一为 CMS / 资源站形态,恒返回 'cms'。
* 保留方法签名以兼容旧调用。
*
* @return string 始终为 'cms'
*/
public static function get_site_profile(): string {
return HomeCms::get_site_profile();
}
/**
* CMS 功能开关(合并默认值)。
* @return array
*/
public static function get_cms_features(): array {
return HomeCms::get_cms_features();
}
/**
* 单项 CMS 功能开关读取。
*/
public static function cms_feature(string $key, $default = null) {
return HomeCms::cms_feature($key, $default);
}
}
+517
View File
@@ -0,0 +1,517 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class ThemeSearch {
public static function search_placeholder(): string {
$placeholder = trim((string) Settings::get_value('search_placeholder', ''));
return $placeholder !== '' ? $placeholder : __('开启精彩搜索', 'aksrt');
}
public static function search_mode(): string {
$mode = sanitize_key((string) Settings::get_value('search_mode', ''));
if ($mode === '' || !in_array($mode, ['default', 'ajax', 'redirect'], true)) {
$mode = sanitize_key((string) Settings::get_value('search_type', 'default'));
}
return in_array($mode, ['default', 'ajax', 'redirect'], true) ? $mode : 'default';
}
public static function search_history_enabled(): bool {
$settings = Settings::get_all();
return !array_key_exists('search_history_enabled', $settings) || !empty($settings['search_history_enabled']);
}
public static function hot_search_enabled(): bool {
$settings = Settings::get_all();
return !array_key_exists('hot_search_enabled', $settings) || !empty($settings['hot_search_enabled']);
}
public static function hot_search_keywords(): array {
$raw = Settings::get_value('hot_search_keywords', '');
if (is_array($raw)) {
$items = $raw;
} else {
$items = preg_split('/[\r\n,]+/', (string) $raw) ?: [];
}
$items = array_map(
static function ($keyword): string {
return trim(sanitize_text_field((string) $keyword));
},
$items
);
$items = array_values(array_filter(array_unique($items), static function (string $keyword): bool {
return $keyword !== '';
}));
$limit = max(1, min(50, absint(Settings::get_value('search_popular_key_num', 20))));
return array_slice($items, 0, $limit);
}
public static function search_popular_title(): string {
$title = trim((string) Settings::get_value('search_popular_title', ''));
return $title !== '' ? $title : __('热门搜索', 'aksrt');
}
public static function search_popular_sticky(): array {
$raw = Settings::get_value('search_popular_sticky', '');
if (is_array($raw)) {
$items = $raw;
} else {
$items = preg_split('/[\r\n,]+/', (string) $raw) ?: [];
}
$items = array_map(static fn($k) => trim(sanitize_text_field((string) $k)), $items);
return array_values(array_filter(array_unique($items), fn(string $k) => $k !== ''));
}
public static function search_suggestions_enabled(): bool {
return self::search_mode() === 'ajax' && !empty(Settings::get_value('search_suggestions_enabled', false));
}
public static function search_posts_enabled(): bool {
return !empty(Settings::get_value('search_posts', false));
}
public static function search_no_page(): bool {
return !empty(Settings::get_value('search_no_page', false));
}
public static function search_exclude_cats(): array {
$raw = Settings::get_value('search_exclude_cats', '');
if (is_array($raw)) {
$items = $raw;
} else {
$items = preg_split('/[,,\s]+/', (string) $raw) ?: [];
}
$items = array_map(static fn($id) => absint($id), $items);
return array_values(array_filter(array_unique($items), fn(int $id) => $id > 0));
}
public static function search_cat_enabled(): bool {
return !empty(Settings::get_value('search_cat', false));
}
public static function search_cat_in(): string {
return trim((string) Settings::get_value('search_cat_in', ''));
}
public static function search_more_cat_obj(): bool {
return !empty(Settings::get_value('search_more_cat_obj', true));
}
public static function search_custom_key_enabled(): bool {
return !empty(Settings::get_value('search_custom_key_s', false));
}
public static function search_custom_key(): array {
$raw = Settings::get_value('search_custom_key', 'title,content');
if (is_array($raw)) {
$items = $raw;
} else {
$items = preg_split('/[,,\s]+/', (string) $raw) ?: [];
}
$items = array_map(static fn($k) => trim(sanitize_key((string) $k)), $items);
$valid = ['title', 'content', 'excerpt', 'category', 'tag', 'author'];
return array_values(array_intersect(array_filter(array_unique($items)), $valid));
}
public static function search_facets_enabled(): bool {
return !empty(Settings::get_value('search_facets', false));
}
public static function search_facets_order(): array {
$raw = Settings::get_value('search_facets_order', "category\ntag");
$items = is_array($raw) ? $raw : (preg_split('/[\r\n]+/', (string) $raw) ?: []);
$items = array_map(static fn($s) => trim(sanitize_key((string) $s)), $items);
return array_values(array_filter(array_unique($items), fn(string $s) => $s !== ''));
}
public static function get_meilisearch_config(): array {
$s = Settings::get_all();
$enabled = !empty($s['search_ms_enabled']);
$api_url = esc_url_raw(trim((string) ($s['search_ms_api_url'] ?? '')));
$api_key = sanitize_text_field((string) ($s['search_ms_api_key'] ?? ''));
$index_prefix = sanitize_key((string) ($s['search_ms_index_prefix'] ?? 'aksrt'));
$synonyms_raw = trim((string) ($s['search_ms_synonyms'] ?? ''));
$synonyms = [];
if ($synonyms_raw !== '') {
$decoded = json_decode($synonyms_raw, true);
if (is_array($decoded)) {
$synonyms = $decoded;
}
}
return [
'enabled' => $enabled,
'api_url' => $api_url,
'api_key' => $api_key,
'index_prefix' => $index_prefix !== '' ? $index_prefix : 'aksrt',
'synonyms' => $synonyms,
'index_name' => ($index_prefix !== '' ? $index_prefix : 'aksrt') . '_posts',
];
}
public static function filter_search_query($query): void {
if (is_admin() || !$query->is_main_query() || !$query->is_search()) {
return;
}
if (self::search_no_page()) {
$existing = $query->get('post_type');
if (empty($existing)) {
$query->set('post_type', 'post');
}
}
$exclude_cats = self::search_exclude_cats();
if (!empty($exclude_cats)) {
$cat_query = $query->get('cat');
$negatives = array_map(fn(int $id) => -$id, $exclude_cats);
if (!empty($cat_query)) {
$existing = is_array($cat_query) ? $cat_query : explode(',', (string) $cat_query);
$existing = array_map('intval', $existing);
$merged = array_merge($existing, $negatives);
$query->set('cat', implode(',', array_unique($merged)));
} else {
$query->set('cat', implode(',', $negatives));
}
}
}
public static function apply_list_orderby($query): void {
if (is_admin() || !$query->is_main_query()) {
return;
}
if (!$query->is_archive() && !$query->is_search() && !$query->is_home()) {
return;
}
if ($query->get('orderby')) {
return;
}
$s = Settings::get_all();
$key = sanitize_key((string) ($s['list_orderby'] ?? 'modified'));
if ($key === '' || $key === 'default') {
return;
}
switch ($key) {
case 'modified':
$query->set('orderby', 'modified');
$query->set('order', 'DESC');
break;
case 'date':
$query->set('orderby', 'date');
$query->set('order', 'DESC');
break;
case 'comments':
$query->set('orderby', 'comment_count');
$query->set('order', 'DESC');
break;
}
}
public static function get_search_content_types(): array {
$all_types = [
'post' => 'search_type_post',
'user' => 'search_type_user',
'category' => 'search_type_category',
'tag' => 'search_type_tag',
'product' => 'search_type_product',
'topic' => 'search_type_topic',
];
$enabled = [];
$all = Settings::get_all();
foreach ($all_types as $type => $key) {
$default_on = in_array($type, ['post', 'category', 'tag'], true);
$is_enabled = array_key_exists($key, $all)
? !empty(Settings::get_value($key, $default_on))
: $default_on;
if ($is_enabled) {
$enabled[] = $type;
}
}
if (empty($enabled)) {
$enabled = ['post'];
}
$default = sanitize_key((string) Settings::get_value('search_default_type', 'post'));
if (!in_array($default, $enabled, true)) {
$default = $enabled[0];
}
return [
'types' => $enabled,
'default' => $default,
];
}
public static function theme_mode(): string {
$mode = sanitize_key((string) Settings::get_value('theme_mode', 'time-auto'));
$map = ['light' => 'white-theme', 'dark' => 'dark-theme', 'auto' => 'time-auto'];
if (isset($map[$mode])) {
return $map[$mode];
}
$valid = ['white-theme', 'dark-theme', 'time-auto'];
return in_array($mode, $valid, true) ? $mode : 'time-auto';
}
public static function theme_mode_js_value(): string {
$mode = self::theme_mode();
$map = ['white-theme' => 'light', 'dark-theme' => 'dark', 'time-auto' => 'time-auto'];
return $map[$mode] ?? 'time-auto';
}
public static function time_auto_theme_mode(): string {
$hour = (int) current_time('G');
return ($hour >= 20 || $hour < 8) ? 'dark' : 'light';
}
public static function initial_theme_mode(): string {
$cookie = isset($_COOKIE['theme_mode'])
? sanitize_key((string) wp_unslash($_COOKIE['theme_mode']))
: '';
$map = [
'dark-theme' => 'dark',
'white-theme' => 'light',
'dark' => 'dark',
'light' => 'light',
];
if (isset($map[$cookie])) {
return $map[$cookie];
}
$mode = self::theme_mode();
if ($mode === 'dark-theme') {
return 'dark';
}
if ($mode === 'white-theme') {
return 'light';
}
if ($mode === 'time-auto') {
return self::time_auto_theme_mode();
}
return '';
}
public static function primary_color(): string {
$color = trim((string) Settings::get_value('primary_color', '#f04494'));
return preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', $color) ? $color : '#f04494';
}
public static function theme_skin(): string {
$skin = trim((string) Settings::get_value('theme_skin', 'f04494'));
$skin = ltrim($skin, '#');
if (preg_match('/^[0-9a-fA-F]{6}$/', $skin)) {
return '#' . $skin;
}
$legacy_map = [
'pink' => '#f04494',
'blue' => '#627bf5',
'green' => '#36af18',
'purple' => '#ae53f3',
'custom' => '',
];
if (isset($legacy_map[$skin])) {
return $legacy_map[$skin] !== '' ? $legacy_map[$skin] : self::primary_color();
}
return self::primary_color();
}
public static function dark_primary_color(): string {
$color = trim((string) Settings::get_value('dark_primary_color', ''));
if (preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', $color)) {
return $color;
}
return self::primary_color();
}
public static function get_theme_mode_button_positions(): array {
$raw = Settings::get_value('theme_mode_button', ['pc_nav', 'm_menu']);
if (is_array($raw)) {
return array_values(array_filter(array_map('sanitize_text_field', $raw)));
}
if (is_bool($raw)) {
return $raw ? ['pc_nav', 'm_menu'] : [];
}
$str = trim((string) $raw);
if ($str === '' || $str === '0') return [];
$items = preg_split('/[,|]+/', $str) ?: [];
return array_values(array_filter(array_map('sanitize_text_field', $items)));
}
public static function resolved_primary_color(): string {
$skin = self::theme_skin();
return $skin !== '' ? $skin : self::primary_color();
}
public static function border_radius(): int {
$radius = (int) Settings::get_value('border_radius', 8);
return max(0, min(20, $radius));
}
public static function card_radius(): int {
$main = Settings::get_value('theme_main_radius', null);
if ($main !== null && $main !== '') {
return max(0, min(30, (int) $main));
}
return self::border_radius() + 4;
}
public static function is_qj_loading_enabled(): bool {
return (bool) Settings::get_value('qj_loading', false);
}
public static function get_qj_loading_style(): string {
$style = (string) Settings::get_value('qj_dh_xs', 'no1');
if (!preg_match('/^no([1-9]|10)$/', $style)) {
return 'no1';
}
return $style;
}
public static function container_max_width(): int {
$width = (int) Settings::get_value('container_max_width', 1200);
if ($width === 0) {
return 0;
}
return max(1200, min(2200, $width));
}
public static function font_family(): string {
$family = sanitize_key((string) Settings::get_value('font_family', 'system'));
return in_array($family, ['system', 'noto-sans-sc', 'noto-serif-sc', 'custom'], true) ? $family : 'system';
}
public static function get_code_highlight_config(): array {
$s = Settings::get_all();
$light_themes = ['enlighter','bootstrap4','classic','beyond','mowtwo','eclipse','droide','minimal','rowhammer','godzilla','dracula','atomic','monokai'];
$dark_themes = ['dracula','atomic','monokai','enlighter','bootstrap4'];
$light = sanitize_key((string) ($s['highlight_zt'] ?? 'enlighter'));
$dark = sanitize_key((string) ($s['highlight_dark_zt'] ?? 'monokai'));
$max_height_raw = $s['highlight_maxheight'] ?? 400;
$max_height = absint(is_numeric($max_height_raw) ? $max_height_raw : preg_replace('/[^0-9]/', '', $max_height_raw));
if ($max_height === 0 && !in_array($max_height_raw, [0, '0', '0px'], true)) {
$max_height = 400;
}
return [
'enabled' => !isset($s['highlight_kg']) || !empty($s['highlight_kg']),
'lightTheme' => in_array($light, $light_themes, true) ? $light : 'enlighter',
'darkTheme' => in_array($dark, $dark_themes, true) ? $dark : 'monokai',
'lineNumbers' => !empty($s['highlight_hh']),
'showButtons' => !isset($s['highlight_btn']) || !empty($s['highlight_btn']),
'maxHeight' => $max_height,
];
}
public static function get_system_notice_config(): array {
$s = Settings::get_all();
$policy = sanitize_key((string) ($s['system_notice_policy'] ?? ''));
if (!in_array($policy, ['', 'signin', 'vip', 'free', 'auth'], true)) {
$legacy_map = ['always' => '', 'once' => '', 'daily' => ''];
$policy = $legacy_map[$policy] ?? '';
}
$size = sanitize_key((string) ($s['system_notice_size'] ?? ''));
if (!in_array($size, ['', 'modal-mini', 'modal-sm', 'modal-lg'], true)) {
$size = '';
}
$radius_raw = $s['system_notice_radius'] ?? '0';
$radius = absint(preg_replace('/[^0-9]/', '', $radius_raw));
$allowed_colors = ['primary', 'success', 'warning', 'danger', 'info', 'purple', 'dark'];
$buttons = [];
for ($i = 1; $i <= 4; $i++) {
$text = trim((string) ($s["system_notice_btn{$i}_text"] ?? ''));
$link = esc_url_raw((string) ($s["system_notice_btn{$i}_link"] ?? ''));
$color = sanitize_key((string) ($s["system_notice_btn{$i}_color"] ?? 'primary'));
if ($i === 1 && $text === '') {
$text = trim((string) ($s['system_notice_button'] ?? ''));
$link = $link ?: esc_url_raw((string) ($s['system_notice_link'] ?? ''));
}
if ($text === '') continue;
if (!in_array($color, $allowed_colors, true)) {
$color = 'primary';
}
$buttons[] = [
'text' => $text,
'link' => $link,
'color' => $color,
];
}
return [
'enabled' => !empty($s['system_notice_enabled']),
'policy' => $policy,
'title' => trim((string) ($s['system_notice_title'] ?? '')),
'content' => trim((string) ($s['system_notice_content'] ?? '')),
'buttons' => $buttons,
'size' => $size,
'radius' => $radius,
'class' => sanitize_html_class((string) ($s['system_notice_class'] ?? '')),
'title_icon' => trim((string) ($s['system_notice_title_icon'] ?? '')),
'title_class' => sanitize_html_class((string) ($s['system_notice_title_class'] ?? '')),
'title_style' => trim((string) ($s['system_notice_title_style'] ?? '')),
'expires' => absint($s['system_notice_expires'] ?? 24),
];
}
public static function get_sidebar_config(): array {
$s = Settings::get_all();
$position = sanitize_key((string) ($s['sidebar_position'] ?? 'right'));
$width = max(200, min(400, (int) ($s['sidebar_width'] ?? 311)));
return [
'enabled' => !isset($s['sidebar_enabled']) || !empty($s['sidebar_enabled']),
'position' => in_array($position, ['left', 'right'], true) ? $position : 'right',
'width' => $width,
'home' => !isset($s['sidebar_home_enabled']) || !empty($s['sidebar_home_enabled']),
'category' => !isset($s['sidebar_category_enabled']) || !empty($s['sidebar_category_enabled']),
'tag' => !isset($s['sidebar_tag_enabled']) || !empty($s['sidebar_tag_enabled']),
'single' => !isset($s['sidebar_single_enabled']) || !empty($s['sidebar_single_enabled']),
'page' => !isset($s['sidebar_page_enabled']) || !empty($s['sidebar_page_enabled']),
'search' => !isset($s['sidebar_search_enabled']) || !empty($s['sidebar_search_enabled']),
];
}
public static function get_i18n_config(): array {
$s = Settings::get_all();
$enabled = !empty($s['i18n_enabled']) || !empty($s['frontend_translate_enabled']);
$source = $s['i18n_default_lang'] ?? $s['frontend_translate_source'] ?? 'zh-CN';
return [
'enabled' => $enabled,
'autoDetect' => !isset($s['i18n_auto_detect']) || !empty($s['i18n_auto_detect']),
'defaultLang' => $source,
'availableLangs' => $s['i18n_available_langs'] ?? "zh-CN|简体中文\nen|English",
'nomenclature' => $s['i18n_nomenclature'] ?? '',
'switcherPosition' => $s['i18n_switcher_position'] ?? 'footer',
'urlStrategy' => $s['i18n_url_strategy'] ?? 'query',
];
}
}
+388
View File
@@ -0,0 +1,388 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Settings;
defined('ABSPATH') || exit;
class UserRuntime {
/**
* Public user-home display settings.
*
* @return array
*/
public static function get_user_home_settings(): array {
$settings = Settings::get_all();
$allowed_tabs = ['posts', 'comments', 'favorites', 'credits', 'product', 'plate', 'forum', 'follow'];
$parse_csv = function (string $key, string $fallback) use ($settings, $allowed_tabs): array {
$raw = $settings[$key] ?? $fallback;
$items = is_array($raw) ? $raw : preg_split('/[\s,]+/', (string) $raw, -1, PREG_SPLIT_NO_EMPTY);
$clean = [];
foreach ((array) $items as $v) {
$v = sanitize_key((string) $v);
if (in_array($v, $allowed_tabs, true) && !in_array($v, $clean, true)) {
$clean[] = $v;
}
}
return $clean;
};
// Zibll author_main_tab_opt defaults: post, favorite, comment, plate, forum, follow.
$normalized_tabs = $parse_csv('user_home_tabs', 'posts,favorites,comments,follow');
if (empty($normalized_tabs)) {
$normalized_tabs = ['posts', 'favorites', 'comments', 'follow'];
}
$raw_order = $parse_csv('user_home_tabs_order', '');
$ordered = [];
foreach ($raw_order as $tab) {
if (in_array($tab, $normalized_tabs, true)) {
$ordered[] = $tab;
}
}
foreach ($normalized_tabs as $tab) {
if (!in_array($tab, $ordered, true)) {
$ordered[] = $tab;
}
}
$pc_hide = $parse_csv('user_home_tabs_pc_hide', '');
$mobile_hide = $parse_csv('user_home_tabs_mobile_hide', '');
return [
'tabs' => $normalized_tabs,
'tabs_order' => $ordered,
'tabs_pc_hide' => $pc_hide,
'tabs_mobile_hide' => $mobile_hide,
// Public author page is full-width (Zibll author.php). Sidebar option kept as no-op reserved key.
'sidebar' => false,
'cover' => !isset($settings['user_home_cover']) || !empty($settings['user_home_cover']),
'follow' => !isset($settings['user_home_follow']) || !empty($settings['user_home_follow']),
'posts_per_page' => max(5, min(50, (int) ($settings['user_home_posts_per_page'] ?? 10))),
'avatar' => !isset($settings['user_home_avatar']) || !empty($settings['user_home_avatar']),
'name' => !isset($settings['user_home_name']) || !empty($settings['user_home_name']),
'desc' => !isset($settings['user_home_desc']) || !empty($settings['user_home_desc']),
// Body stats row removed; counts live in tabs like Zibll.
'stats' => false,
'social' => !isset($settings['user_home_social']) || !empty($settings['user_home_social']),
'tab_swiper' => !empty($settings['author_main_tab_swiper']),
'message' => !isset($settings['user_home_message']) || !empty($settings['user_home_message']),
'private' => !isset($settings['user_home_private']) || !empty($settings['user_home_private']),
'paging_ajax' => !isset($settings['user_home_paging_ajax']) || !empty($settings['user_home_paging_ajax']),
];
}
/**
* Private user-center display settings.
*
* @return array{tabs:string[],avatar_upload:bool,cover_upload:bool,social_links:bool}
*/
public static function get_user_center_settings(): array {
$settings = Settings::get_all();
$raw_tabs = $settings['user_center_tabs'] ?? 'profile,posts,comments,credits,favorites';
$tabs = is_array($raw_tabs)
? $raw_tabs
: preg_split('/[\s,]+/', (string) $raw_tabs, -1, PREG_SPLIT_NO_EMPTY);
$allowed_tabs = ['profile', 'posts', 'comments', 'favorites', 'credits'];
$normalized_tabs = ['profile'];
foreach ((array) $tabs as $tab) {
$tab = sanitize_key((string) $tab);
if ($tab === 'profile') {
continue;
}
if (in_array($tab, $allowed_tabs, true) && !in_array($tab, $normalized_tabs, true)) {
$normalized_tabs[] = $tab;
}
}
if (!in_array('posts', $normalized_tabs, true) && !in_array('comments', $normalized_tabs, true)) {
$normalized_tabs[] = 'posts';
}
return [
'tabs' => $normalized_tabs,
'avatar_upload' => !isset($settings['user_avatar_upload']) || !empty($settings['user_avatar_upload']),
'cover_upload' => !isset($settings['user_cover_upload']) || !empty($settings['user_cover_upload']),
'social_links' => !isset($settings['user_social_links']) || !empty($settings['user_social_links']),
];
}
/**
* User identity, level, medal, and report governance settings.
*
* @return array{
* verify:array{enabled:bool,public_display:bool,badge_text:string,accept_email_verified:bool,require_manual_review:bool,allow_apply:bool,disable_desc:string,limit_level:int,desc:string,apply_option:string},
* level:array{enabled:bool,public_display:bool,source:string,label_prefix:string,thresholds:array<int,int>},
* medal:array{enabled:bool,public_display:bool,limit:int,categories:array<int,array{cat_name:string,items:array<int,array{slug:string,name:string,desc:string,icon:string,get_type:string,get_val:int}>}>,items_by_slug:array<string,array{slug:string,name:string,desc:string,icon:string,get_type:string,get_val:int,cat_name:string}>},
* badge:array{enabled:bool,public_display:bool,limit:int,dictionary:array<int,array{slug:string,label:string,tone:string,icon_url?:string}>},
* report:array{enabled:bool,public_entry:bool,public_notice:bool,ban_notice_text:string,categories:array<int,array{slug:string,label:string}>}
* }
*/
public static function get_user_identity_settings(): array {
$settings = Settings::get_all();
$verify_badge_text = trim((string) ($settings['user_verify_badge_text'] ?? ''));
$level_prefix = trim((string) ($settings['user_level_prefix'] ?? ''));
$ban_notice_text = trim((string) ($settings['user_report_ban_notice_text'] ?? ''));
$medal = self::parse_medal_catalog($settings);
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']),
'badge_text' => $verify_badge_text !== '' ? $verify_badge_text : __('已认证', 'aksrt'),
'accept_email_verified' => !isset($settings['user_verify_accept_email_verified']) || !empty($settings['user_verify_accept_email_verified']),
'require_manual_review' => !empty($settings['user_verify_require_manual_review']),
'allow_apply' => !isset($settings['user_verify_allow_apply']) || !empty($settings['user_verify_allow_apply']),
'disable_desc' => (string) ($settings['user_verify_disable_desc'] ?? ''),
'limit_level' => max(0, (int) ($settings['user_verify_limit_level'] ?? 0)),
'desc' => (string) ($settings['user_verify_desc'] ?? ''),
'apply_option' => (string) ($settings['user_verify_apply_option'] ?? ''),
],
'level' => [
'enabled' => !empty($settings['user_level_enabled']) || !empty($settings['user_level_s']),
'public_display' => !isset($settings['user_level_public_display']) || !empty($settings['user_level_public_display']),
'source' => (static function () use ($settings) {
$v = $settings['user_level_source'] ?? 'integral';
return in_array($v, ['credits_balance', 'manual_meta', 'integral'], true) ? $v : 'integral';
})(),
'label_prefix' => $level_prefix !== '' ? $level_prefix : 'Lv.',
'thresholds' => self::parse_level_thresholds((string) ($settings['user_level_thresholds'] ?? "1|0\n2|30\n3|100\n4|300")),
'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',
'icon_url' => $item['icon'],
];
}, $medal['items_by_slug'])),
],
'report' => [
'enabled' => !empty($settings['user_report_enabled']),
'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' => $ban_notice_text !== '' ? $ban_notice_text : __('当前账号处于限制状态,部分互动能力已关闭。', 'aksrt'),
'categories' => self::parse_report_categories((string) ($settings['user_report_categories'] ?? "spam|垃圾信息\nabuse|人身攻击\ncopyright|版权投诉")),
],
];
}
/**
* 禁封设置
*/
public static function ban_settings(): array {
$settings = Settings::get_all();
$reasons_raw = (string) ($settings['ban_preset_reason'] ?? "spam|垃圾信息\nabuse|人身攻击\nviolation|违规操作");
$reasons = [];
foreach (preg_split('/\r\n|\r|\n/', $reasons_raw) as $line) {
$line = trim($line);
if ($line === '') {
continue;
}
$parts = explode('|', $line, 2);
$reasons[] = [
'slug' => sanitize_key($parts[0]),
'name' => sanitize_text_field($parts[1] ?? $parts[0]),
];
}
$appeal_keys_raw = (string) ($settings['ban_appeal_keys'] ?? '');
$appeal_keys = array_filter(array_map('trim', explode(',', $appeal_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 = Settings::get_all();
return [
'enabled' => !empty($settings['user_report_enabled']),
'public_entry' => !empty($settings['user_report_public_entry']),
'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<string,mixed> $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',
'bbs_post',
'comment',
'followed',
'like',
'comment_like',
'bbs_adopt',
'bbs_comment_hot',
'bbs_post_hot',
'bbs_essence',
'bbs_score',
'views',
'pay_income',
'pay_rebate',
'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<int,int>
*/
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<int,array{slug:string,label:string,tone:string,icon_url:string}>
*/
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 [];
}
/**
* @return array<int,array{slug:string,label:string}>
*/
public static function parse_report_categories(string $raw): array {
$lines = preg_split('/[\r\n]+/', $raw, -1, PREG_SPLIT_NO_EMPTY);
$items = [];
foreach ((array) $lines as $line) {
$parts = array_map('trim', explode('|', (string) $line));
if (count($parts) < 2) {
continue;
}
$slug = sanitize_key((string) $parts[0]);
$label = sanitize_text_field((string) $parts[1]);
if ($slug === '' || $label === '') {
continue;
}
$items[] = [
'slug' => $slug,
'label' => $label,
];
}
return $items;
}
}