导入 AKSRT 主题开发源码

This commit is contained in:
Claude Code committed 2026-07-03 22:50:34 +08:00
1 parent 9681dd3d48
commit 1268a55dd0
595 files changed
+127705

No files matched your search

+297
View File
@@ -0,0 +1,297 @@
<?php
/**
* Forum board template.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$forum = class_exists('AKSRT_Forum') ? AKSRT_Forum::get_instance() : null;
$current_user_id = get_current_user_id();
$board = get_queried_object();
$board_id = $board ? (int) $board->term_id : 0;
$paged = max(1, (int) get_query_var('paged', 1));
$view = $forum ? $forum->get_board_view($board_id, [
'user_id' => $current_user_id,
'paged' => $paged,
]) : [
'board' => null,
'topics' => [],
'sticky_topics' => [],
'moderators' => [],
'pagination' => ['page' => 1, 'max_pages' => 1, 'total' => 0],
'permissions' => [],
'application' => null,
'board_options' => [],
];
$board_data = $view['board'] ?? null;
$topics = $view['topics'] ?? [];
$sticky_topics = $view['sticky_topics'] ?? [];
$moderators = $view['moderators'] ?? [];
$permissions = $view['permissions'] ?? [];
$application = $view['application'] ?? null;
$board_options = $view['board_options'] ?? [];
$compose_url = add_query_arg('board', $board_id, home_url('/compose/'));
$admin_forum_url = admin_url('admin.php?page=aksrt-theme-admin#/forum/topics');
// Forum settings — names
$forum_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_forum_settings() : [];
$forum_names = $forum_settings['names'] ?? [];
$name_topic = $forum_names['posts'] ?? __('主题', 'aksrt');
$name_reply = $forum_names['comment'] ?? __('回复', 'aksrt');
$name_sticky = __('置顶', 'aksrt');
$name_featured = __('精华', 'aksrt');
?>
<main id="main-content" class="site-main container" tabindex="-1">
<div class="aksrt-forum-board container">
<div class="aksrt-forum-board__header">
<div class="aksrt-forum-board__info">
<h1 class="aksrt-forum-board__title"><?php echo esc_html((string) ($board_data['name'] ?? '')); ?></h1>
<?php if (!empty($board_data['description'])) : ?>
<p class="aksrt-forum-board__desc"><?php echo esc_html((string) $board_data['description']); ?></p>
<?php endif; ?>
<div class="aksrt-forum-board__meta">
<span><i class="fas fa-list-alt"></i> <?php printf(esc_html__('%1$d 个%2$s', 'aksrt'), (int) ($board_data['topic_count'] ?? 0), esc_html($name_topic)); ?></span>
<span><i class="fas fa-shield-alt"></i> <?php printf(esc_html__('%d 位版主', 'aksrt'), count($moderators)); ?></span>
</div>
</div>
<div class="aksrt-forum-board__actions">
<?php if (!empty($permissions['can_compose'])) : ?>
<a href="<?php echo esc_url($compose_url); ?>" class="aksrt-forum-board__compose-btn">
<i class="fas fa-pen"></i>
<?php esc_html_e('发布新主题', 'aksrt'); ?>
</a>
<?php else : ?>
<a href="<?php echo esc_url(wp_login_url(get_term_link($board))); ?>" class="aksrt-forum-board__compose-btn">
<i class="fas fa-sign-in-alt"></i>
<?php esc_html_e('登录后发帖', 'aksrt'); ?>
</a>
<?php endif; ?>
<?php if (!empty($permissions['can_moderate'])) : ?>
<a href="<?php echo esc_url($admin_forum_url); ?>" class="aksrt-forum-board__manage-btn">
<i class="fas fa-tools"></i>
<?php esc_html_e('进入治理后台', 'aksrt'); ?>
</a>
<?php endif; ?>
</div>
</div>
<section class="aksrt-forum-board__governance">
<div class="aksrt-forum-board__governance-card">
<h2 class="aksrt-forum-board__section-title"><?php esc_html_e('权限反馈', 'aksrt'); ?></h2>
<?php if (!is_user_logged_in()) : ?>
<p><?php esc_html_e('登录后可以发帖、申请版主,并在有权限时执行治理动作。', 'aksrt'); ?></p>
<?php elseif (!empty($permissions['can_moderate'])) : ?>
<p><?php esc_html_e('你当前拥有本版块治理权限,可在主题页执行审核、移动版块、可见性调整等操作。', 'aksrt'); ?></p>
<?php elseif ($application) : ?>
<p>
<?php esc_html_e('你的版主申请状态:', 'aksrt'); ?>
<strong><?php echo esc_html((string) ($application['status_label'] ?? '')); ?></strong>
</p>
<?php else : ?>
<p><?php esc_html_e('当前可浏览并发帖,若你熟悉本版块内容,可提交版主申请参与治理。', 'aksrt'); ?></p>
<?php endif; ?>
</div>
<div class="aksrt-forum-board__governance-card">
<h2 class="aksrt-forum-board__section-title"><?php esc_html_e('版主团队', 'aksrt'); ?></h2>
<?php if (!empty($moderators)) : ?>
<div class="aksrt-forum-board__moderators">
<?php foreach ($moderators as $moderator) : ?>
<div class="aksrt-forum-board__moderator">
<?php if (!empty($moderator['avatar_url'])) : ?>
<img src="<?php echo esc_url((string) $moderator['avatar_url']); ?>" alt="" width="36" height="36">
<?php endif; ?>
<span><?php echo esc_html((string) ($moderator['display_name'] ?? '')); ?></span>
</div>
<?php endforeach; ?>
</div>
<?php else : ?>
<p><?php esc_html_e('当前还没有设置版主。', 'aksrt'); ?></p>
<?php endif; ?>
</div>
<?php if (is_user_logged_in() && empty($permissions['can_moderate'])) : ?>
<div class="aksrt-forum-board__governance-card">
<h2 class="aksrt-forum-board__section-title"><?php esc_html_e('申请版主', 'aksrt'); ?></h2>
<?php if ($application) : ?>
<p><?php echo esc_html((string) ($application['status_label'] ?? '')); ?></p>
<?php if (!empty($application['review_note'])) : ?>
<p class="aksrt-forum-board__application-note"><?php echo esc_html((string) $application['review_note']); ?></p>
<?php endif; ?>
<?php else : ?>
<form class="aksrt-forum-board__application-form js-aksrt-moderator-apply-form" data-board-id="<?php echo esc_attr((string) $board_id); ?>">
<label for="aksrt-board-apply-reason"><?php esc_html_e('申请理由', 'aksrt'); ?></label>
<textarea id="aksrt-board-apply-reason" name="reason" rows="3" placeholder="<?php esc_attr_e('说明你熟悉该版块内容、希望承担的治理职责等。', 'aksrt'); ?>" required></textarea>
<div class="aksrt-forum-board__application-actions">
<button type="submit" class="aksrt-forum-board__apply-btn"><?php esc_html_e('提交申请', 'aksrt'); ?></button>
<span class="aksrt-forum-board__application-feedback js-aksrt-action-feedback" aria-live="polite"></span>
</div>
</form>
<?php endif; ?>
</div>
<?php endif; ?>
</section>
<?php if (!empty($sticky_topics)) : ?>
<div class="aksrt-forum-board__sticky">
<h3><i class="fas fa-thumbtack"></i> <?php esc_html_e('置顶主题', 'aksrt'); ?></h3>
<?php foreach ($sticky_topics as $topic) : ?>
<a href="<?php echo esc_url((string) ($topic['permalink'] ?? '#')); ?>" class="aksrt-forum-board__sticky-item">
<?php
$sticky_label = (string) ($topic['sticky_label'] ?? '');
if ($sticky_label === '') {
$sticky_label = __('置顶', 'aksrt');
}
?>
<span class="aksrt-forum-board__sticky-badge"><?php echo esc_html($sticky_label); ?></span>
<span class="aksrt-forum-board__sticky-title"><?php echo esc_html((string) ($topic['title'] ?? '')); ?></span>
<span class="aksrt-forum-board__sticky-meta">
<?php echo esc_html((string) ($topic['author']['display_name'] ?? '')); ?>
· <?php echo esc_html((string) ($topic['date'] ?? '')); ?>
</span>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="aksrt-forum-board__topics">
<?php if (!empty($topics)) : ?>
<?php foreach ($topics as $topic) :
$topic_id = (int) ($topic['id'] ?? 0);
?>
<?php
$item_sticky_level_class = (int) ($topic['sticky_level'] ?? 0) > 0
? ' is-sticky is-sticky-' . (int) ($topic['sticky_level'] ?? 0)
: '';
?>
<article class="aksrt-forum-topic-item<?php echo esc_attr($item_sticky_level_class); ?><?php echo !empty($topic['featured']) ? ' is-featured' : ''; ?>">
<div class="aksrt-forum-topic-item__avatar">
<?php if (!empty($topic['author']['avatar_url'])) : ?>
<img src="<?php echo esc_url((string) $topic['author']['avatar_url']); ?>" alt="" width="40" height="40">
<?php endif; ?>
</div>
<?php if (!empty($topic['cover'])) : ?>
<div class="aksrt-forum-topic-item__cover">
<?php if ($topic['cover']['type'] === 'video') : ?>
<div class="aksrt-forum-topic-item__cover-video">
<img src="<?php echo esc_url((string) ($topic['cover']['poster'] ?: $topic['cover']['url'])); ?>" alt="" loading="lazy">
<span class="aksrt-forum-topic-item__cover-play"><i class="fas fa-play-circle"></i></span>
</div>
<?php elseif ($topic['cover']['type'] === 'slide') : ?>
<div class="aksrt-forum-topic-item__cover-slide">
<img src="<?php echo esc_url((string) ($topic['cover']['images'][0] ?? '')); ?>" alt="" loading="lazy">
<span class="aksrt-forum-topic-item__cover-count"><i class="fas fa-images"></i> <?php echo count($topic['cover']['images']); ?></span>
</div>
<?php else : ?>
<img src="<?php echo esc_url((string) ($topic['cover']['url'] ?? '')); ?>" alt="" loading="lazy">
<?php endif; ?>
</div>
<?php endif; ?>
<div class="aksrt-forum-topic-item__body">
<div class="aksrt-forum-topic-item__title">
<?php
$item_sticky_level = (int) ($topic['sticky_level'] ?? 0);
$item_sticky_label = (string) ($topic['sticky_label'] ?? '');
if ($item_sticky_level > 0 && $item_sticky_label !== '') :
$item_sticky_mod = $item_sticky_level >= 3 ? 'global' : ($item_sticky_level === 2 ? 'super' : 'plate');
?>
<span class="aksrt-forum-topic-item__badge aksrt-forum-topic-item__badge--sticky aksrt-forum-topic-item__badge--sticky-<?php echo esc_attr($item_sticky_mod); ?>"><?php echo esc_html($item_sticky_label); ?></span>
<?php endif; ?>
<?php if (!empty($topic['featured'])) : ?><span class="aksrt-forum-topic-item__badge aksrt-forum-topic-item__badge--featured"><?php esc_html_e('精华', 'aksrt'); ?></span><?php endif; ?>
<?php if (($topic['bbs_type'] ?? 'standard') === 'question') : ?>
<?php $q_status = (int) ($topic['question_status'] ?? 0); ?>
<span class="aksrt-forum-topic-item__badge aksrt-forum-topic-item__badge--question<?php echo $q_status === 1 ? ' aksrt-forum-topic-item__badge--solved' : ''; ?>">
<?php echo $q_status === 1 ? esc_html__('已解决', 'aksrt') : esc_html__('提问', 'aksrt'); ?>
</span>
<?php endif; ?>
<?php if (!empty($topic['locked'])) : ?><i class="fas fa-lock aksrt-forum-topic-item__lock"></i><?php endif; ?>
<?php if (!empty($topic['visibility']['key']) && $topic['visibility']['key'] !== 'public') : ?>
<span class="aksrt-forum-topic-item__badge aksrt-forum-topic-item__badge--muted"><?php echo esc_html((string) ($topic['visibility']['label'] ?? '')); ?></span>
<?php endif; ?>
<a href="<?php echo esc_url((string) ($topic['permalink'] ?? '#')); ?>"><?php echo esc_html((string) ($topic['title'] ?? '')); ?></a>
</div>
<div class="aksrt-forum-topic-item__meta">
<span><?php echo esc_html((string) ($topic['author']['display_name'] ?? '')); ?></span>
<span><?php echo esc_html((string) ($topic['date'] ?? '')); ?></span>
<?php if (!empty($topic['special_topic'])) : ?>
<span><?php echo esc_html((string) $topic['special_topic']); ?></span>
<?php endif; ?>
</div>
</div>
<div class="aksrt-forum-topic-item__stats">
<span title="<?php echo esc_attr($name_reply); ?>"><i class="fas fa-comment"></i> <?php echo esc_html(number_format_i18n((int) ($topic['comment_count'] ?? 0))); ?></span>
<span title="<?php esc_attr_e('浏览', 'aksrt'); ?>"><i class="fas fa-eye"></i> <?php echo esc_html(number_format_i18n((int) ($topic['views'] ?? 0))); ?></span>
</div>
<?php if (!empty($permissions['can_moderate'])) : ?>
<div class="aksrt-forum-topic-item__actions">
<?php
$sticky_level = (int) ($topic['sticky_level'] ?? 0);
// 快捷按钮:无置顶↔版块置顶 切换;超级/全局置顶需进入治理表单调整。
$sticky_next = $sticky_level > 0 ? 0 : 1;
?>
<button
type="button"
class="aksrt-forum-topic-item__action js-aksrt-topic-action"
data-topic-id="<?php echo esc_attr((string) $topic_id); ?>"
data-action="sticky"
data-value="<?php echo esc_attr((string) $sticky_next); ?>"
data-value-type="int"
>
<?php echo $sticky_level > 0 ? esc_html__('取消置顶', 'aksrt') : esc_html__('置顶', 'aksrt'); ?>
</button>
<button
type="button"
class="aksrt-forum-topic-item__action js-aksrt-topic-action"
data-topic-id="<?php echo esc_attr((string) $topic_id); ?>"
data-action="locked"
data-value="<?php echo !empty($topic['locked']) ? '0' : '1'; ?>"
>
<?php echo !empty($topic['locked']) ? esc_html__('解锁', 'aksrt') : esc_html__('锁定', 'aksrt'); ?>
</button>
<a href="<?php echo esc_url((string) ($topic['permalink'] ?? '#') . '#aksrt-topic-moderation'); ?>" class="aksrt-forum-topic-item__action aksrt-forum-topic-item__action--link">
<?php esc_html_e('更多治理', 'aksrt'); ?>
</a>
</div>
<?php endif; ?>
</article>
<?php endforeach; ?>
<?php if (($view['pagination']['max_pages'] ?? 1) > 1) : ?>
<div class="aksrt-forum-board__pagination">
<?php
echo paginate_links([
'total' => (int) ($view['pagination']['max_pages'] ?? 1),
'current' => $paged,
'prev_text' => '<i class="fas fa-chevron-left"></i>',
'next_text' => '<i class="fas fa-chevron-right"></i>',
]);
?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="aksrt-forum-board__empty">
<i class="fas fa-comments"></i>
<p><?php esc_html_e('当前版块还没有公开主题。', 'aksrt'); ?></p>
<?php if (!empty($permissions['can_compose'])) : ?>
<a href="<?php echo esc_url($compose_url); ?>"><?php esc_html_e('发布第一个主题', 'aksrt'); ?></a>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>
</main>
<?php get_footer(); ?>
+409
View File
@@ -0,0 +1,409 @@
<?php
/**
* Template Name: 论坛发帖
* Template Post Type: page
*
* §7.4 — wp_editor rich editor + sidebar panels + server-side draft + capability gates.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
if (!is_user_logged_in()) {
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink());
wp_safe_redirect($login_url);
exit;
}
$forum = class_exists('AKSRT_Forum') ? AKSRT_Forum::get_instance() : null;
$current_user_id = get_current_user_id();
$board_id = isset($_GET['board']) ? (int) $_GET['board'] : 0;
$draft_id = isset($_GET['draft']) ? (int) $_GET['draft'] : 0;
$view = $forum ? $forum->get_compose_view($current_user_id, ['board_id' => $board_id]) : [
'boards' => [],
'selected_board_id' => 0,
'visibility_options' => [],
'publish_mode' => 'pending',
'draft_count' => 0,
'drafts' => [],
'capabilities' => [],
];
$forum_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_forum_settings() : [];
$title_placeholder = $forum_settings['other']['t_placeholder'] ?? __('请输入标题', 'aksrt');
$content_placeholder = $forum_settings['other']['c_placeholder'] ?? __('请输入内容', 'aksrt');
$title_maxlen = max(5, min(100, (int) ($forum_settings['other']['title_strlen_limit'] ?? 30)));
$vote_max = (int) ($forum_settings['other']['vote_max'] ?? 8);
$forum_name = $forum_settings['names']['forum'] ?? __('论坛', 'aksrt');
$boards = $view['boards'] ?? [];
$visibility_options = $view['visibility_options'] ?? [];
$publish_mode = (string) ($view['publish_mode'] ?? 'pending');
$draft_count = (int) ($view['draft_count'] ?? 0);
$drafts = $view['drafts'] ?? [];
$caps = $view['capabilities'] ?? [];
$nonce = wp_create_nonce('aksrt_forum_compose');
// Load draft data if resuming
$draft_data = null;
if ($draft_id > 0 && $forum) {
$draft_data = $forum->load_draft_data($current_user_id, $draft_id);
if ($draft_data) {
$board_id = (int) ($draft_data['board_id'] ?? 0);
}
}
$initial_content = $draft_data ? $draft_data['content'] : '';
?>
<main id="main-content" class="site-main container" tabindex="-1">
<div class="aksrt-forum-compose container">
<div class="aksrt-forum-compose__header">
<div>
<h1><i class="fas fa-pen-fancy"></i> <?php esc_html_e('发布新主题', 'aksrt'); ?></h1>
<p class="aksrt-forum-compose__notice">
<?php
echo $publish_mode === 'publish'
? esc_html__('你在当前版块具备直接发布权限。', 'aksrt')
: esc_html__('普通用户发帖将进入审核流程,审核通过后才会公开展示。', 'aksrt');
?>
</p>
</div>
<a href="<?php echo esc_url(home_url('/forums/')); ?>" class="aksrt-forum-compose__back">
<i class="fas fa-arrow-left"></i> <?php printf(esc_html__('返回%s', 'aksrt'), esc_html($forum_name)); ?>
</a>
</div>
<?php if ($draft_count > 0) : ?>
<div class="aksrt-forum-compose__draft-notice">
<i class="fas fa-file-alt"></i>
<?php printf(
esc_html__('您有 %d 篇草稿。', 'aksrt'),
$draft_count
); ?>
<button type="button" class="aksrt-forum-compose__draft-toggle js-aksrt-draft-toggle">
<?php esc_html_e('查看草稿', 'aksrt'); ?>
<i class="fas fa-chevron-down"></i>
</button>
<div class="aksrt-forum-compose__draft-list" id="compose-draft-list" hidden>
<?php foreach ($drafts as $d) : ?>
<div class="aksrt-forum-compose__draft-item" data-draft-id="<?php echo esc_attr((string) $d['id']); ?>">
<a href="<?php echo esc_url(add_query_arg('draft', $d['id'], get_permalink())); ?>" class="aksrt-forum-compose__draft-title">
<?php echo esc_html($d['title']); ?>
</a>
<span class="aksrt-forum-compose__draft-meta">
<?php echo esc_html($d['board_name'] ?: __('未选版块', 'aksrt')); ?> &middot;
<?php echo esc_html(human_time_diff(strtotime($d['date']), current_time('timestamp'))); ?><?php esc_html_e('前', 'aksrt'); ?>
</span>
<button type="button" class="aksrt-forum-compose__draft-delete js-aksrt-delete-draft" data-draft-id="<?php echo esc_attr((string) $d['id']); ?>" title="<?php esc_attr_e('删除草稿', 'aksrt'); ?>">
<i class="fas fa-trash-alt"></i>
</button>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<form
id="aksrt-forum-compose-form"
class="aksrt-forum-compose__form aksrt-forum-compose__layout js-aksrt-forum-compose-form"
data-ajax="<?php echo esc_url(admin_url('admin-ajax.php')); ?>"
data-nonce="<?php echo esc_attr($nonce); ?>"
data-publish-mode="<?php echo esc_attr($publish_mode); ?>"
data-vote-max="<?php echo esc_attr((string) $vote_max); ?>"
data-draft-id="<?php echo esc_attr((string) ($draft_data['draft_id'] ?? 0)); ?>"
>
<!-- ═══ Main content area ═══ -->
<div class="aksrt-forum-compose__main">
<div class="aksrt-forum-compose__field">
<label for="compose-title"><?php esc_html_e('主题标题', 'aksrt'); ?> <span class="required">*</span></label>
<input type="text" id="compose-title" name="title" required
maxlength="<?php echo esc_attr((string) $title_maxlen); ?>"
placeholder="<?php echo esc_attr($title_placeholder); ?>"
value="<?php echo esc_attr($draft_data['title'] ?? ''); ?>"
data-aksrt-maxlength="<?php echo esc_attr((string) $title_maxlen); ?>">
<span class="aksrt-forum-compose__char-counter" id="compose-title-counter">
<span class="js-aksrt-counter-current"><?php echo esc_html((string) mb_strlen($draft_data['title'] ?? '')); ?></span> / <?php echo esc_html((string) $title_maxlen); ?>
</span>
</div>
<div class="aksrt-forum-compose__field aksrt-forum-compose__editor-wrap">
<label><?php esc_html_e('主题内容', 'aksrt'); ?> <span class="required">*</span></label>
<?php
$editor_settings = [
'textarea_name' => 'content',
'textarea_rows' => 18,
'media_buttons' => !empty($caps['can_upload_image']) || !empty($caps['can_upload_video']) || !empty($caps['can_upload_file']),
'teeny' => false,
'quicktags' => true,
'tinymce' => [
'toolbar1' => 'formatselect,bold,italic,underline,strikethrough,blockquote,bullist,numlist,alignleft,aligncenter,alignright,link,unlink,wp_more,fullscreen',
'toolbar2' => 'pastetext,removeformat,charmap,outdent,indent,undo,redo,wp_help',
'plugins' => 'paste,link,lists,fullscreen,wordpress,wplink,charmap',
],
];
if (empty($caps['can_upload_image']) && empty($caps['can_upload_video']) && empty($caps['can_upload_file'])) {
$editor_settings['tinymce']['toolbar1'] = str_replace(',wp_more', '', $editor_settings['tinymce']['toolbar1']);
}
wp_editor($initial_content, 'compose-content', $editor_settings);
?>
</div>
</div>
<!-- ═══ Sidebar panels ═══ -->
<div class="aksrt-forum-compose__sidebar">
<!-- Panel: Submit / Status -->
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-paper-plane"></i> <?php esc_html_e('发布', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<p class="aksrt-forum-compose__status-line">
<i class="fas fa-info-circle"></i>
<span id="compose-status-text">
<?php echo $publish_mode === 'publish'
? esc_html__('状态:直接发布', 'aksrt')
: esc_html__('状态:待审核', 'aksrt'); ?>
</span>
</p>
<div class="aksrt-forum-compose__actions">
<button type="submit" class="aksrt-forum-compose__submit" id="compose-submit">
<i class="fas fa-paper-plane"></i> <?php esc_html_e('提交主题', 'aksrt'); ?>
</button>
<button type="button" class="aksrt-forum-compose__draft" id="compose-draft">
<i class="fas fa-save"></i> <?php esc_html_e('保存草稿', 'aksrt'); ?>
</button>
</div>
<span class="aksrt-forum-compose__feedback js-aksrt-action-feedback" aria-live="polite"></span>
</div>
</div>
<!-- Panel: Board selector -->
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-th-large"></i> <?php esc_html_e('版块', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<div class="aksrt-forum-compose__field aksrt-remote-select" data-search-action="aksrt_forum_search_boards">
<input type="hidden" id="compose-board" name="board_id" value="<?php echo esc_attr((string) $board_id); ?>">
<input type="text" class="aksrt-remote-select__input js-aksrt-remote-input"
placeholder="<?php esc_attr_e('搜索版块…', 'aksrt'); ?>"
autocomplete="off"
value="<?php
if ($board_id > 0) {
foreach ($boards as $b) {
if (((int) ($b['id'] ?? 0)) === $board_id) {
echo esc_attr((string) ($b['name'] ?? ''));
break;
}
}
}
?>">
<div class="aksrt-remote-select__dropdown" hidden></div>
</div>
</div>
</div>
<!-- Panel: Post type -->
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-tag"></i> <?php esc_html_e('类型 & 分类', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<div class="aksrt-forum-compose__field">
<label for="compose-bbs-type"><?php esc_html_e('帖子类型', 'aksrt'); ?></label>
<select id="compose-bbs-type" name="bbs_type">
<option value="standard" <?php selected($draft_data['bbs_type'] ?? '', 'standard'); ?>><?php esc_html_e('标准帖子', 'aksrt'); ?></option>
<option value="question" <?php selected($draft_data['bbs_type'] ?? '', 'question'); ?>><?php esc_html_e('提问', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-forum-compose__field">
<label for="compose-special"><?php esc_html_e('专题/系列', 'aksrt'); ?></label>
<input type="text" id="compose-special" name="special_topic" maxlength="80"
placeholder="<?php esc_attr_e('例如:站点重构周报', 'aksrt'); ?>"
value="<?php echo esc_attr($draft_data['special_topic'] ?? ''); ?>">
</div>
<?php if (taxonomy_exists('aksrt_forum_topic_cat')) : ?>
<div class="aksrt-forum-compose__field aksrt-remote-select" data-search-action="aksrt_forum_search_topics">
<label><?php esc_html_e('话题', 'aksrt'); ?></label>
<input type="hidden" name="topic_cat" value="<?php echo esc_attr($draft_data['topic_cat'] ?? ''); ?>">
<input type="text" class="aksrt-remote-select__input js-aksrt-remote-input"
placeholder="<?php esc_attr_e('搜索话题…', 'aksrt'); ?>"
autocomplete="off"
value="<?php echo esc_attr($draft_data['topic_cat'] ?? ''); ?>">
<div class="aksrt-remote-select__dropdown" hidden></div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Panel: Tags -->
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-tags"></i> <?php esc_html_e('标签', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<div class="aksrt-forum-compose__field aksrt-tag-input" data-search-action="aksrt_forum_search_tags" data-max="5">
<input type="hidden" id="compose-tags" name="tags" value="<?php echo esc_attr($draft_data['tags'] ?? ''); ?>">
<div class="aksrt-tag-input__tokens" id="compose-tag-tokens"></div>
<input type="text" class="aksrt-tag-input__input js-aksrt-tag-search"
placeholder="<?php esc_attr_e('输入标签名,回车添加', 'aksrt'); ?>"
autocomplete="off" maxlength="30">
<div class="aksrt-tag-input__dropdown" hidden></div>
</div>
</div>
</div>
<!-- Panel: Visibility -->
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-eye"></i> <?php esc_html_e('可见性', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<div class="aksrt-forum-compose__field">
<select id="compose-visibility" name="visibility">
<?php foreach ($visibility_options as $option) : ?>
<option value="<?php echo esc_attr((string) ($option['value'] ?? '')); ?>"
<?php selected($draft_data['visibility'] ?? 'public', $option['value'] ?? ''); ?>>
<?php echo esc_html((string) ($option['label'] ?? '')); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="aksrt-forum-compose__visibility-extra" id="compose-credits-cost-field" style="display:none;">
<label for="compose-credits-cost"><?php esc_html_e('积分价格', 'aksrt'); ?></label>
<input type="number" id="compose-credits-cost" name="credits_cost" min="1" max="99999"
value="<?php echo esc_attr((string) ($draft_data['credits_cost'] ?? '')); ?>"
placeholder="<?php esc_attr_e('查看需要的积分数', 'aksrt'); ?>">
</div>
<div class="aksrt-forum-compose__visibility-extra" id="compose-pay-price-field" style="display:none;">
<label for="compose-pay-price"><?php esc_html_e('付费价格(¥)', 'aksrt'); ?></label>
<input type="number" id="compose-pay-price" name="pay_price" min="0.01" step="0.01" max="99999"
value="<?php echo esc_attr((string) ($draft_data['pay_price'] ?? '')); ?>"
placeholder="<?php esc_attr_e('查看需要的金额', 'aksrt'); ?>">
</div>
<div class="aksrt-forum-compose__visibility-extra" id="compose-role-field" style="display:none;">
<label for="compose-visibility-roles"><?php esc_html_e('允许的角色', 'aksrt'); ?></label>
<?php $selected_roles = $draft_data['visibility_roles'] ?? []; ?>
<select id="compose-visibility-roles" name="visibility_roles[]" multiple>
<?php foreach (wp_roles()->get_names() as $role_slug => $role_name) : ?>
<option value="<?php echo esc_attr($role_slug); ?>"
<?php echo in_array($role_slug, $selected_roles, true) ? 'selected' : ''; ?>>
<?php echo esc_html(translate_user_role($role_name)); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<!-- Panel: Cover -->
<?php if (!empty($caps['can_set_cover'])) : ?>
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-image"></i> <?php esc_html_e('封面', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<div class="aksrt-forum-compose__field">
<select id="compose-cover-type" name="cover_type">
<option value="" <?php selected($draft_data['cover_type'] ?? '', ''); ?>><?php esc_html_e('无封面', 'aksrt'); ?></option>
<?php if (!empty($caps['can_set_cover_image'])) : ?>
<option value="image" <?php selected($draft_data['cover_type'] ?? '', 'image'); ?>><?php esc_html_e('图片封面', 'aksrt'); ?></option>
<?php endif; ?>
<?php if (!empty($caps['can_set_cover_slide'])) : ?>
<option value="slide" <?php selected($draft_data['cover_type'] ?? '', 'slide'); ?>><?php esc_html_e('幻灯片封面', 'aksrt'); ?></option>
<?php endif; ?>
<?php if (!empty($caps['can_set_cover_video'])) : ?>
<option value="video" <?php selected($draft_data['cover_type'] ?? '', 'video'); ?>><?php esc_html_e('视频封面', 'aksrt'); ?></option>
<?php endif; ?>
</select>
</div>
<div class="aksrt-forum-compose__cover-upload" id="compose-cover-image-field" style="display:none;">
<input type="hidden" name="cover_image" id="compose-cover-image" value="<?php echo esc_attr($draft_data['cover_image'] ?? ''); ?>">
<div class="aksrt-forum-compose__cover-preview" id="compose-cover-preview">
<?php if (!empty($draft_data['cover_image'])) : ?>
<img src="<?php echo esc_url($draft_data['cover_image']); ?>" alt="">
<?php endif; ?>
</div>
<button type="button" class="aksrt-forum-compose__cover-btn" id="compose-cover-upload-btn"><?php esc_html_e('选择图片', 'aksrt'); ?></button>
</div>
<div class="aksrt-forum-compose__cover-upload" id="compose-cover-slide-field" style="display:none;">
<input type="hidden" name="featured_slide" id="compose-featured-slide"
value="<?php echo esc_attr(!empty($draft_data['featured_slide']) ? wp_json_encode($draft_data['featured_slide']) : ''); ?>">
<div class="aksrt-forum-compose__cover-preview" id="compose-slide-preview">
<?php if (!empty($draft_data['featured_slide'])) : foreach ($draft_data['featured_slide'] as $su) : ?>
<img src="<?php echo esc_url($su); ?>" alt="">
<?php endforeach; endif; ?>
</div>
<button type="button" class="aksrt-forum-compose__cover-btn" id="compose-slide-upload-btn"><?php esc_html_e('选择图片', 'aksrt'); ?></button>
</div>
<div class="aksrt-forum-compose__cover-upload" id="compose-cover-video-field" style="display:none;">
<input type="url" name="featured_video" id="compose-featured-video"
value="<?php echo esc_attr($draft_data['featured_video'] ?? ''); ?>"
placeholder="<?php esc_attr_e('https://...mp4', 'aksrt'); ?>">
</div>
</div>
</div>
<?php endif; ?>
<!-- Panel: Vote -->
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title">
<label class="aksrt-forum-compose__checkbox">
<input type="checkbox" id="compose-vote-enabled" name="vote_enabled" value="1"
<?php checked(!empty($draft_data['vote_enabled'])); ?>>
<span><i class="fas fa-poll"></i> <?php esc_html_e('投票', 'aksrt'); ?></span>
</label>
</h3>
<div class="aksrt-forum-compose__panel-body aksrt-forum-compose__vote-fields" <?php echo empty($draft_data['vote_enabled']) ? 'hidden' : ''; ?>>
<div class="aksrt-forum-compose__field">
<label for="compose-vote-title"><?php esc_html_e('投票标题', 'aksrt'); ?></label>
<input type="text" id="compose-vote-title" name="vote_title" maxlength="120"
value="<?php echo esc_attr($draft_data['vote_title'] ?? ''); ?>"
placeholder="<?php esc_attr_e('你更希望改造哪个模块?', 'aksrt'); ?>">
</div>
<div class="aksrt-forum-compose__field">
<label class="aksrt-forum-compose__checkbox">
<input type="checkbox" id="compose-vote-multiple" name="vote_multiple" value="1"
<?php checked(!empty($draft_data['vote_multiple'])); ?>>
<span><?php esc_html_e('允许多选', 'aksrt'); ?></span>
</label>
</div>
<div class="aksrt-forum-compose__field">
<label for="compose-vote-type"><?php esc_html_e('投票类型', 'aksrt'); ?></label>
<select id="compose-vote-type" name="vote_type">
<option value="standard" <?php selected($draft_data['vote_type'] ?? '', 'standard'); ?>><?php esc_html_e('标准投票', 'aksrt'); ?></option>
<option value="pk" <?php selected($draft_data['vote_type'] ?? '', 'pk'); ?>><?php esc_html_e('PK 双选', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-forum-compose__field">
<label for="compose-vote-expiry"><?php esc_html_e('投票有效期', 'aksrt'); ?></label>
<select id="compose-vote-expiry" name="vote_expiry_days">
<option value="0" <?php selected((int) ($draft_data['vote_expiry_days'] ?? 0), 0); ?>><?php esc_html_e('永久', 'aksrt'); ?></option>
<option value="1" <?php selected((int) ($draft_data['vote_expiry_days'] ?? 0), 1); ?>><?php esc_html_e('1 天', 'aksrt'); ?></option>
<option value="7" <?php selected((int) ($draft_data['vote_expiry_days'] ?? 0), 7); ?>><?php esc_html_e('7 天', 'aksrt'); ?></option>
<option value="30" <?php selected((int) ($draft_data['vote_expiry_days'] ?? 0), 30); ?>><?php esc_html_e('30 天', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-forum-compose__field">
<label for="compose-vote-options"><?php printf(esc_html__('选项(最多 %d 项)', 'aksrt'), (int) $vote_max); ?></label>
<textarea id="compose-vote-options" name="vote_options" rows="4"
placeholder="<?php esc_attr_e("每行一个选项", 'aksrt'); ?>"><?php echo esc_textarea($draft_data['vote_options'] ?? ''); ?></textarea>
</div>
</div>
</div>
</div><!-- /.aksrt-forum-compose__sidebar -->
</form>
</div>
</main>
<?php get_footer(); ?>
+362
View File
@@ -0,0 +1,362 @@
<?php
/**
* 论坛帖子编辑模板
*
* §7.4 — Full-field AJAX editing with wp_editor, matching compose capabilities.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!is_user_logged_in()) {
wp_safe_redirect(wp_login_url(get_permalink()));
exit;
}
$topic_id = get_the_ID();
$user_id = get_current_user_id();
$forum = class_exists('AKSRT_Forum') ? AKSRT_Forum::get_instance() : null;
if (!$forum || !$forum->can_edit_topic($user_id, $topic_id)) {
wp_safe_redirect(get_permalink($topic_id));
exit;
}
$topic = get_post($topic_id);
// Board
$board_id = 0;
$board_name = '';
if ($forum->is_plate_cpt_active()) {
$board_id = (int) get_post_meta($topic_id, '_aksrt_plate_id', true);
if ($board_id > 0) {
$board_name = get_the_title($board_id);
}
} else {
$terms = wp_get_post_terms($topic_id, 'aksrt_forum_board', ['fields' => 'all']);
if (!is_wp_error($terms) && !empty($terms)) {
$board_id = (int) $terms[0]->term_id;
$board_name = (string) $terms[0]->name;
}
}
// Tags
$tags = wp_get_post_terms($topic_id, 'aksrt_forum_tag', ['fields' => 'names']);
$tag_str = !is_wp_error($tags) && !empty($tags) ? implode(', ', $tags) : '';
// Topic category
$topic_cat = '';
if (taxonomy_exists('aksrt_forum_topic_cat')) {
$cats = wp_get_post_terms($topic_id, 'aksrt_forum_topic_cat', ['fields' => 'names']);
if (!is_wp_error($cats) && !empty($cats)) {
$topic_cat = $cats[0];
}
}
// Post meta
$visibility = get_post_meta($topic_id, '_aksrt_visibility', true) ?: 'public';
$bbs_type = get_post_meta($topic_id, '_aksrt_bbs_type', true) ?: 'standard';
$special_topic = get_post_meta($topic_id, '_aksrt_special_topic', true) ?: '';
$cover_type = get_post_meta($topic_id, '_aksrt_cover_type', true) ?: '';
$cover_image = get_post_meta($topic_id, '_aksrt_cover_image', true) ?: '';
$featured_slide = get_post_meta($topic_id, '_aksrt_featured_slide', true) ?: [];
$featured_video = get_post_meta($topic_id, '_aksrt_featured_video', true) ?: '';
$credits_cost = (int) get_post_meta($topic_id, '_aksrt_credits_cost', true);
$pay_price = get_post_meta($topic_id, '_aksrt_pay_price', true) ?: '';
$visibility_roles = get_post_meta($topic_id, '_aksrt_visibility_roles', true) ?: [];
$vote_enabled = get_post_meta($topic_id, '_aksrt_vote_enabled', true) === '1';
$vote_title = get_post_meta($topic_id, '_aksrt_vote_title', true) ?: '';
$vote_multiple = get_post_meta($topic_id, '_aksrt_vote_multiple', true) === '1';
$vote_type = get_post_meta($topic_id, '_aksrt_vote_type', true) ?: 'standard';
$vote_expiry_days = (int) get_post_meta($topic_id, '_aksrt_vote_expiry_days', true);
$vote_options_raw = get_post_meta($topic_id, '_aksrt_vote_options', true) ?: [];
$vote_options_text = is_array($vote_options_raw) ? implode("\n", array_column($vote_options_raw, 'label')) : '';
// Settings & capabilities
$forum_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_forum_settings() : [];
$title_maxlen = max(5, min(100, (int) ($forum_settings['other']['title_strlen_limit'] ?? 30)));
$vote_max = (int) ($forum_settings['other']['vote_max'] ?? 8);
$caps = $forum->get_compose_capabilities($user_id);
$visibility_options = $forum->get_visibility_options();
$nonce = wp_create_nonce('aksrt_forum_compose');
get_header();
?>
<main id="main-content" class="site-main container" tabindex="-1">
<div class="aksrt-forum-compose container">
<div class="aksrt-forum-compose__header">
<div>
<h1><i class="fas fa-edit"></i> <?php esc_html_e('编辑帖子', 'aksrt'); ?></h1>
</div>
<a href="<?php echo esc_url(get_permalink($topic_id)); ?>" class="aksrt-forum-compose__back">
<i class="fas fa-arrow-left"></i> <?php esc_html_e('返回帖子', 'aksrt'); ?>
</a>
</div>
<form
id="aksrt-forum-compose-form"
class="aksrt-forum-compose__form aksrt-forum-compose__layout js-aksrt-forum-edit-form"
data-ajax="<?php echo esc_url(admin_url('admin-ajax.php')); ?>"
data-nonce="<?php echo esc_attr($nonce); ?>"
data-topic-id="<?php echo esc_attr((string) $topic_id); ?>"
data-vote-max="<?php echo esc_attr((string) $vote_max); ?>"
>
<!-- ═══ Main content area ═══ -->
<div class="aksrt-forum-compose__main">
<div class="aksrt-forum-compose__field">
<label for="compose-title"><?php esc_html_e('主题标题', 'aksrt'); ?> <span class="required">*</span></label>
<input type="text" id="compose-title" name="title" required
maxlength="<?php echo esc_attr((string) $title_maxlen); ?>"
value="<?php echo esc_attr($topic->post_title); ?>"
data-aksrt-maxlength="<?php echo esc_attr((string) $title_maxlen); ?>">
<span class="aksrt-forum-compose__char-counter" id="compose-title-counter">
<span class="js-aksrt-counter-current"><?php echo esc_html((string) mb_strlen($topic->post_title)); ?></span> / <?php echo esc_html((string) $title_maxlen); ?>
</span>
</div>
<div class="aksrt-forum-compose__field aksrt-forum-compose__editor-wrap">
<label><?php esc_html_e('主题内容', 'aksrt'); ?> <span class="required">*</span></label>
<?php
wp_editor($topic->post_content, 'compose-content', [
'textarea_name' => 'content',
'textarea_rows' => 18,
'media_buttons' => !empty($caps['can_upload_image']) || !empty($caps['can_upload_video']) || !empty($caps['can_upload_file']),
'teeny' => false,
'quicktags' => true,
'tinymce' => [
'toolbar1' => 'formatselect,bold,italic,underline,strikethrough,blockquote,bullist,numlist,alignleft,aligncenter,alignright,link,unlink,wp_more,fullscreen',
'toolbar2' => 'pastetext,removeformat,charmap,outdent,indent,undo,redo,wp_help',
'plugins' => 'paste,link,lists,fullscreen,wordpress,wplink,charmap',
],
]);
?>
</div>
</div>
<!-- ═══ Sidebar panels ═══ -->
<div class="aksrt-forum-compose__sidebar">
<!-- Panel: Submit -->
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-save"></i> <?php esc_html_e('保存', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<div class="aksrt-forum-compose__actions">
<button type="submit" class="aksrt-forum-compose__submit" id="compose-submit">
<i class="fas fa-save"></i> <?php esc_html_e('保存修改', 'aksrt'); ?>
</button>
<a href="<?php echo esc_url(get_permalink($topic_id)); ?>" class="aksrt-forum-compose__draft">
<?php esc_html_e('取消', 'aksrt'); ?>
</a>
</div>
<span class="aksrt-forum-compose__feedback js-aksrt-action-feedback" aria-live="polite"></span>
</div>
</div>
<!-- Panel: Board (read-only in edit mode) -->
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-th-large"></i> <?php esc_html_e('版块', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<p class="aksrt-forum-compose__readonly"><?php echo esc_html($board_name ?: __('未分配', 'aksrt')); ?></p>
</div>
</div>
<!-- Panel: Type & Classification -->
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-tag"></i> <?php esc_html_e('类型 & 分类', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<div class="aksrt-forum-compose__field">
<label for="compose-bbs-type"><?php esc_html_e('帖子类型', 'aksrt'); ?></label>
<select id="compose-bbs-type" name="bbs_type">
<option value="standard" <?php selected($bbs_type, 'standard'); ?>><?php esc_html_e('标准帖子', 'aksrt'); ?></option>
<option value="question" <?php selected($bbs_type, 'question'); ?>><?php esc_html_e('提问', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-forum-compose__field">
<label for="compose-special"><?php esc_html_e('专题/系列', 'aksrt'); ?></label>
<input type="text" id="compose-special" name="special_topic" maxlength="80"
value="<?php echo esc_attr($special_topic); ?>">
</div>
<?php if (taxonomy_exists('aksrt_forum_topic_cat')) : ?>
<div class="aksrt-forum-compose__field aksrt-remote-select" data-search-action="aksrt_forum_search_topics">
<label><?php esc_html_e('话题', 'aksrt'); ?></label>
<input type="hidden" name="topic_cat" value="<?php echo esc_attr($topic_cat); ?>">
<input type="text" class="aksrt-remote-select__input js-aksrt-remote-input"
placeholder="<?php esc_attr_e('搜索话题…', 'aksrt'); ?>"
autocomplete="off"
value="<?php echo esc_attr($topic_cat); ?>">
<div class="aksrt-remote-select__dropdown" hidden></div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Panel: Tags -->
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-tags"></i> <?php esc_html_e('标签', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<div class="aksrt-forum-compose__field aksrt-tag-input" data-search-action="aksrt_forum_search_tags" data-max="5">
<input type="hidden" id="compose-tags" name="tags" value="<?php echo esc_attr($tag_str); ?>">
<div class="aksrt-tag-input__tokens" id="compose-tag-tokens"></div>
<input type="text" class="aksrt-tag-input__input js-aksrt-tag-search"
placeholder="<?php esc_attr_e('输入标签名,回车添加', 'aksrt'); ?>"
autocomplete="off" maxlength="30">
<div class="aksrt-tag-input__dropdown" hidden></div>
</div>
</div>
</div>
<!-- Panel: Visibility -->
<?php if (!empty($caps['can_set_visibility'])) : ?>
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-eye"></i> <?php esc_html_e('可见性', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<div class="aksrt-forum-compose__field">
<select id="compose-visibility" name="visibility">
<?php foreach ($visibility_options as $option) : ?>
<option value="<?php echo esc_attr((string) ($option['value'] ?? '')); ?>"
<?php selected($visibility, $option['value'] ?? ''); ?>>
<?php echo esc_html((string) ($option['label'] ?? '')); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="aksrt-forum-compose__visibility-extra" id="compose-credits-cost-field" style="display:none;">
<label for="compose-credits-cost"><?php esc_html_e('积分价格', 'aksrt'); ?></label>
<input type="number" id="compose-credits-cost" name="credits_cost" min="1" max="99999"
value="<?php echo esc_attr((string) $credits_cost); ?>">
</div>
<div class="aksrt-forum-compose__visibility-extra" id="compose-pay-price-field" style="display:none;">
<label for="compose-pay-price"><?php esc_html_e('付费价格(¥)', 'aksrt'); ?></label>
<input type="number" id="compose-pay-price" name="pay_price" min="0.01" step="0.01" max="99999"
value="<?php echo esc_attr($pay_price); ?>">
</div>
<div class="aksrt-forum-compose__visibility-extra" id="compose-role-field" style="display:none;">
<label for="compose-visibility-roles"><?php esc_html_e('允许的角色', 'aksrt'); ?></label>
<select id="compose-visibility-roles" name="visibility_roles[]" multiple>
<?php foreach (wp_roles()->get_names() as $role_slug => $role_name) : ?>
<option value="<?php echo esc_attr($role_slug); ?>"
<?php echo in_array($role_slug, $visibility_roles, true) ? 'selected' : ''; ?>>
<?php echo esc_html(translate_user_role($role_name)); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<?php endif; ?>
<!-- Panel: Cover -->
<?php if (!empty($caps['can_set_cover'])) : ?>
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title"><i class="fas fa-image"></i> <?php esc_html_e('封面', 'aksrt'); ?></h3>
<div class="aksrt-forum-compose__panel-body">
<div class="aksrt-forum-compose__field">
<select id="compose-cover-type" name="cover_type">
<option value="" <?php selected($cover_type, ''); ?>><?php esc_html_e('无封面', 'aksrt'); ?></option>
<?php if (!empty($caps['can_set_cover_image'])) : ?>
<option value="image" <?php selected($cover_type, 'image'); ?>><?php esc_html_e('图片封面', 'aksrt'); ?></option>
<?php endif; ?>
<?php if (!empty($caps['can_set_cover_slide'])) : ?>
<option value="slide" <?php selected($cover_type, 'slide'); ?>><?php esc_html_e('幻灯片封面', 'aksrt'); ?></option>
<?php endif; ?>
<?php if (!empty($caps['can_set_cover_video'])) : ?>
<option value="video" <?php selected($cover_type, 'video'); ?>><?php esc_html_e('视频封面', 'aksrt'); ?></option>
<?php endif; ?>
</select>
</div>
<div class="aksrt-forum-compose__cover-upload" id="compose-cover-image-field" style="display:none;">
<input type="hidden" name="cover_image" id="compose-cover-image" value="<?php echo esc_attr($cover_image); ?>">
<div class="aksrt-forum-compose__cover-preview" id="compose-cover-preview">
<?php if ($cover_image) : ?>
<img src="<?php echo esc_url($cover_image); ?>" alt="">
<?php endif; ?>
</div>
<button type="button" class="aksrt-forum-compose__cover-btn" id="compose-cover-upload-btn"><?php esc_html_e('选择图片', 'aksrt'); ?></button>
</div>
<div class="aksrt-forum-compose__cover-upload" id="compose-cover-slide-field" style="display:none;">
<input type="hidden" name="featured_slide" id="compose-featured-slide"
value="<?php echo esc_attr(is_array($featured_slide) && !empty($featured_slide) ? wp_json_encode($featured_slide) : ''); ?>">
<div class="aksrt-forum-compose__cover-preview" id="compose-slide-preview">
<?php if (is_array($featured_slide)) : foreach ($featured_slide as $su) : ?>
<img src="<?php echo esc_url($su); ?>" alt="">
<?php endforeach; endif; ?>
</div>
<button type="button" class="aksrt-forum-compose__cover-btn" id="compose-slide-upload-btn"><?php esc_html_e('选择图片', 'aksrt'); ?></button>
</div>
<div class="aksrt-forum-compose__cover-upload" id="compose-cover-video-field" style="display:none;">
<input type="url" name="featured_video" id="compose-featured-video"
value="<?php echo esc_attr($featured_video); ?>"
placeholder="<?php esc_attr_e('https://...mp4', 'aksrt'); ?>">
</div>
</div>
</div>
<?php endif; ?>
<!-- Panel: Vote -->
<?php if (!empty($caps['can_set_vote'])) : ?>
<div class="aksrt-forum-compose__panel">
<h3 class="aksrt-forum-compose__panel-title">
<label class="aksrt-forum-compose__checkbox">
<input type="checkbox" id="compose-vote-enabled" name="vote_enabled" value="1"
<?php checked($vote_enabled); ?>>
<span><i class="fas fa-poll"></i> <?php esc_html_e('投票', 'aksrt'); ?></span>
</label>
</h3>
<div class="aksrt-forum-compose__panel-body aksrt-forum-compose__vote-fields" <?php echo !$vote_enabled ? 'hidden' : ''; ?>>
<div class="aksrt-forum-compose__field">
<label for="compose-vote-title"><?php esc_html_e('投票标题', 'aksrt'); ?></label>
<input type="text" id="compose-vote-title" name="vote_title" maxlength="120"
value="<?php echo esc_attr($vote_title); ?>">
</div>
<div class="aksrt-forum-compose__field">
<label class="aksrt-forum-compose__checkbox">
<input type="checkbox" id="compose-vote-multiple" name="vote_multiple" value="1"
<?php checked($vote_multiple); ?>>
<span><?php esc_html_e('允许多选', 'aksrt'); ?></span>
</label>
</div>
<div class="aksrt-forum-compose__field">
<label for="compose-vote-type"><?php esc_html_e('投票类型', 'aksrt'); ?></label>
<select id="compose-vote-type" name="vote_type">
<option value="standard" <?php selected($vote_type, 'standard'); ?>><?php esc_html_e('标准投票', 'aksrt'); ?></option>
<option value="pk" <?php selected($vote_type, 'pk'); ?>><?php esc_html_e('PK 双选', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-forum-compose__field">
<label for="compose-vote-expiry"><?php esc_html_e('投票有效期', 'aksrt'); ?></label>
<select id="compose-vote-expiry" name="vote_expiry_days">
<option value="0" <?php selected($vote_expiry_days, 0); ?>><?php esc_html_e('永久', 'aksrt'); ?></option>
<option value="1" <?php selected($vote_expiry_days, 1); ?>><?php esc_html_e('1 天', 'aksrt'); ?></option>
<option value="7" <?php selected($vote_expiry_days, 7); ?>><?php esc_html_e('7 天', 'aksrt'); ?></option>
<option value="30" <?php selected($vote_expiry_days, 30); ?>><?php esc_html_e('30 天', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-forum-compose__field">
<label for="compose-vote-options"><?php printf(esc_html__('选项(最多 %d 项)', 'aksrt'), (int) $vote_max); ?></label>
<textarea id="compose-vote-options" name="vote_options" rows="4"><?php echo esc_textarea($vote_options_text); ?></textarea>
</div>
</div>
</div>
<?php endif; ?>
</div><!-- /.aksrt-forum-compose__sidebar -->
</form>
</div>
</main>
<?php get_footer(); ?>
+107
View File
@@ -0,0 +1,107 @@
<?php
/**
* Plate category (分区) archive template.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$forum = class_exists('AKSRT_Forum') ? AKSRT_Forum::get_instance() : null;
$term = get_queried_object();
$cat_id = $term ? (int) $term->term_id : 0;
$forum_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_forum_settings() : [];
$forum_names = $forum_settings['names'] ?? [];
$forum_name = $forum_names['forum'] ?? __('论坛', 'aksrt');
$plates = [];
if ($forum && $cat_id > 0) {
$plates = $forum->get_sorted_plates('menu_order', 'ASC', 100);
$plates = array_filter($plates, static function ($plate) use ($cat_id): bool {
return isset($plate['plate_cat']['id']) && (int) $plate['plate_cat']['id'] === $cat_id;
});
$plates = array_values($plates);
}
$cat_moderator_ids = get_term_meta($cat_id, '_aksrt_cat_moderator', true);
$cat_moderators = [];
if (is_array($cat_moderator_ids)) {
foreach ($cat_moderator_ids as $uid) {
$user = get_userdata((int) $uid);
if ($user) {
$cat_moderators[] = [
'id' => (int) $user->ID,
'name' => $user->display_name,
'avatar' => function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user->ID, 36) : get_avatar_url($user->ID, ['size' => 36]),
];
}
}
}
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<?php if (function_exists('aksrt_breadcrumbs')) { aksrt_breadcrumbs(); } ?>
<div class="aksrt-forum-plate-cat container">
<div class="aksrt-forum-plate-cat__header">
<h1><?php echo esc_html($term ? $term->name : ''); ?></h1>
<?php if ($term && $term->description) : ?>
<p><?php echo esc_html($term->description); ?></p>
<?php endif; ?>
<?php if (!empty($cat_moderators)) : ?>
<div class="aksrt-forum-plate-cat__moderators">
<span><?php esc_html_e('分区版主:', 'aksrt'); ?></span>
<?php foreach ($cat_moderators as $mod) : ?>
<span class="aksrt-forum-plate-cat__moderator">
<?php if (!empty($mod['avatar'])) : ?>
<img src="<?php echo esc_url($mod['avatar']); ?>" alt="" width="24" height="24">
<?php endif; ?>
<?php echo esc_html($mod['name']); ?>
</span>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<div class="aksrt-forum-plate-cat__plates">
<?php if (!empty($plates)) : ?>
<?php foreach ($plates as $plate) : ?>
<a href="<?php echo esc_url((string) ($plate['url'] ?? '#')); ?>" class="aksrt-forum-plate-cat__plate-card">
<?php if (!empty($plate['cover_url'])) : ?>
<img class="aksrt-forum-plate-cat__plate-cover" src="<?php echo esc_url($plate['cover_url']); ?>" alt="" loading="lazy">
<?php elseif (!empty($plate['icon'])) : ?>
<div class="aksrt-forum-plate-cat__plate-icon"><i class="<?php echo esc_attr($plate['icon']); ?>"></i></div>
<?php endif; ?>
<div class="aksrt-forum-plate-cat__plate-info">
<h3><?php echo esc_html((string) ($plate['name'] ?? '')); ?></h3>
<p><?php echo esc_html((string) ($plate['description'] ?? '')); ?></p>
<div class="aksrt-forum-plate-cat__plate-meta">
<span><?php printf(esc_html__('%d 主题', 'aksrt'), (int) ($plate['post_count'] ?? 0)); ?></span>
<span><?php printf(esc_html__('%d 关注', 'aksrt'), (int) ($plate['follow_count'] ?? 0)); ?></span>
<?php if (!empty($plate['is_hot'])) : ?>
<span class="aksrt-forum-plate-cat__hot-badge"><?php esc_html_e('热门', 'aksrt'); ?></span>
<?php endif; ?>
</div>
</div>
</a>
<?php endforeach; ?>
<?php else : ?>
<div class="aksrt-forum-plate-cat__empty">
<p><?php esc_html_e('该分区暂无版块。', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
<div class="aksrt-forum-plate-cat__back">
<a href="<?php echo esc_url(home_url('/forums/')); ?>">
<i class="fas fa-arrow-left"></i> <?php printf(esc_html__('返回%s', 'aksrt'), esc_html($forum_name)); ?>
</a>
</div>
</div>
</main>
<?php get_footer(); ?>
+312
View File
@@ -0,0 +1,312 @@
<?php
/**
* Plate CPT single template (§7.2 板块独立页面).
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$forum = class_exists('AKSRT_Forum') ? AKSRT_Forum::get_instance() : null;
$current_user_id = get_current_user_id();
$plate_id = get_queried_object_id();
$paged = max(1, (int) get_query_var('paged', 1));
$view = $forum ? $forum->get_board_view($plate_id, [
'user_id' => $current_user_id,
'paged' => $paged,
]) : [
'board' => null,
'topics' => [],
'sticky_topics' => [],
'moderators' => [],
'pagination' => ['page' => 1, 'max_pages' => 1, 'total' => 0],
'permissions' => [],
'application' => null,
'board_options' => [],
];
$board_data = $view['board'] ?? null;
$topics = $view['topics'] ?? [];
$sticky_topics = $view['sticky_topics'] ?? [];
$moderators = $view['moderators'] ?? [];
$permissions = $view['permissions'] ?? [];
$application = $view['application'] ?? null;
$compose_url = add_query_arg('board', $plate_id, home_url('/compose/'));
$forum_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_forum_settings() : [];
$forum_names = $forum_settings['names'] ?? [];
$name_topic = $forum_names['posts'] ?? __('主题', 'aksrt');
$name_reply = $forum_names['comment'] ?? __('回复', 'aksrt');
$is_followed = $forum && $current_user_id > 0 ? $forum->is_user_following_plate($current_user_id, $plate_id) : false;
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<?php if (function_exists('aksrt_breadcrumbs')) { aksrt_breadcrumbs(); } ?>
<?php if (!empty($permissions['access_denied'])) : ?>
<div class="aksrt-forum-board container">
<div class="aksrt-forum-board__access-denied">
<i class="fas fa-lock"></i>
<h2><?php esc_html_e('访问受限', 'aksrt'); ?></h2>
<p><?php
$reason = $permissions['access_reason'] ?? 'login';
if ($reason === 'login') {
esc_html_e('请登录后访问此版块。', 'aksrt');
} elseif ($reason === 'follow') {
esc_html_e('请先关注此版块后才能浏览内容。', 'aksrt');
} elseif ($reason === 'role') {
esc_html_e('你当前的角色无权访问此版块。', 'aksrt');
}
?></p>
</div>
</div>
<?php else : ?>
<div class="aksrt-forum-board container">
<div class="aksrt-forum-board__header">
<div class="aksrt-forum-board__info">
<?php if (!empty($board_data['cover_url'])) : ?>
<img class="aksrt-forum-board__cover" src="<?php echo esc_url((string) $board_data['cover_url']); ?>" alt="" loading="lazy">
<?php endif; ?>
<h1 class="aksrt-forum-board__title"><?php echo esc_html((string) ($board_data['name'] ?? '')); ?></h1>
<?php if (!empty($board_data['description'])) : ?>
<p class="aksrt-forum-board__desc"><?php echo esc_html((string) $board_data['description']); ?></p>
<?php endif; ?>
<div class="aksrt-forum-board__meta">
<span><i class="fas fa-list-alt"></i> <?php printf(esc_html__('%1$d 个%2$s', 'aksrt'), (int) ($board_data['post_count'] ?? $board_data['topic_count'] ?? 0), esc_html($name_topic)); ?></span>
<span><i class="fas fa-comments"></i> <?php printf(esc_html__('%d 条%s', 'aksrt'), (int) ($board_data['reply_count'] ?? 0), esc_html($name_reply)); ?></span>
<span><i class="fas fa-eye"></i> <?php printf(esc_html__('%d 浏览', 'aksrt'), (int) ($board_data['views'] ?? 0)); ?></span>
<span><i class="fas fa-heart"></i> <?php printf(esc_html__('%d 关注', 'aksrt'), (int) ($board_data['follow_count'] ?? 0)); ?></span>
<span><i class="fas fa-shield-alt"></i> <?php printf(esc_html__('%d 位版主', 'aksrt'), count($moderators)); ?></span>
</div>
</div>
<div class="aksrt-forum-board__actions">
<?php if ($current_user_id > 0) : ?>
<button type="button"
class="aksrt-forum-board__follow-btn js-aksrt-follow-plate"
data-plate-id="<?php echo esc_attr((string) $plate_id); ?>"
data-followed="<?php echo $is_followed ? '1' : '0'; ?>">
<i class="fas <?php echo $is_followed ? 'fa-heart' : 'fa-heart'; ?>"></i>
<?php echo $is_followed ? esc_html__('已关注', 'aksrt') : esc_html__('关注', 'aksrt'); ?>
</button>
<?php endif; ?>
<?php if (!empty($permissions['can_compose'])) : ?>
<a href="<?php echo esc_url($compose_url); ?>" class="aksrt-forum-board__compose-btn">
<i class="fas fa-pen"></i>
<?php esc_html_e('发布新主题', 'aksrt'); ?>
</a>
<?php endif; ?>
<?php
$is_forum_admin = $forum && $forum->is_forum_admin($current_user_id);
$is_plate_author = $board_data && (int) ($board_data['author_id'] ?? 0) === $current_user_id;
?>
<?php if ($is_forum_admin || $is_plate_author) : ?>
<button type="button" class="aksrt-forum-board__manage-btn js-aksrt-edit-plate"
data-plate-id="<?php echo esc_attr((string) $plate_id); ?>"
data-name="<?php echo esc_attr((string) ($board_data['name'] ?? '')); ?>"
data-description="<?php echo esc_attr((string) ($board_data['description'] ?? '')); ?>"
data-icon="<?php echo esc_attr((string) ($board_data['icon'] ?? '')); ?>"
data-cover="<?php echo esc_attr((string) ($board_data['cover_url'] ?? '')); ?>">
<i class="fas fa-cog"></i> <?php esc_html_e('编辑版块', 'aksrt'); ?>
</button>
<?php endif; ?>
<?php if ($is_forum_admin) : ?>
<button type="button" class="aksrt-forum-board__manage-btn aksrt-forum-board__manage-btn--danger js-aksrt-delete-plate"
data-plate-id="<?php echo esc_attr((string) $plate_id); ?>"
data-topic-count="<?php echo esc_attr((string) ($board_data['post_count'] ?? $board_data['topic_count'] ?? 0)); ?>">
<i class="fas fa-trash-alt"></i> <?php esc_html_e('删除版块', 'aksrt'); ?>
</button>
<?php endif; ?>
</div>
</div>
<?php if (!empty($board_data['content'])) : ?>
<div class="aksrt-forum-board__content entry-content">
<?php echo wp_kses_post((string) $board_data['content']); ?>
</div>
<?php endif; ?>
<section class="aksrt-forum-board__governance">
<div class="aksrt-forum-board__governance-card">
<h2 class="aksrt-forum-board__section-title"><?php esc_html_e('版主团队', 'aksrt'); ?></h2>
<?php if (!empty($moderators)) : ?>
<div class="aksrt-forum-board__moderators">
<?php foreach ($moderators as $moderator) : ?>
<div class="aksrt-forum-board__moderator">
<?php if (!empty($moderator['avatar'])) : ?>
<img src="<?php echo esc_url((string) $moderator['avatar']); ?>" alt="" width="36" height="36">
<?php endif; ?>
<span><?php echo esc_html((string) ($moderator['name'] ?? $moderator['display_name'] ?? '')); ?></span>
<?php if (!empty($moderator['role'])) : ?>
<small class="aksrt-forum-board__moderator-role"><?php echo esc_html((string) $moderator['role']); ?></small>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php else : ?>
<p><?php esc_html_e('当前还没有设置版主。', 'aksrt'); ?></p>
<?php endif; ?>
</div>
<div class="aksrt-forum-board__governance-card">
<h2 class="aksrt-forum-board__section-title"><?php esc_html_e('版主申请', 'aksrt'); ?></h2>
<?php if (!is_user_logged_in()) : ?>
<p class="aksrt-forum-board__application-note"><?php esc_html_e('登录后可申请协助管理本版块。', 'aksrt'); ?></p>
<a href="<?php echo esc_url(wp_login_url(get_permalink())); ?>" class="aksrt-forum-board__apply-btn"><?php esc_html_e('登录后申请', 'aksrt'); ?></a>
<?php elseif (!empty($application)) : ?>
<p class="aksrt-forum-board__application-note">
<?php printf(
esc_html__('你最近一次申请状态:%s', 'aksrt'),
esc_html((string) ($application['status_label'] ?? __('待处理', 'aksrt')))
); ?>
</p>
<?php if (!empty($application['reason'])) : ?>
<blockquote class="aksrt-forum-board__application-reason"><?php echo esc_html((string) $application['reason']); ?></blockquote>
<?php endif; ?>
<?php if (!empty($application['review_note'])) : ?>
<p class="aksrt-forum-board__application-note"><?php printf(esc_html__('审核备注:%s', 'aksrt'), esc_html((string) $application['review_note'])); ?></p>
<?php endif; ?>
<?php if (($application['status'] ?? '') !== 'pending' && !empty($permissions['can_apply_moderator'])) : ?>
<form class="aksrt-forum-board__application-form js-aksrt-moderator-apply-form" data-board-id="<?php echo esc_attr((string) $plate_id); ?>">
<label for="aksrt-plate-apply-reason"><?php esc_html_e('重新申请理由', 'aksrt'); ?></label>
<textarea id="aksrt-plate-apply-reason" name="reason" rows="4" placeholder="<?php esc_attr_e('请简要说明你的社区经验、擅长领域或管理计划。', 'aksrt'); ?>"></textarea>
<div class="aksrt-forum-board__application-actions">
<button type="submit" class="aksrt-forum-board__apply-btn"><?php esc_html_e('重新提交申请', 'aksrt'); ?></button>
<span class="aksrt-forum-board__application-feedback js-aksrt-action-feedback" aria-live="polite"></span>
</div>
</form>
<?php endif; ?>
<?php elseif (!empty($permissions['can_apply_moderator'])) : ?>
<p class="aksrt-forum-board__application-note"><?php esc_html_e('如果你熟悉本版块内容并愿意协助维护秩序,可以提交版主申请。', 'aksrt'); ?></p>
<form class="aksrt-forum-board__application-form js-aksrt-moderator-apply-form" data-board-id="<?php echo esc_attr((string) $plate_id); ?>">
<label for="aksrt-plate-apply-reason"><?php esc_html_e('申请理由', 'aksrt'); ?></label>
<textarea id="aksrt-plate-apply-reason" name="reason" rows="4" placeholder="<?php esc_attr_e('请简要说明你的社区经验、擅长领域或管理计划。', 'aksrt'); ?>"></textarea>
<div class="aksrt-forum-board__application-actions">
<button type="submit" class="aksrt-forum-board__apply-btn"><?php esc_html_e('提交版主申请', 'aksrt'); ?></button>
<span class="aksrt-forum-board__application-feedback js-aksrt-action-feedback" aria-live="polite"></span>
</div>
</form>
<?php else : ?>
<p class="aksrt-forum-board__application-note"><?php esc_html_e('你当前已是本版块管理成员,或暂不满足申请条件。', 'aksrt'); ?></p>
<?php endif; ?>
</div>
</section>
<?php if (!empty($sticky_topics)) : ?>
<div class="aksrt-forum-board__sticky">
<h3><i class="fas fa-thumbtack"></i> <?php esc_html_e('置顶主题', 'aksrt'); ?></h3>
<?php foreach ($sticky_topics as $topic) : ?>
<a href="<?php echo esc_url((string) ($topic['permalink'] ?? '#')); ?>" class="aksrt-forum-board__sticky-item">
<?php
$sticky_label = (string) ($topic['sticky_label'] ?? __('置顶', 'aksrt'));
?>
<span class="aksrt-forum-board__sticky-badge"><?php echo esc_html($sticky_label); ?></span>
<span class="aksrt-forum-board__sticky-title"><?php echo esc_html((string) ($topic['title'] ?? '')); ?></span>
<span class="aksrt-forum-board__sticky-meta">
<?php echo esc_html((string) ($topic['author']['display_name'] ?? '')); ?>
· <?php echo esc_html((string) ($topic['date'] ?? '')); ?>
</span>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="aksrt-forum-board__topics">
<?php if (!empty($topics)) : ?>
<?php foreach ($topics as $topic) : ?>
<?php
$topic_id = (int) ($topic['id'] ?? 0);
$item_sticky_level = (int) ($topic['sticky_level'] ?? 0);
$item_sticky_class = $item_sticky_level > 0
? ' is-sticky is-sticky-' . $item_sticky_level
: '';
?>
<article class="aksrt-forum-topic-item<?php echo esc_attr($item_sticky_class); ?><?php echo !empty($topic['featured']) ? ' is-featured' : ''; ?>">
<div class="aksrt-forum-topic-item__avatar">
<?php if (!empty($topic['author']['avatar_url'])) : ?>
<img src="<?php echo esc_url((string) $topic['author']['avatar_url']); ?>" alt="" width="40" height="40">
<?php endif; ?>
</div>
<div class="aksrt-forum-topic-item__body">
<div class="aksrt-forum-topic-item__title">
<?php if (!empty($topic['featured'])) : ?>
<span class="aksrt-forum-topic-item__badge aksrt-forum-topic-item__badge--featured"><?php esc_html_e('精华', 'aksrt'); ?></span>
<?php endif; ?>
<?php if (($topic['bbs_type'] ?? 'standard') === 'question') : ?>
<?php $q_status = (int) ($topic['question_status'] ?? 0); ?>
<span class="aksrt-forum-topic-item__badge aksrt-forum-topic-item__badge--question<?php echo $q_status === 1 ? ' aksrt-forum-topic-item__badge--solved' : ''; ?>">
<?php echo $q_status === 1 ? esc_html__('已解决', 'aksrt') : esc_html__('提问', 'aksrt'); ?>
</span>
<?php endif; ?>
<a href="<?php echo esc_url((string) ($topic['permalink'] ?? '#')); ?>"><?php echo esc_html((string) ($topic['title'] ?? '')); ?></a>
</div>
<div class="aksrt-forum-topic-item__meta">
<span><?php echo esc_html((string) ($topic['author']['display_name'] ?? '')); ?></span>
<span><?php echo esc_html((string) ($topic['date'] ?? '')); ?></span>
</div>
</div>
<?php if (!empty($topic['cover'])) : ?>
<div class="aksrt-forum-topic-item__cover">
<?php if ($topic['cover']['type'] === 'video') : ?>
<div class="aksrt-forum-topic-item__cover-video">
<?php if (!empty($topic['cover']['poster'])) : ?>
<img src="<?php echo esc_url((string) $topic['cover']['poster']); ?>" alt="" loading="lazy">
<?php endif; ?>
<i class="fas fa-play-circle"></i>
</div>
<?php elseif ($topic['cover']['type'] === 'slide') : ?>
<div class="aksrt-forum-topic-item__cover-slide">
<?php if (!empty($topic['cover']['images'][0])) : ?>
<img src="<?php echo esc_url((string) $topic['cover']['images'][0]); ?>" alt="" loading="lazy">
<?php endif; ?>
<span class="aksrt-forum-topic-item__cover-count"><i class="fas fa-images"></i> <?php echo count($topic['cover']['images']); ?></span>
</div>
<?php else : ?>
<img src="<?php echo esc_url((string) ($topic['cover']['url'] ?? '')); ?>" alt="" loading="lazy">
<?php endif; ?>
</div>
<?php endif; ?>
<div class="aksrt-forum-topic-item__stats">
<span title="<?php echo esc_attr($name_reply); ?>"><i class="fas fa-comment"></i> <?php echo esc_html(number_format_i18n((int) ($topic['comment_count'] ?? 0))); ?></span>
<span title="<?php esc_attr_e('浏览', 'aksrt'); ?>"><i class="fas fa-eye"></i> <?php echo esc_html(number_format_i18n((int) ($topic['views'] ?? 0))); ?></span>
</div>
</article>
<?php endforeach; ?>
<?php if (($view['pagination']['max_pages'] ?? 1) > 1) : ?>
<div class="aksrt-forum-board__pagination">
<?php
echo paginate_links([
'total' => (int) ($view['pagination']['max_pages'] ?? 1),
'current' => $paged,
'prev_text' => '<i class="fas fa-chevron-left"></i>',
'next_text' => '<i class="fas fa-chevron-right"></i>',
]);
?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="aksrt-forum-board__empty">
<i class="fas fa-comments"></i>
<p><?php esc_html_e('当前版块还没有公开主题。', 'aksrt'); ?></p>
<?php if (!empty($permissions['can_compose'])) : ?>
<a href="<?php echo esc_url($compose_url); ?>"><?php esc_html_e('发布第一个主题', 'aksrt'); ?></a>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</main>
<?php get_footer(); ?>
+92
View File
@@ -0,0 +1,92 @@
<?php
/**
* 论坛标签归档模板
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$term = get_queried_object();
$paged = max(1, (int) get_query_var('paged'));
$forum = class_exists('AKSRT_Forum') ? AKSRT_Forum::get_instance() : null;
$view = $forum ? $forum->get_tag_archive_view([
'term_id' => $term->term_id,
'paged' => $paged,
]) : ['topics' => [], 'term' => null, 'total' => 0, 'max_pages' => 0];
$topics = $view['topics'];
$total = $view['total'];
$max_pages = $view['max_pages'];
$term_name = $term->name ?? '';
$term_desc = $term->description ?? '';
?>
<main id="main-content" class="site-main container" tabindex="-1">
<div class="aksrt-forum-tag container">
<header class="aksrt-forum-tag__header">
<h1 class="aksrt-forum-tag__title">
<i class="fas fa-tag"></i>
<?php echo esc_html($term_name); ?>
</h1>
<?php if ($term_desc) : ?>
<p class="aksrt-forum-tag__desc"><?php echo esc_html($term_desc); ?></p>
<?php endif; ?>
<p class="aksrt-forum-tag__count"><?php printf(esc_html__('共 %d 个话题', 'aksrt'), $total); ?></p>
</header>
<?php if (!empty($topics)) : ?>
<div class="aksrt-forum-tag__list">
<?php foreach ($topics as $topic) : ?>
<article class="aksrt-forum-topic-card">
<div class="aksrt-forum-topic-card__body">
<h3 class="aksrt-forum-topic-card__title">
<a href="<?php echo esc_url($topic['permalink']); ?>"><?php echo esc_html($topic['title']); ?></a>
</h3>
<?php if ($topic['excerpt']) : ?>
<p class="aksrt-forum-topic-card__excerpt"><?php echo esc_html(wp_trim_words($topic['excerpt'], 30)); ?></p>
<?php endif; ?>
<div class="aksrt-forum-topic-card__meta">
<span class="aksrt-forum-topic-card__author">
<i class="fas fa-user"></i> <?php echo esc_html($topic['author']); ?>
</span>
<span class="aksrt-forum-topic-card__date">
<i class="fas fa-clock"></i> <?php echo esc_html($topic['date']); ?>
</span>
<span class="aksrt-forum-topic-card__comments">
<i class="fas fa-comment"></i> <?php echo (int) $topic['comment_count']; ?>
</span>
<span class="aksrt-forum-topic-card__views">
<i class="fas fa-eye"></i> <?php echo (int) $topic['views']; ?>
</span>
</div>
</div>
</article>
<?php endforeach; ?>
</div>
<?php if ($max_pages > 1) : ?>
<nav class="aksrt-forum-tag__pagination" aria-label="<?php esc_attr_e('话题分页', 'aksrt'); ?>">
<?php
echo paginate_links([
'total' => $max_pages,
'current' => $paged,
'prev_text' => '<i class="fas fa-chevron-left"></i>',
'next_text' => '<i class="fas fa-chevron-right"></i>',
]);
?>
</nav>
<?php endif; ?>
<?php else : ?>
<div class="aksrt-forum-tag__empty">
<i class="fas fa-comments"></i>
<p><?php esc_html_e('该标签下暂无话题', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
</main>
<?php get_footer(); ?>
+635
View File
@@ -0,0 +1,635 @@
<?php
/**
* Forum topic template.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
if (!have_posts()) {
get_template_part('templates/parts/content', 'none');
get_footer();
return;
}
the_post();
$forum = class_exists('AKSRT_Forum') ? AKSRT_Forum::get_instance() : null;
$current_user_id = get_current_user_id();
$topic_id = get_the_ID();
$view = $forum ? $forum->get_topic_view($topic_id, $current_user_id) : [
'topic' => null,
'board' => null,
'permissions' => [],
'board_options' => [],
'visibility_options' => [],
'review_options' => [],
];
$topic = $view['topic'] ?? null;
$board = $view['board'] ?? null;
$permissions = $view['permissions'] ?? [];
$board_options = $view['board_options'] ?? [];
$visibility_options = $view['visibility_options'] ?? [];
$review_options = $view['review_options'] ?? [];
// Forum settings for names, breadcrumbs, reply
$forum_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_forum_settings() : [];
$forum_names = $forum_settings['names'] ?? [];
$forum_bc = $forum_settings['post_page'] ?? [];
$reply_settings = $forum_settings['reply'] ?? [];
$show_breadcrumbs = $forum_bc['breadcrumbs'] ?? true;
$bc_show_home = $forum_bc['breadcrumbs_bbs_home'] ?? true;
$bc_forum_name = $forum_bc['breadcrumbs_bbs_home_name'] ?: ($forum_names['forum'] ?? __('论坛', 'aksrt'));
$reply_placeholder = $reply_settings['placeholder'] ?? __('写下你的回复…', 'aksrt');
$name_sticky = $forum_names['posts'] ?? __('置顶', 'aksrt');
$name_featured = $forum_names['topic'] ?? __('精华', 'aksrt');
$name_reply = $forum_names['comment'] ?? __('回复', 'aksrt');
if (!$topic) {
get_template_part('templates/parts/content', 'none');
get_footer();
return;
}
if (empty($topic['can_view'])) :
$deny_vis = $topic['visibility']['key'] ?? '';
$credits_cost = (int) get_post_meta($topic_id, '_aksrt_credits_cost', true);
$pay_price = get_post_meta($topic_id, '_aksrt_pay_price', true);
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<div class="aksrt-forum-topic container">
<div class="aksrt-forum-topic__denied">
<i class="fas fa-eye-slash"></i>
<h1><?php esc_html_e('当前主题暂不可访问', 'aksrt'); ?></h1>
<?php if ($deny_vis === 'login') : ?>
<p><?php esc_html_e('该主题仅限登录用户查看,请先登录后重试。', 'aksrt'); ?></p>
<a href="<?php echo esc_url(wp_login_url(get_permalink($topic_id))); ?>" class="aksrt-forum-topic__primary-btn"><?php esc_html_e('登录查看', 'aksrt'); ?></a>
<?php elseif ($deny_vis === 'comment') : ?>
<p><?php esc_html_e('评论后即可查看该主题的完整内容。', 'aksrt'); ?></p>
<?php elseif ($deny_vis === 'role') : ?>
<p><?php esc_html_e('你当前的角色无权查看此主题。', 'aksrt'); ?></p>
<?php elseif ($deny_vis === 'credits' && $credits_cost > 0) : ?>
<p><?php printf(esc_html__('查看该主题需要 %d 积分。', 'aksrt'), $credits_cost); ?></p>
<?php if (is_user_logged_in()) : ?>
<button type="button" class="aksrt-forum-topic__primary-btn js-aksrt-unlock-topic"
data-topic-id="<?php echo esc_attr((string) $topic_id); ?>"
data-cost="<?php echo esc_attr((string) $credits_cost); ?>">
<i class="fas fa-unlock"></i> <?php printf(esc_html__('使用 %d 积分解锁', 'aksrt'), $credits_cost); ?>
</button>
<span class="aksrt-forum-topic__unlock-feedback js-aksrt-action-feedback" aria-live="polite"></span>
<?php else : ?>
<a href="<?php echo esc_url(wp_login_url(get_permalink($topic_id))); ?>" class="aksrt-forum-topic__primary-btn"><?php esc_html_e('登录后解锁', 'aksrt'); ?></a>
<?php endif; ?>
<?php elseif ($deny_vis === 'pay') : ?>
<p><?php esc_html_e('该主题为付费内容,请购买后查看。', 'aksrt'); ?></p>
<?php if (!empty($pay_price)) : ?>
<span class="aksrt-forum-topic__pay-price"><?php printf(esc_html__('价格:¥%s', 'aksrt'), esc_html((string) $pay_price)); ?></span>
<?php endif; ?>
<?php else : ?>
<p><?php esc_html_e('该主题当前处于受限可见或审核状态。', 'aksrt'); ?></p>
<?php endif; ?>
</div>
</div>
</main>
<?php
get_footer();
return;
endif;
if ($forum) {
$forum->increment_view_count($topic_id);
$topic['views'] = (int) ($topic['views'] ?? 0) + 1;
}
$is_locked = !empty($topic['locked']);
$comments_enabled = !empty($permissions['can_reply']);
?>
<main id="main-content" class="site-main container" tabindex="-1">
<div class="aksrt-forum-topic container">
<?php if ($show_breadcrumbs) : ?>
<div class="aksrt-forum-topic__breadcrumb">
<?php if ($bc_show_home) : ?>
<a href="<?php echo esc_url(home_url('/forums/')); ?>"><?php echo esc_html($bc_forum_name); ?></a>
<?php endif; ?>
<?php if ($board) : ?>
<?php if ($bc_show_home) : ?><span class="aksrt-forum-topic__sep">/</span><?php endif; ?>
<a href="<?php echo esc_url((string) ($board['url'] ?? '#')); ?>"><?php echo esc_html((string) ($board['name'] ?? '')); ?></a>
<?php endif; ?>
<span class="aksrt-forum-topic__sep">/</span>
<span><?php echo esc_html((string) ($topic['title'] ?? '')); ?></span>
</div>
<?php endif; ?>
<header class="aksrt-forum-topic__header">
<div class="aksrt-forum-topic__header-top">
<h1 class="aksrt-forum-topic__title">
<?php
$sticky_level = (int) ($topic['sticky_level'] ?? 0);
$sticky_label = (string) ($topic['sticky_label'] ?? '');
if ($sticky_level > 0 && $sticky_label !== '') :
$sticky_mod = $sticky_level >= 3 ? 'global' : ($sticky_level === 2 ? 'super' : 'plate');
?>
<span class="aksrt-forum-topic__badge aksrt-forum-topic__badge--sticky aksrt-forum-topic__badge--sticky-<?php echo esc_attr($sticky_mod); ?>">
<?php echo esc_html($sticky_label); ?>
</span>
<?php endif; ?>
<?php if (!empty($topic['featured'])) : ?><span class="aksrt-forum-topic__badge aksrt-forum-topic__badge--featured"><?php esc_html_e('精华', 'aksrt'); ?></span><?php endif; ?>
<?php if ($is_locked) : ?><span class="aksrt-forum-topic__badge aksrt-forum-topic__badge--locked"><?php esc_html_e('已锁定', 'aksrt'); ?></span><?php endif; ?>
<?php if (($topic['visibility']['key'] ?? 'public') !== 'public') : ?><span class="aksrt-forum-topic__badge aksrt-forum-topic__badge--visibility"><?php echo esc_html((string) ($topic['visibility']['label'] ?? '')); ?></span><?php endif; ?>
<?php if (($topic['bbs_type'] ?? 'standard') === 'question') : ?>
<?php $q_status = (int) ($topic['question_status'] ?? 0); ?>
<span class="aksrt-forum-topic__badge aksrt-forum-topic__badge--question<?php echo $q_status === 1 ? ' aksrt-forum-topic__badge--solved' : ''; ?>">
<?php echo $q_status === 1 ? esc_html($forum_names['question_ok_badge'] ?? __('已解决', 'aksrt')) : esc_html($forum_names['question_badge'] ?? __('提问', 'aksrt')); ?>
</span>
<?php endif; ?>
<?php echo esc_html((string) ($topic['title'] ?? '')); ?>
</h1>
<div class="aksrt-forum-topic__meta">
<span class="aksrt-forum-topic__author">
<?php if (!empty($topic['author']['avatar_url'])) : ?>
<img src="<?php echo esc_url((string) $topic['author']['avatar_url']); ?>" alt="" width="24" height="24">
<?php endif; ?>
<?php echo esc_html((string) ($topic['author']['display_name'] ?? '')); ?>
</span>
<span><i class="fas fa-clock"></i> <?php echo esc_html((string) ($topic['date'] ?? '')); ?></span>
<span><i class="fas fa-eye"></i> <?php echo esc_html(number_format_i18n((int) ($topic['views'] ?? 0))); ?></span>
<span><i class="fas fa-comment"></i> <?php echo esc_html(number_format_i18n((int) ($topic['comment_count'] ?? 0))); ?></span>
<?php if (!empty($topic['review_status']['label'])) : ?>
<span><i class="fas fa-shield-check"></i> <?php echo esc_html((string) $topic['review_status']['label']); ?></span>
<?php endif; ?>
<?php if (($topic['score'] ?? 0) !== 0) : ?>
<span class="aksrt-forum-topic__score-badge"><i class="fas fa-star"></i> <?php echo esc_html(number_format_i18n((int) $topic['score'])); ?></span>
<?php endif; ?>
<?php if (($topic['favorite_count'] ?? 0) > 0) : ?>
<span><i class="fas fa-bookmark"></i> <?php echo esc_html(number_format_i18n((int) $topic['favorite_count'])); ?></span>
<?php endif; ?>
</div>
<div class="aksrt-forum-topic__quick-actions">
<?php if (is_user_logged_in()) : ?>
<button type="button" class="aksrt-forum-topic__quick-action js-aksrt-report-trigger"
data-target-type="post"
data-target-id="<?php echo esc_attr((string) $topic_id); ?>"
data-target-title="<?php echo esc_attr((string) ($topic['title'] ?? '')); ?>">
<i class="far fa-flag"></i>
<?php esc_html_e('举报', 'aksrt'); ?>
</button>
<?php endif; ?>
<?php if (is_user_logged_in()) : ?>
<?php $is_fav = !empty($topic['is_favorited']); ?>
<button type="button"
class="aksrt-forum-topic__quick-action js-aksrt-topic-favorite"
data-topic-id="<?php echo esc_attr((string) $topic_id); ?>"
data-favorited="<?php echo $is_fav ? '1' : '0'; ?>">
<i class="<?php echo $is_fav ? 'fas' : 'far'; ?> fa-bookmark"></i>
<span class="js-aksrt-favorite-label"><?php echo $is_fav ? esc_html__('已收藏', 'aksrt') : esc_html__('收藏', 'aksrt'); ?></span>
</button>
<?php endif; ?>
<?php if (!empty($permissions['can_edit'])) : ?>
<a href="<?php echo esc_url(add_query_arg('edit', '1', get_permalink($topic_id))); ?>" class="aksrt-forum-topic__quick-action">
<i class="fas fa-edit"></i>
<?php esc_html_e('编辑主题', 'aksrt'); ?>
</a>
<?php endif; ?>
<?php if (!empty($permissions['can_moderate'])) : ?>
<a href="#aksrt-topic-moderation" class="aksrt-forum-topic__quick-action">
<i class="fas fa-tools"></i>
<?php esc_html_e('治理动作', 'aksrt'); ?>
</a>
<?php endif; ?>
</div>
</header>
<div class="aksrt-forum-report-modal" id="aksrt-report-modal" hidden>
<div class="aksrt-forum-report-modal__backdrop js-aksrt-report-close"></div>
<div class="aksrt-forum-report-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="aksrt-report-title">
<div class="aksrt-forum-report-modal__head">
<h2 id="aksrt-report-title"><?php esc_html_e('提交举报', 'aksrt'); ?></h2>
<button type="button" class="aksrt-forum-report-modal__close js-aksrt-report-close" aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>">×</button>
</div>
<p class="aksrt-forum-report-modal__target js-aksrt-report-target"></p>
<form class="aksrt-forum-report-modal__form js-aksrt-report-form">
<input type="hidden" name="target_type" value="post">
<input type="hidden" name="target_id" value="<?php echo esc_attr((string) $topic_id); ?>">
<div class="aksrt-forum-topic__field">
<label for="aksrt-report-category"><?php esc_html_e('举报分类', 'aksrt'); ?></label>
<select id="aksrt-report-category" name="category">
<option value="spam"><?php esc_html_e('垃圾信息', 'aksrt'); ?></option>
<option value="abuse"><?php esc_html_e('人身攻击', 'aksrt'); ?></option>
<option value="copyright"><?php esc_html_e('版权投诉', 'aksrt'); ?></option>
<option value="other"><?php esc_html_e('其他问题', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-forum-topic__field">
<label for="aksrt-report-reason"><?php esc_html_e('举报说明', 'aksrt'); ?></label>
<textarea id="aksrt-report-reason" name="reason" rows="4" required placeholder="<?php esc_attr_e('请尽量描述清楚问题,以便管理员处理。', 'aksrt'); ?>"></textarea>
</div>
<div class="aksrt-forum-report-modal__actions">
<button type="submit" class="aksrt-forum-topic__primary-btn"><?php esc_html_e('提交举报', 'aksrt'); ?></button>
<span class="aksrt-forum-report-modal__feedback js-aksrt-action-feedback" aria-live="polite"></span>
</div>
</form>
</div>
</div>
<?php if (!empty($topic['cover'])) : ?>
<div class="aksrt-forum-topic__cover aksrt-forum-topic__cover--<?php echo esc_attr($topic['cover']['type']); ?>">
<?php if ($topic['cover']['type'] === 'video') : ?>
<div class="aksrt-forum-topic__cover-player" data-src="<?php echo esc_url((string) $topic['cover']['url']); ?>" data-poster="<?php echo esc_url((string) ($topic['cover']['poster'] ?? '')); ?>">
<?php if (!empty($topic['cover']['poster'])) : ?>
<img src="<?php echo esc_url((string) $topic['cover']['poster']); ?>" alt="" class="aksrt-forum-topic__cover-poster">
<?php endif; ?>
<button type="button" class="aksrt-forum-topic__cover-play-btn"><i class="fas fa-play-circle"></i></button>
</div>
<?php elseif ($topic['cover']['type'] === 'slide') : ?>
<div class="aksrt-forum-topic__cover-slides">
<?php foreach (($topic['cover']['images'] ?? []) as $slide_url) : ?>
<img src="<?php echo esc_url((string) $slide_url); ?>" alt="" loading="lazy">
<?php endforeach; ?>
</div>
<?php else : ?>
<img src="<?php echo esc_url((string) ($topic['cover']['url'] ?? '')); ?>" alt="" class="aksrt-forum-topic__cover-img">
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (!empty($topic['special_topic']) || !empty($topic['tags']) || !empty($topic['topic_cat'])) : ?>
<div class="aksrt-forum-topic__taxonomy">
<?php if (!empty($topic['topic_cat'])) : ?>
<a class="aksrt-forum-topic__chip aksrt-forum-topic__chip--topic-cat" href="<?php echo esc_url((string) ($topic['topic_cat']['url'] ?? '#')); ?>">
<?php echo esc_html((string) ($topic['topic_cat']['name'] ?? '')); ?>
</a>
<?php endif; ?>
<?php if (!empty($topic['special_topic'])) : ?>
<span class="aksrt-forum-topic__chip aksrt-forum-topic__chip--special"><?php echo esc_html((string) $topic['special_topic']); ?></span>
<?php endif; ?>
<?php foreach (($topic['tags'] ?? []) as $tag) : ?>
<a class="aksrt-forum-topic__chip" href="<?php echo esc_url((string) ($tag['url'] ?? '#')); ?>">
#<?php echo esc_html((string) ($tag['name'] ?? '')); ?>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
<article class="aksrt-forum-topic__content">
<?php the_content(); ?>
</article>
<?php
$score_extra_max = (int) ($forum_settings['post_page']['score_extra_max'] ?? 5);
$score_deduct_max = (int) ($forum_settings['post_page']['score_deduct_max'] ?? 3);
$current_score = (int) ($topic['score'] ?? 0);
?>
<?php if (is_user_logged_in() && (int) ($topic['author_id'] ?? 0) !== get_current_user_id()) : ?>
<div class="aksrt-forum-topic__score-actions" data-topic-id="<?php echo esc_attr((string) $topic_id); ?>">
<span class="aksrt-forum-topic__score-total">
<i class="fas fa-star"></i>
<span class="js-aksrt-score-value"><?php echo esc_html(number_format_i18n($current_score)); ?></span>
<?php esc_html_e('分', 'aksrt'); ?>
</span>
<?php if ($score_extra_max > 0) : ?>
<button type="button" class="aksrt-forum-topic__score-btn aksrt-forum-topic__score-btn--up js-aksrt-topic-score" data-amount="1" title="<?php esc_attr_e('加分', 'aksrt'); ?>">
<i class="fas fa-plus"></i>
</button>
<?php endif; ?>
<?php if ($score_deduct_max > 0) : ?>
<button type="button" class="aksrt-forum-topic__score-btn aksrt-forum-topic__score-btn--down js-aksrt-topic-score" data-amount="-1" title="<?php esc_attr_e('减分', 'aksrt'); ?>">
<i class="fas fa-minus"></i>
</button>
<?php endif; ?>
<span class="aksrt-forum-topic__score-feedback js-aksrt-action-feedback" aria-live="polite"></span>
</div>
<?php endif; ?>
<?php if (!empty($topic['vote']['enabled'])) : ?>
<?php
$vote_type = $topic['vote']['vote_type'] ?? 'standard';
$vote_expired = !empty($topic['vote']['expired']);
$vote_display = $topic['vote']['display_mode'] ?? 'percent';
$vote_expiry = (int) ($topic['vote']['expiry_days'] ?? 0);
$vote_disabled = $vote_expired || !empty($topic['vote']['has_voted']);
?>
<section class="aksrt-forum-topic__vote<?php echo $vote_type === 'pk' ? ' aksrt-forum-topic__vote--pk' : ''; ?><?php echo $vote_expired ? ' aksrt-forum-topic__vote--expired' : ''; ?>">
<div class="aksrt-forum-topic__vote-head">
<h2><?php echo esc_html((string) ($topic['vote']['title'] ?: __('参与投票', 'aksrt'))); ?></h2>
<span>
<?php printf(esc_html__('%d 人已投票', 'aksrt'), (int) ($topic['vote']['total_votes'] ?? 0)); ?>
<?php if ($vote_type === 'pk') : ?>
<span class="aksrt-forum-topic__vote-type-badge"><?php esc_html_e('PK', 'aksrt'); ?></span>
<?php endif; ?>
<?php if ($vote_expired) : ?>
<span class="aksrt-forum-topic__vote-expired-badge"><?php esc_html_e('已结束', 'aksrt'); ?></span>
<?php elseif ($vote_expiry > 0) : ?>
<span class="aksrt-forum-topic__vote-expiry-info"><?php printf(esc_html__('%d 天有效期', 'aksrt'), $vote_expiry); ?></span>
<?php endif; ?>
</span>
</div>
<form class="aksrt-forum-topic__vote-form js-aksrt-topic-vote-form" data-topic-id="<?php echo esc_attr((string) $topic_id); ?>" data-multiple="<?php echo !empty($topic['vote']['multiple']) ? '1' : '0'; ?>">
<div class="aksrt-forum-topic__vote-options">
<?php foreach (($topic['vote']['options'] ?? []) as $option) :
$selected = !empty($option['selected']);
$count = (int) ($option['count'] ?? 0);
$total_votes = max(1, (int) ($topic['vote']['total_votes'] ?? 0));
$ratio = (int) round(($count / $total_votes) * 100);
?>
<label class="aksrt-forum-topic__vote-option<?php echo $selected ? ' is-selected' : ''; ?>">
<input
type="<?php echo !empty($topic['vote']['multiple']) ? 'checkbox' : 'radio'; ?>"
name="option_ids[]"
value="<?php echo esc_attr((string) ($option['id'] ?? '')); ?>"
<?php checked($selected); ?>
<?php disabled($vote_disabled); ?>
>
<span class="aksrt-forum-topic__vote-label"><?php echo esc_html((string) ($option['label'] ?? '')); ?></span>
<span class="aksrt-forum-topic__vote-count">
<?php if ($vote_display === 'number') : ?>
<?php echo esc_html(number_format_i18n($count)); ?>
<?php else : ?>
<?php echo esc_html(number_format_i18n($count)); ?> / <?php echo esc_html((string) $ratio); ?>%
<?php endif; ?>
</span>
</label>
<?php endforeach; ?>
</div>
<div class="aksrt-forum-topic__vote-actions">
<?php if ($vote_expired) : ?>
<span class="aksrt-forum-topic__vote-feedback is-error"><?php esc_html_e('投票已结束。', 'aksrt'); ?></span>
<?php elseif (!empty($topic['vote']['has_voted'])) : ?>
<span class="aksrt-forum-topic__vote-feedback is-success"><?php esc_html_e('你已经参与过本次投票。', 'aksrt'); ?></span>
<?php else : ?>
<button type="submit" class="aksrt-forum-topic__primary-btn"><?php esc_html_e('提交投票', 'aksrt'); ?></button>
<span class="aksrt-forum-topic__vote-feedback js-aksrt-action-feedback" aria-live="polite"></span>
<?php endif; ?>
</div>
</form>
</section>
<?php endif; ?>
<?php if (!empty($permissions['can_moderate'])) : ?>
<section id="aksrt-topic-moderation" class="aksrt-forum-topic__moderation">
<div class="aksrt-forum-topic__moderation-head">
<h2><?php esc_html_e('主题治理', 'aksrt'); ?></h2>
<p><?php esc_html_e('支持审核、移动版块、可见性调整以及置顶/精华/锁定等治理动作。', 'aksrt'); ?></p>
</div>
<?php if (!empty($topic['moderation_note'])) : ?>
<div class="aksrt-forum-topic__moderation-note">
<strong><?php esc_html_e('最近治理备注:', 'aksrt'); ?></strong>
<span><?php echo esc_html((string) $topic['moderation_note']); ?></span>
</div>
<?php endif; ?>
<form class="aksrt-forum-topic__moderation-form js-aksrt-topic-moderation-form" data-topic-id="<?php echo esc_attr((string) $topic_id); ?>">
<div class="aksrt-forum-topic__moderation-grid">
<div class="aksrt-forum-topic__field">
<label for="aksrt-topic-moderation-action"><?php esc_html_e('治理动作', 'aksrt'); ?></label>
<select id="aksrt-topic-moderation-action" name="action" class="js-aksrt-moderation-action" required>
<option value="review"><?php esc_html_e('审核状态', 'aksrt'); ?></option>
<option value="move_board"><?php esc_html_e('移动版块', 'aksrt'); ?></option>
<option value="visibility"><?php esc_html_e('修改可见性', 'aksrt'); ?></option>
<option value="sticky"><?php esc_html_e('置顶切换', 'aksrt'); ?></option>
<option value="featured"><?php esc_html_e('精华切换', 'aksrt'); ?></option>
<option value="locked"><?php esc_html_e('锁定切换', 'aksrt'); ?></option>
<option value="close"><?php esc_html_e('关闭帖子', 'aksrt'); ?></option>
<option value="hide"><?php esc_html_e('隐藏帖子', 'aksrt'); ?></option>
<option value="delete"><?php esc_html_e('删除到回收站', 'aksrt'); ?></option>
<?php if (get_post_status($topic_id) === 'trash') : ?>
<option value="undelete"><?php esc_html_e('撤销删除', 'aksrt'); ?></option>
<?php endif; ?>
<?php if (($topic['bbs_type'] ?? 'standard') === 'question') : ?>
<option value="question_status"><?php esc_html_e('提问状态', 'aksrt'); ?></option>
<?php endif; ?>
</select>
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field" data-field="review">
<label for="aksrt-topic-review-status"><?php esc_html_e('审核结果', 'aksrt'); ?></label>
<select id="aksrt-topic-review-status" name="review_status">
<?php foreach ($review_options as $option) : ?>
<option value="<?php echo esc_attr((string) ($option['value'] ?? '')); ?>" <?php selected((string) (($topic['review_status']['key'] ?? '')) === (string) ($option['value'] ?? '')); ?>>
<?php echo esc_html((string) ($option['label'] ?? '')); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field" data-field="move_board" hidden>
<label for="aksrt-topic-target-board"><?php esc_html_e('目标版块', 'aksrt'); ?></label>
<select id="aksrt-topic-target-board" name="target_board_id">
<?php foreach ($board_options as $option) : ?>
<option value="<?php echo esc_attr((string) ($option['id'] ?? '')); ?>" <?php selected((int) ($board['id'] ?? 0), (int) ($option['id'] ?? 0)); ?>>
<?php echo esc_html((string) ($option['name'] ?? '')); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field" data-field="visibility" hidden>
<label for="aksrt-topic-visibility"><?php esc_html_e('可见性', 'aksrt'); ?></label>
<select id="aksrt-topic-visibility" name="visibility">
<?php foreach ($visibility_options as $option) : ?>
<option value="<?php echo esc_attr((string) ($option['value'] ?? '')); ?>" <?php selected((string) (($topic['visibility']['key'] ?? '')) === (string) ($option['value'] ?? '')); ?>>
<?php echo esc_html((string) ($option['label'] ?? '')); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field js-aksrt-visibility-extra" data-field="visibility" data-visibility-field="credits" hidden>
<label for="aksrt-topic-credits-cost"><?php esc_html_e('积分价格', 'aksrt'); ?></label>
<input id="aksrt-topic-credits-cost" name="credits_cost" type="number" min="1" max="99999" value="<?php echo esc_attr((string) ((int) get_post_meta($topic_id, '_aksrt_credits_cost', true))); ?>">
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field js-aksrt-visibility-extra" data-field="visibility" data-visibility-field="pay" hidden>
<label for="aksrt-topic-pay-price"><?php esc_html_e('付费价格(¥)', 'aksrt'); ?></label>
<input id="aksrt-topic-pay-price" name="pay_price" type="number" min="0.01" step="0.01" max="99999" value="<?php echo esc_attr((string) get_post_meta($topic_id, '_aksrt_pay_price', true)); ?>">
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field js-aksrt-visibility-extra" data-field="visibility" data-visibility-field="role" hidden>
<label for="aksrt-topic-visibility-roles"><?php esc_html_e('允许角色', 'aksrt'); ?></label>
<?php $current_visibility_roles = (array) get_post_meta($topic_id, '_aksrt_visibility_roles', true); ?>
<select id="aksrt-topic-visibility-roles" name="visibility_roles[]" multiple>
<?php foreach (wp_roles()->get_names() as $role_slug => $role_name) : ?>
<option value="<?php echo esc_attr($role_slug); ?>" <?php selected(in_array($role_slug, $current_visibility_roles, true)); ?>>
<?php echo esc_html(translate_user_role($role_name)); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field" data-field="sticky" hidden>
<label for="aksrt-topic-sticky"><?php esc_html_e('置顶级别', 'aksrt'); ?></label>
<select id="aksrt-topic-sticky" name="sticky_value">
<?php
$current_sticky = (int) ($topic['sticky_level'] ?? 0);
$sticky_levels = [
0 => __('不置顶', 'aksrt'),
1 => __('版块置顶', 'aksrt'),
2 => __('超级置顶', 'aksrt'),
3 => __('全局置顶', 'aksrt'),
];
foreach ($sticky_levels as $level => $label) :
?>
<option value="<?php echo esc_attr((string) $level); ?>" <?php selected($current_sticky, $level); ?>>
<?php echo esc_html($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field" data-field="featured" hidden>
<label for="aksrt-topic-featured"><?php esc_html_e('精华状态', 'aksrt'); ?></label>
<select id="aksrt-topic-featured" name="featured_value">
<option value="1" <?php selected(!empty($topic['featured'])); ?>><?php esc_html_e('设为精华', 'aksrt'); ?></option>
<option value="0" <?php selected(empty($topic['featured'])); ?>><?php esc_html_e('取消精华', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field" data-field="locked" hidden>
<label for="aksrt-topic-locked"><?php esc_html_e('锁定状态', 'aksrt'); ?></label>
<select id="aksrt-topic-locked" name="locked_value">
<option value="1" <?php selected(!empty($topic['locked'])); ?>><?php esc_html_e('锁定主题', 'aksrt'); ?></option>
<option value="0" <?php selected(empty($topic['locked'])); ?>><?php esc_html_e('解除锁定', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field" data-field="close" hidden>
<label for="aksrt-topic-close"><?php esc_html_e('关闭状态', 'aksrt'); ?></label>
<select id="aksrt-topic-close" name="close_value">
<option value="1"><?php esc_html_e('关闭帖子', 'aksrt'); ?></option>
<option value="0"><?php esc_html_e('重新开放', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field" data-field="hide" hidden>
<label for="aksrt-topic-hide"><?php esc_html_e('隐藏状态', 'aksrt'); ?></label>
<select id="aksrt-topic-hide" name="hide_value">
<option value="1"><?php esc_html_e('隐藏帖子', 'aksrt'); ?></option>
<option value="0"><?php esc_html_e('恢复显示', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field" data-field="delete" hidden>
<label for="aksrt-topic-delete-confirm"><?php esc_html_e('删除确认', 'aksrt'); ?></label>
<select id="aksrt-topic-delete-confirm" name="delete_value">
<option value="1"><?php esc_html_e('确认删除到回收站', 'aksrt'); ?></option>
</select>
<small><?php esc_html_e('删除后作者会收到通知,主题可在回收站中恢复。', 'aksrt'); ?></small>
</div>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field" data-field="undelete" hidden>
<label for="aksrt-topic-undelete-confirm"><?php esc_html_e('恢复确认', 'aksrt'); ?></label>
<select id="aksrt-topic-undelete-confirm" name="undelete_value">
<option value="1"><?php esc_html_e('确认恢复主题', 'aksrt'); ?></option>
</select>
</div>
<?php if (($topic['bbs_type'] ?? 'standard') === 'question') : ?>
<div class="aksrt-forum-topic__field js-aksrt-moderation-field" data-field="question_status" hidden>
<label for="aksrt-topic-question-status"><?php esc_html_e('提问状态', 'aksrt'); ?></label>
<select id="aksrt-topic-question-status" name="question_status_value">
<option value="0" <?php selected((int) ($topic['question_status'] ?? 0), 0); ?>><?php esc_html_e('未解决', 'aksrt'); ?></option>
<option value="1" <?php selected((int) ($topic['question_status'] ?? 0), 1); ?>><?php esc_html_e('已解决', 'aksrt'); ?></option>
</select>
</div>
<?php endif; ?>
</div>
<div class="aksrt-forum-topic__field">
<label for="aksrt-topic-moderation-note"><?php esc_html_e('治理备注', 'aksrt'); ?></label>
<textarea id="aksrt-topic-moderation-note" name="note" rows="3" placeholder="<?php esc_attr_e('记录处理原因,便于后续审计和沟通。', 'aksrt'); ?>"></textarea>
</div>
<div class="aksrt-forum-topic__moderation-actions">
<button type="submit" class="aksrt-forum-topic__primary-btn"><?php esc_html_e('执行治理动作', 'aksrt'); ?></button>
<span class="aksrt-forum-topic__moderation-feedback js-aksrt-action-feedback" aria-live="polite"></span>
</div>
</form>
</section>
<?php endif; ?>
<section class="aksrt-forum-topic__replies" id="aksrt-forum-replies"
data-topic-id="<?php echo esc_attr((string) $topic_id); ?>"
data-nonce="<?php echo esc_attr(wp_create_nonce('aksrt_public_nonce')); ?>">
<div class="aksrt-forum-replies__header">
<h2 class="aksrt-forum-topic__replies-title">
<i class="fas fa-comments"></i>
<?php
printf(esc_html__('%1$d 条%2$s', 'aksrt'), (int) get_comments_number(), esc_html($name_reply));
?>
</h2>
<div class="aksrt-forum-replies__tabs">
<button type="button" class="aksrt-forum-replies__tab is-active" data-sort="newest"><?php esc_html_e('最新', 'aksrt'); ?></button>
<button type="button" class="aksrt-forum-replies__tab" data-sort="oldest"><?php esc_html_e('最早', 'aksrt'); ?></button>
<button type="button" class="aksrt-forum-replies__tab" data-sort="hottest"><?php esc_html_e('最热', 'aksrt'); ?></button>
<label class="aksrt-forum-replies__filter">
<input type="checkbox" class="aksrt-forum-replies__author-toggle" value="1">
<?php esc_html_e('只看楼主', 'aksrt'); ?>
</label>
</div>
</div>
<div class="aksrt-forum-replies__list js-aksrt-forum-replies-list">
<?php
if ($forum) {
$reply_data = $forum->get_forum_comments($topic_id, 'newest', false, 1, 30);
$forum->render_forum_comments($reply_data['comments'], $topic_id, $reply_data['adopted_id']);
$reply_max_pages = $reply_data['max_pages'];
$reply_total = $reply_data['total'];
} else {
$reply_max_pages = 1;
$reply_total = 0;
}
?>
</div>
<?php if ($reply_max_pages > 1) : ?>
<div class="aksrt-forum-replies__load-more">
<button type="button" class="aksrt-forum-replies__more-btn js-aksrt-forum-load-more"
data-page="1" data-max="<?php echo esc_attr((string) $reply_max_pages); ?>">
<i class="fas fa-chevron-down"></i> <?php esc_html_e('加载更多回复', 'aksrt'); ?>
</button>
</div>
<?php endif; ?>
</section>
<?php if ($comments_enabled) : ?>
<section class="aksrt-forum-topic__reply-form">
<h2><i class="fas fa-reply"></i> <?php esc_html_e('发表评论', 'aksrt'); ?></h2>
<?php
comment_form([
'title_reply' => '',
'comment_notes_before' => '',
'comment_field' => '<div class="aksrt-forum-topic__reply-field"><textarea name="comment" rows="5" required placeholder="' . esc_attr($reply_placeholder) . '"></textarea></div>',
'submit_button' => '<button type="submit" class="aksrt-forum-topic__reply-btn"><i class="fas fa-paper-plane"></i> %s</button>',
'submit_field' => '<div class="aksrt-forum-topic__reply-actions">%1$s %2$s</div>',
]);
?>
</section>
<?php elseif ($is_locked) : ?>
<div class="aksrt-forum-topic__locked-notice">
<i class="fas fa-lock"></i>
<?php esc_html_e('当前主题已锁定,暂不接受新的回复。', 'aksrt'); ?>
</div>
<?php else : ?>
<div class="aksrt-forum-topic__login-notice">
<a href="<?php echo esc_url(wp_login_url(get_permalink())); ?>"><?php esc_html_e('登录后参与回复', 'aksrt'); ?></a>
</div>
<?php endif; ?>
</div>
</main>
<?php get_footer(); ?>
+68
View File
@@ -0,0 +1,68 @@
<?php
/**
* Template Name: AKSRT - Archives
* Template Post Type: page
*
* 文章归档页:按年月分组展示全站文章。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
global $wpdb;
?>
<main id="main-content" class="container page-content-default" tabindex="-1">
<header class="page-header">
<h1 class="entry-title"><?php esc_html_e('文章归档', 'aksrt'); ?></h1>
</header>
<div class="content-wrap">
<div class="content-layout content-layout--no-sidebar">
<div class="content-layout__primary">
<div class="aksrt-archives container">
<?php
$years = $wpdb->get_results(
"SELECT DISTINCT YEAR(post_date) AS year, MONTH(post_date) AS month, COUNT(*) AS count
FROM {$wpdb->posts}
WHERE post_type = 'post' AND post_status = 'publish'
GROUP BY YEAR(post_date), MONTH(post_date)
ORDER BY year DESC, month DESC"
); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
$current_year = 0;
foreach ($years as $row) :
if ((int) $row->year !== $current_year) :
$current_year = (int) $row->year;
?>
<h2 class="aksrt-archives__year"><?php echo esc_html($current_year); ?></h2>
<?php endif; ?>
<h3 class="aksrt-archives__month">
<?php echo esc_html(sprintf('%d 月 (%d 篇)', (int) $row->month, (int) $row->count)); ?>
</h3>
<ul class="aksrt-archives__list">
<?php
$month_posts = get_posts([
'year' => (int) $row->year,
'monthnum' => (int) $row->month,
'posts_per_page' => -1,
'post_status' => 'publish',
]);
foreach ($month_posts as $mp) :
?>
<li class="aksrt-archives__item">
<span class="aksrt-archives__date"><?php echo esc_html(get_the_date('m-d', $mp)); ?></span>
<a href="<?php echo esc_url(get_permalink($mp->ID)); ?>" class="aksrt-archives__link"><?php echo esc_html($mp->post_title); ?></a>
</li>
<?php endforeach; ?>
</ul>
<?php endforeach; ?>
</div>
</div>
</div>
</main>
<?php
get_footer();
+139
View File
@@ -0,0 +1,139 @@
<?php
/**
* Template Name: AKSRT - Bind Account
* Template Post Type: page
*
* Theme auth route template: /bind-account/
*
* 第三方登录未匹配账号时的绑定页面
* 用户输入已有账号密码完成OAuth绑定
*
* @package AKSRT
*/
// ============================================
// 处理绑定请求(POST)
// ============================================
$bind_error = '';
$bind_verify_url = '';
$oauth_data = null;
$provider_name = '';
$obk = '';
if (isset($_GET['obk'])) {
$obk = sanitize_text_field($_GET['obk']);
$oauth_data = AKSRT_Auth_Service::get_oauth_bind_context($obk);
}
if (! $oauth_data || ! is_array($oauth_data)) {
// 无效或已过期的OAuth数据
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$bind_error = __('绑定会话已过期,请重新使用第三方登录', 'aksrt');
}
} else {
$provider_name = AKSRT_Auth_Service::get_oauth_provider_name($oauth_data['provider'] ?? '');
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['log'], $_POST['pwd']) && empty($bind_error)) {
if ($obk === '' && !empty($_POST['obk'])) {
$obk = sanitize_text_field($_POST['obk']);
}
if ($oauth_data === null && $obk !== '') {
$oauth_data = AKSRT_Auth_Service::get_oauth_bind_context($obk);
}
$result = AKSRT_Auth_Service::bind_oauth_account($_POST, $oauth_data);
if (empty($result['success'])) {
$bind_error = $result['error'] ?? __('绑定失败,请稍后重试', 'aksrt');
$bind_verify_url = $result['verify_url'] ?? '';
} else {
$user = $result['user'];
$provider = $result['provider'];
wp_safe_redirect(add_query_arg(['tab' => 'settings', 'oauth_bound' => $provider], aksrt_get_user_center_url()));
exit;
}
}
get_header();
?>
<main id="main-content" class="site-main" tabindex="-1">
<div class="auth-shell">
<div class="auth-card">
<?php if (! $oauth_data || ! is_array($oauth_data)) : ?>
<!-- 会话过期状态 -->
<div class="auth-card__expired">
<div class="auth-card__logo" aria-hidden="true">AK</div>
<h1 class="auth-card__title"><?php bloginfo('name'); ?></h1>
<p><?php echo esc_html(__('绑定会话已过期或无效', 'aksrt')); ?></p>
<a href="<?php echo esc_url(aksrt_get_login_url()); ?>" class="btn-primary btn-lg auth-card__expired-action">
<?php esc_html_e('返回登录', 'aksrt'); ?>
</a>
</div>
<?php else : ?>
<!-- 正常绑定表单 -->
<div class="auth-card__logo" aria-hidden="true">AK</div>
<h1 class="auth-card__title"><?php bloginfo('name'); ?></h1>
<p class="auth-card__subtitle">
<?php printf(esc_html__('通过 %s 登录,请绑定你的已有账号', 'aksrt'), '<strong>' . esc_html($provider_name) . '</strong>'); ?>
</p>
<?php if (!empty($bind_error)) : ?>
<div class="alert alert--error" role="alert">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/><line x1="12" x2="12.01" y1="16" y2="16"/></svg>
<span>
<?php echo esc_html($bind_error); ?>
<?php if (!empty($bind_verify_url)) : ?>
<a href="<?php echo esc_url($bind_verify_url); ?>"><?php esc_html_e('前往验证邮箱', 'aksrt'); ?></a>
<?php endif; ?>
</span>
</div>
<?php endif; ?>
<form class="auth-form" id="aksrt-bind-form" method="post" action="">
<?php wp_nonce_field('aksrt_bind', '_bind_nonce'); ?>
<input type="hidden" name="obk" value="<?php echo esc_attr($obk); ?>">
<div class="form-field">
<label for="bind_user_login"><?php esc_html_e('邮箱 / 用户名', 'aksrt'); ?></label>
<div class="input-wrap">
<span class="input-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg></span>
<input type="text" name="log" id="bind_user_login" autocomplete="username" required>
</div>
</div>
<div class="form-field form-field--pwd">
<label for="bind_user_pass"><?php esc_html_e('密码', 'aksrt'); ?></label>
<div class="input-wrap">
<span class="input-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></span>
<input type="password" name="pwd" id="bind_user_pass" autocomplete="current-password" required>
<button type="button" class="toggle-pwd" data-target="bind_user_pass" aria-label="<?php esc_attr_e('显示或隐藏密码', 'aksrt'); ?>"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10 7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg></button>
</div>
</div>
<?php if (function_exists('aksrt_render_captcha')) : ?>
<?php aksrt_render_captcha('bind'); ?>
<?php endif; ?>
<button type="submit" name="wp-submit" class="btn-primary btn-lg" style="width:100%;margin-top:8px;">
<?php esc_html_e('绑定并登录', 'aksrt'); ?>
</button>
</form>
<?php if (get_option('users_can_register')) :
$register_url = aksrt_get_register_url();
$register_url = add_query_arg('obk', sanitize_text_field($_GET['obk']), $register_url);
?>
<p class="auth-footer-link">
<?php esc_html_e('还没有账号?', 'aksrt'); ?>
<a href="<?php echo esc_url($register_url); ?>"><?php esc_html_e('注册并绑定账号', 'aksrt'); ?></a>
</p>
<?php endif; ?>
<?php endif; ?><!-- end 正常绑定表单 -->
</div>
</div>
</main>
<?php
get_footer();
+281
View File
@@ -0,0 +1,281 @@
<?php
/**
* Template Name: AKSRT - Cart
* Template Post Type: page
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
if (!is_user_logged_in()) {
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink());
wp_safe_redirect($login_url);
exit;
}
$cart_view = function_exists('aksrt_get_cart_page_view')
? aksrt_get_cart_page_view(get_current_user_id())
: [
'payment_methods' => [],
'shipping_methods' => [],
'address_book' => [],
'coupon_enabled' => false,
'currency_symbol' => '¥',
'pending_orders' => [],
];
$pending_orders = is_array($cart_view['pending_orders'] ?? null) ? $cart_view['pending_orders'] : [];
$shipping_methods = is_array($cart_view['shipping_methods'] ?? null) ? array_values($cart_view['shipping_methods']) : [];
$payment_methods = is_array($cart_view['payment_methods'] ?? null) ? array_values($cart_view['payment_methods']) : [];
$address_book = is_array($cart_view['address_book'] ?? null) ? $cart_view['address_book'] : [];
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<?php if (function_exists('aksrt_breadcrumbs')) { aksrt_breadcrumbs(); } ?>
<div
class="aksrt-cart-page container"
data-aksrt-cart-page
data-currency-symbol="<?php echo esc_attr((string) ($cart_view['currency_symbol'] ?? '¥')); ?>"
data-coupon-enabled="<?php echo !empty($cart_view['coupon_enabled']) ? '1' : '0'; ?>"
>
<div class="aksrt-cart-page__header">
<div>
<h1 class="aksrt-cart-page__title"><?php esc_html_e('购物车', 'aksrt'); ?></h1>
<p class="aksrt-cart-page__subtitle"><?php esc_html_e('在下单前确认地址、运费、优惠券和支付方式。', 'aksrt'); ?></p>
</div>
<?php if (!empty($pending_orders)) : ?>
<div class="aksrt-cart-page__pending">
<span class="aksrt-cart-page__pending-label"><?php esc_html_e('待支付订单', 'aksrt'); ?></span>
<div class="aksrt-cart-page__pending-list">
<?php foreach ($pending_orders as $pending_order) : ?>
<button
type="button"
class="aksrt-cart-page__pending-chip"
data-aksrt-resume-order="<?php echo esc_attr((string) ($pending_order['order_number'] ?? '')); ?>"
>
<?php echo esc_html((string) ($pending_order['order_number'] ?? '')); ?>
</button>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
<div class="aksrt-cart-page__layout">
<section class="aksrt-cart-page__main">
<div class="aksrt-cart-page__panel">
<div class="aksrt-cart-page__panel-head">
<h2><?php esc_html_e('商品清单', 'aksrt'); ?></h2>
<button type="button" class="btn-ghost btn-sm" data-aksrt-clear-cart><?php esc_html_e('清空购物车', 'aksrt'); ?></button>
</div>
<div class="aksrt-cart-page__items" data-aksrt-cart-items>
<div class="aksrt-cart-page__empty">
<i class="fas fa-shopping-cart" aria-hidden="true"></i>
<p><?php esc_html_e('购物车为空,去商品页添加想购买的内容。', 'aksrt'); ?></p>
<a href="<?php echo esc_url(home_url('/')); ?>" class="btn-outline btn-sm"><?php esc_html_e('继续浏览', 'aksrt'); ?></a>
</div>
</div>
</div>
<div class="aksrt-cart-page__grid">
<section class="aksrt-cart-page__panel">
<div class="aksrt-cart-page__panel-head">
<h2><?php esc_html_e('收货地址', 'aksrt'); ?></h2>
<button type="button" class="btn-ghost btn-sm" data-aksrt-address-add>
<i class="fas fa-plus" aria-hidden="true"></i> <?php esc_html_e('新增地址', 'aksrt'); ?>
</button>
</div>
<div class="aksrt-cart-page__option-list" data-aksrt-address-list>
<?php if (empty($address_book)) : ?>
<div class="aksrt-cart-page__address-empty" data-aksrt-address-empty>
<i class="fas fa-map-marker-alt" aria-hidden="true"></i>
<p><?php esc_html_e('暂无收货地址,请先新增。', 'aksrt'); ?></p>
</div>
<?php endif; ?>
<?php foreach ($address_book as $index => $address) : ?>
<div class="aksrt-cart-page__option-card aksrt-cart-page__address-card" data-aksrt-address-card="<?php echo esc_attr((string) ($address['id'] ?? 0)); ?>">
<label class="aksrt-cart-page__address-label">
<input
type="radio"
name="aksrt_cart_address"
value="<?php echo esc_attr((string) ($address['id'] ?? 0)); ?>"
<?php checked($index === 0 || !empty($address['is_default'])); ?>
>
<span class="aksrt-cart-page__option-body">
<strong>
<?php echo esc_html((string) ($address['name'] ?? '')); ?>
<?php if (!empty($address['is_default'])) : ?>
<span class="aksrt-cart-page__default-tag"><?php esc_html_e('默认', 'aksrt'); ?></span>
<?php endif; ?>
</strong>
<span><?php echo esc_html((string) ($address['phone'] ?? '')); ?></span>
<span><?php echo esc_html((string) ($address['full_text'] ?? '')); ?></span>
</span>
</label>
<div class="aksrt-cart-page__address-actions">
<button type="button" class="btn-ghost btn-xs" data-aksrt-address-edit="<?php echo esc_attr((string) ($address['id'] ?? 0)); ?>"><?php esc_html_e('编辑', 'aksrt'); ?></button>
<button type="button" class="btn-ghost btn-xs" data-aksrt-address-delete="<?php echo esc_attr((string) ($address['id'] ?? 0)); ?>"><?php esc_html_e('删除', 'aksrt'); ?></button>
<?php if (empty($address['is_default'])) : ?>
<button type="button" class="btn-ghost btn-xs" data-aksrt-address-set-default="<?php echo esc_attr((string) ($address['id'] ?? 0)); ?>"><?php esc_html_e('设为默认', 'aksrt'); ?></button>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</section>
<!-- Address Editor Modal -->
<div class="aksrt-modal" data-aksrt-address-modal hidden>
<div class="aksrt-modal__overlay"></div>
<div class="aksrt-modal__dialog">
<div class="aksrt-modal__header">
<h3 data-aksrt-address-modal-title><?php esc_html_e('新增地址', 'aksrt'); ?></h3>
<button type="button" class="aksrt-modal__close" data-aksrt-address-modal-close>&times;</button>
</div>
<form class="aksrt-modal__body" data-aksrt-address-form>
<input type="hidden" name="address_id" value="">
<div class="aksrt-cart-page__field">
<label><?php esc_html_e('收货人', 'aksrt'); ?> *</label>
<input type="text" name="name" class="aksrt-cart-page__input" required maxlength="50">
</div>
<div class="aksrt-cart-page__field">
<label><?php esc_html_e('手机号', 'aksrt'); ?> *</label>
<input type="tel" name="phone" class="aksrt-cart-page__input" required maxlength="20">
</div>
<div class="aksrt-cart-page__field-row">
<div class="aksrt-cart-page__field">
<label><?php esc_html_e('省份', 'aksrt'); ?></label>
<input type="text" name="province" class="aksrt-cart-page__input" maxlength="50">
</div>
<div class="aksrt-cart-page__field">
<label><?php esc_html_e('城市', 'aksrt'); ?></label>
<input type="text" name="city" class="aksrt-cart-page__input" maxlength="50">
</div>
<div class="aksrt-cart-page__field">
<label><?php esc_html_e('区/县', 'aksrt'); ?></label>
<input type="text" name="district" class="aksrt-cart-page__input" maxlength="50">
</div>
</div>
<div class="aksrt-cart-page__field">
<label><?php esc_html_e('详细地址', 'aksrt'); ?> *</label>
<input type="text" name="address" class="aksrt-cart-page__input" required maxlength="200">
</div>
<label class="aksrt-cart-page__checkbox">
<input type="checkbox" name="is_default" value="1">
<span><?php esc_html_e('设为默认地址', 'aksrt'); ?></span>
</label>
<div class="aksrt-modal__footer">
<button type="button" class="btn-ghost" data-aksrt-address-modal-close><?php esc_html_e('取消', 'aksrt'); ?></button>
<button type="submit" class="btn-primary"><?php esc_html_e('保存地址', 'aksrt'); ?></button>
</div>
</form>
</div>
</div>
<section class="aksrt-cart-page__panel">
<div class="aksrt-cart-page__panel-head">
<h2><?php esc_html_e('配送与支付', 'aksrt'); ?></h2>
</div>
<div class="aksrt-cart-page__option-columns">
<div>
<h3 class="aksrt-cart-page__mini-title"><?php esc_html_e('配送方式', 'aksrt'); ?></h3>
<div class="aksrt-cart-page__option-list" data-aksrt-shipping-list>
<?php foreach ($shipping_methods as $index => $method) : ?>
<label class="aksrt-cart-page__option-card">
<input
type="radio"
name="aksrt_cart_shipping"
value="<?php echo esc_attr((string) ($method['value'] ?? '')); ?>"
<?php checked($index === 0); ?>
>
<span class="aksrt-cart-page__option-body">
<strong><?php echo esc_html((string) ($method['label'] ?? '')); ?></strong>
<span><?php echo esc_html((string) ($method['description'] ?? '')); ?></span>
</span>
</label>
<?php endforeach; ?>
</div>
</div>
<div>
<h3 class="aksrt-cart-page__mini-title"><?php esc_html_e('支付方式', 'aksrt'); ?></h3>
<div class="aksrt-cart-page__option-list" data-aksrt-payment-list>
<?php foreach ($payment_methods as $index => $method) : ?>
<label class="aksrt-cart-page__option-card">
<input
type="radio"
name="aksrt_cart_payment"
value="<?php echo esc_attr((string) ($method['value'] ?? '')); ?>"
<?php checked($index === 0); ?>
>
<span class="aksrt-cart-page__option-body">
<strong><?php echo esc_html((string) ($method['label'] ?? '')); ?></strong>
<span><?php echo esc_html((string) ($method['description'] ?? '')); ?></span>
</span>
</label>
<?php endforeach; ?>
</div>
</div>
</div>
</section>
<section class="aksrt-cart-page__panel">
<div class="aksrt-cart-page__panel-head">
<h2><?php esc_html_e('优惠与备注', 'aksrt'); ?></h2>
</div>
<div class="aksrt-cart-page__coupon-box">
<label class="aksrt-cart-page__field">
<span><?php esc_html_e('优惠券', 'aksrt'); ?></span>
<div class="aksrt-cart-page__coupon-row">
<input type="text" class="aksrt-cart-page__input" data-aksrt-coupon-code placeholder="<?php esc_attr_e('输入优惠码后试算', 'aksrt'); ?>">
<button type="button" class="btn-outline btn-sm" data-aksrt-preview-checkout><?php esc_html_e('应用并试算', 'aksrt'); ?></button>
</div>
</label>
<p class="aksrt-cart-page__hint" data-aksrt-preview-message><?php esc_html_e('试算后会展示优惠抵扣、运费和最终应付金额。', 'aksrt'); ?></p>
<label class="aksrt-cart-page__field">
<span><?php esc_html_e('订单备注', 'aksrt'); ?></span>
<textarea class="aksrt-cart-page__textarea" data-aksrt-customer-note rows="4" placeholder="<?php esc_attr_e('例如发票要求、发货备注等。', 'aksrt'); ?>"></textarea>
</label>
</div>
</section>
</div>
</section>
<aside class="aksrt-cart-page__sidebar">
<section class="aksrt-cart-page__panel aksrt-cart-page__summary">
<div class="aksrt-cart-page__panel-head">
<h2><?php esc_html_e('订单摘要', 'aksrt'); ?></h2>
</div>
<dl class="aksrt-cart-page__summary-list">
<div>
<dt><?php esc_html_e('商品小计', 'aksrt'); ?></dt>
<dd data-aksrt-summary-subtotal>¥0.00</dd>
</div>
<div>
<dt><?php esc_html_e('运费', 'aksrt'); ?></dt>
<dd data-aksrt-summary-shipping>¥0.00</dd>
</div>
<div>
<dt><?php esc_html_e('优惠抵扣', 'aksrt'); ?></dt>
<dd data-aksrt-summary-discount>-¥0.00</dd>
</div>
</dl>
<div class="aksrt-cart-page__summary-total">
<span><?php esc_html_e('应付总额', 'aksrt'); ?></span>
<strong data-aksrt-summary-total>¥0.00</strong>
</div>
<button type="button" class="btn-primary btn-lg aksrt-cart-page__submit" data-aksrt-create-order disabled>
<?php esc_html_e('创建订单', 'aksrt'); ?>
</button>
</section>
</aside>
</div>
</div>
</main>
<?php get_footer(); ?>
+130
View File
@@ -0,0 +1,130 @@
<?php
/**
* Template Name: AKSRT - Categories
* Template Post Type: page
*
* 分类汇总页面:列出全站所有分类。
*
* 用法:在后台新建一个 Page,标题任意(默认显示页面标题,否则用"分类目录"),
* 模板选择 "AKSRT - Categories";或将 slug 设为 "categories" 自动匹配。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
// ─── 数据准备 ─────────────────────────────────────────────
$all_terms = get_terms([
'taxonomy' => 'category',
'hide_empty' => false, // 全部展示,含 0 篇文章的分类
'orderby' => 'menu_order name',
'order' => 'ASC',
]);
if (is_wp_error($all_terms)) {
$all_terms = [];
}
$top_level = [];
$children_by_parent = [];
foreach ($all_terms as $term) {
if ((int) $term->parent === 0) {
$top_level[] = $term;
} else {
$children_by_parent[(int) $term->parent][] = $term;
}
}
$total_cats = count($all_terms);
$total_counts = wp_count_posts();
$total_posts = $total_counts && isset($total_counts->publish) ? (int) $total_counts->publish : 0;
$page_title = trim((string) get_the_title());
if ($page_title === '') {
$page_title = __('分类目录', 'aksrt');
}
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<?php if (function_exists('aksrt_breadcrumbs')) {
aksrt_breadcrumbs();
} ?>
<header class="page-header aksrt-cats-header">
<h1 class="entry-title"><?php echo esc_html($page_title); ?></h1>
<p class="aksrt-cats-header__meta">
<?php
printf(
/* translators: 1: total category count, 2: total published post count */
esc_html__('共 %1$d 个分类 · 共 %2$d 篇文章', 'aksrt'),
$total_cats,
$total_posts
);
?>
</p>
</header>
<?php if (!empty($top_level)) : ?>
<section class="aksrt-cats-grid" aria-label="<?php esc_attr_e('分类列表', 'aksrt'); ?>">
<?php foreach ($top_level as $cat) :
$cat_id = (int) $cat->term_id;
$cat_link = get_category_link($cat_id);
$cover_url = class_exists('AKSRT_Settings')
? AKSRT_Settings::get_category_cover_url($cat_id)
: (get_template_directory_uri() . '/assets/img/placeholder.svg');
$desc = class_exists('AKSRT_Settings')
? AKSRT_Settings::get_category_recommend_desc($cat_id)
: (string) $cat->description;
$children = $children_by_parent[$cat_id] ?? [];
?>
<article class="aksrt-cat-card">
<a class="aksrt-cat-card__cover"
href="<?php echo esc_url($cat_link); ?>"
style="background-image: url('<?php echo esc_url($cover_url); ?>');"
aria-label="<?php echo esc_attr($cat->name); ?>">
<span class="aksrt-cat-card__count-badge">
<?php printf(esc_html__('%d 篇', 'aksrt'), (int) $cat->count); ?>
</span>
</a>
<div class="aksrt-cat-card__body">
<h2 class="aksrt-cat-card__title">
<a href="<?php echo esc_url($cat_link); ?>"><?php echo esc_html($cat->name); ?></a>
</h2>
<?php if ($desc !== '') : ?>
<p class="aksrt-cat-card__desc"><?php echo esc_html(wp_trim_words(wp_strip_all_tags($desc), 40, '…')); ?></p>
<?php endif; ?>
<?php if (!empty($children)) : ?>
<ul class="aksrt-cat-card__children" aria-label="<?php esc_attr_e('子分类', 'aksrt'); ?>">
<?php foreach ($children as $child) : ?>
<li>
<a href="<?php echo esc_url(get_category_link((int) $child->term_id)); ?>">
<span class="aksrt-cat-card__child-name"><?php echo esc_html($child->name); ?></span>
<span class="aksrt-cat-card__child-count"><?php echo (int) $child->count; ?></span>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
</article>
<?php endforeach; ?>
</section>
<?php else : ?>
<div class="no-results aksrt-cats-empty">
<p><?php esc_html_e('暂无分类。', 'aksrt'); ?></p>
</div>
<?php endif; ?>
<p class="aksrt-cats-footer">
<a class="btn-outline" href="<?php echo esc_url(home_url('/')); ?>">
<?php esc_html_e('返回首页', 'aksrt'); ?>
</a>
</p>
</main>
<?php
get_footer();
+167
View File
@@ -0,0 +1,167 @@
<?php
/**
* Template Name: AKSRT - Document Navigation
* Template Post Type: page
*
* 文档导航页:文档站式导航。
* 支持侧边章节树、当前章节高亮、索引卡片、最近更新列表、基础搜索。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
// Read doc mode settings
$doc_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_docs_mode_settings() : [];
$default_order = $doc_settings['default_order'] ?? 'menu_order';
$presenter = class_exists('AKSRT_Docs_Presenter') ? AKSRT_Docs_Presenter::get_instance() : null;
// Get root-level pages
$pages = $presenter
? $presenter->get_root_pages($default_order)
: get_pages([
'sort_column' => $default_order === 'title' ? 'post_title' : ($default_order === 'date' ? 'post_date' : 'menu_order, post_title'),
'sort_order' => 'ASC',
'parent' => 0,
'hierarchical' => 0,
'post_status' => 'publish',
]);
// Recent updates
$recent_pages = $presenter
? $presenter->get_recent_pages(8)
: get_pages([
'sort_column' => 'post_modified',
'sort_order' => 'DESC',
'number' => 8,
'post_status' => 'publish',
]);
$show_sidebar = !empty($doc_settings['toc_enabled']) || !empty($recent_pages);
?>
<main id="main-content" class="container page-content-default" tabindex="-1">
<header class="aksrt-doc-nav-header">
<h1 class="aksrt-doc-nav-header__title"><?php echo esc_html(get_the_title() ?: __('文档导航', 'aksrt')); ?></h1>
<?php $desc = get_the_excerpt(); if ($desc) : ?>
<p class="aksrt-doc-nav-header__desc"><?php echo esc_html($desc); ?></p>
<?php endif; ?>
</header>
<div class="aksrt-doc-nav container" data-aksrt-doc-page-id="<?php echo (int) get_the_ID(); ?>">
<div class="content-wrap">
<div class="content-layout<?php echo $show_sidebar && !empty($recent_pages) ? '' : ' content-layout--no-sidebar'; ?>">
<div class="content-layout__primary">
<!-- Quick search -->
<div class="aksrt-doc-nav__search">
<input type="text" class="aksrt-doc-nav__search-input" placeholder="<?php esc_attr_e('搜索文档...', 'aksrt'); ?>" id="aksrt-doc-search">
<i class="fas fa-search aksrt-doc-nav__search-icon"></i>
</div>
<div class="aksrt-doc-search__results" data-aksrt-doc-search-results hidden></div>
<?php if (!empty($pages)) : ?>
<!-- Toolbar: category chips -->
<div class="aksrt-doc-toolbar">
<div class="aksrt-doc-toolbar__chips">
<button type="button" class="aksrt-doc-toolbar__chip is-active" data-cat-id="0"><?php esc_html_e('全部', 'aksrt'); ?></button>
<?php foreach ($pages as $p) : ?>
<button type="button" class="aksrt-doc-toolbar__chip" data-cat-id="<?php echo (int) $p->ID; ?>"><?php echo esc_html($p->post_title); ?></button>
<?php endforeach; ?>
</div>
<span class="aksrt-doc-toolbar__meta"><?php printf(esc_html__('%d 篇文档', 'aksrt'), count($pages)); ?></span>
</div>
<?php endif; ?>
<div class="aksrt-doc-nav__layout">
<!-- Document index cards -->
<div class="aksrt-doc-nav__main">
<?php if (!empty($pages)) : ?>
<div class="aksrt-doc-nav__grid">
<?php foreach ($pages as $p) :
$children = $presenter
? $presenter->get_flat_tree($p->ID, 1)
: get_pages([
'child_of' => $p->ID,
'sort_column' => 'menu_order',
'sort_order' => 'ASC',
'parent' => $p->ID,
'post_status' => 'publish',
]);
$child_count = count($children);
$thumb = get_the_post_thumbnail_url($p->ID, 'medium');
$excerpt = wp_trim_words($p->post_excerpt, 30, '...');
?>
<a href="<?php echo esc_url(get_permalink($p->ID)); ?>" class="aksrt-doc-index-card" data-cat-id="<?php echo (int) $p->ID; ?>">
<?php if ($thumb) : ?>
<div class="aksrt-doc-index-card__thumb">
<img src="<?php echo esc_url($thumb); ?>" alt=""<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
</div>
<?php else : ?>
<div class="aksrt-doc-index-card__icon">
<i class="fas fa-book-open"></i>
</div>
<?php endif; ?>
<div class="aksrt-doc-index-card__body">
<h3 class="aksrt-doc-index-card__title"><?php echo esc_html($p->post_title); ?></h3>
<?php if ($excerpt) : ?>
<p class="aksrt-doc-index-card__desc"><?php echo esc_html($excerpt); ?></p>
<?php endif; ?>
<div class="aksrt-doc-index-card__meta">
<?php if ($child_count > 0) : ?>
<span class="aksrt-doc-index-card__count">
<i class="fas fa-file-alt"></i>
<?php printf(esc_html__('%d 篇文章', 'aksrt'), $child_count); ?>
</span>
<?php endif; ?>
<span class="aksrt-doc-index-card__date">
<i class="fas fa-clock"></i>
<?php echo esc_html(get_the_date('', $p->ID)); ?>
</span>
</div>
</div>
<i class="fas fa-chevron-right aksrt-doc-index-card__arrow"></i>
</a>
<?php endforeach; ?>
</div>
<?php else : ?>
<div class="aksrt-doc-nav__empty">
<i class="fas fa-book-open"></i>
<p><?php esc_html_e('暂无文档页面。', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
<!-- Sidebar: Recent updates -->
<?php if ($show_sidebar && !empty($recent_pages)) : ?>
<aside class="aksrt-doc-nav__sidebar sidebar">
<div class="aksrt-doc-nav__recent">
<h3 class="aksrt-doc-nav__recent-title">
<i class="fas fa-clock"></i>
<?php esc_html_e('最近更新', 'aksrt'); ?>
</h3>
<ul class="aksrt-doc-nav__recent-list">
<?php foreach ($recent_pages as $rp) : ?>
<li class="aksrt-doc-nav__recent-item">
<a href="<?php echo esc_url(get_permalink($rp->ID)); ?>">
<span class="aksrt-doc-nav__recent-name"><?php echo esc_html($rp->post_title); ?></span>
<time class="aksrt-doc-nav__recent-date"><?php echo esc_html(get_the_date('', $rp->ID)); ?></time>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
</aside>
<?php endif; ?>
</div>
</div>
</div>
</main>
<?php
get_footer();
+220
View File
@@ -0,0 +1,220 @@
<?php
/**
* Template Name: AKSRT - Download Center
* Template Post Type: page
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$paged = max(1, (int) get_query_var('paged'));
$per_page = 12;
$type_filter = isset($_GET['resource_type']) ? sanitize_key((string) $_GET['resource_type']) : '';
$resource_type_labels = function_exists('aksrt_get_pay_types') ? aksrt_get_pay_types() : [];
unset($resource_type_labels[AKSRT_Domain_Model::PAY_TYPE_READING]);
if ($type_filter !== '' && !isset($resource_type_labels[$type_filter])) {
$type_filter = '';
}
$query_args = [
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => $per_page,
'paged' => $paged,
'orderby' => 'date',
'order' => 'DESC',
'ignore_sticky_posts' => true,
'meta_query' => [
[
'key' => AKSRT_Domain_Model::RESOURCE_MARKER_KEY,
'value' => '1',
],
],
];
if ($type_filter !== '') {
$query_args['meta_query'][] = [
'key' => AKSRT_Domain_Model::PAY_TYPE_KEY,
'value' => $type_filter,
];
}
$resource_query = new WP_Query($query_args);
$is_logged_in = is_user_logged_in();
$user_credits = 0;
$user_download_count = 0;
if ($is_logged_in) {
$uid = get_current_user_id();
if (class_exists('AKSRT_Credits')) {
$user_credits = AKSRT_Credits::balance($uid);
}
$dl_meta = get_user_meta($uid, '_aksrt_downloaded_resources', true);
$user_download_count = is_array($dl_meta) ? count($dl_meta) : 0;
}
$currency_name = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_currency_name() : __('积分', 'aksrt');
$dl_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_cms_features() : [];
$dl_requires_login = !empty($dl_settings['download_requires_login']);
?>
<main id="main-content" class="site-main container" tabindex="-1">
<?php get_template_part('templates/parts/download/download-hero'); ?>
<div class="aksrt-download-center container">
<div class="content-wrap">
<div class="content-layout">
<div class="content-layout__primary">
<div class="aksrt-dl-status-bar">
<?php if ($is_logged_in) : ?>
<div class="aksrt-dl-status-bar__item">
<i class="fas fa-coins" aria-hidden="true"></i>
<span class="aksrt-dl-status-bar__label"><?php echo esc_html($currency_name); ?></span>
<span class="aksrt-dl-status-bar__value"><?php echo esc_html(number_format_i18n($user_credits)); ?></span>
</div>
<div class="aksrt-dl-status-bar__item">
<i class="fas fa-download" aria-hidden="true"></i>
<span class="aksrt-dl-status-bar__label"><?php esc_html_e('已下载', 'aksrt'); ?></span>
<span class="aksrt-dl-status-bar__value"><?php echo esc_html(number_format_i18n($user_download_count)); ?></span>
</div>
<div class="aksrt-dl-status-bar__item aksrt-dl-status-bar__item--hint">
<i class="fas fa-info-circle" aria-hidden="true"></i>
<span><?php esc_html_e('点击资源卡片查看详情和下载状态', 'aksrt'); ?></span>
</div>
<?php else : ?>
<div class="aksrt-dl-status-bar__item aksrt-dl-status-bar__item--hint">
<i class="fas fa-lock" aria-hidden="true"></i>
<span>
<?php if ($dl_requires_login) : ?>
<?php esc_html_e('登录后可查看下载状态和积分余额', 'aksrt'); ?>
<?php else : ?>
<?php esc_html_e('登录后可管理下载记录和积分余额', 'aksrt'); ?>
<?php endif; ?>
</span>
</div>
<div class="aksrt-dl-status-bar__actions">
<a href="<?php echo esc_url(aksrt_get_login_url()); ?>" class="btn-outline btn-sm"><?php esc_html_e('登录', 'aksrt'); ?></a>
<?php if (get_option('users_can_register')) : ?>
<a href="<?php echo esc_url(aksrt_get_register_url()); ?>" class="btn-primary btn-sm"><?php esc_html_e('注册', 'aksrt'); ?></a>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<div class="aksrt-download-center__layout">
<div class="aksrt-download-center__main">
<?php if ($resource_query->have_posts()) : ?>
<div class="aksrt-download-center__grid">
<?php while ($resource_query->have_posts()) : $resource_query->the_post(); ?>
<article class="aksrt-download-card">
<?php if (has_post_thumbnail()) : ?>
<div class="aksrt-download-card__thumb">
<a href="<?php the_permalink(); ?>">
<?php the_post_thumbnail('medium'); ?>
</a>
</div>
<?php endif; ?>
<div class="aksrt-download-card__body">
<h3 class="aksrt-download-card__title">
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</h3>
<p class="aksrt-download-card__excerpt"><?php echo esc_html(wp_trim_words(get_the_excerpt(), 20)); ?></p>
<div class="aksrt-download-card__meta">
<span class="aksrt-download-card__date">
<i class="fas fa-clock"></i>
<?php echo esc_html(get_the_date()); ?>
</span>
<?php
$dl_count = (int) get_post_meta(get_the_ID(), AKSRT_Domain_Model::RESOURCE_DOWNLOAD_COUNT_KEY, true);
if ($dl_count > 0) :
?>
<span class="aksrt-download-card__downloads">
<i class="fas fa-download"></i>
<?php echo esc_html(number_format_i18n($dl_count)); ?>
</span>
<?php endif; ?>
</div>
<?php
$card_meta = function_exists('aksrt_get_resource_meta') ? aksrt_get_resource_meta(get_the_ID()) : [];
$card_price = (float) ($card_meta['price'] ?? 0);
$price_label = function_exists('aksrt_resource_price_label')
? aksrt_resource_price_label($card_price)
: ['text' => __('免费', 'aksrt'), 'free' => true];
?>
<div class="aksrt-download-card__footer">
<span class="aksrt-download-card__price<?php echo $price_label['free'] ? ' is-free' : ' is-paid'; ?>">
<?php echo esc_html($price_label['text']); ?>
</span>
<a href="<?php the_permalink(); ?>" class="aksrt-download-card__action">
<?php esc_html_e('查看详情', 'aksrt'); ?>
<i class="fas fa-arrow-right" aria-hidden="true"></i>
</a>
</div>
</div>
</article>
<?php endwhile; ?>
</div>
<?php
if (function_exists('aksrt_render_pagination')) {
aksrt_render_pagination($resource_query);
}
wp_reset_postdata();
?>
<?php else : ?>
<div class="aksrt-download-center__empty">
<i class="fas fa-folder-open"></i>
<p><?php esc_html_e('暂无可下载资源。', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
<aside class="aksrt-download-center__sidebar sidebar">
<?php get_template_part('templates/parts/download/download-permission-card'); ?>
<div class="aksrt-dl-sidebar-card">
<h3 class="aksrt-dl-sidebar-card__title">
<i class="fas fa-folder"></i>
<?php esc_html_e('资源类型', 'aksrt'); ?>
</h3>
<?php if (!empty($resource_type_labels)) : ?>
<ul class="aksrt-dl-sidebar-card__list">
<li class="<?php echo $type_filter === '' ? 'is-active' : ''; ?>">
<a href="<?php echo esc_url(get_permalink()); ?>">
<?php esc_html_e('全部资源', 'aksrt'); ?>
</a>
</li>
<?php foreach ($resource_type_labels as $type_key => $type_label) : ?>
<li class="<?php echo $type_filter === $type_key ? 'is-active' : ''; ?>">
<a href="<?php echo esc_url(add_query_arg('resource_type', $type_key, get_permalink())); ?>">
<?php echo esc_html($type_label); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<div class="aksrt-dl-sidebar-card">
<h3 class="aksrt-dl-sidebar-card__title">
<i class="fas fa-lightbulb"></i>
<?php esc_html_e('下载须知', 'aksrt'); ?>
</h3>
<div class="aksrt-dl-sidebar-card__tips">
<p><i class="fas fa-shield-alt" aria-hidden="true"></i> <?php esc_html_e('所有资源均经过安全检测', 'aksrt'); ?></p>
<p><i class="fas fa-clock" aria-hidden="true"></i> <?php esc_html_e('下载链接 10 分钟内有效', 'aksrt'); ?></p>
<p><i class="fas fa-sync-alt" aria-hidden="true"></i> <?php esc_html_e('已下载资源可再次下载', 'aksrt'); ?></p>
</div>
</div>
</div>
</div>
</div>
</main>
<?php
get_footer();
+331
View File
@@ -0,0 +1,331 @@
<?php
/**
* Template Name: AKSRT - Forums
* Template Post Type: page
*
* §7.10 — Tab engine + quick post + plate grid + follow/synthesis/pending streams.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$forum = class_exists('AKSRT_Forum') ? AKSRT_Forum::get_instance() : null;
$current_user_id = get_current_user_id();
$view = $forum ? $forum->get_home_view($current_user_id) : [
'boards' => [],
'recent_topics' => [],
'hot_topics' => [],
'stats' => [],
'permissions' => ['can_compose' => false, 'can_manage' => false],
'applications' => [],
'home_tabs' => [],
'active_index' => 1,
'quick_posts' => true,
];
$boards = $view['boards'] ?? [];
$recent_topics = $view['recent_topics'] ?? [];
$hot_topics = $view['hot_topics'] ?? [];
$stats = $view['stats'] ?? [];
$permissions = $view['permissions'] ?? [];
$applications = $view['applications'] ?? [];
$compose_url = home_url('/compose/');
$forum_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_forum_settings() : [];
$forum_names = $forum_settings['names'] ?? [];
$name_forum = $forum_names['forum'] ?? __('论坛', 'aksrt');
$name_plate = $forum_names['plate'] ?? __('版块', 'aksrt');
$name_topic = $forum_names['posts'] ?? __('主题', 'aksrt');
$name_reply = $forum_names['comment'] ?? __('回复', 'aksrt');
$admin_forum_url = admin_url('admin.php?page=aksrt-theme-admin#/forum');
$home_tabs = $view['home_tabs'] ?? [];
$active_index = max(0, min(count($home_tabs) - 1, (int) ($view['active_index'] ?? 1)));
$quick_posts = !empty($view['quick_posts']);
$nonce = wp_create_nonce('aksrt_public_nonce');
// Resolve active tab from query param or default
$active_tab_type = isset($_GET['forum_tab']) ? sanitize_key(wp_unslash($_GET['forum_tab'])) : '';
if ($active_tab_type === '' && isset($home_tabs[$active_index])) {
$active_tab_type = $home_tabs[$active_index]['type'] ?? 'synthesis';
}
if ($active_tab_type === '') {
$active_tab_type = 'synthesis';
}
?>
<main id="main-content" class="site-main container" tabindex="-1">
<header class="aksrt-forum-header">
<div class="aksrt-forum-header__content">
<h1 class="aksrt-forum-header__title"><?php echo esc_html(get_the_title() ?: __('社区论坛', 'aksrt')); ?></h1>
<?php if (get_the_excerpt()) : ?>
<p class="aksrt-forum-header__desc"><?php echo esc_html(get_the_excerpt()); ?></p>
<?php endif; ?>
<div class="aksrt-forum-header__actions">
<?php if (!empty($permissions['can_compose'])) : ?>
<a class="aksrt-forum-header__action aksrt-forum-header__action--primary" href="<?php echo esc_url($compose_url); ?>">
<i class="fas fa-pen"></i> <?php esc_html_e('发布主题', 'aksrt'); ?>
</a>
<?php else : ?>
<a class="aksrt-forum-header__action aksrt-forum-header__action--primary" href="<?php echo esc_url(wp_login_url(get_permalink())); ?>">
<i class="fas fa-sign-in-alt"></i> <?php esc_html_e('登录后发帖', 'aksrt'); ?>
</a>
<?php endif; ?>
<?php if (!empty($permissions['can_manage'])) : ?>
<a class="aksrt-forum-header__action" href="<?php echo esc_url($admin_forum_url); ?>">
<i class="fas fa-tools"></i> <?php esc_html_e('进入治理后台', 'aksrt'); ?>
</a>
<?php endif; ?>
</div>
</div>
<div class="aksrt-forum-header__stats">
<div class="aksrt-forum-header__stat">
<span class="aksrt-forum-header__stat-num"><?php echo esc_html(number_format_i18n((int) ($stats['board_count'] ?? 0))); ?></span>
<span class="aksrt-forum-header__stat-label"><?php echo esc_html($name_plate); ?></span>
</div>
<div class="aksrt-forum-header__stat">
<span class="aksrt-forum-header__stat-num"><?php echo esc_html(number_format_i18n((int) ($stats['topic_count'] ?? 0))); ?></span>
<span class="aksrt-forum-header__stat-label"><?php echo esc_html($name_topic); ?></span>
</div>
<div class="aksrt-forum-header__stat">
<span class="aksrt-forum-header__stat-num"><?php echo esc_html(number_format_i18n((int) ($stats['post_count'] ?? 0))); ?></span>
<span class="aksrt-forum-header__stat-label"><?php echo esc_html($name_reply); ?></span>
</div>
<div class="aksrt-forum-header__stat">
<span class="aksrt-forum-header__stat-num"><?php echo esc_html(number_format_i18n((int) ($stats['moderator_count'] ?? 0))); ?></span>
<span class="aksrt-forum-header__stat-label"><?php esc_html_e('版主', 'aksrt'); ?></span>
</div>
</div>
</header>
<?php if (isset($_GET['compose_status']) && $_GET['compose_status'] === 'pending') : ?>
<div class="aksrt-alert aksrt-alert--info" data-aksrt-flash-query="compose_status">
<?php esc_html_e('你的主题已提交审核,审核通过后会在版块中展示。', 'aksrt'); ?>
</div>
<?php endif; ?>
<div class="aksrt-forum container">
<div class="content-wrap">
<div class="content-layout content-layout--no-sidebar">
<div class="content-layout__primary">
<div class="aksrt-forum__layout">
<div class="aksrt-forum__main">
<?php if ($quick_posts && !empty($permissions['can_compose'])) : ?>
<div class="aksrt-forum-quick-post">
<a class="aksrt-forum-quick-post__input" href="<?php echo esc_url($compose_url); ?>">
<?php echo get_avatar($current_user_id, 32); ?>
<span><?php esc_html_e('分享你的想法…', 'aksrt'); ?></span>
</a>
</div>
<?php endif; ?>
<?php if (!empty($home_tabs)) : ?>
<nav class="aksrt-forum-home-tabs" id="aksrt-forum-home-tabs"
data-nonce="<?php echo esc_attr($nonce); ?>"
data-ajax-url="<?php echo esc_attr(admin_url('admin-ajax.php')); ?>">
<?php foreach ($home_tabs as $i => $tab) :
$tab_type = sanitize_key($tab['type'] ?? 'synthesis');
$tab_label = esc_html($tab['label'] ?? $tab_type);
$is_active = $tab_type === $active_tab_type;
if ($tab_type === 'pending' && empty($permissions['can_manage'])) continue;
?>
<button type="button"
class="aksrt-forum-home-tabs__btn<?php echo $is_active ? ' is-active' : ''; ?>"
data-tab-type="<?php echo esc_attr($tab_type); ?>">
<?php echo $tab_label; ?>
</button>
<?php endforeach; ?>
<?php if (!empty($permissions['can_manage'])) : ?>
<button type="button"
class="aksrt-forum-home-tabs__btn<?php echo $active_tab_type === 'pending' ? ' is-active' : ''; ?>"
data-tab-type="pending">
<?php esc_html_e('待审核', 'aksrt'); ?>
<?php if ((int) ($stats['pending_topic_count'] ?? 0) > 0) : ?>
<span class="aksrt-forum-home-tabs__badge"><?php echo esc_html((string) ($stats['pending_topic_count'] ?? 0)); ?></span>
<?php endif; ?>
</button>
<?php endif; ?>
</nav>
<?php endif; ?>
<div class="aksrt-forum-home-content" id="aksrt-forum-home-content">
<?php if ($active_tab_type === 'plate') : ?>
<?php if (!empty($boards)) : ?>
<div class="aksrt-forum-boards__grid">
<?php foreach ($boards as $board) :
$board_id = (int) ($board['id'] ?? 0);
?>
<article class="aksrt-forum-board-card">
<a class="aksrt-forum-board-card__link" href="<?php echo esc_url((string) ($board['url'] ?? '#')); ?>">
<div class="aksrt-forum-board-card__icon">
<?php if (!empty($board['thumbnail_url'])) : ?>
<img src="<?php echo esc_url((string) $board['thumbnail_url']); ?>" alt="" width="48" height="48">
<?php else : ?>
<i class="<?php echo esc_attr((string) ($board['icon'] ?: 'fas fa-comments')); ?>"></i>
<?php endif; ?>
</div>
<div class="aksrt-forum-board-card__body">
<div class="aksrt-forum-board-card__head">
<h3 class="aksrt-forum-board-card__title"><?php echo esc_html((string) ($board['name'] ?? '')); ?></h3>
</div>
<?php if (!empty($board['description'])) : ?>
<p class="aksrt-forum-board-card__desc"><?php echo esc_html((string) $board['description']); ?></p>
<?php endif; ?>
<div class="aksrt-forum-board-card__meta">
<span><i class="fas fa-file-alt"></i> <?php echo esc_html(number_format_i18n((int) ($board['topic_count'] ?? $board['post_count'] ?? 0))); ?></span>
<span><i class="fas fa-comment"></i> <?php echo esc_html(number_format_i18n((int) ($board['reply_count'] ?? 0))); ?></span>
<span><i class="fas fa-heart"></i> <?php echo esc_html(number_format_i18n((int) ($board['follow_count'] ?? 0))); ?></span>
</div>
</div>
</a>
</article>
<?php endforeach; ?>
</div>
<?php else : ?>
<div class="aksrt-forum__empty">
<i class="fas fa-comments"></i>
<p><?php esc_html_e('当前还没有论坛版块。', 'aksrt'); ?></p>
</div>
<?php endif; ?>
<?php elseif ($active_tab_type === 'follow') : ?>
<?php
$follow_data = $forum ? $forum->get_follow_stream($current_user_id) : ['items' => []];
if (!empty($follow_data['items'])) : ?>
<div class="aksrt-forum-topics__list js-aksrt-home-topic-list">
<?php foreach ($follow_data['items'] as $topic) { $forum->render_topic_row($topic); } ?>
</div>
<?php if ($follow_data['max_pages'] > 1) : ?>
<div class="aksrt-forum-home__load-more">
<button type="button" class="aksrt-forum-replies__more-btn js-aksrt-home-load-more"
data-page="1" data-max="<?php echo esc_attr((string) $follow_data['max_pages']); ?>"
data-tab="follow">
<i class="fas fa-chevron-down"></i> <?php esc_html_e('加载更多', 'aksrt'); ?>
</button>
</div>
<?php endif; ?>
<?php elseif ($current_user_id > 0) : ?>
<div class="aksrt-forum__empty">
<i class="far fa-heart"></i>
<p><?php esc_html_e('你还没有关注任何版块,去看看有哪些版块吧!', 'aksrt'); ?></p>
</div>
<?php else : ?>
<div class="aksrt-forum__empty">
<i class="fas fa-sign-in-alt"></i>
<p><?php esc_html_e('登录后即可看到你关注的版块内容。', 'aksrt'); ?></p>
</div>
<?php endif; ?>
<?php elseif ($active_tab_type === 'pending') : ?>
<?php
$pending_data = $forum ? $forum->get_pending_stream($current_user_id) : ['items' => []];
if (!empty($pending_data['items'])) : ?>
<div class="aksrt-forum-topics__list js-aksrt-home-topic-list">
<?php foreach ($pending_data['items'] as $topic) { $forum->render_topic_row($topic); } ?>
</div>
<?php else : ?>
<div class="aksrt-forum__empty">
<i class="fas fa-check-circle"></i>
<p><?php esc_html_e('没有待审核的主题。', 'aksrt'); ?></p>
</div>
<?php endif; ?>
<?php else : /* synthesis (default) */ ?>
<?php if (!empty($recent_topics)) : ?>
<div class="aksrt-forum-topics__list js-aksrt-home-topic-list">
<?php foreach ($recent_topics as $topic) {
if ($forum) { $forum->render_topic_row($topic); }
} ?>
</div>
<?php else : ?>
<div class="aksrt-forum__empty">
<i class="fas fa-feather-alt"></i>
<p><?php esc_html_e('还没有公开主题,欢迎创建第一个讨论。', 'aksrt'); ?></p>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
<aside class="aksrt-forum__sidebar">
<div class="aksrt-forum-sidebar-card">
<h3 class="aksrt-forum-sidebar-card__title">
<i class="fas fa-fire"></i>
<?php printf(esc_html__('热门%s', 'aksrt'), esc_html($name_topic)); ?>
</h3>
<?php if (!empty($hot_topics)) : ?>
<ul class="aksrt-forum-sidebar-card__list">
<?php foreach ($hot_topics as $index => $topic) : ?>
<li class="aksrt-forum-sidebar-card__item">
<span class="aksrt-forum-sidebar-card__rank"><?php echo esc_html((string) ($index + 1)); ?></span>
<a href="<?php echo esc_url((string) ($topic['permalink'] ?? '#')); ?>" class="aksrt-forum-sidebar-card__link">
<?php echo esc_html((string) ($topic['title'] ?? '')); ?>
</a>
<span class="aksrt-forum-sidebar-card__views"><?php echo esc_html(number_format_i18n((int) ($topic['views'] ?? 0))); ?></span>
</li>
<?php endforeach; ?>
</ul>
<?php else : ?>
<p class="aksrt-forum-sidebar-card__empty"><?php esc_html_e('暂无热门主题', 'aksrt'); ?></p>
<?php endif; ?>
</div>
<?php if (!empty($boards)) : ?>
<div class="aksrt-forum-sidebar-card">
<h3 class="aksrt-forum-sidebar-card__title">
<i class="fas fa-th-list"></i>
<?php echo esc_html($name_plate); ?>
</h3>
<ul class="aksrt-forum-sidebar-card__list">
<?php foreach (array_slice($boards, 0, 8) as $board) : ?>
<li class="aksrt-forum-sidebar-card__item">
<a href="<?php echo esc_url((string) ($board['url'] ?? '#')); ?>" class="aksrt-forum-sidebar-card__link">
<?php echo esc_html((string) ($board['name'] ?? '')); ?>
</a>
<span class="aksrt-forum-sidebar-card__views"><?php echo esc_html(number_format_i18n((int) ($board['topic_count'] ?? $board['post_count'] ?? 0))); ?></span>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<?php if (!empty($applications)) : ?>
<div class="aksrt-forum-sidebar-card">
<h3 class="aksrt-forum-sidebar-card__title">
<i class="fas fa-shield-alt"></i>
<?php esc_html_e('版主申请状态', 'aksrt'); ?>
</h3>
<ul class="aksrt-forum-sidebar-card__list aksrt-forum-sidebar-card__list--applications">
<?php foreach ($applications as $board_id => $application) :
if (!$application) continue;
$board_name = '';
foreach ($boards as $board) {
if ((int) ($board['id'] ?? 0) === (int) $board_id) {
$board_name = (string) ($board['name'] ?? '');
break;
}
}
?>
<li class="aksrt-forum-sidebar-card__item aksrt-forum-sidebar-card__item--stack">
<strong><?php echo esc_html($board_name); ?></strong>
<span><?php echo esc_html((string) ($application['status_label'] ?? '')); ?></span>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
</aside>
</div>
</div>
</div>
</div>
</div>
</main>
<?php get_footer(); ?>
+61
View File
@@ -0,0 +1,61 @@
<?php
/**
* Template Name: 友情链接
* Template Post Type: page
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$links = function_exists('aksrt_get_friend_links') ? aksrt_get_friend_links() : [];
?>
<main id="main-content" class="container page-content-default" tabindex="-1">
<header class="page-header">
<h1 class="entry-title"><?php esc_html_e('友情链接', 'aksrt'); ?></h1>
<p><?php esc_html_e('与这些优秀的站点交换友链', 'aksrt'); ?></p>
</header>
<div class="content-wrap">
<div class="content-layout content-layout--no-sidebar">
<div class="content-layout__primary">
<?php if (! empty($links)) : ?>
<div class="friends-grid">
<?php foreach ($links as $link) : ?>
<a class="friend-card" href="<?php echo esc_url($link['url'] ?? '#'); ?>" target="_blank" rel="noopener noreferrer">
<?php
$logo = $link['logo'] ?? '';
if ($logo) {
echo '<img class="friend-card__avatar" src="' . esc_url($logo) . '" alt="">';
} else {
$initial = function_exists('mb_substr') ? mb_substr((string) ($link['name'] ?? 'A'), 0, 1, 'UTF-8') : substr((string) ($link['name'] ?? 'A'), 0, 1);
echo '<div class="friend-card__avatar" style="display:flex;align-items:center;justify-content:center;font-weight:800;color:var(--color-primary);font-family:var(--font-heading);">' . esc_html($initial) . '</div>';
}
?>
<div class="friend-card__info">
<h3 class="friend-card__name"><?php echo esc_html($link['name'] ?? ''); ?></h3>
<p class="friend-card__desc"><?php echo esc_html($link['description'] ?? ''); ?></p>
</div>
</a>
<?php endforeach; ?>
</div>
<?php else : ?>
<div class="no-results">
<p><?php esc_html_e('暂无友链,请在主题后台添加。', 'aksrt'); ?></p>
</div>
<?php endif; ?>
<p style="text-align:center;margin-top:32px;">
<a class="btn-outline" href="<?php echo esc_url(home_url('/')); ?>"><?php esc_html_e('返回首页', 'aksrt'); ?></a>
</p>
</div>
</div>
</div>
</main>
<?php
get_footer();
+11
View File
@@ -0,0 +1,11 @@
<?php
/**
* Template Name: AKSRT - Login
* Template Post Type: page
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
require AKSRT_DIR . '/templates/pages/page-sign.php';
+236
View File
@@ -0,0 +1,236 @@
<?php
/**
* Template Name: 消息中心
* Template Post Type: page
*
* 独立消息中心页面 — 两栏布局(左侧导航 + 右侧消息列表)
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
if ( ! is_user_logged_in() ) {
$login_url = function_exists( 'aksrt_get_login_url' ) ? aksrt_get_login_url() : wp_login_url( get_permalink() );
wp_safe_redirect( $login_url );
exit;
}
$user_id = get_current_user_id();
// 获取所有通知(持久化 + 即时状态)
$all_notifications = function_exists( 'aksrt_get_user_all_notifications' )
? aksrt_get_user_all_notifications( $user_id )
: [];
// 筛选类型
$current_type = isset( $_GET['msg_type'] ) ? sanitize_key( $_GET['msg_type'] ) : 'all';
// 按类型过滤
$filtered = $all_notifications;
if ( $current_type === 'unread' ) {
$filtered = array_filter( $filtered, static fn( $n ) => empty( $n['read'] ) );
} elseif ( $current_type !== 'all' ) {
$filtered = array_filter( $filtered, static fn( $n ) => ( $n['type'] ?? '' ) === $current_type );
}
$filtered = array_values( $filtered );
// 分页
$per_page = 20;
$current_page = max( 1, (int) ( $_GET['msg_page'] ?? 1 ) );
$total = count( $filtered );
$total_pages = max( 1, (int) ceil( $total / $per_page ) );
$offset = ( $current_page - 1 ) * $per_page;
$paged_items = array_slice( $filtered, $offset, $per_page );
// 统计各类型数量
$type_counts = [ 'all' => count( $all_notifications ), 'unread' => 0 ];
foreach ( $all_notifications as $n ) {
$t = $n['type'] ?? 'notice';
$type_counts[ $t ] = ( $type_counts[ $t ] ?? 0 ) + 1;
if ( empty( $n['read'] ) ) {
$type_counts['unread']++;
}
}
// 导航定义
$nav_items = [
'all' => __( '全部消息', 'aksrt' ),
'unread' => __( '未读', 'aksrt' ),
'conversation' => __( '私信', 'aksrt' ),
'comment_reply' => __( '评论回复', 'aksrt' ),
'comment_approved' => __( '评论审核', 'aksrt' ),
'credits_added' => __( '积分增加', 'aksrt' ),
'credits_deducted' => __( '积分扣减', 'aksrt' ),
'download_ready' => __( '下载就绪', 'aksrt' ),
'upload_moderation'=> __( '图片审核', 'aksrt' ),
'notice' => __( '系统通知', 'aksrt' ),
];
// 图标映射
$icon_map = [
'comment_reply' => 'fas fa-comment-dots',
'comment_approved' => 'fas fa-check-circle',
'credits_added' => 'fas fa-coins',
'credits_deducted' => 'fas fa-coins',
'download_ready' => 'fas fa-download',
'upload_moderation'=> 'fas fa-image',
'verify_email' => 'fas fa-envelope-open-text',
'pending_comments' => 'fas fa-clock',
'conversation' => 'fas fa-envelope',
'notice' => 'fas fa-bell',
];
?>
<div class="messages-center container">
<aside class="messages-center__nav">
<ul class="messages-nav__list">
<?php foreach ( $nav_items as $key => $label ) :
$count = (int) ( $type_counts[ $key ] ?? 0 );
if ( $key !== 'all' && $key !== 'unread' && $count < 1 ) continue;
$is_active = $current_type === $key;
?>
<li>
<a href="<?php echo esc_url( add_query_arg( [ 'msg_type' => $key, 'msg_page' => 1 ] ) ); ?>"
class="messages-nav__item<?php echo $is_active ? ' is-active' : ''; ?>"
data-type="<?php echo esc_attr( $key ); ?>">
<span><?php echo esc_html( $label ); ?></span>
<?php if ( $count > 0 ) : ?>
<span class="messages-nav__badge" data-type="<?php echo esc_attr( $key ); ?>" data-count="<?php echo esc_attr( (string) $count ); ?>"><?php echo esc_html( (string) $count ); ?></span>
<?php endif; ?>
</a>
</li>
<?php endforeach; ?>
</ul>
<div class="messages-nav__actions">
<button type="button" class="messages-nav__action-btn js-msg-mark-all-read"><?php esc_html_e( '全部已读', 'aksrt' ); ?></button>
<button type="button" class="messages-nav__action-btn js-msg-clear-read"><?php esc_html_e( '清除已读', 'aksrt' ); ?></button>
</div>
</aside>
<main class="messages-center__content">
<?php if ( $current_type === 'conversation' ) :
$pm = class_exists( 'AKSRT_Private_Message' ) ? AKSRT_Private_Message::get_instance() : null;
$conversations = $pm ? $pm->get_conversations( $user_id, 30 ) : [];
$pm_nonce = wp_create_nonce( 'aksrt_pm' );
?>
<div class="messages-conversations" id="aksrt-pm-conversations"
data-ajax="<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>"
data-nonce="<?php echo esc_attr( $pm_nonce ); ?>">
<?php if ( ! empty( $conversations ) ) : ?>
<div class="aksrt-pm__list">
<?php foreach ( $conversations as $conv ) : ?>
<div class="aksrt-pm__item<?php echo $conv['unread'] > 0 ? ' is-unread' : ''; ?>"
data-conv-id="<?php echo esc_attr( (string) $conv['id'] ); ?>"
data-other-id="<?php echo esc_attr( (string) $conv['other_user_id'] ); ?>">
<img class="aksrt-pm__avatar" src="<?php echo esc_url( $conv['other_avatar'] ); ?>" alt=""<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('avatar') : ' loading="lazy"'; ?> />
<div class="aksrt-pm__info">
<div class="aksrt-pm__name">
<?php echo esc_html( $conv['other_name'] ); ?>
<?php if ( $conv['unread'] > 0 ) : ?>
<span class="aksrt-pm__badge"><?php echo esc_html( (string) $conv['unread'] ); ?></span>
<?php endif; ?>
</div>
<div class="aksrt-pm__preview"><?php echo esc_html( mb_substr( $conv['last_message'], 0, 50 ) ); ?></div>
</div>
<div class="aksrt-pm__time"><?php echo esc_html( $conv['last_message_at'] ? mysql2date( 'm-d H:i', $conv['last_message_at'] ) : '' ); ?></div>
</div>
<?php endforeach; ?>
</div>
<?php else : ?>
<div class="messages-empty">
<span class="messages-empty__icon"><i class="fas fa-envelope"></i></span>
<p class="messages-empty__text"><?php esc_html_e( '暂无私信', 'aksrt' ); ?></p>
<p class="messages-empty__hint"><?php esc_html_e( '你可以在用户主页发起私信。', 'aksrt' ); ?></p>
</div>
<?php endif; ?>
</div>
<div class="aksrt-pm__thread" id="aksrt-pm-thread" style="display:none;">
<div class="aksrt-pm__thread-header">
<button type="button" class="aksrt-pm__back-btn" id="aksrt-pm-back"><i class="fas fa-arrow-left"></i> <?php esc_html_e( '返回', 'aksrt' ); ?></button>
<span class="aksrt-pm__thread-name" id="aksrt-pm-thread-name"></span>
</div>
<div class="aksrt-pm__messages" id="aksrt-pm-messages"></div>
<div class="aksrt-pm__compose">
<textarea id="aksrt-pm-input" rows="2" placeholder="<?php esc_attr_e( '输入消息...', 'aksrt' ); ?>"></textarea>
<button type="button" id="aksrt-pm-send" class="aksrt-pm__send-btn"><i class="fas fa-paper-plane"></i></button>
</div>
</div>
<?php /* conversation.js is enqueued via AKSRT_Scripts */ ?>
<?php elseif ( ! empty( $paged_items ) ) : ?>
<div class="messages-list">
<?php foreach ( $paged_items as $notice ) :
$n_type = (string) ( $notice['type'] ?? 'notice' );
$n_title = (string) ( $notice['title'] ?? '' );
$n_desc = (string) ( $notice['desc'] ?? '' );
$n_url = (string) ( $notice['url'] ?? '' );
$n_id = (string) ( $notice['id'] ?? '' );
$is_read = ! empty( $notice['read'] );
$is_state = ! empty( $notice['stateful'] );
$created = (string) ( $notice['created_at'] ?? '' );
$time_str = $is_state
? __( '状态提醒', 'aksrt' )
: ( $created !== '' ? get_date_from_gmt( $created, 'Y-m-d H:i' ) : '' );
$icon_cls = $icon_map[ $n_type ] ?? 'fas fa-bell';
?>
<div class="message-item<?php echo ! $is_read ? ' is-unread' : ''; ?>" data-notification-id="<?php echo esc_attr( $n_id ); ?>">
<span class="message-item__icon">
<i class="<?php echo esc_attr( $icon_cls ); ?>" aria-hidden="true"></i>
</span>
<div class="message-item__body">
<div class="message-item__title">
<?php echo esc_html( $n_title ); ?>
<?php if ( ! $is_read && ! $is_state ) : ?>
<span class="message-item__new-badge">NEW</span>
<?php endif; ?>
</div>
<?php if ( $n_desc !== '' ) : ?>
<div class="message-item__desc"><?php echo esc_html( $n_desc ); ?></div>
<?php endif; ?>
<?php if ( $time_str !== '' ) : ?>
<div class="message-item__time"><?php echo esc_html( $time_str ); ?></div>
<?php endif; ?>
</div>
<div class="message-item__actions">
<?php if ( $n_url !== '' ) : ?>
<a href="<?php echo esc_url( $n_url ); ?>" class="message-item__action-btn"><?php esc_html_e( '查看', 'aksrt' ); ?></a>
<?php endif; ?>
<?php if ( ! $is_state && ! $is_read ) : ?>
<button type="button" class="message-item__action-btn js-msg-mark-read" data-id="<?php echo esc_attr( $n_id ); ?>"><?php esc_html_e( '已读', 'aksrt' ); ?></button>
<?php endif; ?>
<?php if ( ! $is_state ) : ?>
<button type="button" class="message-item__action-btn message-item__action-btn--danger js-msg-delete" data-id="<?php echo esc_attr( $n_id ); ?>"><?php esc_html_e( '删除', 'aksrt' ); ?></button>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if ( $total_pages > 1 ) : ?>
<div class="messages-pagination">
<?php for ( $i = 1; $i <= $total_pages; $i++ ) :
$page_url = add_query_arg( [ 'msg_type' => $current_type, 'msg_page' => $i ] );
?>
<?php if ( $i === $current_page ) : ?>
<span class="profile-status-chip is-active"><?php echo esc_html( (string) $i ); ?></span>
<?php else : ?>
<a href="<?php echo esc_url( $page_url ); ?>" class="profile-status-chip"><?php echo esc_html( (string) $i ); ?></a>
<?php endif; ?>
<?php endfor; ?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="messages-empty">
<span class="messages-empty__icon"><i class="fas fa-bell-slash"></i></span>
<p class="messages-empty__text"><?php esc_html_e( '暂无消息', 'aksrt' ); ?></p>
</div>
<?php endif; ?>
</main>
</div>
<?php get_footer(); ?>
+67
View File
@@ -0,0 +1,67 @@
<?php
/**
* Template Name: AKSRT - New Posts
* Template Post Type: page
*
* 最新文章页:按时间倒序展示最新发布的文章。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$paged = max(1, (int) get_query_var('paged'));
$per_page = 20;
$query = new WP_Query([
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => $per_page,
'paged' => $paged,
'orderby' => 'date',
'order' => 'DESC',
]);
?>
<main id="main-content" class="container page-content-default" tabindex="-1">
<header class="page-header">
<h1 class="entry-title"><?php echo esc_html(get_the_title() ?: __('最新文章', 'aksrt')); ?></h1>
</header>
<div class="content-wrap">
<div class="content-layout content-layout--no-sidebar">
<div class="content-layout__primary">
<div class="aksrt-newposts container">
<?php if ($query->have_posts()) : ?>
<div class="aksrt-newposts__list">
<?php while ($query->have_posts()) : $query->the_post(); ?>
<article class="aksrt-newposts__item">
<span class="aksrt-newposts__date"><?php echo esc_html(get_the_date()); ?></span>
<h3 class="aksrt-newposts__title">
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</h3>
<p class="aksrt-newposts__excerpt"><?php echo esc_html(wp_trim_words(get_the_excerpt(), 30)); ?></p>
</article>
<?php endwhile; ?>
</div>
<?php
if (function_exists('aksrt_render_pagination')) {
aksrt_render_pagination($query);
} else {
echo '<div class="aksrt-pagination">' . paginate_links(['total' => $query->max_num_pages]) . '</div>';
}
?>
<?php wp_reset_postdata(); ?>
<?php else : ?>
<p class="aksrt-newposts__empty"><?php esc_html_e('暂无文章。', 'aksrt'); ?></p>
<?php endif; ?>
</div>
</div>
</div>
</div>
</main>
<?php
get_footer();
+253
View File
@@ -0,0 +1,253 @@
<?php
/**
* Template Name: AKSRT - Orders
* Template Post Type: page
*
* Enhanced: 5 tabs, logistics timeline, receipt confirm,
* review button, re-purchase, gift/discount details (§6.10).
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
if (!is_user_logged_in()) {
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink());
wp_safe_redirect($login_url);
exit;
}
$user_id = get_current_user_id();
$per_page = 20;
$paged = max(1, (int) ($_GET['order_page'] ?? 1));
$tab = sanitize_key((string) ($_GET['tab'] ?? ''));
$status = sanitize_key((string) ($_GET['order_status'] ?? ''));
$tab_status_map = [
'pending' => AKSRT_Order::STATUS_PENDING,
'shipped' => AKSRT_Order::STATUS_DELIVERED,
'received' => AKSRT_Order::STATUS_COMPLETED,
'review' => AKSRT_Order::STATUS_PENDING_REVIEW,
'after' => AKSRT_Order::STATUS_REFUNDING,
];
if ($tab !== '' && isset($tab_status_map[$tab])) {
$status = $tab_status_map[$tab];
}
$orders_view = function_exists('aksrt_get_orders_page_view')
? aksrt_get_orders_page_view($user_id, $paged, $per_page, $status)
: ['items' => [], 'total' => 0, 'total_pages' => 1, 'page' => 1];
$orders = (array) ($orders_view['items'] ?? []);
$total_pages = max(1, (int) ($orders_view['total_pages'] ?? 1));
$shop = class_exists('AKSRT_Shop') ? AKSRT_Shop::get_instance() : null;
$shipping = class_exists('AKSRT_Shipping') ? AKSRT_Shipping::get_instance() : null;
$tabs = [
'' => __('全部', 'aksrt'),
'pending' => __('待支付', 'aksrt'),
'shipped' => __('待收货', 'aksrt'),
'review' => __('待评价', 'aksrt'),
'after' => __('售后', 'aksrt'),
];
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<div class="container aksrt-orders-page" data-aksrt-order-center>
<div class="aksrt-orders-page__header">
<h1><?php esc_html_e('我的订单', 'aksrt'); ?></h1>
</div>
<!-- 5-tab navigation -->
<div class="aksrt-orders-page__tabs">
<?php foreach ($tabs as $tab_key => $tab_label) :
$is_active = $tab === $tab_key;
$tab_url = $tab_key !== '' ? add_query_arg(['tab' => $tab_key, 'order_page' => 1]) : remove_query_arg(['tab', 'order_status', 'order_page']);
?>
<a href="<?php echo esc_url($tab_url); ?>" class="aksrt-orders-page__tab<?php echo $is_active ? ' is-active' : ''; ?>">
<?php echo esc_html($tab_label); ?>
</a>
<?php endforeach; ?>
</div>
<?php if (!empty($orders)) : ?>
<div class="aksrt-orders-page__list">
<?php foreach ($orders as $order) :
$on = (string) ($order['order_number'] ?? '');
$os = (string) ($order['status'] ?? '');
$items = $order['items'] ?? [];
?>
<article class="aksrt-orders-page__card" data-aksrt-order-card="<?php echo esc_attr($on); ?>">
<!-- Card header -->
<div class="aksrt-orders-page__card-head">
<span class="aksrt-orders-page__card-number"><?php echo esc_html($on); ?></span>
<time class="aksrt-orders-page__card-time"><?php echo esc_html((string) ($order['created_at'] ?? '')); ?></time>
<span class="aksrt-orders-page__card-status"><?php echo esc_html((string) ($order['status_label'] ?? '')); ?></span>
</div>
<!-- Order items -->
<div class="aksrt-orders-page__card-items">
<?php if (!empty($items)) : ?>
<?php foreach ($items as $item) : ?>
<div class="aksrt-orders-page__card-item">
<?php if (!empty($item['image'])) : ?>
<img src="<?php echo esc_url((string) $item['image']); ?>" alt="" class="aksrt-orders-page__card-img">
<?php endif; ?>
<div class="aksrt-orders-page__card-item-info">
<span class="aksrt-orders-page__card-item-title"><?php echo esc_html((string) ($item['product_title'] ?? '')); ?></span>
<?php if (!empty($item['options_label'])) : ?>
<small class="aksrt-orders-page__card-item-opts"><?php echo esc_html((string) $item['options_label']); ?></small>
<?php endif; ?>
</div>
<div class="aksrt-orders-page__card-item-price">
<strong>¥<?php echo esc_html(number_format((float) ($item['pay_price'] ?? $item['unit_price'] ?? 0), 2)); ?></strong>
<span>×<?php echo esc_html((string) ($item['quantity'] ?? 1)); ?></span>
</div>
</div>
<?php endforeach; ?>
<?php else : ?>
<div class="aksrt-orders-page__card-item">
<span class="aksrt-orders-page__card-item-title"><?php echo esc_html((string) ($order['product_name'] ?? __('商品订单', 'aksrt'))); ?></span>
<strong>¥<?php echo esc_html(number_format((float) ($order['amount'] ?? 0), 2)); ?></strong>
</div>
<?php endif; ?>
</div>
<!-- Price summary -->
<div class="aksrt-orders-page__card-summary">
<span><?php esc_html_e('合计', 'aksrt'); ?></span>
<strong>¥<?php echo esc_html(number_format((float) ($order['amount'] ?? 0), 2)); ?></strong>
<?php if ((float) ($order['discount'] ?? 0) > 0) : ?>
<small>(<?php printf(esc_html__('已优惠 ¥%s', 'aksrt'), number_format((float) $order['discount'], 2)); ?>)</small>
<?php endif; ?>
</div>
<!-- Logistics timeline (collapsed) -->
<?php if (!empty($order['shipping_records'])) : ?>
<div class="aksrt-orders-page__timeline" data-aksrt-timeline>
<h4 class="aksrt-orders-page__timeline-toggle" data-aksrt-toggle-timeline>
<i class="fas fa-truck"></i> <?php esc_html_e('物流信息', 'aksrt'); ?> <i class="fas fa-chevron-down"></i>
</h4>
<div class="aksrt-orders-page__timeline-body" hidden>
<?php foreach ((array) ($order['timeline'] ?? []) as $tl) : ?>
<div class="aksrt-orders-page__timeline-item">
<time><?php echo esc_html((string) ($tl['time'] ?? '')); ?></time>
<span><?php echo esc_html((string) ($tl['label'] ?? '')); ?></span>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<!-- Actions -->
<div class="aksrt-orders-page__card-actions">
<?php if (!empty($order['actions']['can_pay'])) : ?>
<button type="button" class="btn-primary btn-sm" data-aksrt-order-pay="<?php echo esc_attr($on); ?>"><?php esc_html_e('去支付', 'aksrt'); ?></button>
<?php endif; ?>
<?php if (!empty($order['actions']['can_receipt'])) : ?>
<button type="button" class="btn-primary btn-sm" data-aksrt-order-receipt="<?php echo esc_attr($on); ?>"><?php esc_html_e('确认收货', 'aksrt'); ?></button>
<?php endif; ?>
<?php if ($os === AKSRT_Order::STATUS_PENDING_REVIEW) : ?>
<button type="button" class="btn-primary btn-sm" data-aksrt-order-review="<?php echo esc_attr($on); ?>"><?php esc_html_e('去评价', 'aksrt'); ?></button>
<?php endif; ?>
<?php if (!empty($order['actions']['can_cancel'])) : ?>
<button type="button" class="btn-ghost btn-sm" data-aksrt-order-cancel="<?php echo esc_attr($on); ?>"><?php esc_html_e('取消', 'aksrt'); ?></button>
<?php endif; ?>
<?php if (in_array($os, [AKSRT_Order::STATUS_COMPLETED, AKSRT_Order::STATUS_PENDING_REVIEW], true)) : ?>
<button type="button" class="btn-outline btn-sm" data-aksrt-order-rebuy="<?php echo esc_attr($on); ?>"><?php esc_html_e('再次购买', 'aksrt'); ?></button>
<?php endif; ?>
<?php if (!empty($order['actions']['can_after_sale'])) : ?>
<button type="button" class="btn-outline btn-sm" data-aksrt-order-after-sale="<?php echo esc_attr($on); ?>"><?php esc_html_e('售后', 'aksrt'); ?></button>
<?php endif; ?>
<?php if (!empty($order['time_left'])) : ?>
<span class="aksrt-orders-page__countdown" data-aksrt-order-countdown="<?php echo esc_attr((string) $order['time_left']); ?>">
<?php printf(esc_html__('%d秒后关闭', 'aksrt'), (int) $order['time_left']); ?>
</span>
<?php endif; ?>
</div>
</article>
<?php endforeach; ?>
</div>
<?php if ($total_pages > 1) : ?>
<div class="aksrt-orders-page__pagination">
<?php for ($i = 1; $i <= $total_pages; $i++) :
$page_url = add_query_arg(['order_page' => $i]);
?>
<?php if ($i === $paged) : ?>
<span class="aksrt-orders-page__page-chip is-active"><?php echo esc_html((string) $i); ?></span>
<?php else : ?>
<a href="<?php echo esc_url($page_url); ?>" class="aksrt-orders-page__page-chip"><?php echo esc_html((string) $i); ?></a>
<?php endif; ?>
<?php endfor; ?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="aksrt-orders-page__empty">
<i class="fas fa-receipt" aria-hidden="true"></i>
<p><?php esc_html_e('暂无订单记录。', 'aksrt'); ?></p>
</div>
<?php endif; ?>
<!-- After-sale modal -->
<div class="aksrt-modal" data-aksrt-aftersale-modal hidden>
<div class="aksrt-modal__overlay"></div>
<div class="aksrt-modal__dialog">
<div class="aksrt-modal__header">
<h3><?php esc_html_e('申请售后', 'aksrt'); ?></h3>
<button type="button" class="aksrt-modal__close" data-aksrt-aftersale-modal-close>&times;</button>
</div>
<form class="aksrt-modal__body" data-aksrt-aftersale-form>
<input type="hidden" name="order_number" value="">
<div class="aksrt-orders-page__field">
<label><?php esc_html_e('售后类型', 'aksrt'); ?></label>
<select name="type" class="aksrt-orders-page__select">
<?php foreach (AKSRT_After_Sale::get_valid_types() as $type_key => $type_label) : ?>
<option value="<?php echo esc_attr($type_key); ?>"><?php echo esc_html($type_label); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="aksrt-orders-page__field">
<label><?php esc_html_e('售后原因', 'aksrt'); ?> *</label>
<textarea name="reason" class="aksrt-orders-page__textarea" rows="4" required placeholder="<?php esc_attr_e('请描述问题', 'aksrt'); ?>"></textarea>
</div>
<div class="aksrt-modal__footer">
<button type="button" class="btn-ghost" data-aksrt-aftersale-modal-close><?php esc_html_e('取消', 'aksrt'); ?></button>
<button type="submit" class="btn-primary"><?php esc_html_e('提交', 'aksrt'); ?></button>
</div>
</form>
</div>
</div>
<!-- Favorites section -->
<?php
$fav_tab = sanitize_key((string) ($_GET['section'] ?? ''));
if ($fav_tab === 'favorites' && $shop) :
$fav_data = $shop->get_user_favorites($user_id);
?>
<section class="aksrt-orders-page__favorites">
<h2><?php esc_html_e('收藏商品', 'aksrt'); ?></h2>
<?php if (!empty($fav_data['items'])) : ?>
<div class="aksrt-orders-page__fav-grid">
<?php foreach ($fav_data['items'] as $fav) : ?>
<a href="<?php echo esc_url((string) ($fav['permalink'] ?? '#')); ?>" class="aksrt-orders-page__fav-card">
<?php if (!empty($fav['image'])) : ?>
<img src="<?php echo esc_url((string) $fav['image']); ?>" alt="" loading="lazy">
<?php endif; ?>
<h3><?php echo esc_html((string) ($fav['title'] ?? '')); ?></h3>
<strong>¥<?php echo esc_html(number_format((float) ($fav['price'] ?? 0), 2)); ?></strong>
</a>
<?php endforeach; ?>
</div>
<?php else : ?>
<p><?php esc_html_e('暂无收藏商品', 'aksrt'); ?></p>
<?php endif; ?>
</section>
<?php endif; ?>
</div>
</main>
<?php get_footer(); ?>
+59
View File
@@ -0,0 +1,59 @@
<?php
/**
* Template Name: AKSRT - Posts Navigation
* Template Post Type: page
*
* 文章导航页:分类+标签的导航聚合页。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$categories = get_categories(['hide_empty' => true, 'orderby' => 'count', 'order' => 'DESC', 'number' => 20]);
$tags = get_tags(['hide_empty' => true, 'orderby' => 'count', 'order' => 'DESC', 'number' => 40]);
?>
<main id="main-content" class="container page-content-default" tabindex="-1">
<header class="page-header">
<h1 class="entry-title"><?php echo esc_html(get_the_title() ?: __('文章导航', 'aksrt')); ?></h1>
</header>
<div class="content-wrap">
<div class="content-layout content-layout--no-sidebar">
<div class="content-layout__primary">
<div class="aksrt-posts-navs container">
<?php if (!empty($categories)) : ?>
<section class="aksrt-posts-navs__section">
<h2 class="aksrt-posts-navs__heading"><?php esc_html_e('热门分类', 'aksrt'); ?></h2>
<div class="aksrt-posts-navs__cats">
<?php foreach ($categories as $cat) : ?>
<a href="<?php echo esc_url(get_category_link($cat->term_id)); ?>" class="aksrt-posts-navs__cat-chip">
<span class="aksrt-posts-navs__cat-name"><?php echo esc_html($cat->name); ?></span>
<span class="aksrt-posts-navs__cat-count"><?php echo esc_html($cat->count); ?></span>
</a>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<?php if (!empty($tags)) : ?>
<section class="aksrt-posts-navs__section">
<h2 class="aksrt-posts-navs__heading"><?php esc_html_e('热门标签', 'aksrt'); ?></h2>
<div class="aksrt-posts-navs__tags">
<?php foreach ($tags as $tag) : ?>
<a href="<?php echo esc_url(get_tag_link($tag->term_id)); ?>" class="aksrt-posts-navs__tag"><?php echo esc_html($tag->name); ?></a>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
</div>
</div>
</div>
</div>
</main>
<?php
get_footer();
+170
View File
@@ -0,0 +1,170 @@
<?php
/**
* Template Name: AKSRT - Product Edit
* Template Post Type: page
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
if (!is_user_logged_in()) {
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink());
wp_safe_redirect($login_url);
exit;
}
$product_id = (int) ($_GET['product_id'] ?? 0);
$edit_view = function_exists('aksrt_get_product_edit_page_view')
? aksrt_get_product_edit_page_view(get_current_user_id(), $product_id)
: ['allowed' => false, 'is_edit' => false, 'product' => [], 'taxonomy' => ['categories' => [], 'tags' => []], 'shipping_modes' => [], 'status_options' => []];
$product = (array) ($edit_view['product'] ?? []);
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<?php if (function_exists('aksrt_breadcrumbs')) { aksrt_breadcrumbs(); } ?>
<div
class="aksrt-product-editor container"
data-aksrt-product-editor
data-product-id="<?php echo esc_attr((string) ($product['id'] ?? 0)); ?>"
data-is-edit="<?php echo !empty($edit_view['is_edit']) ? '1' : '0'; ?>"
>
<?php if (empty($edit_view['allowed'])) : ?>
<div class="aksrt-product-editor__empty">
<i class="fas fa-lock" aria-hidden="true"></i>
<p><?php esc_html_e('你没有权限编辑该商品。', 'aksrt'); ?></p>
</div>
<?php else : ?>
<div class="aksrt-product-editor__header">
<div>
<h1 class="aksrt-product-editor__title">
<?php echo !empty($edit_view['is_edit']) ? esc_html__('编辑商品', 'aksrt') : esc_html__('发布商品', 'aksrt'); ?>
</h1>
<p class="aksrt-product-editor__subtitle"><?php esc_html_e('管理主图、图集、分类标签、发货方式、运费和售后说明。', 'aksrt'); ?></p>
</div>
</div>
<form class="aksrt-product-editor__layout" data-aksrt-product-form>
<section class="aksrt-product-editor__main">
<div class="aksrt-product-editor__panel">
<h2><?php esc_html_e('基础信息', 'aksrt'); ?></h2>
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('商品名称', 'aksrt'); ?></span>
<input type="text" name="title" class="aksrt-product-editor__input" value="<?php echo esc_attr((string) ($product['title'] ?? '')); ?>" required>
</label>
<div class="aksrt-product-editor__columns">
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('价格', 'aksrt'); ?></span>
<input type="number" name="price" class="aksrt-product-editor__input" min="0" step="0.01" value="<?php echo esc_attr((string) ($product['price'] ?? 0)); ?>" required>
</label>
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('库存', 'aksrt'); ?></span>
<input type="number" name="stock" class="aksrt-product-editor__input" min="0" step="1" value="<?php echo esc_attr((string) ($product['stock'] ?? 0)); ?>" required>
</label>
</div>
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('商品简介', 'aksrt'); ?></span>
<textarea name="excerpt" class="aksrt-product-editor__textarea" rows="4"><?php echo esc_textarea((string) ($product['excerpt'] ?? '')); ?></textarea>
</label>
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('商品详情', 'aksrt'); ?></span>
<textarea name="content" class="aksrt-product-editor__textarea" rows="10"><?php echo esc_textarea((string) ($product['content'] ?? '')); ?></textarea>
</label>
</div>
<div class="aksrt-product-editor__panel">
<h2><?php esc_html_e('商品素材', 'aksrt'); ?></h2>
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('主图 URL', 'aksrt'); ?></span>
<input type="url" name="image" class="aksrt-product-editor__input" value="<?php echo esc_attr((string) ($product['image'] ?? '')); ?>" placeholder="https://">
</label>
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('图集 URL(每行一个)', 'aksrt'); ?></span>
<textarea name="gallery" class="aksrt-product-editor__textarea" rows="5"><?php echo esc_textarea(implode("\n", (array) ($product['gallery'] ?? []))); ?></textarea>
</label>
</div>
<div class="aksrt-product-editor__panel">
<h2><?php esc_html_e('发货与售后', 'aksrt'); ?></h2>
<div class="aksrt-product-editor__columns">
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('发货方式', 'aksrt'); ?></span>
<select name="shipping_mode" class="aksrt-product-editor__input">
<?php foreach ((array) ($edit_view['shipping_modes'] ?? []) as $mode) : ?>
<option value="<?php echo esc_attr((string) ($mode['value'] ?? '')); ?>" <?php selected((string) ($product['shipping_mode'] ?? ''), (string) ($mode['value'] ?? '')); ?>>
<?php echo esc_html((string) ($mode['label'] ?? '')); ?>
</option>
<?php endforeach; ?>
</select>
</label>
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('额外运费', 'aksrt'); ?></span>
<input type="number" name="shipping_fee" class="aksrt-product-editor__input" min="0" step="0.01" value="<?php echo esc_attr((string) ($product['shipping_fee'] ?? 0)); ?>">
</label>
</div>
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('售后说明', 'aksrt'); ?></span>
<textarea name="after_sale_policy" class="aksrt-product-editor__textarea" rows="4"><?php echo esc_textarea((string) ($product['after_sale_policy'] ?? '')); ?></textarea>
</label>
<label class="aksrt-product-editor__checkbox">
<input type="checkbox" name="support_refund" value="1" <?php checked(!empty($product['support_refund'])); ?>>
<span><?php esc_html_e('支持退款/售后入口', 'aksrt'); ?></span>
</label>
</div>
</section>
<aside class="aksrt-product-editor__sidebar">
<div class="aksrt-product-editor__panel">
<h2><?php esc_html_e('发布设置', 'aksrt'); ?></h2>
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('状态', 'aksrt'); ?></span>
<select name="status" class="aksrt-product-editor__input">
<?php foreach ((array) ($edit_view['status_options'] ?? []) as $status_option) : ?>
<option value="<?php echo esc_attr((string) ($status_option['value'] ?? 'draft')); ?>" <?php selected((string) ($product['status'] ?? 'draft'), (string) ($status_option['value'] ?? 'draft')); ?>>
<?php echo esc_html((string) ($status_option['label'] ?? '')); ?>
</option>
<?php endforeach; ?>
</select>
</label>
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('分类(按住 Ctrl 可多选)', 'aksrt'); ?></span>
<select name="categories[]" class="aksrt-product-editor__input" multiple size="6">
<?php foreach ((array) (($edit_view['taxonomy']['categories'] ?? [])) as $category) : ?>
<option value="<?php echo esc_attr((string) ($category['id'] ?? 0)); ?>" <?php echo in_array((int) ($category['id'] ?? 0), (array) ($product['categories'] ?? []), true) ? 'selected' : ''; ?>>
<?php echo esc_html((string) ($category['name'] ?? '')); ?>
</option>
<?php endforeach; ?>
</select>
</label>
<label class="aksrt-product-editor__field">
<span><?php esc_html_e('标签(英文逗号分隔)', 'aksrt'); ?></span>
<input type="text" name="tags" class="aksrt-product-editor__input" value="<?php echo esc_attr(implode(', ', (array) ($product['tags'] ?? []))); ?>">
</label>
</div>
<div class="aksrt-product-editor__panel">
<h2><?php esc_html_e('动作', 'aksrt'); ?></h2>
<div class="aksrt-product-editor__actions">
<button type="submit" class="btn-primary btn-lg"><?php echo !empty($edit_view['is_edit']) ? esc_html__('保存修改', 'aksrt') : esc_html__('发布商品', 'aksrt'); ?></button>
<a href="<?php echo esc_url(function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : home_url('/')); ?>" class="btn-ghost btn-lg"><?php esc_html_e('返回中心', 'aksrt'); ?></a>
</div>
<p class="aksrt-product-editor__hint" data-aksrt-product-editor-message><?php esc_html_e('保存后会回到用户中心,商品信息供订单页和商品详情页复用。', 'aksrt'); ?></p>
</div>
</aside>
</form>
<?php endif; ?>
</div>
</main>
<?php get_footer(); ?>
+413
View File
@@ -0,0 +1,413 @@
<?php
/**
* Template Name: AKSRT - Product Detail
* Template Post Type: page
*
* Enhanced: options selector, video gallery, params/services modal,
* review tab, favorites, related products, mobile tabbar (§6.10).
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$product_id = (int) ($_GET['product_id'] ?? 0);
if ($product_id <= 0) {
$product_id = (int) get_query_var('product_id', 0);
}
$shop = class_exists('AKSRT_Shop') ? AKSRT_Shop::get_instance() : null;
$product = function_exists('aksrt_get_product_page_view')
? aksrt_get_product_page_view($product_id)
: ($shop ? $shop->get_product_detail_view($product_id) : []);
$user_id = get_current_user_id();
$is_fav = $shop && $user_id > 0 ? $shop->is_favorite($user_id, $product_id) : false;
$options = $product['options'] ?? [];
$has_options = !empty($options);
$price_range = $product['price_range'] ?? [];
$params = $product['params'] ?? [];
$services = $product['services'] ?? [];
$pay_modo = $product['pay_modo'] ?? '0';
$scores = class_exists('AKSRT_Product_Review') ? AKSRT_Product_Review::get_instance()->get_product_scores($product_id) : [];
$reviews = class_exists('AKSRT_Product_Review') ? AKSRT_Product_Review::get_instance()->get_reviews($product_id, ['per_page' => 5]) : ['items' => [], 'total' => 0];
$author_id = (int) get_post_field('post_author', $product_id);
$author = $author_id > 0 ? get_userdata($author_id) : null;
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<?php if (function_exists('aksrt_breadcrumbs')) { aksrt_breadcrumbs(); } ?>
<?php if (!empty($product)) : ?>
<div
class="aksrt-product-page container"
data-aksrt-product-page
data-product-id="<?php echo esc_attr((string) $product_id); ?>"
data-options='<?php echo esc_attr(wp_json_encode($options, JSON_UNESCAPED_UNICODE)); ?>'
>
<div class="aksrt-product-page__layout">
<!-- Gallery -->
<section class="aksrt-product-page__gallery">
<?php if (!empty($product['image'])) : ?>
<div class="aksrt-product-page__primary-image" data-aksrt-main-image>
<img src="<?php echo esc_url((string) $product['image']); ?>" alt="<?php echo esc_attr((string) ($product['title'] ?? '')); ?>" id="product-main-img">
</div>
<?php else : ?>
<div class="aksrt-product-page__placeholder">
<i class="fas fa-image" aria-hidden="true"></i>
</div>
<?php endif; ?>
<?php
$gallery_items = (array) ($product['gallery'] ?? []);
$cover_videos = (array) (get_post_meta($product_id, '_aksrt_cover_videos', true) ?: []);
?>
<?php if (!empty($gallery_items) || !empty($cover_videos)) : ?>
<div class="aksrt-product-page__thumbs">
<?php foreach ($cover_videos as $video_url) : ?>
<button type="button" class="aksrt-product-page__thumb aksrt-product-page__thumb--video" data-aksrt-product-video="<?php echo esc_url($video_url); ?>">
<i class="fas fa-play-circle"></i>
</button>
<?php endforeach; ?>
<?php foreach ($gallery_items as $gallery_image) : ?>
<button type="button" class="aksrt-product-page__thumb" data-aksrt-product-thumb="<?php echo esc_url($gallery_image); ?>">
<img src="<?php echo esc_url($gallery_image); ?>" alt="">
</button>
<?php endforeach; ?>
</div>
<?php endif; ?>
</section>
<!-- Summary -->
<section class="aksrt-product-page__summary">
<div class="aksrt-product-page__badges">
<?php if ($pay_modo === 'points') : ?>
<span class="aksrt-product-page__badge aksrt-product-page__badge--points"><?php esc_html_e('积分商品', 'aksrt'); ?></span>
<?php endif; ?>
<?php if (!empty($product['marketing']['headline'])) : ?>
<span class="aksrt-product-page__badge aksrt-product-page__badge--promo"><?php echo esc_html((string) $product['marketing']['headline']); ?></span>
<?php endif; ?>
<?php if (!empty($product['after_sale']['support_refund'])) : ?>
<span class="aksrt-product-page__badge"><?php esc_html_e('支持售后', 'aksrt'); ?></span>
<?php endif; ?>
</div>
<h1 class="aksrt-product-page__title"><?php echo esc_html((string) ($product['title'] ?? '')); ?></h1>
<!-- Price row with range support -->
<div class="aksrt-product-page__price-row">
<?php if ($has_options && !empty($price_range) && $price_range['min'] !== $price_range['max']) : ?>
<strong class="aksrt-product-page__price" data-aksrt-dynamic-price>
<?php echo $pay_modo === 'points' ? '' : '¥'; ?><?php echo esc_html(number_format($price_range['min'], 2)); ?> - <?php echo $pay_modo === 'points' ? '' : '¥'; ?><?php echo esc_html(number_format($price_range['max'], 2)); ?>
<?php if ($pay_modo === 'points') : ?><small><?php esc_html_e('积分', 'aksrt'); ?></small><?php endif; ?>
</strong>
<?php else : ?>
<strong class="aksrt-product-page__price" data-aksrt-dynamic-price>
<?php echo $pay_modo === 'points' ? '' : '¥'; ?><?php echo esc_html(number_format((float) ($product['price'] ?? 0), 2)); ?>
<?php if ($pay_modo === 'points') : ?><small><?php esc_html_e('积分', 'aksrt'); ?></small><?php endif; ?>
</strong>
<?php endif; ?>
<span class="aksrt-product-page__sales"><?php printf(esc_html__('已售 %d', 'aksrt'), (int) ($product['sales'] ?? 0)); ?></span>
<?php if (!empty($scores['average'])) : ?>
<span class="aksrt-product-page__rating"><i class="fas fa-star"></i> <?php echo esc_html((string) $scores['average']); ?></span>
<?php endif; ?>
</div>
<?php if (!empty($product['excerpt'])) : ?>
<p class="aksrt-product-page__excerpt"><?php echo esc_html((string) $product['excerpt']); ?></p>
<?php endif; ?>
<!-- Product options selector -->
<?php if ($has_options) : ?>
<div class="aksrt-product-page__options" data-aksrt-options-selector>
<?php foreach ($options as $group_idx => $group) : ?>
<div class="aksrt-product-page__option-group" data-group-index="<?php echo esc_attr((string) $group_idx); ?>">
<span class="aksrt-product-page__option-label"><?php echo esc_html((string) ($group['name'] ?? '')); ?></span>
<div class="aksrt-product-page__option-values aksrt-product-page__option-values--<?php echo esc_attr($group['view_mode'] ?? 'list'); ?>">
<?php foreach ((array) ($group['values'] ?? []) as $val) : ?>
<button type="button"
class="aksrt-product-page__option-btn"
data-option-group="<?php echo esc_attr((string) $group_idx); ?>"
data-option-label="<?php echo esc_attr((string) ($val['label'] ?? '')); ?>"
data-price-change="<?php echo esc_attr((string) ($val['price_change'] ?? 0)); ?>"
<?php if (!empty($val['image'])) : ?>data-option-image="<?php echo esc_url($val['image']); ?>"<?php endif; ?>
>
<?php if (($group['view_mode'] ?? '') === 'img' && !empty($val['image'])) : ?>
<img src="<?php echo esc_url($val['image']); ?>" alt="<?php echo esc_attr($val['label']); ?>">
<?php endif; ?>
<span><?php echo esc_html((string) ($val['label'] ?? '')); ?></span>
<?php if ((float) ($val['price_change'] ?? 0) !== 0.0) : ?>
<em><?php echo (float) $val['price_change'] > 0 ? '+' : ''; ?><?php echo esc_html(number_format((float) $val['price_change'], 2)); ?></em>
<?php endif; ?>
</button>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
<div class="aksrt-product-page__option-result" data-aksrt-option-result hidden>
<span data-aksrt-selected-labels></span>
<strong data-aksrt-resolved-price></strong>
<span data-aksrt-resolved-stock></span>
</div>
</div>
<?php endif; ?>
<!-- Meta info -->
<dl class="aksrt-product-page__meta">
<div>
<dt><?php esc_html_e('库存', 'aksrt'); ?></dt>
<dd data-aksrt-stock-display><?php
$stock_val = (int) ($product['stock'] ?? 0);
echo $stock_val <= -1 ? esc_html__('充足', 'aksrt') : esc_html((string) $stock_val);
?></dd>
</div>
<div>
<dt><?php esc_html_e('配送', 'aksrt'); ?></dt>
<dd><?php echo esc_html((string) (($product['shipping']['delivery_note'] ?? '') ?: __('标准配送', 'aksrt'))); ?></dd>
</div>
<div>
<dt><?php esc_html_e('运费', 'aksrt'); ?></dt>
<dd><?php
$ship_fee = (float) ($product['shipping']['fee'] ?? 0);
echo $ship_fee > 0 ? '¥' . esc_html(number_format($ship_fee, 2)) : esc_html__('包邮', 'aksrt');
?></dd>
</div>
</dl>
<!-- Params & Services quick links -->
<?php if (!empty($params)) : ?>
<button type="button" class="aksrt-product-page__info-trigger" data-aksrt-show-params>
<i class="fas fa-list-ul"></i> <?php esc_html_e('商品参数', 'aksrt'); ?> <i class="fas fa-chevron-right"></i>
</button>
<?php endif; ?>
<?php if (!empty($services)) : ?>
<button type="button" class="aksrt-product-page__info-trigger" data-aksrt-show-services>
<i class="fas fa-shield-alt"></i> <?php esc_html_e('服务保障', 'aksrt'); ?> <i class="fas fa-chevron-right"></i>
</button>
<?php endif; ?>
<!-- Purchase box -->
<div class="aksrt-product-page__purchase-box">
<label class="aksrt-product-page__field">
<span><?php esc_html_e('购买数量', 'aksrt'); ?></span>
<div class="aksrt-product-page__quantity">
<button type="button" class="aksrt-product-page__qty-btn" data-aksrt-qty-action="dec">-</button>
<input type="number" min="1" max="<?php echo esc_attr((string) max(1, $stock_val > 0 ? $stock_val : 999)); ?>" value="1" class="aksrt-product-page__qty-input" data-aksrt-quantity>
<button type="button" class="aksrt-product-page__qty-btn" data-aksrt-qty-action="inc">+</button>
</div>
</label>
<div class="aksrt-product-page__actions">
<?php if (is_user_logged_in() && !empty($product['purchase_enabled'])) : ?>
<button type="button" class="btn-outline btn-lg" data-aksrt-add-cart><?php esc_html_e('加入购物车', 'aksrt'); ?></button>
<button type="button" class="btn-primary btn-lg" data-aksrt-buy-now>
<?php echo $pay_modo === 'points' ? esc_html__('积分兑换', 'aksrt') : esc_html__('立即下单', 'aksrt'); ?>
</button>
<?php elseif (!is_user_logged_in()) : ?>
<a href="<?php echo esc_url(function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink())); ?>" class="btn-primary btn-lg">
<?php esc_html_e('登录后购买', 'aksrt'); ?>
</a>
<?php else : ?>
<button type="button" class="btn-primary btn-lg" disabled><?php esc_html_e('暂不可购买', 'aksrt'); ?></button>
<?php endif; ?>
</div>
<!-- Auxiliary actions -->
<div class="aksrt-product-page__aux-actions">
<button type="button" class="aksrt-product-page__aux-btn<?php echo $is_fav ? ' is-active' : ''; ?>" data-aksrt-toggle-fav="<?php echo esc_attr((string) $product_id); ?>">
<i class="<?php echo $is_fav ? 'fas' : 'far'; ?> fa-heart"></i>
<?php esc_html_e('收藏', 'aksrt'); ?>
</button>
<button type="button" class="aksrt-product-page__aux-btn" data-aksrt-share>
<i class="fas fa-share-alt"></i> <?php esc_html_e('分享', 'aksrt'); ?>
</button>
</div>
</div>
<!-- Merchant info -->
<?php if ($author) : ?>
<div class="aksrt-product-page__merchant">
<img src="<?php echo esc_url(function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($author_id) : get_avatar_url($author_id)); ?>" alt="" class="aksrt-product-page__merchant-avatar">
<div>
<strong><?php echo esc_html($author->display_name); ?></strong>
<a href="<?php echo esc_url(get_author_posts_url($author_id)); ?>?tab=products" class="aksrt-product-page__merchant-link"><?php esc_html_e('进入店铺', 'aksrt'); ?></a>
</div>
</div>
<?php endif; ?>
</section>
</div>
<!-- Tabs: detail / reviews / custom -->
<div class="aksrt-product-page__tabs">
<nav class="aksrt-product-page__tab-nav">
<button class="aksrt-product-page__tab-btn is-active" data-aksrt-tab="detail"><?php esc_html_e('商品详情', 'aksrt'); ?></button>
<button class="aksrt-product-page__tab-btn" data-aksrt-tab="reviews">
<?php esc_html_e('评价', 'aksrt'); ?>
<?php if (($scores['total'] ?? 0) > 0) : ?>
<em>(<?php echo esc_html((string) $scores['total']); ?>)</em>
<?php endif; ?>
</button>
</nav>
<div class="aksrt-product-page__tab-panel is-active" data-aksrt-tab-panel="detail">
<div class="aksrt-product-page__content">
<?php echo wp_kses_post((string) ($product['content'] ?? '')); ?>
</div>
</div>
<div class="aksrt-product-page__tab-panel" data-aksrt-tab-panel="reviews">
<?php if (!empty($scores['total'])) : ?>
<div class="aksrt-product-page__review-summary">
<strong class="aksrt-product-page__review-avg"><?php echo esc_html((string) ($scores['average'] ?? '0')); ?></strong>
<span class="aksrt-product-page__review-level"><?php echo esc_html((string) ($scores['level'] ?? '')); ?></span>
<div class="aksrt-product-page__review-counts">
<span><?php printf(esc_html__('好评 %d', 'aksrt'), (int) ($scores['good'] ?? 0)); ?></span>
<span><?php printf(esc_html__('差评 %d', 'aksrt'), (int) ($scores['bad'] ?? 0)); ?></span>
<span><?php printf(esc_html__('有图 %d', 'aksrt'), (int) ($scores['has_image'] ?? 0)); ?></span>
</div>
</div>
<?php endif; ?>
<div class="aksrt-product-page__review-list" data-aksrt-review-list>
<?php foreach (($reviews['items'] ?? []) as $review) : ?>
<div class="aksrt-product-page__review-item">
<div class="aksrt-product-page__review-header">
<img src="<?php echo esc_url((string) ($review['user_avatar'] ?? '')); ?>" alt="" class="aksrt-product-page__review-avatar">
<span class="aksrt-product-page__review-name"><?php echo esc_html((string) ($review['user_name'] ?? '')); ?></span>
<span class="aksrt-product-page__review-date"><?php echo esc_html((string) ($review['date'] ?? '')); ?></span>
<span class="aksrt-product-page__review-stars">
<?php for ($s = 0; $s < 5; $s++) : ?>
<i class="<?php echo $s < (int) ($review['rating_average'] ?? 0) ? 'fas' : 'far'; ?> fa-star"></i>
<?php endfor; ?>
</span>
</div>
<p class="aksrt-product-page__review-text"><?php echo esc_html((string) ($review['content'] ?? '')); ?></p>
<?php if (!empty($review['images'])) : ?>
<div class="aksrt-product-page__review-images">
<?php foreach ((array) $review['images'] as $rimg) : ?>
<img src="<?php echo esc_url($rimg); ?>" alt="" class="aksrt-product-page__review-img">
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php if (empty($reviews['items'])) : ?>
<p class="aksrt-product-page__review-empty"><?php esc_html_e('暂无评价', 'aksrt'); ?></p>
<?php endif; ?>
</div>
</div>
</div>
<!-- Related products -->
<?php
$related_query = new WP_Query([
'post_type' => 'aksrt_product',
'posts_per_page' => 6,
'post_status' => 'publish',
'post__not_in' => [$product_id],
'tax_query' => !empty($product['categories']) ? [[
'taxonomy' => 'aksrt_product_cat',
'field' => 'term_id',
'terms' => wp_list_pluck($product['categories'], 'term_id'),
]] : [],
'orderby' => 'rand',
]);
?>
<?php if ($related_query->have_posts()) : ?>
<section class="aksrt-product-page__related">
<h2><?php esc_html_e('相关推荐', 'aksrt'); ?></h2>
<div class="aksrt-product-page__related-grid">
<?php while ($related_query->have_posts()) : $related_query->the_post(); ?>
<a href="<?php echo esc_url(get_permalink()); ?>" class="aksrt-product-page__related-card">
<?php $thumb = $shop ? $shop->resolve_main_image(get_the_ID()) : get_the_post_thumbnail_url(get_the_ID(), 'medium'); ?>
<?php if ($thumb) : ?>
<img src="<?php echo esc_url($thumb); ?>" alt="<?php the_title_attribute(); ?>" loading="lazy">
<?php endif; ?>
<h3><?php the_title(); ?></h3>
<strong>¥<?php echo esc_html(number_format((float) get_post_meta(get_the_ID(), '_aksrt_price', true), 2)); ?></strong>
</a>
<?php endwhile; wp_reset_postdata(); ?>
</div>
</section>
<?php endif; ?>
</div>
<!-- Params modal -->
<?php if (!empty($params)) : ?>
<div class="aksrt-modal" data-aksrt-params-modal hidden>
<div class="aksrt-modal__overlay"></div>
<div class="aksrt-modal__dialog">
<div class="aksrt-modal__header">
<h3><?php esc_html_e('商品参数', 'aksrt'); ?></h3>
<button type="button" class="aksrt-modal__close" data-aksrt-modal-close>&times;</button>
</div>
<div class="aksrt-modal__body">
<table class="aksrt-product-page__params-table">
<?php foreach ($params as $param) : ?>
<tr>
<th><?php echo esc_html((string) ($param['name'] ?? '')); ?></th>
<td><?php echo esc_html((string) ($param['value'] ?? '')); ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
</div>
</div>
<?php endif; ?>
<!-- Services modal -->
<?php if (!empty($services)) : ?>
<div class="aksrt-modal" data-aksrt-services-modal hidden>
<div class="aksrt-modal__overlay"></div>
<div class="aksrt-modal__dialog">
<div class="aksrt-modal__header">
<h3><?php esc_html_e('服务保障', 'aksrt'); ?></h3>
<button type="button" class="aksrt-modal__close" data-aksrt-modal-close>&times;</button>
</div>
<div class="aksrt-modal__body">
<?php foreach ($services as $service) : ?>
<div class="aksrt-product-page__service-item">
<?php if (!empty($service['icon'])) : ?>
<img src="<?php echo esc_url($service['icon']); ?>" alt="" class="aksrt-product-page__service-icon">
<?php else : ?>
<i class="fas fa-check-circle"></i>
<?php endif; ?>
<div>
<strong><?php echo esc_html((string) ($service['name'] ?? '')); ?></strong>
<?php if (!empty($service['desc'])) : ?>
<p><?php echo esc_html((string) $service['desc']); ?></p>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<?php endif; ?>
<!-- Mobile purchase tabbar -->
<div class="aksrt-product-page__mobile-bar" data-aksrt-mobile-purchase>
<button type="button" class="aksrt-product-page__mobile-btn" data-aksrt-toggle-fav="<?php echo esc_attr((string) $product_id); ?>">
<i class="<?php echo $is_fav ? 'fas' : 'far'; ?> fa-heart"></i>
</button>
<button type="button" class="aksrt-product-page__mobile-btn" data-aksrt-add-cart>
<i class="fas fa-shopping-cart"></i>
</button>
<button type="button" class="aksrt-product-page__mobile-buy btn-primary" data-aksrt-buy-now>
<?php echo $pay_modo === 'points' ? esc_html__('积分兑换', 'aksrt') : esc_html__('立即购买', 'aksrt'); ?>
</button>
</div>
<?php else : ?>
<div class="aksrt-product-page container">
<div class="aksrt-product-page__empty">
<i class="fas fa-box-open" aria-hidden="true"></i>
<p><?php esc_html_e('商品不存在或已下架。', 'aksrt'); ?></p>
</div>
</div>
<?php endif; ?>
</main>
<?php get_footer(); ?>
File diff suppressed because it is too large. Load diff
+11
View File
@@ -0,0 +1,11 @@
<?php
/**
* Template Name: AKSRT - Register
* Template Post Type: page
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
require AKSRT_DIR . '/templates/pages/page-sign.php';
+172
View File
@@ -0,0 +1,172 @@
<?php
/**
* Template Name: AKSRT - Reset Password
* Template Post Type: page
*
* Theme auth route template: /reset-password/
*
* 统一处理:
* 1. 忘记密码(无 token 参数)— 输入邮箱/用户名请求重置
* 2. 设置密码(有 token+uid 参数)— 邮件链接进入后设置新密码
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
// ============================================
// 检测当前模式:设置密码 or 请求重置
// ============================================
$context = AKSRT_Auth_Service::get_reset_context($_GET);
$mode = $context['mode'];
$errors = [];
$success = '';
$token = $context['token'];
$user_id = $context['user_id'];
if (!empty($context['error'])) {
$errors[] = $context['error'];
}
// ============================================
// 处理表单提交
// ============================================
// 模式1:请求重置密码(忘记密码表单提交)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['aksrt_action']) && $_POST['aksrt_action'] === 'request_reset') {
$result = AKSRT_Auth_Service::request_password_reset($_POST);
if (!empty($result['success'])) {
$success = $result['message'] ?? __('如果该账号存在,重置邮件已发送到注册邮箱', 'aksrt');
} else {
$errors[] = $result['error'] ?? __('重置请求失败,请稍后重试', 'aksrt');
}
}
// 模式2:设置新密码(从邮件链接进入)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['aksrt_action']) && $_POST['aksrt_action'] === 'set_password') {
$result = AKSRT_Auth_Service::reset_password_with_token($_POST);
if (!empty($result['success'])) {
$mode = 'success';
$success = $result['message'] ?? __('密码已设置成功!', 'aksrt');
} else {
$errors[] = $result['error'] ?? __('重置密码失败,请稍后重试', 'aksrt');
$mode = $result['mode'] ?? 'invalid';
}
}
get_header();
?>
<main id="main-content" class="site-main" tabindex="-1">
<div class="auth-shell">
<div class="auth-card">
<div class="auth-card__logo" aria-hidden="true">AK</div>
<h1 class="auth-card__title"><?php bloginfo('name'); ?></h1>
<?php if ($mode === 'success') : ?>
<!-- 密码设置成功 -->
<p class="auth-card__subtitle"><?php esc_html_e('密码设置成功!', 'aksrt'); ?></p>
<div class="alert alert--success" role="alert">
<span><?php echo esc_html($success); ?></span>
</div>
<a href="<?php echo esc_url(aksrt_get_login_url()); ?>" class="btn-primary btn-lg" style="width:100%;display:block;text-align:center;text-decoration:none;margin-top:16px;">
<?php esc_html_e('立即登录', 'aksrt'); ?>
</a>
<?php elseif ($mode === 'invalid') : ?>
<!-- 链接无效或已过期 -->
<p class="auth-card__subtitle"><?php esc_html_e('重置链接无效或已过期', 'aksrt'); ?></p>
<?php if (!empty($errors)) : ?>
<div class="alert alert--error" role="alert">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/><line x1="12" x2="12.01" y1="16" y2="16"/></svg>
<span><?php echo esc_html($errors[0]); ?></span>
</div>
<?php endif; ?>
<a href="<?php echo esc_url(aksrt_get_reset_password_url()); ?>" class="btn-primary btn-lg" style="width:100%;display:block;text-align:center;text-decoration:none;margin-top:16px;">
<?php esc_html_e('重新申请重置密码', 'aksrt'); ?>
</a>
<?php elseif ($mode === 'reset') : ?>
<!-- 设置新密码表单 -->
<p class="auth-card__subtitle"><?php esc_html_e('请设置你的新密码', 'aksrt'); ?></p>
<?php if (!empty($errors)) : ?>
<div class="alert alert--error" role="alert">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/><line x1="12" x2="12.01" y1="16" y2="16"/></svg>
<span><?php echo esc_html($errors[0]); ?></span>
</div>
<?php endif; ?>
<form class="auth-form" id="aksrt-set-password-form" method="post" action="">
<?php wp_nonce_field('aksrt_set_password', '_setpw_nonce'); ?>
<input type="hidden" name="aksrt_action" value="set_password">
<input type="hidden" name="reset_token" value="<?php echo esc_attr($token); ?>">
<input type="hidden" name="reset_uid" value="<?php echo esc_attr((string) $user_id); ?>">
<div class="form-field form-field--pwd">
<label for="pass1"><?php esc_html_e('新密码', 'aksrt'); ?></label>
<div class="input-wrap">
<span class="input-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></span>
<input type="password" name="pass1" id="pass1" autocomplete="new-password" required minlength="6">
<button type="button" class="toggle-pwd" data-target="pass1" aria-label="<?php esc_attr_e('显示或隐藏密码', 'aksrt'); ?>"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg></button>
</div>
</div>
<div class="form-field form-field--pwd">
<label for="pass2"><?php esc_html_e('确认新密码', 'aksrt'); ?></label>
<div class="input-wrap">
<span class="input-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></span>
<input type="password" name="pass2" id="pass2" autocomplete="new-password" required minlength="6">
<button type="button" class="toggle-pwd" data-target="pass2" aria-label="<?php esc_attr_e('显示或隐藏密码', 'aksrt'); ?>"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg></button>
</div>
</div>
<button type="submit" class="btn-primary btn-lg btn-block"><?php esc_html_e('设置新密码', 'aksrt'); ?></button>
</form>
<?php else : ?>
<!-- 请求重置密码表单(忘记密码) -->
<p class="auth-card__subtitle"><?php esc_html_e('输入邮箱或用户名,我们将发送重置链接', 'aksrt'); ?></p>
<?php if (!empty($errors)) : ?>
<div class="alert alert--error" role="alert">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/><line x1="12" x2="12.01" y1="16" y2="16"/></svg>
<span><?php echo esc_html($errors[0]); ?></span>
</div>
<?php endif; ?>
<?php if (!empty($success)) : ?>
<div class="alert alert--success" role="alert">
<span><?php echo esc_html($success); ?></span>
</div>
<?php endif; ?>
<form class="auth-form" id="aksrt-request-reset-form" method="post" action="">
<?php wp_nonce_field('aksrt_reset_password', '_reset_nonce'); ?>
<input type="hidden" name="aksrt_action" value="request_reset">
<div class="form-field">
<label for="user_login"><?php esc_html_e('邮箱 / 用户名', 'aksrt'); ?></label>
<div class="input-wrap">
<span class="input-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg></span>
<input type="text" name="user_login" id="user_login" autocomplete="username" required>
</div>
</div>
<?php aksrt_render_captcha('lostpassword'); ?>
<button type="submit" class="btn-primary btn-lg btn-block"><?php esc_html_e('发送重置链接', 'aksrt'); ?></button>
</form>
<p class="auth-footer-link">
<?php esc_html_e('想起密码了?', 'aksrt'); ?>
<a href="<?php echo esc_url(aksrt_get_login_url()); ?>"><?php esc_html_e('返回登录', 'aksrt'); ?></a>
</p>
<?php endif; ?>
</div>
</div>
</main>
<?php
get_footer();
+127
View File
@@ -0,0 +1,127 @@
<?php
/**
* Template Name: AKSRT - Revenue
* Template Post Type: page
*
* 创作者收益中心页面 — 显示收益概览、分成记录和提现入口
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
if (!is_user_logged_in()) {
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink());
wp_safe_redirect($login_url);
exit;
}
$user_id = get_current_user_id();
// 通过 helper 获取收益视图模型
$revenue_view = function_exists('aksrt_get_revenue_page_view')
? aksrt_get_revenue_page_view($user_id)
: ['balance' => 0, 'total_earned' => 0, 'total_withdrawn' => 0, 'records' => []];
$balance = (float) ($revenue_view['balance'] ?? 0);
$total_earned = (float) ($revenue_view['total_earned'] ?? 0);
$total_withdrawn = (float) ($revenue_view['total_withdrawn'] ?? 0);
$records = $revenue_view['records'] ?? [];
$per_page = 20;
$paged = max(1, (int) ($_GET['rev_page'] ?? 1));
$total_records = count($records);
$total_pages = max(1, (int) ceil($total_records / $per_page));
$paged_records = array_slice($records, ($paged - 1) * $per_page, $per_page);
?>
<div class="container aksrt-revenue-page">
<div class="aksrt-revenue-page__header">
<h2><?php esc_html_e('创作收益', 'aksrt'); ?></h2>
</div>
<!-- 收益概览 -->
<div class="aksrt-revenue-page__stats">
<div class="aksrt-revenue-page__stat-card">
<div class="aksrt-revenue-page__stat-icon"><i class="fas fa-wallet"></i></div>
<div class="aksrt-revenue-page__stat-info">
<span class="aksrt-revenue-page__stat-value">¥<?php echo esc_html(number_format($balance, 2)); ?></span>
<span class="aksrt-revenue-page__stat-label"><?php esc_html_e('可提现余额', 'aksrt'); ?></span>
</div>
</div>
<div class="aksrt-revenue-page__stat-card">
<div class="aksrt-revenue-page__stat-icon"><i class="fas fa-chart-line"></i></div>
<div class="aksrt-revenue-page__stat-info">
<span class="aksrt-revenue-page__stat-value">¥<?php echo esc_html(number_format($total_earned, 2)); ?></span>
<span class="aksrt-revenue-page__stat-label"><?php esc_html_e('累计收益', 'aksrt'); ?></span>
</div>
</div>
<div class="aksrt-revenue-page__stat-card">
<div class="aksrt-revenue-page__stat-icon"><i class="fas fa-money-check-alt"></i></div>
<div class="aksrt-revenue-page__stat-info">
<span class="aksrt-revenue-page__stat-value">¥<?php echo esc_html(number_format($total_withdrawn, 2)); ?></span>
<span class="aksrt-revenue-page__stat-label"><?php esc_html_e('已提现', 'aksrt'); ?></span>
</div>
</div>
</div>
<!-- 提现按钮 -->
<?php if ($balance > 0) : ?>
<div class="aksrt-revenue-page__actions">
<a href="<?php echo esc_url(add_query_arg(['action' => 'withdraw'])); ?>" class="aksrt-revenue-page__withdraw-btn">
<i class="fas fa-hand-holding-usd"></i> <?php esc_html_e('申请提现', 'aksrt'); ?>
</a>
</div>
<?php endif; ?>
<!-- 分成记录 -->
<div class="aksrt-revenue-page__section">
<h3><?php esc_html_e('收益明细', 'aksrt'); ?></h3>
<?php if (!empty($paged_records)) : ?>
<div class="aksrt-revenue-page__list">
<?php foreach ($paged_records as $record) :
$r_type = (string) ($record['type'] ?? 'earning');
?>
<div class="aksrt-revenue-page__item">
<div class="aksrt-revenue-page__item-icon">
<i class="<?php echo $r_type === 'withdrawal' ? 'fas fa-arrow-up' : 'fas fa-arrow-down'; ?>"
style="color: <?php echo $r_type === 'withdrawal' ? '#f5222d' : '#52c41a'; ?>;"></i>
</div>
<div class="aksrt-revenue-page__item-info">
<span class="aksrt-revenue-page__item-title"><?php echo esc_html($record['title'] ?? ''); ?></span>
<span class="aksrt-revenue-page__item-time"><?php echo esc_html($record['created_at'] ?? ''); ?></span>
</div>
<div class="aksrt-revenue-page__item-amount" style="color: <?php echo $r_type === 'withdrawal' ? '#f5222d' : '#52c41a'; ?>;">
<?php echo $r_type === 'withdrawal' ? '-' : '+'; ?>¥<?php echo esc_html(number_format((float) ($record['amount'] ?? 0), 2)); ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if ($total_pages > 1) : ?>
<div class="aksrt-revenue-page__pagination">
<?php for ($i = 1; $i <= $total_pages; $i++) :
$page_url = add_query_arg(['rev_page' => $i]);
?>
<?php if ($i === $paged) : ?>
<span class="aksrt-revenue-page__page-chip is-active"><?php echo esc_html((string) $i); ?></span>
<?php else : ?>
<a href="<?php echo esc_url($page_url); ?>" class="aksrt-revenue-page__page-chip"><?php echo esc_html((string) $i); ?></a>
<?php endif; ?>
<?php endfor; ?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="aksrt-revenue-page__empty">
<i class="fas fa-chart-pie"></i>
<p><?php esc_html_e('暂无收益记录', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
</div>
<?php get_footer(); ?>
+345
View File
@@ -0,0 +1,345 @@
<?php
/**
* Template Name: AKSRT - 登录/注册
* Template Post Type: page
*
* @package AKSRT
*/
@session_start();
$route_tab = function_exists('aksrt_get_auth_tab') ? aksrt_get_auth_tab() : 'login';
$requested_tab = isset($_GET['tab']) ? sanitize_text_field(wp_unslash((string) $_GET['tab'])) : '';
$tab_aliases = [
'login' => 'signin',
'signin' => 'signin',
'register' => 'signup',
'signup' => 'signup',
];
$tab_source = $requested_tab !== '' ? $requested_tab : $route_tab;
$tab = $tab_aliases[$tab_source] ?? 'signin';
$redirect_to = !empty($_GET['redirect_to']) ? esc_url_raw(wp_unslash((string) $_GET['redirect_to'])) : home_url();
$registration_open = class_exists('AKSRT_Settings') && AKSRT_Settings::registration_enabled();
$titles = [
'signin' => __('用户登录', 'aksrt'),
'signup' => __('用户注册', 'aksrt'),
];
$page_title = ($titles[$tab] ?? __('登录 / 注册', 'aksrt')) . ' - ' . get_bloginfo('name');
$interim = isset($_REQUEST['interim-login']);
// 搂4.1 Defense-in-depth: redirect out when close_sign is active (backup of auth router gate)
if (class_exists('AKSRT_Settings') && AKSRT_Settings::close_sign() && !is_user_logged_in() && !$interim) {
wp_safe_redirect(home_url('/'), 302);
exit;
}
// §4.1 close_signin: password login disabled — force signin tab to show social-only message
$close_signin = class_exists('AKSRT_Settings') ? AKSRT_Settings::close_signin() : false;
if ($close_signin && $tab === 'signin') {
// Don't allow password form submission
$_SERVER['REQUEST_METHOD'] = 'GET';
}
$user_id = get_current_user_id();
if ($user_id) {
if ($interim && is_super_admin()) {
echo '<!DOCTYPE HTML><html><body class="login interim-login-success"><div style="padding:60px 0;text-align:center;color:#1a88fd;">' . esc_html__('登录成功', 'aksrt') . '</div></body></html>';
exit;
}
wp_safe_redirect($redirect_to);
exit;
}
if ($tab === 'signup' && !$registration_open) {
$tab = 'signin';
}
remove_action('wp_footer', 'aksrt_render_footer_tabbar', 20);
remove_action('wp_footer', 'aksrt_render_float_buttons', 30);
$background_html = '';
$sign_ui = class_exists('AKSRT_Settings') ? AKSRT_Settings::sign_page_ui() : [];
$card_position = $sign_ui['card_position'] ?? 'center';
$sign_show_logo = $sign_ui['show_logo'] ?? true;
$sign_show_header = $sign_ui['show_header'] ?? true;
$sign_theme_mode = $sign_ui['theme_mode'] ?? 'default';
if (class_exists('AKSRT_Settings')) {
$bg_ids = $sign_ui['background'] ?? [];
if (is_array($bg_ids) && !empty($bg_ids)) {
$bg_url = wp_get_attachment_url((int) $bg_ids[array_rand($bg_ids)]);
if ($bg_url) {
$background_html = '<div class="sign-bg" style="background-image:url(' . esc_url($bg_url) . ');"></div>';
}
} elseif (is_string($bg_ids) && $bg_ids !== '') {
// BC: comma-separated string from old storage
$ids = array_filter(array_map('absint', explode(',', $bg_ids)));
if (!empty($ids)) {
$bg_url = wp_get_attachment_url($ids[array_rand($ids)]);
if ($bg_url) {
$background_html = '<div class="sign-bg" style="background-image:url(' . esc_url($bg_url) . ');"></div>';
}
}
}
}
$login_error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['log'], $_POST['pwd']) && $tab === 'signin') {
if (class_exists('AKSRT_Auth_Service')) {
$result = AKSRT_Auth_Service::login_user($_POST, false);
if (empty($result['success'])) {
$login_error = $result['error'] ?? __('登录失败', 'aksrt');
} else {
wp_safe_redirect($result['redirect'] ?? home_url('/'));
exit;
}
}
}
$register_error = '';
$register_success = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['user_login'], $_POST['user_email']) && $tab === 'signup') {
if (class_exists('AKSRT_Auth_Service')) {
$result = AKSRT_Auth_Service::register_user($_POST, ['oauth_bind_data' => null, 'obk' => '']);
if (!empty($result['success'])) {
if (!empty($result['verification_required'])) {
$register_success = sprintf(
'%s <a href="%s">%s</a>',
esc_html($result['message'] ?? __('Registration successful. Please complete email verification.', 'aksrt')),
esc_url($result['verify_url'] ?? ''),
esc_html__('前往验证', 'aksrt')
);
} else {
$register_success = sprintf(
__('Registration successful. Please continue on the <a href="%s">login page</a>.', 'aksrt'),
esc_url(aksrt_get_login_url())
);
}
} else {
$register_error = $result['error'] ?? __('注册失败', 'aksrt');
}
}
}
$invite_prefill = '';
if (!empty($_GET['invite'])) {
$invite_prefill = sanitize_key(wp_unslash($_GET['invite']));
} elseif (!empty($_COOKIE['aksrt_invite'])) {
$invite_prefill = sanitize_key(wp_unslash($_COOKIE['aksrt_invite']));
}
?>
<!DOCTYPE html>
<html lang="<?php echo esc_attr(str_replace('_', '-', get_bloginfo('language'))); ?>">
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
<meta name="robots" content="noindex, nofollow">
<title><?php echo esc_html($page_title); ?></title>
<?php wp_head(); ?>
<style>
.sign-page{min-height:100vh;display:flex;align-items:center;justify-content:center;position:relative;padding:70px 16px 40px}
.sign-bg{position:fixed;inset:0;background-repeat:no-repeat;background-size:cover;background-position:center;z-index:0}
.sign-card{position:relative;z-index:1;width:100%;max-width:420px;background:var(--main-bg-color,#fff);border-radius:var(--main-radius,8px);box-shadow:0 0 10px var(--main-shadow,rgba(116,116,116,0.08));padding:30px 35px}
.sign-card--left{margin-right:auto}
.sign-card--right{margin-left:auto}
.sign-card--center{margin:0 auto}
.sign-logo{text-align:center;margin-bottom:20px}
.sign-logo img{height:36px}
.sign-logo__text{display:inline-flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:12px;background:var(--focus-color,#f04494);color:#fff;font-size:18px;font-weight:700;text-decoration:none}
.sign-tabs{display:flex;border-bottom:2px solid var(--main-border-color,rgba(50,50,50,0.06));margin-bottom:20px}
.sign-tab{flex:1;padding:10px 0;border:none;background:none;font-size:15px;cursor:pointer;color:var(--muted-color,#777);border-bottom:2px solid transparent;margin-bottom:-2px;transition:all .2s;text-align:center;font-weight:500}
.sign-tab.is-active{color:var(--focus-color,#f04494);border-bottom-color:var(--focus-color,#f04494);font-weight:600}
.sign-tab:hover{color:var(--focus-color,#f04494)}
.sign-panel{display:none}
.sign-panel.is-active{display:block}
.sign-error{background:rgba(239,68,68,0.08);border:1px solid rgba(239,68,68,0.2);border-radius:var(--main-radius,8px);padding:10px 14px;font-size:13px;color:#ef4444;margin-bottom:16px}
.sign-success{background:rgba(16,185,129,0.08);border:1px solid rgba(16,185,129,0.2);border-radius:var(--main-radius,8px);padding:10px 14px;font-size:13px;color:#10b981;margin-bottom:16px}
.sign-form{display:flex;flex-direction:column;gap:16px}
.sign-form .form-field{display:flex;flex-direction:column;gap:6px}
.sign-form label{font-size:14px;font-weight:500;color:var(--main-color,#4e5358)}
.sign-form .input-wrap{position:relative}
.sign-form .input-wrap input{width:100%;height:44px;padding:0 14px;border:1px solid transparent;background:var(--body-bg-color,#f5f6f7);border-radius:var(--main-radius,8px);font-size:14px;color:var(--key-color,#333);transition:border-color .15s}
.sign-form .input-wrap input:focus{outline:none;border-color:var(--focus-color,#f04494);box-shadow:0 0 2px 3px var(--focus-shadow-color,rgba(253,83,161,0.4))}
.sign-form .toggle-pwd{position:absolute;right:10px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;color:var(--muted-2-color,#999);padding:4px}
.sign-form .form-row-between{display:flex;align-items:center;justify-content:space-between;font-size:13px}
.sign-form .form-row-between a{color:var(--focus-color,#f04494)}
.sign-form .form-row-between label{display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;color:var(--muted-color,#777)}
.sign-form .btn-submit{width:100%;height:44px;border:none;border-radius:var(--main-radius,8px);background:var(--focus-color,#f04494);color:#fff;font-size:15px;font-weight:600;cursor:pointer;transition:opacity .15s}
.sign-form .btn-submit:hover{opacity:.85}
.sign-form .btn-submit:disabled{opacity:.5;cursor:not-allowed}
.sign-hint{margin:0;font-size:13px;color:var(--muted-color,#777)}
.sign-terms{display:flex;align-items:flex-start;gap:8px;font-size:13px;cursor:pointer;color:var(--muted-color,#777)}
.sign-footer{text-align:center;margin-top:20px;font-size:14px;color:var(--muted-color,#777)}
.sign-footer a{color:var(--focus-color,#f04494)}
.sign-footer-bar{position:fixed;bottom:0;left:0;right:0;text-align:center;padding:10px;font-size:12px;color:var(--muted-2-color,#999);z-index:1}
@media(max-width:480px){.sign-card{padding:24px 20px}}
</style>
</head>
<body <?php body_class('page-template-user-sign' . ($sign_theme_mode !== 'default' ? ' sign-theme-' . esc_attr($sign_theme_mode) : '')); ?>>
<?php echo $background_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<main class="sign-page">
<div class="sign-card sign-card--<?php echo esc_attr($card_position); ?>">
<?php if ($sign_show_logo) : ?>
<div class="sign-logo">
<?php $logo_url = class_exists('AKSRT_Settings') ? AKSRT_Settings::user_logo_url() : ''; ?>
<?php if ($logo_url) : ?>
<a href="<?php echo esc_url(home_url('/')); ?>"><img src="<?php echo esc_url($logo_url); ?>" alt="<?php echo esc_attr(get_bloginfo('name')); ?>"></a>
<?php else : ?>
<a href="<?php echo esc_url(home_url('/')); ?>" class="sign-logo__text"><?php echo esc_html(mb_substr(get_bloginfo('name'), 0, 2)); ?></a>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ($sign_show_header) : ?>
<div class="sign-tabs">
<button type="button" class="sign-tab<?php echo $tab === 'signin' ? ' is-active' : ''; ?>" data-sign-tab="signin"><?php esc_html_e('登录', 'aksrt'); ?></button>
<?php if ($registration_open) : ?>
<button type="button" class="sign-tab<?php echo $tab === 'signup' ? ' is-active' : ''; ?>" data-sign-tab="signup"><?php esc_html_e('注册', 'aksrt'); ?></button>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="sign-panel<?php echo $tab === 'signin' ? ' is-active' : ''; ?>" data-sign-panel="signin">
<?php if ($login_error) : ?><div class="sign-error"><?php echo esc_html($login_error); ?></div><?php endif; ?>
<?php if ($close_signin) : ?>
<div class="sign-notice" style="padding:16px;text-align:center;color:var(--muted-2-color,#666);">
<?php esc_html_e('This site currently supports third-party account sign-in only. Please use one of the social login buttons below.', 'aksrt'); ?>
</div>
<?php else : ?>
<form class="sign-form" id="sign-login-form" method="post" action="">
<?php wp_nonce_field('aksrt_login', '_login_nonce'); ?>
<div class="form-field">
<label for="sign-user-login"><?php esc_html_e('Email / Username', 'aksrt'); ?></label>
<div class="input-wrap"><input type="text" name="log" id="sign-user-login" autocomplete="username" required placeholder="<?php esc_attr_e('Please enter your email or username', 'aksrt'); ?>"></div>
</div>
<div class="form-field">
<label for="sign-user-pass"><?php esc_html_e('密码', 'aksrt'); ?></label>
<div class="input-wrap">
<input type="password" name="pwd" id="sign-user-pass" autocomplete="current-password" required>
<button type="button" class="toggle-pwd" data-target="sign-user-pass"><i class="fas fa-eye"></i></button>
</div>
</div>
<div class="form-row-between">
<label><input type="checkbox" name="rememberme" value="forever"> <?php esc_html_e('Remember me', 'aksrt'); ?></label>
<a href="<?php echo esc_url(aksrt_get_reset_password_url()); ?>"><?php esc_html_e('Forgot password?', 'aksrt'); ?></a>
</div>
<?php if (function_exists('aksrt_render_captcha')) aksrt_render_captcha('login'); ?>
<input type="hidden" name="redirect_to" value="<?php echo esc_url($redirect_to); ?>">
<button type="submit" class="btn-submit"><?php esc_html_e('登录', 'aksrt'); ?></button>
</form>
<?php endif; // close_signin gate ?>
<?php if (class_exists('AKSRT_OAuth')) : ?>
<?php $social = AKSRT_OAuth::get_instance()->render_social_buttons(); ?>
<?php if ($social) echo '<div style="margin-top:20px;">' . $social . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
<?php if ($registration_open) : ?>
<div class="sign-footer"><?php esc_html_e('Don\'t have an account yet?', 'aksrt'); ?> <a href="#" data-sign-switch="signup"><?php esc_html_e('Register now', 'aksrt'); ?></a></div>
<?php endif; ?>
</div>
<?php if ($registration_open) : ?>
<div class="sign-panel<?php echo $tab === 'signup' ? ' is-active' : ''; ?>" data-sign-panel="signup">
<?php if ($register_error) : ?><div class="sign-error"><?php echo esc_html($register_error); ?></div><?php endif; ?>
<?php if ($register_success) : ?><div class="sign-success"><?php echo wp_kses($register_success, ['a' => ['href' => []]]); ?></div><?php endif; ?>
<form class="sign-form" id="sign-register-form" method="post" action="" novalidate>
<?php wp_nonce_field('aksrt_register', '_register_nonce'); ?>
<div class="form-field">
<label for="sign-reg-login"><?php esc_html_e('Username', 'aksrt'); ?></label>
<div class="input-wrap"><input type="text" name="user_login" id="sign-reg-login" autocomplete="username" required placeholder="<?php esc_attr_e('请输入用户名', 'aksrt'); ?>"></div>
</div>
<div class="form-field">
<label for="sign-reg-email"><?php esc_html_e('Email Address', 'aksrt'); ?></label>
<div class="input-wrap"><input type="email" name="user_email" id="sign-reg-email" autocomplete="email" required placeholder="<?php esc_attr_e('Please enter your email', 'aksrt'); ?>"></div>
</div>
<div class="form-field">
<label for="sign-reg-pass"><?php esc_html_e('登录密码', 'aksrt'); ?></label>
<div class="input-wrap">
<input type="password" name="user_pass" id="sign-reg-pass" autocomplete="new-password" required minlength="6">
<button type="button" class="toggle-pwd" data-target="sign-reg-pass"><i class="fas fa-eye"></i></button>
</div>
</div>
<div class="form-field">
<label for="sign-reg-pass2"><?php esc_html_e('确认密码', 'aksrt'); ?></label>
<div class="input-wrap">
<input type="password" name="user_pass_confirm" id="sign-reg-pass2" autocomplete="new-password" required minlength="6">
<button type="button" class="toggle-pwd" data-target="sign-reg-pass2"><i class="fas fa-eye"></i></button>
</div>
</div>
<?php
$invite_cfg = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_invite_registration_config() : null;
if ($invite_cfg && ($invite_cfg['register_field'] ?? 'hidden') !== 'hidden') {
$mode = (string) $invite_cfg['register_field'];
$invit_hint = $mode === 'required'
? (string) ($invite_cfg['must_desc'] ?? '')
: (string) ($invite_cfg['open_desc'] ?? '');
?>
<div class="form-field">
<label for="sign-invite"><?php esc_html_e('邀请码', 'aksrt'); ?><?php echo $mode === 'optional' ? ' <small style="color:var(--muted-color);">(' . esc_html__('选填', 'aksrt') . ')</small>' : ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></label>
<div class="input-wrap"><input type="text" name="invite_code" id="sign-invite" maxlength="32" autocomplete="off" value="<?php echo esc_attr($invite_prefill); ?>" <?php echo $mode === 'required' ? 'required' : ''; ?>></div>
<?php if ($invit_hint !== '') : ?>
<p class="sign-hint"><?php echo wp_kses_post($invit_hint); ?></p>
<?php endif; ?>
</div>
<?php } ?>
<label class="sign-terms">
<input type="checkbox" name="agree_terms" value="1" required>
<span><?php esc_html_e('I agree to the Terms of Service', 'aksrt'); ?></span>
</label>
<?php if (class_exists('AKSRT_Settings') && AKSRT_Settings::registration_requires_email_verification()) : ?>
<p class="sign-hint"><?php esc_html_e('Email verification is required after registration.', 'aksrt'); ?></p>
<?php endif; ?>
<?php if (function_exists('aksrt_render_captcha')) aksrt_render_captcha('register'); ?>
<button type="submit" class="btn-submit"><?php esc_html_e('注册', 'aksrt'); ?></button>
</form>
<div class="sign-footer"><?php esc_html_e('Already have an account?', 'aksrt'); ?> <a href="#" data-sign-switch="signin"><?php esc_html_e('Sign in now', 'aksrt'); ?></a></div>
</div>
<?php endif; ?>
</div>
</main>
<script>
(function(){
document.querySelectorAll('[data-sign-tab]').forEach(function(tab){
tab.addEventListener('click',function(){
var target=this.getAttribute('data-sign-tab');
this.parentElement.querySelectorAll('.sign-tab').forEach(function(t){t.classList.remove('is-active');});
this.classList.add('is-active');
var card=this.closest('.sign-card');
card.querySelectorAll('.sign-panel[data-sign-panel]').forEach(function(p){p.classList.remove('is-active');});
var panel=card.querySelector('[data-sign-panel="'+target+'"]');
if(panel)panel.classList.add('is-active');
});
});
document.querySelectorAll('[data-sign-switch]').forEach(function(link){
link.addEventListener('click',function(e){
e.preventDefault();
var target=this.getAttribute('data-sign-switch');
var btn=document.querySelector('[data-sign-tab="'+target+'"]');
if(btn)btn.click();
});
});
document.querySelectorAll('.toggle-pwd').forEach(function(btn){
btn.addEventListener('click',function(){
var input=document.getElementById(this.getAttribute('data-target'));
if(!input)return;
var isPw=input.type==='password';
input.type=isPw?'text':'password';
this.querySelector('i').className=isPw?'fas fa-eye-slash':'fas fa-eye';
});
});
})();
</script>
<?php if (!$interim) : ?>
<div class="sign-footer-bar">
<?php
$footer_text = $sign_ui['footer'] ?? '';
echo $footer_text ? wp_kses_post($footer_text) : '&copy; ' . date('Y') . ' ' . esc_html(get_bloginfo('name'));
?>
</div>
<?php endif; ?>
<?php wp_footer(); ?>
</body>
</html>
+148
View File
@@ -0,0 +1,148 @@
<?php
/**
* Template Name: AKSRT - Special Page
*
* 专题聚合页模板 — 用于内容专题、活动页、产品介绍页等。
* 读取 AKSRT_Settings 中的 page_display.special 配置,支持 per-page meta 覆盖。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$post_id = get_the_ID();
// 从门面 getter 读取专题页设置(validated & sanitized)
$special = class_exists('AKSRT_Settings')
? AKSRT_Settings::get_special_page_settings()
: [];
$defaults = [
'cover_enabled' => true,
'summary_enabled' => true,
'summary_length' => 200,
'default_cover' => '',
'layout' => 'grid',
'columns' => 2,
'source_type' => 'category',
'source_ids' => '',
'subpage_display' => 'cards',
'subpage_count' => 6,
'sidebar' => true,
'pagination' => 'numeric',
'per_page' => 12,
'post_count' => false,
'orderby' => 'date',
];
$settings = array_merge($defaults, $special);
// Per-page meta override
$meta_layout = get_post_meta($post_id, '_aksrt_special_layout', true);
$meta_columns = get_post_meta($post_id, '_aksrt_special_columns', true);
$meta_source = get_post_meta($post_id, '_aksrt_special_source_type', true);
$meta_ids = get_post_meta($post_id, '_aksrt_special_source_ids', true);
if ($meta_layout) $settings['layout'] = sanitize_key($meta_layout);
if ($meta_columns) $settings['columns'] = max(1, min(4, (int) $meta_columns));
if ($meta_source) $settings['source_type'] = sanitize_key($meta_source);
if ($meta_ids !== '' && $meta_ids !== false) $settings['source_ids'] = $meta_ids;
$layout = in_array($settings['layout'], ['grid', 'list', 'masonry'], true) ? $settings['layout'] : 'grid';
$columns = max(1, min(4, (int) $settings['columns']));
$show_cover = !empty($settings['cover_enabled']);
$show_summary = !empty($settings['summary_enabled']);
$summary_len = max(50, min(500, (int) $settings['summary_length']));
$subpage_style = in_array($settings['subpage_display'], ['cards', 'list', 'timeline'], true) ? $settings['subpage_display'] : 'cards';
$per_page = max(4, min(48, (int) $settings['per_page']));
$orderby = in_array($settings['orderby'], ['date', 'modified', 'views', 'comments'], true) ? $settings['orderby'] : 'date';
// 聚合内容源
$source_type = sanitize_key($settings['source_type']);
$source_ids = array_filter(array_map('absint', explode(',', (string) $settings['source_ids'])));
$aggregated_posts = [];
if (!empty($source_ids)) {
$args = [
'posts_per_page' => $per_page,
'post_status' => 'publish',
'orderby' => ($orderby === 'modified') ? 'modified' : 'date',
'order' => 'DESC',
];
if ($source_type === 'category') {
$args['category__in'] = $source_ids;
} elseif ($source_type === 'tag') {
$args['tag__in'] = $source_ids;
}
$aggregated_query = new WP_Query($args);
$aggregated_posts = $aggregated_query->posts;
}
// 子页面列表
$children_per_page = max(1, min(20, (int) $settings['subpage_count']));
$children = get_pages([
'child_of' => $post_id,
'sort_column' => 'menu_order',
'sort_order' => 'ASC',
'parent' => $post_id,
'number' => $children_per_page,
]);
?>
<main id="main-content" class="site-main content-shell content-shell--page aksrt-special--<?php echo esc_attr($layout); ?>" tabindex="-1">
<?php if (function_exists('aksrt_breadcrumbs')) { aksrt_breadcrumbs(); } ?>
<?php if ($show_cover) :
$default_cover = $settings['default_cover'] ?? '';
$show_post_count = !empty($settings['post_count']);
include get_template_directory() . '/templates/parts/special/special-cover.php';
endif; ?>
<div class="aksrt-special-content container">
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<div class="aksrt-special-content__body">
<?php the_content(); ?>
</div>
<?php endwhile; endif; ?>
</div>
<?php if (!empty($aggregated_posts)) : ?>
<section class="aksrt-special-aggregated container">
<h2 class="aksrt-special-aggregated__title"><?php esc_html_e('相关内容', 'aksrt'); ?></h2>
<?php
// Use the special-grid part
$grid_posts = $aggregated_posts;
$show_dates = true;
include get_template_directory() . '/templates/parts/special/special-grid.php';
?>
</section>
<?php endif; ?>
<?php if (!empty($children)) : ?>
<section class="aksrt-special-children container">
<h2 class="aksrt-special-children__title"><?php esc_html_e('子页面', 'aksrt'); ?></h2>
<div class="aksrt-special-children__grid aksrt-special-children__grid--<?php echo esc_attr($subpage_style); ?>" style="--aksrt-cols:<?php echo esc_attr((string) $columns); ?>">
<?php foreach ($children as $child) :
$child_img = get_the_post_thumbnail_url($child->ID, 'medium');
?>
<a href="<?php echo esc_url(get_permalink($child->ID)); ?>" class="aksrt-special-child-card">
<?php if ($child_img) : ?>
<div class="aksrt-special-child-card__image">
<img src="<?php echo esc_url($child_img); ?>" alt=""<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
</div>
<?php endif; ?>
<div class="aksrt-special-child-card__body">
<h3 class="aksrt-special-child-card__title"><?php echo esc_html($child->post_title); ?></h3>
<?php if ($child->post_excerpt !== '') : ?>
<p class="aksrt-special-child-card__desc"><?php echo esc_html(wp_trim_words($child->post_excerpt, 30)); ?></p>
<?php endif; ?>
</div>
</a>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
</main>
<?php
get_footer();
+292
View File
@@ -0,0 +1,292 @@
<?php
/**
* Template Name: AKSRT - Submit
*
* 前台投稿页面模板。
* 支持标题、摘要、分类、标签、正文、封面、草稿保存。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!is_user_logged_in()) {
wp_redirect(aksrt_get_login_url(get_permalink()));
exit;
}
$user_id = get_current_user_id();
$can_submit = class_exists('AKSRT_Front_Submit') && AKSRT_Front_Submit::get_instance()->can_submit($user_id);
if (!$can_submit) {
wp_redirect(home_url('/'));
exit;
}
// Check if editing existing draft
$edit_id = isset($_GET['edit']) ? absint($_GET['edit']) : 0;
$edit_post = null;
if ($edit_id > 0) {
$edit_post = get_post($edit_id);
if (!$edit_post || (int) $edit_post->post_author !== $user_id || $edit_post->post_status !== 'draft') {
$edit_post = null;
$edit_id = 0;
}
}
get_header();
// Submit settings
$submit_settings = [];
if (class_exists('AKSRT_Settings')) {
$all = AKSRT_Settings::get_all();
$submit_settings = [
'enabled' => !empty($all['front_submit_enabled']),
'review_mode' => $all['front_submit_review_mode'] ?? 'pending',
'allow_cover' => !isset($all['front_submit_cover']) || !empty($all['front_submit_cover']),
'allow_excerpt' => !isset($all['front_submit_excerpt']) || !empty($all['front_submit_excerpt']),
'allow_category' => !isset($all['front_submit_category']) || !empty($all['front_submit_category']),
'allow_tags' => !isset($all['front_submit_tags']) || !empty($all['front_submit_tags']),
'max_images' => (int) ($all['front_submit_max_images'] ?? 5),
'allowed_cat_ids' => is_array($all['post_article_cat'] ?? null) ? array_map('intval', $all['post_article_cat']) : [],
'topics_s' => !empty($all['post_article_topics_s']),
'custom_filter_s' => !isset($all['post_article_custom_filter_s']) || !empty($all['post_article_custom_filter_s']),
];
}
// §3.7 topics: load topics taxonomy terms
$topics_terms = [];
if (!empty($submit_settings['topics_s']) && taxonomy_exists('topics')) {
$topics_terms = get_terms(['taxonomy' => 'topics', 'hide_empty' => false, 'orderby' => 'name', 'order' => 'ASC']);
if (is_wp_error($topics_terms)) $topics_terms = [];
}
$current_topics = [];
if ($edit_post && taxonomy_exists('topics')) {
$terms = wp_get_object_terms($edit_post->ID, 'topics', ['fields' => 'ids']);
if (!is_wp_error($terms)) $current_topics = $terms;
}
// §3.4 custom_filter: load dimension definitions for submit form
$custom_dimensions = [];
if (!empty($submit_settings['custom_filter_s']) && class_exists('AKSRT_Settings')) {
$filter_all = AKSRT_Settings::get_post_filter_settings();
$custom_dimensions = $filter_all['custom_filter'];
}
// §3.4 custom_filter: load current values for edit mode
$current_cf = [];
if ($edit_post && !empty($custom_dimensions)) {
foreach ($custom_dimensions as $dim) {
$current_cf[$dim['key']] = get_post_meta($edit_post->ID, '_aksrt_cf_' . $dim['key'], true);
}
}
// §3.7 post_article_cat: filter categories by allowed list
$cat_args = [
'taxonomy' => 'category',
'hide_empty' => false,
'orderby' => 'name',
'order' => 'ASC',
];
if (!empty($submit_settings['allowed_cat_ids'])) {
$cat_args['include'] = $submit_settings['allowed_cat_ids'];
}
$categories = get_categories($cat_args);
// Current values for edit mode
$current_title = $edit_post ? $edit_post->post_title : '';
$current_content = $edit_post ? $edit_post->post_content : '';
$current_excerpt = $edit_post ? $edit_post->post_excerpt : '';
$current_cats = $edit_post ? wp_get_post_categories($edit_post->ID) : [];
$current_tags = $edit_post ? implode(', ', wp_get_post_tags($edit_post->ID, ['fields' => 'names'])) : '';
$current_thumb = $edit_post ? get_the_post_thumbnail_url($edit_post->ID, 'large') : '';
?>
<main id="main-content" class="site-main content-shell content-shell--page" tabindex="-1">
<div class="aksrt-submit-page container">
<header class="aksrt-submit-page__header">
<h1 class="aksrt-submit-page__title">
<?php echo $edit_post ? esc_html__('编辑草稿', 'aksrt') : esc_html__('投稿', 'aksrt'); ?>
</h1>
<p class="aksrt-submit-page__desc">
<?php echo $edit_post
? esc_html__('继续编辑你的草稿,完成后提交审核。', 'aksrt')
: esc_html__('分享你的内容,提交后将进入审核流程。', 'aksrt'); ?>
</p>
</header>
<form class="aksrt-submit-form" id="aksrt-submit-form" data-aksrt-submit-form data-dirty="false">
<?php if ($edit_id > 0) : ?>
<input type="hidden" name="edit_id" value="<?php echo esc_attr((string) $edit_id); ?>">
<?php endif; ?>
<div class="aksrt-form-group">
<label for="submit-title"><?php esc_html_e('标题', 'aksrt'); ?> *</label>
<input type="text" id="submit-title" name="title" class="aksrt-form-input" required maxlength="200" placeholder="<?php esc_attr_e('请输入文章标题', 'aksrt'); ?>" value="<?php echo esc_attr($current_title); ?>">
</div>
<?php if (!empty($submit_settings['allow_excerpt'])) : ?>
<div class="aksrt-form-group">
<label for="submit-excerpt"><?php esc_html_e('摘要', 'aksrt'); ?></label>
<textarea id="submit-excerpt" name="excerpt" class="aksrt-form-input" rows="3" maxlength="300" placeholder="<?php esc_attr_e('可选:文章摘要', 'aksrt'); ?>"><?php echo esc_textarea($current_excerpt); ?></textarea>
</div>
<?php endif; ?>
<div class="aksrt-submit-form__row">
<?php if (!empty($submit_settings['allow_category'])) : ?>
<div class="aksrt-form-group aksrt-submit-form__col">
<label for="submit-category"><?php esc_html_e('分类', 'aksrt'); ?></label>
<select id="submit-category" name="category" class="aksrt-form-input">
<option value=""><?php esc_html_e('选择分类', 'aksrt'); ?></option>
<?php foreach ($categories as $cat) : ?>
<option value="<?php echo esc_attr((string) $cat->term_id); ?>" <?php selected(in_array($cat->term_id, $current_cats)); ?>><?php echo esc_html($cat->name); ?></option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<?php if (!empty($submit_settings['allow_tags'])) : ?>
<div class="aksrt-form-group aksrt-submit-form__col">
<label for="submit-tags"><?php esc_html_e('标签', 'aksrt'); ?></label>
<input type="text" id="submit-tags" name="tags" class="aksrt-form-input" placeholder="<?php esc_attr_e('多个标签用逗号分隔', 'aksrt'); ?>" value="<?php echo esc_attr($current_tags); ?>">
</div>
<?php endif; ?>
</div>
<?php if (!empty($submit_settings['topics_s']) && !empty($topics_terms)) : ?>
<div class="aksrt-form-group">
<label for="submit-topics"><?php esc_html_e('专题', 'aksrt'); ?></label>
<select id="submit-topics" name="topics[]" class="aksrt-form-input" multiple>
<?php foreach ($topics_terms as $topic) : ?>
<option value="<?php echo esc_attr((string) $topic->term_id); ?>" <?php selected(in_array($topic->term_id, $current_topics)); ?>><?php echo esc_html($topic->name); ?></option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<?php if (!empty($submit_settings['custom_filter_s']) && !empty($custom_dimensions)) : ?>
<?php foreach ($custom_dimensions as $dim) : ?>
<div class="aksrt-form-group">
<label for="submit-cf-<?php echo esc_attr($dim['key']); ?>"><?php echo esc_html($dim['name']); ?></label>
<select id="submit-cf-<?php echo esc_attr($dim['key']); ?>" name="cf_<?php echo esc_attr($dim['key']); ?>" class="aksrt-form-input">
<option value=""><?php printf(esc_html__('选择%s', 'aksrt'), esc_html($dim['name'])); ?></option>
<?php foreach ($dim['vals'] as $val) : ?>
<option value="<?php echo esc_attr($val['key']); ?>" <?php selected(!empty($current_cf[$dim['key']]) && $current_cf[$dim['key']] === $val['key']); ?>><?php echo esc_html($val['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
<?php endforeach; ?>
<?php endif; ?>
<div class="aksrt-form-group">
<label for="submit-content"><?php esc_html_e('内容', 'aksrt'); ?> *</label>
<div class="aksrt-submit-form__editor-toolbar">
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="bold" title="<?php esc_attr_e('加粗', 'aksrt'); ?>"><i class="fas fa-bold"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="italic" title="<?php esc_attr_e('斜体', 'aksrt'); ?>"><i class="fas fa-italic"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="code" title="<?php esc_attr_e('代码', 'aksrt'); ?>"><i class="fas fa-code"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="link" title="<?php esc_attr_e('链接', 'aksrt'); ?>"><i class="fas fa-link"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="image" title="<?php esc_attr_e('图片', 'aksrt'); ?>"><i class="fas fa-image"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="quote" title="<?php esc_attr_e('引用', 'aksrt'); ?>"><i class="fas fa-quote-left"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="heading" title="<?php esc_attr_e('标题', 'aksrt'); ?>"><i class="fas fa-heading"></i></button>
<button type="button" class="aksrt-submit-form__toolbar-btn" data-action="list" title="<?php esc_attr_e('列表', 'aksrt'); ?>"><i class="fas fa-list-ul"></i></button>
</div>
<textarea id="submit-content" name="content" class="aksrt-form-input aksrt-submit-form__textarea" rows="16" required placeholder="<?php esc_attr_e('请输入文章内容(支持 HTML)', 'aksrt'); ?>"><?php echo esc_textarea($current_content); ?></textarea>
<div class="aksrt-submit-form__word-count" id="submit-word-count"></div>
</div>
<?php if (!empty($submit_settings['allow_cover'])) : ?>
<div class="aksrt-form-group">
<label for="submit-thumbnail"><?php esc_html_e('封面图', 'aksrt'); ?></label>
<div class="aksrt-submit-form__cover-input">
<input type="url" id="submit-thumbnail" name="thumbnail" class="aksrt-form-input" placeholder="<?php esc_attr_e('输入封面图地址', 'aksrt'); ?>" value="<?php echo esc_url($current_thumb); ?>">
<button type="button" class="aksrt-submit-form__cover-btn" id="aksrt-pick-cover">
<i class="fas fa-image"></i>
<?php esc_html_e('选择图片', 'aksrt'); ?>
</button>
</div>
<div class="aksrt-submit-form__cover-preview" id="cover-preview" <?php echo $current_thumb ? '' : 'style="display:none"'; ?>>
<img id="cover-preview-img" src="<?php echo esc_url($current_thumb); ?>" alt="">
<button type="button" class="aksrt-submit-form__cover-remove" id="cover-remove" title="<?php esc_attr_e('移除封面', 'aksrt'); ?>">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<?php endif; ?>
<div class="aksrt-submit-form__actions">
<button type="submit" class="aksrt-auth-submit" id="aksrt-submit-btn">
<i class="fas fa-paper-plane"></i>
<?php echo $edit_post ? esc_html__('更新投稿', 'aksrt') : esc_html__('提交投稿', 'aksrt'); ?>
</button>
<button type="button" class="btn-outline" id="aksrt-save-draft">
<i class="fas fa-save"></i>
<?php esc_html_e('保存草稿', 'aksrt'); ?>
</button>
</div>
<input type="hidden" name="_nonce" value="<?php echo esc_attr(wp_create_nonce('aksrt_submit')); ?>">
<input type="hidden" name="status" id="submit-status" value="pending">
</form>
<!-- Draft list -->
<?php
$user_drafts = get_posts([
'post_type' => 'post',
'post_status' => 'draft',
'author' => $user_id,
'numberposts' => 10,
'orderby' => 'modified',
'order' => 'DESC',
]);
if (!empty($user_drafts)) :
?>
<div class="aksrt-submit-drafts">
<h3 class="aksrt-submit-drafts__title">
<i class="fas fa-file-alt"></i>
<?php esc_html_e('我的草稿', 'aksrt'); ?>
</h3>
<div class="aksrt-submit-drafts__list">
<?php foreach ($user_drafts as $draft) : ?>
<div class="aksrt-submit-drafts__item">
<a href="<?php echo esc_url(add_query_arg('edit', $draft->ID, get_permalink())); ?>" class="aksrt-submit-drafts__link">
<span class="aksrt-submit-drafts__name"><?php echo esc_html($draft->post_title ?: __('无标题', 'aksrt')); ?></span>
<time class="aksrt-submit-drafts__date"><?php echo esc_html(get_the_date('Y-m-d H:i', $draft->ID)); ?></time>
</a>
<button type="button" class="aksrt-submit-drafts__delete" data-draft-id="<?php echo esc_attr((string) $draft->ID); ?>" title="<?php esc_attr_e('删除草稿', 'aksrt'); ?>">
<i class="fas fa-trash"></i>
</button>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
</main>
<?php
// Enqueue the front-submit script
$front_submit_path = get_template_directory() . '/assets/js/front-submit.js';
if (file_exists($front_submit_path)) {
wp_enqueue_script(
'aksrt-front-submit',
get_template_directory_uri() . '/assets/js/front-submit.js',
['aksrt-script'],
(string) filemtime($front_submit_path),
true
);
wp_localize_script('aksrt-front-submit', 'aksrtSubmitData', [
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('aksrt_submit_nonce'),
'i18n' => [
'confirmLeave' => __('你有未保存的更改,确定要离开吗?', 'aksrt'),
'saving' => __('保存中...', 'aksrt'),
'saved' => __('草稿已保存', 'aksrt'),
'submitting' => __('提交中...', 'aksrt'),
'submitted' => __('投稿已提交', 'aksrt'),
'error' => __('操作失败,请重试', 'aksrt'),
'confirmDelete' => __('确定要删除这个草稿吗?', 'aksrt'),
],
]);
}
get_footer();
+556
View File
@@ -0,0 +1,556 @@
<?php
/**
* Public user homepage template.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$target_user_nicename = get_query_var('user_nicename');
$user = $target_user_nicename ? get_user_by('slug', $target_user_nicename) : null;
if (!$user) {
wp_safe_redirect(home_url('/'));
exit;
}
$user_id = (int) $user->ID;
// 数据聚合:复用共享 helper
$dashboard = function_exists('aksrt_get_user_dashboard_view')
? aksrt_get_user_dashboard_view($user_id, ['viewer_id' => get_current_user_id()])
: [];
$is_own_profile = !empty($dashboard['is_own_profile']);
$user_home_settings = class_exists('AKSRT_Settings')
? AKSRT_Settings::get_user_home_settings()
: [
'tabs' => ['posts', 'comments', 'favorites'],
'sidebar' => true,
'cover' => true,
'follow' => true,
'posts_per_page' => 10,
];
$post_count = (int) ($dashboard['post_count'] ?? 0);
$comment_count = (int) ($dashboard['comment_count'] ?? 0);
$favorite_post_ids = $dashboard['favorite_post_ids'] ?? [];
$favorite_count = (int) ($dashboard['favorite_count'] ?? 0);
$user_contacts = $dashboard['user_contacts'] ?? [];
$user_center_settings = class_exists('AKSRT_Settings')
? AKSRT_Settings::get_user_center_settings()
: ['social_links' => true];
$per_page = (int) $user_home_settings['posts_per_page'];
$credits_enabled = !empty($dashboard['credits_enabled']);
$credit_balance = $dashboard['credit_balance'] ?? 0.0;
$currency_name = $dashboard['currency_name'] ?? __('积分', 'aksrt');
$user_home_manager = class_exists('AKSRT_User_Home') ? AKSRT_User_Home::get_instance() : null;
$visible_tabs = $user_home_manager ? $user_home_manager->get_visible_tabs($is_own_profile) : [];
$profile_signals = $dashboard['profile_signals'] ?? [];
$profile_notice = $dashboard['profile_notice'] ?? null;
$show_report_button = !empty($dashboard['show_report']);
$enabled_tabs = array_keys($visible_tabs);
if (empty($enabled_tabs)) {
$enabled_tabs = ['posts', 'comments', 'favorites'];
}
// Apply admin-configured display order
$_uh_order = $user_home_settings['tabs_order'] ?? [];
$_uh_ordered = [];
foreach ($_uh_order as $_t) {
if (in_array($_t, $enabled_tabs, true)) {
$_uh_ordered[] = $_t;
}
}
foreach ($enabled_tabs as $_t) {
if (!in_array($_t, $_uh_ordered, true)) {
$_uh_ordered[] = $_t;
}
}
$enabled_tabs = $_uh_ordered;
// Device visibility: build PC/mobile tab arrays
$_uh_pc_hide = $user_home_settings['tabs_pc_hide'] ?? [];
$_uh_mobile_hide = $user_home_settings['tabs_mobile_hide'] ?? [];
$enabled_tabs_pc = array_values(array_diff($enabled_tabs, $_uh_pc_hide));
$enabled_tabs_mobile = array_values(array_diff($enabled_tabs, $_uh_mobile_hide));
$_uh_is_mobile = function_exists('wp_is_mobile') && wp_is_mobile();
$display_tabs = $_uh_is_mobile ? $enabled_tabs_mobile : $enabled_tabs_pc;
$_uh_default_tab = !empty($display_tabs) ? $display_tabs[0] : $enabled_tabs[0];
$forum_instance = class_exists('AKSRT_Forum') ? AKSRT_Forum::get_instance() : null;
$forum_stats = $forum_instance ? $forum_instance->get_user_forum_stats($user_id) : [];
$forum_topic_count = (int) ($forum_stats['topic_count'] ?? 0);
$forum_followed_count = (int) ($forum_stats['followed_plate_count'] ?? 0);
$tab_counts = [
'posts' => $post_count,
'comments' => $comment_count,
'favorites' => $favorite_count,
'credits' => $credits_enabled && $is_own_profile ? (int) round($credit_balance) : 0,
'product' => 0,
'plate' => 0,
'forum' => $forum_topic_count,
'follow' => $forum_followed_count,
];
$tab_labels = [
'posts' => __('文章', 'aksrt'),
'comments' => __('评论', 'aksrt'),
'favorites' => __('收藏', 'aksrt'),
'credits' => sprintf(__('我的%s', 'aksrt'), $currency_name),
'product' => __('商品', 'aksrt'),
'plate' => __('圈子', 'aksrt'),
'forum' => __('论坛', 'aksrt'),
'follow' => __('关注', 'aksrt'),
];
foreach ($visible_tabs as $tab_key => $tab_config) {
if (!empty($tab_config['label'])) {
$tab_labels[$tab_key] = (string) $tab_config['label'];
}
}
$active_tab = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : $_uh_default_tab;
if (!in_array($active_tab, $enabled_tabs, true)) {
$active_tab = $_uh_default_tab;
}
$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);
require_once AKSRT_DIR . '/templates/parts/user-shared-helpers.php';
$svg_empty_post = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><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"/><path d="M9 13h6M9 17h6M9 9h1"/></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" stroke-linecap="round" stroke-linejoin="round"><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" stroke-linecap="round" stroke-linejoin="round"><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>';
$home_url = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($user) : home_url('/');
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<div class="content-layout<?php echo !empty($user_home_settings['sidebar']) ? '' : ' content-layout--no-sidebar'; ?> aksrt-user-home__layout">
<div class="content-layout__primary">
<?php
// 作者 Hero 区:共享 partial
set_query_var('user', $user);
set_query_var('dashboard', $dashboard);
set_query_var('user_home_settings', $user_home_settings);
set_query_var('is_author_page', false);
get_template_part('templates/parts/user/author-hero');
?>
<div class="dashboard dashboard--public" data-default-tab="<?php echo esc_attr($_uh_default_tab); ?>">
<div class="dashboard-tabs" role="tablist">
<?php foreach ($enabled_tabs as $tab_key) :
$_tab_class = $tab_key === $active_tab ? 'is-active' : '';
$_pc_hidden = in_array($tab_key, $_uh_pc_hide, true);
$_mobile_hidden = in_array($tab_key, $_uh_mobile_hide, true);
if ($_pc_hidden) $_tab_class .= ' tab-hide-pc';
if ($_mobile_hidden) $_tab_class .= ' tab-hide-mobile';
?>
<button type="button" class="<?php echo esc_attr(trim($_tab_class)); ?>" data-tab="<?php echo esc_attr($tab_key); ?>" role="tab">
<?php echo esc_html($tab_labels[$tab_key] ?? ucfirst($tab_key)); ?>
<span class="dashboard-tabs__count"><?php echo esc_html((string) ($tab_counts[$tab_key] ?? 0)); ?></span>
</button>
<?php endforeach; ?>
</div>
<?php if (in_array('posts', $enabled_tabs, true)) : ?>
<div id="panel-posts" class="dashboard-panel<?php echo $active_tab === 'posts' ? ' is-active' : ''; ?>" role="tabpanel"<?php echo $active_tab !== 'posts' ? ' hidden' : ''; ?>>
<?php
$author_posts = new WP_Query([
'author' => $user_id,
'posts_per_page' => $per_page,
'post_status' => 'publish',
'paged' => $posts_page,
'no_found_rows' => false,
'update_post_meta_cache' => false,
]);
if ($author_posts->have_posts()) :
?>
<div class="profile-list">
<?php while ($author_posts->have_posts()) : $author_posts->the_post(); ?>
<?php
set_query_var('aksrt_profile_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;
?>
</div>
<?php endif; ?>
<?php if (in_array('comments', $enabled_tabs, true)) : ?>
<div id="panel-comments" class="dashboard-panel<?php echo $active_tab === 'comments' ? ' is-active' : ''; ?>" role="tabpanel"<?php echo $active_tab !== 'comments' ? ' hidden' : ''; ?>>
<?php
$user_comments = get_comments([
'user_id' => $user_id,
'status' => 'approve',
'number' => $per_page,
'offset' => ($comments_page - 1) * $per_page,
'type' => 'comment',
'update_comment_meta_cache' => false,
]);
if ($user_comments) :
?>
<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');
?>
<?php endforeach; ?>
<?php set_query_var('aksrt_profile_comment', null); ?>
</div>
<?php
$cm_total_pages = (int) ceil($comment_count / $per_page);
aksrt_render_profile_pagination($cm_total_pages, $comments_page, 'comments_page');
else :
aksrt_render_profile_empty($svg_empty_comment, __('TA 还没有发表评论', 'aksrt'));
endif;
?>
</div>
<?php endif; ?>
<?php if (in_array('favorites', $enabled_tabs, true)) : ?>
<div id="panel-favorites" class="dashboard-panel<?php echo $active_tab === 'favorites' ? ' is-active' : ''; ?>" role="tabpanel"<?php echo $active_tab !== 'favorites' ? ' hidden' : ''; ?>>
<?php if (!empty($favorite_post_ids)) : ?>
<?php
$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">
<?php while ($favorites_query->have_posts()) : $favorites_query->the_post(); ?>
<?php
set_query_var('aksrt_profile_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;
?>
<?php else : ?>
<?php aksrt_render_profile_empty($svg_empty_favorite, __('TA 还没有收藏文章', 'aksrt')); ?>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (in_array('credits', $enabled_tabs, true) && $credits_enabled && $is_own_profile) : ?>
<div id="panel-credits" class="dashboard-panel<?php echo $active_tab === 'credits' ? ' is-active' : ''; ?>" role="tabpanel"<?php echo $active_tab !== 'credits' ? ' hidden' : ''; ?>>
<div class="user-home-balance-card">
<p class="user-home-balance-card__label"><?php echo esc_html(sprintf(__('当前%s余额', 'aksrt'), $currency_name)); ?></p>
<div class="user-home-balance-card__value"><?php echo esc_html(aksrt_format_credits($credit_balance)); ?></div>
<p class="user-home-balance-card__hint"><?php esc_html_e('完整账单和赚取/消费记录请在个人中心查看。', 'aksrt'); ?></p>
<a href="<?php echo esc_url(aksrt_get_user_center_url('credits')); ?>" class="btn-outline btn-sm"><?php esc_html_e('进入个人中心', 'aksrt'); ?></a>
</div>
</div>
<?php endif; ?>
<?php
// Placeholder panels for extended tabs (product, plate)
$_uh_extended_tabs = ['product', 'plate'];
$_uh_placeholder_msgs = [
'product' => __('暂无商品内容', 'aksrt'),
'plate' => __('暂无圈子内容', 'aksrt'),
];
foreach ($_uh_extended_tabs as $_uh_ext) :
if (!in_array($_uh_ext, $enabled_tabs, true)) continue;
?>
<div id="panel-<?php echo esc_attr($_uh_ext); ?>" class="dashboard-panel<?php echo $active_tab === $_uh_ext ? ' is-active' : ''; ?>" role="tabpanel"<?php echo $active_tab !== $_uh_ext ? ' hidden' : ''; ?>>
<?php aksrt_render_profile_empty($svg_empty_post, $_uh_placeholder_msgs[$_uh_ext]); ?>
</div>
<?php endforeach; ?>
<?php if (in_array('forum', $enabled_tabs, true)) : ?>
<div id="panel-forum" class="dashboard-panel<?php echo $active_tab === 'forum' ? ' is-active' : ''; ?>" role="tabpanel"<?php echo $active_tab !== 'forum' ? ' hidden' : ''; ?>>
<?php
$forum_page = max(1, isset($_GET['forum_page']) ? (int) $_GET['forum_page'] : 1);
$forum_sub = isset($_GET['forum_sub']) ? sanitize_key(wp_unslash($_GET['forum_sub'])) : 'topics';
$forum_sub = in_array($forum_sub, ['topics', 'favorites', 'plates'], true) ? $forum_sub : 'topics';
?>
<div class="uh-forum__subtabs">
<a class="uh-forum__subtab<?php echo $forum_sub === 'topics' ? ' is-active' : ''; ?>"
href="<?php echo esc_url(add_query_arg(['tab' => 'forum', 'forum_sub' => 'topics', 'forum_page' => 1], $home_url)); ?>">
<i class="fas fa-file-alt"></i> <?php esc_html_e('帖子', 'aksrt'); ?>
<span class="uh-forum__subtab-count"><?php echo esc_html((string) $forum_topic_count); ?></span>
</a>
<a class="uh-forum__subtab<?php echo $forum_sub === 'favorites' ? ' is-active' : ''; ?>"
href="<?php echo esc_url(add_query_arg(['tab' => 'forum', 'forum_sub' => 'favorites', 'forum_page' => 1], $home_url)); ?>">
<i class="fas fa-star"></i> <?php esc_html_e('收藏帖子', 'aksrt'); ?>
<span class="uh-forum__subtab-count"><?php echo esc_html((string) ($forum_stats['favorite_count'] ?? 0)); ?></span>
</a>
<a class="uh-forum__subtab<?php echo $forum_sub === 'plates' ? ' is-active' : ''; ?>"
href="<?php echo esc_url(add_query_arg(['tab' => 'forum', 'forum_sub' => 'plates', 'forum_page' => 1], $home_url)); ?>">
<i class="fas fa-th-large"></i> <?php esc_html_e('版块', 'aksrt'); ?>
<span class="uh-forum__subtab-count"><?php echo esc_html((string) ($forum_stats['managed_plate_count'] ?? 0)); ?></span>
</a>
</div>
<?php if ($forum_sub === 'topics' && $forum_instance) :
$forum_topics = $forum_instance->get_user_forum_topics($user_id, ['page' => $forum_page, 'limit' => $per_page]);
if (!empty($forum_topics['items'])) : ?>
<div class="uh-forum__list">
<?php foreach ($forum_topics['items'] as $ft) : ?>
<div class="uh-forum__item">
<div class="uh-forum__item-body">
<a class="uh-forum__item-title" href="<?php echo esc_url(get_permalink((int) ($ft['id'] ?? 0))); ?>">
<?php echo esc_html((string) ($ft['title'] ?? '')); ?>
</a>
<div class="uh-forum__item-meta">
<span><i class="far fa-eye"></i> <?php echo esc_html((string) ($ft['views'] ?? 0)); ?></span>
<span><i class="far fa-comment"></i> <?php echo esc_html((string) ($ft['comment_count'] ?? 0)); ?></span>
<time><?php echo esc_html((string) ($ft['date'] ?? '')); ?></time>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if ($forum_topics['max_pages'] > 1) : ?>
<div class="uh-forum__pagination">
<?php
echo paginate_links([
'base' => add_query_arg('forum_page', '%#%', $home_url),
'format' => '',
'current' => $forum_page,
'total' => $forum_topics['max_pages'],
'add_args' => ['tab' => 'forum', 'forum_sub' => 'topics'],
]);
?>
</div>
<?php endif; ?>
<?php else : ?>
<?php aksrt_render_profile_empty($svg_empty_post, __('暂无论坛帖子', 'aksrt')); ?>
<?php endif; ?>
<?php elseif ($forum_sub === 'favorites' && $forum_instance) :
$fav_topics = $forum_instance->get_user_favorite_topics($user_id, $forum_page, $per_page);
if (!empty($fav_topics['items'])) : ?>
<div class="uh-forum__list">
<?php foreach ($fav_topics['items'] as $ft) : ?>
<div class="uh-forum__item">
<div class="uh-forum__item-body">
<a class="uh-forum__item-title" href="<?php echo esc_url(get_permalink((int) ($ft['id'] ?? 0))); ?>">
<?php echo esc_html((string) ($ft['title'] ?? '')); ?>
</a>
<div class="uh-forum__item-meta">
<span><i class="far fa-eye"></i> <?php echo esc_html((string) ($ft['views'] ?? 0)); ?></span>
<span><i class="far fa-comment"></i> <?php echo esc_html((string) ($ft['comment_count'] ?? 0)); ?></span>
<time><?php echo esc_html((string) ($ft['date'] ?? '')); ?></time>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if ($fav_topics['max_pages'] > 1) : ?>
<div class="uh-forum__pagination">
<?php
echo paginate_links([
'base' => add_query_arg('forum_page', '%#%', $home_url),
'format' => '',
'current' => $forum_page,
'total' => $fav_topics['max_pages'],
'add_args' => ['tab' => 'forum', 'forum_sub' => 'favorites'],
]);
?>
</div>
<?php endif; ?>
<?php else : ?>
<?php aksrt_render_profile_empty($svg_empty_favorite, __('暂无收藏帖子', 'aksrt')); ?>
<?php endif; ?>
<?php elseif ($forum_sub === 'plates' && $forum_instance) :
$managed_plates = $forum_instance->get_user_managed_plates($user_id);
$followed_plates = $forum_instance->get_user_followed_plates($user_id);
$has_plates = !empty($managed_plates['owned']) || !empty($managed_plates['moderating']) || !empty($followed_plates);
if ($has_plates) : ?>
<?php if (!empty($managed_plates['owned'])) : ?>
<h3 class="uh-forum__section-title"><i class="fas fa-crown"></i> <?php esc_html_e('创建的版块', 'aksrt'); ?></h3>
<div class="uh-forum__plate-grid">
<?php foreach ($managed_plates['owned'] as $plate) : ?>
<a class="uh-forum__plate-card" href="<?php echo esc_url(get_permalink((int) ($plate['id'] ?? 0))); ?>">
<div class="uh-forum__plate-name"><?php echo esc_html((string) ($plate['name'] ?? '')); ?></div>
<div class="uh-forum__plate-stats">
<span><?php printf(esc_html__('%d 帖子', 'aksrt'), (int) ($plate['topic_count'] ?? 0)); ?></span>
<span><?php printf(esc_html__('%d 关注', 'aksrt'), (int) ($plate['follow_count'] ?? 0)); ?></span>
</div>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($managed_plates['moderating'])) : ?>
<h3 class="uh-forum__section-title"><i class="fas fa-shield-alt"></i> <?php esc_html_e('管理的版块', 'aksrt'); ?></h3>
<div class="uh-forum__plate-grid">
<?php foreach ($managed_plates['moderating'] as $plate) : ?>
<a class="uh-forum__plate-card" href="<?php echo esc_url(get_permalink((int) ($plate['id'] ?? 0))); ?>">
<div class="uh-forum__plate-name"><?php echo esc_html((string) ($plate['name'] ?? '')); ?></div>
<div class="uh-forum__plate-stats">
<span><?php printf(esc_html__('%d 帖子', 'aksrt'), (int) ($plate['topic_count'] ?? 0)); ?></span>
</div>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($followed_plates)) : ?>
<h3 class="uh-forum__section-title"><i class="fas fa-heart"></i> <?php esc_html_e('关注的版块', 'aksrt'); ?></h3>
<div class="uh-forum__plate-grid">
<?php foreach ($followed_plates as $plate) : ?>
<a class="uh-forum__plate-card" href="<?php echo esc_url(get_permalink((int) ($plate['id'] ?? 0))); ?>">
<div class="uh-forum__plate-name"><?php echo esc_html((string) ($plate['name'] ?? '')); ?></div>
<div class="uh-forum__plate-stats">
<span><?php printf(esc_html__('%d 帖子', 'aksrt'), (int) ($plate['topic_count'] ?? 0)); ?></span>
<span><?php printf(esc_html__('%d 关注', 'aksrt'), (int) ($plate['follow_count'] ?? 0)); ?></span>
</div>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php else : ?>
<?php aksrt_render_profile_empty($svg_empty_post, __('暂无版块数据', 'aksrt')); ?>
<?php endif; ?>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (in_array('follow', $enabled_tabs, true)) : ?>
<div id="panel-follow" class="dashboard-panel<?php echo $active_tab === 'follow' ? ' is-active' : ''; ?>" role="tabpanel"<?php echo $active_tab !== 'follow' ? ' hidden' : ''; ?>>
<?php
if ($forum_instance) :
$followed = $forum_instance->get_user_followed_plates($user_id);
if (!empty($followed)) : ?>
<div class="uh-forum__plate-grid">
<?php foreach ($followed as $plate) : ?>
<a class="uh-forum__plate-card" href="<?php echo esc_url(get_permalink((int) ($plate['id'] ?? 0))); ?>">
<div class="uh-forum__plate-name"><?php echo esc_html((string) ($plate['name'] ?? '')); ?></div>
<div class="uh-forum__plate-stats">
<span><?php printf(esc_html__('%d 帖子', 'aksrt'), (int) ($plate['topic_count'] ?? 0)); ?></span>
<span><?php printf(esc_html__('%d 关注', 'aksrt'), (int) ($plate['follow_count'] ?? 0)); ?></span>
</div>
</a>
<?php endforeach; ?>
</div>
<?php else : ?>
<?php aksrt_render_profile_empty($svg_empty_post, __('暂未关注任何版块', 'aksrt')); ?>
<?php endif;
else :
aksrt_render_profile_empty($svg_empty_post, __('暂无关注内容', 'aksrt'));
endif;
?>
</div>
<?php endif; ?>
</div>
</div>
<?php if (!empty($user_home_settings['sidebar'])) : ?>
<div class="sidebar uc-sidebar aksrt-user-home__aside" role="complementary" aria-label="<?php esc_attr_e('用户主页侧栏', 'aksrt'); ?>">
<div class="uc-sidebar__section">
<div class="uc-sidebar__title"><?php esc_html_e('主页概览', 'aksrt'); ?></div>
<div class="uc-stats">
<?php foreach ($enabled_tabs as $tab_key) : ?>
<a class="uc-stats__item" href="<?php echo esc_url(add_query_arg('tab', $tab_key, $home_url)); ?>">
<span class="uc-stats__value"><?php echo esc_html((string) ($tab_counts[$tab_key] ?? 0)); ?></span>
<span class="uc-stats__label"><?php echo esc_html($tab_labels[$tab_key] ?? ucfirst($tab_key)); ?></span>
</a>
<?php endforeach; ?>
</div>
</div>
<?php if (!empty($user_contacts) && !empty($user_center_settings['social_links'])) : ?>
<div class="uc-sidebar__section">
<div class="uc-sidebar__title"><?php esc_html_e('公开联系', 'aksrt'); ?></div>
<div class="aksrt-user-home__contacts">
<?php if (function_exists('aksrt_render_social_links')) : ?>
<?php aksrt_render_social_links($user_contacts); ?>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<div class="uc-sidebar__section">
<div class="uc-sidebar__title"><?php esc_html_e('快捷入口', 'aksrt'); ?></div>
<div class="uc-sidebar__buttons">
<a href="<?php echo esc_url($home_url); ?>" class="uc-sidebar__btn">
<i class="fas fa-user" aria-hidden="true"></i>
<span><?php esc_html_e('主页首页', 'aksrt'); ?></span>
</a>
<?php if ($is_own_profile) : ?>
<a href="<?php echo esc_url(aksrt_get_user_center_url()); ?>" class="uc-sidebar__btn">
<i class="fas fa-cog" aria-hidden="true"></i>
<span><?php esc_html_e('个人中心', 'aksrt'); ?></span>
</a>
<?php endif; ?>
</div>
</div>
<?php if ($is_own_profile) : ?>
<?php
$orders_url = function_exists('aksrt_get_orders_page_url') ? aksrt_get_orders_page_url() : '';
$show_vip = class_exists('AKSRT_Settings') && !empty(AKSRT_Settings::get_all()['payment_vip_enabled']);
$credits_enabled = class_exists('AKSRT_Settings') && AKSRT_Settings::credits_enabled();
?>
<?php if ($orders_url || $show_vip || $credits_enabled) : ?>
<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 href="<?php echo esc_url(aksrt_get_user_center_url()); ?>" class="uc-sidebar__btn">
<i class="fas fa-crown" aria-hidden="true"></i>
<span><?php esc_html_e('VIP 会员', 'aksrt'); ?></span>
</a>
<?php endif; ?>
<?php if ($credits_enabled) : ?>
<a href="<?php echo esc_url(aksrt_get_user_center_url()); ?>" class="uc-sidebar__btn">
<i class="fas fa-coins" aria-hidden="true"></i>
<span><?php echo esc_html(AKSRT_Settings::get_currency_name()); ?></span>
</a>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<?php endif; ?>
</aside>
<?php endif; ?>
</div>
</main>
<?php get_footer(); ?>
+149
View File
@@ -0,0 +1,149 @@
<?php
/**
* Template Name: AKSRT - Verify Email
* Template Post Type: page
*
* Theme auth route template: /verify-email/
*
* @package AKSRT
*/
$verify_error = '';
$verify_success = '';
$redirect_url = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['verify_email_resend'])) {
$result = AKSRT_Auth_Service::resend_email_verification($_POST);
if (!empty($result['success'])) {
$verify_success = $result['message'] ?? __('验证邮件已重新发送,请注意查收。', 'aksrt');
} else {
$verify_error = $result['error'] ?? __('验证邮件发送失败,请稍后重试。', 'aksrt');
}
} elseif (isset($_POST['verify_email_submit'])) {
$result = AKSRT_Auth_Service::verify_email_code_submission($_POST);
if (!empty($result['success'])) {
$verify_success = $result['message'] ?? __('邮箱验证成功,即将进入你的账号。', 'aksrt');
$redirect_url = $result['redirect'] ?? '';
} else {
$verify_error = $result['error'] ?? __('邮箱验证码错误或已过期。', 'aksrt');
}
}
}
$verify_context = AKSRT_Auth_Service::get_email_verification_context($_GET);
if ($verify_error === '' && !empty($verify_context['error'])) {
$verify_error = $verify_context['error'];
}
if ($verify_success === '' && !empty($verify_context['message'])) {
$verify_success = $verify_context['message'];
}
if ($redirect_url === '' && !empty($verify_context['redirect'])) {
$redirect_url = $verify_context['redirect'];
}
$verify_user = $verify_context['user'] ?? null;
$verify_uid = (int) ($verify_context['uid'] ?? 0);
$verify_email = (string) ($verify_context['email'] ?? '');
$verify_mode = (string) ($verify_context['mode'] ?? 'invalid');
$verify_method= (string) ($verify_context['method'] ?? '');
$verify_vk = (string) ($verify_context['access_key'] ?? '');
get_header();
?>
<main id="main-content" class="site-main" tabindex="-1">
<div class="auth-shell">
<div class="auth-card">
<div class="auth-card__logo" aria-hidden="true">AK</div>
<h1 class="auth-card__title"><?php bloginfo('name'); ?></h1>
<?php if ($verify_mode === 'completed' || $verify_mode === 'verified') : ?>
<p class="auth-card__subtitle"><?php esc_html_e('邮箱验证成功', 'aksrt'); ?></p>
<?php elseif ($verify_mode === 'code') : ?>
<p class="auth-card__subtitle"><?php esc_html_e('请输入邮箱验证码完成验证', 'aksrt'); ?></p>
<?php elseif ($verify_mode === 'pending') : ?>
<p class="auth-card__subtitle"><?php esc_html_e('验证邮件已经发出,请点击邮箱中的链接完成验证', 'aksrt'); ?></p>
<?php else : ?>
<p class="auth-card__subtitle"><?php esc_html_e('邮箱验证状态异常,请重新检查链接或重新发送验证邮件', 'aksrt'); ?></p>
<?php endif; ?>
<?php if ($verify_error !== '') : ?>
<div class="alert alert--error" role="alert">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/><line x1="12" x2="12.01" y1="16" y2="16"/></svg>
<span><?php echo esc_html($verify_error); ?></span>
</div>
<?php endif; ?>
<?php if ($verify_success !== '') : ?>
<div class="alert alert--success" role="alert">
<span><?php echo esc_html($verify_success); ?></span>
</div>
<?php endif; ?>
<?php if (($verify_mode === 'completed' || $verify_mode === 'verified') && $redirect_url !== '') : ?>
<p class="auth-footer-link">
<a href="<?php echo esc_url($redirect_url); ?>"><?php esc_html_e('立即进入账号', 'aksrt'); ?></a>
</p>
<script>
setTimeout(function(){ window.location.href = <?php echo wp_json_encode($redirect_url); ?>; }, 1800);
</script>
<?php elseif ($verify_mode === 'code') : ?>
<form class="auth-form" method="post" action="">
<?php wp_nonce_field('aksrt_verify_email', '_verify_email_nonce'); ?>
<input type="hidden" name="verify_uid" value="<?php echo esc_attr((string) $verify_uid); ?>">
<input type="hidden" name="vk" value="<?php echo esc_attr($verify_vk); ?>">
<div class="form-field">
<label for="verify_email_code"><?php esc_html_e('邮箱验证码', 'aksrt'); ?></label>
<div class="input-wrap">
<span class="input-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg></span>
<input type="text" name="email_code" id="verify_email_code" inputmode="numeric" maxlength="6" pattern="[0-9]*" autocomplete="one-time-code" required>
</div>
</div>
<?php if ($verify_email !== '') : ?>
<p class="auth-card__subtitle" style="margin-top:-8px;"><?php echo esc_html(sprintf(__('验证码已发送到 %s', 'aksrt'), $verify_email)); ?></p>
<?php endif; ?>
<?php if (function_exists('aksrt_render_captcha')) : ?>
<?php aksrt_render_captcha('verify_email'); ?>
<?php endif; ?>
<button type="submit" name="verify_email_submit" class="btn-primary btn-lg btn-block"><?php esc_html_e('验证并登录', 'aksrt'); ?></button>
</form>
<form method="post" action="" style="margin-top:12px;">
<?php wp_nonce_field('aksrt_verify_email', '_verify_email_nonce'); ?>
<input type="hidden" name="verify_uid" value="<?php echo esc_attr((string) $verify_uid); ?>">
<input type="hidden" name="vk" value="<?php echo esc_attr($verify_vk); ?>">
<?php if (function_exists('aksrt_render_captcha')) : ?>
<?php aksrt_render_captcha('verify_email'); ?>
<?php endif; ?>
<button type="submit" name="verify_email_resend" class="btn-primary btn-lg" style="width:100%;background:transparent;color:var(--color-primary);border:1px solid var(--color-primary);"><?php esc_html_e('重新发送验证码', 'aksrt'); ?></button>
</form>
<?php elseif ($verify_mode === 'pending' && $verify_uid > 0 && $verify_vk !== '') : ?>
<?php
// method 缺失(历史账号 / meta 被外部清理)→ 回退到设置默认值
$resend_method = in_array($verify_method, ['link', 'code'], true)
? $verify_method
: (class_exists('AKSRT_Settings') ? AKSRT_Settings::registration_verification_method() : 'link');
?>
<?php if ($verify_email !== '') : ?>
<p class="auth-card__subtitle"><?php echo esc_html(sprintf(__('当前待验证邮箱:%s', 'aksrt'), $verify_email)); ?></p>
<?php endif; ?>
<form method="post" action="">
<?php wp_nonce_field('aksrt_verify_email', '_verify_email_nonce'); ?>
<input type="hidden" name="verify_uid" value="<?php echo esc_attr((string) $verify_uid); ?>">
<input type="hidden" name="vk" value="<?php echo esc_attr($verify_vk); ?>">
<?php if (function_exists('aksrt_render_captcha')) : ?>
<?php aksrt_render_captcha('verify_email'); ?>
<?php endif; ?>
<button type="submit" name="verify_email_resend" class="btn-primary btn-lg" style="width:100%;"><?php echo esc_html($resend_method === 'code' ? __('重新发送验证码', 'aksrt') : __('重新发送验证邮件', 'aksrt')); ?></button>
</form>
<?php endif; ?>
<p class="auth-footer-link">
<a href="<?php echo esc_url(aksrt_get_login_url()); ?>"><?php esc_html_e('返回登录', 'aksrt'); ?></a>
</p>
</div>
</div>
</main>
<?php
get_footer();
+39
View File
@@ -0,0 +1,39 @@
<?php
/**
* 通用空状态模板
*
* 用于列表页无内容时的统一展示。
*
* @package AKSRT
* @since 5.0.0
*
* @var string $icon FA icon class (default: far fa-inbox)
* @var string $title 主标题
* @var string $desc 描述文字
* @var string $action_url 操作按钮链接
* @var string $action_text 操作按钮文字
* @var string $size 尺寸: sm / md (default: md)
*/
defined('ABSPATH') || exit;
$icon = $args['icon'] ?? 'far fa-inbox';
$title = $args['title'] ?? __('暂无内容', 'aksrt');
$desc = $args['desc'] ?? '';
$action_url = $args['action_url'] ?? '';
$action_text = $args['action_text'] ?? '';
$size = $args['size'] ?? 'md';
?>
<div class="empty-state <?php echo $size === 'sm' ? 'empty-state--sm' : ''; ?>">
<div class="empty-state__icon"><i class="<?php echo esc_attr($icon); ?>"></i></div>
<p class="empty-state__title"><?php echo esc_html($title); ?></p>
<?php if ($desc) : ?>
<p class="empty-state__desc"><?php echo esc_html($desc); ?></p>
<?php endif; ?>
<?php if ($action_url && $action_text) : ?>
<a href="<?php echo esc_url($action_url); ?>" class="btn btn-primary btn-sm empty-state__action">
<?php echo esc_html($action_text); ?>
</a>
<?php endif; ?>
</div>
+49
View File
@@ -0,0 +1,49 @@
<?php
/**
* 图片画廊模板
*
* 用于文章内嵌画廊块或图片列表展示。
*
* @package AKSRT
* @since 5.0.0
*
* @var array $images 图片数组 [{src, alt, caption, width, height}]
* @var string $layout 布局: grid / masonry / carousel
* @var int $columns 列数 (default: 3)
* @var bool $lightbox 是否启用灯箱 (default: true)
*/
defined('ABSPATH') || exit;
$images = $args['images'] ?? [];
$layout = $args['layout'] ?? 'grid';
$columns = max(1, min(6, (int) ($args['columns'] ?? 3)));
$lightbox = $args['lightbox'] ?? true;
if (empty($images)) return;
?>
<div class="content-gallery content-gallery--<?php echo esc_attr($layout); ?>" style="--gallery-columns: <?php echo $columns; ?>;">
<?php foreach ($images as $index => $img) :
$src = $img['src'] ?? '';
$alt = $img['alt'] ?? '';
$caption = $img['caption'] ?? '';
if (!$src) continue;
?>
<figure class="content-gallery__item" data-index="<?php echo $index; ?>">
<a href="<?php echo esc_url($src); ?>"
class="content-gallery__link"
<?php echo $lightbox ? 'data-lightbox="gallery"' : ''; ?>
data-caption="<?php echo esc_attr($caption); ?>">
<img src="<?php echo esc_url($src); ?>"
alt="<?php echo esc_attr($alt); ?>"
class="content-gallery__img"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('content') : ' loading="lazy"'; ?>
width="<?php echo esc_attr($img['width'] ?? 800); ?>"
height="<?php echo esc_attr($img['height'] ?? 600); ?>">
</a>
<?php if ($caption) : ?>
<figcaption class="content-gallery__caption"><?php echo esc_html($caption); ?></figcaption>
<?php endif; ?>
</figure>
<?php endforeach; ?>
</div>
+174
View File
@@ -0,0 +1,174 @@
<?php
/**
* Post Card / List Item Template — Unified Wrapper (v4.0)
*
* This template delegates to the unified shared renderer (aksrt_render_post).
* It preserves backward compatibility for callers using get_template_part().
*
* @package AKSRT
* @since 4.0.0
*/
defined('ABSPATH') || exit;
// If the shared renderer is available, delegate entirely
if (function_exists('aksrt_render_post')) {
$args = isset($args) && is_array($args) ? $args : [];
// Resolve layout mode from query var or args
$forced_layout = (string) get_query_var('aksrt_post_card_layout', '');
$mode = 'card';
if (is_string($forced_layout) && in_array($forced_layout, ['card', 'list', 'grid-cms'], true)) {
$mode = $forced_layout;
} elseif (!empty($args['mode']) && in_array($args['mode'], ['card', 'list', 'grid-cms', 'mini'], true)) {
$mode = $args['mode'];
} elseif (function_exists('aksrt_get_post_flow') && aksrt_get_post_flow(get_the_ID()) === 'cms') {
$mode = 'grid-cms';
} elseif (class_exists('AKSRT_Settings')) {
$archive_layout = AKSRT_Settings::get_archive_layout();
$mode = in_array($archive_layout, ['card', 'list', 'grid-cms'], true) ? $archive_layout : 'card';
}
// Resolve scene context
$scene = (string) get_query_var('aksrt_post_card_scene', '');
$context = ($scene === 'home' || is_front_page() || (is_home() && !is_paged())) ? 'home' : 'archive';
$config = array_merge($args, [
'mode' => $mode,
'context' => $context,
]);
echo aksrt_render_post($config); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return;
}
// ============================================================
// Fallback: self-contained rendering when shared renderer unavailable
// ============================================================
$args = isset($args) && is_array($args) ? $args : [];
$lazy_cfg = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_lazyload_config() : ['enabled' => false, 'placeholder_url' => ''];
$lazy_on = !empty($lazy_cfg['enabled']);
$placeholder_url = !empty($lazy_cfg['placeholder_url']) ? (string) $lazy_cfg['placeholder_url'] : (get_template_directory_uri() . '/assets/img/placeholder.svg');
$forced_layout = (string) get_query_var('aksrt_post_card_layout', '');
$layout = 'card';
if (is_string($forced_layout) && in_array($forced_layout, ['card', 'list', 'masonry', 'grid-cms'], true)) {
$layout = $forced_layout;
} elseif (function_exists('aksrt_get_post_flow') && aksrt_get_post_flow(get_the_ID()) === 'cms') {
$layout = 'grid-cms';
} elseif (class_exists('AKSRT_Settings')) {
$layout = AKSRT_Settings::get_archive_layout();
}
$post_id = get_the_ID();
$author_id = get_the_author_meta('ID');
$author = get_the_author();
$avatar_url = get_avatar_url($author_id, ['size' => 40]);
$time_ago = human_time_diff(get_the_time('U'), current_time('timestamp')) . __('前', 'aksrt');
$excerpt = wp_trim_words(get_the_excerpt(), 'list' === $layout ? 30 : 20, '...');
$tags = get_the_tags() ?: [];
$categories = get_the_category() ?: [];
$comment_count = (int) get_comments_number();
$views = function_exists('aksrt_get_post_views') ? aksrt_get_post_views($post_id) : 0;
$is_sticky = is_sticky($post_id);
$title_html = isset($args['title_html']) ? (string) $args['title_html'] : get_the_title();
$resource_badge = function_exists('aksrt_resource_card_badge_text') ? aksrt_resource_card_badge_text($post_id) : '';
$img_url = '';
if (has_post_thumbnail()) {
$img_url = get_the_post_thumbnail_url($post_id, 'medium_large');
} elseif (function_exists('aksrt_get_first_content_image_url')) {
$img_url = (string) aksrt_get_first_content_image_url($post_id);
}
$has_img = !empty($img_url);
$scene = (string) get_query_var('aksrt_post_card_scene', '');
$is_home_scene = $scene === 'home' || is_front_page() || (is_home() && !is_paged());
$category_limit = $is_home_scene ? 1 : 3;
$tag_limit = $is_home_scene ? 2 : 3;
// Badges HTML
ob_start();
?>
<div class="item-tags scroll-x no-scrollbar">
<?php if ($resource_badge !== '') : ?>
<badge class="meta-pay but jb-yellow"><?php echo esc_html($resource_badge); ?></badge>
<?php endif; ?>
<?php foreach (array_slice($categories, 0, $category_limit) as $category) : ?>
<a href="<?php echo esc_url(get_category_link($category->term_id)); ?>" class="but"><i class="far fa-folder-open" aria-hidden="true"></i> <?php echo esc_html($category->name); ?></a>
<?php endforeach; ?>
<?php foreach (array_slice($tags, 0, $tag_limit) as $tag) : ?>
<a href="<?php echo esc_url(get_tag_link($tag->term_id)); ?>" class="but"># <?php echo esc_html($tag->name); ?></a>
<?php endforeach; ?>
</div>
<?php
$badges_html = trim((string) ob_get_clean());
// Meta HTML
ob_start();
?>
<div class="item-meta muted-2-color flex jsb ac">
<item class="meta-author flex ac">
<img src="<?php echo esc_url($avatar_url); ?>" alt="" class="avatar-mini" width="20" height="20" loading="lazy">
<name class="meta-name"><?php echo esc_html($author); ?></name>
<item class="meta-time"><?php echo esc_html($time_ago); ?></item>
</item>
<div class="meta-right">
<item class="meta-view"><i class="fas fa-eye" aria-hidden="true"></i> <?php echo esc_html(function_exists('aksrt_format_number') ? aksrt_format_number($views) : (string) $views); ?></item>
<item class="meta-view"><i class="fas fa-comment" aria-hidden="true"></i> <?php echo esc_html((string) $comment_count); ?></item>
</div>
</div>
<?php
$meta_html = trim((string) ob_get_clean());
if ('list' === $layout) : ?>
<article <?php post_class('posts-item list ajax-item'); ?>>
<div class="item-thumbnail">
<a href="<?php the_permalink(); ?>">
<?php if ($has_img && $lazy_on) : ?>
<span class="aksrt-skeleton"></span>
<img class="fit-cover aksrt-lazy" data-src="<?php echo esc_url($img_url); ?>" src="<?php echo esc_url($placeholder_url); ?>" alt="<?php echo esc_attr(get_the_title()); ?>">
<?php elseif ($has_img) : ?>
<img class="fit-cover" src="<?php echo esc_url($img_url); ?>" alt="<?php echo esc_attr(get_the_title()); ?>">
<?php else : ?>
<img class="fit-cover" src="<?php echo esc_url($placeholder_url); ?>" alt="">
<?php endif; ?>
</a>
<?php if ($is_sticky) : ?>
<span class="img-badge left badge-sticky"><?php esc_html_e('置顶', 'aksrt'); ?></span>
<?php endif; ?>
</div>
<div class="item-body">
<h2 class="item-heading"><a href="<?php the_permalink(); ?>"><?php echo wp_kses_post($title_html); ?></a></h2>
<p class="item-excerpt"><?php echo wp_kses_post(esc_html($excerpt)); ?></p>
<?php echo $badges_html; ?>
<?php echo $meta_html; ?>
</div>
</article>
<?php else : ?>
<article <?php post_class('posts-item card ajax-item' . ($is_home_scene ? ' posts-item--home' : '')); ?>>
<div class="item-thumbnail">
<a href="<?php the_permalink(); ?>">
<?php if ($has_img && $lazy_on) : ?>
<span class="aksrt-skeleton"></span>
<img class="fit-cover aksrt-lazy" data-src="<?php echo esc_url($img_url); ?>" src="<?php echo esc_url($placeholder_url); ?>" alt="<?php echo esc_attr(get_the_title()); ?>">
<?php elseif ($has_img) : ?>
<img class="fit-cover" src="<?php echo esc_url($img_url); ?>" alt="<?php echo esc_attr(get_the_title()); ?>">
<?php else : ?>
<img class="fit-cover" src="<?php echo esc_url($placeholder_url); ?>" alt="">
<?php endif; ?>
</a>
<?php if ($is_sticky) : ?>
<span class="img-badge left badge-sticky"><?php esc_html_e('置顶', 'aksrt'); ?></span>
<?php endif; ?>
</div>
<div class="item-body">
<h2 class="item-heading"><a href="<?php the_permalink(); ?>"><?php echo wp_kses_post($title_html); ?></a></h2>
<?php echo $badges_html; ?>
<?php echo $meta_html; ?>
</div>
</article>
<?php endif; ?>
@@ -0,0 +1,50 @@
<?php
/**
* Profile Comment List Item — image-less comment row for user center.
*
* 通过 set_query_var() 接收:
* - aksrt_profile_comment: WP_Comment 对象
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$comment = get_query_var('aksrt_profile_comment');
if (!$comment instanceof WP_Comment) {
return;
}
$post = get_post((int) $comment->comment_post_ID);
if (!$post) {
return;
}
$snippet = wp_trim_words(wp_strip_all_tags($comment->comment_content), 30, '…');
$post_title = get_the_title($post);
$comment_link = get_comment_link($comment);
$post_link = get_permalink($post);
$date_iso = get_comment_date('c', $comment);
$date_label = get_comment_date('Y-m-d', $comment);
$time_diff = human_time_diff(strtotime($comment->comment_date), current_time('timestamp'));
?>
<article class="profile-comment-item">
<a href="<?php echo esc_url($comment_link); ?>" class="profile-comment-item__quote-link" aria-label="<?php esc_attr_e('查看评论', 'aksrt'); ?>">
<span class="profile-comment-item__quote-mark" aria-hidden="true">&ldquo;</span>
<span class="profile-comment-item__quote-text"><?php echo esc_html($snippet); ?></span>
</a>
<div class="profile-comment-item__meta">
<span class="profile-comment-item__meta-item profile-comment-item__meta-item--post">
<i class="far fa-file-alt" aria-hidden="true"></i>
<?php esc_html_e('在', 'aksrt'); ?>
<a href="<?php echo esc_url($post_link); ?>" class="profile-comment-item__post-link"><?php echo esc_html($post_title); ?></a>
</span>
<span class="profile-comment-item__meta-sep" aria-hidden="true">·</span>
<time class="profile-comment-item__meta-item" datetime="<?php echo esc_attr($date_iso); ?>" title="<?php echo esc_attr($date_label); ?>">
<?php
/* translators: %s: human time diff */
printf(esc_html__('%s前', 'aksrt'), esc_html($time_diff));
?>
</time>
</div>
</article>
@@ -0,0 +1,106 @@
<?php
/**
* Profile List Item — image-less list row for user center.
*
* 用于:
* - 我的文章 / 我的收藏 / 公开主页文章列表
*
* 通过 set_query_var() 接收以下参数(可选):
* - aksrt_profile_item_action: ['type'=>'edit'|'view'|'unfavorite', 'url'=>'...', 'label'=>'...']
* - aksrt_profile_item_status: 文章状态徽章文本(如 "草稿" / "私密")
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$action = get_query_var('aksrt_profile_item_action', []);
if (!is_array($action)) {
$action = [];
}
$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', '');
$post_id = get_the_ID();
$permalink = get_permalink();
$title = get_the_title();
$date_iso = get_the_date('c');
$date_label = get_the_date();
$excerpt = wp_trim_words(get_the_excerpt(), 30, '…');
$comment_count = (int) get_comments_number();
$categories = get_the_category() ?: [];
$tags = get_the_tags() ?: [];
?>
<article <?php post_class('profile-list-item'); ?>>
<div class="profile-list-item__main">
<div class="profile-list-item__head">
<h3 class="profile-list-item__title">
<a href="<?php echo esc_url($permalink); ?>"><?php echo esc_html($title); ?></a>
<?php if ($status_label) : ?>
<span class="profile-list-item__status-badge"><?php echo esc_html($status_label); ?></span>
<?php endif; ?>
</h3>
<time class="profile-list-item__date" datetime="<?php echo esc_attr($date_iso); ?>" title="<?php echo esc_attr($date_label); ?>">
<?php echo esc_html($date_label); ?>
</time>
</div>
<?php if ($excerpt) : ?>
<p class="profile-list-item__excerpt"><?php echo esc_html($excerpt); ?></p>
<?php endif; ?>
<div class="profile-list-item__meta">
<?php foreach ($categories as $cat) : ?>
<a href="<?php echo esc_url(get_category_link($cat->term_id)); ?>" class="profile-list-item__chip profile-list-item__chip--category">
<i class="fas fa-folder" aria-hidden="true"></i>
<span><?php echo esc_html($cat->name); ?></span>
</a>
<?php endforeach; ?>
<?php foreach (array_slice($tags, 0, 4) as $tag) : ?>
<a href="<?php echo esc_url(get_tag_link($tag->term_id)); ?>" class="profile-list-item__chip profile-list-item__chip--tag">
#<?php echo esc_html($tag->name); ?>
</a>
<?php endforeach; ?>
<span class="profile-list-item__meta-spacer"></span>
<span class="profile-list-item__meta-item profile-list-item__meta-item--comments" title="<?php esc_attr_e('评论数', 'aksrt'); ?>">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/></svg>
<span><?php echo esc_html((string) $comment_count); ?></span>
</span>
<?php if ($action_type && $action_url) :
$action_class = 'profile-list-item__action profile-list-item__action--' . sanitize_html_class($action_type);
$icon_svg = '';
switch ($action_type) {
case 'edit':
$icon_svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 1 1 3 3L7 19l-4 1 1-4Z"/></svg>';
break;
case 'unfavorite':
$icon_svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 17.27 18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>';
break;
case 'view':
default:
$icon_svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8S1 12 1 12Z"/><circle cx="12" cy="12" r="3"/></svg>';
break;
}
if ('unfavorite' === $action_type) :
?>
<button type="button" class="<?php echo esc_attr($action_class); ?>" data-post-id="<?php echo esc_attr((string) $post_id); ?>" data-action="unfavorite" title="<?php echo esc_attr($action_label); ?>">
<span class="profile-list-item__action-icon"><?php echo $icon_svg; // phpcs:ignore ?></span>
<span class="profile-list-item__action-label"><?php echo esc_html($action_label); ?></span>
</button>
<?php else : ?>
<a href="<?php echo esc_url($action_url); ?>" class="<?php echo esc_attr($action_class); ?>" title="<?php echo esc_attr($action_label); ?>">
<span class="profile-list-item__action-icon"><?php echo $icon_svg; // phpcs:ignore ?></span>
<span class="profile-list-item__action-label"><?php echo esc_html($action_label); ?></span>
</a>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
</article>
+70
View File
@@ -0,0 +1,70 @@
<?php
/**
* Document Chapter Navigation
*
* Shows previous/next chapter navigation at the bottom of a document page.
* Uses AKSRT_Docs_Presenter for chapter resolution.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$current_id = get_the_ID();
$presenter = class_exists('AKSRT_Docs_Presenter') ? AKSRT_Docs_Presenter::get_instance() : null;
if ($presenter) {
$nav = $presenter->get_chapter_nav($current_id);
$prev_page = $nav['prev'];
$next_page = $nav['next'];
} else {
$parent_id = wp_get_post_parent_id($current_id);
$siblings = get_pages([
'parent' => $parent_id ?: 0,
'sort_column' => 'menu_order, post_title',
'sort_order' => 'ASC',
'post_status' => 'publish',
]);
$current_index = -1;
foreach ($siblings as $i => $sibling) {
if ((int) $sibling->ID === $current_id) {
$current_index = $i;
break;
}
}
$prev_page = $current_index > 0 ? $siblings[$current_index - 1] : null;
$next_page = ($current_index >= 0 && $current_index < count($siblings) - 1)
? $siblings[$current_index + 1]
: null;
}
if (!$prev_page && !$next_page) return;
?>
<nav class="aksrt-doc-chapter-nav" aria-label="<?php esc_attr_e('章节导航', 'aksrt'); ?>">
<?php if ($prev_page) : ?>
<a href="<?php echo esc_url(get_permalink($prev_page->ID)); ?>" class="aksrt-doc-chapter-nav__prev">
<span class="aksrt-doc-chapter-nav__label">
<i class="fas fa-arrow-left"></i>
<?php esc_html_e('上一篇', 'aksrt'); ?>
</span>
<span class="aksrt-doc-chapter-nav__title"><?php echo esc_html($prev_page->post_title); ?></span>
</a>
<?php else : ?>
<div class="aksrt-doc-chapter-nav__prev aksrt-doc-chapter-nav__prev--disabled"></div>
<?php endif; ?>
<?php if ($next_page) : ?>
<a href="<?php echo esc_url(get_permalink($next_page->ID)); ?>" class="aksrt-doc-chapter-nav__next">
<span class="aksrt-doc-chapter-nav__label">
<?php esc_html_e('下一篇', 'aksrt'); ?>
<i class="fas fa-arrow-right"></i>
</span>
<span class="aksrt-doc-chapter-nav__title"><?php echo esc_html($next_page->post_title); ?></span>
</a>
<?php else : ?>
<div class="aksrt-doc-chapter-nav__next aksrt-doc-chapter-nav__next--disabled"></div>
<?php endif; ?>
</nav>
+55
View File
@@ -0,0 +1,55 @@
<?php
/**
* Document Index Card
*
* Shows a card for a document page on the document navigation index.
* Used in the document navigation page to list available documents.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$page_id = get_the_ID();
$children = get_pages([
'parent' => $page_id,
'sort_column' => 'menu_order',
'sort_order' => 'ASC',
'post_status' => 'publish',
]);
$child_count = count($children);
$thumb = get_the_post_thumbnail_url($page_id, 'medium');
$excerpt = wp_trim_words(get_the_excerpt(), 30, '...');
?>
<a href="<?php the_permalink(); ?>" class="aksrt-doc-index-card">
<?php if ($thumb) : ?>
<div class="aksrt-doc-index-card__thumb">
<img src="<?php echo esc_url($thumb); ?>" alt=""<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
</div>
<?php else : ?>
<div class="aksrt-doc-index-card__icon">
<i class="fas fa-book-open"></i>
</div>
<?php endif; ?>
<div class="aksrt-doc-index-card__body">
<h3 class="aksrt-doc-index-card__title"><?php the_title(); ?></h3>
<?php if ($excerpt) : ?>
<p class="aksrt-doc-index-card__desc"><?php echo esc_html($excerpt); ?></p>
<?php endif; ?>
<div class="aksrt-doc-index-card__meta">
<?php if ($child_count > 0) : ?>
<span class="aksrt-doc-index-card__count">
<i class="fas fa-file-alt"></i>
<?php printf(esc_html__('%d 篇文章', 'aksrt'), $child_count); ?>
</span>
<?php endif; ?>
<span class="aksrt-doc-index-card__date">
<i class="fas fa-clock"></i>
<?php echo esc_html(get_the_date('', $page_id)); ?>
</span>
</div>
</div>
<i class="fas fa-chevron-right aksrt-doc-index-card__arrow"></i>
</a>
+83
View File
@@ -0,0 +1,83 @@
<?php
/**
* Docs Mode: Sidebar Navigation
*
* 文档模式侧边栏:显示文档目录树,当前章节高亮。
* Uses AKSRT_Docs_Presenter for tree construction.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$current_id = get_the_ID();
$parent_id = wp_get_post_parent_id($current_id);
$root_id = $parent_id ?: $current_id;
$presenter = class_exists('AKSRT_Docs_Presenter') ? AKSRT_Docs_Presenter::get_instance() : null;
if ($presenter) {
$doc_pages = $presenter->get_flat_tree($root_id, 3);
} else {
$doc_pages = get_pages([
'child_of' => $root_id,
'sort_column' => 'menu_order, post_title',
'sort_order' => 'ASC',
'parent' => $root_id,
]);
}
$root_page = get_post($root_id);
// Quick search for docs
$doc_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_docs_mode_settings() : [];
$show_search = !empty($doc_settings['toc_enabled']);
?>
<aside class="aksrt-doc-sidebar" role="navigation" aria-label="<?php esc_attr_e('文档导航', 'aksrt'); ?>">
<div class="aksrt-doc-sidebar__header">
<a href="<?php echo esc_url(get_permalink($root_id)); ?>" class="aksrt-doc-sidebar__root">
<i class="fas fa-book aksrt-doc-sidebar__root-icon"></i>
<span><?php echo esc_html($root_page->post_title ?? __('文档', 'aksrt')); ?></span>
</a>
</div>
<?php if ($show_search) : ?>
<div class="aksrt-doc-sidebar__search">
<input type="text" class="aksrt-doc-sidebar__search-input" placeholder="<?php esc_attr_e('搜索文档...', 'aksrt'); ?>" aria-label="<?php esc_attr_e('搜索文档', 'aksrt'); ?>">
<i class="fas fa-search aksrt-doc-sidebar__search-icon"></i>
</div>
<?php endif; ?>
<?php if (!empty($doc_pages)) : ?>
<ul class="aksrt-doc-sidebar__nav">
<?php foreach ($doc_pages as $dp) :
$depth = isset($dp->_depth) ? (int) $dp->_depth : 0;
$is_current = ((int) $dp->ID === $current_id);
$is_ancestor = ((int) $dp->ID === $parent_id);
$li_class = 'aksrt-doc-sidebar__item';
if ($is_current) $li_class .= ' is-current';
if ($is_ancestor) $li_class .= ' is-ancestor';
if ($depth > 0) $li_class .= ' aksrt-doc-sidebar__item--depth-' . $depth;
$has_children = false;
if ($presenter) {
$sub = get_pages(['parent' => $dp->ID, 'post_status' => 'publish', 'fields' => 'ids']);
$has_children = !empty($sub);
} else {
$sub_pages = get_pages(['child_of' => $dp->ID, 'sort_column' => 'menu_order', 'sort_order' => 'ASC', 'parent' => $dp->ID]);
$has_children = !empty($sub_pages);
}
$is_open = $is_current || $is_ancestor;
?>
<li class="<?php echo esc_attr($li_class); ?>" style="<?php echo $depth > 0 ? 'padding-left:' . ($depth * 16) . 'px;' : ''; ?>">
<a href="<?php echo esc_url(get_permalink($dp->ID)); ?>" class="aksrt-doc-sidebar__link<?php echo $is_current ? ' is-active' : ''; ?>">
<?php if ($has_children) : ?>
<i class="fas fa-chevron-<?php echo $is_open ? 'down' : 'right'; ?> aksrt-doc-sidebar__toggle" aria-hidden="true"></i>
<?php endif; ?>
<span><?php echo esc_html($dp->post_title); ?></span>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</aside>
+41
View File
@@ -0,0 +1,41 @@
<?php
/**
* Docs Mode: Table of Contents
*
* 文档模式目录:从文章内容中提取 h2/h3 生成右侧目录。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$content = get_the_content();
if (empty($content)) return;
// 提取 h2/h3 标题
$headings = [];
if (preg_match_all('/<h([2-3])[^>]*(?:id="([^"]*)")?[^>]*>(.*?)<\/h\1>/is', $content, $matches, PREG_SET_ORDER)) {
foreach ($matches as $m) {
$level = (int) $m[1];
$id = !empty($m[2]) ? $m[2] : '';
$text = wp_strip_all_tags($m[3]);
if ($text === '') continue;
if ($id === '') {
$id = 'toc-' . sanitize_title($text);
}
$headings[] = ['level' => $level, 'id' => $id, 'text' => $text];
}
}
if (empty($headings)) return;
?>
<nav class="aksrt-doc-toc" aria-label="<?php esc_attr_e('文章目录', 'aksrt'); ?>">
<div class="aksrt-doc-toc__title"><?php esc_html_e('目录', 'aksrt'); ?></div>
<ul class="aksrt-doc-toc__list">
<?php foreach ($headings as $h) : ?>
<li class="aksrt-doc-toc__item aksrt-doc-toc__item--h<?php echo esc_attr((string) $h['level']); ?>">
<a href="#<?php echo esc_attr($h['id']); ?>"><?php echo esc_html($h['text']); ?></a>
</li>
<?php endforeach; ?>
</ul>
</nav>
+46
View File
@@ -0,0 +1,46 @@
<?php
/**
* Docs Mode: Document Tree
*
* 文档模式树形导航:递归展示文档层级结构。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_render_doc_tree')) {
function aksrt_render_doc_tree($parent_id, $current_id, $depth = 0) {
$children = get_pages([
'parent' => $parent_id,
'sort_column' => 'menu_order, post_title',
'sort_order' => 'ASC',
]);
if (empty($children)) return;
$indent = str_repeat(' ', $depth);
echo $indent . '<ul class="aksrt-doc-tree__level aksrt-doc-tree__level--' . esc_attr((string) ($depth + 1)) . '">';
foreach ($children as $child) {
$is_current = ($child->ID === $current_id);
$has_children = (int) get_pages(['child_of' => $child->ID, 'parent' => $child->ID, 'hide_empty' => true, 'fields' => 'ids']) > 0;
echo $indent . ' <li class="aksrt-doc-tree__item' . ($is_current ? ' is-current' : '') . '">';
echo '<a href="' . esc_url(get_permalink($child->ID)) . '">' . esc_html($child->post_title) . '</a>';
if ($has_children && $depth < 3) {
aksrt_render_doc_tree($child->ID, $current_id, $depth + 1);
}
echo '</li>';
}
echo $indent . '</ul>';
}
}
$current_id = get_the_ID();
$root_id = wp_get_post_parent_id($current_id) ?: $current_id;
?>
<nav class="aksrt-doc-tree" aria-label="<?php esc_attr_e('文档树', 'aksrt'); ?>">
<div class="aksrt-doc-tree__root">
<a href="<?php echo esc_url(get_permalink($root_id)); ?>"><?php echo esc_html(get_the_title($root_id)); ?></a>
</div>
<?php aksrt_render_doc_tree($root_id, $current_id); ?>
</nav>
@@ -0,0 +1,68 @@
<?php
/**
* Download Center Hero Section
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$page_title = get_the_title() ?: __('下载中心', 'aksrt');
$page_desc = get_the_excerpt() ?: __('浏览和下载本站提供的各类资源。', 'aksrt');
$current_filter = isset($_GET['resource_type']) ? sanitize_key((string) $_GET['resource_type']) : '';
$resource_type_labels = function_exists('aksrt_get_pay_types') ? aksrt_get_pay_types() : [];
unset($resource_type_labels[AKSRT_Domain_Model::PAY_TYPE_READING]);
$resource_count_query = new WP_Query([
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => 1,
'fields' => 'ids',
'meta_query' => [
[
'key' => AKSRT_Domain_Model::RESOURCE_MARKER_KEY,
'value' => '1',
],
],
]);
$total_published = (int) $resource_count_query->found_posts;
wp_reset_postdata();
?>
<div class="aksrt-download-hero">
<div class="aksrt-download-hero__bg"></div>
<div class="aksrt-download-hero__content">
<h1 class="aksrt-download-hero__title"><?php echo esc_html($page_title); ?></h1>
<?php if ($page_desc) : ?>
<p class="aksrt-download-hero__desc"><?php echo esc_html($page_desc); ?></p>
<?php endif; ?>
<div class="aksrt-download-hero__stats">
<div class="aksrt-download-hero__stat">
<span class="aksrt-download-hero__stat-num"><?php echo esc_html(number_format_i18n($total_published)); ?></span>
<span class="aksrt-download-hero__stat-label"><?php esc_html_e('资源总数', 'aksrt'); ?></span>
</div>
<?php if (!empty($resource_type_labels)) : ?>
<div class="aksrt-download-hero__stat">
<span class="aksrt-download-hero__stat-num"><?php echo esc_html(count($resource_type_labels)); ?></span>
<span class="aksrt-download-hero__stat-label"><?php esc_html_e('资源类型', 'aksrt'); ?></span>
</div>
<?php endif; ?>
</div>
<?php if (!empty($resource_type_labels)) : ?>
<div class="aksrt-download-hero__filters">
<a href="<?php echo esc_url(get_permalink()); ?>" class="aksrt-download-hero__filter<?php echo $current_filter === '' ? ' aksrt-download-hero__filter--active' : ''; ?>">
<?php esc_html_e('全部', 'aksrt'); ?>
</a>
<?php foreach ($resource_type_labels as $type_key => $type_label) : ?>
<a
href="<?php echo esc_url(add_query_arg('resource_type', $type_key, get_permalink())); ?>"
class="aksrt-download-hero__filter<?php echo $current_filter === $type_key ? ' aksrt-download-hero__filter--active' : ''; ?>"
>
<?php echo esc_html($type_label); ?>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
@@ -0,0 +1,116 @@
<?php
/**
* Download Permission Card
*
* Shows download status, permission requirements, and action buttons.
* Uses AKSRT_Download_State for unified state resolution.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$post_id = get_the_ID();
$is_logged_in = is_user_logged_in();
// Use unified state resolver if available
if (class_exists('AKSRT_Download_State')) {
$state = AKSRT_Download_State::get_instance()->resolve($post_id);
$status = $state['state'];
$can_download = $state['can_download'];
$status_text = $state['message'];
$action_label = $state['action_label'];
$action_url = $state['action_url'];
$download_cost = $state['cost'];
$user_credits = $state['balance'];
$login_url = $state['login_url'];
} else {
// Fallback: legacy logic
$download_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_cms_features() : [];
$requires_login = !empty($download_settings['download_requires_login']);
$requires_credits = !empty($download_settings['download_requires_credits']);
$download_cost = (int) ($download_settings['download_cost'] ?? 0);
$has_downloaded = false;
if ($is_logged_in) {
$downloaded = get_user_meta(get_current_user_id(), '_aksrt_downloaded_resources', true);
$downloaded = is_array($downloaded) ? $downloaded : [];
$has_downloaded = in_array($post_id, $downloaded, true);
}
$user_credits = 0;
if ($is_logged_in && class_exists('AKSRT_Credits')) {
$user_credits = AKSRT_Credits::balance(get_current_user_id());
}
$status = 'available';
$status_text = '';
$can_download = true;
$login_url = wp_login_url(get_permalink($post_id));
$action_label = __('立即下载', 'aksrt');
$action_url = '';
if (!$is_logged_in && $requires_login) {
$status = 'not_logged_in';
$status_text = __('请先登录后下载', 'aksrt');
$can_download = false;
$action_label = __('登录下载', 'aksrt');
$action_url = $login_url;
} elseif ($requires_credits && $download_cost > 0 && $user_credits < $download_cost && !$has_downloaded) {
$status = 'insufficient_credits';
$status_text = sprintf(__('需要 %d 积分,当前余额 %d', 'aksrt'), $download_cost, $user_credits);
$can_download = false;
$action_label = __('获取积分', 'aksrt');
$action_url = home_url('/user/credits');
} elseif ($has_downloaded) {
$status = 'owned';
$status_text = __('已下载,可再次下载', 'aksrt');
}
}
// Map state to icon
$state_icons = [
'available' => ['icon' => 'fa-check-circle', 'color' => 'success'],
'owned' => ['icon' => 'fa-download', 'color' => 'success'],
'not_logged_in' => ['icon' => 'fa-lock', 'color' => 'warning'],
'insufficient_credits' => ['icon' => 'fa-coins', 'color' => 'warning'],
'no_permission' => ['icon' => 'fa-ban', 'color' => 'warning'],
'pending_delivery' => ['icon' => 'fa-clock', 'color' => 'info'],
];
$state_info = $state_icons[$status] ?? $state_icons['available'];
?>
<div class="aksrt-dl-perm-card aksrt-dl-perm-card--<?php echo esc_attr($status); ?>">
<div class="aksrt-dl-perm-card__status">
<i class="fas <?php echo esc_attr($state_info['icon']); ?> aksrt-dl-perm-card__icon aksrt-dl-perm-card__icon--<?php echo esc_attr($state_info['color']); ?>"></i>
<span><?php echo esc_html($status_text); ?></span>
</div>
<?php if (isset($download_cost) && $download_cost > 0) : ?>
<div class="aksrt-dl-perm-card__cost">
<i class="fas fa-coins"></i>
<span><?php printf(esc_html__('下载消耗 %d 积分', 'aksrt'), $download_cost); ?></span>
<?php if ($is_logged_in) : ?>
<span class="aksrt-dl-perm-card__balance">(<?php printf(esc_html__('当前: %d', 'aksrt'), $user_credits); ?>)</span>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="aksrt-dl-perm-card__actions">
<?php if ($can_download) : ?>
<a href="<?php echo esc_url(get_permalink($post_id)); ?>" class="aksrt-dl-perm-card__btn aksrt-dl-perm-card__btn--primary">
<i class="fas fa-download"></i>
<?php echo esc_html($action_label); ?>
</a>
<?php elseif ($action_url !== '') : ?>
<a href="<?php echo esc_url($action_url); ?>" class="aksrt-dl-perm-card__btn aksrt-dl-perm-card__btn--primary">
<i class="fas fa-arrow-right"></i>
<?php echo esc_html($action_label); ?>
</a>
<?php else : ?>
<span class="aksrt-dl-perm-card__btn aksrt-dl-perm-card__btn--outline aksrt-dl-perm-card__btn--disabled">
<i class="fas fa-info-circle"></i>
<?php echo esc_html($action_label); ?>
</span>
<?php endif; ?>
</div>
</div>
+10
View File
@@ -0,0 +1,10 @@
<?php
/**
* Fixed homepage layout inspired by Zibll.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
aksrt_homepage_render_layout();
@@ -0,0 +1,104 @@
<?php
/**
* Home Module · Banner — v4.0 Zibll-Inspired
*
* 内联轮播条,使用与 header-slider 相同的 Swiper 架构,
* 但以圆角卡片形式嵌入内容流中(非全宽)。
*
* 支持配置:
* - 自动播放
* - 圆角卡片样式
* - 标题/副标题叠加
* - 点击跳转
*
* @package AKSRT
* @version 4.0.0
*/
defined('ABSPATH') || exit;
if (class_exists('AKSRT_Settings') && !AKSRT_Settings::homepage_module_banner_enabled()) {
return;
}
$banners = function_exists('aksrt_get_banners') ? aksrt_get_banners(8, 'banner') : [];
if (empty($banners)) return;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$slide_count = count($banners);
// Swiper config — Banner uses a subset of the global slider config
$slider_cfg = function_exists('aksrt_get_slider_config') ? aksrt_get_slider_config() : [];
$swiper_config = [
'effect' => 'slide', // Banner always uses slide effect
'loop' => $slide_count > 1 && ($slider_cfg['loop'] ?? true),
'speed' => 500,
'autoplay' => $slide_count > 1 ? ['delay' => ($slider_cfg['autoplay_interval'] ?? 5000), 'disableOnInteraction' => false] : false,
'pagination' => false,
'navigation' => !empty($slider_cfg['navigation']),
'keyboard' => ['enabled' => true],
'spaceBetween' => ($slider_cfg['space_between'] ?? 0),
];
?>
<div class="home-module home-module--banner">
<div class="banner-module">
<div class="swiper banner-module__swiper" data-swiper-config="<?php echo esc_attr(wp_json_encode($swiper_config)); ?>">
<div class="swiper-wrapper">
<?php foreach ($banners as $index => $slide) :
$device_vis = $slide['device_visibility'] ?? 'all';
$slide_classes = 'swiper-slide';
if ($device_vis === 'pc') {
$slide_classes .= ' banner-slide--pc-only';
} elseif ($device_vis === 'mobile') {
$slide_classes .= ' banner-slide--mobile-only';
}
$link_target = !empty($slide['link_target']) ? $slide['link_target'] : '_self';
?>
<div class="<?php echo esc_attr($slide_classes); ?>"<?php if (!empty($slide['link'])) : ?> data-link="<?php echo esc_url($slide['link']); ?>" data-target="<?php echo esc_attr($link_target); ?>"<?php endif; ?>>
<div class="banner-module__slide-bg">
<?php if (!empty($slide['image'])) : ?>
<img
src="<?php echo esc_url($slide['image']); ?>"
alt="<?php echo esc_attr($slide['title'] ?: get_bloginfo('name')); ?>"
loading="<?php echo 0 === $index ? 'eager' : 'lazy'; ?>"
decoding="async"
class="fit-cover"
>
<?php else : ?>
<div class="banner-module__slide-fallback"></div>
<?php endif; ?>
</div>
<div class="banner-module__slide-overlay"></div>
<?php
$show_text = !isset($slide['show_text']) || $slide['show_text'];
$has_title = !empty($slide['title']);
$has_subtitle = !empty($slide['subtitle']);
?>
<?php if ($show_text && ($has_title || $has_subtitle)) : ?>
<div class="banner-module__content">
<?php if ($has_title) : ?>
<h3 class="banner-module__title"><?php echo esc_html($slide['title']); ?></h3>
<?php endif; ?>
<?php if ($has_subtitle) : ?>
<p class="banner-module__subtitle"><?php echo esc_html($slide['subtitle']); ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php if ($slide_count > 1) : ?>
<!-- 前后箭头 -->
<div class="banner-module__prev swiper-button-prev"></div>
<div class="banner-module__next swiper-button-next"></div>
<?php endif; ?>
</div>
</div>
<?php if (function_exists('aksrt_render_admin_quick_link')) : ?>
<div class="home-module__admin-tools">
<?php aksrt_render_admin_quick_link('page-display/header-components', __('顶部组件设置', 'aksrt')); ?>
</div>
<?php endif; ?>
</div>
@@ -0,0 +1,41 @@
<?php
/**
* Home Module · Button Group
*
* config: { title?, buttons: [{ label, url, style?, icon? }], align? }
*
* 按钮组模块,用于 CTA 引导。
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$title = (string) ($cfg['title'] ?? '');
$buttons = isset($cfg['buttons']) && is_array($cfg['buttons']) ? $cfg['buttons'] : [];
$align = sanitize_key((string) ($cfg['align'] ?? 'center'));
if (empty($buttons)) return;
?>
<section class="home-module home-module--button-group container" style="text-align:<?php echo esc_attr($align); ?>">
<?php if ($title !== '') : ?>
<h2 class="home-module__title"><?php echo esc_html($title); ?></h2>
<?php endif; ?>
<div class="home-module__buttons">
<?php foreach ($buttons as $btn) :
$label = (string) ($btn['label'] ?? '');
$url = (string) ($btn['url'] ?? '#');
$style = sanitize_key((string) ($btn['style'] ?? 'primary'));
$icon = (string) ($btn['icon'] ?? '');
if ($label === '') continue;
?>
<a href="<?php echo esc_url($url); ?>" class="aksrt-btn aksrt-btn--<?php echo esc_attr($style); ?>">
<?php if ($icon !== '') : ?>
<i class="<?php echo esc_attr($icon); ?>" aria-hidden="true"></i>
<?php endif; ?>
<span><?php echo esc_html($label); ?></span>
</a>
<?php endforeach; ?>
</div>
</section>
@@ -0,0 +1,72 @@
<?php
/**
* Home Module · Category
*
* config: { cat_id, count, layout, columns, title? }
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$cat_id = isset($cfg['cat_id']) ? (int) $cfg['cat_id'] : 0;
$count = max(1, min(20, (int) ($cfg['count'] ?? 8)));
$layout = (string) ($cfg['layout'] ?? 'card');
$columns = max(1, min(6, (int) ($cfg['columns'] ?? 4)));
$title = (string) ($cfg['title'] ?? '');
if (!in_array($layout, ['card', 'list', 'masonry', 'grid-cms'], true)) {
$layout = 'card';
}
$args = [
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
];
if ($cat_id > 0) {
$args['cat'] = $cat_id;
// 分类页通常希望同时展示资源 post,post 已天然包含资源;旧 CPT 兼容时合并
$args['post_type'] = function_exists('aksrt_resource_post_types')
? array_unique(array_merge(['post'], aksrt_resource_post_types()))
: ['post'];
} else {
$args['post_type'] = 'post';
}
$query = new WP_Query($args);
if (!$query->have_posts()) {
wp_reset_postdata();
return;
}
$display_title = $title;
if ($display_title === '' && $cat_id > 0) {
$term = get_term($cat_id, 'category');
if ($term && !is_wp_error($term)) $display_title = $term->name;
}
$more_link = $cat_id > 0 ? get_category_link($cat_id) : '';
?>
<section class="home-module home-module--category container" aria-label="<?php echo esc_attr($display_title ?: __('分类', 'aksrt')); ?>">
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($display_title ?: __('精选内容', 'aksrt')); ?></h2>
<?php if ($more_link) : ?>
<a class="home-module__more" href="<?php echo esc_url($more_link); ?>">
<?php esc_html_e('查看更多', 'aksrt'); ?> <i class="fas fa-arrow-right" aria-hidden="true"></i>
</a>
<?php endif; ?>
</header>
<div class="post-grid post-grid--<?php echo esc_attr($layout); ?>" style="--cols:<?php echo (int) $columns; ?>;">
<?php
set_query_var('aksrt_post_card_layout', $layout);
while ($query->have_posts()) {
$query->the_post();
get_template_part('templates/parts/content', 'post-card');
}
set_query_var('aksrt_post_card_layout', '');
wp_reset_postdata();
?>
</div>
</section>
@@ -0,0 +1,123 @@
<?php
/**
* Home Module · Category Recommend Cards
*
* 4 张分类卡片 2×2 布局,每张卡片包含:
* 上部:封面图 + 浏览量胶囊 + 分类名/描述 + 文章数 + 更多链接
* 下部:5 篇最新文章列表(标题 + 相对发布时间)
*
* config: { title?, cat_ids[], show_views?, show_dates? }
*
* 封面图来源:分类 term meta → 最新文章缩略图 → 占位图
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$module_title = (string) ($cfg['title'] ?? __('分类推荐', 'aksrt'));
$cat_ids = array_map('absint', (array) ($cfg['cat_ids'] ?? []));
$cat_ids = array_slice(array_filter($cat_ids, fn($id) => $id > 0), 0, 4);
$show_views = !isset($cfg['show_views']) || !empty($cfg['show_views']);
$show_dates = !isset($cfg['show_dates']) || !empty($cfg['show_dates']);
if (empty($cat_ids)) return;
// 懒加载配置
$lazy_cfg = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_lazyload_config() : ['enabled' => false, 'placeholder_url' => ''];
$lazy_on = !empty($lazy_cfg['enabled']);
$placeholder = $lazy_cfg['placeholder_url'] ?: (get_template_directory_uri() . '/assets/img/placeholder.svg');
// 组装卡片数据
$cards = [];
foreach ($cat_ids as $idx => $cat_id) {
$term = get_term($cat_id, 'category');
if (!$term || is_wp_error($term)) continue;
// 封面图
$cover_url = class_exists('AKSRT_Settings')
? AKSRT_Settings::get_category_cover_url($cat_id)
: $placeholder;
// 分类描述(优先 term meta 推荐描述,其次原生 description)
$desc = class_exists('AKSRT_Settings')
? AKSRT_Settings::get_category_recommend_desc($cat_id)
: (string) $term->description;
// 5 篇最新文章
$posts = get_posts([
'category__in' => [$cat_id],
'posts_per_page' => 5,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
'no_found_rows' => true,
]);
// 浏览量
$views = $show_views ? aksrt_get_category_total_views($cat_id) : 0;
$cards[] = [
'term' => $term,
'cover' => $cover_url,
'desc' => $desc,
'posts' => $posts,
'views' => $views,
'count' => (int) $term->count,
'link' => get_category_link($cat_id),
];
}
if (empty($cards)) return;
?>
<section class="home-module home-module--cat-cards" aria-label="<?php echo esc_attr($module_title); ?>">
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($module_title); ?></h2>
</header>
<div class="cat-cards-grid">
<?php foreach ($cards as $card) : ?>
<div class="cat-card">
<div class="cat-card__top">
<div class="cat-card__banner">
<?php if ($show_views && $card['views'] > 0) : ?>
<span class="cat-card__views"><?php echo esc_html(function_exists('aksrt_format_number') ? aksrt_format_number($card['views']) : number_format_i18n($card['views'])); ?></span>
<?php endif; ?>
<?php if ($lazy_on) : ?>
<span class="aksrt-skeleton"></span>
<img class="cat-card__cover aksrt-lazy" data-src="<?php echo esc_url($card['cover']); ?>" src="<?php echo esc_url($placeholder); ?>" alt="<?php echo esc_attr($card['term']->name); ?>">
<?php else : ?>
<img class="cat-card__cover" src="<?php echo esc_url($card['cover']); ?>" alt="<?php echo esc_attr($card['term']->name); ?>">
<?php endif; ?>
</div>
<div class="cat-card__info">
<div>
<div class="cat-card__name"><?php echo esc_html($card['term']->name); ?></div>
<?php if ($card['desc'] !== '') : ?>
<div class="cat-card__desc"><?php echo esc_html($card['desc']); ?></div>
<?php endif; ?>
</div>
<div class="cat-card__foot">
<span><?php printf(esc_html__('%d篇文章', 'aksrt'), $card['count']); ?></span>
<a href="<?php echo esc_url($card['link']); ?>" class="cat-card__more"><?php esc_html_e('>更多文章', 'aksrt'); ?></a>
</div>
</div>
</div>
<?php if (!empty($card['posts'])) : ?>
<ul class="cat-card__list">
<?php foreach ($card['posts'] as $p) : ?>
<li>
<a href="<?php echo esc_url(get_permalink($p->ID)); ?>"><?php echo esc_html(get_the_title($p->ID)); ?></a>
<?php if ($show_dates) : ?>
<span class="cat-card__time"><?php echo esc_html(aksrt_time_ago($p->post_date)); ?></span>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</section>
@@ -0,0 +1,43 @@
<?php
/**
* Home Module · Data Grid
*
* config: { title?, items: [{ value, label, icon? }], columns? }
*
* 数据展示网格,用于统计数据、里程碑等。
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$title = (string) ($cfg['title'] ?? '');
$items = isset($cfg['items']) && is_array($cfg['items']) ? $cfg['items'] : [];
$columns = max(2, min(6, (int) ($cfg['columns'] ?? 4)));
if (empty($items)) return;
?>
<section class="home-module home-module--data-grid container">
<?php if ($title !== '') : ?>
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($title); ?></h2>
</header>
<?php endif; ?>
<div class="home-module__data-grid" style="grid-template-columns:repeat(<?php echo esc_attr((string) $columns); ?>, 1fr)">
<?php foreach ($items as $item) :
$value = (string) ($item['value'] ?? '');
$label = (string) ($item['label'] ?? '');
$icon = (string) ($item['icon'] ?? '');
if ($value === '' && $label === '') continue;
?>
<div class="aksrt-data-cell">
<?php if ($icon !== '') : ?>
<i class="<?php echo esc_attr($icon); ?>" aria-hidden="true"></i>
<?php endif; ?>
<span class="aksrt-data-cell__value"><?php echo esc_html($value); ?></span>
<span class="aksrt-data-cell__label"><?php echo esc_html($label); ?></span>
</div>
<?php endforeach; ?>
</div>
</section>
@@ -0,0 +1,39 @@
<?php
/**
* Home Module · FAQ
*
* config: { title?, items: [{ q, a }], columns? }
*
* FAQ 手风琴模块。
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$title = (string) ($cfg['title'] ?? '');
$items = isset($cfg['items']) && is_array($cfg['items']) ? $cfg['items'] : [];
$columns = max(1, min(3, (int) ($cfg['columns'] ?? 1)));
if (empty($items)) return;
?>
<section class="home-module home-module--faq container">
<?php if ($title !== '') : ?>
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($title); ?></h2>
</header>
<?php endif; ?>
<div class="home-module__faq-grid" style="column-count:<?php echo esc_attr((string) $columns); ?>">
<?php foreach ($items as $i => $item) :
$q = (string) ($item['q'] ?? '');
$a = (string) ($item['a'] ?? '');
if ($q === '') continue;
?>
<details class="aksrt-faq-item">
<summary class="aksrt-faq-item__question"><?php echo esc_html($q); ?></summary>
<div class="aksrt-faq-item__answer"><?php echo wp_kses_post($a); ?></div>
</details>
<?php endforeach; ?>
</div>
</section>
@@ -0,0 +1,47 @@
<?php
/**
* Home Module · Friend Links
*
* config: { title? }
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$title = (string) ($cfg['title'] ?? '');
$friend_links = aksrt_get_friend_links();
if (!is_array($friend_links) || empty($friend_links)) return;
if (empty($friend_links)) return;
?>
<section class="home-module home-module--friends container" aria-label="<?php echo esc_attr($title ?: __('友情链接', 'aksrt')); ?>">
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($title ?: __('友情链接', 'aksrt')); ?></h2>
</header>
<ul class="home-module__friends-list">
<?php foreach ($friend_links as $link) :
$url = $link['url'] ?? '';
$name = $link['name'] ?? '';
$description = $link['description'] ?? '';
$logo = $link['logo'] ?? '';
if (!$url || !$name) continue;
?>
<li>
<a href="<?php echo esc_url($url); ?>" target="_blank" rel="noopener noreferrer external" class="home-module__friend-card">
<?php if ($logo) : ?>
<img src="<?php echo esc_url($logo); ?>" alt="" class="home-module__friend-avatar"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
<?php endif; ?>
<div class="home-module__friend-meta">
<strong><?php echo esc_html($name); ?></strong>
<?php if ($description) : ?>
<span><?php echo esc_html($description); ?></span>
<?php endif; ?>
</div>
</a>
</li>
<?php endforeach; ?>
</ul>
</section>
@@ -0,0 +1,332 @@
<?php
/**
* Home module: header components hero + floating search + icon cards.
*
* @package AKSRT
* @version 4.1.0
*/
defined('ABSPATH') || exit;
if (class_exists('AKSRT_Settings') && !AKSRT_Settings::homepage_module_hero_enabled()) {
return;
}
// Device visibility gate: header_components_device_visibility ('' = all, 'only_pc' = PC only, 'only_sm' = mobile only)
$_hero_show_type = '';
if (class_exists('AKSRT_Settings')) {
$_hero_slider_cfg = AKSRT_Settings::get_header_components_settings();
$_hero_show_type = $_hero_slider_cfg['show_type'] ?? '';
}
if ($_hero_show_type === 'only_pc' && wp_is_mobile()) {
return;
}
if ($_hero_show_type === 'only_sm' && !wp_is_mobile()) {
return;
}
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) {
$cfg = [];
}
$hero_banners = function_exists('aksrt_get_banners') ? aksrt_get_banners(8, 'hero') : [];
$settings = function_exists('aksrt_get_all_settings') ? aksrt_get_all_settings() : [];
$search_cfg = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_header_components_search_config() : ['show' => false, 'keywords' => [], 'categories' => [], 'type_options' => []];
$show_search = !empty($search_cfg['show']) || !empty($cfg['with_search']);
$show_cards = !empty($settings['header_components_cards_enabled']);
$icon_cards = isset($settings['header_components_icon_cards']) ? (array) $settings['header_components_icon_cards'] : [];
$card_bg_mode = !empty($settings['header_components_card_shared_bg']) ? 'shared' : 'individual';
$card_blur = !empty($settings['header_components_card_blur']);
$filter_blur_devices = isset($settings['header_components_filter_blur_devices']) ? (array) $settings['header_components_filter_blur_devices'] : ($card_blur ? ['pc', 'mobile'] : []);
$card_show_devices = isset($settings['header_components_card_show_devices']) ? (array) $settings['header_components_card_show_devices'] : ['pc', 'mobile'];
$card_icon_shape = $settings['header_components_card_icon_shape'] ?? 'circle';
$slide_count = count($hero_banners);
$visible_card_count = ($show_cards && !empty($icon_cards)) ? count($icon_cards) : 0;
$container_classes = ['header-components-container', 'relative', 'card-' . $visible_card_count];
if (in_array('pc', $filter_blur_devices, true)) {
$container_classes[] = 'has-blur-pc';
}
if (in_array('mobile', $filter_blur_devices, true)) {
$container_classes[] = 'has-blur-mobile';
}
if (!in_array('pc', $card_show_devices, true)) {
$container_classes[] = 'hide-cards-pc';
}
if (!in_array('mobile', $card_show_devices, true)) {
$container_classes[] = 'hide-cards-mobile';
}
$search_placeholder = function_exists('aksrt_get_search_placeholder') ? aksrt_get_search_placeholder() : __('开启精彩搜索', 'aksrt');
$search_value = trim((string) get_search_query());
$search_type = function_exists('aksrt_get_search_form_type') ? aksrt_get_search_form_type() : 'post';
$render_hero_search = static function (string $search_id) use ($search_placeholder, $search_value, $search_type, $search_cfg): void {
$_kw = $search_cfg['keywords'] ?? [];
$_cats = $search_cfg['categories'] ?? [];
$_types = $search_cfg['type_options'] ?? [];
$_show_cat = !empty($search_cfg['show_input_cat']) && !empty($_cats);
$_show_type = !empty($search_cfg['show_type']) && !empty($_types);
?>
<div class="header-components-search">
<div class="search-input">
<form role="search" method="get" action="<?php echo esc_url(home_url('/')); ?>" class="search-form" data-aksrt-search-form="hero">
<input type="hidden" name="aksrt_search_type" value="<?php echo esc_attr($search_type); ?>">
<div class="line-form<?php echo ($_show_cat || $_show_type) ? ' has-addons' : ''; ?>">
<?php if ($_show_cat) : ?>
<select name="cat" class="header-components-search__cat" aria-label="<?php esc_attr_e('选择分类', 'aksrt'); ?>">
<option value=""><?php esc_html_e('全部分类', 'aksrt'); ?></option>
<?php foreach ($_cats as $_c) : ?>
<option value="<?php echo esc_attr($_c['id']); ?>"><?php echo esc_html($_c['name']); ?></option>
<?php endforeach; ?>
</select>
<?php endif; ?>
<?php if ($_show_type) : ?>
<div class="header-components-search__types">
<?php foreach ($_types as $_idx => $_t) : ?>
<label class="header-components-search__type-label">
<input type="radio" name="aksrt_search_type" value="<?php echo esc_attr($_t['key']); ?>"<?php echo $_idx === 0 ? ' checked' : ''; ?> class="header-components-search__type-radio">
<span class="header-components-search__type-text"><?php echo esc_html($_t['label']); ?></span>
</label>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="search-input-text">
<label class="screen-reader-text" for="<?php echo esc_attr($search_id); ?>"><?php esc_html_e('搜索', 'aksrt'); ?></label>
<input
type="search"
id="<?php echo esc_attr($search_id); ?>"
name="s"
class="line-form-input"
value="<?php echo esc_attr($search_value); ?>"
placeholder=" "
autocomplete="off"
>
<i class="line-form-line"></i>
<div class="scale-placeholder<?php echo $search_value !== '' ? ' is-focus' : ''; ?>" default="<?php echo esc_attr($search_placeholder); ?>">
<?php echo esc_html($search_placeholder); ?>
</div>
<div class="abs-right">
<button type="submit" class="null header-components-search__submit" aria-label="<?php echo esc_attr__('搜索', 'aksrt'); ?>">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.3-4.3"></path>
</svg>
</button>
</div>
</div>
</div>
</form>
</div>
<?php if (!empty($_kw)) : ?>
<div class="header-components-search__keywords">
<?php foreach ($_kw as $_kw_text) : ?>
<a href="<?php echo esc_url(add_query_arg('s', rawurlencode($_kw_text), home_url('/'))); ?>" class="header-components-search__kw-tag"><?php echo esc_html($_kw_text); ?></a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php
};
$render_icon_cards = static function () use ($show_cards, $icon_cards, $card_bg_mode, $card_icon_shape): void {
if (!$show_cards || empty($icon_cards)) {
return;
}
?>
<div class="header-components-card">
<div class="header-components-card__container container">
<?php if ($card_bg_mode === 'shared') : ?>
<div class="header-components-card__wrap header-components-card__wrap--shared">
<div class="header-components-card__row">
<?php foreach ($icon_cards as $card) : ?>
<?php $card['icon_shape'] = $card_icon_shape; ?>
<div class="header-components-card__item">
<?php echo aksrt_render_icon_cover_card($card); ?>
</div>
<?php endforeach; ?>
</div>
</div>
<?php else : ?>
<div class="header-components-card__row">
<?php foreach ($icon_cards as $card) : ?>
<?php $card['icon_shape'] = $card_icon_shape; ?>
<div class="header-components-card__item">
<div class="header-components-card__item-bg">
<?php echo aksrt_render_icon_cover_card($card); ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<?php
};
if (!empty($hero_banners)) :
$slider_cfg = function_exists('aksrt_get_slider_config') ? aksrt_get_slider_config() : [];
$swiper_config = [
'effect' => $slider_cfg['effect'] ?? 'fade',
'loop' => $slide_count > 1 && ($slider_cfg['loop'] ?? true),
'speed' => 600,
'autoplay' => $slide_count > 1 ? ['delay' => ($slider_cfg['autoplay_interval'] ?? 5000), 'disableOnInteraction' => false] : false,
'keyboard' => ['enabled' => true],
];
if (!empty($slider_cfg['pagination'])) {
$swiper_config['pagination'] = ['clickable' => true];
}
if (!empty($slider_cfg['navigation'])) {
$swiper_config['navigation'] = true;
}
$pc_height = $slider_cfg['pc_height'] ?? '';
$mobile_height = $slider_cfg['mobile_height'] ?? '';
$height_style = '';
if ($pc_height !== '') {
$height_style .= '--pc-height:' . esc_attr($pc_height) . ';';
}
if ($mobile_height !== '') {
$height_style .= '--m-height:' . esc_attr($mobile_height) . ';';
}
?>
<div class="<?php echo esc_attr(implode(' ', $container_classes)); ?>" id="aksrt-header-components"<?php if ($height_style !== '') : ?> style="<?php echo $height_style; ?>"<?php endif; ?>>
<div class="swiper header-components slide-header" data-swiper-config="<?php echo esc_attr(wp_json_encode($swiper_config)); ?>">
<div class="swiper-wrapper">
<?php foreach ($hero_banners as $index => $slide) :
$device_vis = $slide['device_visibility'] ?? 'all';
$slide_classes = 'swiper-slide';
if ($device_vis === 'pc') {
$slide_classes .= ' hero-slide--pc-only';
} elseif ($device_vis === 'mobile') {
$slide_classes .= ' hero-slide--mobile-only';
}
if (!empty($slide['video_url'])) {
$slide_classes .= ' hero-slide--has-video';
}
$link_target = !empty($slide['link_target']) ? $slide['link_target'] : '_self';
$has_layers = !empty($slide['layers']) && is_array($slide['layers']);
$text_parallax = !empty($slide['text_parallax']);
$text_style = '';
if (!empty($slide['text_size_pc'])) {
$text_style .= '--hero-text-size-pc:' . esc_attr($slide['text_size_pc']) . ';';
}
if (!empty($slide['text_size_m'])) {
$text_style .= '--hero-text-size-m:' . esc_attr($slide['text_size_m']) . ';';
}
if ($text_parallax) {
$text_style .= '--hero-text-parallax:1;';
}
?>
<div class="<?php echo esc_attr($slide_classes); ?>"<?php if (!empty($slide['link'])) : ?> data-link="<?php echo esc_url($slide['link']); ?>" data-target="<?php echo esc_attr($link_target); ?>"<?php endif; ?>>
<div class="header-components__slide-bg">
<?php if (!empty($slide['video_url'])) : ?>
<video class="header-components__video" autoplay muted loop playsinline preload="auto">
<source src="<?php echo esc_url($slide['video_url']); ?>" type="video/mp4">
</video>
<?php endif; ?>
<?php if (!empty($slide['image'])) : ?>
<img
src="<?php echo esc_url($slide['image']); ?>"
alt="<?php echo esc_attr($slide['title'] ?: get_bloginfo('name')); ?>"
loading="<?php echo 0 === $index ? 'eager' : 'lazy'; ?>"
decoding="async"
class="fit-cover"
>
<?php elseif (empty($slide['video_url'])) : ?>
<div class="header-components__slide-fallback"></div>
<?php endif; ?>
<?php if ($has_layers) : ?>
<?php foreach ($slide['layers'] as $layer) :
if (empty($layer['image'])) continue;
$layer_classes = 'header-components__layer';
if (!empty($layer['parallax'])) $layer_classes .= ' header-components__layer--parallax';
if (!empty($layer['free_size'])) $layer_classes .= ' header-components__layer--free-size';
$layer_style = '';
if (!empty($layer['align']) && $layer['align'] !== 'center') {
$layer_style .= 'background-position:' . esc_attr($layer['align']) . ';';
}
if (!empty($layer['parallax'])) {
$pscale = isset($layer['parallax_scale']) ? (float) $layer['parallax_scale'] : 0.3;
$popacity = isset($layer['parallax_opacity']) ? (float) $layer['parallax_opacity'] : 0.6;
$layer_style .= '--layer-parallax-scale:' . esc_attr($pscale) . ';';
$layer_style .= '--layer-parallax-opacity:' . esc_attr($popacity) . ';';
}
?>
<div class="<?php echo esc_attr($layer_classes); ?>" style="background-image:url('<?php echo esc_url($layer['image']); ?>')<?php echo $layer_style ? ';' . $layer_style : ''; ?>"></div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div class="header-components__slide-overlay"></div>
<?php
$show_text = !isset($slide['show_text']) || $slide['show_text'];
$has_title = !empty($slide['title']);
$has_subtitle = !empty($slide['subtitle']);
$has_description = !empty($slide['description']);
$text_align = $slide['text_align'] ?? 'left';
?>
<?php if ($show_text && ($has_title || $has_subtitle || $has_description)) : ?>
<div class="header-components__content slide-text<?php echo $text_align !== 'left' ? ' text-' . esc_attr($text_align) : ''; ?><?php echo $text_parallax ? ' has-text-parallax' : ''; ?>"<?php echo $text_style ? ' style="' . $text_style . '"' : ''; ?>>
<?php if ($has_title) : ?>
<h2 class="header-components__title slide-title"><?php echo esc_html($slide['title']); ?></h2>
<?php endif; ?>
<?php if ($has_subtitle) : ?>
<div class="header-components__subtitle slide-desc"><?php echo esc_html($slide['subtitle']); ?></div>
<?php endif; ?>
<?php if ($has_description) : ?>
<div class="header-components__description"><?php echo esc_html($slide['description']); ?></div>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php if ($show_search) : ?>
<?php $render_hero_search('header-search-input'); ?>
<?php endif; ?>
</div>
<?php $render_icon_cards(); ?>
<?php if (function_exists('aksrt_render_admin_quick_link')) : ?>
<div class="home-module__admin-tools">
<?php aksrt_render_admin_quick_link('page-display/header-components', __('顶部组件设置', 'aksrt')); ?>
</div>
<?php endif; ?>
</div>
<?php else : ?>
<?php
$is_admin_user = current_user_can('manage_options');
$hero_admin_url = admin_url('admin.php?page=aksrt-settings#/content/hero');
?>
<div class="<?php echo esc_attr(implode(' ', $container_classes)); ?>" id="aksrt-header-components">
<div class="header-components slide-header header-components--gradient">
<div class="header-components__content slide-text text-center">
<h1 class="header-components__title slide-title"><?php bloginfo('name'); ?></h1>
<div class="header-components__subtitle slide-desc"><?php echo esc_html(get_bloginfo('description', 'display')); ?></div>
<?php if ($is_admin_user) : ?>
<p class="header-components__admin-hint" role="note">
<?php esc_html_e('No Hero slides found. Showing fallback site info.', 'aksrt'); ?>
<a class="header-components__admin-link" href="<?php echo esc_url($hero_admin_url); ?>">
<?php esc_html_e('Go to Content -> Hero Components', 'aksrt'); ?>
</a>
</p>
<?php endif; ?>
</div>
<?php if ($show_search) : ?>
<?php $render_hero_search('header-search-input-fallback'); ?>
<?php endif; ?>
</div>
<?php $render_icon_cards(); ?>
<?php if (function_exists('aksrt_render_admin_quick_link')) : ?>
<div class="home-module__admin-tools">
<?php aksrt_render_admin_quick_link('page-display/header-components', __('顶部组件设置', 'aksrt')); ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
@@ -0,0 +1,52 @@
<?php
/**
* Home Module · Highlight Cards
*
* config: { title?, items: [{ title, desc, icon?, image?, url? }], columns? }
*
* 亮点卡片模块,用于产品特性、服务介绍等。
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$title = (string) ($cfg['title'] ?? '');
$items = isset($cfg['items']) && is_array($cfg['items']) ? $cfg['items'] : [];
$columns = max(1, min(4, (int) ($cfg['columns'] ?? 3)));
if (empty($items)) return;
?>
<section class="home-module home-module--highlight-cards container">
<?php if ($title !== '') : ?>
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($title); ?></h2>
</header>
<?php endif; ?>
<div class="home-module__highlight-grid" style="grid-template-columns:repeat(<?php echo esc_attr((string) $columns); ?>, 1fr)">
<?php foreach ($items as $item) :
$item_title = (string) ($item['title'] ?? '');
$item_desc = (string) ($item['desc'] ?? '');
$item_icon = (string) ($item['icon'] ?? '');
$item_image = (string) ($item['image'] ?? '');
$item_url = (string) ($item['url'] ?? '');
if ($item_title === '') continue;
$card_tag = $item_url !== '' ? 'a' : 'div';
$card_attrs = $item_url !== '' ? ' href="' . esc_url($item_url) . '"' : '';
?>
<<?php echo esc_attr($card_tag); ?> class="aksrt-highlight-card"<?php echo $card_attrs; ?>>
<?php if ($item_image !== '') : ?>
<img class="aksrt-highlight-card__image" src="<?php echo esc_url($item_image); ?>" alt=""<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
<?php elseif ($item_icon !== '') : ?>
<div class="aksrt-highlight-card__icon"><i class="<?php echo esc_attr($item_icon); ?>"></i></div>
<?php endif; ?>
<h3 class="aksrt-highlight-card__title"><?php echo esc_html($item_title); ?></h3>
<?php if ($item_desc !== '') : ?>
<p class="aksrt-highlight-card__desc"><?php echo esc_html($item_desc); ?></p>
<?php endif; ?>
</<?php echo esc_attr($card_tag); ?>>
<?php endforeach; ?>
</div>
</section>
@@ -0,0 +1,111 @@
<?php
/**
* Home Module · Hotspot 热点推荐
*
* 纯文章列表(无图片),仅桌面端显示。
* config: { count, post_type, source, title? }
*
* source:
* 'auto' → 优先读取 aksrt_recommend meta,不足时用最新补充
* 'recent' → 按时间倒序
* 'views' → 按阅读量排序
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$count = max(3, min(20, (int) ($cfg['count'] ?? 6)));
$post_type = (string) ($cfg['post_type'] ?? 'post');
$source = (string) ($cfg['source'] ?? 'auto');
$title = (string) ($cfg['title'] ?? '');
if (!in_array($source, ['auto', 'recent', 'views'], true)) {
$source = 'auto';
}
if (!in_array($post_type, ['post', 'aksrt_resource'], true)) {
$post_type = 'post';
}
$is_resource = $post_type === 'aksrt_resource';
$ptypes = $is_resource && function_exists('aksrt_resource_post_types')
? aksrt_resource_post_types()
: [$post_type];
// 构建查询参数
$args = [
'post_type' => $ptypes,
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
];
if ($source === 'auto') {
// 优先推荐文章,不足时用最新补充
$args['meta_query'] = [
'relation' => 'OR',
['key' => 'aksrt_recommend', 'value' => '1', 'compare' => '='],
['key' => 'aksrt_recommend', 'compare' => 'NOT EXISTS'],
];
$args['orderby'] = 'modified';
$args['order'] = 'DESC';
} elseif ($source === 'views') {
$args['meta_key'] = '_aksrt_res_view_count';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
} else {
$args['orderby'] = 'date';
$args['order'] = 'DESC';
}
$query = new WP_Query($args);
if (!$query->have_posts()) {
wp_reset_postdata();
return;
}
$default_title = __('热点推荐', 'aksrt');
$display_title = $title ?: $default_title;
?>
<section class="home-module home-module--hotspot hotspot--desktop-only" aria-label="<?php echo esc_attr($display_title); ?>">
<header class="home-module__header">
<h2 class="home-module__title">
<i class="fas fa-fire" aria-hidden="true"></i>
<?php echo esc_html($display_title); ?>
</h2>
</header>
<ol class="hotspot__list">
<?php
$rank = 1;
while ($query->have_posts()) :
$query->the_post();
$cats = get_the_category();
$cat_name = $cats ? $cats[0]->name : '';
$views = (int) get_post_meta(get_the_ID(), '_aksrt_res_view_count', true);
?>
<li class="hotspot__item">
<span class="hotspot__num hotspot__num--<?php echo $rank <= 3 ? 'top' : 'normal'; ?>"><?php echo (int) $rank; ?></span>
<div class="hotspot__body">
<a class="hotspot__title" href="<?php the_permalink(); ?>" title="<?php the_title_attribute(); ?>">
<?php the_title(); ?>
</a>
<div class="hotspot__meta">
<?php if ($cat_name) : ?>
<span class="hotspot__cat"><?php echo esc_html($cat_name); ?></span>
<?php endif; ?>
<span class="hotspot__date"><?php echo esc_html(get_the_date('Y-m-d')); ?></span>
<?php if ($views > 0) : ?>
<span class="hotspot__views"><?php echo esc_html(number_format_i18n($views)); ?> <?php esc_html_e('阅读', 'aksrt'); ?></span>
<?php endif; ?>
</div>
</div>
</li>
<?php
$rank++;
endwhile;
wp_reset_postdata();
?>
</ol>
</section>
@@ -0,0 +1,29 @@
<?php
/**
* Home Module · HTML
*
* config: { html, title? }
*
* 已通过 wp_kses_post 在写入时清洗,渲染时直接输出。
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$html = (string) ($cfg['html'] ?? '');
$title = (string) ($cfg['title'] ?? '');
if ($html === '') return;
?>
<section class="home-module home-module--html container">
<?php if ($title !== '') : ?>
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($title); ?></h2>
</header>
<?php endif; ?>
<div class="home-module__html-body">
<?php echo wp_kses_post($html); ?>
</div>
</section>
@@ -0,0 +1,103 @@
<?php
/**
* Home Module · More (Discover Categories)
*
* Maps to zibll widget-more capability.
* config: { title?, cat_ids[], count, columns, show_post_count, show_latest_posts, latest_count }
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) {
$cfg = [];
}
$title = trim((string) ($cfg['title'] ?? ''));
$cat_ids = isset($cfg['cat_ids']) && is_array($cfg['cat_ids']) ? array_map('absint', $cfg['cat_ids']) : [];
$count = max(1, min(12, (int) ($cfg['count'] ?? 6)));
$columns = max(2, min(4, (int) ($cfg['columns'] ?? 3)));
$show_post_count = !isset($cfg['show_post_count']) || !empty($cfg['show_post_count']);
$show_latest_posts = !isset($cfg['show_latest_posts']) || !empty($cfg['show_latest_posts']);
$latest_count = max(1, min(10, (int) ($cfg['latest_count'] ?? 5)));
// Fetch categories.
if (!empty($cat_ids)) {
$categories = get_categories([
'include' => array_slice($cat_ids, 0, $count),
'orderby' => 'include',
]);
} else {
$categories = get_categories([
'number' => $count,
'orderby' => 'count',
'order' => 'DESC',
'hide_empty' => true,
]);
}
if (empty($categories)) {
return;
}
?>
<section class="home-module home-module--more container" aria-label="<?php echo esc_attr($title ?: __('发现更多', 'aksrt')); ?>">
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($title ?: __('发现更多', 'aksrt')); ?></h2>
</header>
<div class="home-module__more-grid" style="--cols: <?php echo (int) $columns; ?>;">
<?php foreach ($categories as $cat) :
$cat_link = get_category_link($cat);
$cover = '';
if (function_exists('aksrt_get_category_cover')) {
$cover = aksrt_get_category_cover($cat->term_id);
}
?>
<div class="home-module__more-card">
<a href="<?php echo esc_url($cat_link); ?>" class="home-module__more-link">
<?php if ($cover) : ?>
<div class="home-module__more-cover">
<img src="<?php echo esc_url($cover); ?>" alt=""<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
</div>
<?php else : ?>
<div class="home-module__more-cover home-module__more-cover--placeholder">
<i class="fas fa-folder" aria-hidden="true"></i>
</div>
<?php endif; ?>
<div class="home-module__more-body">
<strong class="home-module__more-name"><?php echo esc_html($cat->name); ?></strong>
<?php if ($show_post_count) : ?>
<span class="home-module__more-count">
<?php
/* translators: %d: number of posts */
printf(esc_html__('%d 篇文章', 'aksrt'), (int) $cat->count);
?>
</span>
<?php endif; ?>
</div>
</a>
<?php if ($show_latest_posts) :
$recent = get_posts([
'category' => $cat->term_id,
'posts_per_page' => $latest_count,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
]);
if (!empty($recent)) :
?>
<ul class="home-module__more-posts">
<?php foreach ($recent as $rp) : ?>
<li>
<a href="<?php echo esc_url(get_permalink($rp)); ?>"><?php echo esc_html($rp->post_title); ?></a>
</li>
<?php endforeach; ?>
</ul>
<?php
endif;
endif;
?>
</div>
<?php endforeach; ?>
</div>
</section>
@@ -0,0 +1,54 @@
<?php
/**
* Home Module · Partner Marquee
*
* config: { title?, items: [{ name, image, url? }], speed? }
*
* 合作伙伴横向滚动模块。
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$title = (string) ($cfg['title'] ?? '');
$items = isset($cfg['items']) && is_array($cfg['items']) ? $cfg['items'] : [];
$speed = max(10, min(120, (int) ($cfg['speed'] ?? 30)));
if (empty($items)) return;
?>
<section class="home-module home-module--partner-marquee container">
<?php if ($title !== '') : ?>
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($title); ?></h2>
</header>
<?php endif; ?>
<div class="aksrt-marquee" style="--marquee-duration:<?php echo esc_attr((string) $speed); ?>s">
<div class="aksrt-marquee__track">
<?php
// 渲染两份以实现无缝循环
for ($pass = 0; $pass < 2; $pass++) :
foreach ($items as $partner) :
$name = (string) ($partner['name'] ?? '');
$image = (string) ($partner['image'] ?? '');
$url = (string) ($partner['url'] ?? '');
if ($image === '' && $name === '') continue;
$tag = $url !== '' ? 'a' : 'span';
$attrs = $url !== '' ? ' href="' . esc_url($url) . '" target="_blank" rel="noopener"' : '';
?>
<<?php echo esc_attr($tag); ?> class="aksrt-marquee__item"<?php echo $attrs; ?>>
<?php if ($image !== '') : ?>
<img src="<?php echo esc_url($image); ?>" alt="<?php echo esc_attr($name); ?>"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
<?php else : ?>
<span><?php echo esc_html($name); ?></span>
<?php endif; ?>
</<?php echo esc_attr($tag); ?>>
<?php
endforeach;
endfor;
?>
</div>
</div>
</section>
@@ -0,0 +1,54 @@
<?php
/**
* Home Module · Product Showcase
*
* config: { title?, products: [{ title, desc, image, price?, url? }], columns? }
*
* 产品展示模块,用于商城/资源推荐。
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$title = (string) ($cfg['title'] ?? '');
$products = isset($cfg['products']) && is_array($cfg['products']) ? $cfg['products'] : [];
$columns = max(1, min(4, (int) ($cfg['columns'] ?? 3)));
if (empty($products)) return;
?>
<section class="home-module home-module--product-showcase container">
<?php if ($title !== '') : ?>
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($title); ?></h2>
</header>
<?php endif; ?>
<div class="home-module__product-grid" style="grid-template-columns:repeat(<?php echo esc_attr((string) $columns); ?>, 1fr)">
<?php foreach ($products as $product) :
$p_title = (string) ($product['title'] ?? '');
$p_desc = (string) ($product['desc'] ?? '');
$p_image = (string) ($product['image'] ?? '');
$p_price = (string) ($product['price'] ?? '');
$p_url = (string) ($product['url'] ?? '');
if ($p_title === '') continue;
?>
<a href="<?php echo esc_url($p_url ?: '#'); ?>" class="aksrt-product-card">
<?php if ($p_image !== '') : ?>
<div class="aksrt-product-card__image">
<img src="<?php echo esc_url($p_image); ?>" alt="<?php echo esc_attr($p_title); ?>"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
</div>
<?php endif; ?>
<div class="aksrt-product-card__body">
<h3 class="aksrt-product-card__title"><?php echo esc_html($p_title); ?></h3>
<?php if ($p_desc !== '') : ?>
<p class="aksrt-product-card__desc"><?php echo esc_html($p_desc); ?></p>
<?php endif; ?>
<?php if ($p_price !== '') : ?>
<span class="aksrt-product-card__price"><?php echo esc_html($p_price); ?></span>
<?php endif; ?>
</div>
</a>
<?php endforeach; ?>
</div>
</section>
@@ -0,0 +1,109 @@
<?php
/**
* Home Module · Ranking
*
* config: { metric: 'views|downloads|comments', count, post_type, title? }
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$metric = (string) ($cfg['metric'] ?? 'downloads');
$count = max(1, min(20, (int) ($cfg['count'] ?? 6)));
$post_type_cfg = (string) ($cfg['post_type'] ?? 'aksrt_resource');
$title = (string) ($cfg['title'] ?? '');
if (!in_array($metric, ['views', 'downloads', 'comments'], true)) {
$metric = 'downloads';
}
$is_resource_module = $post_type_cfg === 'aksrt_resource';
$args = [
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
];
if ($is_resource_module) {
$args['post_type'] = function_exists('aksrt_resource_post_types') ? aksrt_resource_post_types() : ['post'];
if (!post_type_exists('aksrt_resource')) {
$args['meta_query'] = [[
'key' => '_aksrt_is_resource',
'value' => '1',
'compare' => '=',
]];
}
} else {
$args['post_type'] = 'post';
}
switch ($metric) {
case 'views':
$args['meta_key'] = '_aksrt_res_view_count';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
break;
case 'comments':
$args['orderby'] = 'comment_count';
$args['order'] = 'DESC';
break;
case 'downloads':
default:
$args['meta_key'] = '_aksrt_res_download_count';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
break;
}
$query = new WP_Query($args);
if (!$query->have_posts()) {
wp_reset_postdata();
return;
}
$metric_label = [
'views' => __('阅读量', 'aksrt'),
'downloads' => __('下载量', 'aksrt'),
'comments' => __('评论数', 'aksrt'),
][$metric];
$display_title = $title ?: sprintf(__('%s 排行榜', 'aksrt'), $metric_label);
?>
<section class="home-module home-module--ranking container" aria-label="<?php echo esc_attr($display_title); ?>">
<header class="home-module__header">
<h2 class="home-module__title">
<i class="fas fa-trophy" aria-hidden="true"></i>
<?php echo esc_html($display_title); ?>
</h2>
</header>
<ol class="home-module__ranking-list">
<?php
$rank = 1;
while ($query->have_posts()) :
$query->the_post();
$value = 0;
if ($metric === 'views') {
$value = (int) get_post_meta(get_the_ID(), '_aksrt_res_view_count', true);
} elseif ($metric === 'comments') {
$value = (int) get_comments_number();
} else {
$value = (int) get_post_meta(get_the_ID(), '_aksrt_res_download_count', true);
}
?>
<li class="home-module__ranking-item home-module__ranking-item--<?php echo $rank <= 3 ? 'top' : 'normal'; ?>">
<span class="home-module__ranking-num"><?php echo (int) $rank; ?></span>
<a class="home-module__ranking-link" href="<?php the_permalink(); ?>">
<?php the_title(); ?>
</a>
<span class="home-module__ranking-value"><?php echo esc_html(number_format_i18n($value)); ?></span>
</li>
<?php
$rank++;
endwhile;
wp_reset_postdata();
?>
</ol>
</section>
@@ -0,0 +1,84 @@
<?php
/**
* Home Module · Recent
*
* config: { post_type, count, layout, title? }
*
* post_type 取值:
* 'post' → 最新文章
* 'aksrt_resource' → 最新资源(v3.4 起兼容 post + _aksrt_is_resource)
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$post_type = (string) ($cfg['post_type'] ?? 'post');
$count = max(1, min(20, (int) ($cfg['count'] ?? 6)));
$layout = (string) ($cfg['layout'] ?? 'card');
$title = (string) ($cfg['title'] ?? '');
if (!in_array($layout, ['card', 'list', 'masonry', 'grid-cms'], true)) {
$layout = 'card';
}
if (!in_array($post_type, ['post', 'aksrt_resource'], true)) $post_type = 'post';
$is_resource_module = $post_type === 'aksrt_resource';
$args = [
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
'orderby' => 'date',
'order' => 'DESC',
];
if ($is_resource_module) {
$args['post_type'] = function_exists('aksrt_resource_post_types') ? aksrt_resource_post_types() : ['post'];
if (!post_type_exists('aksrt_resource')) {
$args['meta_query'] = [[
'key' => '_aksrt_is_resource',
'value' => '1',
'compare' => '=',
]];
}
} else {
$args['post_type'] = 'post';
}
$query = new WP_Query($args);
if (!$query->have_posts()) {
wp_reset_postdata();
return;
}
$default_title = $is_resource_module ? __('最新资源', 'aksrt') : __('最新文章', 'aksrt');
$display_title = $title ?: $default_title;
$archive_link = $is_resource_module
? (function_exists('aksrt_resource_archive_url') ? aksrt_resource_archive_url() : home_url('/resources/'))
: get_permalink(get_option('page_for_posts'));
?>
<section class="home-module home-module--recent container" aria-label="<?php echo esc_attr($display_title); ?>">
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($display_title); ?></h2>
<?php if ($archive_link) : ?>
<a class="home-module__more" href="<?php echo esc_url($archive_link); ?>">
<?php esc_html_e('查看更多', 'aksrt'); ?> <i class="fas fa-arrow-right" aria-hidden="true"></i>
</a>
<?php endif; ?>
</header>
<div class="post-grid post-grid--<?php echo esc_attr($layout); ?>">
<?php
set_query_var('aksrt_post_card_layout', $layout);
set_query_var('aksrt_post_card_scene', 'home');
while ($query->have_posts()) {
$query->the_post();
get_template_part('templates/parts/content', 'post-card');
}
set_query_var('aksrt_post_card_layout', '');
set_query_var('aksrt_post_card_scene', '');
wp_reset_postdata();
?>
</div>
</section>
@@ -0,0 +1,17 @@
<?php
/**
* Home Module · Spacing
*
* config: { height? }
*
* 间距模块,用于模块间留白。
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$height = max(8, min(200, (int) ($cfg['height'] ?? 40)));
?>
<div class="home-module--spacing" style="height:<?php echo esc_attr((string) $height); ?>px" aria-hidden="true"></div>
@@ -0,0 +1,108 @@
<?php
/**
* Home Module · Tabs
*
* config: { cat_ids: int[], count, layout, title? }
*
* P6 阶段先实现"按 Tab 标题分组渲染",Tab 切换交互在 P9 阶段引入 JS。
* 默认行为:渲染选中分类(cat_ids[0])的卡片,其它 Tab 用 data-* 标记,
* 配合 cms-home-modules.js 切换 active。
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$cat_ids = isset($cfg['cat_ids']) && is_array($cfg['cat_ids']) ? array_map('intval', $cfg['cat_ids']) : [];
$cat_ids = array_filter($cat_ids, fn($x) => $x > 0);
$count = max(1, min(20, (int) ($cfg['count'] ?? 8)));
$layout = (string) ($cfg['layout'] ?? 'grid-cms');
$title = (string) ($cfg['title'] ?? '');
if (!in_array($layout, ['card', 'list', 'masonry', 'grid-cms'], true)) {
$layout = 'grid-cms';
}
if (empty($cat_ids)) {
// 没有指定 → 取站点最热的两个分类
$top_cats = get_terms([
'taxonomy' => 'category',
'orderby' => 'count',
'order' => 'DESC',
'number' => 4,
'hide_empty' => true,
'fields' => 'ids',
]);
if (is_wp_error($top_cats)) $top_cats = [];
$cat_ids = array_map('intval', $top_cats);
}
if (empty($cat_ids)) return;
$tabs = [];
foreach ($cat_ids as $cid) {
$term = get_term($cid, 'category');
if (!$term || is_wp_error($term)) continue;
$tabs[] = [
'id' => $cid,
'name' => $term->name,
'link' => get_category_link($cid),
];
}
if (empty($tabs)) return;
$active_id = $tabs[0]['id'];
?>
<section class="home-module home-module--tabs container home-tab-content index-tab-main" aria-label="<?php echo esc_attr($title ?: __('分类切换', 'aksrt')); ?>">
<header class="home-module__header home-module__header--tabs">
<h2 class="home-module__title"><?php echo esc_html($title ?: __('热门分类', 'aksrt')); ?></h2>
<nav class="home-module__tabs" role="tablist" data-aksrt-tabs>
<?php foreach ($tabs as $i => $t) : ?>
<button type="button"
role="tab"
class="home-module__tab<?php echo $i === 0 ? ' is-active' : ''; ?>"
aria-selected="<?php echo $i === 0 ? 'true' : 'false'; ?>"
data-tab-target="cat-<?php echo (int) $t['id']; ?>">
<?php echo esc_html($t['name']); ?>
</button>
<?php endforeach; ?>
</nav>
</header>
<?php foreach ($tabs as $i => $t) :
$tab_post_types = function_exists('aksrt_resource_post_types')
? array_unique(array_merge(['post'], aksrt_resource_post_types()))
: ['post'];
$query = new WP_Query([
'cat' => $t['id'],
'post_type' => $tab_post_types,
'posts_per_page' => $count,
'post_status' => 'publish',
'no_found_rows' => true,
]);
?>
<div class="home-module__tab-panel tab-pane<?php echo $i === 0 ? ' is-active' : ''; ?>"
role="tabpanel"
data-tab-id="cat-<?php echo (int) $t['id']; ?>"
<?php echo $i !== 0 ? 'hidden' : ''; ?>>
<?php if ($query->have_posts()) : ?>
<div class="posts-row ajaxpager tab-content">
<?php
set_query_var('aksrt_post_card_layout', $layout === 'list' ? 'list' : 'card');
set_query_var('aksrt_post_card_scene', 'home');
while ($query->have_posts()) {
$query->the_post();
get_template_part('templates/parts/content', 'post-card');
}
set_query_var('aksrt_post_card_layout', '');
set_query_var('aksrt_post_card_scene', '');
wp_reset_postdata();
?>
</div>
<?php else : ?>
<p class="home-module__empty"><?php esc_html_e('该分类暂无内容', 'aksrt'); ?></p>
<?php endif; ?>
</div>
<?php endforeach; ?>
</section>
@@ -0,0 +1,40 @@
<?php
/**
* Home Module · Tag Cloud
*
* config: { limit, title? }
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$limit = max(5, min(100, (int) ($cfg['limit'] ?? 30)));
$title = (string) ($cfg['title'] ?? '');
$tags = get_terms([
'taxonomy' => 'post_tag',
'orderby' => 'count',
'order' => 'DESC',
'number' => $limit,
'hide_empty' => true,
]);
if (is_wp_error($tags) || empty($tags)) return;
?>
<section class="home-module home-module--tags container" aria-label="<?php echo esc_attr($title ?: __('热门标签', 'aksrt')); ?>">
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($title ?: __('热门标签', 'aksrt')); ?></h2>
</header>
<ul class="home-module__tag-cloud">
<?php foreach ($tags as $tag) : ?>
<li>
<a href="<?php echo esc_url(get_tag_link($tag->term_id)); ?>" class="home-module__tag">
<span class="home-module__tag-name">#<?php echo esc_html($tag->name); ?></span>
<span class="home-module__tag-count"><?php echo (int) $tag->count; ?></span>
</a>
</li>
<?php endforeach; ?>
</ul>
</section>
@@ -0,0 +1,32 @@
<?php
/**
* Home Module · Text Title
*
* config: { title, subtitle?, align?, tag? }
*
* 纯文本标题模块,用于页面分隔和标题引导。
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) $cfg = [];
$title = (string) ($cfg['title'] ?? '');
$subtitle = (string) ($cfg['subtitle'] ?? '');
$align = sanitize_key((string) ($cfg['align'] ?? 'left'));
$tag = sanitize_key((string) ($cfg['tag'] ?? 'h2'));
if ($title === '' && $subtitle === '') return;
$valid_tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
if (!in_array($tag, $valid_tags, true)) $tag = 'h2';
?>
<section class="home-module home-module--text-title container" style="text-align:<?php echo esc_attr($align); ?>">
<?php if ($title !== '') : ?>
<<?php echo esc_attr($tag); ?> class="home-module__title"><?php echo esc_html($title); ?></<?php echo esc_attr($tag); ?>>
<?php endif; ?>
<?php if ($subtitle !== '') : ?>
<p class="home-module__subtitle"><?php echo esc_html($subtitle); ?></p>
<?php endif; ?>
</section>
@@ -0,0 +1,85 @@
<?php
/**
* Home Module · User Cards
*
* Maps to zibll widget-user capability.
* config: { title?, count, columns, order(recent/posts/manual), user_ids[], show_stats }
*/
defined('ABSPATH') || exit;
$cfg = get_query_var('aksrt_module_config', []);
if (!is_array($cfg)) {
$cfg = [];
}
$title = trim((string) ($cfg['title'] ?? ''));
$count = max(1, min(12, (int) ($cfg['count'] ?? 4)));
$columns = max(2, min(6, (int) ($cfg['columns'] ?? 4)));
$order = sanitize_key((string) ($cfg['order'] ?? 'recent'));
$user_ids = isset($cfg['user_ids']) && is_array($cfg['user_ids']) ? array_map('absint', $cfg['user_ids']) : [];
$show_stats = !isset($cfg['show_stats']) || !empty($cfg['show_stats']);
// Build user query.
if ($order === 'manual' && !empty($user_ids)) {
$query_args = [
'include' => array_slice($user_ids, 0, $count),
'orderby' => 'include',
'number' => $count,
];
} elseif ($order === 'posts') {
$query_args = [
'number' => $count,
'orderby' => 'post_count',
'order' => 'DESC',
];
} else {
// 'recent' = newest registrations.
$query_args = [
'number' => $count,
'orderby' => 'registered',
'order' => 'DESC',
];
}
$users = get_users($query_args);
if (empty($users)) {
return;
}
?>
<section class="home-module home-module--users container" aria-label="<?php echo esc_attr($title ?: __('推荐用户', 'aksrt')); ?>">
<header class="home-module__header">
<h2 class="home-module__title"><?php echo esc_html($title ?: __('推荐用户', 'aksrt')); ?></h2>
</header>
<div class="home-module__users-grid" style="--cols: <?php echo (int) $columns; ?>;">
<?php foreach ($users as $user) :
$uid = $user->ID;
$avatar = get_avatar_url($uid, ['size' => 96]);
$display = $user->display_name ?: $user->user_login;
$bio = get_user_meta($uid, 'description', true);
$post_cnt = count_user_posts($uid);
$profile = function_exists('aksrt_get_user_home_url')
? aksrt_get_user_home_url($uid)
: get_author_posts_url($uid);
?>
<div class="home-module__user-card">
<a href="<?php echo esc_url($profile); ?>" class="home-module__user-link">
<img src="<?php echo esc_url($avatar); ?>" alt="" class="home-module__user-avatar"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
<strong class="home-module__user-name"><?php echo esc_html($display); ?></strong>
</a>
<?php if ($bio) : ?>
<p class="home-module__user-bio"><?php echo esc_html(wp_trim_words($bio, 12, '...')); ?></p>
<?php endif; ?>
<?php if ($show_stats) : ?>
<span class="home-module__user-stats">
<span class="home-module__user-stat">
<i class="fas fa-file-alt" aria-hidden="true"></i>
<?php echo (int) $post_cnt; ?>
</span>
</span>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</section>
+40
View File
@@ -0,0 +1,40 @@
<?php
/**
* 登录提示模板
*
* 在需要登录才能访问的内容区域展示。
*
* @package AKSRT
* @since 5.0.0
*
* @var string $message 提示文字
* @var string $redirect 登录后跳转地址
*/
defined('ABSPATH') || exit;
$message = $args['message'] ?? __('登录后查看完整内容', 'aksrt');
$redirect = $args['redirect'] ?? get_permalink();
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url($redirect) : wp_login_url($redirect);
$register_url = function_exists('aksrt_get_register_url') ? aksrt_get_register_url() : wp_registration_url();
$can_register = get_option('users_can_register');
?>
<div class="login-prompt">
<div class="login-prompt__inner">
<div class="login-prompt__icon">
<i class="fas fa-lock"></i>
</div>
<p class="login-prompt__text"><?php echo esc_html($message); ?></p>
<div class="login-prompt__btns">
<a href="<?php echo esc_url($login_url); ?>" class="btn btn-primary btn-sm">
<i class="fas fa-sign-in-alt"></i> <?php esc_html_e('登录', 'aksrt'); ?>
</a>
<?php if ($can_register) : ?>
<a href="<?php echo esc_url($register_url); ?>" class="btn btn-outline-primary btn-sm">
<i class="fas fa-user-plus"></i> <?php esc_html_e('注册', 'aksrt'); ?>
</a>
<?php endif; ?>
</div>
</div>
</div>
+255
View File
@@ -0,0 +1,255 @@
<?php
/**
* Modal auth dialog.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (is_user_logged_in()) {
return;
}
$oauth_providers = [];
if (class_exists('AKSRT_OAuth')) {
$oauth_providers = AKSRT_OAuth::get_instance()->get_auth_providers_data();
}
$registration_open = true;
$email_verification = false;
$close_signin = false;
$invite_field_mode = 'hidden';
$invite_field_hint = '';
$invite_prefill = '';
$show_qrcode_tab = false;
if (class_exists('AKSRT_Settings')) {
$registration_open = AKSRT_Settings::registration_enabled();
$email_verification = AKSRT_Settings::registration_requires_email_verification();
$close_signin = AKSRT_Settings::close_signin();
$invite_cfg = AKSRT_Settings::get_invite_registration_config();
$invite_field_mode = (string) ($invite_cfg['register_field'] ?? 'hidden');
$invite_field_hint = $invite_field_mode === 'required'
? (string) ($invite_cfg['must_desc'] ?? '')
: (string) ($invite_cfg['open_desc'] ?? '');
}
if (!empty($_GET['invite'])) {
$invite_prefill = sanitize_key(wp_unslash($_GET['invite']));
} elseif (!empty($_COOKIE['aksrt_invite'])) {
$invite_prefill = sanitize_key(wp_unslash($_COOKIE['aksrt_invite']));
}
foreach ($oauth_providers as $provider) {
if (($provider['name'] ?? '') === 'weixingzh') {
$show_qrcode_tab = true;
break;
}
}
$site_name = get_bloginfo('name');
$reset_url = function_exists('aksrt_get_reset_password_url') ? aksrt_get_reset_password_url() : wp_lostpassword_url();
?>
<div class="aksrt-modal-auth" id="aksrt-modal-auth" aria-hidden="true" role="dialog" aria-modal="true" aria-label="<?php esc_attr_e('Sign In', 'aksrt'); ?>">
<div class="aksrt-modal-auth__overlay"></div>
<div class="aksrt-modal-auth__card">
<button type="button" class="aksrt-modal-auth__close" aria-label="<?php esc_attr_e('Close', 'aksrt'); ?>">
<i class="fas fa-times"></i>
</button>
<div class="aksrt-modal-auth__header">
<div class="aksrt-modal-auth__logo">AK</div>
<h2 class="aksrt-modal-auth__title"><?php echo esc_html($site_name); ?></h2>
</div>
<div class="aksrt-modal-auth__tabs" role="tablist">
<button type="button" class="aksrt-modal-auth__tab is-active" data-tab="login" role="tab" aria-selected="true">
<?php esc_html_e('Sign In', 'aksrt'); ?>
</button>
<?php if ($registration_open) : ?>
<button type="button" class="aksrt-modal-auth__tab" data-tab="register" role="tab" aria-selected="false">
<?php esc_html_e('Register', 'aksrt'); ?>
</button>
<?php endif; ?>
<?php if ($show_qrcode_tab) : ?>
<button type="button" class="aksrt-modal-auth__tab" data-tab="qrcode" role="tab" aria-selected="false">
<?php esc_html_e('QR Login', 'aksrt'); ?>
</button>
<?php endif; ?>
</div>
<div class="aksrt-modal-auth__panel is-active" data-panel="login">
<?php if ($close_signin) : ?>
<div class="aksrt-modal-auth__error is-success" id="aksrt-modal-login-error">
<?php esc_html_e('Password sign-in is currently disabled. Please use one of the social login methods below.', 'aksrt'); ?>
</div>
<?php else : ?>
<form class="aksrt-modal-auth__form" id="aksrt-modal-login-form">
<div class="aksrt-modal-auth__error" id="aksrt-modal-login-error" hidden></div>
<?php wp_nonce_field('aksrt_login', '_login_nonce'); ?>
<div class="aksrt-modal-auth__field">
<label for="modal-login-user"><?php esc_html_e('Email / Username', 'aksrt'); ?></label>
<input type="text" id="modal-login-user" name="log" autocomplete="username" required placeholder="<?php esc_attr_e('Please enter your email or username', 'aksrt'); ?>">
</div>
<div class="aksrt-modal-auth__field">
<label for="modal-login-pass"><?php esc_html_e('Password', 'aksrt'); ?></label>
<div class="aksrt-modal-auth__pass-wrap">
<input type="password" id="modal-login-pass" name="pwd" autocomplete="current-password" required placeholder="<?php esc_attr_e('Please enter your password', 'aksrt'); ?>">
<button type="button" class="aksrt-modal-auth__pass-toggle" aria-label="<?php esc_attr_e('Toggle password visibility', 'aksrt'); ?>">
<i class="far fa-eye"></i>
</button>
</div>
</div>
<?php if (function_exists('aksrt_render_captcha')) : ?>
<?php aksrt_render_captcha('login'); ?>
<?php endif; ?>
<div class="aksrt-modal-auth__actions">
<a href="<?php echo esc_url($reset_url); ?>" class="aksrt-modal-auth__link"><?php esc_html_e('Forgot password?', 'aksrt'); ?></a>
</div>
<button type="submit" class="aksrt-modal-auth__submit">
<span><?php esc_html_e('Sign In', 'aksrt'); ?></span>
<i class="fas fa-spinner fa-spin" hidden></i>
</button>
</form>
<?php endif; ?>
</div>
<?php if ($registration_open) : ?>
<div class="aksrt-modal-auth__panel" data-panel="register">
<form class="aksrt-modal-auth__form" id="aksrt-modal-register-form">
<div class="aksrt-modal-auth__error" id="aksrt-modal-register-error" hidden></div>
<?php wp_nonce_field('aksrt_register', '_register_nonce'); ?>
<div class="aksrt-modal-auth__field">
<label for="modal-reg-user"><?php esc_html_e('Username', 'aksrt'); ?></label>
<input type="text" id="modal-reg-user" name="user_login" autocomplete="username" required placeholder="<?php esc_attr_e('Please enter a username', 'aksrt'); ?>">
</div>
<div class="aksrt-modal-auth__field">
<label for="modal-reg-email"><?php esc_html_e('Email', 'aksrt'); ?></label>
<input type="email" id="modal-reg-email" name="user_email" autocomplete="email" required placeholder="<?php esc_attr_e('Please enter your email', 'aksrt'); ?>">
</div>
<div class="aksrt-modal-auth__field">
<label for="modal-reg-pass"><?php esc_html_e('Password', 'aksrt'); ?></label>
<div class="aksrt-modal-auth__pass-wrap">
<input type="password" id="modal-reg-pass" name="user_pass" autocomplete="new-password" required minlength="6" placeholder="<?php esc_attr_e('At least 6 characters', 'aksrt'); ?>">
<button type="button" class="aksrt-modal-auth__pass-toggle" aria-label="<?php esc_attr_e('Toggle password visibility', 'aksrt'); ?>">
<i class="far fa-eye"></i>
</button>
</div>
</div>
<div class="aksrt-modal-auth__field">
<label for="modal-reg-pass-confirm"><?php esc_html_e('Confirm Password', 'aksrt'); ?></label>
<div class="aksrt-modal-auth__pass-wrap">
<input type="password" id="modal-reg-pass-confirm" name="user_pass_confirm" autocomplete="new-password" required minlength="6" placeholder="<?php esc_attr_e('Re-enter your password', 'aksrt'); ?>">
<button type="button" class="aksrt-modal-auth__pass-toggle" aria-label="<?php esc_attr_e('Toggle password visibility', 'aksrt'); ?>">
<i class="far fa-eye"></i>
</button>
</div>
</div>
<?php if ($invite_field_mode !== 'hidden') : ?>
<div class="aksrt-modal-auth__field">
<label for="modal-reg-invite">
<?php esc_html_e('邀请码', 'aksrt'); ?>
<?php if ($invite_field_mode === 'optional') : ?>
<small><?php esc_html_e('(选填)', 'aksrt'); ?></small>
<?php endif; ?>
</label>
<input type="text" id="modal-reg-invite" name="invite_code" value="<?php echo esc_attr($invite_prefill); ?>" <?php echo $invite_field_mode === 'required' ? 'required' : ''; ?> placeholder="<?php esc_attr_e('请输入邀请码', 'aksrt'); ?>">
<?php if ($invite_field_hint !== '') : ?>
<p class="aksrt-modal-auth__hint"><?php echo wp_kses_post($invite_field_hint); ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
<label class="aksrt-modal-auth__checkbox aksrt-modal-auth__agreement">
<input type="checkbox" name="agree_terms" value="1" required>
<span><?php esc_html_e('I agree to the Terms of Service', 'aksrt'); ?></span>
</label>
<?php if ($email_verification) : ?>
<p class="aksrt-modal-auth__hint"><?php esc_html_e('Email verification is required after registration.', 'aksrt'); ?></p>
<?php endif; ?>
<?php if (function_exists('aksrt_render_captcha')) : ?>
<?php aksrt_render_captcha('register'); ?>
<?php endif; ?>
<button type="submit" class="aksrt-modal-auth__submit">
<span><?php esc_html_e('Register', 'aksrt'); ?></span>
<i class="fas fa-spinner fa-spin" hidden></i>
</button>
</form>
</div>
<?php endif; ?>
<?php if ($show_qrcode_tab) : ?>
<div class="aksrt-modal-auth__panel" data-panel="qrcode">
<div class="aksrt-modal-auth__form">
<div class="aksrt-modal-auth__error" id="aksrt-modal-qrcode-error" hidden></div>
<div class="aksrt-modal-auth__qr-code"></div>
<p class="aksrt-modal-auth__hint"><?php esc_html_e('Use WeChat to scan the QR code and complete sign-in on your phone.', 'aksrt'); ?></p>
</div>
</div>
<?php endif; ?>
<?php if (!empty($oauth_providers)) : ?>
<div class="aksrt-modal-auth__divider">
<span><?php esc_html_e('Or continue with', 'aksrt'); ?></span>
</div>
<div class="aksrt-modal-auth__oauth">
<?php foreach ($oauth_providers as $provider) :
$provider_name = (string) ($provider['name'] ?? '');
$label = $provider['label'] ?? $provider_name;
$url = $provider['url'] ?? '#';
$icon = $provider['icon'] ?? '';
?>
<a
href="<?php echo esc_url($url); ?>"
class="aksrt-modal-auth__oauth-btn social-login-item <?php echo esc_attr($provider_name); ?>"
data-provider="<?php echo esc_attr($provider_name); ?>"
title="<?php echo esc_attr($label); ?>"
>
<?php echo $icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="aksrt-modal-auth__footer">
<span class="aksrt-modal-auth__footer-text" data-for="login">
<?php if ($registration_open) : ?>
<?php esc_html_e('No account yet?', 'aksrt'); ?>
<button type="button" class="aksrt-modal-auth__switch" data-switch="register"><?php esc_html_e('Register now', 'aksrt'); ?></button>
<?php elseif ($show_qrcode_tab) : ?>
<?php esc_html_e('Prefer QR sign-in?', 'aksrt'); ?>
<button type="button" class="aksrt-modal-auth__switch" data-switch="qrcode"><?php esc_html_e('Open QR Login', 'aksrt'); ?></button>
<?php endif; ?>
</span>
<?php if ($registration_open) : ?>
<span class="aksrt-modal-auth__footer-text" data-for="register" hidden>
<?php esc_html_e('Already have an account?', 'aksrt'); ?>
<button type="button" class="aksrt-modal-auth__switch" data-switch="login"><?php esc_html_e('Sign in now', 'aksrt'); ?></button>
</span>
<?php endif; ?>
<?php if ($show_qrcode_tab) : ?>
<span class="aksrt-modal-auth__footer-text" data-for="qrcode" hidden>
<?php esc_html_e('Want to use your password instead?', 'aksrt'); ?>
<button type="button" class="aksrt-modal-auth__switch" data-switch="login"><?php esc_html_e('Back to Sign In', 'aksrt'); ?></button>
</span>
<?php endif; ?>
</div>
</div>
</div>
+93
View File
@@ -0,0 +1,93 @@
<?php
/**
* Reusable Page Header Template Part.
*
* Renders a unified page header for archive / search / page templates.
* Style is driven by the global setting `page_header_style` with optional
* per-page post-meta override (`page_header_style`).
*
* Query vars consumed (set via set_query_var before calling get_template_part):
* aksrt_ph_style – override style ('' | '1' | '2' | '3'), defaults to global setting
* aksrt_ph_eyebrow – eyebrow label text
* aksrt_ph_title – title HTML (already escaped by caller)
* aksrt_ph_desc – description text
* aksrt_ph_cover – cover image URL override
* aksrt_ph_extra – extra HTML appended inside the body area (e.g. admin quick-links)
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
// Resolve style: explicit query var > global setting.
$ph_style = get_query_var('aksrt_ph_style', null);
if ($ph_style === null) {
$ph_style = class_exists('AKSRT_Settings')
? (AKSRT_Settings::get_other_page_settings()['header_style'] ?? '1')
: '1';
}
// '' means "don't show header"
if ($ph_style === '') {
return;
}
$ph_eyebrow = get_query_var('aksrt_ph_eyebrow', '');
$ph_title = get_query_var('aksrt_ph_title', '');
$ph_desc = get_query_var('aksrt_ph_desc', '');
$ph_extra = get_query_var('aksrt_ph_extra', '');
// Cover image: explicit > global setting > post thumbnail.
$ph_cover = get_query_var('aksrt_ph_cover', '');
if (empty($ph_cover) && class_exists('AKSRT_Settings')) {
$other = AKSRT_Settings::get_other_page_settings();
$ph_cover = $other['header_cover_img'] ?? '';
}
if (empty($ph_cover) && has_post_thumbnail()) {
$ph_cover = get_the_post_thumbnail_url(get_the_ID(), 'full');
}
// Map style value to CSS modifier class.
$style_class = 'term-page-header--simple';
if ($ph_style === '2') {
$style_class = 'term-page-header--card';
} elseif ($ph_style === '3') {
$style_class = 'term-page-header--cover';
}
// Cover inline style (for cover style).
$cover_style = '';
if ($ph_style === '3' && !empty($ph_cover)) {
$cover_style = ' style="background-image:url(\'' . esc_url($ph_cover) . '\')"';
}
?>
<header class="page-header term-page-header <?php echo esc_attr($style_class); ?>">
<?php if ($ph_style === '3' && !empty($ph_cover)) : ?>
<div class="term-page-header__cover-bg"<?php echo $cover_style; /* phpcs:ignore -- escaped above */ ?>>
<div class="term-page-header__cover-overlay"></div>
</div>
<?php endif; ?>
<div class="term-page-header__body<?php echo $ph_style === '3' ? ' term-page-header__body--overlay' : ''; ?>">
<div>
<?php if ($ph_eyebrow !== '') : ?>
<p class="term-page-header__eyebrow"><?php echo esc_html($ph_eyebrow); ?></p>
<?php endif; ?>
<h1 class="entry-title"><?php echo $ph_title; /* phpcs:ignore -- caller must escape */ ?></h1>
<?php if ($ph_desc !== '') : ?>
<p class="term-page-header__desc"><?php echo esc_html($ph_desc); ?></p>
<?php endif; ?>
</div>
<?php if ($ph_extra !== '') : ?>
<?php echo $ph_extra; /* phpcs:ignore -- caller must escape */ ?>
<?php endif; ?>
</div>
<?php if ($ph_style === '2' && !empty($ph_cover)) : ?>
<div class="term-page-header__cover"<?php echo $cover_style; /* phpcs:ignore -- escaped above */ ?>></div>
<?php endif; ?>
</header>
+400
View File
@@ -0,0 +1,400 @@
<?php
/**
* Template Name: 用户中心
* Template Post Type: page
*
* 单一页面模板,根据场景显示不同内容:
* - 登录用户查看自己 → 仪表盘面板(个人信息 / 文章 / 评论 / 收藏 / 通知 / 设置)
* - 查看其他用户主页 → 公开用户主页(头像 / 简介 / 文章 / 评论)
*
* @package AKSRT
*/
// 加载共享辅助函数(分页、空状态)
require_once AKSRT_DIR . '/templates/parts/user-shared-helpers.php';
get_header(); ?>
<div id="toast-container"></div>
<script>function showToast(text,d){d=d||2500;var c=document.getElementById('toast-container');if(!c){c=document.createElement('div');c.id='toast-container';document.body.appendChild(c);}var t=document.createElement('div');t.className='toast-item';t.textContent=text;c.appendChild(t);setTimeout(function(){t.classList.add('fade-out');setTimeout(function(){if(t.parentNode)t.parentNode.removeChild(t)},300)},d)}</script>
<?php // ─── Post/Redirect/Get:处理表单提交后跳转,防止刷新重复提交 ──
if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_POST['action'], $_POST['_profile_nonce'] )
&& $_POST['action'] === 'aksrt_save_profile'
&& wp_verify_nonce( $_POST['_profile_nonce'], 'aksrt_profile_save' )
) {
$uid = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
if ( $uid && $uid === get_current_user_id() ) {
if ( isset( $_POST['nickname'] ) ) {
wp_update_user( [ 'ID' => $uid, 'display_name' => sanitize_text_field( $_POST['nickname'] ) ] );
}
if ( isset( $_POST['description'] ) ) {
wp_update_user( [ 'ID' => $uid, 'description' => sanitize_textarea_field( $_POST['description'] ) ] );
}
$contacts = [];
if ( isset( $_POST['contacts'] ) && is_array( $_POST['contacts'] ) ) {
foreach ( $_POST['contacts'] as $c ) {
$u = isset( $c['url'] ) ? esc_url_raw( $c['url'] ) : '';
$i = isset( $c['icon'] ) ? sanitize_key( $c['icon'] ) : '';
// 清理 FA5 类名前缀:fab fa-xxx / fabfa-xxx / fa-xxx → xxx
$i = preg_replace('/^(fab|fas|far)[\s-]*/', '', $i);
$i = preg_replace('/^fa[\s-]*/', '', $i);
$i = preg_replace('/^[\s-]+/', '', $i);
$i = trim( $i, '- ' );
if ( ! $u || ! $i ) continue;
$n = aksrt_icon_key_to_name( $i );
$contacts[] = [ 'name' => $n, 'url' => $u, 'icon' => $i ];
}
}
update_user_meta( $uid, 'aksrt_user_contacts', $contacts );
}
wp_safe_redirect( add_query_arg( 'saved', '1', aksrt_get_user_center_url() ) );
exit;
}
// ─── 确定目标用户 ─────────────────────────────────────────────
// 来源: /user/{昵称} (自定义重写规则)
// 注意: get_queried_object_id() 在页面中返回的是页面 ID 而非用户 ID
$target_user_id = 0;
$target_user_nicename = get_query_var('user_nicename');
if ( $target_user_nicename ) {
$user = get_user_by( 'slug', $target_user_nicename );
if ( $user ) {
$target_user_id = $user->ID;
}
}
// 兜底:检查 queried object
if ( ! $target_user_id ) {
$qid = get_queried_object_id();
if ( $qid && get_userdata( $qid ) ) {
$target_user_id = $qid;
}
}
$current_user = wp_get_current_user();
$target_user = $target_user_id ? get_userdata( $target_user_id ) : null;
$is_own_profile = false;
if ( ! $target_user ) {
if ( is_user_logged_in() ) {
$target_user = $current_user;
$target_user_id = $current_user->ID;
$is_own_profile = true;
} else {
wp_safe_redirect( aksrt_get_login_url( aksrt_get_user_center_url() ) );
exit;
}
} elseif ( is_user_logged_in() && (int) $current_user->ID === (int) $target_user->ID ) {
$is_own_profile = true;
}
if ( $target_user_nicename && ! $is_own_profile && function_exists( 'aksrt_get_user_home_url' ) ) {
wp_safe_redirect( aksrt_get_user_home_url( $target_user ) );
exit;
}
$user_id = $target_user->ID;
$post_count = (int) count_user_posts( $user_id, 'post', true );
$comment_count = (int) get_comments( [ 'user_id' => $user_id, 'count' => true, 'status' => 'approve' ] );
// 加载用户自定义联系方式
$user_contacts = get_user_meta( $user_id, 'aksrt_user_contacts', true ) ?: [];
if ( ! is_array( $user_contacts ) ) $user_contacts = [];
$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,
];
$user_home_settings = class_exists( 'AKSRT_Settings' )
? AKSRT_Settings::get_user_home_settings()
: [
'tabs' => [ 'posts', 'comments', 'favorites' ],
'sidebar' => true,
'cover' => true,
'follow' => true,
'posts_per_page' => 10,
];
$user_home_manager = class_exists( 'AKSRT_User_Home' ) ? AKSRT_User_Home::get_instance() : null;
$profile_signals = $user_home_manager ? $user_home_manager->get_profile_signals( $user_id ) : [];
$profile_notice = $user_home_manager ? $user_home_manager->get_profile_notice( $user_id ) : [];
$show_report_button = $user_home_manager ? $user_home_manager->show_report_entry() : false;
$public_visible_tabs = $user_home_manager
? $user_home_manager->get_visible_tabs( false )
: [
'posts' => [ 'label' => __( '文章', 'aksrt' ) ],
'comments' => [ 'label' => __( '评论', 'aksrt' ) ],
'favorites' => [ 'label' => __( '收藏', 'aksrt' ) ],
];
// 用户自定义背景图
$user_background_url = get_user_meta( $user_id, 'aksrt_background_image', true ) ?: '';
$allow_bg_upload = $is_own_profile
&& ! empty( $user_center_settings['cover_upload'] )
&& class_exists( 'AKSRT_Settings' )
&& aksrt_get_setting( 'allow_background_upload', false );
$show_avatar_edit = $is_own_profile && ! empty( $user_center_settings['avatar_upload'] );
$avatar_settings_url = admin_url( 'profile.php' );
$public_home_url = function_exists( 'aksrt_get_user_home_url' ) ? aksrt_get_user_home_url( $target_user ) : home_url( '/' );
$favorite_post_ids = function_exists( 'aksrt_get_user_interaction_posts' )
? aksrt_get_user_interaction_posts( '_aksrt_favorite_posts', $user_id )
: [];
$favorite_count = count( $favorite_post_ids );
$notification_unread_count = $is_own_profile && function_exists( 'aksrt_get_user_unread_notification_count' )
? aksrt_get_user_unread_notification_count( $user_id )
: 0;
$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_tab_keys = array_keys( $dashboard_tabs );
$public_tab_keys = array_keys( $public_visible_tabs );
if ( empty( $public_tab_keys ) ) {
$public_visible_tabs = [
'posts' => [ 'label' => __( '文章', 'aksrt' ) ],
'comments' => [ 'label' => __( '评论', 'aksrt' ) ],
'favorites' => [ 'label' => __( '收藏', 'aksrt' ) ],
];
$public_tab_keys = array_keys( $public_visible_tabs );
}
$public_tab_labels = [
'posts' => __( '文章', 'aksrt' ),
'comments' => __( '评论', 'aksrt' ),
'favorites' => __( '收藏', 'aksrt' ),
];
foreach ( $public_visible_tabs as $public_tab_key => $public_tab_config ) {
if ( ! empty( $public_tab_config['label'] ) ) {
$public_tab_labels[ $public_tab_key ] = (string) $public_tab_config['label'];
}
}
$public_tab_counts = [
'posts' => $post_count,
'comments' => $comment_count,
'favorites' => $favorite_count,
];
// 消息中心页面 URL
$messages_page = get_pages( [ 'meta_key' => '_wp_page_template', 'meta_value' => 'templates/pages/page-messages.php', 'number' => 1 ] );
$messages_url = ! empty( $messages_page ) ? get_permalink( $messages_page[0] ) : aksrt_get_user_center_url( 'notif' );
// ─── 文章状态计数(仅自己模式;用于状态筛选 chip) ────────────
$user_status_counts = [
'publish' => 0,
'draft' => 0,
'pending' => 0,
'private' => 0,
];
if ( $is_own_profile ) {
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 );
}
// ─── 分页与筛选参数(自己 / 他人 共享) ────────────────────────
$per_page = $is_own_profile ? 10 : max( 5, (int) ( $user_home_settings['posts_per_page'] ?? 10 ) );
$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;
$public_default_tab = $public_tab_keys[0] ?? 'posts';
$public_active_tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : $public_default_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;
}
if ( ! in_array( $public_active_tab, $public_tab_keys, true ) ) {
$public_active_tab = $public_default_tab;
}
$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 );
$valid_post_status = [ 'all', 'publish', 'draft', 'pending', 'private' ];
$post_status_filter = isset( $_GET['post_status'] ) ? sanitize_key( wp_unslash( $_GET['post_status'] ) ) : 'publish';
if ( ! in_array( $post_status_filter, $valid_post_status, true ) ) {
$post_status_filter = 'publish';
}
// 空状态图标 SVG(提取以减少重复)
$svg_empty_post = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><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"/><path d="M9 13h6M9 17h6M9 9h1"/></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" stroke-linecap="round" stroke-linejoin="round"><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" stroke-linecap="round" stroke-linejoin="round"><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>';
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<?php if ( $is_own_profile ) : ?>
<!-- ===================== 本人模式:用户中心 ===================== -->
<div class="user-center">
<?php
$oauth_action = isset( $_GET['oauth_action'] ) ? sanitize_key( wp_unslash( $_GET['oauth_action'] ) ) : '';
$oauth_notice_provider = isset( $_GET['oauth_bound'] ) ? sanitize_key( wp_unslash( $_GET['oauth_bound'] ) ) : '';
$oauth_unbound_provider = isset( $_GET['oauth_unbound'] ) ? sanitize_key( wp_unslash( $_GET['oauth_unbound'] ) ) : '';
$oauth_notice_names = [
'github' => 'GitHub',
'gitee' => 'Gitee',
'qq' => 'QQ',
'google' => 'Google',
];
?>
<?php if ( isset( $_GET['saved'] ) || $oauth_notice_provider || $oauth_unbound_provider ) : ?>
<script>
document.addEventListener('DOMContentLoaded',function(){
<?php if ( $oauth_notice_provider && $oauth_action === 'login' ) : ?>
showToast('<?php echo esc_js( sprintf( __( '%s 登录成功', 'aksrt' ), $oauth_notice_names[ $oauth_notice_provider ] ?? ucfirst( $oauth_notice_provider ) ) ); ?>');
<?php elseif ( $oauth_notice_provider ) : ?>
showToast('<?php echo esc_js( sprintf( __( '%s 绑定成功', 'aksrt' ), $oauth_notice_names[ $oauth_notice_provider ] ?? ucfirst( $oauth_notice_provider ) ) ); ?>');
<?php elseif ( $oauth_unbound_provider ) : ?>
showToast('<?php echo esc_js( sprintf( __( '%s 已解绑', 'aksrt' ), $oauth_notice_names[ $oauth_unbound_provider ] ?? ucfirst( $oauth_unbound_provider ) ) ); ?>');
<?php else : ?>
showToast('已保存');
<?php endif; ?>
if(window.history.replaceState){
var u=new URL(window.location);
u.searchParams.delete('saved');
u.searchParams.delete('oauth_bound');
u.searchParams.delete('oauth_unbound');
u.searchParams.delete('oauth_action');
window.history.replaceState({},'',u);
}
});
</script>
<?php endif; ?>
<!-- 头图 + 用户卡 -->
<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">
<?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>
<!-- 两栏布局 -->
<div class="uc-layout">
<!-- 左侧边栏 -->
<aside class="uc-sidebar">
<!-- 数据统计 -->
<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 ) : ?>
<button type="button" class="uc-sidebar__btn" 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>
</button>
<?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
$orders_url = function_exists('aksrt_get_orders_page_url') ? aksrt_get_orders_page_url() : '';
$show_vip = class_exists('AKSRT_Settings') && !empty(AKSRT_Settings::get_all()['payment_vip_enabled']);
$show_credit_tabs = class_exists('AKSRT_Settings') && AKSRT_Settings::credits_enabled();
+403
View File
@@ -0,0 +1,403 @@
<?php
defined('ABSPATH') || exit;
$affiliate_enabled = $show_credit_tabs && !empty(AKSRT_Settings::get_all()['payment_affiliate_enabled']);
?>
<?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) : ?>
<button type="button" class="uc-sidebar__btn" data-tab="credits">
<i class="fas fa-crown" aria-hidden="true"></i>
<span><?php esc_html_e('VIP 会员', 'aksrt'); ?></span>
</button>
<?php endif; ?>
<?php if ($show_credit_tabs) : ?>
<button type="button" class="uc-sidebar__btn" data-tab="credits">
<i class="fas fa-coins" aria-hidden="true"></i>
<span><?php echo esc_html(AKSRT_Settings::get_currency_name()); ?></span>
</button>
<?php endif; ?>
<?php if ($affiliate_enabled) : ?>
<button type="button" class="uc-sidebar__btn" data-tab="credits">
<i class="fas fa-share-alt" aria-hidden="true"></i>
<span><?php esc_html_e('推广返佣', 'aksrt'); ?></span>
</button>
<?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>
</aside>
<!-- 右侧内容区 -->
<div class="uc-content">
<!-- 移动端抽屉遮罩 -->
<div class="uc-drawer-overlay" id="uc-drawer-overlay"></div>
<!-- 面板内容 -->
<div class="dashboard-tabs uc-mobile-tabs" role="tablist">
<?php foreach ( $dashboard_tabs as $tab_key => $tab ) : ?>
<button type="button" class="<?php echo $active_tab === $tab_key ? 'is-active' : ''; ?>" data-tab="<?php echo esc_attr( $tab_key ); ?>" role="tab"><?php echo esc_html( $tab['label'] ); ?></button>
<?php endforeach; ?>
</div>
<?php
$show_credit_tabs = class_exists( 'AKSRT_Settings' ) && AKSRT_Settings::credits_enabled();
$show_invite_section = $show_credit_tabs && class_exists( 'AKSRT_Settings' ) && AKSRT_Settings::invite_enabled() && AKSRT_Settings::invite_registration_enabled();
$currency_name = $show_credit_tabs ? AKSRT_Settings::get_currency_name() : __( '积分', 'aksrt' );
?>
<!-- ==================== 个人信息(可编辑) ==================== -->
<div id="panel-profile" class="dashboard-panel<?php echo 'profile' === $active_tab ? ' is-active' : ''; ?>" role="tabpanel"<?php echo 'profile' !== $active_tab ? ' hidden' : ''; ?>>
<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">
<!-- 昵称 -->
<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>
</div>
<!-- ==================== 我的文章 ==================== -->
<div id="panel-posts" class="dashboard-panel<?php echo 'posts' === $active_tab ? ' is-active' : ''; ?>" role="tabpanel"<?php echo 'posts' !== $active_tab ? ' hidden' : ''; ?>>
<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;
}
set_query_var( 'aksrt_profile_item_status', $status_label );
$edit_link = get_edit_post_link();
set_query_var( 'aksrt_profile_item_action', $edit_link ? [
'type' => 'edit',
'url' => $edit_link,
'label' => __( '编辑', 'aksrt' ),
] : [] );
get_template_part( 'templates/parts/content', 'profile-list-item' );
endwhile; ?>
</div>
<?php aksrt_render_profile_pagination( (int) $q->max_num_pages, $posts_page, 'posts_page' ); ?>
<?php
wp_reset_postdata();
set_query_var( 'aksrt_profile_item_action', [] );
set_query_var( 'aksrt_profile_item_status', '' );
else :
aksrt_render_profile_empty(
$svg_empty_post,
__( '还没有文章', 'aksrt' ),
__( '试试发布你的第一篇文章吧。', 'aksrt' ),
admin_url( 'post-new.php' ),
__( '写文章', 'aksrt' )
);
endif; ?>
</div>
</div>
<!-- ==================== 我的评论 ==================== -->
<div id="panel-comments" class="dashboard-panel<?php echo 'comments' === $active_tab ? ' is-active' : ''; ?>" role="tabpanel"<?php echo 'comments' !== $active_tab ? ' hidden' : ''; ?>>
<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 ) :
set_query_var( 'aksrt_profile_comment', $c );
get_template_part( 'templates/parts/content', 'profile-comment-item' );
endforeach;
set_query_var( 'aksrt_profile_comment', null ); ?>
</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>
</div>
<!-- ==================== 我的收藏 ==================== -->
<div id="panel-favorites" class="dashboard-panel<?php echo 'favorites' === $active_tab ? ' is-active' : ''; ?>" role="tabpanel"<?php echo 'favorites' !== $active_tab ? ' hidden' : ''; ?>>
<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();
set_query_var( 'aksrt_profile_item_action', [
'type' => 'unfavorite',
'url' => '#',
'label' => __( '取消收藏', 'aksrt' ),
] );
set_query_var( 'aksrt_profile_item_status', '' );
get_template_part( 'templates/parts/content', 'profile-list-item' );
endwhile; ?>
</div>
<?php aksrt_render_profile_pagination( (int) $favorites_query->max_num_pages, $favorites_page, 'favorites_page' ); ?>
<?php
wp_reset_postdata();
set_query_var( 'aksrt_profile_item_action', [] );
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>
</div>
<?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 id="panel-credits" class="dashboard-panel<?php echo 'credits' === $active_tab ? ' is-active' : ''; ?>" role="tabpanel"<?php echo 'credits' !== $active_tab ? ' hidden' : ''; ?>>
<!-- 余额卡片 -->
<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)) : ?>
+403
View File
@@ -0,0 +1,403 @@
<?php
defined('ABSPATH') || exit;
<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>
</div>
<!-- ==================== 账号设置 ==================== -->
<div id="panel-settings" class="dashboard-panel<?php echo 'settings' === $active_tab ? ' is-active' : ''; ?>" role="tabpanel"<?php echo 'settings' !== $active_tab ? ' hidden' : ''; ?>>
<?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' ); ?>">
</div>
<!-- ==================== 修改密码弹窗 ==================== -->
<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>
</div><!-- /.uc-content -->
</div><!-- /.uc-layout -->
</div><!-- /.user-center -->
<?php else : ?>
<!-- ===================== 他人模式:公开用户主页 ===================== -->
<header class="profile-public-header"<?php if ( ! empty( $user_home_settings['cover'] ) && $user_background_url ) echo ' style="--user-bg:url(' . esc_url( $user_background_url ) . ')"'; ?>>
<div class="profile-public-left">
<?php echo get_avatar( $user_id, 96, '', $target_user->display_name ); ?>
<h1><?php echo esc_html( $target_user->display_name ); ?></h1>
<?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; ?>
<?php if ( ! empty( $user_contacts ) && ! empty( $user_center_settings['social_links'] ) ) : ?>
<div class="profile-public-contacts">
<?php if ( function_exists( 'aksrt_render_social_links' ) ) : ?>
<?php aksrt_render_social_links( $user_contacts ); ?>
<?php else : ?>
<?php foreach ( $user_contacts as $c ) : ?>
<a href="<?php echo esc_url( $c['url'] ?? '#' ); ?>" target="_blank" rel="noopener" title="<?php echo esc_attr( $c['name'] ?? '' ); ?>">
<?php echo esc_html( $c['name'] ?? '' ); ?>
</a>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<div class="profile-public-right">
<?php if ( $target_user->description ) : ?>
<p><?php echo nl2br( esc_html( $target_user->description ) ); ?></p>
<?php else : ?>
<p class="profile-public-empty"><?php esc_html_e( '这个人很懒,还没有填写个人简介', 'aksrt' ); ?></p>
<?php endif; ?>
<div class="uc-header__actions">
<?php if ( ! empty( $user_home_settings['follow'] ) ) : ?>
<?php if ( is_user_logged_in() ) : ?>
<button type="button" class="btn-outline btn-sm aksrt-user-home__follow-btn" data-aksrt-follow="<?php echo (int) $user_id; ?>" title="<?php esc_attr_e( '关注该用户', 'aksrt' ); ?>">
<i class="far fa-heart" aria-hidden="true"></i>
<span><?php esc_html_e( '关注', 'aksrt' ); ?></span>
+222
View File
@@ -0,0 +1,222 @@
<?php
defined('ABSPATH') || exit;
</button>
<?php else : ?>
<a href="<?php echo esc_url( aksrt_get_login_url( $public_home_url ) ); ?>" class="btn-outline btn-sm aksrt-user-home__follow-btn">
<i class="far fa-heart" aria-hidden="true"></i>
<span><?php esc_html_e( '登录后关注', 'aksrt' ); ?></span>
</a>
<?php endif; ?>
<?php endif; ?>
<?php if ( $show_report_button ) : ?>
<?php if ( is_user_logged_in() ) : ?>
<button type="button" class="btn-ghost btn-sm aksrt-user-home__report-btn" data-aksrt-report='{"target_type":"user","target_id":<?php echo (int) $user_id; ?>}' title="<?php esc_attr_e( '举报该用户', 'aksrt' ); ?>">
<i class="far fa-flag" aria-hidden="true"></i>
<span><?php esc_html_e( '举报', 'aksrt' ); ?></span>
</button>
<?php else : ?>
<a href="<?php echo esc_url( aksrt_get_login_url( $public_home_url ) ); ?>" class="btn-ghost btn-sm aksrt-user-home__report-btn">
<i class="far fa-flag" aria-hidden="true"></i>
<span><?php esc_html_e( '登录后举报', 'aksrt' ); ?></span>
</a>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
</header>
<?php if ( ! empty( $profile_notice ) ) : ?>
<div class="aksrt-user-home__notice aksrt-user-home__notice--<?php echo esc_attr( $profile_notice['state'] ); ?>">
<i class="fas fa-exclamation-circle" aria-hidden="true"></i>
<span><?php echo esc_html( $profile_notice['text'] ); ?></span>
</div>
<?php endif; ?>
<div class="dashboard dashboard--public" data-default-tab="<?php echo esc_attr( $public_default_tab ); ?>">
<div class="dashboard-tabs" role="tablist">
<?php foreach ( $public_tab_keys as $public_tab_key ) : ?>
<button type="button" class="<?php echo $public_active_tab === $public_tab_key ? 'is-active' : ''; ?>" data-tab="<?php echo esc_attr( $public_tab_key ); ?>" role="tab">
<?php echo esc_html( $public_tab_labels[ $public_tab_key ] ?? ucfirst( $public_tab_key ) ); ?>
<span class="dashboard-tabs__count"><?php echo esc_html( (string) ( $public_tab_counts[ $public_tab_key ] ?? 0 ) ); ?></span>
</button>
<?php endforeach; ?>
</div>
<?php if ( in_array( 'posts', $public_tab_keys, true ) ) : ?>
<div id="panel-posts" class="dashboard-panel<?php echo 'posts' === $public_active_tab ? ' is-active' : ''; ?>" role="tabpanel"<?php echo 'posts' !== $public_active_tab ? ' hidden' : ''; ?>>
<?php
$author_posts = new WP_Query( [
'author' => $user_id,
'posts_per_page' => $per_page,
'post_status' => 'publish',
'paged' => $posts_page,
'no_found_rows' => false,
'update_post_meta_cache' => false,
] );
if ( $author_posts->have_posts() ) : ?>
<div class="profile-list">
<?php while ( $author_posts->have_posts() ) : $author_posts->the_post();
set_query_var( 'aksrt_profile_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' );
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; ?>
</div>
<?php endif; ?>
<?php if ( in_array( 'comments', $public_tab_keys, true ) ) : ?>
<div id="panel-comments" class="dashboard-panel<?php echo 'comments' === $public_active_tab ? ' is-active' : ''; ?>" role="tabpanel"<?php echo 'comments' !== $public_active_tab ? ' hidden' : ''; ?>>
<?php
$user_comments = get_comments( [
'user_id' => $user_id,
'status' => 'approve',
'number' => $per_page,
'offset' => ( $comments_page - 1 ) * $per_page,
'type' => 'comment',
'update_comment_meta_cache' => false,
] );
if ( $user_comments ) : ?>
<div class="profile-comment-list">
<?php foreach ( $user_comments as $c ) :
set_query_var( 'aksrt_profile_comment', $c );
get_template_part( 'templates/parts/content', 'profile-comment-item' );
endforeach;
set_query_var( 'aksrt_profile_comment', null ); ?>
</div>
<?php
$cm_total_pages = (int) ceil( $comment_count / $per_page );
aksrt_render_profile_pagination( $cm_total_pages, $comments_page, 'comments_page' );
?>
<?php else :
aksrt_render_profile_empty( $svg_empty_comment, __( 'TA 还没有发表评论', 'aksrt' ) );
endif; ?>
</div>
<?php endif; ?>
<?php if ( in_array( 'favorites', $public_tab_keys, true ) ) : ?>
<div id="panel-favorites" class="dashboard-panel<?php echo 'favorites' === $public_active_tab ? ' is-active' : ''; ?>" role="tabpanel"<?php echo 'favorites' !== $public_active_tab ? ' hidden' : ''; ?>>
<?php
if ( ! empty( $favorite_post_ids ) ) {
$favorite_query = new WP_Query( [
'post_type' => 'post',
'post__in' => $favorite_post_ids,
'orderby' => 'post__in',
'posts_per_page' => $per_page,
'paged' => $favorites_page,
'no_found_rows' => false,
'update_post_meta_cache' => false,
] );
} else {
$favorite_query = null;
}
if ( $favorite_query && $favorite_query->have_posts() ) :
?>
<div class="profile-list">
<?php while ( $favorite_query->have_posts() ) : $favorite_query->the_post();
set_query_var( 'aksrt_profile_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' );
endwhile; ?>
</div>
<?php
aksrt_render_profile_pagination( (int) $favorite_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; ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ( $is_own_profile ) : ?>
<!-- 配置对象:供 user-center.js 使用 -->
<script>window.aksrtUserCenter = {
ajaxUrl: <?php echo wp_json_encode( admin_url( 'admin-ajax.php' ) ); ?>,
publicNonce: <?php echo wp_json_encode( wp_create_nonce( 'aksrt_public_nonce' ) ); ?>,
settingsNonce: <?php echo wp_json_encode( wp_create_nonce( 'aksrt_account_settings' ) ); ?>,
userId: <?php echo (int) $user_id; ?>,
defaultTab: <?php echo wp_json_encode( $default_dashboard_tab ); ?>,
i18n: {
contactUrlPlaceholder: <?php echo wp_json_encode( __( '链接 URL', 'aksrt' ) ); ?>,
deleteLabel: <?php echo wp_json_encode( __( '删除', 'aksrt' ) ); ?>,
saving: <?php echo wp_json_encode( __( '保存中…', 'aksrt' ) ); ?>,
saveBtn: <?php echo wp_json_encode( __( '保存修改', 'aksrt' ) ); ?>,
saved: <?php echo wp_json_encode( __( '已保存', 'aksrt' ) ); ?>,
saveFailed: <?php echo wp_json_encode( __( '保存失败', 'aksrt' ) ); ?>,
networkError: <?php echo wp_json_encode( __( '网络错误,请重试', 'aksrt' ) ); ?>,
confirmUnfavorite: <?php echo wp_json_encode( __( '确定取消收藏这篇文章?', 'aksrt' ) ); ?>,
unfavorited: <?php echo wp_json_encode( __( '已取消收藏', 'aksrt' ) ); ?>,
operationFailed: <?php echo wp_json_encode( __( '操作失败', 'aksrt' ) ); ?>,
markedRead: <?php echo wp_json_encode( __( '已标记为已读', 'aksrt' ) ); ?>,
notificationDeleted: <?php echo wp_json_encode( __( '已删除通知', 'aksrt' ) ); ?>,
allMarkedRead: <?php echo wp_json_encode( __( '已全部标记为已读', 'aksrt' ) ); ?>,
selectNotifications: <?php echo wp_json_encode( __( '请先选择要标记的通知', 'aksrt' ) ); ?>,
selectedMarkedRead: <?php echo wp_json_encode( __( '已标记所选通知为已读', 'aksrt' ) ); ?>,
confirmDeleteNotification: <?php echo wp_json_encode( __( '确定删除这条通知?', 'aksrt' ) ); ?>,
confirmBatchDelete: <?php echo wp_json_encode( __( '确定批量删除所选通知?', 'aksrt' ) ); ?>,
batchDeleted: <?php echo wp_json_encode( __( '已删除所选通知', 'aksrt' ) ); ?>,
sendToCurrentEmail: <?php echo wp_json_encode( __( '发送到当前邮箱', 'aksrt' ) ); ?>,
sendToNewEmail: <?php echo wp_json_encode( __( '发送到新邮箱', 'aksrt' ) ); ?>,
sendCode: <?php echo wp_json_encode( __( '发送验证码', 'aksrt' ) ); ?>,
sending: <?php echo wp_json_encode( __( '发送中...', 'aksrt' ) ); ?>,
codeSent: <?php echo wp_json_encode( __( '验证码已发送', 'aksrt' ) ); ?>,
sendFailed: <?php echo wp_json_encode( __( '发送失败', 'aksrt' ) ); ?>,
fillRequired: <?php echo wp_json_encode( __( '请填写必填项', 'aksrt' ) ); ?>,
passwordMismatch: <?php echo wp_json_encode( __( '两次输入的新密码不一致', 'aksrt' ) ); ?>,
submitting: <?php echo wp_json_encode( __( '提交中...', 'aksrt' ) ); ?>,
confirmUnbind: <?php echo wp_json_encode( __( '确定解除绑定?', 'aksrt' ) ); ?>,
unbinding: <?php echo wp_json_encode( __( '解除中...', 'aksrt' ) ); ?>,
unbindSuccess: <?php echo wp_json_encode( __( '解除成功', 'aksrt' ) ); ?>,
unbindFailed: <?php echo wp_json_encode( __( '解除失败', 'aksrt' ) ); ?>,
unbind: <?php echo wp_json_encode( __( '解除', 'aksrt' ) ); ?>
}
};</script>
<?php if ( $allow_bg_upload ) : ?>
<script>
window.aksrtAuthorPage = Object.assign({}, window.aksrtAuthorPage || {}, {
coverUpload: {
enabled: true,
restUrl: <?php echo wp_json_encode( rest_url( 'aksrt/v1/user/background' ) ); ?>,
restNonce: <?php echo wp_json_encode( wp_create_nonce( 'wp_rest' ) ); ?>,
buttonId: 'aksrt-upload-bg-btn',
inputId: 'aksrt-bg-file-input',
imageId: 'aksrt-cover-img',
fieldName: 'background',
maxSize: 2097152,
fallbackImage: <?php echo wp_json_encode( AKSRT_URI . '/assets/img/person_background.png' ); ?>,
idleText: <?php echo wp_json_encode( __( '编辑', 'aksrt' ) ); ?>,
loadingText: <?php echo wp_json_encode( __( '上传中…', 'aksrt' ) ); ?>,
tooLargeText: <?php echo wp_json_encode( __( '图片大小不能超过 2MB', 'aksrt' ) ); ?>,
successText: <?php echo wp_json_encode( __( '背景图已更新', 'aksrt' ) ); ?>,
errorText: <?php echo wp_json_encode( __( '上传失败', 'aksrt' ) ); ?>,
networkErrorText: <?php echo wp_json_encode( __( '网络错误,请重试', 'aksrt' ) ); ?>
}
});
</script>
<?php endif; ?>
<?php endif; ?>
</main>
<?php
get_footer();
+280
View File
@@ -0,0 +1,280 @@
<?php
/**
* Resource Download Box — v2.0
*
* 单篇资源页的下载区,模仿 zibll pay-box 双栏布局:
* - 左侧:资源缩略图 + 付费类型徽章 + 销量徽章
* - 右侧:价格区(原价划线 + 现价 + VIP 价)+ 下载按钮 + 服务保障
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$meta = get_query_var('aksrt_resource_meta', null);
if (!is_array($meta)) {
$meta = function_exists('aksrt_get_resource_meta') ? aksrt_get_resource_meta(get_the_ID()) : null;
}
if (!is_array($meta)) return;
$post_id = get_the_ID();
$downloads = is_array($meta['downloads']) ? $meta['downloads'] : [];
$pay_type = $meta['pay_type'] ?? 'download';
$price_label = function_exists('aksrt_resource_price_label')
? aksrt_resource_price_label((float) $meta['price'])
: ['text' => '', 'free' => true];
// 下载策略检查
$gate = function_exists('aksrt_can_download') ? aksrt_can_download($post_id) : true;
$gate_blocked = is_wp_error($gate);
$gate_code = $gate_blocked ? $gate->get_error_code() : '';
$gate_data = $gate_blocked ? (array) $gate->get_error_data() : [];
$paid_settings = class_exists('AKSRT_Settings')
? AKSRT_Settings::get_paid_content_settings()
: ['download_credit' => true, 'download_captcha' => false];
$cost = function_exists('aksrt_resource_download_cost') ? aksrt_resource_download_cost($post_id) : (int) ceil((float) $meta['price']);
$credit_active = !empty($paid_settings['download_credit']) && $cost > 0;
$captcha_active = !empty($paid_settings['download_captcha']) || !empty($meta['require_captcha']);
if ($captcha_active && function_exists('aksrt_is_captcha_enabled_for') && !aksrt_is_captcha_enabled_for('download')) {
$captcha_active = false;
}
$balance = (int) ($gate_data['balance'] ?? 0);
$login_url = (string) ($gate_data['login_url'] ?? wp_login_url(get_permalink($post_id)));
$is_need_login = $gate_blocked && $gate_code === 'aksrt_need_login';
$is_low_credit = $gate_blocked && $gate_code === 'aksrt_credits_insufficient';
// 缩略图
$thumbnail = get_the_post_thumbnail_url($post_id, 'medium') ?: '';
$has_thumbnail = $thumbnail !== '';
// 原价 & VIP 价
$original_price = (float) ($meta['original_price'] ?? 0);
$vip1_price = (float) ($meta['vip1_price'] ?? 0);
$vip2_price = (float) ($meta['vip2_price'] ?? 0);
$has_original = $original_price > 0 && $original_price > (float) $meta['price'];
$has_vip = $vip1_price > 0 || $vip2_price > 0;
// 销量
$download_count = (int) ($meta['download_count'] ?? 0);
$sales_count = (int) ($meta['sales_count'] ?? 0);
$display_count = max($download_count, $sales_count);
// 付费类型徽章
$pay_type_badge = function_exists('aksrt_resource_pay_type_badge') ? aksrt_resource_pay_type_badge($pay_type) : '';
// 货币名称
$currency = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_currency_name() : __('积分', 'aksrt');
?>
<section class="widget resource-download-box resource-download-box--v2<?php echo $gate_blocked ? ' is-blocked' : ''; ?>"
data-post-id="<?php echo esc_attr((string) $post_id); ?>"
data-price="<?php echo esc_attr((string) $meta['price']); ?>"
data-credit="<?php echo $credit_active ? '1' : '0'; ?>"
data-captcha="<?php echo $captcha_active ? '1' : '0'; ?>"
aria-label="<?php esc_attr_e('下载区', 'aksrt'); ?>">
<?php if ($display_count > 0) : ?>
<span class="resource-download-box__sales-badge">
<i class="fas fa-fire" aria-hidden="true"></i>
<?php
/* translators: %s: 下载/销量数 */
printf(esc_html__('已售 %s', 'aksrt'), number_format_i18n($display_count));
?>
</span>
<?php endif; ?>
<div class="resource-download-box__body">
<?php if ($has_thumbnail) : ?>
<div class="resource-download-box__thumb-col">
<img class="resource-download-box__thumb" src="<?php echo esc_url($thumbnail); ?>" alt="<?php echo esc_attr(get_the_title()); ?>"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
<?php if ($pay_type_badge) : ?>
<div class="resource-download-box__pay-tag"><?php echo $pay_type_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="resource-download-box__content-col">
<header class="resource-download-box__header">
<h2 class="resource-download-box__title">
<i class="fas fa-cloud-download-alt" aria-hidden="true"></i>
<?php esc_html_e('立即下载', 'aksrt'); ?>
</h2>
<?php if (!empty($downloads)) : ?>
<span class="resource-download-box__count">
<?php echo esc_html(sprintf(_n('%s 个下载源', '%s 个下载源', count($downloads), 'aksrt'), number_format_i18n(count($downloads)))); ?>
</span>
<?php endif; ?>
</header>
<!-- 价格区 -->
<div class="resource-download-box__pricing">
<span class="resource-download-box__price<?php echo $price_label['free'] ? ' is-free' : ''; ?>">
<?php echo esc_html($price_label['text']); ?>
</span>
<?php if ($has_original) : ?>
<span class="resource-download-box__original-price">
<?php echo esc_html(aksrt_resource_price_amount($original_price) . ' ' . $currency); ?>
</span>
<?php endif; ?>
<?php echo $pay_type_badge && !$has_thumbnail ? $pay_type_badge : ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php if ($has_vip) : ?>
<div class="resource-download-box__vip-pricing">
<?php if ($vip1_price > 0) : ?>
<span class="resource-download-box__vip-badge resource-download-box__vip-badge--1">
<i class="fas fa-crown" aria-hidden="true"></i>
<?php
/* translators: %s: VIP1 价格 */
printf(esc_html__('VIP1 %s', 'aksrt'), aksrt_resource_price_amount($vip1_price));
?>
</span>
<?php endif; ?>
<?php if ($vip2_price > 0) : ?>
<span class="resource-download-box__vip-badge resource-download-box__vip-badge--2">
<i class="fas fa-gem" aria-hidden="true"></i>
<?php
/* translators: %s: VIP2 价格 */
printf(esc_html__('VIP2 %s', 'aksrt'), aksrt_resource_price_amount($vip2_price));
?>
</span>
<?php endif; ?>
</div>
<?php endif; ?>
<!-- 下载按钮区 -->
<?php if (empty($downloads)) : ?>
<div class="resource-download-box__notice resource-download-box__notice--empty">
<p><i class="fas fa-info-circle" aria-hidden="true"></i> <?php esc_html_e('该资源暂未提供下载链接。', 'aksrt'); ?></p>
</div>
<?php else : ?>
<ul class="resource-download-box__list">
<?php foreach ($downloads as $idx => $row) :
$label = isset($row['label']) ? (string) $row['label'] : '';
if ($label === '') {
$label = sprintf(__('下载源 %d', 'aksrt'), $idx + 1);
}
$type = isset($row['type']) ? (string) $row['type'] : 'direct';
$url = isset($row['url']) ? (string) $row['url'] : '';
$extract_code = isset($row['extract_code']) ? trim((string) $row['extract_code']) : '';
$has_extract = $extract_code !== '';
$service = function_exists('aksrt_detect_cloud_service') ? aksrt_detect_cloud_service($url) : ['key' => 'other', 'name' => $label, 'color' => '#6b7280'];
if (strpos($label, '下载源') === 0 && $service['key'] !== 'other') {
$label = $service['name'];
}
if ($is_need_login) {
$btn_tag = 'a';
$btn_text = __('登录下载', 'aksrt');
$btn_icon = 'fa-sign-in-alt';
$btn_modifier = ' resource-download-box__btn--login';
} elseif ($is_low_credit) {
$btn_tag = 'button';
$btn_text = sprintf(__('付费下载(%d 积分)', 'aksrt'), $cost);
$btn_icon = 'fa-coins';
$btn_modifier = ' resource-download-box__btn--credit-low';
} elseif ($credit_active && $cost > 0) {
$btn_tag = 'button';
$btn_text = sprintf(__('下载(%d 积分)', 'aksrt'), $cost);
$btn_icon = 'fa-download';
$btn_modifier = ' resource-download-box__btn--paid';
} else {
$btn_tag = 'button';
$btn_text = $label;
$btn_icon = 'fa-download';
$btn_modifier = '';
}
?>
<li class="resource-download-box__item">
<?php if ($btn_tag === 'a') : ?>
<a
class="resource-download-box__btn btn-primary resource-download-box__btn--service-<?php echo esc_attr($service['key']); ?><?php echo esc_attr($btn_modifier); ?>"
href="<?php echo esc_url($login_url); ?>"
data-source-index="<?php echo esc_attr((string) $idx); ?>"
data-source-type="<?php echo esc_attr($type); ?>"
style="--service-color: <?php echo esc_attr($service['color']); ?>"
>
<i class="fas <?php echo esc_attr($btn_icon); ?>" aria-hidden="true"></i>
<span><?php echo esc_html($btn_text); ?></span>
<span class="resource-download-box__service-badge" style="background: <?php echo esc_attr($service['color']); ?>">
<?php echo esc_html($service['name']); ?>
</span>
</a>
<?php else : ?>
<button
type="button"
class="resource-download-box__btn btn-primary resource-download-box__btn--service-<?php echo esc_attr($service['key']); ?><?php echo esc_attr($btn_modifier); ?>"
data-action="<?php echo $is_low_credit ? 'aksrt-need-credits' : 'aksrt-download'; ?>"
data-source-index="<?php echo esc_attr((string) $idx); ?>"
data-source-type="<?php echo esc_attr($type); ?>"
data-has-extract="<?php echo $has_extract ? '1' : '0'; ?>"
data-balance="<?php echo esc_attr((string) $balance); ?>"
data-cost="<?php echo esc_attr((string) $cost); ?>"
style="--service-color: <?php echo esc_attr($service['color']); ?>"
>
<i class="fas <?php echo esc_attr($btn_icon); ?>" aria-hidden="true"></i>
<span><?php echo esc_html($btn_text); ?></span>
<?php if ($is_low_credit) : ?>
<small class="resource-download-box__btn-sub">
<?php printf(esc_html__('当前余额 %d', 'aksrt'), $balance); ?>
</small>
<?php else : ?>
<span class="resource-download-box__service-badge" style="background: <?php echo esc_attr($service['color']); ?>">
<?php echo esc_html($service['name']); ?>
</span>
<?php endif; ?>
</button>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<!-- 服务保障 -->
<div class="resource-download-box__guarantees">
<span class="resource-download-box__guarantee">
<i class="fas fa-shield-alt" aria-hidden="true"></i>
<?php esc_html_e('安全检测', 'aksrt'); ?>
</span>
<span class="resource-download-box__guarantee">
<i class="fas fa-sync-alt" aria-hidden="true"></i>
<?php esc_html_e('持续更新', 'aksrt'); ?>
</span>
<span class="resource-download-box__guarantee">
<i class="fas fa-headset" aria-hidden="true"></i>
<?php esc_html_e('售后支持', 'aksrt'); ?>
</span>
</div>
</div>
</div>
<?php if ($is_need_login) : ?>
<p class="resource-download-box__hint resource-download-box__hint--login">
<i class="fas fa-info-circle" aria-hidden="true"></i>
<?php esc_html_e('登录后可下载此资源。', 'aksrt'); ?>
</p>
<?php elseif ($is_low_credit) : ?>
<p class="resource-download-box__hint resource-download-box__hint--credit">
<i class="fas fa-info-circle" aria-hidden="true"></i>
<?php printf(esc_html__('当前余额 %1$d 积分,需要 %2$d 积分。可通过签到、阅读、邀请好友等方式获取积分。', 'aksrt'), $balance, $cost); ?>
</p>
<?php endif; ?>
<?php if ($captcha_active && function_exists('aksrt_render_captcha')) : ?>
<div class="resource-download-box__captcha resource-download-box__captcha--hidden" data-aksrt-download-captcha>
<?php aksrt_render_captcha('download'); ?>
</div>
<?php endif; ?>
<footer class="resource-download-box__footer">
<p class="resource-download-box__notice-text">
<i class="fas fa-info-circle" aria-hidden="true"></i>
<?php esc_html_e('请确认资源安全后再使用,本站仅作信息聚合与分享,不对资源内容负责。', 'aksrt'); ?>
</p>
</footer>
</section>
+122
View File
@@ -0,0 +1,122 @@
<?php
/**
* Resource Meta Box
*
* 单篇资源页用:版本/大小/授权/兼容/演示 等元信息表。
*
* 由 single-aksrt_resource.php 调用:
* set_query_var('aksrt_resource_meta', $meta);
* get_template_part('templates/parts/resource-meta-box');
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$meta = get_query_var('aksrt_resource_meta', null);
if (!is_array($meta)) {
$meta = function_exists('aksrt_get_resource_meta') ? aksrt_get_resource_meta(get_the_ID()) : null;
}
if (!is_array($meta)) return;
// 字段定义及显示顺序
$rows = [
'version' => __('版本', 'aksrt'),
'size' => __('大小', 'aksrt'),
'compat' => __('兼容性', 'aksrt'),
'license' => __('授权', 'aksrt'),
];
// 至少要有一个非空字段才渲染
$has_any = false;
foreach ($rows as $k => $_label) {
if (!empty($meta[$k])) { $has_any = true; break; }
}
$has_demo = !empty($meta['demo_url']);
$has_downloads = ($meta['download_count'] ?? 0) > 0;
if (!$has_any && !$has_demo && !$has_downloads) return;
?>
<aside class="resource-meta-box" aria-label="<?php esc_attr_e('资源信息', 'aksrt'); ?>">
<header class="resource-meta-box__header">
<h2 class="resource-meta-box__title"><?php esc_html_e('资源信息', 'aksrt'); ?></h2>
</header>
<dl class="resource-meta-box__list">
<?php if (!empty($meta['version'])) : ?>
<div class="resource-meta-box__row">
<dt class="resource-meta-box__key">
<i class="<?php echo esc_attr(function_exists('aksrt_resource_meta_icon') ? aksrt_resource_meta_icon('version') : 'fas fa-code-branch'); ?>" aria-hidden="true"></i>
<?php echo esc_html($rows['version']); ?>
</dt>
<dd class="resource-meta-box__val"><?php echo esc_html((string) $meta['version']); ?></dd>
</div>
<?php endif; ?>
<?php if (!empty($meta['size'])) : ?>
<div class="resource-meta-box__row">
<dt class="resource-meta-box__key">
<i class="<?php echo esc_attr(function_exists('aksrt_resource_meta_icon') ? aksrt_resource_meta_icon('size') : 'fas fa-hdd'); ?>" aria-hidden="true"></i>
<?php echo esc_html($rows['size']); ?>
</dt>
<dd class="resource-meta-box__val"><?php echo esc_html((string) $meta['size']); ?></dd>
</div>
<?php endif; ?>
<?php if (!empty($meta['compat'])) : ?>
<div class="resource-meta-box__row">
<dt class="resource-meta-box__key">
<i class="<?php echo esc_attr(function_exists('aksrt_resource_meta_icon') ? aksrt_resource_meta_icon('compat') : 'fas fa-laptop'); ?>" aria-hidden="true"></i>
<?php echo esc_html($rows['compat']); ?>
</dt>
<dd class="resource-meta-box__val"><?php echo esc_html((string) $meta['compat']); ?></dd>
</div>
<?php endif; ?>
<?php if ($has_demo) : ?>
<div class="resource-meta-box__row">
<dt class="resource-meta-box__key">
<i class="<?php echo esc_attr(function_exists('aksrt_resource_meta_icon') ? aksrt_resource_meta_icon('demo_url') : 'fas fa-external-link-alt'); ?>" aria-hidden="true"></i>
<?php esc_html_e('演示地址', 'aksrt'); ?>
</dt>
<dd class="resource-meta-box__val">
<a class="resource-meta-box__demo-link" href="<?php echo esc_url($meta['demo_url']); ?>" target="_blank" rel="noopener noreferrer">
<?php esc_html_e('打开演示', 'aksrt'); ?>
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
</a>
</dd>
</div>
<?php endif; ?>
<?php if (!empty($meta['license'])) : ?>
<div class="resource-meta-box__row">
<dt class="resource-meta-box__key">
<i class="<?php echo esc_attr(function_exists('aksrt_resource_meta_icon') ? aksrt_resource_meta_icon('license') : 'fas fa-shield-alt'); ?>" aria-hidden="true"></i>
<?php echo esc_html($rows['license']); ?>
</dt>
<dd class="resource-meta-box__val"><?php echo esc_html((string) $meta['license']); ?></dd>
</div>
<?php endif; ?>
<?php if ($has_downloads) : ?>
<div class="resource-meta-box__row">
<dt class="resource-meta-box__key">
<i class="<?php echo esc_attr(function_exists('aksrt_resource_meta_icon') ? aksrt_resource_meta_icon('download') : 'fas fa-download'); ?>" aria-hidden="true"></i>
<?php esc_html_e('下载次数', 'aksrt'); ?>
</dt>
<dd class="resource-meta-box__val"><?php echo esc_html(number_format_i18n((int) $meta['download_count'])); ?></dd>
</div>
<?php endif; ?>
<?php if (($meta['view_count'] ?? 0) > 0) : ?>
<div class="resource-meta-box__row">
<dt class="resource-meta-box__key">
<i class="<?php echo esc_attr(function_exists('aksrt_resource_meta_icon') ? aksrt_resource_meta_icon('views') : 'fas fa-eye'); ?>" aria-hidden="true"></i>
<?php esc_html_e('浏览次数', 'aksrt'); ?>
</dt>
<dd class="resource-meta-box__val"><?php echo esc_html(number_format_i18n((int) $meta['view_count'])); ?></dd>
</div>
<?php endif; ?>
</dl>
</aside>
+64
View File
@@ -0,0 +1,64 @@
<?php
/**
* Resource Related
*
* 单篇资源页底部的「相关资源」区块。
* 取同分类下其它资源(post + _aksrt_is_resource = 1,兼容旧 aksrt_resource CPT),按下载量倒序。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$current_id = get_the_ID();
$cat_ids = wp_get_post_categories($current_id, ['fields' => 'ids']);
if (empty($cat_ids)) return;
$post_types = function_exists('aksrt_resource_post_types') ? aksrt_resource_post_types() : ['post'];
$args = [
'post_type' => $post_types,
'posts_per_page' => 6,
'post__not_in' => [$current_id],
'category__in' => array_map('intval', $cat_ids),
'meta_key' => '_aksrt_res_download_count',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'no_found_rows' => true,
];
if (!post_type_exists('aksrt_resource')) {
$args['meta_query'] = [
'relation' => 'AND',
[
'key' => '_aksrt_is_resource',
'value' => '1',
'compare' => '=',
],
];
}
$query = new WP_Query($args);
if (!$query->have_posts()) {
wp_reset_postdata();
return;
}
?>
<section class="resource-related" aria-label="<?php esc_attr_e('相关资源', 'aksrt'); ?>">
<header class="resource-related__header">
<h2 class="resource-related__title"><?php esc_html_e('相关资源', 'aksrt'); ?></h2>
</header>
<div class="post-grid post-grid--grid-cms resource-related__grid">
<?php
set_query_var('aksrt_post_card_layout', 'grid-cms');
while ($query->have_posts()) {
$query->the_post();
get_template_part('templates/parts/content', 'post-card');
}
set_query_var('aksrt_post_card_layout', '');
wp_reset_postdata();
?>
</div>
</section>
+106
View File
@@ -0,0 +1,106 @@
<?php
/**
* Shared author info bar for single post/resource content.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$extra_class = (string) get_query_var('aksrt_author_bar_class', '');
$show_actions = (bool) get_query_var('aksrt_author_bar_actions', false);
$show_bio = (bool) get_query_var('aksrt_author_bar_bio', false);
$meta_items = get_query_var('aksrt_author_bar_meta_items', []);
$meta_items = is_array($meta_items) ? array_values(array_filter($meta_items, static function ($meta_item) {
if (is_array($meta_item)) {
return !empty($meta_item['text']);
}
return is_string($meta_item) && $meta_item !== '';
})) : [];
$updated_text = sprintf(
esc_html__('%s前更新', 'aksrt'),
esc_html(human_time_diff(get_the_modified_time('U'), current_time('timestamp')))
);
$author_bio = trim((string) get_the_author_meta('description'));
$author_id = (int) get_the_author_meta('ID');
$classes = trim('article-avatar ' . $extra_class . ($show_actions ? ' article-avatar--with-actions' : ''));
?>
<div class="<?php echo esc_attr($classes); ?>">
<div class="author-box flex ac">
<div class="article-avatar__image">
<?php echo get_avatar($author_id, 52, '', esc_attr(get_the_author()), ['class' => 'article-avatar']); ?>
</div>
<div class="article-avatar__body">
<a href="<?php echo esc_url(get_author_posts_url($author_id)); ?>" class="article-avatar__name"><?php the_author(); ?></a>
<div class="comt-meta muted-2-color">
<item class="post-time"><i class="far fa-clock" aria-hidden="true"></i> <?php echo $updated_text; ?></item>
<?php foreach ($meta_items as $meta_item) : ?>
<?php
$meta_text = is_array($meta_item) ? (string) ($meta_item['text'] ?? '') : (string) $meta_item;
$meta_icon = is_array($meta_item) ? (string) ($meta_item['icon'] ?? '') : '';
if ($meta_text === '') {
continue;
}
?>
<item class="meta-view"><?php if ($meta_icon !== '') : ?><i class="<?php echo esc_attr($meta_icon); ?>" aria-hidden="true"></i> <?php endif; ?><?php echo esc_html($meta_text); ?></item>
<?php endforeach; ?>
</div>
<?php if ($show_bio && $author_bio !== '') : ?>
<p class="article-avatar__bio"><?php echo esc_html($author_bio); ?></p>
<?php endif; ?>
</div>
</div>
<?php if ($show_actions) : ?>
<i class="line-form-line" aria-hidden="true"></i>
<div class="single-metabox abs-right" aria-label="<?php esc_attr_e('作者操作', 'aksrt'); ?>">
<?php if (is_user_logged_in() && $author_id !== get_current_user_id()) : ?>
<button type="button" class="author-action-btn author-action-btn--follow" data-aksrt-follow="<?php echo $author_id; ?>">
<i class="far fa-heart" aria-hidden="true"></i>
<span><?php esc_html_e('关注', 'aksrt'); ?></span>
</button>
<a href="<?php echo esc_url(add_query_arg('msg_type', 'conversation', function_exists('aksrt_get_messages_url') ? aksrt_get_messages_url() : home_url('/messages/'))); ?>" class="author-action-btn author-action-btn--ghost">
<i class="far fa-comment-dots" aria-hidden="true"></i>
<span><?php esc_html_e('私信', 'aksrt'); ?></span>
</a>
<?php elseif (!is_user_logged_in()) : ?>
<a href="<?php echo esc_url(function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink())); ?>" class="author-action-btn author-action-btn--follow">
<i class="far fa-heart" aria-hidden="true"></i>
<span><?php esc_html_e('登录后关注', 'aksrt'); ?></span>
</a>
<?php endif; ?>
<div class="post-drop-meta">
<button type="button" class="post-drop-meta__toggle author-action-btn author-action-btn--ghost" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
<span><?php esc_html_e('更多', 'aksrt'); ?></span>
</button>
<div class="post-drop-meta__menu" aria-hidden="true">
<?php if (current_user_can('edit_post', get_the_ID())) : ?>
<a href="<?php echo esc_url(get_edit_post_link(get_the_ID())); ?>" class="post-drop-meta__item">
<i class="far fa-edit" aria-hidden="true"></i>
<span><?php esc_html_e('编辑文章', 'aksrt'); ?></span>
</a>
<?php endif; ?>
<button type="button" class="post-drop-meta__item" data-share-copy data-share-url="<?php echo esc_url(get_permalink()); ?>">
<i class="fas fa-link" aria-hidden="true"></i>
<span><?php esc_html_e('复制链接', 'aksrt'); ?></span>
</button>
<?php if (is_user_logged_in()) : ?>
<button type="button" class="post-drop-meta__item" data-aksrt-report='{"target_type":"post","target_id":<?php echo (int) get_the_ID(); ?>}'>
<i class="far fa-flag" aria-hidden="true"></i>
<span><?php esc_html_e('举报', 'aksrt'); ?></span>
</button>
<?php else : ?>
<a href="<?php echo esc_url(function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink())); ?>" class="post-drop-meta__item">
<i class="far fa-flag" aria-hidden="true"></i>
<span><?php esc_html_e('登录后举报', 'aksrt'); ?></span>
</a>
<?php endif; ?>
</div>
</div>
</div>
<?php endif; ?>
</div>
+66
View File
@@ -0,0 +1,66 @@
<?php
/**
* Special Page Cover
*
* Renders the cover section for a special/topic page with
* background image, title, summary, and metadata.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$post_id = get_the_ID();
$cover_img = get_the_post_thumbnail_url($post_id, 'large');
$default_cover = isset($default_cover) ? $default_cover : '';
$show_post_count = isset($show_post_count) ? $show_post_count : true;
if (empty($cover_img) && !empty($default_cover)) {
$cover_img = $default_cover;
}
$title = get_the_title();
$excerpt = get_the_excerpt();
$summary_len = isset($summary_len) ? (int) $summary_len : 200;
if ($excerpt) {
$excerpt = wp_trim_words($excerpt, $summary_len, '...');
}
// Per-page meta
$cover_color = get_post_meta($post_id, '_aksrt_special_cover_color', true);
$cover_style = '';
if ($cover_color) {
$cover_style = 'background-color:' . sanitize_hex_color($cover_color) . ';';
}
?>
<header class="aksrt-special-cover" <?php echo $cover_style ? 'style="' . esc_attr($cover_style) . '"' : ''; ?>>
<?php if ($cover_img) : ?>
<div class="aksrt-special-cover__bg">
<img src="<?php echo esc_url($cover_img); ?>" alt=""<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('cover') : ' loading="lazy"'; ?> class="aksrt-special-cover__img">
<div class="aksrt-special-cover__overlay"></div>
</div>
<?php endif; ?>
<div class="aksrt-special-cover__content">
<h1 class="aksrt-special-cover__title"><?php echo esc_html($title); ?></h1>
<?php if ($excerpt) : ?>
<p class="aksrt-special-cover__summary"><?php echo esc_html($excerpt); ?></p>
<?php endif; ?>
<div class="aksrt-special-cover__meta">
<span class="aksrt-special-cover__date">
<i class="fas fa-calendar-alt"></i>
<?php echo esc_html(get_the_date()); ?>
</span>
<?php
if ($show_post_count) :
$children = get_pages(['child_of' => $post_id, 'parent' => $post_id, 'post_status' => 'publish', 'fields' => 'ids']);
if (!empty($children)) : ?>
<span class="aksrt-special-cover__count">
<i class="fas fa-file-alt"></i>
<?php printf(esc_html__('%d 篇文章', 'aksrt'), count($children)); ?>
</span>
<?php endif; endif; ?>
</div>
</div>
</header>
+46
View File
@@ -0,0 +1,46 @@
<?php
/**
* Special Page Grid
*
* Renders the content grid for a special/topic page.
* Supports grid, list, and masonry layouts.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$posts = isset($posts) ? $posts : [];
$layout = isset($layout) ? $layout : 'grid';
$columns = isset($columns) ? (int) $columns : 2;
$show_dates = isset($show_dates) ? $show_dates : true;
if (empty($posts)) return;
?>
<div class="aksrt-special-grid aksrt-special-grid--<?php echo esc_attr($layout); ?>" style="--aksrt-cols:<?php echo esc_attr((string) $columns); ?>">
<?php foreach ($posts as $post) :
setup_postdata($post);
$img = get_the_post_thumbnail_url($post->ID, 'medium');
?>
<article class="aksrt-special-grid__item">
<a href="<?php echo esc_url(get_permalink($post->ID)); ?>" class="aksrt-special-grid__link">
<?php if ($img) : ?>
<div class="aksrt-special-grid__thumb">
<img src="<?php echo esc_url($img); ?>" alt=""<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
</div>
<?php endif; ?>
<div class="aksrt-special-grid__body">
<h3 class="aksrt-special-grid__title"><?php echo esc_html($post->post_title); ?></h3>
<?php if ($post->post_excerpt) : ?>
<p class="aksrt-special-grid__desc"><?php echo esc_html(wp_trim_words($post->post_excerpt, 20)); ?></p>
<?php endif; ?>
<?php if ($show_dates) : ?>
<time class="aksrt-special-grid__date" datetime="<?php echo esc_attr(get_the_date('c', $post->ID)); ?>">
<?php echo esc_html(get_the_date('', $post->ID)); ?>
</time>
<?php endif; ?>
</div>
</a>
</article>
<?php endforeach; wp_reset_postdata(); ?>
</div>
+1
View File
@@ -0,0 +1 @@
<!-- 本人中心 partial 模板目录 -->
@@ -0,0 +1,81 @@
<?php
/**
* 用户中心 — 我的评论
*
* @package AKSRT
* @since 5.0.0
*/
defined('ABSPATH') || exit;
$user_id = get_current_user_id();
$per_page = 15;
$paged = max(1, get_query_var('paged'));
$comments = get_comments([
'user_id' => $user_id,
'status' => 'approve',
'post_status' => 'publish',
'number' => $per_page,
'offset' => ($paged - 1) * $per_page,
'orderby' => 'comment_date_gmt',
'order' => 'DESC',
'count' => true,
]);
$total_comments = $comments;
$comments = get_comments([
'user_id' => $user_id,
'status' => 'approve',
'post_status' => 'publish',
'number' => $per_page,
'offset' => ($paged - 1) * $per_page,
'orderby' => 'comment_date_gmt',
'order' => 'DESC',
]);
$max_pages = ceil($total_comments / $per_page);
?>
<div class="uc-comments" id="uc-comments">
<?php if (!empty($comments)) : ?>
<div class="uc-comments__list">
<?php foreach ($comments as $comment) :
$post = get_post($comment->comment_post_ID);
if (!$post) continue;
?>
<div class="uc-comments__item">
<div class="uc-comments__header">
<a href="<?php echo esc_url(get_permalink($post->ID)); ?>" class="uc-comments__post-title">
<?php echo esc_html($post->post_title); ?>
</a>
<span class="uc-comments__date"><?php echo esc_html(get_comment_date('Y-m-d', $comment)); ?></span>
</div>
<div class="uc-comments__body">
<?php echo function_exists('aksrt_render_comment_content') ? aksrt_render_comment_content((string) get_comment_text($comment)) : wp_kses_post((string) $comment->comment_content); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if ($max_pages > 1) : ?>
<div class="aksrt-pagination">
<?php
echo paginate_links([
'base' => str_replace(999999999, '%#%', esc_url(get_pagenum_link(999999999))),
'format' => '?paged=%#%',
'current' => $paged,
'total' => $max_pages,
'prev_text' => '<i class="fas fa-chevron-left"></i>',
'next_text' => '<i class="fas fa-chevron-right"></i>',
]);
?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="empty-state empty-state--sm">
<div class="empty-state__icon"><i class="far fa-comment-dots"></i></div>
<p class="empty-state__title"><?php esc_html_e('暂无评论', 'aksrt'); ?></p>
<p class="empty-state__desc"><?php esc_html_e('去文章下方发表你的看法吧~', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
@@ -0,0 +1,59 @@
<?php
/**
* 用户中心 — 我的积分
*
* @package AKSRT
* @since 5.0.0
*/
defined('ABSPATH') || exit;
$user_id = get_current_user_id();
$credits_enabled = class_exists('AKSRT_Settings') && AKSRT_Settings::get_credits_settings()['enabled'] ?? false;
if (!$credits_enabled) {
return;
}
$balance = function_exists('aksrt_get_user_credits') ? aksrt_get_user_credits($user_id) : 0;
$currency_name = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_credits_settings()['currency_name'] ?? __('积分', 'aksrt') : __('积分', 'aksrt');
// 获取积分记录
$records = function_exists('aksrt_get_credit_logs') ? aksrt_get_credit_logs($user_id, 20) : [];
?>
<div class="uc-credits" id="uc-credits">
<!-- 积分概览 -->
<div class="uc-credits__overview">
<div class="uc-credits__balance">
<span class="uc-credits__balance-num"><?php echo esc_html(number_format($balance, 2)); ?></span>
<span class="uc-credits__balance-label"><?php echo esc_html($currency_name); ?></span>
</div>
</div>
<!-- 积分记录 -->
<?php if (!empty($records)) : ?>
<div class="uc-credits__records">
<h4 class="uc-credits__section-title"><?php esc_html_e('积分记录', 'aksrt'); ?></h4>
<div class="uc-credits__list">
<?php foreach ($records as $record) : ?>
<div class="uc-credits__record">
<div class="uc-credits__record-info">
<span class="uc-credits__record-desc"><?php echo esc_html($record['description'] ?? ''); ?></span>
<span class="uc-credits__record-date"><?php echo esc_html($record['date'] ?? ''); ?></span>
</div>
<span class="uc-credits__record-amount <?php echo ($record['amount'] ?? 0) > 0 ? 'uc-credits__record-amount--plus' : 'uc-credits__record-amount--minus'; ?>">
<?php echo ($record['amount'] ?? 0) > 0 ? '+' : ''; ?><?php echo esc_html(number_format($record['amount'] ?? 0, 2)); ?>
</span>
</div>
<?php endforeach; ?>
</div>
</div>
<?php else : ?>
<div class="empty-state empty-state--sm">
<div class="empty-state__icon"><i class="fas fa-coins"></i></div>
<p class="empty-state__title"><?php esc_html_e('暂无积分记录', 'aksrt'); ?></p>
<p class="empty-state__desc"><?php esc_html_e('每天签到、发布内容都可以获得积分哦~', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
@@ -0,0 +1,98 @@
<?php
/**
* 用户中心 — 仪表盘面板
*
* 显示当前登录用户的基本信息概览、积分、VIP状态、快捷操作。
*
* @package AKSRT
* @since 5.0.0
*/
defined('ABSPATH') || exit;
$user_id = get_current_user_id();
$user = wp_get_current_user();
$dashboard = function_exists('aksrt_get_user_dashboard_view')
? aksrt_get_user_dashboard_view($user_id, ['viewer_id' => $user_id])
: [];
$credits_enabled = !empty($dashboard['credits_enabled']);
$credit_balance = $dashboard['credit_balance'] ?? 0;
$currency_name = $dashboard['currency_name'] ?? __('积分', 'aksrt');
$is_vip = function_exists('aksrt_is_vip') && aksrt_is_vip($user_id);
$post_count = (int) ($dashboard['post_count'] ?? 0);
$comment_count = (int) ($dashboard['comment_count'] ?? 0);
$favorite_count = (int) ($dashboard['favorite_count'] ?? 0);
$order_count = (int) ($dashboard['order_count'] ?? 0);
?>
<div class="uc-dashboard">
<!-- 用户信息卡片 -->
<div class="uc-dashboard__profile">
<div class="uc-dashboard__avatar">
<?php echo get_avatar($user_id, 80); ?>
<?php if ($is_vip) : ?>
<span class="aksrt-vip-badge aksrt-vip-badge--sm">VIP</span>
<?php endif; ?>
</div>
<div class="uc-dashboard__info">
<h3 class="uc-dashboard__name"><?php echo esc_html($user->display_name); ?></h3>
<p class="uc-dashboard__desc">
<?php
$desc = $user->description ?: __('这个人很懒,什么都没写~', 'aksrt');
echo esc_html(wp_trim_words($desc, 30));
?>
</p>
<div class="uc-dashboard__meta">
<span><i class="fas fa-calendar-alt"></i> <?php echo esc_html(__('注册于', 'aksrt') . ' ' . date_i18n(get_option('date_format'), strtotime($user->user_registered))); ?></span>
</div>
</div>
<a href="<?php echo esc_url(get_edit_profile_url($user_id)); ?>" class="btn btn-outline-primary btn-sm">
<i class="fas fa-edit"></i> <?php esc_html_e('编辑资料', 'aksrt'); ?>
</a>
</div>
<!-- 数据统计 -->
<div class="uc-dashboard__stats">
<a href="<?php echo esc_url(get_author_posts_url($user_id)); ?>" class="uc-dashboard__stat">
<span class="uc-dashboard__stat-num"><?php echo $post_count; ?></span>
<span class="uc-dashboard__stat-label"><?php esc_html_e('文章', 'aksrt'); ?></span>
</a>
<a href="#uc-comments" class="uc-dashboard__stat">
<span class="uc-dashboard__stat-num"><?php echo $comment_count; ?></span>
<span class="uc-dashboard__stat-label"><?php esc_html_e('评论', 'aksrt'); ?></span>
</a>
<a href="#uc-favorites" class="uc-dashboard__stat">
<span class="uc-dashboard__stat-num"><?php echo $favorite_count; ?></span>
<span class="uc-dashboard__stat-label"><?php esc_html_e('收藏', 'aksrt'); ?></span>
</a>
<?php if ($credits_enabled) : ?>
<a href="#uc-credits" class="uc-dashboard__stat">
<span class="uc-dashboard__stat-num"><?php echo esc_html(number_format($credit_balance, 2)); ?></span>
<span class="uc-dashboard__stat-label"><?php echo esc_html($currency_name); ?></span>
</a>
<?php endif; ?>
</div>
<!-- 快捷操作 -->
<div class="uc-dashboard__actions">
<a href="<?php echo esc_url(get_edit_profile_url($user_id)); ?>" class="uc-dashboard__action">
<i class="fas fa-user-edit"></i>
<span><?php esc_html_e('编辑资料', 'aksrt'); ?></span>
</a>
<a href="<?php echo esc_url(get_edit_profile_url($user_id) . '#password'); ?>" class="uc-dashboard__action">
<i class="fas fa-key"></i>
<span><?php esc_html_e('修改密码', 'aksrt'); ?></span>
</a>
<?php if ($order_count > 0) : ?>
<a href="<?php echo esc_url(home_url('/orders/')); ?>" class="uc-dashboard__action">
<i class="fas fa-shopping-bag"></i>
<span><?php esc_html_e('我的订单', 'aksrt'); ?></span>
</a>
<?php endif; ?>
<a href="<?php echo esc_url(wp_logout_url(home_url())); ?>" class="uc-dashboard__action uc-dashboard__action--danger">
<i class="fas fa-sign-out-alt"></i>
<span><?php esc_html_e('退出登录', 'aksrt'); ?></span>
</a>
</div>
</div>
@@ -0,0 +1,75 @@
<?php
/**
* 用户中心 — 我的收藏
*
* @package AKSRT
* @since 5.0.0
*/
defined('ABSPATH') || exit;
$user_id = get_current_user_id();
$per_page = 10;
$paged = max(1, get_query_var('paged'));
$favorite_ids = get_user_meta($user_id, 'aksrt_favorite_posts', true);
$favorite_ids = is_array($favorite_ids) ? $favorite_ids : [];
$total = count($favorite_ids);
// 分页切片
$offset = ($paged - 1) * $per_page;
$page_ids = array_slice($favorite_ids, $offset, $per_page);
$max_pages = ceil($total / $per_page);
?>
<div class="uc-favorites" id="uc-favorites">
<?php if (!empty($page_ids)) : ?>
<div class="uc-favorites__list">
<?php foreach ($page_ids as $fid) :
$post = get_post((int) $fid);
if (!$post || $post->post_status !== 'publish') continue;
?>
<div class="uc-favorites__item">
<div class="uc-favorites__content">
<h4 class="uc-favorites__title">
<a href="<?php echo esc_url(get_permalink($post->ID)); ?>"><?php echo esc_html($post->post_title); ?></a>
</h4>
<div class="uc-favorites__meta">
<span><i class="far fa-clock"></i> <?php echo esc_html(get_the_date('Y-m-d', $post->ID)); ?></span>
<span><i class="far fa-eye"></i> <?php echo esc_html(get_post_meta($post->ID, 'aksrt_post_views', true) ?: 0); ?></span>
</div>
<button class="uc-favorites__remove" data-action="remove-favorite" data-post-id="<?php echo esc_attr($post->ID); ?>">
<i class="fas fa-heart-broken"></i> <?php esc_html_e('取消收藏', 'aksrt'); ?>
</button>
</div>
<?php if (has_post_thumbnail($post->ID)) : ?>
<a href="<?php echo esc_url(get_permalink($post->ID)); ?>" class="uc-favorites__thumb">
<?php echo get_the_post_thumbnail($post->ID, 'thumbnail'); ?>
</a>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php if ($max_pages > 1) : ?>
<div class="aksrt-pagination">
<?php
echo paginate_links([
'base' => str_replace(999999999, '%#%', esc_url(get_pagenum_link(999999999))),
'format' => '?paged=%#%',
'current' => $paged,
'total' => $max_pages,
'prev_text' => '<i class="fas fa-chevron-left"></i>',
'next_text' => '<i class="fas fa-chevron-right"></i>',
]);
?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="empty-state empty-state--sm">
<div class="empty-state__icon"><i class="far fa-heart"></i></div>
<p class="empty-state__title"><?php esc_html_e('暂无收藏', 'aksrt'); ?></p>
<p class="empty-state__desc"><?php esc_html_e('看到喜欢的文章,点击收藏按钮即可保存~', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
+71
View File
@@ -0,0 +1,71 @@
<?php
/**
* 用户中心 — 我的文章
*
* @package AKSRT
* @since 5.0.0
*/
defined('ABSPATH') || exit;
$user_id = get_current_user_id();
$per_page = 10;
$paged = max(1, get_query_var('paged'));
$query = new WP_Query([
'author' => $user_id,
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => $per_page,
'paged' => $paged,
'no_found_rows' => false,
]);
?>
<div class="uc-posts" id="uc-posts">
<?php if ($query->have_posts()) : ?>
<div class="uc-posts__list">
<?php while ($query->have_posts()) : $query->the_post(); ?>
<div class="uc-posts__item">
<div class="uc-posts__content">
<h4 class="uc-posts__title">
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</h4>
<div class="uc-posts__meta">
<span><i class="far fa-clock"></i> <?php echo esc_html(get_the_date()); ?></span>
<span><i class="far fa-eye"></i> <?php echo esc_html(get_post_meta(get_the_ID(), 'aksrt_post_views', true) ?: 0); ?></span>
<span><i class="far fa-comment"></i> <?php echo esc_html(get_comments_number()); ?></span>
</div>
</div>
<?php if (has_post_thumbnail()) : ?>
<a href="<?php the_permalink(); ?>" class="uc-posts__thumb">
<?php the_post_thumbnail('thumbnail'); ?>
</a>
<?php endif; ?>
</div>
<?php endwhile; ?>
</div>
<?php if ($query->max_num_pages > 1) : ?>
<div class="aksrt-pagination">
<?php
echo paginate_links([
'base' => str_replace(999999999, '%#%', esc_url(get_pagenum_link(999999999))),
'format' => '?paged=%#%',
'current' => $paged,
'total' => $query->max_num_pages,
'prev_text' => '<i class="fas fa-chevron-left"></i>',
'next_text' => '<i class="fas fa-chevron-right"></i>',
]);
?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="empty-state empty-state--sm">
<div class="empty-state__icon"><i class="far fa-file-alt"></i></div>
<p class="empty-state__title"><?php esc_html_e('暂无文章', 'aksrt'); ?></p>
<p class="empty-state__desc"><?php esc_html_e('你还没有发布过文章,去写一篇吧~', 'aksrt'); ?></p>
</div>
<?php endif; ?>
<?php wp_reset_postdata(); ?>
</div>
+1
View File
@@ -0,0 +1 @@
<!-- 公开用户主页 partial 模板目录 -->
+76
View File
@@ -0,0 +1,76 @@
<?php
/**
* 公开用户主页 — 评论列表
*
* @package AKSRT
* @since 5.0.0
*/
defined('ABSPATH') || exit;
$user_id = $args['user_id'] ?? 0;
$per_page = $args['per_page'] ?? 15;
$paged = max(1, get_query_var('paged'));
$total = get_comments([
'user_id' => $user_id,
'status' => 'approve',
'post_status' => 'publish',
'count' => true,
]);
$comments = get_comments([
'user_id' => $user_id,
'status' => 'approve',
'post_status' => 'publish',
'number' => $per_page,
'offset' => ($paged - 1) * $per_page,
'orderby' => 'comment_date_gmt',
'order' => 'DESC',
]);
$max_pages = ceil($total / $per_page);
?>
<div class="uh-comments">
<?php if (!empty($comments)) : ?>
<div class="uh-comments__list">
<?php foreach ($comments as $comment) :
$post = get_post($comment->comment_post_ID);
if (!$post) continue;
?>
<div class="uh-comments__item">
<div class="uh-comments__header">
<a href="<?php echo esc_url(get_permalink($post->ID)); ?>" class="uh-comments__post-title">
<?php echo esc_html($post->post_title); ?>
</a>
<span class="uh-comments__date"><?php echo esc_html(get_comment_date('Y-m-d', $comment)); ?></span>
</div>
<div class="uh-comments__body">
<?php echo function_exists('aksrt_render_comment_content') ? aksrt_render_comment_content((string) get_comment_text($comment)) : wp_kses_post((string) $comment->comment_content); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if ($max_pages > 1) : ?>
<div class="aksrt-pagination">
<?php
echo paginate_links([
'base' => str_replace(999999999, '%#%', esc_url(get_pagenum_link(999999999))),
'format' => '?tab=comments&paged=%#%',
'current' => $paged,
'total' => $max_pages,
'prev_text' => '<i class="fas fa-chevron-left"></i>',
'next_text' => '<i class="fas fa-chevron-right"></i>',
]);
?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="empty-state empty-state--sm">
<div class="empty-state__icon"><i class="far fa-comment-dots"></i></div>
<p class="empty-state__title"><?php esc_html_e('暂无评论', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
+66
View File
@@ -0,0 +1,66 @@
<?php
/**
* 公开用户主页 — Hero 封面区
*
* @package AKSRT
* @since 5.0.0
*/
defined('ABSPATH') || exit;
$user = $args['user'] ?? null;
$user_id = $user ? (int) $user->ID : 0;
$dashboard = $args['dashboard'] ?? [];
$settings = $args['settings'] ?? [];
$show_cover = $settings['cover'] ?? true;
$show_follow = $settings['follow'] ?? true;
$is_own = $dashboard['is_own_profile'] ?? false;
$viewer_id = $dashboard['viewer_id'] ?? 0;
if (!$user) return;
$cover_url = get_user_meta($user_id, 'aksrt_user_cover', true) ?: '';
$avatar = get_avatar_url($user_id, ['size' => 96]);
$desc = $user->description ?: '';
$is_vip = function_exists('aksrt_is_vip') && aksrt_is_vip($user_id);
?>
<div class="uh-hero <?php echo $cover_url ? 'uh-hero--has-cover' : ''; ?>" <?php echo $cover_url ? 'style="background-image:url(' . esc_url($cover_url) . ')"' : ''; ?>>
<div class="uh-hero__overlay"></div>
<div class="uh-hero__content">
<div class="uh-hero__avatar">
<img src="<?php echo esc_url($avatar); ?>" alt="<?php echo esc_attr($user->display_name); ?>" width="96" height="96">
<?php if ($is_vip) : ?>
<span class="aksrt-vip-badge">VIP</span>
<?php endif; ?>
</div>
<div class="uh-hero__info">
<h2 class="uh-hero__name">
<?php echo esc_html($user->display_name); ?>
<?php if ($is_own) : ?>
<a href="<?php echo esc_url(get_edit_profile_url($user_id)); ?>" class="uh-hero__edit" title="<?php esc_attr_e('编辑资料', 'aksrt'); ?>">
<i class="fas fa-pen"></i>
</a>
<?php endif; ?>
</h2>
<?php if ($desc) : ?>
<p class="uh-hero__desc"><?php echo esc_html(wp_trim_words($desc, 50)); ?></p>
<?php endif; ?>
<div class="uh-hero__meta">
<span><i class="fas fa-calendar-alt"></i> <?php echo esc_html(date_i18n(get_option('date_format'), strtotime($user->user_registered))); ?></span>
<span><i class="fas fa-file-alt"></i> <?php echo esc_html($dashboard['post_count'] ?? 0); ?> <?php esc_html_e('篇文章', 'aksrt'); ?></span>
</div>
</div>
<?php if ($show_follow && !$is_own && $viewer_id > 0) : ?>
<?php
$is_following = function_exists('aksrt_is_following') && aksrt_is_following($viewer_id, $user_id);
?>
<button class="btn <?php echo $is_following ? 'btn-outline-primary' : 'btn-primary'; ?> uh-hero__follow-btn"
data-action="toggle-follow"
data-target-user="<?php echo esc_attr($user_id); ?>">
<i class="fas <?php echo $is_following ? 'fa-user-check' : 'fa-user-plus'; ?>"></i>
<?php echo $is_following ? esc_html__('已关注', 'aksrt') : esc_html__('关注', 'aksrt'); ?>
</button>
<?php endif; ?>
</div>
</div>
+55
View File
@@ -0,0 +1,55 @@
<?php
/**
* 公开用户主页 — 文章列表
*
* @package AKSRT
* @since 5.0.0
*/
defined('ABSPATH') || exit;
$user_id = $args['user_id'] ?? 0;
$per_page = $args['per_page'] ?? 10;
$paged = max(1, get_query_var('paged'));
$query = new WP_Query([
'author' => $user_id,
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => $per_page,
'paged' => $paged,
'no_found_rows' => false,
]);
if (!$query->have_posts()) :
wp_reset_postdata();
?>
<div class="empty-state empty-state--sm">
<div class="empty-state__icon"><i class="far fa-file-alt"></i></div>
<p class="empty-state__title"><?php esc_html_e('暂无文章', 'aksrt'); ?></p>
</div>
<?php return;
endif;
?>
<div class="uh-posts">
<?php while ($query->have_posts()) : $query->the_post(); ?>
<?php get_template_part('templates/parts/content-post-card'); ?>
<?php endwhile; ?>
<?php if ($query->max_num_pages > 1) : ?>
<div class="aksrt-pagination">
<?php
echo paginate_links([
'base' => str_replace(999999999, '%#%', esc_url(get_pagenum_link(999999999))),
'format' => '?tab=posts&paged=%#%',
'current' => $paged,
'total' => $query->max_num_pages,
'prev_text' => '<i class="fas fa-chevron-left"></i>',
'next_text' => '<i class="fas fa-chevron-right"></i>',
]);
?>
</div>
<?php endif; ?>
<?php wp_reset_postdata(); ?>
</div>
+106
View File
@@ -0,0 +1,106 @@
<?php
/**
* 公开用户主页 — 侧边栏
*
* @package AKSRT
* @since 5.0.0
*/
defined('ABSPATH') || exit;
$user = $args['user'] ?? null;
$user_id = $user ? (int) $user->ID : 0;
$dashboard = $args['dashboard'] ?? [];
$settings = $args['settings'] ?? [];
$show_follow = $settings['follow'] ?? true;
$is_own = $dashboard['is_own_profile'] ?? false;
$viewer_id = $dashboard['viewer_id'] ?? 0;
$contacts = $dashboard['user_contacts'] ?? [];
if (!$user) return;
?>
<div class="uh-sidebar">
<!-- 数据统计 -->
<div class="widget">
<div class="widget-title"><i class="fas fa-chart-bar"></i> <?php esc_html_e('数据概览', 'aksrt'); ?></div>
<div class="widget-content">
<div class="uh-sidebar__stats">
<div class="uh-sidebar__stat">
<span class="uh-sidebar__stat-num"><?php echo esc_html($dashboard['post_count'] ?? 0); ?></span>
<span class="uh-sidebar__stat-label"><?php esc_html_e('文章', 'aksrt'); ?></span>
</div>
<div class="uh-sidebar__stat">
<span class="uh-sidebar__stat-num"><?php echo esc_html($dashboard['comment_count'] ?? 0); ?></span>
<span class="uh-sidebar__stat-label"><?php esc_html_e('评论', 'aksrt'); ?></span>
</div>
<div class="uh-sidebar__stat">
<span class="uh-sidebar__stat-num"><?php echo esc_html($dashboard['follower_count'] ?? 0); ?></span>
<span class="uh-sidebar__stat-label"><?php esc_html_e('粉丝', 'aksrt'); ?></span>
</div>
</div>
</div>
</div>
<!-- 社交链接 -->
<?php if (!empty($contacts)) : ?>
<div class="widget">
<div class="widget-title"><i class="fas fa-link"></i> <?php esc_html_e('社交链接', 'aksrt'); ?></div>
<div class="widget-content">
<div class="uh-sidebar__contacts">
<?php foreach ($contacts as $contact) : ?>
<a href="<?php echo esc_url($contact['url'] ?? '#'); ?>" target="_blank" rel="noopener noreferrer" class="uh-sidebar__contact" title="<?php echo esc_attr($contact['name'] ?? ''); ?>">
<i class="fab fa-<?php echo esc_attr($contact['icon'] ?? 'link'); ?>"></i>
<span><?php echo esc_html($contact['name'] ?? ''); ?></span>
</a>
<?php endforeach; ?>
</div>
</div>
</div>
<?php endif; ?>
<!-- 关注/粉丝 -->
<?php if ($show_follow && !$is_own && $viewer_id > 0) : ?>
<?php
$following_ids = function_exists('aksrt_get_following_ids') ? aksrt_get_following_ids($user_id, 6) : [];
$follower_ids = function_exists('aksrt_get_follower_ids') ? aksrt_get_follower_ids($user_id, 6) : [];
?>
<?php if (!empty($following_ids) || !empty($follower_ids)) : ?>
<div class="widget">
<div class="widget-title"><i class="fas fa-users"></i> <?php esc_html_e('社交关系', 'aksrt'); ?></div>
<div class="widget-content">
<?php if (!empty($following_ids)) : ?>
<div class="uh-sidebar__follow-section">
<span class="uh-sidebar__follow-label"><?php esc_html_e('关注', 'aksrt'); ?></span>
<div class="uh-sidebar__avatars">
<?php foreach ($following_ids as $fid) :
$f_user = get_userdata($fid);
if (!$f_user) continue;
?>
<a href="<?php echo esc_url(get_author_posts_url($fid)); ?>">
<?php echo get_avatar($fid, 32); ?>
</a>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php if (!empty($follower_ids)) : ?>
<div class="uh-sidebar__follow-section">
<span class="uh-sidebar__follow-label"><?php esc_html_e('粉丝', 'aksrt'); ?></span>
<div class="uh-sidebar__avatars">
<?php foreach ($follower_ids as $fid) :
$f_user = get_userdata($fid);
if (!$f_user) continue;
?>
<a href="<?php echo esc_url(get_author_posts_url($fid)); ?>">
<?php echo get_avatar($fid, 32); ?>
</a>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
+58
View File
@@ -0,0 +1,58 @@
<?php
/**
* 用户页面共享辅助函数
*
* 分页渲染和空状态卡片,供本人中心和公开主页共用。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_render_profile_pagination')) {
/**
* 渲染分页
*
* @param int $total_pages 总页数
* @param int $current 当前页
* @param string $param URL 参数名
*/
function aksrt_render_profile_pagination($total_pages, $current, $param) {
if ($total_pages <= 1) {
return;
}
$base = remove_query_arg($param);
$base = add_query_arg($param, '%#%', $base);
$links = paginate_links([
'base' => $base,
'format' => '',
'current' => $current,
'total' => $total_pages,
'prev_text' => '<span aria-hidden="true">&lsaquo;</span><span class="screen-reader-text">' . esc_html__('上一页', 'aksrt') . '</span>',
'next_text' => '<span aria-hidden="true">&rsaquo;</span><span class="screen-reader-text">' . esc_html__('下一页', 'aksrt') . '</span>',
'mid_size' => 1,
'end_size' => 1,
]);
if ($links) {
echo '<nav class="profile-pagination" aria-label="' . esc_attr__('分页', 'aksrt') . '">' . $links . '</nav>'; // phpcs:ignore
}
}
}
if (!function_exists('aksrt_render_profile_empty')) {
/**
* 渲染空状态卡片
*/
function aksrt_render_profile_empty($icon_svg, $title, $desc = '', $cta_url = '', $cta_label = '') {
echo '<div class="profile-empty">';
echo '<div class="profile-empty__icon" aria-hidden="true">' . $icon_svg . '</div>'; // phpcs:ignore
echo '<h4 class="profile-empty__title">' . esc_html($title) . '</h4>';
if ($desc) {
echo '<p class="profile-empty__desc">' . esc_html($desc) . '</p>';
}
if ($cta_url && $cta_label) {
echo '<a class="btn-primary btn-sm profile-empty__cta" href="' . esc_url($cta_url) . '">' . esc_html($cta_label) . '</a>';
}
echo '</div>';
}
}
+153
View File
@@ -0,0 +1,153 @@
<?php
/**
* Author Hero — shared hero section for author archive and public user homepage.
*
* Expected variables (passed via get_template_part or set_query_var):
* $user (WP_User) Target user object.
* $dashboard (array) Data from aksrt_get_user_dashboard_view().
* $user_home_settings (array) User home display settings.
* $is_author_page (bool) True when rendering on author.php (archive context).
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (empty($user) || !($user instanceof WP_User)) {
return;
}
$user_id = (int) $user->ID;
$dashboard = $dashboard ?? [];
$settings = $user_home_settings ?? [];
$is_author = !empty($is_author_page);
$home_url = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($user) : home_url('/');
$show_cover = !empty($settings['cover']);
$show_follow = !empty($settings['follow']);
$post_count = (int) ($dashboard['post_count'] ?? 0);
$comment_count = (int) ($dashboard['comment_count'] ?? 0);
$favorite_count = (int) ($dashboard['favorite_count'] ?? 0);
$contacts = $dashboard['user_contacts'] ?? [];
$social_on = !empty($dashboard['social_links_on']);
$signals = $dashboard['profile_signals'] ?? [];
$notice = $dashboard['profile_notice'] ?? null;
$show_report = !empty($dashboard['show_report']);
$bg_url = $dashboard['background_url'] ?? '';
$is_own = !empty($dashboard['is_own_profile']);
?>
<div class="aksrt-author-hero<?php echo $show_cover ? '' : ' aksrt-author-hero--no-cover'; ?>">
<?php if ($show_cover) : ?>
<div class="aksrt-author-hero__cover">
<img class="aksrt-author-hero__cover-img"
src="<?php echo esc_url($bg_url ? $bg_url : get_theme_file_uri('assets/img/person_background.png')); ?>"
alt=""<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('cover') : ' loading="lazy"'; ?>>
<div class="aksrt-author-hero__cover-mask"></div>
</div>
<?php endif; ?>
<div class="aksrt-author-hero__body">
<div class="aksrt-author-hero__avatar">
<?php
if (function_exists('aksrt_get_avatar_html')) {
echo aksrt_get_avatar_html($user_id, 120, ['loading' => 'lazy']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo get_avatar($user_id, 120, '', $user->display_name); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
</div>
<div class="aksrt-author-hero__info">
<h1 class="aksrt-author-hero__name">
<?php echo esc_html($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 ($user->description) : ?>
<p class="aksrt-author-hero__desc"><?php echo esc_html($user->description); ?></p>
<?php endif; ?>
<?php if (!empty($signals)) : ?>
<div class="aksrt-identity-chips">
<?php foreach ($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="aksrt-author-hero__stats">
<span class="aksrt-author-hero__stat">
<strong><?php echo esc_html((string) $post_count); ?></strong>
<span><?php esc_html_e('文章', 'aksrt'); ?></span>
</span>
<span class="aksrt-author-hero__stat">
<strong><?php echo esc_html((string) $comment_count); ?></strong>
<span><?php esc_html_e('评论', 'aksrt'); ?></span>
</span>
<span class="aksrt-author-hero__stat">
<strong><?php echo esc_html((string) $favorite_count); ?></strong>
<span><?php esc_html_e('收藏', 'aksrt'); ?></span>
</span>
</div>
<?php if (!empty($contacts) && $social_on) : ?>
<div class="aksrt-author-hero__contacts">
<?php if (function_exists('aksrt_render_social_links')) : ?>
<?php aksrt_render_social_links($contacts); ?>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<div class="aksrt-author-hero__actions">
<?php if ($is_own) : ?>
<a href="<?php echo esc_url(aksrt_get_user_center_url()); ?>" class="btn-ghost btn-sm">
<i class="fas fa-cog" aria-hidden="true"></i>
<span><?php esc_html_e('个人中心', 'aksrt'); ?></span>
</a>
<?php elseif ($show_follow) : ?>
<?php if (is_user_logged_in()) : ?>
<button type="button" class="btn-outline btn-sm aksrt-author-hero__follow-btn" data-aksrt-follow="<?php echo (int) $user_id; ?>" title="<?php esc_attr_e('关注该用户', 'aksrt'); ?>">
<i class="far fa-heart" aria-hidden="true"></i>
<span><?php esc_html_e('关注', 'aksrt'); ?></span>
</button>
<?php else : ?>
<a href="<?php echo esc_url(aksrt_get_login_url($home_url)); ?>" class="btn-outline btn-sm aksrt-author-hero__follow-btn">
<i class="far fa-heart" aria-hidden="true"></i>
<span><?php esc_html_e('登录后关注', 'aksrt'); ?></span>
</a>
<?php endif; ?>
<?php endif; ?>
<?php if ($show_report) : ?>
<?php if (is_user_logged_in()) : ?>
<button type="button" class="btn-ghost btn-sm aksrt-author-hero__report-btn" data-aksrt-report='{"target_type":"user","target_id":<?php echo (int) $user_id; ?>}' title="<?php esc_attr_e('举报该用户', 'aksrt'); ?>">
<i class="far fa-flag" aria-hidden="true"></i>
</button>
<?php else : ?>
<a href="<?php echo esc_url(aksrt_get_login_url($home_url)); ?>" class="btn-ghost btn-sm aksrt-author-hero__report-btn">
<i class="far fa-flag" aria-hidden="true"></i>
</a>
<?php endif; ?>
<?php endif; ?>
<?php if ($is_author && function_exists('aksrt_render_admin_quick_link')) { aksrt_render_admin_quick_link('page-display/user-home', __('用户主页设置', 'aksrt')); } ?>
</div>
</div>
</div>
<?php if (!empty($notice)) : ?>
<div class="aksrt-author-hero__notice aksrt-author-hero__notice--<?php echo esc_attr($notice['state']); ?>">
<i class="fas fa-exclamation-circle" aria-hidden="true"></i>
<span><?php echo esc_html($notice['text']); ?></span>
</div>
<?php endif; ?>
+94
View File
@@ -0,0 +1,94 @@
<?php
/**
* 商品分类归档模板
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$term = get_queried_object();
$paged = max(1, (int) get_query_var('paged'));
$shop = class_exists('AKSRT_Shop') ? AKSRT_Shop::get_instance() : null;
$view = $shop ? $shop->get_product_archive_view([
'taxonomy' => 'aksrt_product_cat',
'term_id' => $term->term_id,
'paged' => $paged,
]) : ['products' => [], 'term' => null, 'total' => 0, 'max_pages' => 0];
$products = $view['products'];
$total = $view['total'];
$max_pages = $view['max_pages'];
$term_name = $term->name ?? '';
$term_desc = $term->description ?? '';
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<?php if (function_exists('aksrt_breadcrumbs')) aksrt_breadcrumbs(); ?>
<div class="aksrt-product-archive container">
<header class="aksrt-product-archive__header">
<h1 class="aksrt-product-archive__title"><?php echo esc_html($term_name); ?></h1>
<?php if ($term_desc) : ?>
<p class="aksrt-product-archive__desc"><?php echo esc_html($term_desc); ?></p>
<?php endif; ?>
<p class="aksrt-product-archive__count"><?php printf(esc_html__('共 %d 件商品', 'aksrt'), $total); ?></p>
</header>
<?php if (!empty($products)) : ?>
<div class="aksrt-product-archive__grid" id="aksrt-product-grid">
<?php foreach ($products as $product) : ?>
<article class="aksrt-product-card">
<a href="<?php echo esc_url($product['permalink']); ?>" class="aksrt-product-card__link">
<?php if ($product['image']) : ?>
<div class="aksrt-product-card__image">
<img src="<?php echo esc_url($product['image']); ?>" alt="<?php echo esc_attr($product['title']); ?>"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
</div>
<?php else : ?>
<div class="aksrt-product-card__image aksrt-product-card__image--placeholder">
<i class="fas fa-image"></i>
</div>
<?php endif; ?>
<div class="aksrt-product-card__body">
<h3 class="aksrt-product-card__title"><?php echo esc_html($product['title']); ?></h3>
<?php if ($product['excerpt']) : ?>
<p class="aksrt-product-card__excerpt"><?php echo esc_html(wp_trim_words($product['excerpt'], 15)); ?></p>
<?php endif; ?>
<div class="aksrt-product-card__footer">
<span class="aksrt-product-card__price">¥<?php echo esc_html(number_format($product['price'], 2)); ?></span>
<?php if ($product['sales'] > 0) : ?>
<span class="aksrt-product-card__sales"><?php printf(esc_html__('已售 %d', 'aksrt'), $product['sales']); ?></span>
<?php endif; ?>
</div>
</div>
</a>
</article>
<?php endforeach; ?>
</div>
<?php if ($max_pages > 1) : ?>
<nav class="aksrt-product-archive__pagination" aria-label="<?php esc_attr_e('商品分页', 'aksrt'); ?>">
<?php
echo paginate_links([
'total' => $max_pages,
'current' => $paged,
'prev_text' => '<i class="fas fa-chevron-left"></i>',
'next_text' => '<i class="fas fa-chevron-right"></i>',
]);
?>
</nav>
<?php endif; ?>
<?php else : ?>
<div class="aksrt-product-archive__empty">
<i class="fas fa-box-open"></i>
<p><?php esc_html_e('该分类下暂无商品', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
</main>
<?php
wp_enqueue_script('aksrt-product-archive', get_template_directory_uri() . '/assets/js/product-archive.js', [], AKSRT_VERSION, true);
get_footer();
+93
View File
@@ -0,0 +1,93 @@
<?php
/**
* 商品标签归档模板
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
$term = get_queried_object();
$paged = max(1, (int) get_query_var('paged'));
$shop = class_exists('AKSRT_Shop') ? AKSRT_Shop::get_instance() : null;
$view = $shop ? $shop->get_product_archive_view([
'taxonomy' => 'aksrt_product_tag',
'term_id' => $term->term_id,
'paged' => $paged,
]) : ['products' => [], 'term' => null, 'total' => 0, 'max_pages' => 0];
$products = $view['products'];
$total = $view['total'];
$max_pages = $view['max_pages'];
$term_name = $term->name ?? '';
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<?php if (function_exists('aksrt_breadcrumbs')) aksrt_breadcrumbs(); ?>
<div class="aksrt-product-archive container">
<header class="aksrt-product-archive__header">
<h1 class="aksrt-product-archive__title">
<i class="fas fa-tag"></i>
<?php echo esc_html($term_name); ?>
</h1>
<p class="aksrt-product-archive__count"><?php printf(esc_html__('共 %d 件商品', 'aksrt'), $total); ?></p>
</header>
<?php if (!empty($products)) : ?>
<div class="aksrt-product-archive__grid" id="aksrt-product-grid">
<?php foreach ($products as $product) : ?>
<article class="aksrt-product-card">
<a href="<?php echo esc_url($product['permalink']); ?>" class="aksrt-product-card__link">
<?php if ($product['image']) : ?>
<div class="aksrt-product-card__image">
<img src="<?php echo esc_url($product['image']); ?>" alt="<?php echo esc_attr($product['title']); ?>"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
</div>
<?php else : ?>
<div class="aksrt-product-card__image aksrt-product-card__image--placeholder">
<i class="fas fa-image"></i>
</div>
<?php endif; ?>
<div class="aksrt-product-card__body">
<h3 class="aksrt-product-card__title"><?php echo esc_html($product['title']); ?></h3>
<?php if ($product['excerpt']) : ?>
<p class="aksrt-product-card__excerpt"><?php echo esc_html(wp_trim_words($product['excerpt'], 15)); ?></p>
<?php endif; ?>
<div class="aksrt-product-card__footer">
<span class="aksrt-product-card__price">¥<?php echo esc_html(number_format($product['price'], 2)); ?></span>
<?php if ($product['sales'] > 0) : ?>
<span class="aksrt-product-card__sales"><?php printf(esc_html__('已售 %d', 'aksrt'), $product['sales']); ?></span>
<?php endif; ?>
</div>
</div>
</a>
</article>
<?php endforeach; ?>
</div>
<?php if ($max_pages > 1) : ?>
<nav class="aksrt-product-archive__pagination" aria-label="<?php esc_attr_e('商品分页', 'aksrt'); ?>">
<?php
echo paginate_links([
'total' => $max_pages,
'current' => $paged,
'prev_text' => '<i class="fas fa-chevron-left"></i>',
'next_text' => '<i class="fas fa-chevron-right"></i>',
]);
?>
</nav>
<?php endif; ?>
<?php else : ?>
<div class="aksrt-product-archive__empty">
<i class="fas fa-box-open"></i>
<p><?php esc_html_e('该标签下暂无商品', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
</main>
<?php
wp_enqueue_script('aksrt-product-archive', get_template_directory_uri() . '/assets/js/product-archive.js', [], AKSRT_VERSION, true);
get_footer();
+67
View File
@@ -0,0 +1,67 @@
<?php
/**
* Shortcode template: [aksrt_archive]
*
* 紧凑版文章归档(按年月分组)。供 AKSRT_Shortcodes::render_archive() 通过
* AKSRT_Templates::render_archive_shortcode() 以输出缓冲方式 include。
*
* 可用变量:$attrs(短代码属性)、$content。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
global $wpdb;
$limit = isset($attrs['limit']) ? max(0, (int) $attrs['limit']) : 0; // 0 = 全部
$rows = $wpdb->get_results(
"SELECT DISTINCT YEAR(post_date) AS year, MONTH(post_date) AS month, COUNT(*) AS count
FROM {$wpdb->posts}
WHERE post_type = 'post' AND post_status = 'publish'
GROUP BY YEAR(post_date), MONTH(post_date)
ORDER BY year DESC, month DESC"
); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
if (empty($rows)) {
echo '<div class="aksrt-archives aksrt-archives--shortcode aksrt-archives--empty">'
. esc_html__('暂无文章。', 'aksrt') . '</div>';
return;
}
$current_year = 0;
$shown = 0;
?>
<div class="aksrt-archives aksrt-archives--shortcode">
<?php foreach ($rows as $row) :
if ((int) $row->year !== $current_year) :
$current_year = (int) $row->year;
?>
<h2 class="aksrt-archives__year"><?php echo esc_html((string) $current_year); ?></h2>
<?php endif; ?>
<h3 class="aksrt-archives__month"><?php echo esc_html(sprintf(__('%1$d 月(%2$d 篇)', 'aksrt'), (int) $row->month, (int) $row->count)); ?></h3>
<ul class="aksrt-archives__list">
<?php
$month_posts = get_posts([
'year' => (int) $row->year,
'monthnum' => (int) $row->month,
'posts_per_page' => -1,
'post_status' => 'publish',
'no_found_rows' => true,
]);
foreach ($month_posts as $mp) :
if ($limit > 0 && $shown >= $limit) {
break;
}
$shown++;
?>
<li class="aksrt-archives__item">
<span class="aksrt-archives__date"><?php echo esc_html(get_the_date('m-d', $mp)); ?></span>
<a href="<?php echo esc_url(get_permalink($mp->ID)); ?>" class="aksrt-archives__link"><?php echo esc_html(get_the_title($mp)); ?></a>
</li>
<?php endforeach; ?>
</ul>
<?php if ($limit > 0 && $shown >= $limit) { break; } ?>
<?php endforeach; ?>
</div>
+58
View File
@@ -0,0 +1,58 @@
<?php
/**
* Shortcode template: [aksrt_profile]
*
* 当前登录用户的资料卡。供 AKSRT_Shortcodes::render_profile() 通过
* AKSRT_Templates::render_profile_shortcode() 以输出缓冲方式 include。
*
* 可用变量:$attrs(短代码属性,支持 user="<ID 或 login>" 指定用户)、$content。
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
// 解析目标用户:属性指定 > 当前登录用户。
$target = isset($attrs['user']) ? trim((string) $attrs['user']) : '';
$user = null;
if ($target !== '') {
$user = is_numeric($target) ? get_user_by('id', (int) $target) : get_user_by('login', $target);
} elseif (is_user_logged_in()) {
$user = wp_get_current_user();
}
if (!($user instanceof WP_User) || $user->ID <= 0) {
echo '<div class="aksrt-profile-card aksrt-profile-card--shortcode aksrt-profile-card--guest">'
. '<p>' . esc_html__('登录后查看个人资料。', 'aksrt') . '</p>';
if (function_exists('aksrt_get_login_url')) {
echo '<a class="btn-primary btn-sm" href="' . esc_url(aksrt_get_login_url()) . '">' . esc_html__('登录', 'aksrt') . '</a>';
}
echo '</div>';
return;
}
$user_id = (int) $user->ID;
$avatar = function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user_id, ['size' => 96]) : get_avatar_url($user_id, ['size' => 96]);
$home_url = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($user) : get_author_posts_url($user_id);
$credits = function_exists('aksrt_user_credits') ? (int) aksrt_user_credits($user_id) : 0;
$post_count = (int) count_user_posts($user_id, 'post', true);
$bio = (string) get_the_author_meta('description', $user_id);
$show_credits = class_exists('AKSRT_Settings') ? AKSRT_Settings::credits_enabled() : true;
?>
<div class="aksrt-profile-card aksrt-profile-card--shortcode">
<a class="aksrt-profile-card__avatar" href="<?php echo esc_url($home_url); ?>">
<img src="<?php echo esc_url($avatar); ?>" alt="<?php echo esc_attr($user->display_name); ?>" width="64" height="64"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('avatar') : ' loading="lazy"'; ?>>
</a>
<div class="aksrt-profile-card__body">
<a class="aksrt-profile-card__name" href="<?php echo esc_url($home_url); ?>"><?php echo esc_html($user->display_name); ?></a>
<?php if ($bio !== '') : ?>
<p class="aksrt-profile-card__bio"><?php echo esc_html(wp_trim_words($bio, 24, '…')); ?></p>
<?php endif; ?>
<div class="aksrt-profile-card__stats">
<span class="aksrt-profile-card__stat"><strong><?php echo esc_html(number_format_i18n($post_count)); ?></strong><?php esc_html_e('文章', 'aksrt'); ?></span>
<?php if ($show_credits) : ?>
<span class="aksrt-profile-card__stat"><strong><?php echo esc_html(number_format_i18n($credits)); ?></strong><?php esc_html_e('积分', 'aksrt'); ?></span>
<?php endif; ?>
</div>
</div>
</div>