Expand render_topic_row into zibll-like detail cards (author card, cover, excerpt, multi-image grid, vote strip, plate + action-meta) and keep mini compact variant for dense lists.
719 lines
31 KiB
PHP
719 lines
31 KiB
PHP
<?php
|
|
/**
|
|
* @package AKSRT
|
|
*/
|
|
|
|
namespace AKSRT\Forum;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class ViewBuilder {
|
|
use ViewBuilderPart1;
|
|
use ViewBuilderPart2;
|
|
|
|
|
|
private Forum $forum;
|
|
|
|
public function __construct(Forum $forum) {
|
|
$this->forum = $forum;
|
|
}
|
|
|
|
public function get_topic_list(array $args = []): array {
|
|
$board_id = (int) ($args['board_id'] ?? 0);
|
|
$limit = max(1, min(100, (int) ($args['limit'] ?? ($args['per_page'] ?? 20))));
|
|
$page = max(1, (int) ($args['page'] ?? $args['paged'] ?? 1));
|
|
$offset = array_key_exists('offset', $args)
|
|
? max(0, (int) $args['offset'])
|
|
: ($page - 1) * $limit;
|
|
$status = sanitize_key((string) ($args['status'] ?? 'publish'));
|
|
$keyword = sanitize_text_field((string) ($args['keyword'] ?? ''));
|
|
$visibility = sanitize_key((string) ($args['visibility'] ?? ''));
|
|
$review_status = sanitize_key((string) ($args['review_status'] ?? ''));
|
|
$author_id = (int) ($args['author_id'] ?? 0);
|
|
$orderby = sanitize_key((string) ($args['orderby'] ?? ($args['sort'] ?? 'date')));
|
|
$viewer_user_id = (int) ($args['viewer_user_id'] ?? get_current_user_id());
|
|
$include_hidden = !empty($args['include_hidden']);
|
|
|
|
// Settings tab aliases: hot→views, essence/featured→featured filter.
|
|
if ($orderby === 'hot') {
|
|
$orderby = 'views';
|
|
}
|
|
|
|
$query_args = [
|
|
'post_type' => 'aksrt_forum_topic',
|
|
'posts_per_page' => $limit,
|
|
'offset' => $offset,
|
|
'post_status' => $this->forum->normalize_post_status_query($status),
|
|
'order' => 'DESC',
|
|
'ignore_sticky_posts'=> true,
|
|
];
|
|
|
|
$meta_query = [];
|
|
|
|
if ($orderby === 'views') {
|
|
$query_args['orderby'] = 'meta_value_num';
|
|
$query_args['meta_key'] = '_aksrt_views';
|
|
} elseif ($orderby === 'recommend') {
|
|
$query_args['orderby'] = 'meta_value_num';
|
|
$query_args['meta_key'] = '_aksrt_recommend_score';
|
|
} elseif ($orderby === 'comment_count') {
|
|
$query_args['orderby'] = 'comment_count';
|
|
} elseif ($orderby === 'score') {
|
|
$query_args['orderby'] = 'meta_value_num';
|
|
$query_args['meta_key'] = '_aksrt_score';
|
|
} elseif ($orderby === 'favorites') {
|
|
$query_args['orderby'] = 'meta_value_num';
|
|
$query_args['meta_key'] = '_aksrt_favorite_count';
|
|
} elseif ($orderby === 'last_reply') {
|
|
$query_args['orderby'] = 'meta_value';
|
|
$query_args['meta_key'] = '_aksrt_last_reply';
|
|
$query_args['meta_type']= 'DATETIME';
|
|
} elseif ($orderby === 'featured' || $orderby === 'essence') {
|
|
// 推荐 / 精华 share _aksrt_featured (Zibll essence = featured).
|
|
$meta_query[] = [
|
|
'key' => '_aksrt_featured',
|
|
'value' => '1',
|
|
'compare' => '=',
|
|
];
|
|
$query_args['orderby'] = 'date';
|
|
} else {
|
|
$query_args['orderby'] = 'date';
|
|
$orderby = 'date';
|
|
}
|
|
|
|
if ($keyword !== '') {
|
|
$query_args['s'] = $keyword;
|
|
}
|
|
|
|
if ($author_id > 0) {
|
|
$query_args['author'] = $author_id;
|
|
}
|
|
|
|
$plate_id = (int) ($args['plate_id'] ?? 0);
|
|
$plate_ids = $args['plate_ids'] ?? [];
|
|
if (!empty($plate_ids) && is_array($plate_ids)) {
|
|
$meta_query[] = [
|
|
'key' => '_aksrt_plate_id',
|
|
'value' => array_map('intval', $plate_ids),
|
|
'type' => 'NUMERIC',
|
|
'compare' => 'IN',
|
|
];
|
|
} elseif ($plate_id > 0) {
|
|
$meta_query[] = [
|
|
'key' => '_aksrt_plate_id',
|
|
'value' => $plate_id,
|
|
'type' => 'NUMERIC',
|
|
'compare' => '=',
|
|
];
|
|
} elseif ($board_id > 0) {
|
|
$query_args['tax_query'] = [[
|
|
'taxonomy' => 'aksrt_forum_board',
|
|
'field' => 'term_id',
|
|
'terms' => $board_id,
|
|
]];
|
|
}
|
|
|
|
if ($visibility !== '') {
|
|
$meta_query[] = [
|
|
'key' => '_aksrt_visibility',
|
|
'value' => $visibility,
|
|
'compare' => '=',
|
|
];
|
|
}
|
|
|
|
if ($review_status !== '') {
|
|
$meta_query[] = [
|
|
'key' => '_aksrt_review_status',
|
|
'value' => $review_status,
|
|
'compare' => '=',
|
|
];
|
|
}
|
|
|
|
if (!empty($meta_query)) {
|
|
if (count($meta_query) > 1) {
|
|
$meta_query['relation'] = 'AND';
|
|
}
|
|
$query_args['meta_query'] = $meta_query;
|
|
}
|
|
|
|
$query = new \WP_Query($query_args);
|
|
$items = [];
|
|
|
|
while ($query->have_posts()) {
|
|
$query->the_post();
|
|
$post = get_post(get_the_ID());
|
|
if (!$post) {
|
|
continue;
|
|
}
|
|
|
|
$topic = $this->forum->build_topic_payload($post, $viewer_user_id);
|
|
if (!$include_hidden && empty($topic['can_view'])) {
|
|
continue;
|
|
}
|
|
|
|
$items[] = $topic;
|
|
}
|
|
|
|
wp_reset_postdata();
|
|
|
|
return [
|
|
'items' => $items,
|
|
'total' => (int) $query->found_posts,
|
|
'max_pages' => (int) $query->max_num_pages,
|
|
'page' => $page,
|
|
'orderby' => $orderby,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get forum homepage view.
|
|
*/
|
|
public function get_home_view(int $user_id = 0): array {
|
|
$boards = $this->forum->get_boards();
|
|
$stats = $this->get_stats();
|
|
$synthesis = $this->get_synthesis_stream(1, 15, $user_id);
|
|
$hot = $this->get_topic_list([
|
|
'limit' => 8,
|
|
'status' => 'publish',
|
|
'viewer_user_id' => $user_id,
|
|
'orderby' => 'views',
|
|
]);
|
|
|
|
$applications = [];
|
|
if ($user_id > 0) {
|
|
foreach ($boards as $board) {
|
|
$applications[(int) $board['id']] = $this->forum->get_board_application_for_user($user_id, (int) $board['id']);
|
|
}
|
|
}
|
|
|
|
$config = $this->forum->get_config();
|
|
$home_tabs = $config['homepage']['tabs'] ?? [
|
|
['type' => 'follow', 'label' => '关注'],
|
|
['type' => 'synthesis', 'label' => '综合'],
|
|
['type' => 'plate', 'label' => '版块'],
|
|
];
|
|
$active_index = (int) ($config['homepage']['tab_active_index'] ?? 1);
|
|
$quick_posts = !empty($config['homepage']['quick_posts']);
|
|
|
|
return [
|
|
'boards' => $boards,
|
|
'recent_topics' => $synthesis['items'],
|
|
'synthesis' => $synthesis,
|
|
'hot_topics' => $hot['items'],
|
|
'stats' => $stats,
|
|
'permissions' => [
|
|
'can_compose' => $user_id > 0 && $this->forum->forum_user_can($user_id, 'compose_post'),
|
|
'can_manage' => $this->forum->is_forum_admin($user_id),
|
|
'can_create_plate'=> $this->forum->forum_user_can($user_id, 'plate_create'),
|
|
'user_tier' => $user_id > 0 ? $this->forum->get_user_plate_tier($user_id, 0) : Forum::TIER_NONE,
|
|
],
|
|
'applications' => $applications,
|
|
'home_tabs' => $home_tabs,
|
|
'active_index' => $active_index,
|
|
'quick_posts' => $quick_posts,
|
|
'compose_url' => function_exists('aksrt_get_forum_compose_url') ? aksrt_get_forum_compose_url() : home_url('/'),
|
|
'home_url' => function_exists('aksrt_get_forum_home_url') ? aksrt_get_forum_home_url() : home_url('/'),
|
|
];
|
|
}
|
|
|
|
// ── §7.10 Tab data streams ──────────────────────────────────────────
|
|
|
|
/**
|
|
* Get topics from plates the user follows.
|
|
*
|
|
* @return array{items:array,total:int,max_pages:int}
|
|
*/
|
|
public function get_follow_stream(int $user_id, int $page = 1, int $limit = 15): array {
|
|
if ($user_id <= 0) {
|
|
return ['items' => [], 'total' => 0, 'max_pages' => 1];
|
|
}
|
|
$follow_ids = get_user_meta($user_id, 'aksrt_follow_plate', true);
|
|
$follow_ids = is_array($follow_ids) ? array_filter(array_map('intval', $follow_ids)) : [];
|
|
if (empty($follow_ids)) {
|
|
return ['items' => [], 'total' => 0, 'max_pages' => 1];
|
|
}
|
|
|
|
return $this->get_topic_list([
|
|
'plate_ids' => $follow_ids,
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
'status' => 'publish',
|
|
'viewer_user_id' => $user_id,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get synthesis (all topics) stream sorted by date.
|
|
*
|
|
* @return array{items:array,total:int,max_pages:int}
|
|
*/
|
|
public function get_synthesis_stream(int $page = 1, int $limit = 15, int $viewer = 0): array {
|
|
return $this->get_topic_list([
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
'status' => 'publish',
|
|
'viewer_user_id' => $viewer,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get pending-review topics for moderators.
|
|
*
|
|
* @return array{items:array,total:int,max_pages:int}
|
|
*/
|
|
public function get_pending_stream(int $user_id, int $page = 1, int $limit = 15): array {
|
|
if ($user_id <= 0 || !$this->forum->is_forum_admin($user_id)) {
|
|
return ['items' => [], 'total' => 0, 'max_pages' => 1];
|
|
}
|
|
return $this->get_topic_list([
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
'status' => 'pending',
|
|
'viewer_user_id' => $user_id,
|
|
'include_hidden' => true,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* AJAX: Load a forum home tab stream.
|
|
*/
|
|
public function ajax_home_tab(): void {
|
|
check_ajax_referer('aksrt_public_nonce', 'nonce');
|
|
|
|
$tab_type = sanitize_key((string) ($_POST['tab_type'] ?? 'synthesis'));
|
|
$page = max(1, (int) ($_POST['page'] ?? 1));
|
|
$user_id = get_current_user_id();
|
|
|
|
switch ($tab_type) {
|
|
case 'follow':
|
|
$result = $this->get_follow_stream($user_id, $page);
|
|
break;
|
|
case 'pending':
|
|
$result = $this->get_pending_stream($user_id, $page);
|
|
break;
|
|
case 'plate':
|
|
ob_start();
|
|
$this->render_board_cards($this->forum->get_boards());
|
|
$html = ob_get_clean();
|
|
wp_send_json_success([
|
|
'html' => $html,
|
|
'type' => 'plate',
|
|
'boards' => $this->forum->get_boards(),
|
|
'total' => count($this->forum->get_boards()),
|
|
'max_pages' => 1,
|
|
'page' => 1,
|
|
'has_more' => false,
|
|
]);
|
|
return;
|
|
default:
|
|
$result = $this->get_synthesis_stream($page, 15, $user_id);
|
|
}
|
|
|
|
ob_start();
|
|
if (!empty($result['items'])) {
|
|
foreach ($result['items'] as $topic) {
|
|
$this->render_topic_row($topic);
|
|
}
|
|
} else {
|
|
echo '<div class="aksrt-forum__empty"><i class="far fa-comment-dots"></i><p>' . esc_html__('内容空空如也', 'aksrt') . '</p></div>';
|
|
}
|
|
$html = ob_get_clean();
|
|
|
|
wp_send_json_success([
|
|
'html' => $html,
|
|
'type' => $tab_type,
|
|
'total' => $result['total'],
|
|
'max_pages' => $result['max_pages'],
|
|
'page' => $page,
|
|
'has_more' => $page < $result['max_pages'],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Public AJAX: plate/board topic list with sort tabs.
|
|
*/
|
|
public function ajax_board_topics(): void {
|
|
check_ajax_referer('aksrt_public_nonce', 'nonce');
|
|
|
|
$plate_id = (int) ($_POST['plate_id'] ?? $_GET['plate_id'] ?? 0);
|
|
$board_id = (int) ($_POST['board_id'] ?? $_GET['board_id'] ?? 0);
|
|
$page = max(1, (int) ($_POST['page'] ?? $_GET['page'] ?? 1));
|
|
$orderby = sanitize_key((string) ($_POST['orderby'] ?? $_POST['sort'] ?? $_GET['orderby'] ?? $_GET['sort'] ?? 'date'));
|
|
$limit = max(1, min(50, (int) ($_POST['limit'] ?? $_GET['limit'] ?? 20)));
|
|
$user_id = get_current_user_id();
|
|
|
|
$allowed = ['date', 'featured', 'essence', 'last_reply', 'hot', 'views', 'recommend', 'comment_count', 'score', 'favorites'];
|
|
if (!in_array($orderby, $allowed, true)) {
|
|
$orderby = 'date';
|
|
}
|
|
|
|
$list_args = [
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
'status' => 'publish',
|
|
'viewer_user_id' => $user_id,
|
|
'orderby' => $orderby,
|
|
];
|
|
if ($plate_id > 0) {
|
|
$list_args['plate_id'] = $plate_id;
|
|
} elseif ($board_id > 0) {
|
|
$list_args['board_id'] = $board_id;
|
|
}
|
|
|
|
$result = $this->get_topic_list($list_args);
|
|
|
|
ob_start();
|
|
if (!empty($result['items'])) {
|
|
foreach ($result['items'] as $topic) {
|
|
$this->render_topic_row($topic);
|
|
}
|
|
} else {
|
|
$compose = function_exists('aksrt_get_forum_compose_url')
|
|
? aksrt_get_forum_compose_url($plate_id > 0 ? ['board' => $plate_id] : ($board_id > 0 ? ['board' => $board_id] : []))
|
|
: '';
|
|
echo '<div class="aksrt-forum-board__empty aksrt-forum__empty">';
|
|
echo '<i class="fas fa-comments"></i>';
|
|
echo '<p>' . esc_html__('内容空空如也', 'aksrt') . '</p>';
|
|
if ($compose !== '') {
|
|
echo '<a href="' . esc_url($compose) . '">' . esc_html__('发布第一个主题', 'aksrt') . '</a>';
|
|
}
|
|
echo '</div>';
|
|
}
|
|
$html = ob_get_clean();
|
|
|
|
wp_send_json_success([
|
|
'html' => $html,
|
|
'total' => $result['total'],
|
|
'max_pages' => $result['max_pages'],
|
|
'page' => $page,
|
|
'orderby' => $orderby,
|
|
'has_more' => $page < $result['max_pages'],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Render plate/board cards grid (home plate tab).
|
|
*
|
|
* @param array<int, array<string, mixed>> $boards
|
|
*/
|
|
public function render_board_cards(array $boards): void {
|
|
$names = $this->forum->get_config()['names'] ?? [];
|
|
$name_topic = $names['posts'] ?? __('主题', 'aksrt');
|
|
if (empty($boards)) {
|
|
echo '<div class="aksrt-forum__empty"><i class="far fa-folder-open"></i><p>' . esc_html__('内容空空如也', 'aksrt') . '</p></div>';
|
|
return;
|
|
}
|
|
echo '<div class="aksrt-forum-board-card__grid">';
|
|
foreach ($boards as $board) {
|
|
$url = (string) ($board['url'] ?? '#');
|
|
$name = (string) ($board['name'] ?? '');
|
|
$desc = (string) ($board['description'] ?? '');
|
|
$count = (int) ($board['post_count'] ?? $board['topic_count'] ?? 0);
|
|
$cover = (string) ($board['cover_url'] ?? $board['thumbnail_url'] ?? '');
|
|
?>
|
|
<a class="aksrt-forum-board-card" href="<?php echo esc_url($url); ?>">
|
|
<?php if ($cover !== '') : ?>
|
|
<span class="aksrt-forum-board-card__cover" style="background-image:url(<?php echo esc_url($cover); ?>)"></span>
|
|
<?php else : ?>
|
|
<span class="aksrt-forum-board-card__cover aksrt-forum-board-card__cover--empty"><i class="fas fa-comments"></i></span>
|
|
<?php endif; ?>
|
|
<span class="aksrt-forum-board-card__body">
|
|
<span class="aksrt-forum-board-card__title"><?php echo esc_html($name); ?></span>
|
|
<?php if ($desc !== '') : ?>
|
|
<span class="aksrt-forum-board-card__desc"><?php echo esc_html(wp_trim_words($desc, 18, '…')); ?></span>
|
|
<?php endif; ?>
|
|
<span class="aksrt-forum-board-card__meta">
|
|
<i class="fas fa-list-alt"></i>
|
|
<?php printf(esc_html__('%1$d 个%2$s', 'aksrt'), $count, esc_html($name_topic)); ?>
|
|
</span>
|
|
</span>
|
|
</a>
|
|
<?php
|
|
}
|
|
echo '</div>';
|
|
}
|
|
|
|
/**
|
|
* Render a single topic row (reusable for AJAX and initial render).
|
|
*
|
|
* @param array $topic Topic payload.
|
|
* @param string $style detail|mini (zibll forum-posts parity).
|
|
*/
|
|
public function render_topic_row(array $topic, string $style = 'detail'): void {
|
|
$style = $style === 'mini' ? 'mini' : 'detail';
|
|
$board = !empty($topic['boards'][0]) ? $topic['boards'][0] : null;
|
|
$names = $this->forum->get_config()['names'] ?? [];
|
|
$sticky_level = (int) ($topic['sticky_level'] ?? (!empty($topic['sticky']) ? 1 : 0));
|
|
$permalink = (string) ($topic['permalink'] ?? '#');
|
|
$cover = is_array($topic['cover'] ?? null) ? $topic['cover'] : null;
|
|
$vote = is_array($topic['vote'] ?? null) ? $topic['vote'] : null;
|
|
$excerpt = trim((string) ($topic['excerpt'] ?? ''));
|
|
$topic_cat = is_array($topic['topic_cat'] ?? null) ? $topic['topic_cat'] : null;
|
|
|
|
$row_class = 'aksrt-forum-topic-row aksrt-forum-posts forum-posts ajax-item ' . $style;
|
|
if ($sticky_level > 0) {
|
|
$row_class .= ' is-sticky is-sticky-' . $sticky_level;
|
|
}
|
|
if (!empty($topic['featured'])) {
|
|
$row_class .= ' is-featured';
|
|
}
|
|
|
|
$badges = '';
|
|
if ($sticky_level > 0) {
|
|
$badges .= '<span class="aksrt-forum-topic-row__badge aksrt-forum-topic-row__badge--sticky badge-topping">'
|
|
. esc_html((string) ($topic['sticky_label'] ?? __('置顶', 'aksrt'))) . '</span>';
|
|
}
|
|
if (!empty($topic['featured'])) {
|
|
$badges .= '<span class="aksrt-forum-topic-row__badge aksrt-forum-topic-row__badge--featured badge-essence">'
|
|
. esc_html__('精华', 'aksrt') . '</span>';
|
|
}
|
|
if (($topic['bbs_type'] ?? '') === 'question') {
|
|
$q_status = (int) ($topic['question_status'] ?? 0);
|
|
$badges .= '<span class="aksrt-forum-topic-row__badge aksrt-forum-topic-row__badge--question'
|
|
. ($q_status === 1 ? ' is-solved' : '') . '">'
|
|
. esc_html($q_status === 1
|
|
? (string) ($names['question_ok_badge'] ?? __('已解决', 'aksrt'))
|
|
: (string) ($names['question_badge'] ?? __('提问', 'aksrt')))
|
|
. '</span>';
|
|
}
|
|
|
|
$title_html = $badges
|
|
. '<h3 class="aksrt-forum-topic-row__title forum-title">'
|
|
. '<a href="' . esc_url($permalink) . '">' . esc_html((string) ($topic['title'] ?? '')) . '</a></h3>';
|
|
|
|
if ($style === 'mini') {
|
|
?>
|
|
<article class="<?php echo esc_attr($row_class); ?>" data-topic-id="<?php echo esc_attr((string) ($topic['id'] ?? 0)); ?>">
|
|
<div class="aksrt-forum-topic-row__avatar forum-user">
|
|
<?php if (!empty($topic['author']['avatar_url'])) : ?>
|
|
<img src="<?php echo esc_url((string) $topic['author']['avatar_url']); ?>" alt="" width="40" height="40" loading="lazy" class="forum-avatar">
|
|
<?php endif; ?>
|
|
</div>
|
|
<div class="aksrt-forum-topic-row__body entry-info">
|
|
<div class="aksrt-forum-topic-row__title-wrap"><?php echo $title_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
|
|
<div class="aksrt-forum-topic-row__meta item-meta flex ac jsb">
|
|
<div class="meta-left">
|
|
<span class="aksrt-forum-topic-row__author"><?php echo esc_html((string) ($topic['author']['display_name'] ?? '')); ?></span>
|
|
<span class="aksrt-forum-topic-row__time icon-circle"><?php echo esc_html((string) ($topic['date'] ?? '')); ?></span>
|
|
</div>
|
|
<div class="meta-right action-meta">
|
|
<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>
|
|
<span title="<?php esc_attr_e('回复', 'aksrt'); ?>"><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-thumbs-up"></i> <?php echo esc_html(number_format_i18n((int) ($topic['score'] ?? 0))); ?></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
<?php
|
|
return;
|
|
}
|
|
|
|
// detail
|
|
$list_imgs = $this->extract_topic_list_images($topic, 4);
|
|
?>
|
|
<article class="<?php echo esc_attr($row_class); ?>" data-topic-id="<?php echo esc_attr((string) ($topic['id'] ?? 0)); ?>">
|
|
<div class="aksrt-forum-topic-row__author-card mb10 flex ac">
|
|
<div class="aksrt-forum-topic-row__avatar">
|
|
<?php if (!empty($topic['author']['avatar_url'])) : ?>
|
|
<img src="<?php echo esc_url((string) $topic['author']['avatar_url']); ?>" alt="" width="44" height="44" loading="lazy">
|
|
<?php endif; ?>
|
|
</div>
|
|
<div class="aksrt-forum-topic-row__author-meta">
|
|
<div class="aksrt-forum-topic-row__author-name"><?php echo esc_html((string) ($topic['author']['display_name'] ?? '')); ?></div>
|
|
<div class="aksrt-forum-topic-row__time muted-2-color em09"><?php echo esc_html((string) ($topic['date'] ?? '')); ?></div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if (is_array($cover) && !empty($cover['type'])) : ?>
|
|
<a class="aksrt-forum-topic-row__cover forum-lists-cover" href="<?php echo esc_url($permalink); ?>">
|
|
<?php if (($cover['type'] ?? '') === 'video') : ?>
|
|
<?php if (!empty($cover['poster'])) : ?>
|
|
<img src="<?php echo esc_url((string) $cover['poster']); ?>" alt="" loading="lazy">
|
|
<?php endif; ?>
|
|
<i class="fas fa-play-circle"></i>
|
|
<?php elseif (($cover['type'] ?? '') === 'slide' && !empty($cover['images'][0])) : ?>
|
|
<img src="<?php echo esc_url((string) $cover['images'][0]); ?>" alt="" loading="lazy">
|
|
<span class="aksrt-forum-topic-row__cover-count"><i class="fas fa-images"></i></span>
|
|
<?php elseif (!empty($cover['url'])) : ?>
|
|
<img src="<?php echo esc_url((string) $cover['url']); ?>" alt="" loading="lazy">
|
|
<?php endif; ?>
|
|
</a>
|
|
<?php endif; ?>
|
|
|
|
<div class="aksrt-forum-topic-row__body">
|
|
<div class="aksrt-forum-topic-row__title-wrap"><?php echo $title_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
|
|
|
|
<div class="content mt6">
|
|
<?php if ($topic_cat && !empty($topic_cat['name'])) : ?>
|
|
<a class="aksrt-forum-topic-row__topic-link focus-color mr3" href="<?php echo esc_url((string) ($topic_cat['url'] ?? '#')); ?>">
|
|
<i class="fas fa-hashtag"></i><?php echo esc_html((string) $topic_cat['name']); ?>
|
|
</a>
|
|
<?php endif; ?>
|
|
<?php if ($excerpt !== '') : ?>
|
|
<div class="excerpt"><?php echo esc_html($excerpt); ?></div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php if (!empty($list_imgs) && empty($cover)) : ?>
|
|
<div class="imgbox-container lists-imgs mt6 count-<?php echo esc_attr((string) count($list_imgs)); ?>">
|
|
<?php foreach ($list_imgs as $img_url) : ?>
|
|
<a class="lists-imgs__item cursor-zoom-in" href="<?php echo esc_url($permalink); ?>">
|
|
<img src="<?php echo esc_url($img_url); ?>" alt="" loading="lazy">
|
|
</a>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if (!empty($vote['enabled']) && !empty($vote['options']) && is_array($vote['options'])) : ?>
|
|
<div class="aksrt-forum-topic-row__vote mt10">
|
|
<?php
|
|
$total_votes = 0;
|
|
foreach ($vote['options'] as $opt) {
|
|
$total_votes += (int) ($opt['count'] ?? 0);
|
|
}
|
|
foreach (array_slice($vote['options'], 0, 4) as $opt) :
|
|
$cnt = (int) ($opt['count'] ?? 0);
|
|
$pct = $total_votes > 0 ? round($cnt * 100 / $total_votes) : 0;
|
|
?>
|
|
<div class="aksrt-forum-topic-row__vote-opt">
|
|
<span class="aksrt-forum-topic-row__vote-label"><?php echo esc_html((string) ($opt['label'] ?? $opt['text'] ?? '')); ?></span>
|
|
<span class="aksrt-forum-topic-row__vote-bar"><i style="width:<?php echo esc_attr((string) $pct); ?>%"></i></span>
|
|
<span class="aksrt-forum-topic-row__vote-pct"><?php echo esc_html((string) $pct); ?>%</span>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="flex ac jsb mt10 aksrt-forum-topic-row__footer">
|
|
<?php if ($board) : ?>
|
|
<a class="but but-plate aksrt-forum-topic-row__board" href="<?php echo esc_url((string) ($board['url'] ?? '#')); ?>">
|
|
<?php echo esc_html((string) ($board['name'] ?? '')); ?>
|
|
<i class="fa fa-angle-right ml6"></i>
|
|
</a>
|
|
<?php else : ?>
|
|
<span class="plate-null"></span>
|
|
<?php endif; ?>
|
|
<div class="action-meta flex0 flex jsb aksrt-forum-topic-row__actions">
|
|
<span title="<?php esc_attr_e('评分', 'aksrt'); ?>"><i class="fas fa-thumbs-up"></i> <?php echo esc_html(number_format_i18n((int) ($topic['score'] ?? 0))); ?></span>
|
|
<a href="<?php echo esc_url($permalink); ?>#comments" title="<?php esc_attr_e('回复', 'aksrt'); ?>"><i class="fas fa-comment"></i> <?php echo esc_html(number_format_i18n((int) ($topic['comment_count'] ?? 0))); ?></a>
|
|
<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>
|
|
<span title="<?php esc_attr_e('收藏', 'aksrt'); ?>"><i class="fas fa-star"></i> <?php echo esc_html(number_format_i18n((int) ($topic['favorite_count'] ?? 0))); ?></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Pull up to N image URLs from cover slides or content HTML for list grids.
|
|
*
|
|
* @return array<int,string>
|
|
*/
|
|
private function extract_topic_list_images(array $topic, int $max = 4): array {
|
|
$max = max(1, min(6, $max));
|
|
$out = [];
|
|
$cover = is_array($topic['cover'] ?? null) ? $topic['cover'] : null;
|
|
if ($cover) {
|
|
if (($cover['type'] ?? '') === 'slide' && !empty($cover['images']) && is_array($cover['images'])) {
|
|
foreach ($cover['images'] as $u) {
|
|
$u = esc_url_raw((string) $u);
|
|
if ($u !== '') {
|
|
$out[] = $u;
|
|
}
|
|
if (count($out) >= $max) {
|
|
return $out;
|
|
}
|
|
}
|
|
} elseif (!empty($cover['url'])) {
|
|
$out[] = esc_url_raw((string) $cover['url']);
|
|
}
|
|
}
|
|
if (count($out) >= $max) {
|
|
return $out;
|
|
}
|
|
$content = (string) ($topic['content'] ?? '');
|
|
if ($content !== '' && preg_match_all('/<img[^>]+src=["\']([^"\']+)["\']/i', $content, $m)) {
|
|
foreach ($m[1] as $src) {
|
|
$src = esc_url_raw((string) $src);
|
|
if ($src === '' || in_array($src, $out, true)) {
|
|
continue;
|
|
}
|
|
$out[] = $src;
|
|
if (count($out) >= $max) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
public function get_user_forum_topics(int $user_id, array $args = []): array {
|
|
$status = $args['status'] ?? 'publish';
|
|
$page = max(1, (int) ($args['page'] ?? 1));
|
|
$limit = max(1, min(50, (int) ($args['limit'] ?? 10)));
|
|
|
|
$query_args = [
|
|
'post_type' => 'aksrt_forum_topic',
|
|
'author' => $user_id,
|
|
'post_status' => $status,
|
|
'posts_per_page' => $limit,
|
|
'paged' => $page,
|
|
'orderby' => 'date',
|
|
'order' => 'DESC',
|
|
];
|
|
|
|
$query = new \WP_Query($query_args);
|
|
$items = [];
|
|
foreach ($query->posts as $post) {
|
|
$items[] = $this->forum->build_topic_payload($post, $user_id);
|
|
}
|
|
|
|
return [
|
|
'items' => $items,
|
|
'total' => (int) $query->found_posts,
|
|
'max_pages' => (int) $query->max_num_pages,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get a user's favorited forum topics.
|
|
*
|
|
* @return array{items:array,total:int,max_pages:int}
|
|
*/
|
|
public function get_user_favorite_topics(int $user_id, int $page = 1, int $limit = 10): array {
|
|
$fav_ids = get_user_meta($user_id, 'aksrt_favorite_forum_posts', true);
|
|
$fav_ids = is_array($fav_ids) ? array_map('intval', array_filter($fav_ids)) : [];
|
|
|
|
if (empty($fav_ids)) {
|
|
return ['items' => [], 'total' => 0, 'max_pages' => 1];
|
|
}
|
|
|
|
$query = new \WP_Query([
|
|
'post_type' => 'aksrt_forum_topic',
|
|
'post__in' => $fav_ids,
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => $limit,
|
|
'paged' => $page,
|
|
'orderby' => 'post__in',
|
|
]);
|
|
|
|
$items = [];
|
|
foreach ($query->posts as $post) {
|
|
$items[] = $this->forum->build_topic_payload($post, $user_id);
|
|
}
|
|
|
|
return [
|
|
'items' => $items,
|
|
'total' => (int) $query->found_posts,
|
|
'max_pages' => (int) $query->max_num_pages,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get a user's managed plates (plates where user is moderator/author).
|
|
*
|
|
* @return array{owned:array,moderating:array}
|
|
*/
|
|
}
|