From aabc1cd1429623e156de7c7dcc2907c2761df3ae Mon Sep 17 00:00:00 2001
From: unknown
Date: Sun, 19 Jul 2026 01:17:41 +0800
Subject: [PATCH] feat(widgets): deepen buttons, video player, recent comments
Add go_link/hollow/radius/icon slots for buttons; loop/poster/hide
controls for video player; exclude post/keywords on recent comments.
---
assets/css/frontend/widget-batch5.css | 76 ++++++
src/AKSRT/Widgets/ButtonsWidget.php | 257 +++++++++++++--------
src/AKSRT/Widgets/RecentCommentsWidget.php | 176 ++++++++------
src/AKSRT/Widgets/WidgetVideoPlayer.php | 223 +++++++++---------
4 files changed, 455 insertions(+), 277 deletions(-)
diff --git a/assets/css/frontend/widget-batch5.css b/assets/css/frontend/widget-batch5.css
index bfcfd826..95fc7429 100644
--- a/assets/css/frontend/widget-batch5.css
+++ b/assets/css/frontend/widget-batch5.css
@@ -377,3 +377,79 @@
.aksrt-cover-card__bg.is-video video {
width: 100%; height: 100%; object-fit: cover; display: block;
}
+
+.widget-buttons {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--btn-gap, 10px);
+ margin-bottom: 8px;
+}
+.widget-buttons--vertical { flex-direction: column; }
+.widget-buttons.align-center { justify-content: center; }
+.widget-buttons.align-left { justify-content: flex-start; }
+.widget-buttons.align-right { justify-content: flex-end; }
+.widget-buttons__btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ padding: 8px 16px;
+ border-radius: 8px;
+ text-decoration: none;
+ font-size: 13px;
+ font-weight: 600;
+ background: var(--color-primary, #3b82f6);
+ color: #fff !important;
+}
+.widget-buttons.is-radius .widget-buttons__btn { border-radius: 999px; }
+.widget-buttons.is-hollow .widget-buttons__btn {
+ background: transparent;
+ color: var(--color-primary, #3b82f6) !important;
+ border: 1px solid currentColor;
+}
+.widget-buttons__btn--secondary { background: #64748b; }
+.widget-buttons__btn--outline {
+ background: transparent;
+ color: inherit !important;
+ border: 1px solid var(--color-line, #ddd);
+}
+.widget-buttons__btn--text {
+ background: transparent;
+ color: var(--color-primary, #3b82f6) !important;
+ padding-left: 0;
+ padding-right: 0;
+}
+.widget-buttons__btn--pink { background: #ec4899; }
+.widget-buttons__btn--blue { background: #3b82f6; }
+.widget-buttons__btn--green { background: #22c55e; }
+
+.aksrt-video-widget {
+ position: relative;
+ width: 100%;
+ height: 0;
+ border-radius: 8px;
+ overflow: hidden;
+ background: #111;
+ margin-bottom: 10px;
+}
+.aksrt-video-widget iframe,
+.aksrt-video-widget video {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ border: 0;
+ object-fit: cover;
+}
+.aksrt-video-widget.controller-hide video::-webkit-media-controls {
+ display: none !important;
+}
+
+.widget-recent-comments__post {
+ margin-top: 2px;
+ font-size: 11px;
+}
+.widget-recent-comments__post a {
+ color: var(--muted-2-color, #999);
+ text-decoration: none;
+}
diff --git a/src/AKSRT/Widgets/ButtonsWidget.php b/src/AKSRT/Widgets/ButtonsWidget.php
index cadfde31..20eb79b2 100644
--- a/src/AKSRT/Widgets/ButtonsWidget.php
+++ b/src/AKSRT/Widgets/ButtonsWidget.php
@@ -1,5 +1,7 @@
esc_html__('显示一组自定义按钮', 'aksrt'),
- ]);
- }
+ public function __construct() {
+ parent::__construct('aksrt_buttons', esc_html__('AKSRT 按钮组', 'aksrt'), [
+ 'description' => esc_html__('显示一组自定义按钮,支持外链跳转与样式。', 'aksrt'),
+ ]);
+ }
- public function widget($args, $instance): void {
- echo $args['before_widget'];
- $title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base);
- $buttons = isset($instance['buttons']) && is_array($instance['buttons']) ? $instance['buttons'] : [];
- $layout = sanitize_key($instance['layout'] ?? 'vertical');
+ public function widget($args, $instance): void {
+ $buttons = isset($instance['buttons']) && is_array($instance['buttons']) ? $instance['buttons'] : [];
+ $layout = sanitize_key((string) ($instance['layout'] ?? 'horizontal'));
+ if (!in_array($layout, ['vertical', 'horizontal'], true)) {
+ $layout = 'horizontal';
+ }
+ $align = sanitize_key((string) ($instance['align'] ?? 'center'));
+ if (!in_array($align, ['left', 'center', 'right'], true)) {
+ $align = 'center';
+ }
+ $radius = !empty($instance['radius']);
+ $hollow = !empty($instance['hollow']);
+ $go_link = !empty($instance['go_link']);
+ $gap = max(4, min(30, (int) ($instance['gap'] ?? 10)));
- if ($title) echo $args['before_title'] . esc_html($title) . $args['after_title'];
+ $items = [];
+ foreach ($buttons as $btn) {
+ if (!is_array($btn)) {
+ continue;
+ }
+ $text = sanitize_text_field((string) ($btn['text'] ?? ''));
+ $url = esc_url_raw(trim((string) ($btn['url'] ?? '')));
+ if ($text === '') {
+ continue;
+ }
+ if ($url !== '' && $go_link && function_exists('aksrt_go_link')) {
+ $url = aksrt_go_link($url, true);
+ }
+ $items[] = [
+ 'text' => $text,
+ 'url' => $url,
+ 'style' => sanitize_key((string) ($btn['style'] ?? 'primary')),
+ 'icon' => sanitize_text_field((string) ($btn['icon'] ?? $btn['icon_left'] ?? '')),
+ 'icon_right' => sanitize_text_field((string) ($btn['icon_right'] ?? '')),
+ 'new_window' => !empty($btn['new_window']) || !empty($btn['target']),
+ ];
+ }
+ if (!$items) {
+ return;
+ }
- if (!empty($buttons)) : ?>
-
- id_base);
+ if ($title !== '') {
+ echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
- echo $args['after_widget'];
- }
+ $wrap = 'widget-buttons widget-buttons--' . $layout . ' align-' . $align;
+ if ($radius) {
+ $wrap .= ' is-radius';
+ }
+ if ($hollow) {
+ $wrap .= ' is-hollow';
+ }
+ echo '';
+ foreach ($items as $btn) {
+ $style = in_array($btn['style'], ['primary', 'secondary', 'outline', 'text', 'pink', 'blue', 'green', 'yellow', 'purple'], true)
+ ? $btn['style']
+ : 'primary';
+ $cls = 'widget-buttons__btn widget-buttons__btn--' . $style;
+ $href = $btn['url'] !== '' ? $btn['url'] : 'javascript:void(0)';
+ $tgt = $btn['new_window'] && $btn['url'] !== '' ? ' target="_blank" rel="noopener noreferrer"' : '';
+ echo '
';
+ if ($btn['icon'] !== '') {
+ echo ' ';
+ }
+ echo esc_html($btn['text']);
+ if ($btn['icon_right'] !== '') {
+ echo ' ';
+ }
+ echo '';
+ }
+ echo '
';
- public function form($instance): void {
- $buttons = isset($instance['buttons']) && is_array($instance['buttons']) ? $instance['buttons'] : [];
- while (count($buttons) < 3) {
- $buttons[] = ['text' => '', 'url' => '', 'style' => 'primary', 'icon' => '', 'new_window' => false];
- }
- ?>
-
-
-
-
-
-
-
-
-
- $btn) : ?>
-
- sanitize_text_field($btn['text'] ?? ''),
- 'url' => esc_url_raw($btn['url'] ?? ''),
- 'style' => $style,
- 'icon' => sanitize_text_field($btn['icon'] ?? ''),
- 'new_window' => !empty($btn['new_window']),
- ];
- }
- }
- return [
- 'title' => sanitize_text_field($new_instance['title'] ?? ''),
- 'layout' => in_array($new_instance['layout'] ?? '', ['vertical', 'horizontal'], true) ? $new_instance['layout'] : 'vertical',
- 'buttons' => $buttons,
- ];
- }
+ public function form($instance): void {
+ $buttons = isset($instance['buttons']) && is_array($instance['buttons']) ? $instance['buttons'] : [];
+ while (count($buttons) < 4) {
+ $buttons[] = ['text' => '', 'url' => '', 'style' => 'primary', 'icon' => '', 'icon_right' => '', 'new_window' => false];
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ $btn) : ?>
+
+ " value="" placeholder="">
+ " value="" placeholder="">
+ " value="" placeholder="">
+ " value="" placeholder="">
+
+
+
+ $text,
+ 'url' => esc_url_raw(trim((string) ($btn['url'] ?? ''))),
+ 'style' => sanitize_key((string) ($btn['style'] ?? 'primary')),
+ 'icon' => sanitize_text_field((string) ($btn['icon'] ?? '')),
+ 'icon_right' => sanitize_text_field((string) ($btn['icon_right'] ?? '')),
+ 'new_window' => !empty($btn['new_window']),
+ ];
+ if (count($buttons) >= 8) {
+ break;
+ }
+ }
+ }
+ return [
+ 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
+ 'layout' => ($new_instance['layout'] ?? '') === 'vertical' ? 'vertical' : 'horizontal',
+ 'align' => in_array(($new_instance['align'] ?? ''), ['left', 'center', 'right'], true) ? $new_instance['align'] : 'center',
+ 'radius' => !empty($new_instance['radius']) ? 1 : 0,
+ 'hollow' => !empty($new_instance['hollow']) ? 1 : 0,
+ 'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
+ 'gap' => max(4, min(30, (int) ($new_instance['gap'] ?? 10))),
+ 'buttons' => $buttons,
+ ];
+ }
}
diff --git a/src/AKSRT/Widgets/RecentCommentsWidget.php b/src/AKSRT/Widgets/RecentCommentsWidget.php
index f24de874..5820e1aa 100644
--- a/src/AKSRT/Widgets/RecentCommentsWidget.php
+++ b/src/AKSRT/Widgets/RecentCommentsWidget.php
@@ -1,5 +1,7 @@
esc_html__('显示最新评论列表', 'aksrt'),
- ]);
- }
+ public function __construct() {
+ parent::__construct('aksrt_recent_comments', esc_html__('AKSRT 最新评论', 'aksrt'), [
+ 'description' => esc_html__('显示最新评论列表', 'aksrt'),
+ ]);
+ }
- public function widget($args, $instance): void {
- echo $args['before_widget'];
- $title = apply_filters('widget_title', $instance['title'] ?? __('最新评论', 'aksrt'), $instance, $this->id_base);
- $count = max(1, min(20, (int) ($instance['count'] ?? 5)));
- $show_avatar = !empty($instance['show_avatar']);
+ public function widget($args, $instance): void {
+ echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ $title = apply_filters('widget_title', (string) ($instance['title'] ?? __('最新评论', 'aksrt')), $instance, $this->id_base);
+ $count = max(1, min(20, (int) ($instance['count'] ?? $instance['limit'] ?? 5)));
+ $show_avatar = !isset($instance['show_avatar']) || !empty($instance['show_avatar']);
+ $outpost = max(0, (int) ($instance['outpost'] ?? 0));
+ $outer = sanitize_text_field((string) ($instance['outer'] ?? ''));
- if ($title) echo $args['before_title'] . esc_html($title) . $args['after_title'];
+ if ($title !== '') {
+ echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
- $comments = get_comments([
- 'number' => $count,
- 'status' => 'approve',
- 'post_status' => 'publish',
- ]);
+ $query = [
+ 'number' => $count,
+ 'status' => 'approve',
+ 'post_status' => 'publish',
+ ];
+ if ($outpost > 0) {
+ $query['post__not_in'] = [$outpost];
+ }
+ if ($outer !== '') {
+ // Exclude comments containing outer keywords (comma separated).
+ // Implemented post-filter below.
+ }
+ $comments = get_comments($query);
+ if ($outer !== '' && $comments) {
+ $keys = array_filter(array_map('trim', preg_split('/[,,]/', $outer) ?: []));
+ if ($keys) {
+ $comments = array_values(array_filter($comments, static function ($c) use ($keys) {
+ $body = (string) $c->comment_content;
+ foreach ($keys as $k) {
+ if ($k !== '' && str_contains($body, $k)) {
+ return false;
+ }
+ }
+ return true;
+ }));
+ }
+ }
- if ($comments) : ?>
-
-
- ' . esc_html__('暂无评论', 'aksrt') . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
- ';
+ foreach ($comments as $comment) {
+ $post = get_post($comment->comment_post_ID);
+ if (!$post) {
+ continue;
+ }
+ echo '';
+ }
+ echo '';
+ } else {
+ echo '' . esc_html__('暂无评论', 'aksrt') . '
';
+ }
- echo $args['after_widget'];
- }
+ echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
- public function form($instance): void {
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sanitize_text_field($new_instance['title'] ?? ''),
- 'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))),
- 'show_avatar' => !empty($new_instance['show_avatar']),
- ];
- }
+ public function update($new_instance, $old_instance): array {
+ return [
+ 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
+ 'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))),
+ 'outpost' => max(0, (int) ($new_instance['outpost'] ?? 0)),
+ 'outer' => sanitize_text_field((string) ($new_instance['outer'] ?? '')),
+ 'show_avatar' => !empty($new_instance['show_avatar']) ? 1 : 0,
+ ];
+ }
}
diff --git a/src/AKSRT/Widgets/WidgetVideoPlayer.php b/src/AKSRT/Widgets/WidgetVideoPlayer.php
index 40888ed5..d20a2d0f 100644
--- a/src/AKSRT/Widgets/WidgetVideoPlayer.php
+++ b/src/AKSRT/Widgets/WidgetVideoPlayer.php
@@ -1,5 +1,7 @@
__('在侧栏嵌入视频播放器。', 'aksrt')]
- );
- }
+ public function __construct() {
+ parent::__construct(
+ 'aksrt_video_player',
+ __('视频播放器', 'aksrt'),
+ ['description' => __('侧栏/流体区嵌入视频播放器。', 'aksrt')]
+ );
+ }
- public function widget($args, $instance): void {
- $title = !empty($instance['title']) ? $instance['title'] : '';
- $video_url = !empty($instance['video_url']) ? $instance['video_url'] : '';
- $poster = !empty($instance['poster']) ? $instance['poster'] : '';
- $autoplay = !empty($instance['autoplay']);
- $aspect_ratio = !empty($instance['aspect_ratio']) ? $instance['aspect_ratio'] : '16:9';
+ public function widget($args, $instance): void {
+ $title = (string) ($instance['title'] ?? '');
+ $video_url = trim((string) ($instance['video_url'] ?? $instance['url'] ?? ''));
+ $poster = trim((string) ($instance['poster'] ?? $instance['pic'] ?? ''));
+ $autoplay = !empty($instance['autoplay']);
+ $loop = !empty($instance['loop']);
+ $muted = $autoplay || !empty($instance['muted']);
+ $hide_controller = !empty($instance['hide_controller']);
+ $scale = max(20, min(100, (int) ($instance['scale_height'] ?? 56)));
+ $aspect = (string) ($instance['aspect_ratio'] ?? '');
- if (empty($video_url)) return;
+ if ($video_url === '') {
+ return;
+ }
- // 解析视频类型
- $embed_url = $this->get_embed_url($video_url);
- $is_embed = !empty($embed_url);
+ echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ if ($title !== '') {
+ echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
- echo $args['before_widget'];
+ if ($aspect !== '' && str_contains($aspect, ':')) {
+ $parts = explode(':', $aspect);
+ if (count($parts) === 2 && (int) $parts[0] > 0) {
+ $scale = (int) round(((int) $parts[1] / (int) $parts[0]) * 100);
+ }
+ }
- if ($title) {
- echo $args['before_title'] . esc_html($title) . $args['after_title'];
- }
+ $embed = $this->get_embed_url($video_url);
+ $cls = 'aksrt-video-widget' . ($hide_controller ? ' controller-hide' : '');
+ echo '';
+ if ($embed !== '') {
+ $src = $embed;
+ if ($autoplay) {
+ $src = add_query_arg(['autoplay' => '1', 'mute' => '1'], $src);
+ }
+ echo '';
+ } else {
+ $attrs = ' controls playsinline';
+ if ($autoplay) {
+ $attrs .= ' autoplay';
+ }
+ if ($loop) {
+ $attrs .= ' loop';
+ }
+ if ($muted) {
+ $attrs .= ' muted';
+ }
+ if ($poster !== '') {
+ $attrs .= ' poster="' . esc_url($poster) . '"';
+ }
+ echo '';
+ }
+ echo '
';
- // 计算 padding-bottom
- $ratio_parts = explode(':', $aspect_ratio);
- if (count($ratio_parts) === 2 && (int) $ratio_parts[1] > 0) {
- $padding = ((int) $ratio_parts[1] / (int) $ratio_parts[0]) * 100;
- } else {
- $padding = 56.25; // 16:9
- }
+ echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
- echo '
- comment_content, 15, '...')); ?> -
- - ID)); ?> - -