Files
wordpress-theme-aksrt/src/AKSRT/Widgets/WidgetForumPlateLists.php
T
kate522 425ca6cd2e feat(forum): plate lists widget payload + REST
Shared query_payload for plate lists and
/forum/frontend/widgets/plates for remote refresh.
2026-07-20 20:50:58 +08:00

261 lines
9.8 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
/**
* Forum plate list (zibll zib_bbs_widget_ui_plate_lists + ajax).
*
* @package AKSRT
*/
namespace AKSRT\Widgets;
defined('ABSPATH') || exit;
class WidgetForumPlateLists extends \WP_Widget {
public function __construct() {
parent::__construct(
'aksrt_forum_plate_lists',
esc_html__('论坛 - 版块列表(Plate)', 'aksrt'),
[
'description' => esc_html__('展示论坛 plate 版块列表,支持排序/卡片/AJAX。', 'aksrt'),
'classname' => 'widget_aksrt_forum_plate_lists',
]
);
}
/**
* @return array
*/
public static function build_query_args(array $instance): array {
$limit = max(1, min(30, (int) ($instance['showposts'] ?? 8)));
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'menu_order'));
$filter = sanitize_key((string) ($instance['filter'] ?? ''));
$cat_raw = (string) ($instance['cat'] ?? '');
$cat_ids = array_values(array_filter(array_map('intval', preg_split('/[\s,]+/', $cat_raw) ?: [])));
$query_args = [
'post_type' => 'aksrt_forum_plate',
'post_status' => 'publish',
'posts_per_page' => $limit,
'order' => 'ASC',
'no_found_rows' => true,
];
switch ($orderby) {
case 'date':
$query_args['orderby'] = 'date';
$query_args['order'] = 'DESC';
break;
case 'posts':
$query_args['meta_key'] = '_aksrt_posts_count';
$query_args['orderby'] = 'meta_value_num';
$query_args['order'] = 'DESC';
break;
case 'views':
$query_args['meta_key'] = '_aksrt_views';
$query_args['orderby'] = 'meta_value_num';
$query_args['order'] = 'DESC';
break;
case 'follow':
$query_args['meta_key'] = '_aksrt_follow_count';
$query_args['orderby'] = 'meta_value_num';
$query_args['order'] = 'DESC';
break;
default:
$query_args['orderby'] = 'menu_order title';
}
if ($cat_ids) {
$query_args['tax_query'] = [[
'taxonomy' => 'aksrt_plate_cat',
'field' => 'term_id',
'terms' => $cat_ids,
]];
}
if ($filter === 'is_hot') {
$query_args['meta_query'] = [[
'key' => '_aksrt_is_hot',
'value' => '1',
'compare' => '=',
]];
}
return $query_args;
}
public static function render_items_html(array $plates, string $style = 'default'): string {
if (!$plates) {
return '';
}
ob_start();
$self = new self();
if ($style === 'card') {
echo '<div class="aksrt-plate-cards ajax-item">';
foreach ($plates as $post) {
if ($post instanceof \WP_Post) {
$self->render_card($post);
}
}
echo '</div>';
} else {
echo '<div class="aksrt-plate-lists ajax-item">';
foreach ($plates as $post) {
if ($post instanceof \WP_Post) {
$self->render_row($post);
}
}
echo '</div>';
}
return (string) ob_get_clean();
}
/**
* @return array{html:string}
*/
public static function query_payload(array $instance): array {
if (!post_type_exists('aksrt_forum_plate')) {
return ['html' => ''];
}
$style = sanitize_key((string) ($instance['style'] ?? 'default'));
$plates = get_posts(self::build_query_args($instance));
$html = self::render_items_html($plates ?: [], $style === 'card' ? 'card' : 'default');
if ($html === '') {
$html = function_exists('aksrt_get_ajax_null')
? aksrt_get_ajax_null(__('暂无版块', 'aksrt'))
: '<p class="aksrt-plate-empty ajax-item">' . esc_html__('暂无版块', 'aksrt') . '</p>';
}
return ['html' => $html];
}
public function widget($args, $instance): void {
if (!post_type_exists('aksrt_forum_plate')) {
return;
}
$style = sanitize_key((string) ($instance['style'] ?? 'default'));
$payload = self::query_payload($instance);
if ($payload['html'] === '') {
return;
}
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
// IAS-like: first paint is SSR; optional remote refresh via rest for customize.
$rest = rest_url('aksrt/v1/forum/frontend/widgets/plates');
echo '<div class="widget-plate-lists ajaxpager mb20" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '">';
echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '</div>';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
public function render_row(\WP_Post $post): void {
$id = (int) $post->ID;
$url = get_permalink($id);
$thumb = get_the_post_thumbnail_url($id, 'thumbnail');
$icon = (string) get_post_meta($id, '_aksrt_icon', true);
$count = (int) get_post_meta($id, '_aksrt_posts_count', true);
$desc = $post->post_excerpt !== '' ? $post->post_excerpt : wp_trim_words(wp_strip_all_tags($post->post_content), 18);
echo '<a class="aksrt-plate-item" href="' . esc_url($url) . '">';
echo '<div class="aksrt-plate-item__thumb">';
if ($thumb) {
echo '<img src="' . esc_url($thumb) . '" alt="" loading="lazy" />';
} elseif ($icon !== '') {
echo '<i class="' . esc_attr($icon) . '" aria-hidden="true"></i>';
} else {
echo '<i class="fas fa-comments" aria-hidden="true"></i>';
}
echo '</div>';
echo '<div class="aksrt-plate-item__body">';
echo '<div class="aksrt-plate-item__title">' . esc_html(get_the_title($post)) . '</div>';
if ($desc !== '') {
echo '<div class="aksrt-plate-item__desc">' . esc_html($desc) . '</div>';
}
echo '<div class="aksrt-plate-item__meta"><span>' . esc_html(sprintf(/* translators: %d topic count */ __('%d 帖', 'aksrt'), $count)) . '</span></div>';
echo '</div></a>';
}
public function render_card(\WP_Post $post): void {
$id = (int) $post->ID;
$url = get_permalink($id);
$thumb = get_the_post_thumbnail_url($id, 'medium');
$count = (int) get_post_meta($id, '_aksrt_posts_count', true);
$follow = (int) get_post_meta($id, '_aksrt_follow_count', true);
echo '<a class="aksrt-plate-card" href="' . esc_url($url) . '">';
echo '<div class="aksrt-plate-card__cover"' . ($thumb ? ' style="background-image:url(' . esc_url($thumb) . ')"' : '') . '></div>';
echo '<div class="aksrt-plate-card__title">' . esc_html(get_the_title($post)) . '</div>';
echo '<div class="aksrt-plate-card__meta">';
echo '<span>' . esc_html(sprintf(__('%d 帖', 'aksrt'), $count)) . '</span>';
echo '<span>' . esc_html(sprintf(__('%d 关注', 'aksrt'), $follow)) . '</span>';
echo '</div></a>';
}
public function form($instance): void {
?>
<p>
<label><?php esc_html_e('标题', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) ($instance['title'] ?? __('版块列表', 'aksrt'))); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('数量', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="30" name="<?php echo esc_attr($this->get_field_name('showposts')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['showposts'] ?? 8))); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('排序', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('orderby')); ?>">
<option value="menu_order" <?php selected(($instance['orderby'] ?? 'menu_order'), 'menu_order'); ?>><?php esc_html_e('菜单顺序', 'aksrt'); ?></option>
<option value="date" <?php selected(($instance['orderby'] ?? ''), 'date'); ?>><?php esc_html_e('创建时间', 'aksrt'); ?></option>
<option value="posts" <?php selected(($instance['orderby'] ?? ''), 'posts'); ?>><?php esc_html_e('帖子数', 'aksrt'); ?></option>
<option value="views" <?php selected(($instance['orderby'] ?? ''), 'views'); ?>><?php esc_html_e('浏览量', 'aksrt'); ?></option>
<option value="follow" <?php selected(($instance['orderby'] ?? ''), 'follow'); ?>><?php esc_html_e('关注数', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('样式', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('style')); ?>">
<option value="default" <?php selected(($instance['style'] ?? 'default'), 'default'); ?>><?php esc_html_e('列表', 'aksrt'); ?></option>
<option value="card" <?php selected(($instance['style'] ?? ''), 'card'); ?>><?php esc_html_e('卡片', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('筛选', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('filter')); ?>">
<option value="" <?php selected(($instance['filter'] ?? ''), ''); ?>><?php esc_html_e('全部', 'aksrt'); ?></option>
<option value="is_hot" <?php selected(($instance['filter'] ?? ''), 'is_hot'); ?>><?php esc_html_e('仅热门', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('版块分类 term ID(逗号)', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('cat')); ?>" value="<?php echo esc_attr((string) ($instance['cat'] ?? '')); ?>">
</label>
</p>
<?php
}
public function update($new_instance, $old_instance): array {
$orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'menu_order'));
if (!in_array($orderby, ['menu_order', 'date', 'posts', 'views', 'follow'], true)) {
$orderby = 'menu_order';
}
return [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'showposts' => max(1, min(30, (int) ($new_instance['showposts'] ?? 8))),
'orderby' => $orderby,
'style' => ($new_instance['style'] ?? '') === 'card' ? 'card' : 'default',
'filter' => ($new_instance['filter'] ?? '') === 'is_hot' ? 'is_hot' : '',
'cat' => sanitize_text_field((string) ($new_instance['cat'] ?? '')),
];
}
}