用户中心减层:收敛本人入口与 panel 契约

This commit is contained in:
2026-07-10 20:43:49 +08:00
parent 31c95c3bc6
commit dec33ea15a
33 changed files with 1585 additions and 1488 deletions
+50 -5
View File
@@ -401,7 +401,8 @@
padding-bottom: 12px;
border-bottom: 2px solid var(--color-line-soft, rgba(0,0,0,0.06));
}
.dashboard--public .dashboard-tabs button {
.dashboard--public .dashboard-tabs button,
.dashboard--public .dashboard-tabs a {
padding: 8px 16px;
border-radius: 8px;
font-size: 14px;
@@ -413,16 +414,19 @@
transition: all 0.2s ease;
position: relative;
}
.dashboard--public .dashboard-tabs button:hover {
.dashboard--public .dashboard-tabs button:hover,
.dashboard--public .dashboard-tabs a:hover {
color: var(--color-ink);
background: var(--surface-2);
}
.dashboard--public .dashboard-tabs button.is-active {
.dashboard--public .dashboard-tabs button.is-active,
.dashboard--public .dashboard-tabs a.is-active {
color: var(--color-primary);
background: var(--color-primary-soft);
font-weight: 600;
}
.dashboard--public .dashboard-tabs button.is-active::after {
.dashboard--public .dashboard-tabs button.is-active::after,
.dashboard--public .dashboard-tabs a.is-active::after {
content: '';
position: absolute;
bottom: -14px;
@@ -433,7 +437,8 @@
background: var(--color-primary);
border-radius: 1px;
}
.dashboard--public .dashboard-tabs button.is-active .dashboard-tabs__count {
.dashboard--public .dashboard-tabs button.is-active .dashboard-tabs__count,
.dashboard--public .dashboard-tabs a.is-active .dashboard-tabs__count {
color: var(--color-primary);
background: var(--color-primary-soft);
}
@@ -565,3 +570,43 @@
color: #fff;
}
/* 用户中心复用全站 content-layout,避免嵌套网格与双卡片负边距抵消。 */
.aksrt-profile__layout {
margin-top: 20px;
}
.aksrt-profile__aside.page-owned {
position: static;
}
@media (min-width: 992px) {
.aksrt-profile__aside.page-owned {
position: sticky;
}
}
.aksrt-user-center__notice {
margin-bottom: 16px;
padding: 12px 14px;
border-radius: 8px;
color: #166534;
background: rgba(34, 197, 94, 0.12);
}
.aksrt-user-center__notice--error {
color: var(--color-error, #b91c1c);
background: rgba(239, 68, 68, 0.12);
}
.aksrt-profile .uc-mobile-tabs a {
display: inline-flex;
align-items: center;
padding: 8px 14px;
border-radius: 8px;
color: var(--color-ink-3);
text-decoration: none;
}
.aksrt-profile .uc-mobile-tabs a.is-active {
color: var(--color-primary);
background: var(--color-primary-soft);
}
.aksrt-profile .dashboard-panel > .widget-card:first-child,
.aksrt-profile .profile-form > .widget-card:first-child {
margin-top: 0;
}
-10
View File
@@ -17,16 +17,6 @@
padding: 0 0 40px;
}
/* ==================== 两栏布局 — 对齐 zibll: 311px 侧栏 ==================== */
.uc-layout {
display: grid;
grid-template-columns: 311px 1fr;
gap: 20px;
align-items: start;
width: 100%;
}
.uc-header__actions {
position: relative;
}
-4
View File
@@ -82,10 +82,6 @@
/* ==================== 响应式 ==================== */
@media (max-width: 767px) {
.uc-layout {
grid-template-columns: 1fr;
}
.uc-sidebar {
position: fixed;
top: 0;
+14 -6
View File
@@ -18,6 +18,8 @@
var settingsNonce = cfg.settingsNonce || '';
var userId = cfg.userId || 0;
var i18n = cfg.i18n || {};
var tabAliases = cfg.tabAliases || {};
var tabUrls = cfg.tabUrls || {};
/* ==================== Dashboard 导航 ==================== */
function initDashboardNavigation() {
@@ -54,6 +56,10 @@
function switchTo(name) {
if (!panels[name]) name = defaultTab;
if (panels[name].panel.dataset.panelLoaded === 'false') {
window.location.assign(tabUrls[name] || panels[name].panel.dataset.tabUrl || ('?tab=' + encodeURIComponent(name)));
return;
}
Object.keys(panels).forEach(function(key) {
var active = key === name;
@@ -72,10 +78,8 @@
function tabFromUrl() {
var m = window.location.search.match(/[?&]tab=([^&]+)/);
var name = m ? decodeURIComponent(m[1]) : defaultTab;
if (name === 'signin') return 'credits';
if (name === 'invite') return 'settings';
return name;
var name = m ? decodeURIComponent(m[1]) : (cfg.activeTab || defaultTab);
return tabAliases[name] || name;
}
function updateUrl(name) {
@@ -90,16 +94,20 @@
}
tabs.forEach(function(tab) {
tab.addEventListener('click', function() {
tab.addEventListener('click', function(e) {
var name = this.getAttribute('data-tab');
if (!panels[name] || panels[name].panel.dataset.panelLoaded === 'false') return;
e.preventDefault();
switchTo(name);
updateUrl(name);
});
});
sidebarBtns.forEach(function(btn) {
btn.addEventListener('click', function() {
btn.addEventListener('click', function(e) {
var name = this.getAttribute('data-tab');
if (!panels[name] || panels[name].panel.dataset.panelLoaded === 'false') return;
e.preventDefault();
switchTo(name);
updateUrl(name);
});
+7 -1
View File
@@ -71,7 +71,13 @@ require_once AKSRT_DIR . '/templates/parts/user-shared-helpers.php';
<?php if (have_posts()) : ?>
<div class="aksrt-author-page__posts">
<?php while (have_posts()) : the_post(); ?>
<?php get_template_part('templates/parts/content', 'profile-list-item'); ?>
<?php get_template_part('templates/parts/content', 'profile-list-item', [
'action' => [
'type' => 'view',
'url' => get_permalink(),
'label' => __('阅读', 'aksrt'),
],
]); ?>
<?php endwhile; ?>
</div>
+1
View File
@@ -43,6 +43,7 @@ require_once AKSRT_DIR . '/inc/helpers/menu-decoration.php';
require_once AKSRT_DIR . '/inc/helpers/class-aksrt-nav-walker.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-transactions.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-user-dashboard.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-user-center.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-shortcodes.php';
// ============================================================
+9 -49
View File
@@ -880,60 +880,20 @@ add_action('template_redirect', function () {
// AJAX: 保存用户个人资料(显示名称、简介、联系方式)
// ============================================================
add_action('wp_ajax_aksrt_save_profile', function () {
// 验证 nonce
if ( ! isset( $_POST['_profile_nonce'] ) || ! wp_verify_nonce( $_POST['_profile_nonce'], 'aksrt_profile_save' ) ) {
wp_send_json_error( array( 'message' => __( '安全验证失败,请刷新页面重试', 'aksrt' ) ) );
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;
if ( ! $user_id || $user_id !== get_current_user_id() ) {
wp_send_json_error( array( 'message' => __( '无权操作', 'aksrt' ) ) );
$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()]);
}
$errors = array();
// === 更新昵称(display_name) ===
if ( isset( $_POST['nickname'] ) ) {
$name = sanitize_text_field( $_POST['nickname'] );
if ( mb_strlen( $name ) < 1 || mb_strlen( $name ) > 50 ) {
$errors[] = __( '昵称长度需在 1-50 字之间', 'aksrt' );
} else {
wp_update_user( array( 'ID' => $user_id, 'display_name' => $name ) );
}
}
// === 更新个人简介 ===
if ( isset( $_POST['description'] ) ) {
$desc = sanitize_textarea_field( $_POST['description'] );
if ( mb_strlen( $desc ) > 500 ) {
$errors[] = __( '个人简介不能超过 500 字', 'aksrt' );
} else {
wp_update_user( array( 'ID' => $user_id, 'description' => $desc ) );
}
}
// === 更新联系方式 ===
$contacts = array();
if ( isset( $_POST['contacts'] ) && is_array( $_POST['contacts'] ) ) {
foreach ( $_POST['contacts'] as $c ) {
$url = isset( $c['url'] ) ? esc_url_raw( $c['url'] ) : '';
$icon = isset( $c['icon'] ) ? sanitize_key( $c['icon'] ) : '';
$icon = preg_replace('/^(fab|fas|far)[\s-]*/', '', $icon);
$icon = preg_replace('/^fa[\s-]*/', '', $icon);
$icon = preg_replace('/^[\s-]+/', '', $icon);
$icon = trim( $icon, '- ' );
if ( ! $url || ! $icon ) continue;
$name = aksrt_icon_key_to_name( $icon );
$contacts[] = array( 'name' => $name, 'url' => $url, 'icon' => $icon );
}
}
update_user_meta( $user_id, 'aksrt_user_contacts', $contacts );
if ( ! empty( $errors ) ) {
wp_send_json_error( array( 'message' => implode( '<br>', $errors ) ) );
}
wp_send_json_success( array( 'message' => __( '保存成功!', 'aksrt' ) ) );
wp_send_json_success(['message' => __('保存成功!', 'aksrt')]);
});
// ============================================================
+426
View File
@@ -0,0 +1,426 @@
<?php
/**
* User center helpers.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_normalize_user_contacts')) {
/**
* Sanitize the editable public contact list.
*
* @param mixed $raw_contacts Submitted contact rows.
* @return array<int, array{name:string,url:string,icon:string}>
*/
function aksrt_normalize_user_contacts($raw_contacts): array {
$contacts = [];
if (!is_array($raw_contacts)) {
return $contacts;
}
foreach ($raw_contacts as $contact) {
if (!is_array($contact)) {
continue;
}
$url = isset($contact['url']) ? esc_url_raw(wp_unslash($contact['url'])) : '';
$icon = isset($contact['icon']) ? sanitize_key(wp_unslash($contact['icon'])) : '';
$icon = preg_replace('/^(fab|fas|far)[\s-]*/', '', $icon);
$icon = preg_replace('/^fa[\s-]*/', '', (string) $icon);
$icon = trim(preg_replace('/^[\s-]+/', '', (string) $icon), '- ');
if (!$url || !$icon) {
continue;
}
$contacts[] = [
'name' => aksrt_icon_key_to_name($icon),
'url' => $url,
'icon' => $icon,
];
}
return $contacts;
}
}
if (!function_exists('aksrt_save_user_center_profile')) {
/**
* Save the user-center profile form for both AJAX and no-JS requests.
*
* @param int $user_id User being updated.
* @param array $input Untrusted request data.
* @return true|WP_Error
*/
function aksrt_save_user_center_profile(int $user_id, array $input) {
if (!$user_id || $user_id !== get_current_user_id()) {
return new WP_Error('aksrt_profile_forbidden', __('无权操作', 'aksrt'));
}
$errors = [];
$update = ['ID' => $user_id];
if (isset($input['nickname'])) {
$name = sanitize_text_field(wp_unslash($input['nickname']));
if (mb_strlen($name) < 1 || mb_strlen($name) > 50) {
$errors[] = __('昵称长度需在 1-50 字之间', 'aksrt');
} else {
$update['display_name'] = $name;
}
}
if (isset($input['description'])) {
$description = sanitize_textarea_field(wp_unslash($input['description']));
if (mb_strlen($description) > 500) {
$errors[] = __('个人简介不能超过 500 字', 'aksrt');
} else {
$update['description'] = $description;
}
}
if ($errors) {
return new WP_Error('aksrt_profile_invalid', implode('<br>', $errors));
}
if (count($update) > 1) {
$result = wp_update_user($update);
if (is_wp_error($result)) {
return $result;
}
}
update_user_meta($user_id, 'aksrt_user_contacts', aksrt_normalize_user_contacts($input['contacts'] ?? []));
return true;
}
}
if (!function_exists('aksrt_get_user_center_tab_url')) {
/**
* Build a canonical full-page user-center tab URL.
*/
function aksrt_get_user_center_tab_url(string $tab): string {
$url = remove_query_arg([
'tab', 'posts_page', 'comments_page', 'favorites_page', 'credits_page',
'post_status', 'saved', 'profile_error',
], aksrt_get_user_center_url());
return add_query_arg('tab', sanitize_key($tab), $url);
}
}
if (!function_exists('aksrt_normalize_user_center_tabs')) {
/**
* Normalize the dashboard tab contract.
*/
function aksrt_normalize_user_center_tabs(array $tabs): array {
$normalized = [];
foreach ($tabs as $key => $tab) {
$key = sanitize_key((string) $key);
if (!$key || !is_array($tab)) {
continue;
}
$normalized[$key] = [
'label' => (string) ($tab['label'] ?? $key),
'icon' => sanitize_html_class((string) ($tab['icon'] ?? 'circle')),
'template' => isset($tab['template']) ? (string) $tab['template'] : '',
'context_callback' => $tab['context_callback'] ?? null,
];
}
return $normalized;
}
}
if (!function_exists('aksrt_get_user_center_base_context')) {
/**
* Expose the normalized base context independently from panel assembly.
*/
function aksrt_get_user_center_base_context(WP_User $user): array {
$context = aksrt_get_user_center_render_context($user);
unset($context['panels'], $context['account_modals_html']);
return $context;
}
}
if (!function_exists('aksrt_get_user_center_panel_context')) {
/**
* Return one assembled panel contract.
*/
function aksrt_get_user_center_panel_context(string $tab, array $context): array {
return is_array($context['panels'][$tab] ?? null) ? $context['panels'][$tab] : [];
}
}
if (!function_exists('aksrt_capture_user_center_view')) {
/**
* Render an internal user-center view into an inert HTML payload.
*/
function aksrt_capture_user_center_view(string $view, array $context): string {
$path = AKSRT_DIR . '/inc/helpers/user-center-renderers/profile-' . sanitize_file_name($view) . '.php';
if (!is_file($path)) {
return '';
}
ob_start();
$args = ['context' => $context];
require $path;
return (string) ob_get_clean();
}
}
if (!function_exists('aksrt_capture_user_center_panel')) {
/**
* Capture a built-in or registered user-center panel body.
*/
function aksrt_capture_user_center_panel(string $tab_key, array $tab, array $context): string {
$built_in_tabs = ['profile', 'posts', 'comments', 'favorites', 'credits', 'settings'];
if (in_array($tab_key, $built_in_tabs, true)) {
return aksrt_capture_user_center_view($tab_key, $context);
}
$panel_context = $context;
if (is_callable($tab['context_callback'] ?? null)) {
$custom_context = call_user_func($tab['context_callback'], $context, $tab_key);
if (is_array($custom_context)) {
$panel_context = array_merge($panel_context, $custom_context);
}
}
$template = ltrim((string) ($tab['template'] ?? ''), '/');
if ($template === '') {
return '';
}
$template_path = locate_template([$template . '.php', $template], false, false);
if (!$template_path) {
return '';
}
ob_start();
load_template($template_path, false, ['context' => $panel_context]);
return (string) ob_get_clean();
}
}
if (!function_exists('aksrt_build_user_center_render_context')) {
/**
* Assemble all runtime data before template parts are entered.
*/
function aksrt_build_user_center_render_context(array $context): array {
$active_tab = (string) ($context['active_tab'] ?? 'profile');
$context['header_html'] = aksrt_capture_user_center_view('header', $context);
$context['sidebar_html'] = aksrt_capture_user_center_view('sidebar', $context);
$context['mobile_tabs'] = [];
foreach ((array) ($context['dashboard_tabs'] ?? []) as $tab_key => $tab) {
$context['mobile_tabs'][] = [
'key' => (string) $tab_key,
'label' => (string) ($tab['label'] ?? $tab_key),
'url' => aksrt_get_user_center_tab_url((string) $tab_key),
'is_active' => $active_tab === $tab_key,
];
}
$context['panels'] = [];
foreach ((array) ($context['dashboard_tabs'] ?? []) as $tab_key => $tab) {
$context['panels'][$tab_key] = [
'key' => (string) $tab_key,
'url' => aksrt_get_user_center_tab_url((string) $tab_key),
'is_active' => $active_tab === $tab_key,
'is_loaded' => $active_tab === $tab_key,
'html' => $active_tab === $tab_key
? aksrt_capture_user_center_panel((string) $tab_key, (array) $tab, $context)
: '',
];
}
$context['account_modals_html'] = 'settings' === $active_tab
? aksrt_capture_user_center_view('account-modals', $context)
: '';
return $context;
}
}
if (!function_exists('aksrt_get_user_center_render_context')) {
/**
* Resolve the current user's complete render context outside template files.
*/
function aksrt_get_user_center_render_context(WP_User $current_user): array {
$user_id = (int) $current_user->ID;
$user_center_settings = class_exists('AKSRT_Settings')
? AKSRT_Settings::get_user_center_settings()
: [
'tabs' => ['profile', 'posts', 'comments', 'favorites'],
'avatar_upload' => true,
'cover_upload' => true,
'social_links' => true,
];
$dashboard_tabs = class_exists('AKSRT_User_Center')
? AKSRT_User_Center::get_instance()->get_dashboard_tabs()
: [
'profile' => ['label' => __('个人信息', 'aksrt'), 'icon' => 'user'],
'posts' => ['label' => __('我的文章', 'aksrt'), 'icon' => 'file-alt'],
'comments' => ['label' => __('我的评论', 'aksrt'), 'icon' => 'comment'],
'favorites' => ['label' => __('我的收藏', 'aksrt'), 'icon' => 'star'],
'settings' => ['label' => __('账号设置', 'aksrt'), 'icon' => 'cog'],
];
$dashboard_tabs = aksrt_normalize_user_center_tabs($dashboard_tabs);
$dashboard_tab_keys = array_keys($dashboard_tabs);
$default_dashboard_tab = in_array('profile', $dashboard_tab_keys, true) ? 'profile' : ($dashboard_tab_keys[0] ?? 'profile');
$active_tab = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : $default_dashboard_tab;
if ('signin' === $active_tab) {
$active_tab = 'credits';
} elseif ('invite' === $active_tab) {
$active_tab = 'settings';
}
if (!in_array($active_tab, $dashboard_tab_keys, true)) {
$active_tab = $default_dashboard_tab;
}
$dashboard = function_exists('aksrt_get_user_dashboard_view')
? aksrt_get_user_dashboard_view($user_id, ['viewer_id' => $user_id])
: [];
$target_user = $current_user;
$post_count = (int) ($dashboard['post_count'] ?? count_user_posts($user_id, 'post', true));
$comment_count = (int) ($dashboard['comment_count'] ?? get_comments(['user_id' => $user_id, 'count' => true, 'status' => 'approve']));
$favorite_post_ids = $dashboard['favorite_post_ids'] ?? [];
$favorite_count = (int) ($dashboard['favorite_count'] ?? count($favorite_post_ids));
$user_contacts = $dashboard['user_contacts'] ?? get_user_meta($user_id, 'aksrt_user_contacts', true);
$user_contacts = is_array($user_contacts) ? $user_contacts : [];
$profile_signals = $dashboard['profile_signals'] ?? [];
$user_background_url = (string) get_user_meta($user_id, 'aksrt_background_image', true);
$allow_bg_upload = !empty($user_center_settings['cover_upload']) && class_exists('AKSRT_Settings') && aksrt_get_setting('allow_background_upload', false);
$show_avatar_edit = !empty($user_center_settings['avatar_upload']);
$avatar_settings_url = admin_url('profile.php');
$notification_unread_count = function_exists('aksrt_get_user_unread_notification_count') ? aksrt_get_user_unread_notification_count($user_id) : 0;
$messages_url = function_exists('aksrt_get_messages_url')
? aksrt_get_messages_url()
: aksrt_get_user_center_url('notif');
$per_page = 10;
$posts_page = max(1, isset($_GET['posts_page']) ? (int) $_GET['posts_page'] : 1);
$comments_page = max(1, isset($_GET['comments_page']) ? (int) $_GET['comments_page'] : 1);
$favorites_page = max(1, isset($_GET['favorites_page']) ? (int) $_GET['favorites_page'] : 1);
$post_status_filter = isset($_GET['post_status']) ? sanitize_key(wp_unslash($_GET['post_status'])) : 'publish';
if (!in_array($post_status_filter, ['all', 'publish', 'draft', 'pending', 'private'], true)) {
$post_status_filter = 'publish';
}
$user_status_counts = ['publish' => 0, 'draft' => 0, 'pending' => 0, 'private' => 0];
if ('posts' === $active_tab) {
global $wpdb;
$rows = $wpdb->get_results($wpdb->prepare(
"SELECT post_status, COUNT(*) AS num FROM {$wpdb->posts} WHERE post_author = %d AND post_type = 'post' AND post_status IN ('publish','draft','pending','private') GROUP BY post_status",
$user_id
));
foreach ((array) $rows as $row) {
$user_status_counts[$row->post_status] = (int) $row->num;
}
$post_count = (int) array_sum($user_status_counts);
}
$svg_empty_post = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>';
$svg_empty_comment = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>';
$svg_empty_favorite = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>';
require_once AKSRT_DIR . '/templates/parts/user-shared-helpers.php';
$all_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_all() : [];
$orders_url = function_exists('aksrt_get_orders_page_url') ? aksrt_get_orders_page_url() : '';
$show_vip = !empty($all_settings['payment_vip_enabled']);
$show_credit_tabs = class_exists('AKSRT_Settings') && AKSRT_Settings::credits_enabled();
$affiliate_enabled = $show_credit_tabs && !empty($all_settings['payment_affiliate_enabled']);
$currency_name = $show_credit_tabs ? AKSRT_Settings::get_currency_name() : __('积分', 'aksrt');
$tab_urls = [];
foreach ($dashboard_tab_keys as $tab_key) {
$tab_urls[$tab_key] = aksrt_get_user_center_tab_url($tab_key);
}
$render_context = [
'user_center_settings' => $user_center_settings,
'dashboard_tabs' => $dashboard_tabs,
'dashboard_tab_keys' => $dashboard_tab_keys,
'default_dashboard_tab' => $default_dashboard_tab,
'active_tab' => $active_tab,
'dashboard' => $dashboard,
'target_user' => $target_user,
'user_id' => $user_id,
'post_count' => $post_count,
'comment_count' => $comment_count,
'favorite_post_ids' => $favorite_post_ids,
'favorite_count' => $favorite_count,
'user_contacts' => $user_contacts,
'profile_signals' => $profile_signals,
'user_background_url' => $user_background_url,
'allow_bg_upload' => $allow_bg_upload,
'show_avatar_edit' => $show_avatar_edit,
'avatar_settings_url' => $avatar_settings_url,
'notification_unread_count' => $notification_unread_count,
'messages_url' => $messages_url,
'per_page' => $per_page,
'posts_page' => $posts_page,
'comments_page' => $comments_page,
'favorites_page' => $favorites_page,
'post_status_filter' => $post_status_filter,
'user_status_counts' => $user_status_counts,
'svg_empty_post' => $svg_empty_post,
'svg_empty_comment' => $svg_empty_comment,
'svg_empty_favorite' => $svg_empty_favorite,
'orders_url' => $orders_url,
'show_vip' => $show_vip,
'show_credit_tabs' => $show_credit_tabs,
'affiliate_enabled' => $affiliate_enabled,
'currency_name' => $currency_name,
'show_invite_section' => $show_credit_tabs
&& class_exists('AKSRT_Settings')
&& AKSRT_Settings::invite_enabled()
&& AKSRT_Settings::invite_registration_enabled(),
'user_email' => (string) $current_user->user_email,
];
$render_context['runtime_config'] = [
'ajaxUrl' => admin_url('admin-ajax.php'),
'publicNonce' => wp_create_nonce('aksrt_public_nonce'),
'settingsNonce' => wp_create_nonce('aksrt_account_settings'),
'userId' => $user_id,
'defaultTab' => $default_dashboard_tab,
'activeTab' => $active_tab,
'tabAliases' => ['signin' => 'credits', 'invite' => 'settings'],
'tabUrls' => $tab_urls,
'i18n' => [
'contactUrlPlaceholder' => __('链接 URL', 'aksrt'),
'deleteLabel' => __('删除', 'aksrt'),
'saving' => __('保存中…', 'aksrt'),
'saveBtn' => __('保存修改', 'aksrt'),
'saved' => __('已保存', 'aksrt'),
'saveFailed' => __('保存失败', 'aksrt'),
'networkError' => __('网络错误,请重试', 'aksrt'),
'confirmUnfavorite' => __('确定取消收藏这篇文章?', 'aksrt'),
'unfavorited' => __('已取消收藏', 'aksrt'),
'operationFailed' => __('操作失败', 'aksrt'),
'confirmUnbind' => __('确定解除绑定?', 'aksrt'),
'unbinding' => __('解除中...', 'aksrt'),
'unbindSuccess' => __('解除成功', 'aksrt'),
'unbindFailed' => __('解除失败', 'aksrt'),
'unbind' => __('解除', 'aksrt'),
],
];
$render_context['cover_upload_config'] = $allow_bg_upload ? [
'enabled' => true,
'restUrl' => rest_url('aksrt/v1/user/background'),
'restNonce' => wp_create_nonce('wp_rest'),
'buttonId' => 'aksrt-upload-bg-btn',
'inputId' => 'aksrt-bg-file-input',
'imageId' => 'aksrt-cover-img',
'fieldName' => 'background',
'maxSize' => 2097152,
'fallbackImage' => AKSRT_URI . '/assets/img/person_background.png',
'idleText' => __('编辑', 'aksrt'),
'loadingText' => __('上传中…', 'aksrt'),
'tooLargeText' => __('图片大小不能超过 2MB', 'aksrt'),
'successText' => __('背景图已更新', 'aksrt'),
'errorText' => __('上传失败', 'aksrt'),
'networkErrorText' => __('网络错误,请重试', 'aksrt'),
] : [];
$render_context = aksrt_build_user_center_render_context($render_context);
return $render_context;
}
}
@@ -0,0 +1,98 @@
<?php
/** Internal account modal renderer. @package AKSRT */
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
?>
<!-- ==================== 修改密码弹窗 ==================== -->
<div id="pwd-modal" class="aksrt-modal" role="dialog" aria-modal="true" aria-label="<?php esc_attr_e( '修改密码', 'aksrt' ); ?>">
<div class="aksrt-modal__overlay" data-close-modal></div>
<div class="aksrt-modal__box">
<div class="aksrt-modal__header">
<div class="aksrt-modal__title-wrap">
<span class="aksrt-modal__icon"><i class="fas fa-lock" aria-hidden="true"></i></span>
<h4><?php esc_html_e( '修改密码', 'aksrt' ); ?></h4>
</div>
<button type="button" class="aksrt-modal__close" data-close-modal title="<?php esc_attr_e( '关闭', 'aksrt' ); ?>"><i class="fas fa-times" aria-hidden="true"></i></button>
</div>
<form id="pwd-form" autocomplete="off" novalidate>
<div class="modal-field">
<label for="pwd-old"><?php esc_html_e( '原密码', 'aksrt' ); ?></label>
<input type="password" id="pwd-old" name="old_password" required minlength="1">
<button type="button" class="toggle-pwd" data-target="pwd-old"></button>
</div>
<div class="modal-field">
<label for="pwd-new"><?php esc_html_e( '新密码', 'aksrt' ); ?></label>
<input type="password" id="pwd-new" name="new_password" required minlength="6" placeholder="<?php esc_attr_e( '至少6位字符', 'aksrt' ); ?>">
<button type="button" class="toggle-pwd" data-target="pwd-new"></button>
<div class="pwd-strength" id="pwd-strength-bar">
<div class="pwd-strength__fill"></div>
</div>
<span class="pwd-strength__text" id="pwd-strength-text"></span>
</div>
<div class="modal-field">
<label for="pwd-new-confirm"><?php esc_html_e( '确认新密码', 'aksrt' ); ?></label>
<input type="password" id="pwd-new-confirm" name="new_password_confirm" required minlength="6" placeholder="<?php esc_attr_e( '再次输入新密码', 'aksrt' ); ?>">
<button type="button" class="toggle-pwd" data-target="pwd-new-confirm"></button>
</div>
<div class="modal-field modal-field--code">
<label for="pwd-code"><?php esc_html_e( '当前邮箱验证码', 'aksrt' ); ?></label>
<div class="code-input-group">
<input type="text" id="pwd-code" name="email_code" maxlength="6" inputmode="numeric" pattern="[0-9]*" placeholder="6位数字验证码" required>
<button type="button" class="btn-ghost btn-sm code-send-btn" id="pwd-send-code"><?php esc_html_e( '发送到当前邮箱', 'aksrt' ); ?></button>
</div>
</div>
<?php aksrt_render_captcha('change_password'); ?>
<div class="aksrt-modal__footer">
<button type="button" class="btn-ghost btn-sm" data-close-modal><?php esc_html_e( '取消', 'aksrt' ); ?></button>
<button type="submit" class="btn-primary btn-sm" id="pwd-submit"><?php esc_html_e( '确认修改', 'aksrt' ); ?></button>
</div>
</form>
</div>
</div>
<!-- ==================== 更改邮箱弹窗 ==================== -->
<div id="email-modal" class="aksrt-modal" role="dialog" aria-modal="true" aria-label="<?php esc_attr_e( '更改邮箱', 'aksrt' ); ?>">
<div class="aksrt-modal__overlay" data-close-modal></div>
<div class="aksrt-modal__box">
<div class="aksrt-modal__header">
<div class="aksrt-modal__title-wrap">
<span class="aksrt-modal__icon"><i class="far fa-envelope" aria-hidden="true"></i></span>
<h4><?php esc_html_e( '更改邮箱', 'aksrt' ); ?></h4>
</div>
<button type="button" class="aksrt-modal__close" data-close-modal title="<?php esc_attr_e( '关闭', 'aksrt' ); ?>"><i class="fas fa-times" aria-hidden="true"></i></button>
</div>
<form id="email-form" autocomplete="off" novalidate>
<div class="modal-field">
<label><?php esc_html_e( '当前邮箱', 'aksrt' ); ?></label>
<div class="readonly-display"><?php echo esc_html( $user_email ); ?></div>
</div>
<div class="modal-field">
<label for="email-current-password"><?php esc_html_e( '当前密码', 'aksrt' ); ?></label>
<input type="password" id="email-current-password" name="current_password" required minlength="1" placeholder="<?php esc_attr_e( '请输入当前登录密码', 'aksrt' ); ?>">
<button type="button" class="toggle-pwd" data-target="email-current-password"></button>
</div>
<div class="modal-field">
<label for="email-new"><?php esc_html_e( '新邮箱', 'aksrt' ); ?></label>
<input type="email" id="email-new" name="new_email" required placeholder="<?php esc_attr_e( '请输入新的邮箱地址', 'aksrt' ); ?>">
</div>
<div class="modal-field modal-field--code">
<label for="email-code"><?php esc_html_e( '新邮箱验证码', 'aksrt' ); ?></label>
<div class="code-input-group">
<input type="text" id="email-code" name="email_code" maxlength="6" inputmode="numeric" pattern="[0-9]*" placeholder="6位数字验证码" required>
<button type="button" class="btn-ghost btn-sm code-send-btn" id="email-send-code"><?php esc_html_e( '发送到新邮箱', 'aksrt' ); ?></button>
</div>
<p class="settings-hint settings-hint--bottom"><?php esc_html_e( '验证码将发送到你填写的新邮箱,验证通过后才会正式切换。', 'aksrt' ); ?></p>
</div>
<?php aksrt_render_captcha('change_email'); ?>
<div class="aksrt-modal__footer">
<button type="button" class="btn-ghost btn-sm" data-close-modal><?php esc_html_e( '取消', 'aksrt' ); ?></button>
<button type="submit" class="btn-primary btn-sm" id="email-submit"><?php esc_html_e( '确认更改', 'aksrt' ); ?></button>
</div>
</form>
</div>
</div>
@@ -0,0 +1,46 @@
<?php
/**
* User center profile comments part.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
?>
<!-- ==================== 我的评论 ==================== -->
<div class="widget-card">
<div class="profile-list-header">
<h3 class="widget-title widget-title--inline">
<?php printf( esc_html__( '我的评论(%d 条)', 'aksrt' ), $comment_count ); ?>
</h3>
</div>
<?php
$comments_total = $comment_count;
$cs = get_comments( [
'user_id' => $user_id,
'number' => $per_page,
'offset' => ( $comments_page - 1 ) * $per_page,
'status' => 'approve',
'update_comment_meta_cache' => false,
] );
if ( $cs ) : ?>
<div class="profile-comment-list">
<?php foreach ( $cs as $c ) :
get_template_part('templates/parts/content', 'profile-comment-item', ['comment' => $c]);
endforeach; ?>
</div>
<?php
$comments_total_pages = (int) ceil( $comments_total / $per_page );
aksrt_render_profile_pagination( $comments_total_pages, $comments_page, 'comments_page' );
?>
<?php else :
aksrt_render_profile_empty(
$svg_empty_comment,
__( '暂无评论', 'aksrt' ),
__( '在你感兴趣的文章下留下你的看法吧。', 'aksrt' )
);
endif; ?>
</div>
@@ -0,0 +1,178 @@
<?php
/**
* User center profile credits part.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
?>
<?php
$balance = function_exists( 'aksrt_user_credits' ) ? aksrt_user_credits( $user_id ) : 0;
$currency_name = class_exists( 'AKSRT_Settings' ) ? AKSRT_Settings::get_currency_name() : __( '积分', 'aksrt' );
$credits_enabled = class_exists( 'AKSRT_Settings' ) && AKSRT_Settings::credits_enabled();
$vip_settings = class_exists( 'AKSRT_Settings' ) ? AKSRT_Settings::get_payment_vip_settings() : [];
$show_vip_card = ! empty( $vip_settings['enabled'] );
$is_vip_user = function_exists( 'aksrt_is_vip' ) && aksrt_is_vip( $user_id );
$vip_badge_text = function_exists( 'aksrt_get_vip_badge_text' ) ? aksrt_get_vip_badge_text() : 'VIP';
?>
<!-- ==================== 账户余额 ==================== -->
<!-- 余额卡片 -->
<div class="uc-balance-card">
<div class="uc-balance-card__bg"></div>
<div class="uc-balance-card__content">
<div class="uc-balance-card__header">
<span class="uc-balance-card__label"><?php esc_html_e( '账户余额', 'aksrt' ); ?></span>
<span class="uc-balance-card__currency"><?php echo esc_html( $currency_name ); ?></span>
</div>
<div class="uc-balance-card__amount">
<span class="uc-balance-card__value"><?php echo esc_html( aksrt_format_credits( $balance ) ); ?></span>
</div>
<?php if ( $credits_enabled ) : ?>
<div class="uc-balance-card__actions" data-aksrt-signin>
<span class="uc-balance-card__sign-status" data-signin-status></span>
<button type="button" class="uc-balance-card__sign-btn" data-signin-btn disabled>
<i class="fas fa-calendar-check" aria-hidden="true"></i>
<span><?php esc_html_e( '签到', 'aksrt' ); ?></span>
</button>
</div>
<p class="uc-balance-card__tip" data-signin-tip></p>
<?php endif; ?>
</div>
</div>
<?php if ( $show_vip_card ) : ?>
<!-- VIP 会员卡片 -->
<div class="uc-vip-card<?php echo $is_vip_user ? ' uc-vip-card--active' : ''; ?>">
<div class="uc-vip-card__bg"></div>
<div class="uc-vip-card__content">
<div class="uc-vip-card__header">
<div class="uc-vip-card__icon">
<i class="fas fa-crown" aria-hidden="true"></i>
</div>
<div class="uc-vip-card__info">
<span class="uc-vip-card__label"><?php echo esc_html( $vip_settings['name'] ?? __( 'VIP会员', 'aksrt' ) ); ?></span>
<?php if ( $is_vip_user ) : ?>
<span class="uc-vip-card__status uc-vip-card__status--active"><?php esc_html_e( '已开通', 'aksrt' ); ?></span>
<?php else : ?>
<span class="uc-vip-card__status"><?php esc_html_e( '未开通', 'aksrt' ); ?></span>
<?php endif; ?>
</div>
</div>
<?php if ( $is_vip_user ) : ?>
<div class="uc-vip-card__benefits">
<span class="uc-vip-card__benefit"><i class="fas fa-download" aria-hidden="true"></i> <?php esc_html_e( '免费下载', 'aksrt' ); ?></span>
<span class="uc-vip-card__benefit"><i class="fas fa-crown" aria-hidden="true"></i> <?php esc_html_e( '专属身份', 'aksrt' ); ?></span>
</div>
<?php else : ?>
<div class="uc-vip-card__benefits">
<span class="uc-vip-card__benefit"><i class="fas fa-download" aria-hidden="true"></i> <?php esc_html_e( '解锁免费下载', 'aksrt' ); ?></span>
<span class="uc-vip-card__benefit"><i class="fas fa-tag" aria-hidden="true"></i> <?php esc_html_e( '享专属折扣', 'aksrt' ); ?></span>
</div>
<?php endif; ?>
<?php if ( ! $is_vip_user ) : ?>
<a href="<?php echo esc_url( aksrt_get_user_center_url( 'credits' ) ); ?>" class="uc-vip-card__btn">
<?php esc_html_e( '立即开通', 'aksrt' ); ?>
</a>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<?php if ( $credits_enabled && function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('signin') ) : ?>
<div class="aksrt-signin-captcha" data-signin-captcha style="margin-bottom:16px;">
<?php aksrt_render_captcha('signin'); ?>
</div>
<?php endif; ?>
<?php
// 签到日历可视化
$signin_dates = [];
if ($credits_enabled && class_exists('AKSRT_Signin')) {
$signin_log = AKSRT_Signin::get_instance()->get_recent_signins($user_id, 30);
$signin_dates = array_map(function ($entry) {
return $entry['signin_date'] ?? '';
}, $signin_log);
$signin_dates = array_filter($signin_dates);
}
?>
<?php if (!empty($signin_dates)) : ?>
<div class="uc-signin-calendar" style="margin-bottom:16px;">
<h4 class="uc-signin-calendar__title">
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
<?php esc_html_e('最近 30 天签到', 'aksrt'); ?>
</h4>
<div data-aksrt-signin-calendar='<?php echo esc_attr(wp_json_encode(array_values($signin_dates))); ?>'></div>
</div>
<?php endif; ?>
<!-- 积分流水 -->
<div class="uc-transactions">
<div class="uc-transactions__header">
<h3 class="uc-transactions__title">
<i class="fas fa-list-ul" aria-hidden="true"></i>
<?php echo esc_html( sprintf( __( '%s明细', 'aksrt' ), $currency_name ) ); ?>
</h3>
</div>
<?php
$credits_page = max(1, isset($_GET['credits_page']) ? (int) $_GET['credits_page'] : 1);
if ( class_exists( 'AKSRT_Credits' ) ) {
$log = AKSRT_Credits::get_log( [ 'user_id' => $user_id, 'page' => $credits_page, 'per_page' => 10 ] );
} else {
$log = [ 'items' => [], 'total' => 0 ];
}
if ( ! empty( $log['items'] ) ) :
?>
<div class="uc-transactions__list">
<?php foreach ( $log['items'] as $row ) :
$delta = (int) $row['delta'];
$delta_class = $delta >= 0 ? 'is-positive' : 'is-negative';
?>
<div class="uc-transactions__item">
<div class="uc-transactions__icon <?php echo esc_attr( $delta_class ); ?>">
<i class="fas fa-<?php echo $delta >= 0 ? 'arrow-up' : 'arrow-down'; ?>" aria-hidden="true"></i>
</div>
<div class="uc-transactions__info">
<div class="uc-transactions__reason">
<?php echo esc_html( aksrt_credits_label( (string) $row['reason'] ) ); ?>
<?php if ( ! empty( $row['remark'] ) ) : ?>
<span class="uc-transactions__remark">(<?php echo esc_html( (string) $row['remark'] ); ?>)</span>
<?php endif; ?>
</div>
<div class="uc-transactions__time">
<?php echo esc_html( get_date_from_gmt( (string) $row['created_at'], 'Y-m-d H:i' ) ); ?>
</div>
</div>
<div class="uc-transactions__amount">
<span class="uc-transactions__delta <?php echo esc_attr( $delta_class ); ?>">
<?php echo esc_html( ( $delta > 0 ? '+' : '' ) . $delta ); ?>
</span>
<span class="uc-transactions__balance">
<?php
/* translators: %s: 余额 */
printf( esc_html__( '余额 %s', 'aksrt' ), esc_html( (string) (int) $row['balance_after'] ) );
?>
</span>
</div>
</div>
<?php endforeach; ?>
</div>
<?php
$total_pages = (int) ceil( (int) ($log['total'] ?? 0) / 10 );
if ( $total_pages > 1 ) {
aksrt_render_profile_pagination( $total_pages, $credits_page, 'credits_page' );
}
?>
<?php else : ?>
<div class="uc-transactions__empty">
<div class="uc-transactions__empty-icon">
<i class="fas fa-receipt" aria-hidden="true"></i>
</div>
<p><?php echo esc_html( sprintf( __( '还没有任何%s流水', 'aksrt' ), $currency_name ) ); ?></p>
</div>
<?php endif; ?>
</div>
@@ -0,0 +1,65 @@
<?php
/**
* User center profile favorites part.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
?>
<!-- ==================== 我的收藏 ==================== -->
<div class="widget-card">
<div class="profile-list-header">
<h3 class="widget-title widget-title--inline">
<?php printf( esc_html__( '我的收藏(%d 篇)', 'aksrt' ), $favorite_count ); ?>
</h3>
</div>
<?php if ( ! empty( $favorite_post_ids ) ) :
$favorites_query = new WP_Query( [
'post_type' => 'post',
'post_status' => 'publish',
'post__in' => $favorite_post_ids,
'orderby' => 'post__in',
'posts_per_page' => $per_page,
'paged' => $favorites_page,
'ignore_sticky_posts' => true,
'no_found_rows' => false,
'update_post_meta_cache' => false,
] );
if ( $favorites_query->have_posts() ) : ?>
<div class="profile-list" data-list="favorites">
<?php while ( $favorites_query->have_posts() ) : $favorites_query->the_post();
get_template_part('templates/parts/content', 'profile-list-item', [
'action' => [
'type' => 'unfavorite',
'url' => '#',
'label' => __('取消收藏', 'aksrt'),
],
]);
endwhile; ?>
</div>
<?php aksrt_render_profile_pagination( (int) $favorites_query->max_num_pages, $favorites_page, 'favorites_page' ); ?>
<?php
wp_reset_postdata();
else :
aksrt_render_profile_empty(
$svg_empty_favorite,
__( '收藏列表为空', 'aksrt' ),
__( '点击文章页面的星标收藏喜欢的内容。', 'aksrt' ),
home_url( '/' ),
__( '去发现好文', 'aksrt' )
);
endif;
else :
aksrt_render_profile_empty(
$svg_empty_favorite,
__( '收藏列表为空', 'aksrt' ),
__( '点击文章页面的星标收藏喜欢的内容。', 'aksrt' ),
home_url( '/' ),
__( '去发现好文', 'aksrt' )
);
endif; ?>
</div>
@@ -0,0 +1,66 @@
<?php
/**
* User center profile form part.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
?>
<!-- ==================== 个人信息(可编辑) ==================== -->
<form class="profile-form" id="profile-form" method="post">
<?php wp_nonce_field( 'aksrt_profile_save', '_profile_nonce' ); ?>
<input type="hidden" name="action" value="aksrt_save_profile">
<input type="hidden" name="user_id" value="<?php echo (int) $user_id; ?>">
<!-- 昵称 -->
<div class="widget-card">
<h3 class="widget-title"><?php esc_html_e( '昵称', 'aksrt' ); ?></h3>
<input type="text" name="nickname" id="profile-nickname"
value="<?php echo esc_attr( $target_user->display_name ); ?>"
class="profile-input" maxlength="50" required>
<p class="profile-hint"><?php esc_html_e( '修改后会同步更新个人主页地址', 'aksrt' ); ?></p>
</div>
<!-- 个人简介 -->
<div class="widget-card">
<h3 class="widget-title"><?php esc_html_e( '个人简介', 'aksrt' ); ?></h3>
<textarea name="description" id="profile-bio" class="profile-input profile-textarea"
rows="4" maxlength="500" placeholder="<?php esc_attr_e( '介绍一下自己…', 'aksrt' ); ?>"><?php echo esc_textarea( $target_user->description ); ?></textarea>
<p class="profile-hint"><?php esc_html_e( '显示在你的公开用户主页上,最多 500 字', 'aksrt' ); ?></p>
</div>
<?php if ( ! empty( $user_center_settings['social_links'] ) ) : ?>
<!-- 联系方式 -->
<div class="widget-card">
<h3 class="widget-title"><?php esc_html_e( '联系方式', 'aksrt' ); ?></h3>
<p class="profile-hint profile-hint--top"><?php esc_html_e( '添加你的社交链接,显示在公开用户主页', 'aksrt' ); ?></p>
<div id="contacts-list">
<?php foreach ( $user_contacts as $i => $contact ) : ?>
<div class="contact-row" data-index="<?php echo (int) $i; ?>">
<select name="contacts[<?php echo (int) $i; ?>][icon]" class="profile-input contact-icon">
<?php echo aksrt_contact_icon_options( $contact['icon'] ?? '' ); ?>
</select>
<input type="hidden" name="contacts[<?php echo (int) $i; ?>][name]" value="">
<input type="url" name="contacts[<?php echo (int) $i; ?>][url]"
value="<?php echo esc_url( $contact['url'] ?? '' ); ?>"
class="profile-input contact-url" placeholder="<?php esc_attr_e( '链接 URL', 'aksrt' ); ?>">
<button type="button" class="contact-remove" onclick="this.parentElement.remove()" title="<?php esc_attr_e( '删除', 'aksrt' ); ?>">&times;</button>
</div>
<?php endforeach; ?>
</div>
<button type="button" class="btn-ghost btn-sm profile-add-contact" id="add-contact-btn">+ <?php esc_html_e( '添加联系方式', 'aksrt' ); ?></button>
</div>
<?php endif; ?>
<!-- 保存按钮 -->
<div class="profile-form__submit">
<button type="submit" class="btn-primary btn-lg" id="profile-save-btn">
<?php esc_html_e( '保存修改', 'aksrt' ); ?>
</button>
<span id="profile-save-msg" class="profile-form__submit-msg"></span>
</div>
</form>
@@ -0,0 +1,79 @@
<?php
/**
* User center profile header part.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
?>
<!-- 头图 + 用户卡 -->
<div class="uc-header">
<div class="uc-header__cover">
<img id="aksrt-cover-img" class="uc-header__cover-img" src="<?php echo esc_url( $user_background_url ? $user_background_url : get_theme_file_uri( 'assets/img/person_background.png' ) ); ?>" alt="">
<div class="uc-header__cover-mask"></div>
<?php if ( $allow_bg_upload ) : ?>
<button type="button" class="uc-header__cover-upload" id="aksrt-upload-bg-btn" title="<?php esc_attr_e( '编辑背景图', 'aksrt' ); ?>">
<i class="fas fa-pen" aria-hidden="true"></i>
<span><?php esc_html_e( '编辑', 'aksrt' ); ?></span>
</button>
<input type="file" id="aksrt-bg-file-input" accept="image/jpeg,image/png,image/webp,image/gif" style="display:none">
<?php endif; ?>
</div>
<div class="uc-header__content">
<div class="uc-header__avatar">
<?php echo get_avatar( $user_id, 120, '', $target_user->display_name ); ?>
</div>
<div class="uc-header__info">
<h1 class="uc-header__name">
<?php echo esc_html( $target_user->display_name ); ?>
<?php
$vip_badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($user_id) : '';
if ($vip_badge) echo $vip_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
?>
</h1>
<?php if ( $target_user->description ) : ?>
<p class="uc-header__desc"><?php echo esc_html( $target_user->description ); ?></p>
<?php endif; ?>
<?php if ( ! empty( $profile_signals ) ) : ?>
<div class="aksrt-identity-chips">
<?php foreach ( $profile_signals as $signal ) : ?>
<span class="aksrt-identity-chip aksrt-identity-chip--<?php echo esc_attr( $signal['tone'] ); ?>">
<i class="fas fa-<?php echo esc_attr( $signal['icon'] ); ?>" aria-hidden="true"></i>
<span><?php echo esc_html( $signal['label'] ); ?></span>
</span>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="uc-header__meta">
<span class="uc-header__meta-item" title="<?php esc_attr_e( '用户名', 'aksrt' ); ?>">
<i class="fas fa-user" aria-hidden="true"></i> <?php echo esc_html( $target_user->user_login ); ?>
</span>
<?php if ( $target_user->user_email ) : ?>
<span class="uc-header__meta-item" title="<?php esc_attr_e( '邮箱', 'aksrt' ); ?>">
<i class="fas fa-envelope" aria-hidden="true"></i> <?php echo esc_html( $target_user->user_email ); ?>
</span>
<?php endif; ?>
</div>
</div>
<div class="uc-header__actions">
<a href="<?php echo esc_url( $messages_url ); ?>" class="btn-ghost btn-sm">
<i class="fas fa-bell" aria-hidden="true"></i> <?php esc_html_e( '消息通知', 'aksrt' ); ?>
<?php if ( $notification_unread_count > 0 ) : ?>
<span class="uc-header__action-badge"><?php echo esc_html( (string) $notification_unread_count ); ?></span>
<?php endif; ?>
</a>
<?php if ( $show_avatar_edit ) : ?>
<a href="<?php echo esc_url( $avatar_settings_url ); ?>" class="btn-ghost btn-sm">
<i class="fas fa-camera" aria-hidden="true"></i> <?php esc_html_e( '更换头像', 'aksrt' ); ?>
</a>
<?php endif; ?>
<a href="<?php echo esc_url( aksrt_get_user_home_url() ); ?>" class="btn-ghost btn-sm">
<i class="fas fa-map-marker-alt" aria-hidden="true"></i> <?php esc_html_e( '我的主页', 'aksrt' ); ?>
</a>
</div>
</div>
</div>
@@ -0,0 +1,101 @@
<?php
/**
* User center profile posts part.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
?>
<!-- ==================== 我的文章 ==================== -->
<div class="widget-card">
<div class="profile-list-header">
<h3 class="widget-title widget-title--inline">
<?php
/* translators: %d: post count */
printf( esc_html__( '我的文章(%d 篇)', 'aksrt' ), $post_count );
?>
</h3>
<?php
$status_chip_defs = [
'publish' => __( '已发布', 'aksrt' ),
'draft' => __( '草稿', 'aksrt' ),
'pending' => __( '待审', 'aksrt' ),
'private' => __( '私密', 'aksrt' ),
'all' => __( '全部', 'aksrt' ),
];
?>
<div class="profile-status-filter" role="tablist" aria-label="<?php esc_attr_e( '文章状态筛选', 'aksrt' ); ?>">
<?php foreach ( $status_chip_defs as $status_key => $status_text ) :
$count_for_chip = ( 'all' === $status_key )
? $post_count
: ( $user_status_counts[ $status_key ] ?? 0 );
if ( 'publish' !== $status_key && 'all' !== $status_key && $count_for_chip < 1 ) {
continue;
}
$chip_url = add_query_arg( [
'tab' => 'posts',
'post_status' => $status_key,
'posts_page' => 1,
], remove_query_arg( [ 'posts_page' ] ) );
$chip_active = ( $post_status_filter === $status_key );
?>
<a href="<?php echo esc_url( $chip_url ); ?>" class="profile-status-chip<?php echo $chip_active ? ' is-active' : ''; ?>" role="tab" aria-selected="<?php echo $chip_active ? 'true' : 'false'; ?>">
<?php echo esc_html( $status_text ); ?>
<span class="profile-status-chip__count"><?php echo esc_html( (string) $count_for_chip ); ?></span>
</a>
<?php endforeach; ?>
</div>
</div>
<?php
$ps_args = [
'author' => $user_id,
'post_status' => ( 'all' === $post_status_filter )
? [ 'publish', 'draft', 'pending', 'private' ]
: $post_status_filter,
'posts_per_page' => $per_page,
'paged' => $posts_page,
'no_found_rows' => false,
'update_post_meta_cache' => false,
'update_post_term_cache' => true,
];
$q = new WP_Query( $ps_args );
if ( $q->have_posts() ) : ?>
<div class="profile-list">
<?php while ( $q->have_posts() ) : $q->the_post();
$st = get_post_status();
$status_label = '';
if ( 'publish' !== $st ) {
$st_map = [
'draft' => __( '草稿', 'aksrt' ),
'pending' => __( '待审', 'aksrt' ),
'private' => __( '私密', 'aksrt' ),
'future' => __( '定时', 'aksrt' ),
];
$status_label = $st_map[ $st ] ?? $st;
}
$edit_link = get_edit_post_link();
get_template_part('templates/parts/content', 'profile-list-item', [
'action' => $edit_link ? ['type' => 'edit', 'url' => $edit_link, 'label' => __('编辑', 'aksrt')] : [],
'status' => $status_label,
]);
endwhile; ?>
</div>
<?php aksrt_render_profile_pagination( (int) $q->max_num_pages, $posts_page, 'posts_page' ); ?>
<?php
wp_reset_postdata();
else :
aksrt_render_profile_empty(
$svg_empty_post,
__( '还没有文章', 'aksrt' ),
__( '试试发布你的第一篇文章吧。', 'aksrt' ),
admin_url( 'post-new.php' ),
__( '写文章', 'aksrt' )
);
endif; ?>
</div>
@@ -0,0 +1,191 @@
<?php
/**
* User center profile settings part.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
?>
<!-- ==================== 账号设置 ==================== -->
<?php
$oauth = AKSRT_OAuth::get_instance();
$oauth_data = $oauth->get_frontend_bindings_data($user_id);
$user_email = $target_user->user_email ?: '';
$binding_name_map = [
'github' => 'GitHub',
'gitee' => 'Gitee',
'qq' => 'QQ',
'google' => 'Google',
];
$binding_rows = [];
foreach ( (array) ( $oauth_data['bindings'] ?? [] ) as $binding ) {
$key = $binding['key'] ?? '';
if ( ! $key ) continue;
$binding_rows[ $key ] = [
'key' => $key,
'name' => $binding_name_map[ $key ] ?? ( $binding['name'] ?? ucfirst( $key ) ),
'icon' => $binding['icon'] ?? '',
'is_bound' => true,
'url' => '',
];
}
foreach ( (array) ( $oauth_data['available'] ?? [] ) as $binding ) {
$key = $binding['key'] ?? '';
if ( ! $key ) continue;
$binding_rows[ $key ] = [
'key' => $key,
'name' => $binding_name_map[ $key ] ?? ( $binding['name'] ?? ucfirst( $key ) ),
'icon' => $binding['icon'] ?? '',
'is_bound' => false,
'url' => $binding['url'] ?? '',
];
}
?>
<!-- 第三方绑定区域 -->
<div class="settings-section">
<div class="settings-section__header">
<span class="settings-icon"><i class="fas fa-link" aria-hidden="true"></i></span>
<h3 class="widget-title settings-section__title"><?php esc_html_e( '社交账号绑定', 'aksrt' ); ?></h3>
</div>
<?php if ( ! empty( $binding_rows ) ) : ?>
<p class="settings-hint settings-hint--top"><?php esc_html_e( '点击下方按钮管理第三方账号绑定状态:', 'aksrt' ); ?></p>
<div class="binding-list">
<?php foreach ( $binding_rows as $binding ) : ?>
<?php if ( ! empty( $binding['is_bound'] ) ) : ?>
<div class="binding-item">
<span class="binding-item__icon"><?php echo $binding['icon']; ?></span>
<span class="binding-item__meta">
<span class="binding-status">
<i class="fas fa-check-circle badge__icon" aria-hidden="true"></i>
<?php esc_html_e( '已绑定', 'aksrt' ); ?>
</span>
<span class="binding-item__name"><?php echo esc_html( $binding['name'] ); ?></span>
</span>
<button type="button" class="binding-unbind-btn" data-provider="<?php echo esc_attr( $binding['key'] ); ?>">
<?php esc_html_e( '解绑', 'aksrt' ); ?>
</button>
</div>
<?php else : ?>
<a href="<?php echo esc_url( $binding['url'] ); ?>" class="binding-item binding-item--linkable" target="_self">
<span class="binding-item__icon"><?php echo $binding['icon']; ?></span>
<span class="binding-item__meta">
<span class="binding-status binding-status--idle"><?php esc_html_e( '未绑定', 'aksrt' ); ?></span>
<span class="binding-item__name"><?php echo esc_html( $binding['name'] ); ?></span>
</span>
<span class="badge badge--bind"><?php esc_html_e( '绑定', 'aksrt' ); ?></span>
</a>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php else : ?>
<p class="settings-empty"><?php esc_html_e( '暂无可用的第三方登录服务', 'aksrt' ); ?></p>
<?php endif; ?>
</div>
<!-- 安全设置区域 -->
<div class="settings-section">
<div class="settings-section__header">
<span class="settings-icon"><i class="fas fa-lock" aria-hidden="true"></i></span>
<h3 class="widget-title settings-section__title"><?php esc_html_e( '安全设置', 'aksrt' ); ?></h3>
</div>
<div class="security-actions">
<!-- 修改密码 -->
<div class="security-action-item">
<div class="security-action-info">
<span class="security-action-icon"><i class="fas fa-lock" aria-hidden="true"></i></span>
<div class="security-action-text">
<span class="security-action-title"><?php esc_html_e( '修改密码', 'aksrt' ); ?></span>
<span class="security-action-desc"><?php esc_html_e( '通过邮箱验证码验证后可更改登录密码', 'aksrt' ); ?></span>
</div>
</div>
<button type="button" class="btn-primary btn-sm security-trigger" data-modal="pwd-modal">
<?php esc_html_e( '修改', 'aksrt' ); ?>
</button>
</div>
<!-- 更改邮箱 -->
<div class="security-action-item">
<div class="security-action-info">
<span class="security-action-icon"><i class="far fa-envelope" aria-hidden="true"></i></span>
<div class="security-action-text">
<span class="security-action-title"><?php esc_html_e( '更改邮箱', 'aksrt' ); ?></span>
<span class="security-action-desc"><?php esc_html_e( '更新用于接收通知和找回密码的邮箱地址', 'aksrt' ); ?></span>
</div>
</div>
<button type="button" class="btn-primary btn-sm security-trigger" data-modal="email-modal">
<?php esc_html_e( '更改', 'aksrt' ); ?>
</button>
</div>
</div>
</div>
<?php if ( $show_invite_section ) : ?>
<div class="settings-section">
<div class="settings-section__header">
<span class="settings-icon"><i class="fas fa-user-plus" aria-hidden="true"></i></span>
<h3 class="widget-title settings-section__title"><?php esc_html_e( '邀请好友', 'aksrt' ); ?></h3>
</div>
<div class="aksrt-invite-panel" data-aksrt-invite>
<div class="aksrt-invite-hero">
<div class="aksrt-invite-hero__main">
<span class="aksrt-invite-kicker"><?php esc_html_e( '邀请奖励', 'aksrt' ); ?></span>
<p class="aksrt-invite-desc">
<?php echo esc_html( sprintf( __( '将专属链接分享给好友,好友完成注册与验证后即可获得双向%s奖励。', 'aksrt' ), $currency_name ) ); ?>
</p>
</div>
<div class="aksrt-invite-code-card">
<span><?php esc_html_e( '邀请码', 'aksrt' ); ?></span>
<strong data-invite-code>--</strong>
</div>
</div>
<div class="aksrt-invite-link-row">
<input type="text" class="aksrt-invite-link" data-invite-link readonly value="" aria-label="<?php esc_attr_e( '邀请链接', 'aksrt' ); ?>">
<button type="button" class="btn-primary btn-sm aksrt-invite-copy" data-invite-copy>
<i class="fas fa-copy" aria-hidden="true"></i>
<span><?php esc_html_e( '复制链接', 'aksrt' ); ?></span>
</button>
</div>
<div class="aksrt-invite-stats" data-invite-stats>
<div class="aksrt-invite-stat">
<span><?php esc_html_e( '已注册', 'aksrt' ); ?></span>
<strong data-stat="registered">0</strong>
<small><?php esc_html_e( '通过链接完成注册', 'aksrt' ); ?></small>
</div>
<div class="aksrt-invite-stat">
<span><?php esc_html_e( '已验证', 'aksrt' ); ?></span>
<strong data-stat="verified">0</strong>
<small><?php esc_html_e( '完成邮箱验证', 'aksrt' ); ?></small>
</div>
<div class="aksrt-invite-stat">
<span><?php esc_html_e( '已奖励', 'aksrt' ); ?></span>
<strong data-stat="rewarded">0</strong>
<small><?php esc_html_e( '奖励已发放', 'aksrt' ); ?></small>
</div>
</div>
<div class="aksrt-invite-records">
<div class="aksrt-invite-records__header">
<h4><?php esc_html_e( '邀请记录', 'aksrt' ); ?></h4>
<span data-invite-total><?php esc_html_e( '加载中', 'aksrt' ); ?></span>
</div>
<div class="aksrt-invite-record-list is-loading" data-invite-list>
<div class="aksrt-invite-record-empty">
<?php esc_html_e( '正在加载邀请记录...', 'aksrt' ); ?>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<!-- 隐藏字段 -->
<input type="hidden" id="aksrt-settings-nonce" value="<?php echo wp_create_nonce( 'aksrt_account_settings' ); ?>">
<input type="hidden" id="aksrt-oauth-unbind-nonce" value="<?php echo wp_create_nonce( 'aksrt_oauth_unbind' ); ?>">
<input type="hidden" id="aksrt-user-email" value="<?php echo esc_attr( $user_email ); ?>">
<input type="hidden" id="aksrt-ajax-url" value="<?php echo admin_url( 'admin-ajax.php' ); ?>">
<input type="hidden" id="aksrt-login-url" value="<?php echo esc_attr( aksrt_get_login_url() ); ?>">
<input type="hidden" id="aksrt-public-nonce" value="<?php echo wp_create_nonce( 'aksrt_public_nonce' ); ?>">
@@ -0,0 +1,94 @@
<?php
/**
* User center profile sidebar part.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
?>
<!-- 数据统计 -->
<div class="uc-sidebar__section">
<div class="uc-sidebar__title"><?php esc_html_e( '数据统计', 'aksrt' ); ?></div>
<div class="uc-stats">
<?php foreach ( [ 'posts', 'comments', 'favorites' ] as $stat_tab ) : ?>
<?php if ( ! isset( $dashboard_tabs[ $stat_tab ] ) ) { continue; } ?>
<?php
$stat_count = 'posts' === $stat_tab
? $post_count
: ( 'comments' === $stat_tab ? $comment_count : $favorite_count );
?>
<a class="uc-stats__item" href="<?php echo esc_url( add_query_arg( 'tab', $stat_tab, remove_query_arg( 'tab' ) ) ); ?>">
<span class="uc-stats__value"><?php echo esc_html( (string) $stat_count ); ?></span>
<span class="uc-stats__label"><?php echo esc_html( $dashboard_tabs[ $stat_tab ]['label'] ); ?></span>
</a>
<?php endforeach; ?>
<a class="uc-stats__item" href="<?php echo esc_url( $messages_url ); ?>">
<span class="uc-stats__value"><?php echo esc_html( (string) $notification_unread_count ); ?></span>
<span class="uc-stats__label"><?php esc_html_e( '通知', 'aksrt' ); ?></span>
</a>
</div>
</div>
<!-- 我的服务 -->
<div class="uc-sidebar__section">
<div class="uc-sidebar__title"><?php esc_html_e( '我的服务', 'aksrt' ); ?></div>
<div class="uc-sidebar__buttons">
<?php foreach ( $dashboard_tabs as $tab_key => $tab ) : ?>
<a class="uc-sidebar__btn<?php echo $active_tab === $tab_key ? ' is-active' : ''; ?>" href="<?php echo esc_url(aksrt_get_user_center_tab_url($tab_key)); ?>" data-tab="<?php echo esc_attr($tab_key); ?>">
<i class="fas fa-<?php echo esc_attr( $tab['icon'] ); ?>" aria-hidden="true"></i>
<span><?php echo esc_html( $tab['label'] ); ?></span>
</a>
<?php endforeach; ?>
<a href="<?php echo esc_url( $messages_url ); ?>" class="uc-sidebar__btn">
<i class="fas fa-bell" aria-hidden="true"></i>
<span><?php esc_html_e( '消息通知', 'aksrt' ); ?></span>
<?php if ( $notification_unread_count > 0 ) : ?>
<span class="uc-sidebar__badge"><?php echo esc_html( (string) $notification_unread_count ); ?></span>
<?php endif; ?>
</a>
</div>
</div>
<!-- 交易服务 -->
<?php if ($orders_url || $show_vip || $show_credit_tabs) : ?>
<div class="uc-sidebar__section">
<div class="uc-sidebar__title"><?php esc_html_e('权益与消费', 'aksrt'); ?></div>
<div class="uc-sidebar__buttons">
<?php if ($orders_url) : ?>
<a href="<?php echo esc_url($orders_url); ?>" class="uc-sidebar__btn">
<i class="fas fa-receipt" aria-hidden="true"></i>
<span><?php esc_html_e('我的订单', 'aksrt'); ?></span>
</a>
<?php endif; ?>
<?php if ($show_vip) : ?>
<a class="uc-sidebar__btn<?php echo $active_tab === 'credits' ? ' is-active' : ''; ?>" href="<?php echo esc_url(aksrt_get_user_center_tab_url('credits')); ?>" data-tab="credits">
<i class="fas fa-crown" aria-hidden="true"></i>
<span><?php esc_html_e('VIP 会员', 'aksrt'); ?></span>
</a>
<?php endif; ?>
<?php if ($show_credit_tabs) : ?>
<a class="uc-sidebar__btn<?php echo $active_tab === 'credits' ? ' is-active' : ''; ?>" href="<?php echo esc_url(aksrt_get_user_center_tab_url('credits')); ?>" data-tab="credits">
<i class="fas fa-coins" aria-hidden="true"></i>
<span><?php echo esc_html($currency_name); ?></span>
</a>
<?php endif; ?>
<?php if ($affiliate_enabled) : ?>
<a class="uc-sidebar__btn<?php echo $active_tab === 'credits' ? ' is-active' : ''; ?>" href="<?php echo esc_url(aksrt_get_user_center_tab_url('credits')); ?>" data-tab="credits">
<i class="fas fa-share-alt" aria-hidden="true"></i>
<span><?php esc_html_e('推广返佣', 'aksrt'); ?></span>
</a>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<!-- 功能设置 -->
<!-- 移动端抽屉触发器 -->
<button type="button" class="uc-sidebar__drawer-trigger" id="uc-drawer-trigger" aria-label="<?php esc_attr_e( '打开导航', 'aksrt' ); ?>">
<i class="fas fa-bars" aria-hidden="true"></i>
</button>
File diff suppressed because it is too large Load Diff
+7 -11
View File
@@ -174,20 +174,19 @@ $home_url = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url
<div class="profile-list">
<?php while ($author_posts->have_posts()) : $author_posts->the_post(); ?>
<?php
set_query_var('aksrt_profile_item_action', [
get_template_part('templates/parts/content', 'profile-list-item', [
'action' => [
'type' => 'view',
'url' => get_permalink(),
'label' => __('阅读', 'aksrt'),
],
]);
set_query_var('aksrt_profile_item_status', '');
get_template_part('templates/parts/content', 'profile-list-item');
?>
<?php endwhile; ?>
</div>
<?php
aksrt_render_profile_pagination((int) $author_posts->max_num_pages, $posts_page, 'posts_page');
wp_reset_postdata();
set_query_var('aksrt_profile_item_action', []);
else :
aksrt_render_profile_empty($svg_empty_post, __('TA 还没有发布文章', 'aksrt'));
endif;
@@ -211,11 +210,9 @@ $home_url = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url
<div class="profile-comment-list">
<?php foreach ($user_comments as $c) : ?>
<?php
set_query_var('aksrt_profile_comment', $c);
get_template_part('templates/parts/content', 'profile-comment-item');
get_template_part('templates/parts/content', 'profile-comment-item', ['comment' => $c]);
?>
<?php endforeach; ?>
<?php set_query_var('aksrt_profile_comment', null); ?>
</div>
<?php
$cm_total_pages = (int) ceil($comment_count / $per_page);
@@ -247,20 +244,19 @@ $home_url = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url
<div class="profile-list">
<?php while ($favorites_query->have_posts()) : $favorites_query->the_post(); ?>
<?php
set_query_var('aksrt_profile_item_action', [
get_template_part('templates/parts/content', 'profile-list-item', [
'action' => [
'type' => 'view',
'url' => get_permalink(),
'label' => __('阅读', 'aksrt'),
],
]);
set_query_var('aksrt_profile_item_status', '');
get_template_part('templates/parts/content', 'profile-list-item');
?>
<?php endwhile; ?>
</div>
<?php
aksrt_render_profile_pagination((int) $favorites_query->max_num_pages, $favorites_page, 'favorites_page');
wp_reset_postdata();
set_query_var('aksrt_profile_item_action', []);
else :
aksrt_render_profile_empty($svg_empty_favorite, __('TA 还没有收藏文章', 'aksrt'));
endif;
@@ -2,7 +2,7 @@
/**
* Profile Comment List Item — image-less comment row for user center.
*
* 通过 set_query_var() 接收:
* 通过 get_template_part() 的 $args 接收(保留 query var 兼容):
* - aksrt_profile_comment: WP_Comment 对象
*
* @package AKSRT
@@ -10,7 +10,7 @@
defined('ABSPATH') || exit;
$comment = get_query_var('aksrt_profile_comment');
$comment = $args['comment'] ?? get_query_var('aksrt_profile_comment');
if (!$comment instanceof WP_Comment) {
return;
}
@@ -5,7 +5,7 @@
* 用于:
* - 我的文章 / 我的收藏 / 公开主页文章列表
*
* 通过 set_query_var() 接收以下参数(可选):
* 通过 get_template_part() 的 $args 接收以下参数(保留 query var 兼容):
* - aksrt_profile_item_action: ['type'=>'edit'|'view'|'unfavorite', 'url'=>'...', 'label'=>'...']
* - aksrt_profile_item_status: 文章状态徽章文本(如 "草稿" / "私密")
*
@@ -14,7 +14,7 @@
defined('ABSPATH') || exit;
$action = get_query_var('aksrt_profile_item_action', []);
$action = isset($args['action']) ? $args['action'] : get_query_var('aksrt_profile_item_action', []);
if (!is_array($action)) {
$action = [];
}
@@ -22,7 +22,7 @@ $action_type = isset($action['type']) ? (string) $action['type'] : '';
$action_url = isset($action['url']) ? (string) $action['url'] : '';
$action_label = isset($action['label']) ? (string) $action['label'] : '';
$status_label = (string) get_query_var('aksrt_profile_item_status', '');
$status_label = isset($args['status']) ? (string) $args['status'] : (string) get_query_var('aksrt_profile_item_status', '');
$post_id = get_the_ID();
$permalink = get_permalink();
@@ -0,0 +1,5 @@
<?php
/** User center account modals. @package AKSRT */
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
echo $context['account_modals_html'] ?? ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+5
View File
@@ -0,0 +1,5 @@
<?php
/** User center header. @package AKSRT */
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
echo $context['header_html'] ?? ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -0,0 +1,10 @@
<?php
/** User center mobile tabs. @package AKSRT */
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
?>
<div class="dashboard-tabs uc-mobile-tabs" role="tablist">
<?php foreach (($context['mobile_tabs'] ?? []) as $tab) : ?>
<a class="<?php echo !empty($tab['is_active']) ? 'is-active' : ''; ?>" href="<?php echo esc_url($tab['url']); ?>" data-tab="<?php echo esc_attr($tab['key']); ?>" role="tab" aria-selected="<?php echo !empty($tab['is_active']) ? 'true' : 'false'; ?>"><?php echo esc_html($tab['label']); ?></a>
<?php endforeach; ?>
</div>
@@ -0,0 +1,8 @@
<?php
/** User center comments panel. @package AKSRT */
defined('ABSPATH') || exit;
$panel = is_array($args['panel'] ?? null) ? $args['panel'] : [];
?>
<div id="panel-<?php echo esc_attr($panel['key'] ?? 'comments'); ?>" class="dashboard-panel<?php echo !empty($panel['is_active']) ? ' is-active' : ''; ?>" role="tabpanel"<?php echo empty($panel['is_active']) ? ' hidden' : ''; ?> data-panel-loaded="<?php echo !empty($panel['is_loaded']) ? 'true' : 'false'; ?>" data-tab-url="<?php echo esc_url($panel['url'] ?? ''); ?>">
<?php echo $panel['html'] ?? ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
@@ -0,0 +1,8 @@
<?php
/** User center credits panel. @package AKSRT */
defined('ABSPATH') || exit;
$panel = is_array($args['panel'] ?? null) ? $args['panel'] : [];
?>
<div id="panel-<?php echo esc_attr($panel['key'] ?? 'credits'); ?>" class="dashboard-panel<?php echo !empty($panel['is_active']) ? ' is-active' : ''; ?>" role="tabpanel"<?php echo empty($panel['is_active']) ? ' hidden' : ''; ?> data-panel-loaded="<?php echo !empty($panel['is_loaded']) ? 'true' : 'false'; ?>" data-tab-url="<?php echo esc_url($panel['url'] ?? ''); ?>">
<?php echo $panel['html'] ?? ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
@@ -0,0 +1,8 @@
<?php
/** User center favorites panel. @package AKSRT */
defined('ABSPATH') || exit;
$panel = is_array($args['panel'] ?? null) ? $args['panel'] : [];
?>
<div id="panel-<?php echo esc_attr($panel['key'] ?? 'favorites'); ?>" class="dashboard-panel<?php echo !empty($panel['is_active']) ? ' is-active' : ''; ?>" role="tabpanel"<?php echo empty($panel['is_active']) ? ' hidden' : ''; ?> data-panel-loaded="<?php echo !empty($panel['is_loaded']) ? 'true' : 'false'; ?>" data-tab-url="<?php echo esc_url($panel['url'] ?? ''); ?>">
<?php echo $panel['html'] ?? ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
@@ -0,0 +1,8 @@
<?php
/** User center posts panel. @package AKSRT */
defined('ABSPATH') || exit;
$panel = is_array($args['panel'] ?? null) ? $args['panel'] : [];
?>
<div id="panel-<?php echo esc_attr($panel['key'] ?? 'posts'); ?>" class="dashboard-panel<?php echo !empty($panel['is_active']) ? ' is-active' : ''; ?>" role="tabpanel"<?php echo empty($panel['is_active']) ? ' hidden' : ''; ?> data-panel-loaded="<?php echo !empty($panel['is_loaded']) ? 'true' : 'false'; ?>" data-tab-url="<?php echo esc_url($panel['url'] ?? ''); ?>">
<?php echo $panel['html'] ?? ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
@@ -0,0 +1,8 @@
<?php
/** User center profile panel. @package AKSRT */
defined('ABSPATH') || exit;
$panel = is_array($args['panel'] ?? null) ? $args['panel'] : [];
?>
<div id="panel-<?php echo esc_attr($panel['key'] ?? 'profile'); ?>" class="dashboard-panel<?php echo !empty($panel['is_active']) ? ' is-active' : ''; ?>" role="tabpanel"<?php echo empty($panel['is_active']) ? ' hidden' : ''; ?> data-panel-loaded="<?php echo !empty($panel['is_loaded']) ? 'true' : 'false'; ?>" data-tab-url="<?php echo esc_url($panel['url'] ?? ''); ?>">
<?php echo $panel['html'] ?? ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
@@ -0,0 +1,8 @@
<?php
/** User center settings panel. @package AKSRT */
defined('ABSPATH') || exit;
$panel = is_array($args['panel'] ?? null) ? $args['panel'] : [];
?>
<div id="panel-<?php echo esc_attr($panel['key'] ?? 'settings'); ?>" class="dashboard-panel<?php echo !empty($panel['is_active']) ? ' is-active' : ''; ?>" role="tabpanel"<?php echo empty($panel['is_active']) ? ' hidden' : ''; ?> data-panel-loaded="<?php echo !empty($panel['is_loaded']) ? 'true' : 'false'; ?>" data-tab-url="<?php echo esc_url($panel['url'] ?? ''); ?>">
<?php echo $panel['html'] ?? ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
+14
View File
@@ -0,0 +1,14 @@
<?php
/**
* Generic user-center panel wrapper for registered extensions.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$panel = is_array($args['panel'] ?? null) ? $args['panel'] : [];
?>
<div id="panel-<?php echo esc_attr($panel['key'] ?? 'custom'); ?>" class="dashboard-panel<?php echo !empty($panel['is_active']) ? ' is-active' : ''; ?>" role="tabpanel"<?php echo empty($panel['is_active']) ? ' hidden' : ''; ?> data-panel-loaded="<?php echo !empty($panel['is_loaded']) ? 'true' : 'false'; ?>" data-tab-url="<?php echo esc_url($panel['url'] ?? ''); ?>">
<?php echo $panel['html'] ?? ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
@@ -0,0 +1,9 @@
<?php
/** User center runtime configuration. @package AKSRT */
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
?>
<script>window.aksrtUserCenter=<?php echo wp_json_encode($context['runtime_config'] ?? []); ?>;</script>
<?php if (!empty($context['cover_upload_config'])) : ?>
<script>window.aksrtAuthorPage=Object.assign({},window.aksrtAuthorPage||{},{coverUpload:<?php echo wp_json_encode($context['cover_upload_config']); ?>});</script>
<?php endif; ?>
+5
View File
@@ -0,0 +1,5 @@
<?php
/** User center sidebar. @package AKSRT */
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
echo $context['sidebar_html'] ?? ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped