Route multi-tab forum topics through HotTopics mini/rank payload and WidgetAjax style/plate params; wire terms/boards to real REST ajaxpager.
228 lines
8.5 KiB
PHP
228 lines
8.5 KiB
PHP
<?php
|
||
/**
|
||
* Multi-tab forum topic lists (zibll zib_bbs_widget_ui_tab_posts + shared mini rows).
|
||
*
|
||
* @package AKSRT
|
||
*/
|
||
|
||
namespace AKSRT\Widgets;
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
class WidgetForumTabTopics extends \WP_Widget {
|
||
|
||
public function __construct() {
|
||
parent::__construct(
|
||
'aksrt_forum_tab_topics',
|
||
esc_html__('论坛 - 多栏目帖子', 'aksrt'),
|
||
[
|
||
'description' => esc_html__('热门/最新/精华等栏目切换的帖子列表(共享迷你行 + 懒加载)。', 'aksrt'),
|
||
'classname' => 'widget_aksrt_forum_tab_topics',
|
||
]
|
||
);
|
||
}
|
||
|
||
public function widget($args, $instance): void {
|
||
if (!post_type_exists('aksrt_forum_topic')) {
|
||
return;
|
||
}
|
||
$tabs = $this->normalize_tabs($instance['tabs'] ?? []);
|
||
if (!$tabs) {
|
||
$tabs = [
|
||
['title' => __('最新', 'aksrt'), 'orderby' => 'date'],
|
||
['title' => __('热门', 'aksrt'), 'orderby' => 'views'],
|
||
['title' => __('精华', 'aksrt'), 'orderby' => 'essence'],
|
||
];
|
||
}
|
||
$count = max(1, min(20, (int) ($instance['count'] ?? 6)));
|
||
$style = sanitize_key((string) ($instance['style'] ?? 'mini'));
|
||
if (!in_array($style, ['mini', 'rank'], true)) {
|
||
$style = 'mini';
|
||
}
|
||
$plate = max(0, (int) ($instance['plate'] ?? 0));
|
||
$uid = 'aksrt-ftab-' . esc_attr($this->id);
|
||
// Prefer WidgetAjax forum-topics (HotTopics payload with style/plate).
|
||
$rest = rest_url('aksrt/v1/widgets/forum-topics');
|
||
|
||
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
|
||
}
|
||
|
||
echo '<div class="aksrt-ftab widget-tab-posts" id="' . $uid . '" data-aksrt-tabs="1" data-rest="' . esc_url($rest) . '" data-count="' . esc_attr((string) $count) . '" data-style="' . esc_attr($style) . '" data-plate="' . esc_attr((string) $plate) . '">';
|
||
echo '<ul class="aksrt-ftab__nav widget-tab-posts__nav" role="tablist">';
|
||
foreach ($tabs as $i => $tab) {
|
||
$cls = $i === 0 ? ' is-active' : '';
|
||
echo '<li role="presentation"><button type="button" role="tab" class="aksrt-ftab__btn widget-tab-posts__tab' . esc_attr($cls) . '" data-index="' . (int) $i . '" data-orderby="' . esc_attr($tab['orderby']) . '" aria-selected="' . ($i === 0 ? 'true' : 'false') . '">' . esc_html($tab['title']) . '</button></li>';
|
||
}
|
||
echo '</ul>';
|
||
|
||
foreach ($tabs as $i => $tab) {
|
||
$cls = $i === 0 ? ' is-active' : '';
|
||
$loaded = $i === 0 ? '1' : '0';
|
||
echo '<div class="aksrt-ftab__panel widget-tab-posts__panel' . esc_attr($cls) . '" data-index="' . (int) $i . '" data-loaded="' . esc_attr($loaded) . '"' . ($i === 0 ? '' : ' hidden') . ' role="tabpanel">';
|
||
if ($i === 0) {
|
||
$payload = WidgetForumHotTopics::query_payload([
|
||
'count' => $count,
|
||
'orderby' => $tab['orderby'],
|
||
'period' => 'all',
|
||
'style' => $style,
|
||
'plate' => $plate,
|
||
'paginate' => 'none',
|
||
], 1);
|
||
echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||
} else {
|
||
echo '<div class="post_ajax_loader" data-aksrt-ajax-loader></div>';
|
||
}
|
||
echo '</div>';
|
||
}
|
||
echo '</div>';
|
||
?>
|
||
<script>
|
||
(function(){
|
||
var root=document.getElementById(<?php echo wp_json_encode($uid); ?>);
|
||
if(!root||root._aksrtFtab)return; root._aksrtFtab=1;
|
||
var rest=root.getAttribute('data-rest')||'';
|
||
var count=root.getAttribute('data-count')||'6';
|
||
var style=root.getAttribute('data-style')||'mini';
|
||
var plate=root.getAttribute('data-plate')||'0';
|
||
var btns=[].slice.call(root.querySelectorAll('.aksrt-ftab__btn'));
|
||
var panels=[].slice.call(root.querySelectorAll('.aksrt-ftab__panel'));
|
||
function load(panel, orderby){
|
||
if(!panel||panel.getAttribute('data-loaded')==='1'||!rest)return;
|
||
var qs=new URLSearchParams({page:'1',count:count,orderby:orderby||'date',style:style,plate:plate,period:'all',paginate:'none'});
|
||
var url=rest+(rest.indexOf('?')>-1?'&':'?')+qs.toString();
|
||
panel.classList.add('is-ajax-loading');
|
||
fetch(url,{credentials:'same-origin',headers:{Accept:'application/json','X-WP-Nonce':(window.aksrtData&&aksrtData.restNonce)||''}})
|
||
.then(function(r){return r.json()}).then(function(j){
|
||
var d=(j&&j.data)||j||{};
|
||
var html=d.html||'';
|
||
panel.innerHTML=html||'<div class="ajax-item aksrt-ftab__empty muted-2-color">暂无内容</div>';
|
||
panel.setAttribute('data-loaded','1');
|
||
panel.classList.remove('is-ajax-loading');
|
||
}).catch(function(){panel.classList.remove('is-ajax-loading');});
|
||
}
|
||
function go(i){
|
||
btns.forEach(function(b,idx){
|
||
var on=idx===i;
|
||
b.classList.toggle('is-active',on);
|
||
b.setAttribute('aria-selected',on?'true':'false');
|
||
});
|
||
panels.forEach(function(p,idx){
|
||
var on=idx===i;
|
||
p.classList.toggle('is-active',on);
|
||
if(on){p.removeAttribute('hidden');}else{p.setAttribute('hidden','');}
|
||
});
|
||
var btn=btns[i], panel=panels[i];
|
||
if(btn&&panel) load(panel, btn.getAttribute('data-orderby')||'date');
|
||
}
|
||
btns.forEach(function(b){ b.addEventListener('click', function(){ go(parseInt(b.getAttribute('data-index')||'0',10)); }); });
|
||
})();
|
||
</script>
|
||
<?php
|
||
|
||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||
}
|
||
|
||
/**
|
||
* @param mixed $raw
|
||
* @return list<array{title:string,orderby:string}>
|
||
*/
|
||
private function normalize_tabs($raw): array {
|
||
$out = [];
|
||
if (is_string($raw)) {
|
||
foreach (array_filter(explode("\n", $raw)) as $line) {
|
||
$parts = array_map('trim', explode('|', $line));
|
||
if ($parts[0] === '') {
|
||
continue;
|
||
}
|
||
$out[] = [
|
||
'title' => $parts[0],
|
||
'orderby' => sanitize_key($parts[1] ?? 'date'),
|
||
];
|
||
}
|
||
return $out;
|
||
}
|
||
if (is_array($raw)) {
|
||
foreach ($raw as $tab) {
|
||
if (!is_array($tab)) {
|
||
continue;
|
||
}
|
||
$title = trim((string) ($tab['title'] ?? ''));
|
||
if ($title === '') {
|
||
continue;
|
||
}
|
||
$out[] = [
|
||
'title' => $title,
|
||
'orderby' => sanitize_key((string) ($tab['orderby'] ?? 'date')),
|
||
];
|
||
}
|
||
}
|
||
return $out;
|
||
}
|
||
|
||
public function form($instance): void {
|
||
$defaults = [
|
||
'title' => __('论坛栏目', 'aksrt'),
|
||
'count' => 6,
|
||
'style' => 'mini',
|
||
'plate' => 0,
|
||
'tabs' => "最新|date\n热门|views\n精华|essence",
|
||
];
|
||
$instance = wp_parse_args($instance, $defaults);
|
||
$tabs_val = $instance['tabs'];
|
||
if (is_array($tabs_val)) {
|
||
$lines = [];
|
||
foreach ($tabs_val as $t) {
|
||
$lines[] = ($t['title'] ?? '') . '|' . ($t['orderby'] ?? 'date');
|
||
}
|
||
$tabs_val = implode("\n", $lines);
|
||
}
|
||
?>
|
||
<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']); ?>">
|
||
</label>
|
||
</p>
|
||
<p>
|
||
<label><?php esc_html_e('每栏数量', 'aksrt'); ?>
|
||
<input class="tiny-text" type="number" min="1" max="20" name="<?php echo esc_attr($this->get_field_name('count')); ?>" value="<?php echo esc_attr((string) $instance['count']); ?>">
|
||
</label>
|
||
</p>
|
||
<p>
|
||
<label><?php esc_html_e('样式', 'aksrt'); ?>
|
||
<select name="<?php echo esc_attr($this->get_field_name('style')); ?>">
|
||
<option value="mini" <?php selected($instance['style'], 'mini'); ?>><?php esc_html_e('迷你信息行', 'aksrt'); ?></option>
|
||
<option value="rank" <?php selected($instance['style'], 'rank'); ?>><?php esc_html_e('排行榜', 'aksrt'); ?></option>
|
||
</select>
|
||
</label>
|
||
</p>
|
||
<p>
|
||
<label><?php esc_html_e('版块 ID(可选)', 'aksrt'); ?>
|
||
<input class="tiny-text" type="number" min="0" name="<?php echo esc_attr($this->get_field_name('plate')); ?>" value="<?php echo esc_attr((string) ((int) $instance['plate'])); ?>">
|
||
</label>
|
||
</p>
|
||
<p>
|
||
<label><?php esc_html_e('栏目(每行: 标题|排序 date/views/essence/comments)', 'aksrt'); ?>
|
||
<textarea class="widefat" rows="4" name="<?php echo esc_attr($this->get_field_name('tabs')); ?>"><?php echo esc_textarea((string) $tabs_val); ?></textarea>
|
||
</label>
|
||
</p>
|
||
<?php
|
||
}
|
||
|
||
public function update($new_instance, $old_instance): array {
|
||
$style = sanitize_key((string) ($new_instance['style'] ?? 'mini'));
|
||
if (!in_array($style, ['mini', 'rank'], true)) {
|
||
$style = 'mini';
|
||
}
|
||
return [
|
||
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
|
||
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 6))),
|
||
'style' => $style,
|
||
'plate' => max(0, (int) ($new_instance['plate'] ?? 0)),
|
||
'tabs' => sanitize_textarea_field((string) ($new_instance['tabs'] ?? '')),
|
||
];
|
||
}
|
||
}
|