refactor: flatten src/AKSRT → src (drop theme-identifier dir)

Move all domain folders from src/AKSRT/{Domain} to src/{Domain}; remap
Composer PSR-4 "AKSRT\\": "src/"; update audit script + README + ADR
paths; remove dead tools/build-psr4-map.php (scanned deleted inc/classes).
This commit is contained in:
kate522 committed 2026-08-02 22:06:06 +08:00
1 parent 56f1a8175f
commit 8c5b1555c9
285 files changed
+26 -3600

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', '')),
];
}
}
+232
View File
@@ -0,0 +1,232 @@
<?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 = class_exists('\AKSRT\User\Level') ? \AKSRT\User\Level::get_level($user_id) : 0;
if ($user_level >= $level_threshold) {
return true;
}
}
$vip_threshold = (int) ($row['vip'] ?? -1);
if ($vip_threshold >= 0) {
$user_vip = function_exists('aksrt_get_user_vip_level') ? aksrt_get_user_vip_level($user_id) : 0;
if ($user_vip >= $vip_threshold) {
return true;
}
}
if (!empty($row['auth'])) {
if (class_exists('\\AKSRT\\User\\AuthApply') && \AKSRT\User\AuthApply::is_verified($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,
];
}
}
+152
View File
@@ -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;
}
}
+220
View File
@@ -0,0 +1,220 @@
<?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();
}
/**
* 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 {
return PaymentRuntime::get_payment_vip_settings();
}
/**
* Alias for callers that expect get_vip_settings() (header CTA, float buttons).
*
* @return array<string,mixed>
*/
public static function get_vip_settings(): array {
return self::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));
}
}
+301
View File
@@ -0,0 +1,301 @@
<?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',
'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(),
'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();
}
}
+285
View File
@@ -0,0 +1,285 @@
<?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:bool,show_keywords:bool,popular_limit:int,show_input_cat:bool,show_type:bool,keywords:string[],categories:array,type_options:array,before_html:string,after_html:string}
*/
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(50, (int) ($settings['header_components_search_popular_limit'] ?? 6)));
$show_cat = !empty($settings['header_components_search_show_input_cat']);
$show_type = !empty($settings['header_components_search_show_type']);
$in_cat = sanitize_text_field((string) ($settings['header_components_search_in_cat'] ?? ''));
$in_type = sanitize_key((string) ($settings['header_components_search_in_type'] ?? 'post')) ?: 'post';
$more_cats = isset($settings['header_components_search_more_cats'])
? array_values(array_filter(array_map('absint', (array) $settings['header_components_search_more_cats'])))
: [];
// Mobile: hide hot keywords (zibll).
if ($is_mobile) {
$show_kw = false;
}
$keywords = [];
if ($show && $show_kw) {
if (function_exists('aksrt_get_search_hot_keywords')) {
$keywords = (array) aksrt_get_search_hot_keywords();
} else {
$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', array_values($keywords)), 0, $kw_limit);
}
$categories = [];
if ($show && $show_cat) {
$cat_args = ['hide_empty' => true, 'number' => 50];
if ($more_cats) {
$cat_args['include'] = $more_cats;
$cat_args['orderby'] = 'include';
$cat_args['hide_empty'] = false;
}
$cats = get_categories($cat_args);
foreach ($cats as $cat) {
$categories[] = ['id' => (int) $cat->term_id, 'name' => (string) $cat->name];
}
}
$type_options = [];
if ($show && $show_type) {
$types = Settings::get_search_content_types();
$labels = [
'post' => __('文章', 'aksrt'),
'user' => __('用户', 'aksrt'),
'page' => __('页面', 'aksrt'),
];
$enabled = $types['enabled'] ?? $types;
if (is_array($enabled)) {
foreach ($enabled as $type_key => $type_value) {
// Support both map[key=>bool] and list of keys.
if (is_int($type_key) && is_string($type_value)) {
$k = $type_value;
$on = true;
} else {
$k = (string) $type_key;
$on = (bool) $type_value;
}
if (!$on || $k === '') {
continue;
}
$type_options[] = [
'key' => $k,
'label' => $labels[$k] ?? $k,
];
}
}
}
$before_html = isset($settings['header_components_search_before_html'])
? (string) $settings['header_components_search_before_html']
: '';
$after_html = isset($settings['header_components_search_after_html'])
? (string) $settings['header_components_search_after_html']
: '';
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,
'in_cat' => $in_cat,
'in_type' => $in_type,
'more_cats' => $more_cats,
'keywords' => $keywords,
'categories' => $categories,
'type_options' => $type_options,
'before_html' => $before_html,
'after_html' => $after_html,
];
}
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'] ?? '')),
];
}
}
+181
View File
@@ -0,0 +1,181 @@
<?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);
}
/**
* 获取文档模式设置
*/
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();
}
}
+227
View File
@@ -0,0 +1,227 @@
<?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'] ?? 'null'));
$allowed = ['polling', 'qq', 'amap', 'null'];
if (!in_array($provider, $allowed, true)) {
$provider = 'null';
}
$allowed_display = ['comment', 'user_home', 'user_center', 'post'];
// Only fill defaults when the key was never saved — empty array means "none".
if (!array_key_exists('ip_geo_display', $settings)) {
$display_norm = $allowed_display;
} else {
$display_raw = $settings['ip_geo_display'];
$display = is_array($display_raw) ? $display_raw : [];
$display_norm = [];
foreach ($display as $item) {
$key = sanitize_key((string) $item);
if (in_array($key, $allowed_display, true) && !in_array($key, $display_norm, true)) {
$display_norm[] = $key;
}
}
}
$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_cache_ttl' => max(3600, (int) ($settings['ip_geo_cache_ttl'] ?? 86400)),
'ip_geo_display' => $display_norm,
'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']),
];
}
}
+576
View File
@@ -0,0 +1,576 @@
<?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,nav_pay_vip:bool,nav_user_pay_vip:bool
* }
*/
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'] ?? '1'));
$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']),
// Unified with header_newposts_enabled (zibll nav_newposts).
'publish_btn' => !isset($settings['header_newposts_enabled']) || !empty($settings['header_newposts_enabled']),
'mobile_drawer' => !isset($settings['header_mobile_drawer']) || !empty($settings['header_mobile_drawer']),
'nav_style' => $nav_style,
'max_width' => $max_width,
// Zibll default: true when fixed.
'scroll_down_hide' => !isset($settings['header_scroll_down_hide']) || !empty($settings['header_scroll_down_hide']),
'layout' => in_array($layout, ['1', '2', '3'], true) ? $layout : '1',
'mobile_layout' => in_array($ml, ['center', 'left'], true) ? $ml : 'center',
'mobile_align' => in_array($ma, ['top', 'left', 'right'], true) ? $ma : 'left',
'nav_pay_vip' => !isset($settings['header_nav_pay_vip']) || !empty($settings['header_nav_pay_vip']),
'nav_user_pay_vip' => !isset($settings['header_nav_user_pay_vip']) || !empty($settings['header_nav_user_pay_vip']),
];
}
/**
* Header publish button configuration.
*
* @return array{enabled:bool,text:string,radius:int,class:string,types:string[]}
*/
public static function get_header_newposts_config(): array {
$s = self::get_all();
$types = [];
if (!empty($s['header_newposts_types']) && is_array($s['header_newposts_types'])) {
$types = array_values(array_filter(array_map('sanitize_key', $s['header_newposts_types'])));
}
// zibll nav_newposts_radius is bool pill; map true→20px, false→0. Legacy int still accepted once.
$raw_radius = $s['header_newposts_radius'] ?? true;
if (is_bool($raw_radius) || $raw_radius === '0' || $raw_radius === '1' || $raw_radius === 0 || $raw_radius === 1) {
$radius_px = !empty($raw_radius) ? 20 : 0;
$radius_pill = !empty($raw_radius);
} else {
$radius_px = max(0, min(30, (int) $raw_radius));
$radius_pill = $radius_px >= 16;
}
return [
// Default on like zibll nav_newposts when key unset.
'enabled' => !isset($s['header_newposts_enabled']) || !empty($s['header_newposts_enabled']),
'text' => trim((string) ($s['header_newposts_text'] ?? '<i class="fa fa-pencil"></i> 发布')),
'radius' => $radius_px,
'radius_pill' => $radius_pill,
'class' => sanitize_html_class((string) (($s['header_newposts_class'] ?? '') !== '' ? $s['header_newposts_class'] : 'jb-blue')),
'types' => $types,
];
}
/**
* 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('AKSRT\\Settings\\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'] ?? '');
}
/** 获取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'] ?? '')),
];
}
}
+474
View File
@@ -0,0 +1,474 @@
<?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();
// Canonical Zibll-aligned primary list contract.
$main_list_enabled = !array_key_exists('home_posts_list_s', $s) || !empty($s['home_posts_list_s']);
$sort_bar_enabled = !array_key_exists('home_list1_orderby_s', $s) || !empty($s['home_list1_orderby_s']);
$orderby_option = isset($s['home_list1_orderby_option']) && is_array($s['home_list1_orderby_option'])
? $s['home_list1_orderby_option']
: [];
$default_sort = sanitize_key((string) ($orderby_option['orderby'] ?? 'date'));
if (!in_array($default_sort, $allowed_sorts, true)) {
$default_sort = 'date';
}
$sort_options_raw = $orderby_option['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 = sanitize_text_field((string) ($s['index_list_title'] ?? ''));
if ($list_title === '') {
$list_title = __('最新发布', 'aksrt');
}
$exclude_posts = self::normalize_int_array($s['home_exclude_posts'] ?? []);
$exclude_cats = self::normalize_int_array($s['home_exclude_cats'] ?? []);
$tabs_raw = isset($s['home_lists']) && is_array($s['home_lists']) ? $s['home_lists'] : [];
$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),
// 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();
}
}
+106
View File
@@ -0,0 +1,106 @@
<?php
/**
* Identity provider configuration for the admin REST surface.
*
* @package AKSRT
*/
namespace AKSRT\Settings;
use AKSRT\Identity\Provider\ProviderFactory;
use AKSRT\Identity\SecretStore;
defined('ABSPATH') || exit;
final class IdentityAdmin {
private const PROVIDER_FIELDS = [
'aliyun' => ['access_key_id', 'access_key_secret'],
'tencent' => ['secret_id', 'secret_key'],
];
/** @return array<string,mixed> */
public static function read(): array {
$stored = Settings::get_all();
$provider = sanitize_key((string) ($stored['user_verify_provider'] ?? 'aliyun'));
if (!isset(self::PROVIDER_FIELDS[$provider])) {
$provider = 'aliyun';
}
return [
'provider' => $provider,
'providers' => [
'aliyun' => self::provider_state('aliyun'),
'tencent' => self::provider_state('tencent'),
],
];
}
/** @param array<string,mixed> $input @return array<string,mixed> */
public static function update(array $input): array {
if (array_key_exists('provider', $input)) {
$provider = sanitize_key((string) $input['provider']);
if (isset(self::PROVIDER_FIELDS[$provider])) {
Settings::write_domain('users', 'verify', ['user_verify_provider' => $provider]);
}
}
$providers = isset($input['providers']) && is_array($input['providers']) ? $input['providers'] : [];
foreach (self::PROVIDER_FIELDS as $provider => $fields) {
$changes = isset($providers[$provider]) && is_array($providers[$provider]) ? $providers[$provider] : [];
$updates = [];
if (!empty($changes['clear_credentials'])) {
foreach ($fields as $field) {
$updates[$field] = null;
}
} else {
$credentials = isset($changes['credentials']) && is_array($changes['credentials']) ? $changes['credentials'] : [];
foreach ($fields as $field) {
$value = trim((string) ($credentials[$field] ?? ''));
if ($value !== '') {
$updates[$field] = $value;
}
}
}
if ($updates !== []) {
SecretStore::update($provider, $updates);
}
}
return self::read();
}
/** @return array{success:bool,message:string} */
public static function test(string $provider): array {
if (!isset(self::PROVIDER_FIELDS[$provider])) {
return ['success' => false, 'message' => __('不支持的身份认证服务商', 'aksrt')];
}
$valid = ProviderFactory::make($provider)->validate_config();
if (is_wp_error($valid)) {
return ['success' => false, 'message' => $valid->get_error_message()];
}
return ['success' => true, 'message' => sprintf(__('%s配置检查通过', 'aksrt'), self::label($provider))];
}
/** @return array<string,mixed> */
private static function provider_state(string $provider): array {
$secrets = SecretStore::get($provider);
$configured_fields = [];
$missing = [];
foreach (self::PROVIDER_FIELDS[$provider] as $field) {
$configured = !empty($secrets[$field]);
$configured_fields[$field] = $configured;
if (!$configured) {
$missing[] = $field;
}
}
return [
'label' => self::label($provider),
'configured' => $missing === [],
'configured_fields' => $configured_fields,
'missing_fields' => $missing,
];
}
private static function label(string $provider): string {
return $provider === 'aliyun' ? __('阿里云', 'aksrt') : __('腾讯云', 'aksrt');
}
}
+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"]),
];
}
}
+510
View File
@@ -0,0 +1,510 @@
<?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';
}
$default_l1 = "30|59|1|month|限时|月卡\n98|199|3|month|特惠|季卡\n365|599|12|month|超值|年卡\n999|1299|0|day|永久|永久卡";
$default_l2 = "58|99|1|month|限时|月卡\n168|299|3|month|特惠|季卡\n598|999|12|month|超值|年卡\n1299|1999|0|day|永久|永久卡";
$products_1 = sanitize_textarea_field((string) ($settings['payment_vip_1_products'] ?? $default_l1));
$products_2 = sanitize_textarea_field((string) ($settings['payment_vip_2_products'] ?? $default_l2));
// Restore newlines lost only if empty after sanitize — sanitize_textarea_field keeps \n.
if (trim($products_1) === '') {
$products_1 = $default_l1;
}
if (trim($products_2) === '') {
$products_2 = $default_l2;
}
$desc = sanitize_textarea_field((string) ($settings['pay_user_vip_desc'] ?? ''));
$more = (string) ($settings['pay_user_vip_more'] ?? '');
$allowed_equity = [
'li' => [],
'ul' => [],
'ol' => [],
'strong' => [],
'b' => [],
'em' => [],
'i' => ['class' => true],
'span' => ['class' => true],
'br' => [],
'a' => ['href' => true, 'target' => true, 'rel' => true, 'class' => true],
];
return [
'enabled' => $enabled,
'name' => $vip1_enabled ? $name1 : $badge,
'badge_text' => $badge,
'desc' => $desc,
'more' => wp_kses($more, $allowed_equity + ['div' => ['class' => true], 'p' => ['class' => true]]),
'help_text' => $desc !== '' ? $desc : __('会员权益以免扣下载和身份展示为主,开通后立即生效。', 'aksrt'),
'levels' => [
1 => [
'enabled' => $vip1_enabled,
'name' => $name1,
'desc' => sanitize_text_field((string) ($settings['pay_user_vip_1_desc'] ?? __('尊享一级会员特权', 'aksrt'))),
'equity' => wp_kses((string) (($settings['pay_user_vip_1_equity'] ?? '') !== '' ? $settings['pay_user_vip_1_equity'] : '<li>免费下载部分资源</li><li>会员专属身份标识</li><li>付费内容会员价</li>'), $allowed_equity),
'img_icon' => esc_url_raw((string) ($settings['vip_1_img_icon'] ?? '')),
'product_s' => !isset($settings['vip_1_product_s']) || !empty($settings['vip_1_product_s']),
'products_raw' => $products_1,
'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,
'desc' => sanitize_text_field((string) ($settings['pay_user_vip_2_desc'] ?? __('尊享二级会员特权', 'aksrt'))),
'equity' => wp_kses((string) (($settings['pay_user_vip_2_equity'] ?? '') !== '' ? $settings['pay_user_vip_2_equity'] : '<li>更高免费下载额度</li><li>更高等级身份标识</li><li>更多付费内容会员价</li>'), $allowed_equity),
'img_icon' => esc_url_raw((string) ($settings['vip_2_img_icon'] ?? '')),
'product_s' => !isset($settings['vip_2_product_s']) || !empty($settings['vip_2_product_s']),
'products_raw' => $products_2,
'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'] ?? '')),
// Multi-line upgrade product rules — must not collapse newlines.
'product' => sanitize_textarea_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' => sanitize_textarea_field((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();
$gateway_keys = ['wechat', 'alipay', 'paypal', 'stripe', 'balance', 'card_pass', 'epay'];
$gateways = [];
foreach ($gateway_keys as $gw) {
$gateways[$gw] = !empty($settings["payment_{$gw}_enabled"]);
}
return [
'help_text' => __('以渠道开关与凭据是否齐全为准;异步回调由各网关验签。卡密为兑换入口,不进入收银台。', 'aksrt'),
'gateways' => $gateways,
'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'] ?? '')),
'channel_alipay' => !array_key_exists('epay_channel_alipay', $settings) || !empty($settings['epay_channel_alipay']),
'channel_wxpay' => !array_key_exists('epay_channel_wxpay', $settings) || !empty($settings['epay_channel_wxpay']),
'device' => sanitize_key((string) ($settings['epay_device'] ?? 'auto')),
],
];
}
/**
* 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();
$gateway_id = sanitize_key($gateway_id);
$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',
'epay' => 'payment_epay_enabled',
'epay_alipay' => 'payment_epay_enabled',
'epay_wxpay' => 'payment_epay_enabled',
];
$key = $map[$gateway_id] ?? '';
if ($key === '') {
return false;
}
if ($gateway_id === 'balance') {
// Master: 余额充值 → 启用余额功能. Optional kill-switch: payment_balance_enabled=false.
// Missing key (legacy sites) follows pay_balance_s so "open wallet" alone enables pay.
if (empty($settings['pay_balance_s'])) {
return false;
}
if (array_key_exists('payment_balance_enabled', $settings) && empty($settings['payment_balance_enabled'])) {
return false;
}
return true;
}
if (empty($settings[$key])) {
return false;
}
// Epay sub-channels require the matching channel switch (default on when key missing).
if ($gateway_id === 'epay_alipay') {
return !array_key_exists('epay_channel_alipay', $settings) || !empty($settings['epay_channel_alipay']);
}
if ($gateway_id === 'epay_wxpay') {
return !array_key_exists('epay_channel_wxpay', $settings) || !empty($settings['epay_channel_wxpay']);
}
return true;
}
/**
* @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);
}
}
+672
View File
@@ -0,0 +1,672 @@
<?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']);
$rewards_text = sanitize_text_field((string) ($settings['post_rewards_text'] ?? ''));
if ($rewards_text === '') {
$rewards_text = '赞赏';
}
$rewards_amounts = array_values(array_filter(array_map(static function ($value): float {
return round((float) $value, 2);
}, explode(',', (string) ($settings['post_rewards_amounts'] ?? '5,10,20,50,100'))), static fn(float $value): bool => $value > 0));
if ($rewards_amounts === []) {
$rewards_amounts = [5, 10, 20, 50, 100];
}
$rewards_min = max(0.01, round((float) ($settings['post_rewards_min'] ?? 1), 2));
$rewards_max = max($rewards_min, round((float) ($settings['post_rewards_max'] ?? 1000), 2));
$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,
// On-site wallet tipping.
'rewards_enabled' => $rewards_enabled,
'post_rewards_s' => $rewards_enabled,
'rewards_text' => $rewards_text,
'rewards_btn_text' => $rewards_text,
'rewards_amounts' => $rewards_amounts,
'rewards_custom' => !isset($settings['post_rewards_custom']) || !empty($settings['post_rewards_custom']),
'rewards_min' => $rewards_min,
'rewards_max' => $rewards_max,
// 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();
$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)));
if ($title_max < $title_min) {
$title_max = $title_min;
}
$review_mode = sanitize_key((string) ($settings['front_submit_review_mode'] ?? 'pending'));
if (!in_array($review_mode, ['pending', 'publish', 'draft'], true)) {
$review_mode = 'pending';
}
$editor_mode = sanitize_key((string) ($settings['front_submit_editor_mode'] ?? 'rich'));
if (!in_array($editor_mode, ['rich', 'markdown', 'plain'], true)) {
$editor_mode = 'rich';
}
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' => $title_min,
'title_max' => $title_max,
'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_values(array_filter(array_map('intval', $settings['post_article_cat']))) : [],
'default_user_id' => max(0, (int) ($settings['post_article_user'] ?? 0)),
'guest_enabled' => !empty($settings['front_submit_guest_enabled']),
'allow_title' => !isset($settings['front_submit_title']) || !empty($settings['front_submit_title']),
'allow_content' => !isset($settings['front_submit_content']) || !empty($settings['front_submit_content']),
'allow_excerpt' => !isset($settings['front_submit_excerpt']) || !empty($settings['front_submit_excerpt']),
'allow_cover' => !isset($settings['front_submit_cover']) || !empty($settings['front_submit_cover']),
'allow_category' => !isset($settings['front_submit_category']) || !empty($settings['front_submit_category']),
'allow_tags' => !isset($settings['front_submit_tags']) || !empty($settings['front_submit_tags']),
'tags_s' => !isset($settings['front_submit_tags']) || !empty($settings['front_submit_tags']),
'cover_required' => !empty($settings['front_submit_cover_required']),
'max_images' => max(0, min(50, (int) ($settings['front_submit_max_images'] ?? 5))),
'review_mode' => $review_mode,
'editor_mode' => $editor_mode,
'draft_save' => !isset($settings['front_submit_draft_save']) || !empty($settings['front_submit_draft_save']),
'leave_warning' => !isset($settings['front_submit_leave_warning']) || !empty($settings['front_submit_leave_warning']),
'captcha' => !empty($settings['front_submit_captcha']) || !empty($settings['verification_newposts_s']),
'pay_free_s' => !isset($settings['post_article_pay_free_s']) || !empty($settings['post_article_pay_free_s']),
'pay_price_min' => max(0, (float) ($settings['post_article_pay_price_min'] ?? 0)),
'pay_price_max' => max(0, (float) ($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'),
// Extended zibll-parity settings.
'close_comments' => !empty($settings['comment_close_comments']),
'hot_threshold' => max(1, min(999, (int) ($settings['comment_hot_threshold'] ?? 10))),
'quick_s' => !isset($settings['comment_quick_s']) || !empty($settings['comment_quick_s']),
'quick_phrases' => self::parse_comment_quick_phrases((string) ($settings['comment_quick_often'] ?? '')),
'city_s' => !isset($settings['comment_city_s']) || !empty($settings['comment_city_s']),
'city_type' => self::normalize_comment_city_type((string) ($settings['comment_city_type'] ?? 'province_city')),
];
}
/**
* @return array<int,string>
*/
private static function parse_comment_quick_phrases(string $raw): array {
if (trim($raw) === '') {
$raw = "不错,学习了\n感谢分享\n期待更新";
}
$lines = preg_split('/\r\n|\r|\n/', $raw) ?: [];
$out = [];
foreach ($lines as $line) {
$line = trim((string) $line);
if ($line !== '') {
$out[] = $line;
}
}
return array_values(array_unique($out));
}
private static function normalize_comment_city_type(string $type): string {
$type = sanitize_key($type);
// Map legacy zibll pro/city labels.
if ($type === 'pro') {
return 'province';
}
if ($type === 'city') {
return 'province_city';
}
return in_array($type, ['province_city', 'province', 'country_province'], true)
? $type
: 'province_city';
}
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;
}
}
File diff suppressed because it is too large. Load diff
+515
View File
@@ -0,0 +1,515 @@
<?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',
];
$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',
];
}
}
+419
View File
@@ -0,0 +1,419 @@
<?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', '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,level,posts,comments,credits,favorites,auth';
$tabs = is_array($raw_tabs)
? $raw_tabs
: preg_split('/[\s,]+/', (string) $raw_tabs, -1, PREG_SPLIT_NO_EMPTY);
// Built-in switchable panels. VIP is always auto-injected when enabled
// by AKSRT\User\Center — keep it allowed for explicit config too.
$allowed_tabs = ['profile', 'level', 'posts', 'comments', 'favorites', 'credits', 'vip', 'orders', 'auth', 'settings'];
$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';
}
// Ensure VIP appears in configured tabs when membership is on, even if
// the stored option string is older and omits it.
$vip_on = !empty(Settings::get_payment_vip_settings()['enabled']);
if ($vip_on && !in_array('vip', $normalized_tabs, true)) {
// Prefer after credits, else after favorites, else append before settings.
$insert_at = count($normalized_tabs);
foreach (['credits', 'favorites', 'posts'] as $anchor) {
$idx = array_search($anchor, $normalized_tabs, true);
if ($idx !== false) {
$insert_at = (int) $idx + 1;
break;
}
}
array_splice($normalized_tabs, $insert_at, 0, ['vip']);
}
if (!$vip_on) {
$normalized_tabs = array_values(array_filter(
$normalized_tabs,
static fn(string $t): bool => $t !== 'vip'
));
}
return [
'tabs' => $normalized_tabs,
'avatar_upload' => !isset($settings['allow_avatar_upload']) || !empty($settings['allow_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,allow_apply:bool,disable_desc:string,desc:string,types:array<string,array{slug:string,label:string,badge_text:string,fee:float,free_applications:int,require_real_name:bool}>},
* 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();
$ban_notice_text = trim((string) ($settings['user_report_ban_notice_text'] ?? ''));
$verify_types = [
'personal' => [
'slug' => 'personal',
'label' => __('个人认证', 'aksrt'),
'badge_text' => trim((string) ($settings['user_verify_personal_badge_text'] ?? '')) ?: __('个人认证', 'aksrt'),
'fee' => max(0, round((float) ($settings['user_verify_personal_fee'] ?? 0), 2)),
'free_applications' => max(0, (int) ($settings['user_verify_personal_free_applications'] ?? 0)),
'require_real_name' => true,
],
'enterprise' => [
'slug' => 'enterprise',
'label' => __('企业认证', 'aksrt'),
'badge_text' => trim((string) ($settings['user_verify_enterprise_badge_text'] ?? '')) ?: __('企业认证', 'aksrt'),
'fee' => max(0, round((float) ($settings['user_verify_enterprise_fee'] ?? 0), 2)),
'free_applications' => max(0, (int) ($settings['user_verify_enterprise_free_applications'] ?? 0)),
'require_real_name' => true,
],
];
$medal = self::parse_medal_catalog($settings);
$provider = sanitize_key((string) ($settings['user_verify_provider'] ?? 'aliyun'));
if (!in_array($provider, ['aliyun', 'tencent'], true)) {
$provider = 'aliyun';
}
return [
'verify' => [
'enabled' => !isset($settings['user_verify_enabled']) || !empty($settings['user_verify_enabled']),
'public_display' => !isset($settings['user_verify_public_display']) || !empty($settings['user_verify_public_display']),
'allow_apply' => !isset($settings['user_verify_allow_apply']) || !empty($settings['user_verify_allow_apply']),
'disable_desc' => (string) ($settings['user_verify_disable_desc'] ?? ''),
'desc' => (string) ($settings['user_verify_desc'] ?? ''),
'provider' => $provider,
'allow_manual_fallback' => !isset($settings['user_verify_allow_manual_fallback']) || !empty($settings['user_verify_allow_manual_fallback']),
'poll_interval_seconds' => max(3, min(60, (int) ($settings['user_verify_poll_interval_seconds'] ?? 5))),
'types' => $verify_types,
],
'level' => [
'enabled' => !isset($settings['user_level_s']) || !empty($settings['user_level_s']),
'public_display' => !isset($settings['user_level_public_display']) || !empty($settings['user_level_public_display']),
'max' => max(3, min(30, (int) ($settings['user_level_max'] ?? 10))),
],
'medal' => $medal,
// Alias for existing call sites that still read badge.*; dictionary is derived from medal catalog.
'badge' => [
'enabled' => $medal['enabled'],
'public_display' => $medal['public_display'],
'limit' => $medal['limit'],
'dictionary' => array_values(array_map(static function (array $item): array {
return [
'slug' => $item['slug'],
'label' => $item['name'],
'tone' => 'info',
'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',
'comment',
'followed',
'like',
'comment_like',
'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;
}
}