Files
wordpress-theme-aksrt/inc/classes/class-aksrt-user.php
T
kate522andClaude Fable 5 a86649946a feat(user-center): 用户中心前台 1:1 列表/URL/关注/身份
补齐 orders/cart/revenue/messages 页面 URL helper,修复私有中心商务与消息死链,公共主页商品/关注接入真实数据,开启身份 chips governance,并注册消息中心页面模板。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 11:03:30 +08:00

1257 lines
43 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* AKSRT Theme - User System Module v3.2
*
* 当前职责:
* - /user/{昵称} → 用户主页(自定义重写 + 模板拦截)
* - profile 页面仍保留模板页自动创建、缓存加速、修改即刷新
*
* 认证路由(/login /register /bind-account /reset-password /logout)
* 已迁移至 AKSRT_Auth_Router,认证业务已迁移至 AKSRT_Auth_Service。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
// ============================================================
// 一、模板注册表 — 所有特殊页面的"身份证"
// 修改此处即可增删模板页面,无需改动其他逻辑
// ============================================================
$_aksrt_special_templates = [
'login' => [
'file' => 'templates/pages/page-login.php',
'slug' => 'login',
'title' => '登录',
'fallback' => '/login/',
],
'register' => [
'file' => 'templates/pages/page-register.php',
'slug' => 'register',
'title' => '注册',
'fallback' => '/register/',
],
'bind' => [
'file' => 'templates/pages/page-bind-account.php',
'slug' => 'bind-account',
'title' => '绑定账号',
'fallback' => '/bind-account/',
],
'reset-password' => [
'file' => 'templates/pages/page-reset-password.php',
'slug' => 'reset-password',
'title' => '重置密码',
'fallback' => '/reset-password/',
],
'verify-email' => [
'file' => 'templates/pages/page-verify-email.php',
'slug' => 'verify-email',
'title' => '邮箱验证',
'fallback' => '/verify-email/',
],
'categories' => [
'file' => 'templates/pages/page-categories.php',
'slug' => 'categories',
'title' => '分类目录',
'fallback' => '/categories/',
],
'friends' => [
'file' => 'templates/pages/page-friends.php',
'slug' => 'friends',
'title' => '友情链接',
'fallback' => '/friends/',
],
'profile' => [
'file' => 'templates/pages/page-profile.php',
'slug' => 'profile',
'title' => '个人中心',
'fallback' => '/',
],
];
// Zibll-style v2 routing keeps only true fixed template pages in the registry.
$_aksrt_special_templates = [
'sign' => [
'file' => 'templates/pages/page-sign.php',
'slug' => 'sign',
'title' => '登录/注册',
'fallback' => '/sign/',
],
'bind' => [
'file' => 'templates/pages/page-bind-account.php',
'slug' => 'bind-account',
'title' => '绑定账号',
'fallback' => '/bind-account/',
],
'reset-password' => [
'file' => 'templates/pages/page-reset-password.php',
'slug' => 'reset-password',
'title' => '重置密码',
'fallback' => '/reset-password/',
],
'verify-email' => [
'file' => 'templates/pages/page-verify-email.php',
'slug' => 'verify-email',
'title' => '邮箱验证',
'fallback' => '/verify-email/',
],
'categories' => [
'file' => 'templates/pages/page-categories.php',
'slug' => 'categories',
'title' => '分类目录',
'fallback' => '/categories/',
],
'friends' => [
'file' => 'templates/pages/page-friends.php',
'slug' => 'friends',
'title' => '友情链接',
'fallback' => '/friends/',
],
'profile' => [
'file' => 'templates/pages/page-profile.php',
'slug' => 'profile',
'title' => '个人中心',
'fallback' => '/profile/',
],
];
/** @var array 缓存 key=模板文件路径 => value=WP_Post|null */
$_aksrt_template_page_cache = [];
// ============================================================
// 二、核心工具函数
// ============================================================
/**
* 查找使用指定模板文件的页面(按模板匹配,自适应任意 slug)
*
* @param string $template_file 相对于主题的模板路径
* @return WP_Post|null
*/
function aksrt_find_template_page(string $template_file) {
global $_aksrt_template_page_cache;
if (isset($_aksrt_template_page_cache[$template_file])) {
return $_aksrt_template_page_cache[$template_file];
}
$page = null;
$pages = get_pages([
'meta_key' => '_wp_page_template',
'meta_value' => $template_file,
'number' => 1,
'post_status' => 'publish',
]);
if (!empty($pages)) {
$page = $pages[0];
}
$_aksrt_template_page_cache[$template_file] = $page;
return $page;
}
/**
* 获取使用指定模板的页面 URL
*
* @param string $key 注册表 key(当前仅 profile 使用)
* @return string 页面 URL 或回退地址
*/
function aksrt_get_template_url(string $key): string {
global $_aksrt_special_templates;
if (!isset($_aksrt_special_templates[$key])) {
return home_url('/');
}
$cfg = $_aksrt_special_templates[$key];
$page = aksrt_find_template_page($cfg['file']);
if (!$page) {
aksrt_ensure_template_page($key);
$page = aksrt_find_template_page($cfg['file']);
}
if ($page) {
return get_permalink($page->ID);
}
return home_url($cfg['fallback']);
}
/**
* 定位模板文件的绝对路径
*
* @param string $template_file 相对于主题的路径
* @return string|false 文件绝对路径或 false
*/
function aksrt_locate_template_file(string $template_file) {
$path = locate_template([$template_file]);
if ($path) return $path;
$full = AKSRT_DIR . '/' . $template_file;
return file_exists($full) ? $full : false;
}
/**
* Resolve a public-facing page template file from stored meta or page slug.
*/
function aksrt_resolve_page_template_file(string $template_file = '', string $post_slug = ''): string {
$template_file = ltrim($template_file, '/\\');
$post_slug = sanitize_title($post_slug);
$legacy_auth_templates = [
'templates/pages/page-login.php' => 'login',
'templates/pages/page-register.php' => 'register',
'templates/pages/page-bind-account.php' => 'bind',
'templates/pages/page-reset-password.php' => 'reset-password',
'templates/pages/page-verify-email.php' => 'verify-email',
];
if ($template_file !== '') {
if ($template_file === 'templates/pages/page-sign.php') {
return aksrt_get_auth_template_file(aksrt_get_auth_tab(), true);
}
if (!empty($legacy_auth_templates[$template_file])) {
return aksrt_get_auth_template_file((string) $legacy_auth_templates[$template_file], true);
}
if (strpos($template_file, 'templates/pages/') === 0 && aksrt_locate_template_file($template_file)) {
return $template_file;
}
}
if ($post_slug !== '') {
$auth_slug_map = [
'sign' => aksrt_get_auth_tab(),
'login' => 'login',
'register' => 'register',
'bind-account' => 'bind',
'reset-password' => 'reset-password',
'verify-email' => 'verify-email',
];
if (!empty($auth_slug_map[$post_slug])) {
return aksrt_get_auth_template_file((string) $auth_slug_map[$post_slug], true);
}
$slug_map = [
'categories' => 'templates/pages/page-categories.php',
'friends' => 'templates/pages/page-friends.php',
'profile' => 'templates/pages/page-profile.php',
];
if (!empty($slug_map[$post_slug]) && aksrt_locate_template_file($slug_map[$post_slug])) {
return $slug_map[$post_slug];
}
}
return '';
}
/**
* Normalize auth view keys used by the unified sign page.
*/
function aksrt_normalize_auth_tab(string $tab): string {
$tab = sanitize_key($tab);
switch ($tab) {
case 'signin':
case 'login':
return 'login';
case 'signup':
case 'register':
return 'register';
case 'bind':
case 'bind-account':
return 'bind';
case 'reset':
case 'lostpassword':
case 'reset-password':
return 'reset-password';
case 'verify':
case 'verify-email':
return 'verify-email';
default:
return 'login';
}
}
/**
* Whether an auth tab should be rendered by the unified sign page.
*/
function aksrt_auth_tab_uses_unified_sign_page(string $tab): bool {
return in_array(
aksrt_normalize_auth_tab($tab),
['login', 'register'],
true
);
}
/**
* Resolve the auth template file, preferring the unified sign page when present.
*/
function aksrt_get_auth_template_file(string $tab = 'login', bool $prefer_sign_page = true): string {
$normalized_tab = aksrt_normalize_auth_tab($tab);
$sign_template = 'templates/pages/page-sign.php';
if (
$prefer_sign_page
&& aksrt_auth_tab_uses_unified_sign_page($normalized_tab)
&& aksrt_locate_template_file($sign_template)
) {
return $sign_template;
}
switch ($normalized_tab) {
case 'register':
return 'templates/pages/page-register.php';
case 'bind':
return 'templates/pages/page-bind-account.php';
case 'reset-password':
return 'templates/pages/page-reset-password.php';
case 'verify-email':
return 'templates/pages/page-verify-email.php';
default:
return 'templates/pages/page-login.php';
}
}
/**
* Return the active auth view for sign routes and the sign page template.
*/
function aksrt_get_auth_tab(): string {
$query_tab = get_query_var('aksrt_auth_tab');
if (is_string($query_tab) && $query_tab !== '') {
return aksrt_normalize_auth_tab($query_tab);
}
if (!empty($_GET['tab'])) {
return aksrt_normalize_auth_tab((string) wp_unslash($_GET['tab']));
}
$post = get_queried_object();
if ($post instanceof WP_Post) {
return aksrt_normalize_auth_tab((string) $post->post_name);
}
return 'login';
}
/**
* 清除模板页面缓存
*/
function aksrt_clear_template_page_cache(): void {
global $_aksrt_template_page_cache;
$_aksrt_template_page_cache = [];
}
/**
* Prepare the public user-home template context and return its template path.
*/
function aksrt_get_user_home_template_path(): string {
global $_aksrt_special_templates, $aksrt_profile_user;
$nicename = (string) get_query_var('user_nicename');
if ($nicename === '') {
return '';
}
$user = get_user_by('slug', $nicename);
if (!$user instanceof WP_User) {
return '';
}
$template_path = aksrt_locate_template_file('templates/pages/page-user-home.php');
if (!$template_path) {
return '';
}
$aksrt_profile_user = $user;
return $template_path;
}
/**
* Resolve the active special-page template path for the current request.
*/
function aksrt_get_current_special_template_path(): string {
if (get_query_var('user_nicename')) {
return aksrt_get_user_home_template_path();
}
if (get_query_var('aksrt_user_center')) {
$profile_template = aksrt_locate_template_file('templates/pages/page-profile.php');
return $profile_template ?: '';
}
if (get_query_var('aksrt_sign')) {
$auth_template = aksrt_get_auth_template_file((string) get_query_var('aksrt_auth_tab'), true);
$auth_template_path = aksrt_locate_template_file($auth_template);
return $auth_template_path ?: '';
}
if (is_page()) {
$page_id = get_queried_object_id();
if ($page_id > 0) {
$page_template = get_post_meta($page_id, '_wp_page_template', true);
$resolved_template = aksrt_resolve_page_template_file(
is_string($page_template) ? $page_template : '',
(string) get_post_field('post_name', $page_id)
);
if ($resolved_template !== '') {
$resolved_path = aksrt_locate_template_file($resolved_template);
if ($resolved_path) {
return $resolved_path;
}
}
}
}
return '';
}
/**
* 根据当前请求路径识别系统模板页 key。
*
* 用于前台直接访问固定 slug 时的自动补页。
*/
function aksrt_get_requested_template_key(): string {
global $_aksrt_special_templates;
$request_uri = isset($_SERVER['REQUEST_URI']) ? (string) $_SERVER['REQUEST_URI'] : '';
if ($request_uri === '') {
return '';
}
$request_path = wp_parse_url($request_uri, PHP_URL_PATH);
if (!is_string($request_path) || $request_path === '') {
return '';
}
$home_path = (string) wp_parse_url(home_url('/'), PHP_URL_PATH);
if ($home_path !== '' && str_starts_with($request_path, $home_path)) {
$request_path = (string) substr($request_path, strlen($home_path));
}
$request_slug = trim($request_path, '/');
if ($request_slug === '') {
return '';
}
foreach ($_aksrt_special_templates as $key => $cfg) {
$slug = isset($cfg['slug']) ? sanitize_title((string) $cfg['slug']) : '';
if ($slug !== '' && $slug === $request_slug) {
return (string) $key;
}
}
return '';
}
/**
* 确保使用指定模板的页面存在,缺失则自动创建
*
* @param string $key 注册表 key
*/
function aksrt_ensure_template_page(string $key): void {
global $_aksrt_special_templates;
if (!isset($_aksrt_special_templates[$key])) return;
$cfg = $_aksrt_special_templates[$key];
$desired_slug = sanitize_title($cfg['slug'] ?? $key);
// 已有页面使用此模板?
if (aksrt_find_template_page($cfg['file'])) return;
if ($desired_slug !== '') {
$existing_page = get_page_by_path($desired_slug, OBJECT, 'page');
if ($existing_page instanceof WP_Post) {
if ($existing_page->post_status !== 'publish') {
wp_update_post([
'ID' => $existing_page->ID,
'post_status' => 'publish',
]);
}
update_post_meta($existing_page->ID, '_wp_page_template', $cfg['file']);
aksrt_clear_template_page_cache();
return;
}
}
// 自动创建
$page_id = wp_insert_post([
'post_title' => $cfg['title'],
'post_content' => '',
'post_status' => 'publish',
'post_type' => 'page',
'post_name' => $desired_slug !== '' ? $desired_slug : $key,
'comment_status' => 'closed',
'ping_status' => 'closed',
]);
if (!is_wp_error($page_id) && $page_id > 0) {
update_post_meta($page_id, '_wp_page_template', $cfg['file']);
aksrt_clear_template_page_cache();
}
}
// ============================================================
// 三、URL 路由与模板加载
// ============================================================
// --- 3a. /user/{昵称} 自定义重写 ---
add_filter('query_vars', function ($vars) {
$vars[] = 'aksrt_user_center';
$vars[] = 'user_nicename';
return $vars;
});
add_action('template_redirect', function () {
if (is_admin() || wp_doing_ajax() || is_feed() || is_embed() || is_preview()) {
return;
}
$resolved_path = aksrt_get_current_special_template_path();
if ($resolved_path === '') {
return;
}
status_header(200);
load_template($resolved_path, true);
exit;
}, 100);
add_action('init', function () {
add_rewrite_tag('%aksrt_user_center%', '([^&]+)');
add_rewrite_tag('%user_nicename%', '([^/]+)');
add_rewrite_rule('^profile/?$', 'index.php?aksrt_user_center=1', 'top');
add_rewrite_rule('^user/([^/]+)/?$', 'index.php?user_nicename=$matches[1]', 'top');
});
// --- 3b. 统一模板拦截器(核心)---
// 拦截所有使用主题特殊模板的页面请求,确保正确加载
add_filter('template_include', function ($template) {
$resolved_path = aksrt_get_current_special_template_path();
if ($resolved_path !== '') {
return $resolved_path;
}
return $template;
global $_aksrt_special_templates;
// A) /user/{昵称} 路由 → 加载公开主页模板
$nicename = get_query_var('user_nicename');
if (!empty($nicename)) {
$user = get_user_by('slug', $nicename);
if (!$user) return $template; // 不存在的用户 → 默认 404
$tpl = aksrt_locate_template_file('templates/pages/page-user-home.php');
if (!$tpl) {
// 兜底:如果新模板不存在,回退到旧 profile 模板
$tpl = aksrt_locate_template_file($_aksrt_special_templates['profile']['file']);
}
if (!$tpl) return $template;
global $aksrt_profile_user;
$aksrt_profile_user = $user;
return $tpl;
}
// B) 普通页面 → 检测是否使用了我们的特殊模板,确保加载正确的模板文件
// (防止 WP 在某些情况下未能正确解析自定义页面模板)
if (is_page()) {
$page_id = get_queried_object_id();
if ($page_id) {
$page_template = get_post_meta($page_id, '_wp_page_template', true);
if ($page_template && strpos($page_template, 'templates/pages/') === 0) {
$tpl = aksrt_locate_template_file($page_template);
if ($tpl) return $tpl;
}
}
}
return $template;
}, 99);
add_filter('template_include', function ($template) {
$resolved_path = aksrt_get_current_special_template_path();
if ($resolved_path !== '') {
return $resolved_path;
}
if (get_query_var('aksrt_user_center')) {
$profile_template = aksrt_locate_template_file('templates/pages/page-profile.php');
if ($profile_template) {
return $profile_template;
}
}
if (is_page()) {
$page_id = get_queried_object_id();
if ($page_id > 0) {
$page_template = get_post_meta($page_id, '_wp_page_template', true);
$resolved_template = aksrt_resolve_page_template_file(
is_string($page_template) ? $page_template : '',
(string) get_post_field('post_name', $page_id)
);
if ($resolved_template !== '') {
$resolved_path = aksrt_locate_template_file($resolved_template);
if ($resolved_path) {
return $resolved_path;
}
}
}
}
return $template;
}, 100);
// ============================================================
// 四、自动维护 + flush
// ============================================================
add_action('init', function () {
global $_aksrt_special_templates;
// 后台/AJAX:检查并创建缺失页面
$pages_bootstrapped = get_option('aksrt_template_pages_bootstrapped');
$requested_template_key = (!is_admin() && !wp_doing_ajax()) ? aksrt_get_requested_template_key() : '';
if (is_admin() || wp_doing_ajax() || $pages_bootstrapped !== AKSRT_VERSION) {
foreach ($_aksrt_special_templates as $key => $cfg) {
if ($key === 'profile') {
continue;
}
aksrt_ensure_template_page($key);
}
if ($pages_bootstrapped !== AKSRT_VERSION) {
update_option('aksrt_template_pages_bootstrapped', AKSRT_VERSION);
}
} elseif ($requested_template_key !== '' && $requested_template_key !== 'profile') {
aksrt_ensure_template_page($requested_template_key);
}
// 统一 flush 检查
$rewrite_version = AKSRT_VERSION . ':sign-profile-routing-v1';
$flushed = get_option('aksrt_user_rules_flushed');
if ($flushed !== $rewrite_version) {
flush_rewrite_rules(false);
update_option('aksrt_user_rules_flushed', $rewrite_version);
}
});
// 页面变更时清缓存
add_action('save_post', function ($pid) { if (get_post_type($pid) === 'page') aksrt_clear_template_page_cache(); });
add_action('delete_post', function ($pid) { if (get_post_type($pid) === 'page') aksrt_clear_template_page_cache(); });
// ============================================================
// 五、公开 API 函数(兼容旧代码调用)
// ============================================================
if (!function_exists('aksrt_get_user_center_url')) :
/**
* 获取本人中心 URL(私有空间入口)
*
* @param string $tab 可选 tab 参数
* @return string
*/
function aksrt_get_user_center_url(string $tab = ''): string {
if (get_option('permalink_structure')) {
$url = home_url('/profile/');
} else {
$url = add_query_arg('aksrt_user_center', '1', home_url('/'));
}
if ($tab !== '') {
$url = add_query_arg('tab', $tab, $url);
}
return $url;
}
endif;
if (!function_exists('aksrt_get_orders_page_url')) :
/**
* 订单中心页面 URL(模板 templates/pages/page-orders.php)。
* 未指派页面时返回空串,调用方应隐藏入口。
*/
function aksrt_get_orders_page_url(): string {
return function_exists('aksrt_get_page_url_by_template')
? aksrt_get_page_url_by_template('templates/pages/page-orders.php')
: '';
}
endif;
if (!function_exists('aksrt_get_cart_page_url')) :
/**
* 购物车页面 URL(模板 templates/pages/page-cart.php)。
*/
function aksrt_get_cart_page_url(): string {
return function_exists('aksrt_get_page_url_by_template')
? aksrt_get_page_url_by_template('templates/pages/page-cart.php')
: '';
}
endif;
if (!function_exists('aksrt_get_revenue_page_url')) :
/**
* 收益中心页面 URL(模板 templates/pages/page-revenue.php)。
*/
function aksrt_get_revenue_page_url(): string {
return function_exists('aksrt_get_page_url_by_template')
? aksrt_get_page_url_by_template('templates/pages/page-revenue.php')
: '';
}
endif;
if (!function_exists('aksrt_get_messages_page_url')) :
/**
* 消息中心页面 URL(模板 templates/pages/page-messages.php)。
*/
function aksrt_get_messages_page_url(): string {
return function_exists('aksrt_get_page_url_by_template')
? aksrt_get_page_url_by_template('templates/pages/page-messages.php')
: '';
}
endif;
if (!function_exists('aksrt_get_user_home_url')) :
/**
* 获取公开用户主页 URL(/user/{nicename}/)
*
* @param WP_User|int|string|null $user 用户对象、ID、nicename 或 null(当前用户)
* @return string
*/
function aksrt_get_user_home_url($user = null): string {
$slug = '';
if ($user instanceof WP_User) {
$slug = $user->user_nicename;
} elseif (is_int($user) && $user > 0) {
$u = get_userdata($user);
$slug = $u ? $u->user_nicename : '';
} elseif (is_string($user) && $user) {
$slug = $user;
} else {
$current = wp_get_current_user();
$slug = $current->ID ? $current->user_nicename : '';
}
if (!$slug) return home_url('/');
return home_url('/user/' . trailingslashit($slug));
}
endif;
if (!function_exists('aksrt_is_user_center_page')) :
/**
* 判断当前是否为本人中心页面
*/
function aksrt_is_user_center_page(): bool {
return (bool) get_query_var('aksrt_user_center')
|| (is_page_template('templates/pages/page-profile.php') && !(bool) get_query_var('user_nicename'));
}
endif;
if (!function_exists('aksrt_is_user_home_page')) :
/**
* 判断当前是否为公开用户主页
*/
function aksrt_is_user_home_page(): bool {
return (bool) get_query_var('user_nicename')
|| is_page_template('templates/pages/page-user-home.php');
}
endif;
if (!function_exists('aksrt_get_profile_url')) :
/**
* 兼容层:获取用户主页 URL
*
* @deprecated 使用 aksrt_get_user_center_url() 或 aksrt_get_user_home_url() 替代
* @param WP_User|int|string|null $user
* @return string
*/
function aksrt_get_profile_url($user = null): string {
// 无参调用 → 当前用户主页(公开页)
if ($user === null) {
return aksrt_get_user_home_url();
}
return aksrt_get_user_home_url($user);
}
endif;
if (!function_exists('aksrt_get_sign_page_url')) :
/**
* Get the unified auth page URL, with legacy route fallback while the page self-heals.
*
* @param string $tab
* @param array<string, scalar> $args
*/
function aksrt_get_sign_page_url(string $tab = 'login', array $args = []): string {
$tab = aksrt_normalize_auth_tab($tab);
$legacy_fallbacks = [
'login' => home_url('/login/'),
'register' => home_url('/register/'),
'bind' => home_url('/bind-account/'),
'reset-password' => home_url('/reset-password/'),
'verify-email' => home_url('/verify-email/'),
];
$page = aksrt_find_template_page('templates/pages/page-sign.php');
if (!$page) {
aksrt_ensure_template_page('sign');
$page = aksrt_find_template_page('templates/pages/page-sign.php');
}
if ($page instanceof WP_Post) {
$url = get_permalink($page->ID);
$args = ['tab' => $tab] + $args;
return add_query_arg($args, $url);
}
$url = $legacy_fallbacks[$tab] ?? home_url('/login/');
return !empty($args) ? add_query_arg($args, $url) : $url;
}
/**
* Determine whether the current request is served by the unified auth experience.
*/
function aksrt_is_auth_page_request(): bool {
return (bool) get_query_var('aksrt_sign')
|| is_page_template('templates/pages/page-sign.php')
|| is_page_template('templates/pages/page-login.php')
|| is_page_template('templates/pages/page-register.php')
|| is_page_template('templates/pages/page-bind-account.php')
|| is_page_template('templates/pages/page-reset-password.php')
|| is_page_template('templates/pages/page-verify-email.php');
}
endif;
if (!function_exists('aksrt_get_login_url')) :
/** 获取登录页 URL */
function aksrt_get_login_url($redirect = '') {
$url = aksrt_get_sign_page_url('login');
if ($redirect) {
$url = add_query_arg('redirect_to', urlencode($redirect), $url);
}
return $url;
}
endif;
if (!function_exists('aksrt_get_register_url')) :
/** 获取注册页 URL */
function aksrt_get_register_url() {
return aksrt_get_sign_page_url('register');
}
endif;
if (!function_exists('aksrt_get_bind_url')) :
/** 获取绑定账号页面 URL */
function aksrt_get_bind_url($transient_key = '') {
$url = aksrt_get_template_url('bind');
if ($transient_key) {
$url = add_query_arg('obk', $transient_key, $url);
}
return $url;
}
endif;
if (!function_exists('aksrt_get_logout_url')) :
/** 获取退出登录 URL */
function aksrt_get_logout_url($redirect = '') {
$target = $redirect ?: home_url('/');
$url = add_query_arg('redirect_to', rawurlencode($target), home_url('/logout/'));
return wp_nonce_url($url, 'aksrt-logout');
}
endif;
if (!function_exists('aksrt_get_reset_password_url')) :
/**
* 获取重置密码页面 URL
*
* @param string $token 主题重置 token(可选,设置密码时需要)
* @param int $user_id 用户 ID(可选,设置密码时需要)
* @return string
*/
function aksrt_get_reset_password_url(string $token = '', int $user_id = 0): string {
$url = aksrt_get_template_url('reset-password');
if ($token !== '' && $user_id > 0) {
$url = add_query_arg([
'token' => $token,
'uid' => $user_id,
], $url);
}
return $url;
}
endif;
if (!function_exists('aksrt_get_verify_email_url')) :
/** 获取邮箱验证页面 URL */
function aksrt_get_verify_email_url(int $user_id = 0, array $args = []): string {
$url = aksrt_get_template_url('verify-email');
if ($user_id > 0) {
$args['uid'] = $user_id;
}
if (!empty($args)) {
$url = add_query_arg($args, $url);
}
return $url;
}
endif;
require_once __DIR__ . '/class-aksrt-user-notifications.php';
// ============================================================
// 六、作者链接改写
// ============================================================
add_filter('author_link', function ($link, $author_id) {
$user = get_userdata($author_id);
$slug = $user ? $user->user_nicename : 'user-' . $author_id;
return home_url('/user/' . trailingslashit($slug));
}, 10, 2);
add_action('template_redirect', function () {
if (!is_author()) return;
$author_id = get_queried_object_id();
if ($author_id) {
$user = get_userdata($author_id);
$slug = $user ? $user->user_nicename : 'user-' . $author_id;
wp_safe_redirect(home_url('/user/' . trailingslashit($slug)), 301);
exit;
}
});
// ============================================================
// AJAX: 保存用户个人资料(显示名称、简介、联系方式)
// ============================================================
add_action('wp_ajax_aksrt_save_profile', function () {
if (!isset($_POST['_profile_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_profile_nonce'])), 'aksrt_profile_save')) {
wp_send_json_error(['message' => __('安全验证失败,请刷新页面重试', 'aksrt')]);
}
$user_id = isset($_POST['user_id']) ? (int) $_POST['user_id'] : 0;
$result = function_exists('aksrt_save_user_center_profile')
? aksrt_save_user_center_profile($user_id, $_POST)
: new WP_Error('aksrt_profile_unavailable', __('保存失败', 'aksrt'));
if (is_wp_error($result)) {
wp_send_json_error(['message' => $result->get_error_message()]);
}
wp_send_json_success(['message' => __('保存成功!', 'aksrt')]);
});
// ============================================================
// AJAX: 发送邮箱验证码(修改密码/更改邮箱通用)
// ============================================================
if (!function_exists('aksrt_get_email_code_key')) :
function aksrt_get_email_code_key(int $user_id, string $scene): string {
return 'aksrt_email_code_' . $scene . '_' . $user_id;
}
endif;
if (!function_exists('aksrt_get_email_code_rate_key')) :
function aksrt_get_email_code_rate_key(int $user_id, string $scene, string $target = ''): string {
$suffix = $target !== '' ? '_' . md5(strtolower($target)) : '';
return 'aksrt_email_code_rate_' . $scene . '_' . $user_id . $suffix;
}
endif;
if (!function_exists('aksrt_store_email_code')) :
function aksrt_store_email_code(int $user_id, string $scene, string $code, string $target = ''): void {
set_transient(
aksrt_get_email_code_key($user_id, $scene),
[
'hash' => wp_hash_password($code),
'target' => $target,
'issued_at' => time(),
],
5 * MINUTE_IN_SECONDS
);
}
endif;
if (!function_exists('aksrt_consume_email_code')) :
function aksrt_consume_email_code(int $user_id, string $scene, string $code, string $target = ''): bool {
$key = aksrt_get_email_code_key($user_id, $scene);
$stored = get_transient($key);
if (!is_array($stored) || empty($stored['hash']) || !wp_check_password($code, $stored['hash'])) {
return false;
}
if ($target !== '' && strtolower((string) ($stored['target'] ?? '')) !== strtolower($target)) {
return false;
}
delete_transient($key);
return true;
}
endif;
add_action('wp_ajax_aksrt_send_email_code', function () {
if ( ! isset( $_POST['_settings_nonce'] ) || ! wp_verify_nonce( $_POST['_settings_nonce'], 'aksrt_account_settings' ) ) {
wp_send_json_error( array( 'message' => __( '安全验证失败,请刷新页面重试', 'aksrt' ) ) );
}
if ( ! is_user_logged_in() ) {
wp_send_json_error( array( 'message' => __( '请先登录', 'aksrt' ) ) );
}
$scene = isset( $_POST['scene'] ) ? sanitize_key( (string) $_POST['scene'] ) : '';
if ( ! in_array( $scene, array( 'change_password', 'change_email' ), true ) ) {
wp_send_json_error( array( 'message' => __( '无效的验证场景', 'aksrt' ) ) );
}
if ( aksrt_is_captcha_enabled_for( $scene ) ) {
$captcha_result = aksrt_verify_captcha( $_POST );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
}
$current_user = wp_get_current_user();
$target_email = '';
$success_message = '';
if ( $scene === 'change_password' ) {
$target_email = $current_user->user_email;
if ( empty( $target_email ) || ! is_email( $target_email ) ) {
wp_send_json_error( array( 'message' => __( '当前账号未绑定有效邮箱', 'aksrt' ) ) );
}
$success_message = __( '验证码已发送到您的注册邮箱', 'aksrt' );
} else {
$new_email = isset( $_POST['new_email'] ) ? trim( wp_unslash( $_POST['new_email'] ) ) : '';
$current_password = isset( $_POST['current_password'] ) ? trim( wp_unslash( $_POST['current_password'] ) ) : '';
if ( empty( $current_password ) || ! wp_check_password( $current_password, $current_user->user_pass, $current_user->ID ) ) {
wp_send_json_error( array( 'message' => __( '当前密码不正确', 'aksrt' ) ) );
}
if ( empty( $new_email ) || ! is_email( $new_email ) ) {
wp_send_json_error( array( 'message' => __( '请输入有效的新邮箱地址', 'aksrt' ) ) );
}
if ( strtolower( $new_email ) === strtolower( $current_user->user_email ) ) {
wp_send_json_error( array( 'message' => __( '新邮箱与当前邮箱相同', 'aksrt' ) ) );
}
if ( email_exists( $new_email ) ) {
wp_send_json_error( array( 'message' => __( '该邮箱已被其他账号使用', 'aksrt' ) ) );
}
$target_email = $new_email;
$success_message = __( '验证码已发送到新邮箱,请完成验证后再提交', 'aksrt' );
}
// 频率限制:60秒内不能重复发送
$rate_key = aksrt_get_email_code_rate_key( $current_user->ID, $scene, $target_email );
if ( get_transient( $rate_key ) ) {
wp_send_json_error( array( 'message' => __( '操作过于频繁,请稍后再试', 'aksrt' ) ) );
}
set_transient( $rate_key, true, 60 );
// 生成验证码并同步发送,只有真正发信成功后才写入验证码缓存。
$code = str_pad( (string) random_int( 0, 999999 ), 6, '0', STR_PAD_LEFT );
$site_name = get_bloginfo( 'name' );
$default_subject = '[' . $site_name . '] ' . __( '邮箱验证码', 'aksrt' );
$default_content = sprintf(
__( "您好 %s,\n\n您正在进行敏感操作验证,验证码为:\n\n【%s】\n\n验证码有效期为 5 分钟。如非本人操作,请忽略此邮件。", 'aksrt' ),
$current_user->display_name,
$code
);
$sent = class_exists( 'AKSRT_Settings' )
? AKSRT_Settings::send_event_email(
'change_code', $target_email, $default_subject, $default_content,
[ '{username}' => $current_user->display_name, '{site_name}' => $site_name, '{code}' => $code ]
)
: wp_mail( $target_email, $default_subject, $default_content, [ 'Content-Type: text/plain; charset=UTF-8' ] );
if ( ! $sent ) {
delete_transient( $rate_key );
error_log( '[AKSRT] 验证码邮件发送失败 user_id=' . $current_user->ID . ' email=' . $target_email . ' scene=' . $scene );
wp_send_json_error( array( 'message' => __( '验证码邮件发送失败,请稍后重试', 'aksrt' ) ) );
}
aksrt_store_email_code( $current_user->ID, $scene, $code, $target_email );
wp_send_json_success( array( 'message' => $success_message ) );
});
// ============================================================
// AJAX: 修改密码
// ============================================================
add_action('wp_ajax_aksrt_change_password', function () {
if ( ! isset( $_POST['_settings_nonce'] ) || ! wp_verify_nonce( $_POST['_settings_nonce'], 'aksrt_account_settings' ) ) {
wp_send_json_error( array( 'message' => __( '安全验证失败,请刷新页面重试', 'aksrt' ) ) );
}
$user_id = get_current_user_id();
if ( ! $user_id ) {
wp_send_json_error( array( 'message' => __( '请先登录', 'aksrt' ) ) );
}
$old_password = isset( $_POST['old_password'] ) ? trim( wp_unslash( $_POST['old_password'] ) ) : '';
$new_password = isset( $_POST['new_password'] ) ? trim( wp_unslash( $_POST['new_password'] ) ) : '';
$new_password_confirm = isset( $_POST['new_password_confirm'] ) ? trim( wp_unslash( $_POST['new_password_confirm'] ) ) : '';
$email_code = isset( $_POST['email_code'] ) ? sanitize_text_field( $_POST['email_code'] ) : '';
// 验证原密码
$user = get_userdata( $user_id );
if ( ! $user || ! wp_check_password( $old_password, $user->data->user_pass, $user_id ) ) {
wp_send_json_error( array( 'message' => __( '原密码不正确', 'aksrt' ) ) );
}
// 验证新密码强度
if ( mb_strlen( $new_password ) < 6 ) {
wp_send_json_error( array( 'message' => __( '新密码长度至少6位', 'aksrt' ) ) );
}
if ( mb_strlen( $new_password ) > 128 ) {
wp_send_json_error( array( 'message' => __( '新密码过长', 'aksrt' ) ) );
}
if ( $new_password !== $new_password_confirm ) {
wp_send_json_error( array( 'message' => __( '两次输入的新密码不一致', 'aksrt' ) ) );
}
// 人机验证
if ( aksrt_is_captcha_enabled_for( 'change_password' ) ) {
$captcha_result = aksrt_verify_captcha( $_POST );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
}
// 验证邮箱验证码
if ( ! aksrt_consume_email_code( $user_id, 'change_password', $email_code, $user->user_email ) ) {
wp_send_json_error( array( 'message' => __( '邮箱验证码错误或已过期', 'aksrt' ) ) );
}
// 设置新密码
wp_set_password( $new_password, $user_id );
if ( class_exists( 'WP_Session_Tokens' ) ) {
WP_Session_Tokens::get_instance( $user_id )->destroy_all();
}
// 清除登录态
wp_clear_auth_cookie();
wp_send_json_success( array(
'message' => __( '密码修改成功,即将跳转到登录页…', 'aksrt' ),
'redirect_to' => aksrt_get_login_url(),
));
});
// ============================================================
// AJAX: 更改邮箱
// ============================================================
add_action('wp_ajax_aksrt_change_email', function () {
if ( ! isset( $_POST['_settings_nonce'] ) || ! wp_verify_nonce( $_POST['_settings_nonce'], 'aksrt_account_settings' ) ) {
wp_send_json_error( array( 'message' => __( '安全验证失败,请刷新页面重试', 'aksrt' ) ) );
}
$user_id = get_current_user_id();
if ( ! $user_id ) {
wp_send_json_error( array( 'message' => __( '请先登录', 'aksrt' ) ) );
}
$current_password = isset( $_POST['current_password'] ) ? trim( wp_unslash( $_POST['current_password'] ) ) : '';
$new_email = isset( $_POST['new_email'] ) ? trim( wp_unslash( $_POST['new_email'] ) ) : '';
$email_code = isset( $_POST['email_code'] ) ? sanitize_text_field( $_POST['email_code'] ) : '';
$current_user = wp_get_current_user();
if ( empty( $current_password ) || ! wp_check_password( $current_password, $current_user->user_pass, $current_user->ID ) ) {
wp_send_json_error( array( 'message' => __( '当前密码不正确', 'aksrt' ) ) );
}
// 验证新邮箱格式
if ( empty( $new_email ) || ! is_email( $new_email ) ) {
wp_send_json_error( array( 'message' => __( '请输入有效的邮箱地址', 'aksrt' ) ) );
}
if ( strtolower( $new_email ) === strtolower( $current_user->user_email ) ) {
wp_send_json_error( array( 'message' => __( '新邮箱与当前邮箱相同', 'aksrt' ) ) );
}
// 检查邮箱是否已被使用
if ( email_exists( $new_email ) ) {
wp_send_json_error( array( 'message' => __( '该邮箱已被其他账号使用', 'aksrt' ) ) );
}
// 人机验证
if ( aksrt_is_captcha_enabled_for( 'change_email' ) ) {
$captcha_result = aksrt_verify_captcha( $_POST );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
}
// 验证邮箱验证码
if ( ! aksrt_consume_email_code( $user_id, 'change_email', $email_code, $new_email ) ) {
wp_send_json_error( array( 'message' => __( '邮箱验证码错误或已过期', 'aksrt' ) ) );
}
// 更新邮箱
$old_email = $current_user->user_email;
$update_result = wp_update_user( [
'ID' => $user_id,
'user_email' => $new_email,
] );
if ( is_wp_error( $update_result ) ) {
wp_send_json_error( array( 'message' => $update_result->get_error_message() ?: __( '邮箱更新失败,请稍后重试', 'aksrt' ) ) );
}
if ( is_email( $old_email ) ) {
$site_name = get_bloginfo( 'name' );
$default_subject = '[' . $site_name . '] ' . __( '账号邮箱已变更', 'aksrt' );
$default_content = sprintf(
__( "您好 %s,\n\n您的账号邮箱已成功修改为:%s\n\n如果这不是您的操作,请立即联系站点管理员。", 'aksrt' ),
$current_user->display_name,
$new_email
);
if ( class_exists( 'AKSRT_Settings' ) ) {
AKSRT_Settings::send_event_email(
'change_notice', $old_email, $default_subject, $default_content,
[ '{username}' => $current_user->display_name, '{site_name}' => $site_name, '{new_email}' => $new_email ]
);
} else {
wp_mail( $old_email, $default_subject, $default_content, [ 'Content-Type: text/plain; charset=UTF-8' ] );
}
}
// ====== 邮箱变更后重置验证状态(阶段 5)======
// 1. 写入 14 天回滚窗口 meta(管理员可在此期内还原旧邮箱)
// 2. 把 aksrt_email_verified 重置为 0
// 3. 若功能启用 → 立即给新邮箱发送验证邮件
update_user_meta( $user_id, 'aksrt_email_previous', $old_email );
update_user_meta( $user_id, 'aksrt_email_changed_at', time() );
$verify_url = '';
$verify_message = '';
if ( class_exists( 'AKSRT_Settings' ) && class_exists( 'AKSRT_Auth_Service' )
&& AKSRT_Settings::registration_requires_email_verification() ) {
update_user_meta( $user_id, 'aksrt_email_verified', 0 );
// 用户对自身的 edit_user 权限映射到 'read',因此 admin_send_verification 会通过权限校验。
$verify_result = AKSRT_Auth_Service::admin_send_verification( $user_id, $user_id );
if ( ! empty( $verify_result['success'] ) ) {
$verify_url = function_exists('aksrt_get_verify_email_url') ? aksrt_get_verify_email_url( $user_id ) : '';
$verify_message = __( '请前往新邮箱完成验证,否则下次登录将被拦截。', 'aksrt' );
}
}
if ( class_exists( 'WP_Session_Tokens' ) ) {
WP_Session_Tokens::get_instance( $user_id )->destroy_all();
}
// 清除登录态
wp_clear_auth_cookie();
wp_send_json_success( array(
'message' => $verify_message !== ''
? __( '邮箱修改成功,请前往新邮箱完成验证后重新登录…', 'aksrt' )
: __( '邮箱修改成功,即将跳转到登录页…', 'aksrt' ),
'redirect_to' => $verify_url !== '' ? $verify_url : aksrt_get_login_url(),
'verify_url' => $verify_url,
));
});