导入 AKSRT 主题开发源码
This commit is contained in:
1 parent
9681dd3d48
commit
1268a55dd0
595 files changed
+127705
No files matched your search
@@ -0,0 +1,419 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait AKSRT_Forum_Board_Service_Part_1 {
|
||||
|
||||
public function create_plate(array $data): int {
|
||||
$title = sanitize_text_field((string) ($data['name'] ?? ''));
|
||||
if ($title === '') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$plate_id = wp_insert_post([
|
||||
'post_type' => 'aksrt_forum_plate',
|
||||
'post_title' => $title,
|
||||
'post_content' => wp_kses_post((string) ($data['content'] ?? '')),
|
||||
'post_excerpt' => sanitize_textarea_field((string) ($data['description'] ?? '')),
|
||||
'post_status' => 'publish',
|
||||
'post_parent' => (int) ($data['parent_id'] ?? 0),
|
||||
'post_author' => (int) ($data['author_id'] ?? get_current_user_id()),
|
||||
'menu_order' => (int) ($data['sort_order'] ?? 0),
|
||||
], true);
|
||||
|
||||
if (is_wp_error($plate_id)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$icon = sanitize_text_field((string) ($data['icon'] ?? ''));
|
||||
if ($icon !== '') {
|
||||
update_post_meta($plate_id, '_aksrt_icon', $icon);
|
||||
}
|
||||
|
||||
update_post_meta($plate_id, '_aksrt_sort_order', (int) ($data['sort_order'] ?? 0));
|
||||
update_post_meta($plate_id, '_aksrt_posts_count', 0);
|
||||
update_post_meta($plate_id, '_aksrt_reply_count', 0);
|
||||
update_post_meta($plate_id, '_aksrt_follow_count', 0);
|
||||
update_post_meta($plate_id, '_aksrt_plate_views', 0);
|
||||
|
||||
if (!empty($data['plate_cat'])) {
|
||||
wp_set_object_terms($plate_id, [(int) $data['plate_cat']], 'aksrt_plate_cat');
|
||||
}
|
||||
|
||||
// Also create a corresponding taxonomy term for backward compat
|
||||
$term_result = wp_insert_term($title, 'aksrt_forum_board', [
|
||||
'description' => sanitize_textarea_field((string) ($data['description'] ?? '')),
|
||||
]);
|
||||
if (!is_wp_error($term_result)) {
|
||||
$term_id = (int) $term_result['term_id'];
|
||||
update_term_meta($term_id, '_aksrt_sort_order', (int) ($data['sort_order'] ?? 0));
|
||||
if ($icon !== '') {
|
||||
update_term_meta($term_id, '_aksrt_icon', $icon);
|
||||
}
|
||||
update_post_meta($plate_id, '_aksrt_migrated_from_term', $term_id);
|
||||
}
|
||||
|
||||
return $plate_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update plate.
|
||||
*/
|
||||
public function update_plate(int $plate_id, array $data): bool {
|
||||
$post = get_post($plate_id);
|
||||
if (!$post || $post->post_type !== 'aksrt_forum_plate') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$update = ['ID' => $plate_id];
|
||||
|
||||
if (isset($data['name'])) {
|
||||
$name = sanitize_text_field((string) $data['name']);
|
||||
if ($name !== '') {
|
||||
$update['post_title'] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['description'])) {
|
||||
$update['post_excerpt'] = sanitize_textarea_field((string) $data['description']);
|
||||
}
|
||||
|
||||
if (isset($data['content'])) {
|
||||
$update['post_content'] = wp_kses_post((string) $data['content']);
|
||||
}
|
||||
|
||||
if (isset($data['parent_id'])) {
|
||||
$update['post_parent'] = (int) $data['parent_id'];
|
||||
}
|
||||
|
||||
if (isset($data['sort_order'])) {
|
||||
$update['menu_order'] = (int) $data['sort_order'];
|
||||
update_post_meta($plate_id, '_aksrt_sort_order', (int) $data['sort_order']);
|
||||
}
|
||||
|
||||
if (isset($data['author_id'])) {
|
||||
$update['post_author'] = (int) $data['author_id'];
|
||||
}
|
||||
|
||||
$result = wp_update_post($update, true);
|
||||
if (is_wp_error($result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($data['icon'])) {
|
||||
update_post_meta($plate_id, '_aksrt_icon', sanitize_text_field((string) $data['icon']));
|
||||
}
|
||||
|
||||
if (isset($data['allow_view'])) {
|
||||
$allowed_views = ['public', 'login', 'role', 'follow'];
|
||||
$view = in_array($data['allow_view'], $allowed_views, true) ? $data['allow_view'] : 'public';
|
||||
update_post_meta($plate_id, '_aksrt_allow_view', $view);
|
||||
}
|
||||
|
||||
// Sync to legacy taxonomy term if exists
|
||||
$term_id = (int) get_post_meta($plate_id, '_aksrt_migrated_from_term', true);
|
||||
if ($term_id > 0) {
|
||||
$term_update = [];
|
||||
if (isset($update['post_title'])) {
|
||||
$term_update['name'] = $update['post_title'];
|
||||
}
|
||||
if (isset($update['post_excerpt'])) {
|
||||
$term_update['description'] = $update['post_excerpt'];
|
||||
}
|
||||
if (!empty($term_update)) {
|
||||
wp_update_term($term_id, 'aksrt_forum_board', $term_update);
|
||||
}
|
||||
if (isset($data['icon'])) {
|
||||
update_term_meta($term_id, '_aksrt_icon', sanitize_text_field((string) $data['icon']));
|
||||
}
|
||||
if (isset($data['sort_order'])) {
|
||||
update_term_meta($term_id, '_aksrt_sort_order', (int) $data['sort_order']);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete plate (with cascade option).
|
||||
*/
|
||||
public function delete_plate(int $plate_id, string $cascade = 'trash', int $move_to = 0): bool {
|
||||
$post = get_post($plate_id);
|
||||
if (!$post || $post->post_type !== 'aksrt_forum_plate') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$topic_ids = get_posts([
|
||||
'post_type' => 'aksrt_forum_topic',
|
||||
'meta_key' => '_aksrt_plate_id',
|
||||
'meta_value' => $plate_id,
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'post_status' => 'any',
|
||||
]);
|
||||
|
||||
if (!empty($topic_ids)) {
|
||||
if ($cascade === 'move' && $move_to > 0 && $move_to !== $plate_id) {
|
||||
$target = get_post($move_to);
|
||||
if ($target && $target->post_type === 'aksrt_forum_plate') {
|
||||
foreach ($topic_ids as $tid) {
|
||||
update_post_meta((int) $tid, '_aksrt_plate_id', $move_to);
|
||||
}
|
||||
$this->forum->sync_plate_counts($move_to);
|
||||
}
|
||||
} else {
|
||||
foreach ($topic_ids as $tid) {
|
||||
wp_trash_post((int) $tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle child plates (move to top-level)
|
||||
$children = get_posts([
|
||||
'post_type' => 'aksrt_forum_plate',
|
||||
'post_parent' => $plate_id,
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'post_status' => 'any',
|
||||
]);
|
||||
foreach ($children as $child_id) {
|
||||
wp_update_post(['ID' => (int) $child_id, 'post_parent' => 0]);
|
||||
}
|
||||
|
||||
// Remove legacy taxonomy term if linked
|
||||
$term_id = (int) get_post_meta($plate_id, '_aksrt_migrated_from_term', true);
|
||||
if ($term_id > 0) {
|
||||
wp_delete_term($term_id, 'aksrt_forum_board');
|
||||
}
|
||||
|
||||
wp_delete_post($plate_id, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorder plates.
|
||||
*/
|
||||
public function reorder_plates(array $order_map): bool {
|
||||
foreach ($order_map as $plate_id => $sort_order) {
|
||||
$plate_id = (int) $plate_id;
|
||||
if ($plate_id <= 0) continue;
|
||||
|
||||
wp_update_post(['ID' => $plate_id, 'menu_order' => (int) $sort_order]);
|
||||
update_post_meta($plate_id, '_aksrt_sort_order', (int) $sort_order);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve plate_id for a topic (reads _aksrt_plate_id meta, falls back to taxonomy).
|
||||
*/
|
||||
public function get_topic_plate_id(int $topic_id): int {
|
||||
$plate_id = (int) get_post_meta($topic_id, '_aksrt_plate_id', true);
|
||||
if ($plate_id > 0) {
|
||||
return $plate_id;
|
||||
}
|
||||
|
||||
// Fallback: find plate via legacy taxonomy term
|
||||
$terms = get_the_terms($topic_id, 'aksrt_forum_board');
|
||||
if (!$terms || is_wp_error($terms)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$term = $terms[0];
|
||||
// Look up plate post by migrated_from_term meta
|
||||
$plates = get_posts([
|
||||
'post_type' => 'aksrt_forum_plate',
|
||||
'meta_key' => '_aksrt_migrated_from_term',
|
||||
'meta_value' => (string) $term->term_id,
|
||||
'numberposts' => 1,
|
||||
'fields' => 'ids',
|
||||
'post_status' => 'publish',
|
||||
]);
|
||||
|
||||
if (!empty($plates)) {
|
||||
$resolved = (int) $plates[0];
|
||||
update_post_meta($topic_id, '_aksrt_plate_id', $resolved);
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user is a plate moderator (CPT-based).
|
||||
*/
|
||||
public function is_plate_moderator(int $user_id, int $plate_id): bool {
|
||||
if ($user_id <= 0 || $plate_id <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->forum->is_forum_admin($user_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$post = get_post($plate_id);
|
||||
if ($post && (int) $post->post_author === $user_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$moderators = get_post_meta($plate_id, '_aksrt_moderators', true);
|
||||
if (is_array($moderators) && in_array($user_id, $moderators, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plate view (topics + moderators + pagination) for frontend.
|
||||
*/
|
||||
public function get_plate_view(int $plate_id, array $args = []): array {
|
||||
$plate = $this->get_plate($plate_id);
|
||||
if (!$plate) {
|
||||
return [
|
||||
'plate' => null,
|
||||
'topics' => [],
|
||||
'sticky_topics' => [],
|
||||
'moderators' => [],
|
||||
'pagination' => ['page' => 1, 'max_pages' => 1, 'total' => 0],
|
||||
'permissions' => [],
|
||||
'children' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$user_id = (int) ($args['user_id'] ?? get_current_user_id());
|
||||
$paged = max(1, (int) ($args['paged'] ?? 1));
|
||||
|
||||
$topics = $this->forum->get_topic_list([
|
||||
'plate_id' => $plate_id,
|
||||
'page' => $paged,
|
||||
'limit' => 20,
|
||||
]);
|
||||
|
||||
// Global + super + plate sticky
|
||||
$sticky_topics = $this->get_sticky_topics_for_plate($plate_id);
|
||||
|
||||
$children = get_posts([
|
||||
'post_type' => 'aksrt_forum_plate',
|
||||
'post_parent' => $plate_id,
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 50,
|
||||
'orderby' => 'menu_order',
|
||||
'order' => 'ASC',
|
||||
]);
|
||||
|
||||
$child_plates = [];
|
||||
foreach ($children as $child) {
|
||||
$child_plates[] = $this->build_plate_payload($child);
|
||||
}
|
||||
|
||||
return [
|
||||
'plate' => $plate,
|
||||
'topics' => $topics['items'],
|
||||
'sticky_topics' => $sticky_topics,
|
||||
'moderators' => $this->get_plate_moderator_profiles($plate_id),
|
||||
'pagination' => [
|
||||
'page' => $topics['page'],
|
||||
'max_pages' => $topics['max_pages'],
|
||||
'total' => $topics['total'],
|
||||
],
|
||||
'permissions' => [
|
||||
'can_compose' => $user_id > 0 && $this->can_compose_in_plate($user_id, $plate_id),
|
||||
'can_apply_moderator' => $user_id > 0 && $this->forum->forum_user_can($user_id, 'moderator_apply'),
|
||||
'can_moderate' => $this->forum->forum_user_can($user_id, 'topic_moderate', $plate_id),
|
||||
'can_manage_plate' => $this->forum->forum_user_can($user_id, 'plate_manage', $plate_id),
|
||||
'can_manage_mods' => $this->forum->forum_user_can($user_id, 'moderator_manage', $plate_id),
|
||||
'user_tier' => $this->forum->get_user_plate_tier($user_id, $plate_id),
|
||||
],
|
||||
'children' => $child_plates,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sticky topics for a plate (global + super + plate-level).
|
||||
*/
|
||||
private function get_sticky_topics_for_plate(int $plate_id): array {
|
||||
$global_sticky = get_posts([
|
||||
'post_type' => 'aksrt_forum_topic',
|
||||
'post_status' => 'publish',
|
||||
'meta_key' => '_aksrt_sticky',
|
||||
'meta_value' => AKSRT_Forum::STICKY_GLOBAL,
|
||||
'posts_per_page' => 5,
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
]);
|
||||
|
||||
$plate_sticky = get_posts([
|
||||
'post_type' => 'aksrt_forum_topic',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 10,
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
'meta_query' => [
|
||||
'relation' => 'AND',
|
||||
['key' => '_aksrt_plate_id', 'value' => $plate_id, 'type' => 'NUMERIC'],
|
||||
['key' => '_aksrt_sticky', 'value' => 0, 'compare' => '>', 'type' => 'NUMERIC'],
|
||||
['key' => '_aksrt_sticky', 'value' => AKSRT_Forum::STICKY_GLOBAL, 'compare' => '<', 'type' => 'NUMERIC'],
|
||||
],
|
||||
]);
|
||||
|
||||
$items = [];
|
||||
$seen = [];
|
||||
|
||||
foreach (array_merge($global_sticky, $plate_sticky) as $post) {
|
||||
if (isset($seen[$post->ID])) continue;
|
||||
$seen[$post->ID] = true;
|
||||
$items[] = $this->forum->build_topic_payload($post);
|
||||
}
|
||||
|
||||
return array_slice($items, 0, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get moderator user profiles for a plate.
|
||||
*/
|
||||
private function get_plate_moderator_profiles(int $plate_id): array {
|
||||
$post = get_post($plate_id);
|
||||
$profiles = [];
|
||||
|
||||
// Author = super moderator
|
||||
if ($post && $post->post_author > 0) {
|
||||
$user = get_userdata((int) $post->post_author);
|
||||
if ($user) {
|
||||
$profiles[] = [
|
||||
'id' => (int) $user->ID,
|
||||
'name' => $user->display_name,
|
||||
'avatar' => function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user->ID, 40) : get_avatar_url($user->ID, ['size' => 40]),
|
||||
'role' => 'plate_author',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$moderators = get_post_meta($plate_id, '_aksrt_moderators', true);
|
||||
if (!is_array($moderators)) {
|
||||
return $profiles;
|
||||
}
|
||||
|
||||
$roles = get_post_meta($plate_id, '_aksrt_moderator_roles', true);
|
||||
if (!is_array($roles)) {
|
||||
$roles = [];
|
||||
}
|
||||
|
||||
foreach ($moderators as $uid) {
|
||||
$uid = (int) $uid;
|
||||
if ($post && $uid === (int) $post->post_author) continue;
|
||||
|
||||
$user = get_userdata($uid);
|
||||
if (!$user) continue;
|
||||
|
||||
$profiles[] = [
|
||||
'id' => $uid,
|
||||
'name' => $user->display_name,
|
||||
'avatar' => function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($uid, 40) : get_avatar_url($uid, ['size' => 40]),
|
||||
'role' => (string) ($roles[$uid] ?? 'moderator'),
|
||||
];
|
||||
}
|
||||
|
||||
return $profiles;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait AKSRT_Forum_Board_Service_Part_2 {
|
||||
|
||||
|
||||
/**
|
||||
* Get board page view (统一入口 CPT/taxonomy).
|
||||
*/
|
||||
public function get_board_view(int $board_id, array $args = []): array {
|
||||
$user_id = (int) ($args['user_id'] ?? get_current_user_id());
|
||||
$page = max(1, (int) ($args['paged'] ?? 1));
|
||||
|
||||
if ($this->forum->is_plate_cpt_active()) {
|
||||
return $this->get_plate_view_compat($board_id, $user_id, $page);
|
||||
}
|
||||
|
||||
$board = get_term($board_id, 'aksrt_forum_board');
|
||||
|
||||
if (!$board || is_wp_error($board)) {
|
||||
return [
|
||||
'board' => null,
|
||||
'topics' => [],
|
||||
'sticky_topics' => [],
|
||||
'moderators' => [],
|
||||
'pagination' => ['page' => 1, 'max_pages' => 1, 'total' => 0],
|
||||
'permissions' => [],
|
||||
'application' => null,
|
||||
'board_options' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$topics = $this->forum->get_topic_list([
|
||||
'board_id' => $board_id,
|
||||
'page' => $page,
|
||||
'limit' => 20,
|
||||
'status' => 'publish',
|
||||
'viewer_user_id' => $user_id,
|
||||
]);
|
||||
|
||||
$sticky_topics = $this->forum->get_sticky_topics_for_board($board_id);
|
||||
$board_link = get_term_link($board);
|
||||
|
||||
return [
|
||||
'board' => [
|
||||
'id' => $board_id,
|
||||
'name' => (string) $board->name,
|
||||
'description' => (string) $board->description,
|
||||
'content' => '',
|
||||
'icon' => (string) get_term_meta($board_id, '_aksrt_icon', true),
|
||||
'topic_count' => (int) $board->count,
|
||||
'reply_count' => 0,
|
||||
'today_reply_count' => 0,
|
||||
'views' => 0,
|
||||
'follow_count' => 0,
|
||||
'last_reply' => '',
|
||||
'last_post' => '',
|
||||
'url' => !is_wp_error($board_link) ? (string) $board_link : '',
|
||||
'thumbnail_url' => '',
|
||||
'author_id' => 0,
|
||||
'allow_view' => 'public',
|
||||
],
|
||||
'topics' => $topics['items'],
|
||||
'sticky_topics' => array_slice($sticky_topics, 0, 5),
|
||||
'moderators' => $this->forum->get_board_moderator_profiles($board_id),
|
||||
'pagination' => [
|
||||
'page' => $topics['page'],
|
||||
'max_pages' => $topics['max_pages'],
|
||||
'total' => $topics['total'],
|
||||
],
|
||||
'permissions' => [
|
||||
'can_compose' => $user_id > 0,
|
||||
'can_apply_moderator' => $user_id > 0 && !$this->forum->is_board_moderator($user_id, $board_id),
|
||||
'can_moderate' => $this->forum->is_board_moderator($user_id, $board_id),
|
||||
],
|
||||
'application' => $user_id > 0 ? $this->forum->get_board_application_for_user($user_id, $board_id) : null,
|
||||
'board_options' => $this->get_boards(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Plate CPT board view (backwards-compatible with get_board_view return shape).
|
||||
*/
|
||||
private function get_plate_view_compat(int $plate_id, int $user_id, int $page): array {
|
||||
$plate = $this->get_plate($plate_id);
|
||||
if (!$plate) {
|
||||
return [
|
||||
'board' => null,
|
||||
'topics' => [],
|
||||
'sticky_topics' => [],
|
||||
'moderators' => [],
|
||||
'pagination' => ['page' => 1, 'max_pages' => 1, 'total' => 0],
|
||||
'permissions' => [],
|
||||
'application' => null,
|
||||
'board_options' => [],
|
||||
];
|
||||
}
|
||||
|
||||
// Check access permission
|
||||
$allow_view = $plate['allow_view'] ?? 'public';
|
||||
if (!$this->can_view_plate($user_id, $plate_id, $allow_view)) {
|
||||
return [
|
||||
'board' => $plate,
|
||||
'topics' => [],
|
||||
'sticky_topics' => [],
|
||||
'moderators' => $this->get_plate_moderator_profiles($plate_id),
|
||||
'pagination' => ['page' => 1, 'max_pages' => 1, 'total' => 0],
|
||||
'permissions' => [
|
||||
'can_compose' => false,
|
||||
'can_apply_moderator' => false,
|
||||
'can_moderate' => false,
|
||||
'access_denied' => true,
|
||||
'access_reason' => $allow_view,
|
||||
],
|
||||
'application' => null,
|
||||
'board_options' => $this->get_boards(),
|
||||
];
|
||||
}
|
||||
|
||||
$topics = $this->forum->get_topic_list([
|
||||
'plate_id' => $plate_id,
|
||||
'page' => $page,
|
||||
'limit' => 20,
|
||||
'status' => 'publish',
|
||||
'viewer_user_id' => $user_id,
|
||||
]);
|
||||
|
||||
$sticky_topics = $this->get_sticky_topics_for_plate($plate_id);
|
||||
|
||||
return [
|
||||
'board' => $plate,
|
||||
'topics' => $topics['items'],
|
||||
'sticky_topics' => array_slice($sticky_topics, 0, 5),
|
||||
'moderators' => $this->get_plate_moderator_profiles($plate_id),
|
||||
'pagination' => [
|
||||
'page' => $topics['page'],
|
||||
'max_pages' => $topics['max_pages'],
|
||||
'total' => $topics['total'],
|
||||
],
|
||||
'permissions' => [
|
||||
'can_compose' => $user_id > 0 && $this->can_compose_in_plate($user_id, $plate_id),
|
||||
'can_apply_moderator' => $user_id > 0 && $this->forum->forum_user_can($user_id, 'moderator_apply'),
|
||||
'can_moderate' => $this->forum->forum_user_can($user_id, 'topic_moderate', $plate_id),
|
||||
'can_manage_plate' => $this->forum->forum_user_can($user_id, 'plate_manage', $plate_id),
|
||||
'can_manage_mods' => $this->forum->forum_user_can($user_id, 'moderator_manage', $plate_id),
|
||||
'user_tier' => $this->forum->get_user_plate_tier($user_id, $plate_id),
|
||||
],
|
||||
'application' => $user_id > 0 ? $this->forum->get_board_application_for_user($user_id, $plate_id) : null,
|
||||
'board_options' => $this->get_boards(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user can view a plate (§7.2 板块阅读权限).
|
||||
*/
|
||||
public function can_view_plate(int $user_id, int $plate_id, string $allow_view = ''): bool {
|
||||
if ($allow_view === '') {
|
||||
$allow_view = (string) (get_post_meta($plate_id, '_aksrt_allow_view', true) ?: 'public');
|
||||
}
|
||||
|
||||
if ($allow_view === 'public') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($user_id <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_plate_moderator($user_id, $plate_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($allow_view === 'login') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($allow_view === 'follow') {
|
||||
return $this->is_user_following_plate($user_id, $plate_id);
|
||||
}
|
||||
|
||||
if ($allow_view === 'role') {
|
||||
$allowed_roles = get_post_meta($plate_id, '_aksrt_allow_view_roles', true);
|
||||
if (!is_array($allowed_roles) || empty($allowed_roles)) {
|
||||
return true;
|
||||
}
|
||||
$user = get_userdata($user_id);
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
return !empty(array_intersect($user->roles, $allowed_roles));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user can compose in a plate (§7.2 板块发帖限制).
|
||||
*/
|
||||
public function can_compose_in_plate(int $user_id, int $plate_id): bool {
|
||||
if ($user_id <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_plate_moderator($user_id, $plate_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$add_limit = (string) (get_post_meta($plate_id, '_aksrt_add_limit', true) ?: 'all');
|
||||
if ($add_limit === 'all') {
|
||||
return true;
|
||||
}
|
||||
|
||||
$user = get_userdata($user_id);
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($add_limit === 'role') {
|
||||
$allowed_roles = get_post_meta($plate_id, '_aksrt_add_limit_roles', true);
|
||||
if (!is_array($allowed_roles) || empty($allowed_roles)) {
|
||||
return true;
|
||||
}
|
||||
return !empty(array_intersect($user->roles, $allowed_roles));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user is following a plate (§7.2 关注).
|
||||
*/
|
||||
public function is_user_following_plate(int $user_id, int $plate_id): bool {
|
||||
if ($user_id <= 0) {
|
||||
return false;
|
||||
}
|
||||
$follow_list = get_user_meta($user_id, 'aksrt_follow_plate', true);
|
||||
if (!is_array($follow_list)) {
|
||||
return false;
|
||||
}
|
||||
return in_array($plate_id, $follow_list, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle user follow on a plate (§7.2 关注/取消关注).
|
||||
*
|
||||
* @return array{followed:bool,follow_count:int}
|
||||
*/
|
||||
public function toggle_follow_plate(int $user_id, int $plate_id): array {
|
||||
if ($user_id <= 0) {
|
||||
return ['followed' => false, 'follow_count' => 0];
|
||||
}
|
||||
|
||||
$follow_list = get_user_meta($user_id, 'aksrt_follow_plate', true);
|
||||
if (!is_array($follow_list)) {
|
||||
$follow_list = [];
|
||||
}
|
||||
|
||||
$followed = in_array($plate_id, $follow_list, true);
|
||||
if ($followed) {
|
||||
$follow_list = array_values(array_diff($follow_list, [$plate_id]));
|
||||
} else {
|
||||
$follow_list[] = $plate_id;
|
||||
}
|
||||
|
||||
update_user_meta($user_id, 'aksrt_follow_plate', array_values(array_unique(array_map('intval', $follow_list))));
|
||||
|
||||
// Update plate follow_count
|
||||
$count = $this->count_plate_followers($plate_id);
|
||||
update_post_meta($plate_id, '_aksrt_follow_count', $count);
|
||||
|
||||
return [
|
||||
'followed' => !$followed,
|
||||
'follow_count' => $count,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Count followers for a plate.
|
||||
*/
|
||||
private function count_plate_followers(int $plate_id): int {
|
||||
global $wpdb;
|
||||
return (int) $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->usermeta}
|
||||
WHERE meta_key = 'aksrt_follow_plate'
|
||||
AND meta_value LIKE %s",
|
||||
'%i:' . $plate_id . ';%'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plates a user follows.
|
||||
*/
|
||||
public function get_user_followed_plates(int $user_id): array {
|
||||
$follow_list = get_user_meta($user_id, 'aksrt_follow_plate', true);
|
||||
if (!is_array($follow_list) || empty($follow_list)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$plates = get_posts([
|
||||
'post_type' => 'aksrt_forum_plate',
|
||||
'post__in' => array_map('intval', $follow_list),
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 100,
|
||||
'orderby' => 'post__in',
|
||||
]);
|
||||
|
||||
$result = [];
|
||||
foreach ($plates as $plate) {
|
||||
$result[] = $this->build_plate_payload($plate);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user's managed plates (plates where user is moderator/author).
|
||||
*
|
||||
* @return array{owned:array,moderating:array}
|
||||
*/
|
||||
public function get_user_managed_plates(int $user_id): array {
|
||||
$owned = [];
|
||||
$moderating = [];
|
||||
|
||||
if (!$this->forum->is_plate_cpt_active()) {
|
||||
return ['owned' => $owned, 'moderating' => $moderating];
|
||||
}
|
||||
|
||||
// Plates where user is author (超级版主)
|
||||
$own_plates = get_posts([
|
||||
'post_type' => 'aksrt_forum_plate',
|
||||
'author' => $user_id,
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 50,
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
]);
|
||||
foreach ($own_plates as $plate) {
|
||||
$owned[] = $this->build_plate_payload($plate);
|
||||
}
|
||||
|
||||
// Plates where user is a moderator (in _aksrt_moderators list)
|
||||
global $wpdb;
|
||||
$mod_plate_ids = $wpdb->get_col($wpdb->prepare(
|
||||
"SELECT pm.post_id FROM {$wpdb->postmeta} pm
|
||||
INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id AND p.post_type = 'aksrt_forum_plate' AND p.post_status = 'publish'
|
||||
WHERE pm.meta_key = '_aksrt_moderators'",
|
||||
));
|
||||
|
||||
foreach ($mod_plate_ids as $plate_id) {
|
||||
$plate_id = (int) $plate_id;
|
||||
// Skip plates already in owned
|
||||
if (in_array($plate_id, wp_list_pluck($own_plates, 'ID'), true)) {
|
||||
continue;
|
||||
}
|
||||
$mods = get_post_meta($plate_id, '_aksrt_moderators', true);
|
||||
if (is_array($mods) && in_array($user_id, $mods, true)) {
|
||||
$plate = get_post($plate_id);
|
||||
if ($plate) {
|
||||
$moderating[] = $this->build_plate_payload($plate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ['owned' => $owned, 'moderating' => $moderating];
|
||||
}
|
||||
|
||||
// ── Plate utilities (extracted from facade) ──
|
||||
|
||||
public function increment_plate_views(int $plate_id): void {
|
||||
if ($plate_id <= 0 || !$this->forum->is_plate_cpt_active()) {
|
||||
return;
|
||||
}
|
||||
$views = (int) get_post_meta($plate_id, '_aksrt_views', true);
|
||||
update_post_meta($plate_id, '_aksrt_views', $views + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a plate qualifies as "hot" (§7.2 热门版块).
|
||||
*/
|
||||
public function is_hot_plate(int $plate_id): bool {
|
||||
$config = $this->forum->get_config();
|
||||
$threshold = (int) ($config['other']['hot_plate_threshold'] ?? 100);
|
||||
$posts_count = (int) get_post_meta($plate_id, '_aksrt_posts_count', true);
|
||||
$follow_count = (int) get_post_meta($plate_id, '_aksrt_follow_count', true);
|
||||
return ($posts_count + $follow_count * 2) >= $threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plate categories (分区) with moderators.
|
||||
*/
|
||||
public function get_plate_categories(): array {
|
||||
$terms = get_terms([
|
||||
'taxonomy' => 'aksrt_plate_cat',
|
||||
'hide_empty' => false,
|
||||
'orderby' => 'name',
|
||||
'order' => 'ASC',
|
||||
]);
|
||||
|
||||
if (is_wp_error($terms) || empty($terms)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$categories = [];
|
||||
foreach ($terms as $term) {
|
||||
$term_id = (int) $term->term_id;
|
||||
$moderator_ids = get_term_meta($term_id, '_aksrt_cat_moderator', true);
|
||||
if (!is_array($moderator_ids)) {
|
||||
$moderator_ids = [];
|
||||
}
|
||||
|
||||
$categories[] = [
|
||||
'id' => $term_id,
|
||||
'name' => (string) $term->name,
|
||||
'description' => (string) $term->description,
|
||||
'slug' => (string) $term->slug,
|
||||
'count' => (int) $term->count,
|
||||
'parent_id' => (int) $term->parent,
|
||||
'moderators' => array_values(array_unique(array_filter(array_map('intval', $moderator_ids)))),
|
||||
'add_limit' => (string) (get_term_meta($term_id, '_aksrt_add_limit', true) ?: 'all'),
|
||||
'url' => (string) get_term_link($term),
|
||||
];
|
||||
}
|
||||
|
||||
return $categories;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait AKSRT_Forum_Board_Service_Part_3 {
|
||||
|
||||
|
||||
/**
|
||||
* Check if a user is a plate_cat moderator (分区版主).
|
||||
*/
|
||||
public function is_cat_moderator(int $user_id, int $cat_id): bool {
|
||||
if ($user_id <= 0) {
|
||||
return false;
|
||||
}
|
||||
if ($this->forum->is_forum_admin($user_id)) {
|
||||
return true;
|
||||
}
|
||||
$moderator_ids = get_term_meta($cat_id, '_aksrt_cat_moderator', true);
|
||||
if (!is_array($moderator_ids)) {
|
||||
return false;
|
||||
}
|
||||
return in_array($user_id, array_map('intval', $moderator_ids), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plates sorted by a specified dimension (§7.2 多维排序).
|
||||
*/
|
||||
public function get_sorted_plates(string $orderby = 'menu_order', string $order = 'ASC', int $limit = 50): array {
|
||||
$allowed = [
|
||||
'name' => 'title',
|
||||
'date' => 'date',
|
||||
'modified' => 'modified',
|
||||
'menu_order' => 'menu_order',
|
||||
'last_post' => '_aksrt_last_post',
|
||||
'last_reply' => '_aksrt_last_reply',
|
||||
'posts_count' => '_aksrt_posts_count',
|
||||
'reply_count' => '_aksrt_reply_count',
|
||||
'today_reply_count' => '_aksrt_today_reply_count',
|
||||
'views' => '_aksrt_views',
|
||||
'follow_count' => '_aksrt_follow_count',
|
||||
'rand' => 'rand',
|
||||
];
|
||||
|
||||
$query_args = [
|
||||
'post_type' => 'aksrt_forum_plate',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => $limit,
|
||||
'order' => strtoupper($order) === 'ASC' ? 'ASC' : 'DESC',
|
||||
];
|
||||
|
||||
if (isset($allowed[$orderby])) {
|
||||
$meta_key = $allowed[$orderby];
|
||||
if (str_starts_with($meta_key, '_aksrt_')) {
|
||||
$query_args['orderby'] = 'meta_value_num';
|
||||
$query_args['meta_key'] = $meta_key;
|
||||
} else {
|
||||
$query_args['orderby'] = $meta_key;
|
||||
}
|
||||
} else {
|
||||
$query_args['orderby'] = 'menu_order';
|
||||
}
|
||||
|
||||
$posts = get_posts($query_args);
|
||||
$plates = [];
|
||||
foreach ($posts as $post) {
|
||||
$plates[] = $this->build_plate_payload($post);
|
||||
}
|
||||
return $plates;
|
||||
}
|
||||
|
||||
public function maybe_migrate_boards(): void {
|
||||
if (get_option('aksrt_forum_boards_migrated')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$old_boards = get_option('aksrt_forum_boards', []);
|
||||
if (!is_array($old_boards) || empty($old_boards)) {
|
||||
update_option('aksrt_forum_boards_migrated', time());
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($old_boards as $board) {
|
||||
$name = sanitize_text_field((string) ($board['name'] ?? ''));
|
||||
if ($name === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$existing = get_terms([
|
||||
'taxonomy' => 'aksrt_forum_board',
|
||||
'name' => $name,
|
||||
'hide_empty' => false,
|
||||
'fields' => 'ids',
|
||||
]);
|
||||
|
||||
if (!is_wp_error($existing) && !empty($existing)) {
|
||||
$term_id = (int) $existing[0];
|
||||
} else {
|
||||
$result = wp_insert_term($name, 'aksrt_forum_board', [
|
||||
'description' => sanitize_textarea_field((string) ($board['description'] ?? '')),
|
||||
]);
|
||||
if (is_wp_error($result)) {
|
||||
continue;
|
||||
}
|
||||
$term_id = (int) $result['term_id'];
|
||||
}
|
||||
|
||||
$icon = sanitize_text_field((string) ($board['icon'] ?? ''));
|
||||
if ($icon !== '') {
|
||||
update_term_meta($term_id, '_aksrt_icon', $icon);
|
||||
}
|
||||
|
||||
update_term_meta($term_id, '_aksrt_sort_order', (int) ($board['sort_order'] ?? $term_id));
|
||||
|
||||
if (!empty($board['moderators']) && is_array($board['moderators'])) {
|
||||
update_term_meta($term_id, '_aksrt_moderators', array_map('intval', $board['moderators']));
|
||||
}
|
||||
}
|
||||
|
||||
update_option('aksrt_forum_boards_migrated', time());
|
||||
}
|
||||
|
||||
public function ajax_follow_plate(): void {
|
||||
check_ajax_referer('aksrt_forum_action', '_nonce');
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
if ($user_id <= 0) {
|
||||
wp_send_json_error(['message' => __('请先登录', 'aksrt')]);
|
||||
}
|
||||
|
||||
$plate_id = (int) ($_POST['plate_id'] ?? 0);
|
||||
if ($plate_id <= 0) {
|
||||
wp_send_json_error(['message' => __('版块不存在', 'aksrt')]);
|
||||
}
|
||||
|
||||
$result = $this->toggle_follow_plate($user_id, $plate_id);
|
||||
wp_send_json_success($result);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user