feat(widgets): author card, forum tags, multi-tab topic lists
Add post-author card, forum tag term list, and multi-tab forum topic switcher widgets to close remaining zibll user/bbs widget gaps.
This commit is contained in:
1 parent
ee2042f936
commit
bf28ab64b8
6 files changed
+600
No files matched your search
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
/**
|
||||
* Multi-tab forum topic lists (zibll zib_bbs_widget_ui_tab_posts lite).
|
||||
*
|
||||
* @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)));
|
||||
$uid = 'aksrt-ftab-' . esc_attr($this->id);
|
||||
|
||||
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" id="' . $uid . '">';
|
||||
echo '<ul class="aksrt-ftab__nav" role="tablist">';
|
||||
foreach ($tabs as $i => $tab) {
|
||||
$cls = $i === 0 ? ' is-active' : '';
|
||||
echo '<li><button type="button" class="aksrt-ftab__btn' . esc_attr($cls) . '" data-index="' . (int) $i . '">' . esc_html($tab['title']) . '</button></li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
|
||||
foreach ($tabs as $i => $tab) {
|
||||
$cls = $i === 0 ? ' is-active' : '';
|
||||
echo '<div class="aksrt-ftab__panel' . esc_attr($cls) . '" data-index="' . (int) $i . '">';
|
||||
$this->render_list($tab, $count);
|
||||
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 btns=[].slice.call(root.querySelectorAll('.aksrt-ftab__btn'));
|
||||
var panels=[].slice.call(root.querySelectorAll('.aksrt-ftab__panel'));
|
||||
function go(i){
|
||||
btns.forEach(function(b,idx){b.classList.toggle('is-active',idx===i);});
|
||||
panels.forEach(function(p,idx){p.classList.toggle('is-active',idx===i);});
|
||||
}
|
||||
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 array{title:string,orderby:string} $tab
|
||||
*/
|
||||
private function render_list(array $tab, int $count): void {
|
||||
$query = [
|
||||
'post_type' => 'aksrt_forum_topic',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => $count,
|
||||
'order' => 'DESC',
|
||||
];
|
||||
switch ($tab['orderby']) {
|
||||
case 'views':
|
||||
$query['meta_key'] = '_aksrt_views';
|
||||
$query['orderby'] = 'meta_value_num';
|
||||
break;
|
||||
case 'comments':
|
||||
$query['orderby'] = 'comment_count';
|
||||
break;
|
||||
case 'essence':
|
||||
$query['meta_query'] = [[
|
||||
'key' => '_aksrt_essence',
|
||||
'value' => '1',
|
||||
]];
|
||||
$query['orderby'] = 'date';
|
||||
break;
|
||||
default:
|
||||
$query['orderby'] = 'date';
|
||||
}
|
||||
$q = new \WP_Query($query);
|
||||
if (!$q->have_posts()) {
|
||||
echo '<p class="aksrt-ftab__empty">' . esc_html__('暂无内容', 'aksrt') . '</p>';
|
||||
wp_reset_postdata();
|
||||
return;
|
||||
}
|
||||
echo '<ul class="aksrt-ftopic-list is-mini">';
|
||||
while ($q->have_posts()) {
|
||||
$q->the_post();
|
||||
$views = (int) get_post_meta(get_the_ID(), '_aksrt_views', true);
|
||||
echo '<li class="aksrt-ftopic-list__item">';
|
||||
echo '<a class="aksrt-ftopic-list__link" href="' . esc_url(get_permalink()) . '">' . esc_html(get_the_title()) . '</a>';
|
||||
echo '<span class="aksrt-ftopic-list__views">' . esc_html((string) $views) . '</span>';
|
||||
echo '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
wp_reset_postdata();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw
|
||||
* @return array<int, array{title:string,orderby:string}>
|
||||
*/
|
||||
private function normalize_tabs($raw): array {
|
||||
if (is_string($raw)) {
|
||||
$decoded = json_decode($raw, true);
|
||||
$raw = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
if (!is_array($raw)) {
|
||||
return [];
|
||||
}
|
||||
$out = [];
|
||||
foreach ($raw as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$title = sanitize_text_field((string) ($row['title'] ?? ''));
|
||||
if ($title === '') {
|
||||
continue;
|
||||
}
|
||||
$orderby = sanitize_key((string) ($row['orderby'] ?? 'date'));
|
||||
if (!in_array($orderby, ['date', 'views', 'comments', 'essence'], true)) {
|
||||
$orderby = 'date';
|
||||
}
|
||||
$out[] = ['title' => $title, 'orderby' => $orderby];
|
||||
if (count($out) >= 6) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function form($instance): void {
|
||||
$tabs = is_string($instance['tabs'] ?? null)
|
||||
? (string) $instance['tabs']
|
||||
: wp_json_encode($this->normalize_tabs($instance['tabs'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
?>
|
||||
<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('栏目 JSON', 'aksrt'); ?>
|
||||
<textarea class="widefat" rows="5" name="<?php echo esc_attr($this->get_field_name('tabs')); ?>" placeholder='[{"title":"最新","orderby":"date"},{"title":"热门","orderby":"views"},{"title":"精华","orderby":"essence"}]'><?php echo esc_textarea((string) $tabs); ?></textarea>
|
||||
</label>
|
||||
<small><?php esc_html_e('orderby: date|views|comments|essence', 'aksrt'); ?></small>
|
||||
</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) ((int) ($instance['count'] ?? 6))); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance): array {
|
||||
return [
|
||||
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
|
||||
'tabs' => $this->normalize_tabs($new_instance['tabs'] ?? []),
|
||||
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 6))),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user