Rewrite /users/level to Zibll-like user_level_s/max/opt and user_integral_opt. Runtime grants XP with day_max, resolves level, hooks login/register/post/comment/like/favorite/follow. Profile signals read integral-backed levels. audit errors=0.
310 lines
11 KiB
PHP
310 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* AKSRT User Home — 公开用户主页协调类
|
|
*
|
|
* 负责公开用户主页(/user/{nicename}/)的初始化和 tab 注册。
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class AKSRT_User_Home {
|
|
|
|
private static $instance = null;
|
|
|
|
/** @var array Tab 注册表 */
|
|
private $tabs = [];
|
|
|
|
private function __construct() {}
|
|
|
|
public static function get_instance(): self {
|
|
if (null === self::$instance) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
public function init(): void {
|
|
$this->register_default_tabs();
|
|
}
|
|
|
|
/**
|
|
* 注册默认公开 Tab
|
|
*/
|
|
private function register_default_tabs(): void {
|
|
$this->tabs = [
|
|
'posts' => [
|
|
'label' => __('文章', 'aksrt'),
|
|
'icon' => 'file-alt',
|
|
'priority' => 10,
|
|
],
|
|
'comments' => [
|
|
'label' => __('评论', 'aksrt'),
|
|
'icon' => 'comment',
|
|
'priority' => 20,
|
|
],
|
|
'favorites' => [
|
|
'label' => __('收藏', 'aksrt'),
|
|
'icon' => 'star',
|
|
'priority' => 30,
|
|
],
|
|
'credits' => [
|
|
'label' => class_exists('AKSRT_Settings')
|
|
? sprintf(__('我的%s', 'aksrt'), AKSRT_Settings::get_currency_name())
|
|
: __('我的积分', 'aksrt'),
|
|
'icon' => 'wallet',
|
|
'priority' => 40,
|
|
],
|
|
'product' => [
|
|
'label' => __('商品', 'aksrt'),
|
|
'icon' => 'shopping-bag',
|
|
'priority' => 50,
|
|
],
|
|
'plate' => [
|
|
'label' => __('圈子', 'aksrt'),
|
|
'icon' => 'users',
|
|
'priority' => 60,
|
|
],
|
|
'forum' => [
|
|
'label' => __('论坛', 'aksrt'),
|
|
'icon' => 'comments',
|
|
'priority' => 70,
|
|
],
|
|
'follow' => [
|
|
'label' => __('粉丝', 'aksrt'),
|
|
'icon' => 'user-plus',
|
|
'priority' => 80,
|
|
],
|
|
];
|
|
|
|
/**
|
|
* 允许插件/子主题注册额外公开 Tab
|
|
*
|
|
* @param array $tabs Tab 注册表
|
|
*/
|
|
$this->tabs = apply_filters('aksrt_user_home_tabs', $this->tabs);
|
|
}
|
|
|
|
/**
|
|
* 获取 Tab 注册表
|
|
*/
|
|
public function get_tabs(): array {
|
|
return $this->tabs;
|
|
}
|
|
|
|
/**
|
|
* 获取当前用户主页实际可见的公开 Tab。
|
|
*/
|
|
public function get_visible_tabs(bool $is_own_profile = false): array {
|
|
$settings = class_exists('AKSRT_Settings')
|
|
? AKSRT_Settings::get_user_home_settings()
|
|
: [
|
|
'tabs' => ['posts', 'comments', 'favorites'],
|
|
];
|
|
|
|
$visible = [];
|
|
foreach ((array) ($settings['tabs'] ?? []) as $key) {
|
|
if (!isset($this->tabs[$key])) {
|
|
continue;
|
|
}
|
|
|
|
if ($key === 'credits' && (!$is_own_profile || !class_exists('AKSRT_Settings') || !AKSRT_Settings::credits_enabled())) {
|
|
continue;
|
|
}
|
|
|
|
$visible[$key] = $this->tabs[$key];
|
|
}
|
|
|
|
if (empty($visible)) {
|
|
foreach (['posts', 'comments', 'favorites'] as $fallback) {
|
|
if (isset($this->tabs[$fallback])) {
|
|
$visible[$fallback] = $this->tabs[$fallback];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $visible;
|
|
}
|
|
|
|
/**
|
|
* 获取公开展示的身份信号(认证 / 等级 / 徽章)。
|
|
*
|
|
* @return array<int,array{label:string,icon:string,tone:string,icon_url?:string,type?:string,slug?:string,wear?:bool}>
|
|
*/
|
|
public function get_profile_signals(int $user_id): array {
|
|
if ($user_id <= 0 || !class_exists('AKSRT_Settings')) {
|
|
return [];
|
|
}
|
|
|
|
$settings = AKSRT_Settings::get_user_identity_settings();
|
|
$signals = [];
|
|
|
|
if (!empty($settings['verify']['enabled']) && !empty($settings['verify']['public_display']) && $this->is_user_verified($user_id, $settings['verify'])) {
|
|
$signals[] = [
|
|
'type' => 'verify',
|
|
'label' => (string) $settings['verify']['badge_text'],
|
|
'icon' => 'check-circle',
|
|
'tone' => 'success',
|
|
];
|
|
}
|
|
|
|
if (!empty($settings['level']['enabled']) && !empty($settings['level']['public_display'])) {
|
|
$level = $this->get_user_level($user_id, $settings['level']);
|
|
if ($level > 0) {
|
|
$signals[] = [
|
|
'type' => 'level',
|
|
'label' => (string) $settings['level']['label_prefix'] . $level,
|
|
'icon' => 'layer-group',
|
|
'tone' => 'info',
|
|
];
|
|
}
|
|
}
|
|
|
|
if (!empty($settings['badge']['enabled']) && !empty($settings['badge']['public_display'])) {
|
|
$limit = max(1, (int) ($settings['badge']['limit'] ?? 3));
|
|
if (class_exists('AKSRT_User_Badges')) {
|
|
$list = AKSRT_User_Badges::list_for_user($user_id, true);
|
|
$badge_count = 0;
|
|
foreach ((array) ($list['items'] ?? []) as $item) {
|
|
if ($badge_count >= $limit) {
|
|
break;
|
|
}
|
|
$signals[] = [
|
|
'type' => 'badge',
|
|
'slug' => (string) ($item['slug'] ?? ''),
|
|
'label' => (string) ($item['label'] ?? ''),
|
|
'icon' => 'award',
|
|
'tone' => (string) ($item['tone'] ?? 'neutral'),
|
|
'icon_url' => (string) ($item['icon_url'] ?? ''),
|
|
'wear' => !empty($item['wear']),
|
|
];
|
|
$badge_count++;
|
|
}
|
|
} else {
|
|
$badge_map = [];
|
|
foreach ((array) ($settings['badge']['dictionary'] ?? []) as $item) {
|
|
if (!empty($item['slug'])) {
|
|
$badge_map[(string) $item['slug']] = $item;
|
|
}
|
|
}
|
|
$assigned = get_user_meta($user_id, 'aksrt_user_badges', true);
|
|
if (!is_array($assigned)) {
|
|
$assigned = preg_split('/[\s,]+/', (string) $assigned, -1, PREG_SPLIT_NO_EMPTY);
|
|
}
|
|
$wear = sanitize_key((string) get_user_meta($user_id, 'aksrt_wear_badge', true));
|
|
$assigned = array_values(array_filter(array_map(static function ($slug) {
|
|
return sanitize_key((string) $slug);
|
|
}, (array) $assigned)));
|
|
if ($wear !== '' && in_array($wear, $assigned, true) && isset($badge_map[$wear])) {
|
|
$assigned = array_values(array_unique(array_merge([$wear], $assigned)));
|
|
}
|
|
$badge_count = 0;
|
|
foreach ($assigned as $slug) {
|
|
if ($slug === '' || !isset($badge_map[$slug])) {
|
|
continue;
|
|
}
|
|
$signals[] = [
|
|
'type' => 'badge',
|
|
'slug' => $slug,
|
|
'label' => (string) $badge_map[$slug]['label'],
|
|
'icon' => 'award',
|
|
'tone' => (string) ($badge_map[$slug]['tone'] ?? 'neutral'),
|
|
'icon_url' => (string) ($badge_map[$slug]['icon_url'] ?? ''),
|
|
'wear' => ($wear !== '' && $slug === $wear),
|
|
];
|
|
$badge_count++;
|
|
if ($badge_count >= $limit) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $signals;
|
|
}
|
|
|
|
/**
|
|
* 获取公开治理提示。
|
|
*
|
|
* @return array{state:string,text:string}|array{}
|
|
*/
|
|
public function get_profile_notice(int $user_id): array {
|
|
if ($user_id <= 0 || !class_exists('AKSRT_Settings')) {
|
|
return [];
|
|
}
|
|
|
|
$settings = AKSRT_Settings::get_user_identity_settings();
|
|
if (empty($settings['report']['enabled']) || empty($settings['report']['public_notice'])) {
|
|
return [];
|
|
}
|
|
|
|
$state = sanitize_key((string) get_user_meta($user_id, 'aksrt_user_ban_state', true));
|
|
if (!in_array($state, ['muted', 'restricted', 'banned'], true)) {
|
|
return [];
|
|
}
|
|
|
|
return [
|
|
'state' => $state,
|
|
'text' => (string) $settings['report']['ban_notice_text'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 公开主页是否显示举报入口。
|
|
*/
|
|
public function show_report_entry(): bool {
|
|
if (!class_exists('AKSRT_Settings')) {
|
|
return false;
|
|
}
|
|
|
|
$settings = AKSRT_Settings::get_user_identity_settings();
|
|
return !empty($settings['report']['enabled']) && !empty($settings['report']['public_entry']);
|
|
}
|
|
|
|
/**
|
|
* @param array{accept_email_verified:bool} $verify_settings
|
|
*/
|
|
private function is_user_verified(int $user_id, array $verify_settings): bool {
|
|
$manual = sanitize_key((string) get_user_meta($user_id, 'aksrt_user_verify_status', true));
|
|
if ($manual === 'verified') {
|
|
return true;
|
|
}
|
|
|
|
if (!empty($verify_settings['accept_email_verified']) && class_exists('AKSRT_Auth_Service')) {
|
|
return AKSRT_Auth_Service::user_is_email_verified($user_id);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param array{source?:string,thresholds?:array<int,int>} $level_settings
|
|
*/
|
|
private function get_user_level(int $user_id, array $level_settings): int {
|
|
$source = (string) ($level_settings['source'] ?? 'integral');
|
|
if ($source === 'manual_meta') {
|
|
return max(0, (int) get_user_meta($user_id, 'aksrt_user_level', true));
|
|
}
|
|
if (class_exists('AKSRT_User_Level')) {
|
|
return AKSRT_User_Level::get_level($user_id);
|
|
}
|
|
// Fallback: credits_balance thresholds (legacy) if integral class missing.
|
|
$score = function_exists('aksrt_user_credits') ? (int) aksrt_user_credits($user_id) : 0;
|
|
$level = 0;
|
|
foreach ((array) ($level_settings['thresholds'] ?? []) as $candidate => $threshold) {
|
|
if ($score >= (int) $threshold) {
|
|
$level = (int) $candidate;
|
|
}
|
|
}
|
|
return $level;
|
|
}
|
|
|
|
/**
|
|
* 判断当前页面是否为公开用户主页
|
|
*/
|
|
public static function is_page(): bool {
|
|
return function_exists('aksrt_is_user_home_page') && aksrt_is_user_home_page();
|
|
}
|
|
}
|