Files
wordpress-theme-aksrt/templates/pages/page-messages.php
T

351 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Template Name: 消息中心
* Template Post Type: page
*
* 独立消息中心页面 — 两栏布局(左侧导航 + 右侧消息列表)
* 对齐 Zibll msg-center:未读 / 评论 / 系统 / 私信(可开关)
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
get_header();
\AKSRT\WidgetSystem\WidgetAreas::render('msg_top_fluid', 'fluid-widget-wrap');
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;
}
$messages_enabled = !class_exists('\AKSRT\Settings\Settings') || \AKSRT\Settings\Settings::message_enabled();
$pm_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::private_message_settings() : ['enabled' => false];
$pm_enabled = !empty($pm_settings['enabled']);
if (!$messages_enabled) :
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<div class="container messages-center messages-center--disabled">
<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>
<p class="messages-empty__hint"><?php esc_html_e('请联系管理员在后台「用户 → 消息通知」中启用。', 'aksrt'); ?></p>
</div>
</div>
</main>
<?php
\AKSRT\WidgetSystem\WidgetAreas::render('msg_bottom_fluid', 'fluid-widget-wrap');
get_footer();
return;
endif;
$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(wp_unslash((string) $_GET['msg_type'])) : 'unread';
// 类型分组:对齐 Zibll 主 tab 语义,同时兼容历史 type 键。
$type_groups = [
'unread' => null, // special: !read
'all' => null, // special: all
'comment' => ['comment_reply', 'comment_approved', 'pending_comments'],
'credits' => ['credits_added', 'credits_deducted'],
'pay' => ['pay', 'order_paid', 'order_refund', 'shop_order', 'after_sale'],
'vip' => ['vip', 'pay_vip', 'vip_expire', 'vip_upgrade'],
'medal' => ['medal', 'badge', 'level_up'],
'ban_appeal' => ['ban_appeal', 'user_ban', 'restriction'],
'system' => ['notice', 'download_ready', 'upload_moderation', 'verify_email', 'auth_apply', 'auth_apply_reply'],
'conversation' => ['conversation'],
];
if ($current_type === 'conversation' && !$pm_enabled) {
$current_type = 'unread';
}
$allowed_types = array_keys($type_groups);
if (!in_array($current_type, $allowed_types, true)) {
// 兼容旧 URL:msg_type=comment_reply 等
$mapped = 'all';
foreach ($type_groups as $group => $types) {
if (is_array($types) && in_array($current_type, $types, true)) {
$mapped = $group;
break;
}
}
$current_type = $mapped;
}
$filtered = $all_notifications;
if ($current_type === 'unread') {
$filtered = array_filter($filtered, static fn($n) => empty($n['read']));
} elseif ($current_type !== 'all' && $current_type !== 'conversation') {
$types = $type_groups[$current_type] ?? [];
$filtered = array_filter($filtered, static function ($n) use ($types) {
$t = (string) ($n['type'] ?? 'notice');
return in_array($t, $types, true);
});
}
$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,
'comment' => 0,
'credits' => 0,
'pay' => 0,
'vip' => 0,
'medal' => 0,
'ban_appeal' => 0,
'system' => 0,
'conversation' => 0,
];
foreach ($all_notifications as $n) {
$t = (string) ($n['type'] ?? 'notice');
if (empty($n['read'])) {
$type_counts['unread']++;
}
foreach (['comment', 'credits', 'pay', 'vip', 'medal', 'ban_appeal', 'system', 'conversation'] as $group) {
if (!empty($type_groups[$group]) && is_array($type_groups[$group]) && in_array($t, $type_groups[$group], true)) {
$type_counts[$group]++;
}
}
}
if ($pm_enabled && class_exists('\AKSRT\Messages\PrivateMessage')) {
// conversation count prefers live PM conversations when available
$pm_tmp = \AKSRT\Messages\PrivateMessage::get_instance();
if (method_exists($pm_tmp, 'get_conversations')) {
$convs = $pm_tmp->get_conversations($user_id, 100);
if (is_array($convs)) {
$type_counts['conversation'] = count($convs);
}
}
}
$nav_items = [
'unread' => __('未读消息', 'aksrt'),
'all' => __('全部消息', 'aksrt'),
'comment' => __('评论互动', 'aksrt'),
'credits' => __('积分动态', 'aksrt'),
'pay' => __('交易通知', 'aksrt'),
'vip' => __('会员动态', 'aksrt'),
'medal' => __('徽章等级', 'aksrt'),
'ban_appeal' => __('禁封申诉', 'aksrt'),
'system' => __('系统通知', 'aksrt'),
];
if ($pm_enabled) {
$nav_items['conversation'] = __('私信消息', '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',
'pay' => 'fas fa-receipt',
'order_paid' => 'fas fa-receipt',
'order_refund' => 'fas fa-undo',
'after_sale' => 'fas fa-undo-alt',
'vip' => 'fas fa-crown',
'pay_vip' => 'fas fa-crown',
'medal' => 'fas fa-medal',
'badge' => 'fas fa-certificate',
'ban_appeal' => 'fas fa-gavel',
'user_ban' => 'fas fa-ban',
'auth_apply' => 'fas fa-id-badge',
'auth_apply_reply' => 'fas fa-id-badge',
'notice' => 'fas fa-bell',
];
$messages_base = function_exists('aksrt_get_messages_page_url') ? aksrt_get_messages_page_url() : get_permalink();
if (!$messages_base) {
$messages_base = get_permalink();
}
?>
<main id="main-content" class="site-main content-shell" tabindex="-1">
<?php if (function_exists('aksrt_breadcrumbs')) { aksrt_breadcrumbs(); } ?>
<div class="container">
<header class="messages-center__header">
<h1 class="messages-center__title"><?php esc_html_e('消息中心', 'aksrt'); ?></h1>
<p class="messages-center__subtitle"><?php esc_html_e('查看站内通知与互动提醒', 'aksrt'); ?></p>
</header>
<div class="messages-center">
<aside class="messages-center__nav" aria-label="<?php esc_attr_e('消息分类', 'aksrt'); ?>">
<ul class="messages-nav__list">
<?php foreach ($nav_items as $key => $label) :
$count = (int) ($type_counts[$key] ?? 0);
// 始终显示核心 tab;其余无数据时可隐藏(保留 unread/all/system)
if (!in_array($key, ['unread', 'all', 'system', 'conversation'], true) && $count < 1) {
continue;
}
$is_active = $current_type === $key;
$tab_url = add_query_arg(['msg_type' => $key, 'msg_page' => 1], $messages_base);
?>
<li>
<a href="<?php echo esc_url($tab_url); ?>"
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>
<section class="messages-center__content">
<?php if ($current_type === 'conversation' && $pm_enabled) :
$pm = class_exists('\AKSRT\Messages\PrivateMessage') ? \AKSRT\Messages\PrivateMessage::get_instance() : null;
$conversations = $pm ? $pm->get_conversations($user_id, 30) : [];
$pm_nonce = wp_create_nonce('aksrt_pm');
$pm_placeholder = (string) ($pm_settings['placeholder'] ?? __('请输入消息...', 'aksrt'));
$pm_submit = (string) ($pm_settings['submit_text'] ?? __('发送', 'aksrt'));
?>
<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 !empty($conv['unread']) ? ' 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((string) ($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((string) ($conv['other_name'] ?? '')); ?>
<?php if (!empty($conv['unread'])) : ?>
<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((string) ($conv['last_message'] ?? ''), 0, 50)); ?></div>
</div>
<div class="aksrt-pm__time"><?php echo esc_html(!empty($conv['last_message_at']) ? mysql2date('m-d H:i', (string) $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" hidden>
<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 echo esc_attr($pm_placeholder); ?>"></textarea>
<button type="button" id="aksrt-pm-send" class="aksrt-pm__send-btn" aria-label="<?php echo esc_attr($pm_submit); ?>"><i class="fas fa-paper-plane"></i></button>
</div>
</div>
<?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';
?>
<article 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"><?php esc_html_e('新', 'aksrt'); ?></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>
</article>
<?php endforeach; ?>
</div>
<?php if ($total_pages > 1) : ?>
<nav class="messages-pagination" aria-label="<?php esc_attr_e('消息分页', 'aksrt'); ?>">
<?php for ($i = 1; $i <= $total_pages; $i++) :
$page_url = add_query_arg(['msg_type' => $current_type, 'msg_page' => $i], $messages_base);
?>
<?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; ?>
</nav>
<?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; ?>
</section>
</div>
</div>
</main>
<?php \AKSRT\WidgetSystem\WidgetAreas::render('msg_bottom_fluid', 'fluid-widget-wrap'); ?>
<?php get_footer(); ?>