Match the reference layouts for the remaining forum routes: plate pages get the blurred header card, moderator row and the affix tab bar with per-tab filtering, topping posts and moderator-only pending/trash tabs; post pages get the article shell with breadcrumbs, cover, meta row, vote box, tag row and footer actions; compose pages get the reference editor plus settings sidebar (submit/draft, status, access, plate, topic, tag, type, vote) with a cover picker. Saving now honours draft state and the featured image through the REST contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
191 lines
11 KiB
PHP
191 lines
11 KiB
PHP
<?php
|
|
|
|
namespace Jio\Forum;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
/** Forum post detail page (reference article layout). */
|
|
final class PostView {
|
|
public static function render(): void {
|
|
$post = get_post();
|
|
if (!$post instanceof \WP_Post || $post->post_type !== Core::POST) {
|
|
return;
|
|
}
|
|
$id = (int) $post->ID;
|
|
$plate_id = (int) get_post_meta($id, '_jio_forum_plate_id', true);
|
|
$type = (string) get_post_meta($id, '_jio_forum_type', true);
|
|
$user_id = get_current_user_id();
|
|
$can_view = Access::can_view($user_id, $id);
|
|
|
|
echo '<main id="forum" class="jio-forum forum-post"><div class="container"><div class="content-wrap"><div class="content-layout">';
|
|
echo '<div class="fixed-wrap single-wrap"><div class="fixed-wrap-content">';
|
|
\Jio\WidgetSystem\WidgetAreas::render('forum_post_top_fluid');
|
|
do_action('bbs_posts_page_content_top');
|
|
if ($can_view) echo self::cover($post);
|
|
echo self::breadcrumbs($post);
|
|
echo '<article id="article" class="article zib-widget forum-article relative">';
|
|
echo '<div class="article-header clearfix">' . self::title($post) . self::meta($post) . '</div>';
|
|
echo self::content($post, $can_view);
|
|
echo self::footer($post);
|
|
echo '</article>';
|
|
if ($can_view) {
|
|
self::comments($id);
|
|
}
|
|
do_action('bbs_posts_page_content_bottom');
|
|
\Jio\WidgetSystem\WidgetAreas::render('forum_post_bottom_fluid');
|
|
echo '</div></div></div>';
|
|
if (jio_is_show_sidebar()) {
|
|
echo '<div class="sidebar">';
|
|
\Jio\WidgetSystem\WidgetAreas::render('forum_post_sidebar');
|
|
echo '</div>';
|
|
}
|
|
echo '</div></div></main>';
|
|
}
|
|
|
|
private static function breadcrumbs(\WP_Post $post): string {
|
|
$plate_id = (int) get_post_meta($post->ID, '_jio_forum_plate_id', true);
|
|
$icon = '<i class="fa fas fa-map-marker-alt"></i> ';
|
|
$html = '<ul class="breadcrumb">';
|
|
$html .= '<li><a href="' . esc_url(home_url('/')) . '">' . $icon . '首页</a></li>';
|
|
$html .= '<li><a href="' . esc_url(Core::home_url()) . '">论坛</a></li>';
|
|
if ($plate_id > 0) {
|
|
$terms = wp_get_post_terms($plate_id, Core::CATEGORY);
|
|
if (is_array($terms) && $terms) {
|
|
$html .= '<li><a href="' . esc_url(get_term_link($terms[0])) . '">' . esc_html($terms[0]->name) . '</a></li>';
|
|
}
|
|
$html .= '<li><a href="' . esc_url(get_permalink($plate_id)) . '">' . esc_html(get_the_title($plate_id)) . '</a></li>';
|
|
}
|
|
$html .= '<li class="active">' . esc_html(wp_trim_words($post->post_title, 8)) . '</li>';
|
|
return $html . '</ul>';
|
|
}
|
|
|
|
private static function cover(\WP_Post $post): string {
|
|
if (!has_post_thumbnail($post)) return '';
|
|
return '<div class="forum-single-cover mb20"><div class="graphic imgbox-container" style="padding-bottom:45%">' . get_the_post_thumbnail($post, 'large', ['class' => 'fit-cover']) . '</div></div>';
|
|
}
|
|
|
|
private static function title(\WP_Post $post): string {
|
|
return Cards::title($post, 'article-title', '', true);
|
|
}
|
|
|
|
private static function meta(\WP_Post $post): string {
|
|
$author = (int) $post->post_author;
|
|
$name = function_exists('jio_get_user_name') ? jio_get_user_name(['id' => $author, 'class' => '', 'name_class' => 'focus-color']) : esc_html((string) get_the_author_meta('display_name', $author));
|
|
$left = '<div class="meta-left">' . $name
|
|
. '<span class="icon-spot"><span title="' . esc_attr(get_the_date('Y-m-d H:i:s', $post)) . '">' . esc_html(Cards::ago($post->post_date)) . '</span></span></div>';
|
|
$right = '<div class="meta-right">'
|
|
. '<item class="item item-view"><i class="fa far fa-eye mr3"></i>' . esc_html(jio_cut_count((int) get_post_meta($post->ID, '_jio_forum_views', true))) . '</item>'
|
|
. '<a class="item item-comment" href="#comments"><i class="fa far fa-comment mr3"></i>' . esc_html(jio_cut_count((int) $post->comment_count)) . '</a>'
|
|
. '</div>';
|
|
return '<div class="flex ac jsb forum-article-meta">' . $left . $right . '</div>';
|
|
}
|
|
|
|
private static function content(\WP_Post $post, bool $can_view): string {
|
|
$id = (int) $post->ID;
|
|
if (!$can_view) {
|
|
return '<div class="theme-box wp-posts-content"><div class="theme-box">' . self::locked($post) . '</div></div>';
|
|
}
|
|
$html = '<div class="theme-box wp-posts-content">' . apply_filters('the_content', $post->post_content) . '</div>';
|
|
$type = (string) get_post_meta($id, '_jio_forum_type', true);
|
|
if ($type === 'vote') {
|
|
$html .= self::vote($post);
|
|
}
|
|
return $html;
|
|
}
|
|
|
|
private static function locked(\WP_Post $post): string {
|
|
$id = (int) $post->ID;
|
|
$plate_id = (int) get_post_meta($id, '_jio_forum_plate_id', true);
|
|
$user_id = get_current_user_id();
|
|
$html = jio_get_null('抱歉!您暂无查看此内容的权限', 10, 'null-post.svg');
|
|
if ($user_id <= 0) {
|
|
return $html . '<p class="text-center"><a class="but jb-blue padding-lg" href="' . esc_url(jio_get_login_url(get_permalink($post))) . '"><i class="fa fas fa-sign-in-alt mr6"></i>登录后查看</a></p>';
|
|
}
|
|
$plate_rule = (string) get_post_meta($plate_id, '_jio_forum_view_limit', true);
|
|
$post_rule = (string) get_post_meta($id, '_jio_forum_view_limit', true);
|
|
$action = '';
|
|
if (in_array($plate_rule, ['points', 'pay'], true) && !Commerce::has_plate_entitlement($user_id, $plate_id)) {
|
|
$action = '<button type="button" class="but jb-blue padding-lg" data-forum-action="plate-unlock" data-id="' . $plate_id . '"><i class="fa fas fa-unlock-alt mr6"></i>解锁版块</button>';
|
|
} elseif (($plate_rule === 'follow' || $post_rule === 'follow') && !Access::follows($user_id, $plate_id)) {
|
|
$action = Cards::follow_button($plate_id, 'but jb-blue padding-lg');
|
|
} elseif (in_array($post_rule, ['points', 'pay'], true)) {
|
|
$action = '<button type="button" class="but jb-blue padding-lg" data-forum-action="unlock" data-id="' . $id . '"><i class="fa fas fa-unlock-alt mr6"></i>解锁帖子</button>';
|
|
}
|
|
return $html . ($action !== '' ? '<p class="text-center">' . $action . '</p>' : '');
|
|
}
|
|
|
|
private static function vote(\WP_Post $post): string {
|
|
$id = (int) $post->ID;
|
|
$options = (array) get_post_meta($id, '_jio_forum_vote_options', true);
|
|
if (!$options) return '';
|
|
$counts = (array) get_post_meta($id, '_jio_forum_vote_counts', true);
|
|
$total = (int) get_post_meta($id, '_jio_forum_vote_total', true);
|
|
$vote_type = (string) get_post_meta($id, '_jio_forum_vote_type', true);
|
|
$deadline = (int) get_post_meta($id, '_jio_forum_vote_deadline', true);
|
|
$expired = $deadline > 0 && current_time('timestamp') > $deadline;
|
|
$display = (string) jio_get_setting('jio_forum_vote_number_type', 'percentage');
|
|
$voted = get_current_user_id() > 0 && get_user_meta(get_current_user_id(), '_jio_forum_vote_' . $id, true) !== '';
|
|
$html = '<form class="vote-box forum-vote zib-widget" data-forum-form="vote" data-id="' . $id . '"><h2 class="mb20">' . esc_html((string) get_post_meta($id, '_jio_forum_vote_title', true) ?: '参与投票') . '</h2>';
|
|
foreach ($options as $index => $option) {
|
|
$count = (int) ($counts[$index] ?? 0);
|
|
$percent = $total > 0 ? (int) round(100 * $count / $total) : 0;
|
|
$html .= '<label class="vote-item"><input type="' . ($vote_type === 'multiple' ? 'checkbox' : 'radio') . '" name="' . ($vote_type === 'multiple' ? 'option[]' : 'option') . '" value="' . (int) $index . '"' . disabled($voted || $expired, true, false) . '><span class="vote-title">' . esc_html((string) $option) . '</span>';
|
|
if ($display !== 'hide') {
|
|
$html .= '<span class="vote-count em09 muted-2-color">' . esc_html($display === 'number' ? (string) $count : $percent . '%') . '</span>';
|
|
}
|
|
$html .= '<span class="vote-progress" style="width:' . $percent . '%"></span></label>';
|
|
}
|
|
$html .= '<p class="jio-forum__message" role="status"></p>';
|
|
if ($voted) $html .= '<p class="text-center muted-2-color">已投票</p>';
|
|
elseif ($expired) $html .= '<p class="text-center muted-2-color">投票已截止</p>';
|
|
else $html .= '<button type="submit" class="but jb-blue padding-lg"><i class="fa fas fa-check mr6"></i>提交投票</button>';
|
|
return $html . '</form>';
|
|
}
|
|
|
|
private static function footer(\WP_Post $post): string {
|
|
$id = (int) $post->ID;
|
|
$user_id = get_current_user_id();
|
|
$plate_id = (int) get_post_meta($id, '_jio_forum_plate_id', true);
|
|
$html = '<div class="article-tags mb20 em09">';
|
|
foreach (is_array($terms = wp_get_post_terms($id, Core::TOPIC)) ? $terms : [] as $term) {
|
|
$html .= '<a class="but radius mm3 c-red" href="' . esc_url(get_term_link($term)) . '"><i class="fa fas fa-hashtag mr6"></i>' . esc_html($term->name) . '</a>';
|
|
}
|
|
foreach (is_array($tags = wp_get_post_terms($id, Core::TAG)) ? $tags : [] as $term) {
|
|
$html .= '<a class="but radius mm3" href="' . esc_url(get_term_link($term)) . '"><i class="fa fas fa-tag mr6"></i>' . esc_html($term->name) . '</a>';
|
|
}
|
|
$html .= '</div>';
|
|
$html .= '<div class="forum-article-footer flex ac jsb mb10 footer-actions"><div class="left flex ac">';
|
|
if (jio_get_setting('jio_forum_score_enabled', true)) {
|
|
$score = (int) get_post_meta($id, '_jio_forum_score', true);
|
|
$html .= '<button type="button" class="item btn-score" data-forum-action="score" data-direction="up" data-id="' . $id . '"><i class="fa far fa-thumbs-up"></i><text>' . esc_html(jio_cut_count($score)) . '</text></button>';
|
|
$html .= '<button type="button" class="item btn-score" data-forum-action="score" data-direction="down" data-id="' . $id . '"><i class="fa far fa-thumbs-down"></i></button>';
|
|
}
|
|
$html .= '</div><div class="right flex ac">';
|
|
if (function_exists('jio_get_post_share_btn')) $html .= jio_get_post_share_btn($post, 'item', true, false);
|
|
$html .= '<button type="button" class="item" data-forum-action="favorite" data-id="' . $id . '"><i class="fa far fa-bookmark"></i><text>收藏</text></button>';
|
|
$html .= '<button type="button" class="item" data-forum-action="report" data-id="' . $id . '"><i class="fa far fa-flag"></i><text>举报</text></button>';
|
|
if (Access::can_manage_post($user_id, $id)) {
|
|
$html .= '<a class="item" href="' . esc_url(Core::edit_url($id)) . '"><i class="fa far fa-edit"></i><text>编辑</text></a>';
|
|
$html .= '<button type="button" class="item" data-forum-action="delete" data-id="' . $id . '"><i class="fa far fa-trash-alt"></i><text>删除</text></button>';
|
|
}
|
|
$html .= '</div></div>';
|
|
$html .= '<div class="single-footer text-center mb20">';
|
|
if (Access::is_moderator($user_id, $plate_id)) {
|
|
foreach ([['essence', '精华'], ['pin', '置顶'], ['audit', '审核'], ['comments', $post->comment_status === 'open' ? '关闭回复' : '开放回复'], ['move', '移动版块']] as [$action, $label]) {
|
|
$html .= '<button type="button" class="but hollow px12 mr6" data-forum-action="' . $action . '" data-id="' . $id . '">' . esc_html($label) . '</button>';
|
|
}
|
|
}
|
|
$html .= '</div>';
|
|
if (get_post_meta($id, '_jio_forum_type', true) === 'question' && get_post_meta($id, '_jio_forum_adopted_comment', true)) {
|
|
$html .= '<p class="muted-2-color em09 mb20"><i class="fa fas fa-check-circle c-green mr6"></i>已采纳回复</p>';
|
|
}
|
|
return $html;
|
|
}
|
|
|
|
private static function comments(int $post_id): void {
|
|
echo '<div class="theme-box box-body radius8 main-shadow comments-card mb20">';
|
|
View::replies($post_id);
|
|
echo '</div>';
|
|
}
|
|
}
|