diff --git a/assets/css/frontend/widget-batch5.css b/assets/css/frontend/widget-batch5.css index 530b4817..ddc37d63 100644 --- a/assets/css/frontend/widget-batch5.css +++ b/assets/css/frontend/widget-batch5.css @@ -2431,3 +2431,51 @@ a.main-color > .icon-cover-card:hover, /* batch31-33 residual */ .aksrt-fi__img.aksrt-lazy { width: 100%; height: 100%; object-fit: cover; display: block; position: absolute; inset: 0; } .aksrt-fi__video { width: 100%; height: 100%; object-fit: cover; position: absolute; inset: 0; } + + +/* batch34-36 residual */ +.widget-hot-posts--graphic .hot-posts-graphic { margin-bottom: 12px; border-radius: 8px; overflow: hidden; } +.widget-hot-posts--graphic .hot-posts-graphic .graphic { position: relative; overflow: hidden; border-radius: 8px; background: var(--surface-2, #f3f4f6); } +.widget-hot-posts--graphic .hot-posts-graphic img.fit-cover { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; } +.widget-hot-posts--graphic .linear-mask { + position: absolute; inset: 0; + background: linear-gradient(to top, rgba(0,0,0,.62), transparent 55%); + pointer-events: none; +} +.widget-hot-posts--graphic .abs-center.left-bottom { position: absolute; left: 0; right: 0; bottom: 0; color: #fff; } +.widget-hot-posts--graphic .hot-posts-graphic__title { font-weight: 600; line-height: 1.35; } +.widget-hot-posts--graphic .hot-post-item { position: relative; align-items: flex-start; gap: 0; } +.widget-hot-posts--graphic .hot-post-item__thumb { + flex: 0 0 72px; width: 72px; height: 54px; border-radius: 8px; overflow: hidden; + position: relative; background: var(--surface-2, #f3f4f6); +} +.widget-hot-posts--graphic .hot-post-item__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; } +.widget-hot-posts--graphic badge.img-badge.left.hot { + position: absolute; top: 6px; left: 6px; z-index: 2; + padding: 2px 6px; border-radius: 4px; font-size: 11px; line-height: 1.2; color: #fff; +} +.widget-hot-posts--graphic badge.img-badge.jb-red { background: #f04438; } +.widget-hot-posts--graphic badge.img-badge.jb-yellow { background: #f79009; color: #111; } +.widget-hot-posts--graphic badge.img-badge.b-gray { background: #667085; } +.widget-hot-posts--graphic badge.img-badge.em12 { font-size: 12px; } +.pagenav.ajax-pag { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; align-items: center; margin-top: 12px; } +.pagenav.ajax-pag .page-numbers { + display: inline-flex; min-width: 32px; height: 32px; padding: 0 8px; + align-items: center; justify-content: center; border-radius: 8px; + background: var(--surface-2, #f3f4f6); color: inherit; text-decoration: none; font-size: 13px; +} +.pagenav.ajax-pag .page-numbers.current { background: var(--theme-color, #f04494); color: #fff; } +.pagenav.ajax-pag .page-numbers.dots { background: transparent; min-width: auto; } +.widget-tab-posts__skeleton .placeholder-row { + display: flex; gap: 10px; align-items: center; padding: 8px 0; +} +.widget-tab-posts__skeleton .thumb-placeholder { + width: 56px; height: 42px; border-radius: 6px; background: var(--surface-2, #eee); +} +.widget-tab-posts__skeleton .placeholder.k1, +.widget-tab-posts__skeleton .placeholder.s1 { + height: 12px; border-radius: 4px; background: var(--surface-2, #eee); +} +.widget-tab-posts__skeleton .placeholder.k1 { width: 80%; } +.widget-tab-posts__skeleton .placeholder.s1 { width: 48%; } +.posts-mini-lists.aksrt-main-posts { /* zibll mini outer */ } diff --git a/assets/js/widget-set.js b/assets/js/widget-set.js index 8726220f..5ee91678 100644 --- a/assets/js/widget-set.js +++ b/assets/js/widget-set.js @@ -25,11 +25,15 @@ var orderby = tab.getAttribute('data-orderby') || 'date'; var cat = tab.getAttribute('data-cat') || '0'; var tabCount = tab.getAttribute('data-tab-count') || '0'; + var style = widget.getAttribute('data-style') || 'mini'; + var paginate = widget.getAttribute('data-paginate') || 'none'; var qs = new URLSearchParams({ count: defaultCount, orderby: orderby, cat: cat, tab_count: tabCount, + style: style, + paginate: paginate, }); var url = rest + (rest.indexOf('?') > -1 ? '&' : '?') + qs.toString(); panel.classList.add('is-ajax-loading'); @@ -42,7 +46,8 @@ .then(function (j) { var d = (j && j.data) || j || {}; var html = d.html || ''; - panel.innerHTML = html || '

暂无文章

'; + var pag = d.paginate_html || d.paginateHtml || ''; + panel.innerHTML = (html || '

暂无文章

') + (pag || ''); panel.setAttribute('data-loaded', '1'); panel.classList.remove('is-ajax-loading'); }) diff --git a/inc/helpers/aksrt-ajax-list.php b/inc/helpers/aksrt-ajax-list.php index f870ef22..9013a057 100644 --- a/inc/helpers/aksrt-ajax-list.php +++ b/inc/helpers/aksrt-ajax-list.php @@ -109,3 +109,83 @@ if (!function_exists('aksrt_get_remote_modal_link')) { return '<' . $tag . $attrs . '>' . esc_html($text) . ''; } } + +if (!function_exists('aksrt_get_ajax_number_paginate')) { + /** + * Numbered AJAX pager (zibll zib_get_ajax_number_paginate subset). + * + * Links use class next-page ajax-next + ajax-replace so ajax-list.js + * replaces .ajax-item rows inside the nearest .ajaxpager. + * + * @param int $count_all Total items. + * @param int $page Current page (1-based). + * @param int $per_page Per page. + * @param string $ajax_url Base REST/URL (page query key replaced). + * @param string $pag_class Wrapper class. + * @param string $next_class Link base class. + * @param string $query_key Page query arg (default page). + */ + function aksrt_get_ajax_number_paginate( + int $count_all, + int $page = 1, + int $per_page = 10, + string $ajax_url = '', + string $pag_class = 'ajax-pag', + string $next_class = 'next-page ajax-next', + string $query_key = 'page' + ): string { + $page = max(1, $page); + $per_page = max(1, $per_page); + $total_pages = (int) ceil(max(0, $count_all) / $per_page); + if ($total_pages < 2 || $count_all <= 0) { + return '
'; + } + + $base = $ajax_url !== '' ? $ajax_url : '#'; + // Ensure %#% placeholder for page numbers. + if (strpos($base, '%#%') === false) { + $base = add_query_arg($query_key, '%#%', $base); + } + + $end_size = 1; + $mid_size = 2; + $links = []; + $dots = false; + + // Prev. + if ($page > 1) { + $url = esc_url(str_replace('%#%', (string) ($page - 1), $base)); + $links[] = ''; + } + + for ($n = 1; $n <= $total_pages; $n++) { + $in_window = $n <= $end_size + || $n > $total_pages - $end_size + || ($n >= $page - $mid_size && $n <= $page + $mid_size); + if (!$in_window) { + if ($dots) { + continue; + } + $links[] = '…'; + $dots = true; + continue; + } + $dots = false; + if ($n === $page) { + $links[] = '' . (int) $n . ''; + } else { + $url = esc_url(str_replace('%#%', (string) $n, $base)); + $links[] = '' . (int) $n . ''; + } + } + + // Next. + if ($page < $total_pages) { + $url = esc_url(str_replace('%#%', (string) ($page + 1), $base)); + $links[] = ''; + } + + return ''; + } +} + diff --git a/src/AKSRT/Core/WidgetAjax.php b/src/AKSRT/Core/WidgetAjax.php index d1daa471..d8b80ae4 100644 --- a/src/AKSRT/Core/WidgetAjax.php +++ b/src/AKSRT/Core/WidgetAjax.php @@ -61,11 +61,13 @@ class WidgetAjax { 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')), 'cat' => max(0, (int) ($request->get_param('cat') ?? 0)), 'limit_day' => max(0, (int) ($request->get_param('limit_day') ?? 0)), + 'topics' => sanitize_text_field((string) ($request->get_param('topics') ?? '')), + 'pay_type' => sanitize_key((string) ($request->get_param('pay_type') ?? '')), 'style' => sanitize_key((string) ($request->get_param('style') ?? 'list')), 'show_thumb' => $request->get_param('show_thumb') === null ? 1 : (int) $request->get_param('show_thumb'), 'show_meta' => $request->get_param('show_meta') === null ? 1 : (int) $request->get_param('show_meta'), 'show_number' => (int) ($request->get_param('show_number') ?? 0), - 'paginate' => sanitize_key((string) ($request->get_param('paginate') ?? 'ajax')), + 'paginate' => in_array(sanitize_key((string) ($request->get_param('paginate') ?? 'ajax')), ['none','ajax','number'], true) ? sanitize_key((string) ($request->get_param('paginate') ?? 'ajax')) : 'ajax', ]; $page = max(1, (int) ($request->get_param('page') ?? $request->get_param('paged') ?? 1)); return $this->payload(\AKSRT\Widgets\WidgetMainPosts::query_payload($instance, $page), $page); @@ -76,6 +78,15 @@ class WidgetAjax { return $this->fail(); } $default_count = max(1, min(20, (int) ($request->get_param('count') ?? 5))); + $style = sanitize_key((string) ($request->get_param('style') ?? 'mini')); + if (!in_array($style, ['list', 'card', 'mini'], true)) { + $style = 'mini'; + } + $paginate = sanitize_key((string) ($request->get_param('paginate') ?? 'none')); + if (!in_array($paginate, ['none', 'ajax', 'number'], true)) { + $paginate = 'none'; + } + $page = max(1, (int) ($request->get_param('page') ?? 1)); $tab = [ 'title' => '', 'category_id' => max(0, (int) ($request->get_param('cat') ?? $request->get_param('category_id') ?? 0)), @@ -83,15 +94,31 @@ class WidgetAjax { 'count' => max(0, min(20, (int) ($request->get_param('tab_count') ?? 0))), 'enabled' => true, ]; - $html = \AKSRT\Widgets\TabPostsWidget::render_tab_html($tab, $default_count); + $opts = [ + 'style' => $style, + 'paginate' => $paginate, + 'show_thumb' => $request->get_param('show_thumb') === null ? 1 : (int) $request->get_param('show_thumb'), + 'show_meta' => $request->get_param('show_meta') === null ? 1 : (int) $request->get_param('show_meta'), + 'show_number' => (int) ($request->get_param('show_number') ?? 0), + ]; + $payload = \AKSRT\Widgets\TabPostsWidget::render_tab_payload($tab, $default_count, $opts, $page); + $html = (string) ($payload['html'] ?? ''); if ($html === '') { $html = function_exists('aksrt_get_ajax_null') ? aksrt_get_ajax_null(__('暂无文章', 'aksrt')) : '

' . esc_html__('暂无文章', 'aksrt') . '

'; } else { - $html = '
' . $html . '
'; + $html = '
' . $html . '
'; } - return new \WP_REST_Response(['success' => true, 'html' => $html], 200); + return new \WP_REST_Response([ + 'success' => true, + 'html' => $html, + 'paginate_html' => (string) ($payload['paginate_html'] ?? ''), + 'has_more' => !empty($payload['has_more']), + 'page' => (int) ($payload['page'] ?? $page), + 'max_pages' => (int) ($payload['max_pages'] ?? 1), + 'total' => (int) ($payload['total'] ?? 0), + ], 200); } public function rest_paid_posts(\WP_REST_Request $request) { @@ -181,6 +208,8 @@ class WidgetAjax { 'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')), 'cat' => max(0, (int) ($request->get_param('cat') ?? 0)), 'limit_day' => max(0, (int) ($request->get_param('limit_day') ?? 0)), + 'topics' => sanitize_text_field((string) ($request->get_param('topics') ?? '')), + 'pay_type' => sanitize_key((string) ($request->get_param('pay_type') ?? '')), ]; $html = \AKSRT\Widgets\WidgetOnelinePosts::render_html($instance); return new \WP_REST_Response(['success' => true, 'html' => $html], 200); @@ -193,7 +222,7 @@ class WidgetAjax { $instance = [ 'count' => max(1, min(20, (int) ($request->get_param('count') ?? 5))), 'show_avatar' => $request->get_param('show_avatar') === null ? 1 : (int) $request->get_param('show_avatar'), - 'outpost' => max(0, (int) ($request->get_param('outpost') ?? 0)), + 'outpost' => sanitize_text_field((string) ($request->get_param('outpost') ?? '')), 'outer' => sanitize_text_field((string) ($request->get_param('outer') ?? '')), ]; $html = \AKSRT\Widgets\RecentCommentsWidget::render_html($instance); diff --git a/src/AKSRT/Widgets/HotPostsWidget.php b/src/AKSRT/Widgets/HotPostsWidget.php index 430635bd..8d452688 100644 --- a/src/AKSRT/Widgets/HotPostsWidget.php +++ b/src/AKSRT/Widgets/HotPostsWidget.php @@ -70,48 +70,83 @@ class HotPostsWidget extends \WP_Widget { if (!$query->have_posts()) { return ''; } - $show_thumb = !empty($instance['show_thumb']); - $order_by = sanitize_key((string) ($instance['order_by'] ?? 'views')); - $placeholder = get_template_directory_uri() . '/assets/img/placeholder.svg'; + $show_thumb = !isset($instance['show_thumb']) || !empty($instance['show_thumb']); + $order_by = sanitize_key((string) ($instance['order_by'] ?? 'views')); + $placeholder = get_template_directory_uri() . '/assets/img/placeholder.svg'; + $top_tones = ['jb-red', 'jb-yellow', 'b-gray']; ob_start(); $index = $start; while ($query->have_posts()) { $query->the_post(); $index++; - $rank_class = $index <= 3 ? ' rank-top' : ''; - $pid = get_the_ID(); - if ($order_by === 'comments') { - $metric = (int) get_comments_number($pid); - $metric_icon = 'fa-comment-o'; - } elseif ($order_by === 'likes') { - $metric = function_exists('aksrt_get_post_likes') ? (int) aksrt_get_post_likes($pid) : (int) get_post_meta($pid, '_aksrt_like_count', true); - $metric_icon = 'fa-heart'; - } elseif ($order_by === 'favorites') { - $metric = (int) get_post_meta($pid, '_aksrt_favorite_count', true); - $metric_icon = 'fa-star'; - } elseif ($order_by === 'downloads') { - $metric = (int) get_post_meta($pid, '_aksrt_res_download_count', true); - $metric_icon = 'fa-download'; - } else { - $metric = (int) get_post_meta($pid, 'aksrt_post_views', true); - $metric_icon = 'fa-eye'; + $pid = (int) get_the_ID(); + if ($order_by === 'comments') { + $metric = (int) get_comments_number($pid); + $metric_icon = 'fa-comment-o'; + $metric_label = __('条评论', 'aksrt'); + } elseif ($order_by === 'likes') { + $metric = function_exists('aksrt_get_post_likes') ? (int) aksrt_get_post_likes($pid) : (int) get_post_meta($pid, '_aksrt_like_count', true); + $metric_icon = 'fa-heart'; + $metric_label = __('人点赞', 'aksrt'); + } elseif ($order_by === 'favorites') { + $metric = (int) get_post_meta($pid, '_aksrt_favorite_count', true); + $metric_icon = 'fa-star'; + $metric_label = __('人收藏', 'aksrt'); + } elseif ($order_by === 'downloads') { + $metric = (int) get_post_meta($pid, '_aksrt_res_download_count', true); + $metric_icon = 'fa-download'; + $metric_label = __('次下载', 'aksrt'); + } else { + $metric = (int) get_post_meta($pid, 'aksrt_post_views', true); + $metric_icon = 'fa-eye'; + $metric_label = __('次阅读', 'aksrt'); + } + $metric_txt = function_exists('aksrt_format_number') ? aksrt_format_number($metric) : (string) $metric; + $tone = $top_tones[$index - 1] ?? 'b-gray'; + $top_badge = 'TOP' . (int) $index . ''; + $permalink = get_permalink($pid); + $title = get_the_title($pid); + $time_ago = human_time_diff(get_the_time('U'), current_time('timestamp')) . __('前', 'aksrt'); + + // Graphic hero for #1 (zibll zib_hot_posts). + if ($index === 1 && $start === 0) { + $thumb = get_the_post_thumbnail_url($pid, 'large'); + if (!$thumb) { + $thumb = get_the_post_thumbnail_url($pid, 'medium'); } - $metric_txt = function_exists('aksrt_format_number') ? aksrt_format_number($metric) : (string) $metric; + echo '
'; + echo ''; + echo '
'; + if ($thumb) { + echo ''; + } else { + echo ''; + } + echo '
'; + echo '
'; + echo '
' . esc_html($metric_txt . $metric_label) . '
'; + echo '
' . esc_html(wp_html_excerpt($title, 48, '…')) . '
'; + echo '
'; + echo $top_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo '
'; + continue; + } + + $rank_class = $index <= 3 ? ' rank-top' : ''; + $thumb = $show_thumb ? get_the_post_thumbnail_url($pid, 'thumbnail') : ''; ?> -
- - - - +
+ + + + -
- -
+
+
+
+ -
@@ -121,16 +156,14 @@ class HotPostsWidget extends \WP_Widget { return (string) ob_get_clean(); } - /** - * @return array{html:string,paginate_html:string,has_more:bool,page:int,max_pages:int,total:int} - */ + public static function query_payload(array $instance, int $page = 1): array { $page = max(1, $page); $count = max(1, min(20, (int) ($instance['count'] ?? 5))); $paginate = sanitize_key((string) ($instance['paginate'] ?? 'ajax')); $q = new \WP_Query(self::build_query_args($instance, $page)); $items = self::render_items_html($q, $instance, ($page - 1) * $count); - $html = $items !== '' ? '
' . $items . '
' : ''; + $html = $items !== '' ? '
' . $items . '
' : ''; if ($html === '' && $page === 1) { $html = function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无热门文章', 'aksrt')) diff --git a/src/AKSRT/Widgets/RecentCommentsWidget.php b/src/AKSRT/Widgets/RecentCommentsWidget.php index 34a49134..b7c8dd05 100644 --- a/src/AKSRT/Widgets/RecentCommentsWidget.php +++ b/src/AKSRT/Widgets/RecentCommentsWidget.php @@ -56,10 +56,16 @@ class RecentCommentsWidget extends \WP_Widget { 'orderby' => 'comment_date_gmt', 'order' => 'DESC', ]; - // Include forum topics when forum is active. - if (post_type_exists('forum_topic') || post_type_exists('forum_post')) { + // Include forum topics / products when active. + if (post_type_exists('aksrt_forum_topic')) { + $query['post_type'][] = 'aksrt_forum_topic'; + } elseif (post_type_exists('forum_topic') || post_type_exists('forum_post')) { + // Legacy CPT names. $query['post_type'][] = post_type_exists('forum_topic') ? 'forum_topic' : 'forum_post'; } + if (post_type_exists('aksrt_product')) { + $query['post_type'][] = 'aksrt_product'; + } if ($post_not_in) { $query['post__not_in'] = $post_not_in; } @@ -126,7 +132,8 @@ class RecentCommentsWidget extends \WP_Widget { $avatar_url = $user_id > 0 && function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user_id, ['size' => 42]) : get_avatar_url($comment, ['size' => 42]); - $avatar = '
'; + $ph = get_template_directory_uri() . '/assets/img/placeholder.svg'; + $avatar = '
'; } $like = function_exists('aksrt_get_comment_like_html') diff --git a/src/AKSRT/Widgets/TabPostsWidget.php b/src/AKSRT/Widgets/TabPostsWidget.php index 24054024..1e9dcdb9 100644 --- a/src/AKSRT/Widgets/TabPostsWidget.php +++ b/src/AKSRT/Widgets/TabPostsWidget.php @@ -93,30 +93,72 @@ class TabPostsWidget extends \WP_Widget { * @param int $default_count * @return array */ - public static function tab_to_main_instance(array $tab, int $default_count): array { + public static function tab_to_main_instance(array $tab, int $default_count, array $opts = []): array { $count = !empty($tab['count']) ? (int) $tab['count'] : $default_count; + $style = sanitize_key((string) ($opts['style'] ?? $tab['style'] ?? 'mini')); + if (!in_array($style, ['list', 'card', 'mini'], true)) { + $style = 'mini'; + } + $paginate = sanitize_key((string) ($opts['paginate'] ?? $tab['paginate'] ?? 'none')); + if (!in_array($paginate, ['none', 'ajax', 'number'], true)) { + $paginate = 'none'; + } return [ - 'count' => max(1, min(30, $count)), - 'orderby' => sanitize_key((string) ($tab['orderby'] ?? 'date')), - 'cat' => absint($tab['category_id'] ?? 0), - 'style' => 'mini', - 'show_thumb' => 1, - 'show_meta' => 1, - 'show_number'=> 0, - 'paginate' => 'none', + 'count' => max(1, min(30, $count)), + 'orderby' => sanitize_key((string) ($tab['orderby'] ?? 'date')), + 'cat' => absint($tab['category_id'] ?? 0), + 'style' => $style, + 'show_thumb' => !isset($opts['show_thumb']) ? 1 : (int) $opts['show_thumb'], + 'show_meta' => !isset($opts['show_meta']) ? 1 : (int) $opts['show_meta'], + 'show_number' => !empty($opts['show_number']) ? 1 : 0, + 'paginate' => $paginate, ]; } /** * REST helper: render one tab list HTML. + * + * @param array $opts */ - public static function render_tab_html(array $tab, int $default_count): string { - $inst = self::tab_to_main_instance($tab, $default_count); + public static function render_tab_html(array $tab, int $default_count, array $opts = []): string { + $payload = self::render_tab_payload($tab, $default_count, $opts, 1); + return (string) ($payload['html'] ?? ''); + } + + /** + * Full tab payload (html + pager) for REST/ajax. + * + * @param array $opts + * @return array{html:string,paginate_html:string,has_more:bool,page:int,max_pages:int,total:int} + */ + public static function render_tab_payload(array $tab, int $default_count, array $opts = [], int $page = 1): array { + $inst = self::tab_to_main_instance($tab, $default_count, $opts); if (class_exists('\AKSRT\Widgets\WidgetMainPosts')) { - $payload = WidgetMainPosts::query_payload($inst, 1); - return (string) ($payload['html'] ?? ''); + return WidgetMainPosts::query_payload($inst, $page); } - return ''; + return [ + 'html' => '', + 'paginate_html' => '', + 'has_more' => false, + 'page' => 1, + 'max_pages' => 1, + 'total' => 0, + ]; + } + + /** Skeleton rows for lazy tab panels. */ + public static function skeleton_html(int $count = 5, string $style = 'mini'): string { + $count = max(1, min(12, $count)); + ob_start(); + echo '
'; + for ($i = 0; $i < $count; $i++) { + echo '
'; + echo '
'; + echo '
'; + echo '
'; + } + echo '
'; + return (string) ob_get_clean(); } public function widget($args, $instance): void { @@ -124,6 +166,14 @@ class TabPostsWidget extends \WP_Widget { $title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base); $count = absint($instance['count'] ?? 5); + $style = sanitize_key((string) ($instance['style'] ?? 'mini')); + if (!in_array($style, ['list', 'card', 'mini'], true)) { + $style = 'mini'; + } + $paginate = sanitize_key((string) ($instance['paginate'] ?? 'none')); + if (!in_array($paginate, ['none', 'ajax', 'number'], true)) { + $paginate = 'none'; + } $tabs = $this->normalize_tabs($instance['tabs'] ?? 'latest,comments,random'); $tabs = array_values(array_filter($tabs, static fn($t) => !empty($t['enabled']))); if (!$tabs) { @@ -147,7 +197,9 @@ class TabPostsWidget extends \WP_Widget {
+ data-count="" + data-style="" + data-paginate="">
    $tab) : $tab_key = 'tab-' . $i; @@ -175,24 +227,20 @@ class TabPostsWidget extends \WP_Widget { data-loaded="" role="tabpanel"> ' . $html . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - } else { - // Fallback to old mini list path. - $q = new \WP_Query(WidgetMainPosts::build_query_args(self::tab_to_main_instance($tab, $count), 1)); - if ($q->have_posts() && $use_shared) { - echo '
'; - aksrt_render_mini_list($q, ['context' => 'sidebar', 'show_thumb' => true, 'show_meta' => true], true); - echo '
'; + $opts = ['style' => $style, 'paginate' => $paginate]; + $payload = self::render_tab_payload($tab, $count, $opts, 1); + $html = (string) ($payload['html'] ?? ''); + if ($html !== '') { + echo '
' . $html . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + if (!empty($payload['paginate_html'])) { + echo $payload['paginate_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } } else { echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无文章', 'aksrt')) : '

' . esc_html__('暂无文章', 'aksrt') . '

'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } - wp_reset_postdata(); - } - else : ?> -
- + else : ?> +
+
@@ -223,6 +271,24 @@ class TabPostsWidget extends \WP_Widget {

+

+ +

+

+ +


sanitize_text_field((string) ($new_instance['title'] ?? '')), - 'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))), + 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')), + 'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))), + 'style' => $style, + 'paginate' => $paginate, ]; $tabs = []; if (isset($new_instance['tabs']) && is_array($new_instance['tabs'])) { diff --git a/src/AKSRT/Widgets/TagCloudWidget.php b/src/AKSRT/Widgets/TagCloudWidget.php index 862a7341..07ce035c 100644 --- a/src/AKSRT/Widgets/TagCloudWidget.php +++ b/src/AKSRT/Widgets/TagCloudWidget.php @@ -52,7 +52,17 @@ class TagCloudWidget extends \WP_Widget { $palette = ['c-blue', 'c-yellow', 'c-green', 'c-purple', 'c-red', '', 'c-blue-2', 'c-yellow-2', 'c-green-2', 'c-purple-2', 'c-red-2', '']; $i = random_int(0, 10); - $wrap = 'tag-cloud aksrt-tag-cloud author-tag ajax-item' . ($fixed_width ? ' fixed-width' : ''); + $scale = !empty($instance['scale']); + $min_c = null; + $max_c = null; + if ($scale) { + foreach ($tags as $t0) { + $c0 = (int) $t0->count; + $min_c = $min_c === null ? $c0 : min($min_c, $c0); + $max_c = $max_c === null ? $c0 : max($max_c, $c0); + } + } + $wrap = 'zib-widget widget-tag-cloud tag-cloud aksrt-tag-cloud author-tag ajax-item' . ($fixed_width ? ' fixed-width' : ''); ob_start(); echo '
'; foreach ($tags as $tag) { @@ -71,8 +81,15 @@ class TagCloudWidget extends \WP_Widget { $cls .= ' ' . $color; } $tgt = $blank ? ' target="_blank" rel="noopener noreferrer"' : ''; - $count = $show_count ? ' (' . (int) $tag->count . ')' : ''; - echo '' . esc_html($tag->name) . $count . ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + $count_html = $show_count ? ' (' . (int) $tag->count . ')' : ''; + $style_attr = ''; + if ($scale && $max_c !== null && $min_c !== null) { + $span = max(1, $max_c - $min_c); + $ratio = ((int) $tag->count - $min_c) / $span; + $em = round(0.85 + ($ratio * 0.4), 3); + $style_attr = ' style="font-size:' . esc_attr((string) $em) . 'em;--tag-scale:' . esc_attr((string) $em) . ';"'; + } + echo '' . esc_html($tag->name) . $count_html . ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } echo '
'; return (string) ob_get_clean(); diff --git a/src/AKSRT/Widgets/WidgetMainPosts.php b/src/AKSRT/Widgets/WidgetMainPosts.php index a42e3b1b..da78f0a8 100644 --- a/src/AKSRT/Widgets/WidgetMainPosts.php +++ b/src/AKSRT/Widgets/WidgetMainPosts.php @@ -61,6 +61,16 @@ class WidgetMainPosts extends \WP_Widget { $query['orderby'] = 'meta_value_num'; $query['order'] = 'DESC'; break; + case 'favorites': + $query['meta_key'] = '_aksrt_favorite_count'; + $query['orderby'] = 'meta_value_num'; + $query['order'] = 'DESC'; + break; + case 'downloads': + $query['meta_key'] = '_aksrt_res_download_count'; + $query['orderby'] = 'meta_value_num'; + $query['order'] = 'DESC'; + break; case 'rand': $query['orderby'] = 'rand'; break; @@ -72,6 +82,60 @@ class WidgetMainPosts extends \WP_Widget { $query['orderby'] = 'date'; $query['order'] = 'DESC'; } + + // Topics taxonomy (CSV term IDs). + $topics_raw = trim((string) ($instance['topics'] ?? '')); + if ($topics_raw !== '' && taxonomy_exists('topics')) { + $topic_ids = array_values(array_filter(array_map('intval', preg_split('/[,,\s]+/', $topics_raw) ?: []))); + if ($topic_ids) { + $query['tax_query'] = [ + [ + 'taxonomy' => 'topics', + 'field' => 'term_id', + 'terms' => $topic_ids, + ], + ]; + } + } + + // Pay type filter (reading/download/gallery/video/vip or any paid). + $pay_type = sanitize_key((string) ($instance['pay_type'] ?? '')); + if ($pay_type !== '' && $pay_type !== '0' && $pay_type !== 'no') { + $mq = isset($query['meta_query']) && is_array($query['meta_query']) ? $query['meta_query'] : []; + if ($pay_type === 'any' || $pay_type === 'paid') { + $mq[] = [ + 'relation' => 'OR', + [ + 'key' => '_aksrt_pay_price', + 'value' => 0, + 'compare' => '>', + 'type' => 'NUMERIC', + ], + [ + 'key' => '_aksrt_pay_type', + 'value' => ['reading', 'download', 'gallery', 'video', 'vip'], + 'compare' => 'IN', + ], + [ + 'key' => '_aksrt_visibility', + 'value' => ['pay', 'credits'], + 'compare' => 'IN', + ], + ]; + } else { + $mq[] = [ + 'key' => '_aksrt_pay_type', + 'value' => $pay_type, + ]; + } + if ($mq) { + if (count($mq) > 1) { + $mq['relation'] = 'AND'; + } + $query['meta_query'] = $mq; + } + } + return $query; } @@ -179,23 +243,32 @@ class WidgetMainPosts extends \WP_Widget { $max_pages = max(1, (int) $q->max_num_pages); $total = (int) $q->found_posts; $has_more = $page < $max_pages; + $base_qs = array_filter([ + 'count' => $count, + 'orderby' => sanitize_key((string) ($instance['orderby'] ?? 'date')), + 'cat' => (int) ($instance['cat'] ?? 0), + 'limit_day' => (int) ($instance['limit_day'] ?? 0), + 'topics' => (string) ($instance['topics'] ?? ''), + 'pay_type' => sanitize_key((string) ($instance['pay_type'] ?? '')), + 'style' => sanitize_key((string) ($instance['style'] ?? 'list')), + 'show_thumb' => !isset($instance['show_thumb']) || !empty($instance['show_thumb']) ? 1 : 0, + 'show_meta' => !isset($instance['show_meta']) || !empty($instance['show_meta']) ? 1 : 0, + 'show_number' => !empty($instance['show_number']) ? 1 : 0, + 'paginate' => $paginate, + ], static function ($v) { + return $v !== '' && $v !== null; + }); + $rest_base = add_query_arg($base_qs, rest_url('aksrt/v1/widgets/posts')); $paginate_html = ''; if ($paginate === 'ajax' && $has_more) { - $next = add_query_arg(array_filter([ - 'page' => $page + 1, - 'count' => $count, - 'orderby' => sanitize_key((string) ($instance['orderby'] ?? 'date')), - 'cat' => (int) ($instance['cat'] ?? 0), - 'limit_day' => (int) ($instance['limit_day'] ?? 0), - 'style' => sanitize_key((string) ($instance['style'] ?? 'list')), - 'show_thumb' => !isset($instance['show_thumb']) || !empty($instance['show_thumb']) ? 1 : 0, - 'show_meta' => !isset($instance['show_meta']) || !empty($instance['show_meta']) ? 1 : 0, - 'show_number' => !empty($instance['show_number']) ? 1 : 0, - 'paginate' => 'ajax', - ]), rest_url('aksrt/v1/widgets/posts')); + $next = add_query_arg(['page' => $page + 1], $rest_base); $paginate_html = function_exists('aksrt_get_ajax_next_paginate') ? aksrt_get_ajax_next_paginate($total, $page, $count, $next) : '
'; + } elseif ($paginate === 'number' && $max_pages > 1) { + $paginate_html = function_exists('aksrt_get_ajax_number_paginate') + ? aksrt_get_ajax_number_paginate($total, $page, $count, $rest_base) + : '
'; } else { $paginate_html = '
'; } @@ -230,7 +303,11 @@ class WidgetMainPosts extends \WP_Widget { } $rest = rest_url('aksrt/v1/widgets/posts'); - echo '
'; + $wrap_cls = 'aksrt-main-posts style-' . $style . ' ajaxpager'; + if ($style === 'mini') { + $wrap_cls .= ' zib-widget posts-mini-lists'; + } + echo '
'; echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $payload['paginate_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo ''; @@ -268,6 +345,8 @@ class WidgetMainPosts extends \WP_Widget { + + @@ -287,6 +366,25 @@ class WidgetMainPosts extends \WP_Widget { + +

+

+ +

+

+

@@ -304,9 +402,13 @@ class WidgetMainPosts extends \WP_Widget { $style = 'list'; } $orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'date')); - if (!in_array($orderby, ['date', 'modified', 'views', 'comments', 'likes', 'rand'], true)) { + if (!in_array($orderby, ['date', 'modified', 'views', 'comments', 'likes', 'favorites', 'downloads', 'rand'], true)) { $orderby = 'date'; } + $paginate = sanitize_key((string) ($new_instance['paginate'] ?? 'none')); + if (!in_array($paginate, ['none', 'ajax', 'number'], true)) { + $paginate = 'none'; + } return [ 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')), 'count' => max(1, min(30, (int) ($new_instance['count'] ?? 6))), @@ -314,7 +416,9 @@ class WidgetMainPosts extends \WP_Widget { 'orderby' => $orderby, 'cat' => max(0, (int) ($new_instance['cat'] ?? 0)), 'limit_day' => max(0, (int) ($new_instance['limit_day'] ?? 0)), - 'paginate' => sanitize_key((string) ($new_instance['paginate'] ?? 'none')) === 'ajax' ? 'ajax' : 'none', + 'topics' => sanitize_text_field((string) ($new_instance['topics'] ?? '')), + 'pay_type' => sanitize_key((string) ($new_instance['pay_type'] ?? '')), + 'paginate' => $paginate, 'show_thumb' => !empty($new_instance['show_thumb']) ? 1 : 0, 'show_meta' => !empty($new_instance['show_meta']) ? 1 : 0, 'show_number' => !empty($new_instance['show_number']) ? 1 : 0, diff --git a/src/AKSRT/Widgets/WidgetOnelinePosts.php b/src/AKSRT/Widgets/WidgetOnelinePosts.php index e942f9bb..68a3f343 100644 --- a/src/AKSRT/Widgets/WidgetOnelinePosts.php +++ b/src/AKSRT/Widgets/WidgetOnelinePosts.php @@ -63,6 +63,30 @@ class WidgetOnelinePosts extends \WP_Widget { $query['orderby'] = 'date'; $query['order'] = 'DESC'; } + $topics_raw = trim((string) ($instance['topics'] ?? '')); + if ($topics_raw !== '' && taxonomy_exists('topics')) { + $topic_ids = array_values(array_filter(array_map('intval', preg_split('/[,,\s]+/', $topics_raw) ?: []))); + if ($topic_ids) { + $query['tax_query'] = [[ + 'taxonomy' => 'topics', + 'field' => 'term_id', + 'terms' => $topic_ids, + ]]; + } + } + $pay_type = sanitize_key((string) ($instance['pay_type'] ?? '')); + if ($pay_type !== '' && $pay_type !== '0' && $pay_type !== 'no') { + if ($pay_type === 'any' || $pay_type === 'paid') { + $query['meta_query'] = [ + 'relation' => 'OR', + ['key' => '_aksrt_pay_price', 'value' => 0, 'compare' => '>', 'type' => 'NUMERIC'], + ['key' => '_aksrt_pay_type', 'value' => ['reading', 'download', 'gallery', 'video', 'vip'], 'compare' => 'IN'], + ['key' => '_aksrt_visibility', 'value' => ['pay', 'credits'], 'compare' => 'IN'], + ]; + } else { + $query['meta_query'] = [['key' => '_aksrt_pay_type', 'value' => $pay_type]]; + } + } return $query; } @@ -212,6 +236,21 @@ class WidgetOnelinePosts extends \WP_Widget {

+

+ +

+

+ +

$orderby, 'cat' => max(0, (int) ($new_instance['cat'] ?? 0)), 'limit_day' => max(0, (int) ($new_instance['limit_day'] ?? 0)), + 'topics' => sanitize_text_field((string) ($new_instance['topics'] ?? '')), + 'pay_type' => sanitize_key((string) ($new_instance['pay_type'] ?? '')), ]; } } diff --git a/src/AKSRT/Widgets/WidgetPaidPosts.php b/src/AKSRT/Widgets/WidgetPaidPosts.php index e4a35b26..17b4ad7a 100644 --- a/src/AKSRT/Widgets/WidgetPaidPosts.php +++ b/src/AKSRT/Widgets/WidgetPaidPosts.php @@ -61,6 +61,14 @@ class WidgetPaidPosts extends \WP_Widget { } else { $query['orderby'] = 'date'; } + $cat = max(0, (int) ($instance['cat'] ?? 0)); + if ($cat > 0) { + $query['cat'] = $cat; + } + $limit_day = max(0, (int) ($instance['limit_day'] ?? 0)); + if ($limit_day > 0) { + $query['date_query'] = [['after' => $limit_day . ' days ago']]; + } return $query; } @@ -157,9 +165,6 @@ class WidgetPaidPosts extends \WP_Widget { $paginate = sanitize_key((string) ($instance['paginate'] ?? 'ajax')); $instance['paginate'] = $paginate; $payload = self::query_payload($instance, 1); - if ($payload['html'] === '' && empty($payload['total'])) { - return; - } echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped $title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base); @@ -206,6 +211,16 @@ class WidgetPaidPosts extends \WP_Widget {

+

+ +

+

+ +

sanitize_text_field((string) ($new_instance['title'] ?? '')), 'count' => max(1, min(20, (int) ($new_instance['count'] ?? 6))), - 'orderby' => $orderby, + 'orderby' => $orderby, + 'cat' => max(0, (int) ($new_instance['cat'] ?? 0)), + 'limit_day' => max(0, (int) ($new_instance['limit_day'] ?? 0)), 'paginate' => sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) === 'none' ? 'none' : 'ajax', ]; } diff --git a/src/AKSRT/Widgets/WidgetProductList.php b/src/AKSRT/Widgets/WidgetProductList.php index 10cceac7..c9c67078 100644 --- a/src/AKSRT/Widgets/WidgetProductList.php +++ b/src/AKSRT/Widgets/WidgetProductList.php @@ -189,14 +189,32 @@ class WidgetProductList extends \WP_Widget { $paginate_html = ''; } } elseif ($paginate === 'number' && $max_pages > 1) { - $paginate_html = '
' . paginate_links([ - 'base' => add_query_arg('aksrt_product_page', '%#%'), - 'format' => '', - 'current' => $page, - 'total' => $max_pages, - 'prev_text' => '«', - 'next_text' => '»', - ]) . '
'; + $num_base = add_query_arg( + array_filter([ + 'per_page' => max(1, (int) ($instance['per_page'] ?? 8)), + 'orderby' => sanitize_key((string) ($instance['orderby'] ?? 'date')), + 'order' => sanitize_key((string) ($instance['order'] ?? 'DESC')), + 'cat_ids' => (string) ($instance['cat_ids'] ?? ''), + 'tag_ids' => (string) ($instance['tag_ids'] ?? ''), + 'discount_ids' => (string) ($instance['discount_ids'] ?? ''), + 'exclude_cat' => !empty($instance['exclude_cat']) ? 1 : 0, + 'style' => sanitize_key((string) ($instance['style'] ?? 'default')), + 'paginate' => 'number', + ], static function ($v) { + return $v !== '' && $v !== null; + }), + rest_url('aksrt/v1/shop/frontend/widgets/products') + ); + $paginate_html = function_exists('aksrt_get_ajax_number_paginate') + ? aksrt_get_ajax_number_paginate($total, $page, max(1, (int) ($instance['per_page'] ?? 8)), $num_base) + : '
' . paginate_links([ + 'base' => add_query_arg('aksrt_product_page', '%#%'), + 'format' => '', + 'current' => $page, + 'total' => $max_pages, + 'prev_text' => '«', + 'next_text' => '»', + ]) . '
'; } else { $paginate_html = '
'; }