refactor(autoload): migrate theme classes to Composer PSR-4
Move 288 types from global AKSRT_* + inc/classes manual requires into
namespace AKSRT\{Domain} under src/AKSRT/, wire composer.json psr-4,
and slim bootstrap to helpers/modules/hooks only. Split multi-class
widget/gateway files, update admin contract audit paths, and document
the layout in ADR/README.
This commit is contained in:
1 parent
8f349b40fb
commit
aaf458af1c
404 files changed
+13602
-7705
No files matched your search
@@ -0,0 +1,416 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Forum;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait ViewBuilderPart1 {
|
||||
|
||||
public function get_user_managed_plates(int $user_id): array {
|
||||
return $this->forum->get_user_managed_plates($user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forum stats for a user (counts, cached per request).
|
||||
*
|
||||
* @return array{topic_count:int,reply_count:int,favorite_count:int,followed_plate_count:int,managed_plate_count:int}
|
||||
*/
|
||||
public function get_user_forum_stats(int $user_id): array {
|
||||
$topic_count = (int) count_user_posts($user_id, 'aksrt_forum_topic', true);
|
||||
|
||||
global $wpdb;
|
||||
$reply_count = (int) $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->comments} c
|
||||
INNER JOIN {$wpdb->posts} p ON p.ID = c.comment_post_ID
|
||||
WHERE c.user_id = %d AND p.post_type = 'aksrt_forum_topic' AND c.comment_approved = '1'",
|
||||
$user_id
|
||||
));
|
||||
|
||||
$fav_ids = get_user_meta($user_id, 'aksrt_favorite_forum_posts', true);
|
||||
$favorite_count = is_array($fav_ids) ? count(array_filter($fav_ids)) : 0;
|
||||
|
||||
$follow_ids = get_user_meta($user_id, 'aksrt_follow_plate', true);
|
||||
$followed_plate_count = is_array($follow_ids) ? count(array_filter($follow_ids)) : 0;
|
||||
|
||||
$managed = $this->forum->get_user_managed_plates($user_id);
|
||||
$managed_count = count($managed['owned']) + count($managed['moderating']);
|
||||
|
||||
return [
|
||||
'topic_count' => $topic_count,
|
||||
'reply_count' => $reply_count,
|
||||
'favorite_count' => $favorite_count,
|
||||
'followed_plate_count' => $followed_plate_count,
|
||||
'managed_plate_count' => $managed_count,
|
||||
];
|
||||
}
|
||||
|
||||
public function get_topic_view(int $topic_id, int $user_id = 0): array {
|
||||
$post = get_post($topic_id);
|
||||
if (!$post || $post->post_type !== 'aksrt_forum_topic') {
|
||||
return [
|
||||
'topic' => null,
|
||||
'board' => null,
|
||||
'permissions' => [],
|
||||
'board_options' => [],
|
||||
'visibility_options' => $this->forum->get_visibility_options(),
|
||||
'review_options' => $this->forum->get_review_options(),
|
||||
];
|
||||
}
|
||||
|
||||
$topic = $this->forum->build_topic_payload($post, $user_id);
|
||||
$boards = !empty($topic['boards']) ? $topic['boards'] : [];
|
||||
$board = !empty($boards[0]) ? $boards[0] : null;
|
||||
|
||||
return [
|
||||
'topic' => $topic,
|
||||
'board' => $board,
|
||||
'permissions' => [
|
||||
'can_edit' => $this->forum->can_edit_topic($user_id, $topic_id),
|
||||
'can_moderate' => $this->forum->can_moderate_topic($user_id, $topic_id),
|
||||
// 加锁语义统一到原生 comment_status(§7.3):comment_status=closed 或 _aksrt_locked=1 均不可回复。
|
||||
'can_reply' => comments_open($topic_id) && empty($topic['locked']),
|
||||
],
|
||||
'board_options' => $this->forum->get_boards(),
|
||||
'visibility_options' => $this->forum->get_visibility_options(),
|
||||
'review_options' => $this->forum->get_review_options(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get compose page view.
|
||||
*/
|
||||
public function get_compose_view(int $user_id, array $args = []): array {
|
||||
$board_id = (int) ($args['board_id'] ?? 0);
|
||||
|
||||
return [
|
||||
'boards' => $this->forum->get_boards(),
|
||||
'selected_board_id' => $board_id,
|
||||
'visibility_options' => $this->forum->get_visibility_options(),
|
||||
'publish_mode' => $this->forum->can_user_publish_directly($user_id, $board_id) ? 'publish' : 'pending',
|
||||
'draft_count' => $this->forum->get_user_draft_count($user_id),
|
||||
'drafts' => $this->forum->get_user_drafts($user_id, 10),
|
||||
'capabilities' => $this->forum->get_compose_capabilities($user_id),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get forum stats.
|
||||
*/
|
||||
public function get_stats(): array {
|
||||
$boards = $this->forum->get_boards();
|
||||
$topic_count = post_type_exists('aksrt_forum_topic')
|
||||
? (int) (wp_count_posts('aksrt_forum_topic')->publish ?? 0)
|
||||
: 0;
|
||||
$pending_topic_count = post_type_exists('aksrt_forum_topic')
|
||||
? (int) (wp_count_posts('aksrt_forum_topic')->pending ?? 0)
|
||||
: 0;
|
||||
|
||||
global $wpdb;
|
||||
$post_count = (int) $wpdb->get_var(
|
||||
"SELECT COUNT(*) FROM {$wpdb->comments} c
|
||||
INNER JOIN {$wpdb->posts} p ON c.comment_post_ID = p.ID
|
||||
WHERE p.post_type = 'aksrt_forum_topic' AND c.comment_approved = '1'"
|
||||
);
|
||||
|
||||
$moderator_count = 0;
|
||||
$pending_application_count = 0;
|
||||
foreach ($boards as $board) {
|
||||
$moderator_count += (int) ($board['moderator_count'] ?? 0);
|
||||
$pending_application_count += (int) ($board['pending_application_count'] ?? 0);
|
||||
}
|
||||
|
||||
return [
|
||||
'board_count' => count($boards),
|
||||
'topic_count' => $topic_count,
|
||||
'post_count' => $post_count,
|
||||
'pending_topic_count' => $pending_topic_count,
|
||||
'moderator_count' => $moderator_count,
|
||||
'pending_application_count' => $pending_application_count,
|
||||
];
|
||||
}
|
||||
|
||||
public function get_sticky_topics_for_board(int $board_id): array {
|
||||
$viewer_user_id = get_current_user_id();
|
||||
|
||||
// 先取本版块内的置顶帖(plate/super),再取全局置顶帖(跨版块)。
|
||||
$query = new \WP_Query([
|
||||
'post_type' => 'aksrt_forum_topic',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 20,
|
||||
'ignore_sticky_posts' => true,
|
||||
'no_found_rows' => true,
|
||||
'orderby' => 'meta_value_num date',
|
||||
'order' => 'DESC DESC',
|
||||
'meta_query' => [
|
||||
'relation' => 'OR',
|
||||
[
|
||||
'key' => '_aksrt_sticky',
|
||||
'value' => [Forum::STICKY_PLATE, Forum::STICKY_SUPER],
|
||||
'compare' => 'IN',
|
||||
'type' => 'NUMERIC',
|
||||
],
|
||||
[
|
||||
'key' => '_aksrt_sticky',
|
||||
'value' => Forum::STICKY_GLOBAL,
|
||||
'compare' => '=',
|
||||
'type' => 'NUMERIC',
|
||||
],
|
||||
],
|
||||
'tax_query' => [[
|
||||
'taxonomy' => 'aksrt_forum_board',
|
||||
'field' => 'term_id',
|
||||
'terms' => $board_id,
|
||||
]],
|
||||
]);
|
||||
|
||||
$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 (!$topic['can_view']) {
|
||||
continue;
|
||||
}
|
||||
$items[] = $topic;
|
||||
}
|
||||
wp_reset_postdata();
|
||||
|
||||
// 全局置顶帖可能不归属本版块,单独查询后合并去重。
|
||||
$global = new \WP_Query([
|
||||
'post_type' => 'aksrt_forum_topic',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 5,
|
||||
'ignore_sticky_posts' => true,
|
||||
'no_found_rows' => true,
|
||||
'meta_query' => [[
|
||||
'key' => '_aksrt_sticky',
|
||||
'value' => Forum::STICKY_GLOBAL,
|
||||
'compare' => '=',
|
||||
'type' => 'NUMERIC',
|
||||
]],
|
||||
]);
|
||||
|
||||
$existing_ids = array_map(static fn($t) => (int) $t['id'], $items);
|
||||
while ($global->have_posts()) {
|
||||
$global->the_post();
|
||||
$post = get_post(get_the_ID());
|
||||
if (!$post || in_array((int) $post->ID, $existing_ids, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$topic = $this->forum->build_topic_payload($post, $viewer_user_id);
|
||||
if (!$topic['can_view']) {
|
||||
continue;
|
||||
}
|
||||
$items[] = $topic;
|
||||
}
|
||||
wp_reset_postdata();
|
||||
|
||||
// 按 sticky_level DESC, date DESC 排序(全局 > 超级 > 版块)。
|
||||
usort($items, function (array $a, array $b): int {
|
||||
$la = (int) ($a['sticky_level'] ?? 0);
|
||||
$lb = (int) ($b['sticky_level'] ?? 0);
|
||||
if ($la !== $lb) {
|
||||
return $lb <=> $la;
|
||||
}
|
||||
return strcmp((string) $b['date'], (string) $a['date']);
|
||||
});
|
||||
|
||||
return array_slice($items, 0, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get topic detail payload for admin.
|
||||
*/
|
||||
public function get_topic_detail(int $topic_id, int $viewer_user_id = 0): array {
|
||||
$post = get_post($topic_id);
|
||||
if (!$post || $post->post_type !== 'aksrt_forum_topic') {
|
||||
return [
|
||||
'topic' => null,
|
||||
'options' => [
|
||||
'boards' => $this->forum->get_boards(),
|
||||
'visibility' => $this->forum->get_visibility_options(),
|
||||
'review' => $this->forum->get_review_options(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'topic' => $this->forum->build_topic_payload($post, $viewer_user_id),
|
||||
'options' => [
|
||||
'boards' => $this->forum->get_boards(),
|
||||
'visibility' => $this->forum->get_visibility_options(),
|
||||
'review' => $this->forum->get_review_options(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// ── Tags + Search (extracted from facade) ──
|
||||
|
||||
public function get_tags(): array {
|
||||
$terms = get_terms([
|
||||
'taxonomy' => 'aksrt_forum_tag',
|
||||
'hide_empty' => true,
|
||||
'orderby' => 'count',
|
||||
'order' => 'DESC',
|
||||
]);
|
||||
|
||||
if (is_wp_error($terms)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_map(static function ($term): array {
|
||||
$url = get_term_link($term);
|
||||
|
||||
return [
|
||||
'id' => (int) $term->term_id,
|
||||
'name' => (string) $term->name,
|
||||
'count' => (int) $term->count,
|
||||
'url' => !is_wp_error($url) ? (string) $url : '',
|
||||
];
|
||||
}, $terms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tag archive page payload.
|
||||
*/
|
||||
public function get_tag_archive_view(array $args = []): array {
|
||||
$term_id = (int) ($args['term_id'] ?? 0);
|
||||
$paged = max(1, (int) ($args['paged'] ?? 1));
|
||||
$per_page = 20;
|
||||
|
||||
$term = get_term($term_id, 'aksrt_forum_tag');
|
||||
if (is_wp_error($term) || !$term) {
|
||||
return ['topics' => [], 'term' => null, 'total' => 0, 'max_pages' => 0];
|
||||
}
|
||||
|
||||
$query = new \WP_Query([
|
||||
'post_type' => 'aksrt_forum_topic',
|
||||
'posts_per_page' => $per_page,
|
||||
'paged' => $paged,
|
||||
'post_status' => 'publish',
|
||||
'tax_query' => [[
|
||||
'taxonomy' => 'aksrt_forum_tag',
|
||||
'field' => 'term_id',
|
||||
'terms' => $term_id,
|
||||
]],
|
||||
]);
|
||||
|
||||
$topics = [];
|
||||
while ($query->have_posts()) {
|
||||
$query->the_post();
|
||||
$post = get_post(get_the_ID());
|
||||
if ($post) {
|
||||
$topics[] = $this->forum->build_topic_payload($post, get_current_user_id());
|
||||
}
|
||||
}
|
||||
wp_reset_postdata();
|
||||
|
||||
return [
|
||||
'topics' => $topics,
|
||||
'term' => [
|
||||
'id' => (int) $term->term_id,
|
||||
'name' => (string) $term->name,
|
||||
'description' => (string) $term->description,
|
||||
'count' => (int) $term->count,
|
||||
],
|
||||
'total' => (int) $query->found_posts,
|
||||
'max_pages' => (int) $query->max_num_pages,
|
||||
];
|
||||
}
|
||||
|
||||
public function increment_view_count(int $topic_id): void {
|
||||
if ($topic_id <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$views = (int) get_post_meta($topic_id, '_aksrt_views', true);
|
||||
update_post_meta($topic_id, '_aksrt_views', $views + 1);
|
||||
}
|
||||
|
||||
public function ajax_search_boards(): void {
|
||||
$search = sanitize_text_field((string) ($_GET['q'] ?? $_POST['q'] ?? ''));
|
||||
$limit = min(50, max(5, (int) ($_GET['limit'] ?? 20)));
|
||||
|
||||
if ($this->forum->is_plate_cpt_active()) {
|
||||
$args = [
|
||||
'post_type' => 'aksrt_forum_plate',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => $limit,
|
||||
'no_found_rows' => true,
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
];
|
||||
if ($search !== '') {
|
||||
$args['s'] = $search;
|
||||
}
|
||||
$query = new \WP_Query($args);
|
||||
$results = [];
|
||||
while ($query->have_posts()) {
|
||||
$query->the_post();
|
||||
$p = get_post();
|
||||
if ($p) {
|
||||
$results[] = ['id' => $p->ID, 'name' => $p->post_title];
|
||||
}
|
||||
}
|
||||
wp_reset_postdata();
|
||||
} else {
|
||||
$args = [
|
||||
'taxonomy' => 'aksrt_forum_board',
|
||||
'hide_empty' => false,
|
||||
'number' => $limit,
|
||||
'orderby' => 'name',
|
||||
'order' => 'ASC',
|
||||
];
|
||||
if ($search !== '') {
|
||||
$args['search'] = $search;
|
||||
}
|
||||
$terms = get_terms($args);
|
||||
$results = [];
|
||||
if (!is_wp_error($terms)) {
|
||||
foreach ($terms as $term) {
|
||||
$results[] = ['id' => (int) $term->term_id, 'name' => (string) $term->name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json_success($results);
|
||||
}
|
||||
|
||||
public function ajax_search_tags(): void {
|
||||
$search = sanitize_text_field((string) ($_GET['q'] ?? $_POST['q'] ?? ''));
|
||||
$limit = min(50, max(5, (int) ($_GET['limit'] ?? 20)));
|
||||
|
||||
$args = [
|
||||
'taxonomy' => 'aksrt_forum_tag',
|
||||
'hide_empty' => false,
|
||||
'number' => $limit,
|
||||
'orderby' => 'count',
|
||||
'order' => 'DESC',
|
||||
];
|
||||
if ($search !== '') {
|
||||
$args['search'] = $search;
|
||||
}
|
||||
|
||||
$terms = get_terms($args);
|
||||
$results = [];
|
||||
if (!is_wp_error($terms)) {
|
||||
foreach ($terms as $term) {
|
||||
$results[] = [
|
||||
'id' => (int) $term->term_id,
|
||||
'name' => (string) $term->name,
|
||||
'count' => (int) $term->count,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json_success($results);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user