esc_html__('分类封面 + 描述 + 更多文章 CTA + 文章列表聚合卡。', 'aksrt'), 'classname' => 'widget_aksrt_term_aggregation', ] ); } /** @return array */ public static function parse_ids_static(string $raw): array { if ($raw === '') { return []; } $ids = []; foreach (preg_split('/[\s,]+/', $raw) ?: [] as $p) { $id = (int) $p; if ($id > 0) { $ids[] = $id; } } return array_values(array_unique($ids)); } /** * @param \WP_Term $term */ private static function term_cover_url(\WP_Term $term): string { if (class_exists(WidgetTermCards::class) && method_exists(WidgetTermCards::class, 'term_cover_url')) { return WidgetTermCards::term_cover_url($term); } $img_id = (int) get_term_meta($term->term_id, 'thumbnail_id', true); if (!$img_id) { $img_id = (int) get_term_meta($term->term_id, '_aksrt_cover', true); } if ($img_id) { $url = wp_get_attachment_image_url($img_id, 'medium'); if ($url) { return (string) $url; } } $url = (string) get_term_meta($term->term_id, '_aksrt_cover_url', true); return $url !== '' ? $url : get_template_directory_uri() . '/assets/img/placeholder.svg'; } /** * Sum of post views under a term (best-effort). */ private static function term_views_sum(int $term_id, string $taxonomy): int { $q = new \WP_Query([ 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 50, 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => true, 'update_post_term_cache' => false, 'tax_query' => [[ 'taxonomy' => $taxonomy, 'field' => 'term_id', 'terms' => $term_id, ]], ]); $sum = 0; foreach ($q->posts as $pid) { $sum += function_exists('aksrt_get_post_views') ? (int) aksrt_get_post_views((int) $pid) : (int) get_post_meta((int) $pid, 'aksrt_post_views', true); } return $sum; } /** * @param array $instance */ public static function render_html(array $instance): string { $taxonomy = sanitize_key((string) ($instance['taxonomy'] ?? 'category')); if (!taxonomy_exists($taxonomy)) { $taxonomy = 'category'; } $term_ids = self::parse_ids_static((string) ($instance['term_ids'] ?? '')); $count = max(1, min(12, (int) ($instance['count'] ?? 4))); $orderby = sanitize_key((string) ($instance['orderby'] ?? 'date')); $columns = max(1, min(3, (int) ($instance['columns'] ?? 1))); $target_blank = !empty($instance['target_blank']); $tgt = $target_blank ? ' target="_blank" rel="noopener noreferrer"' : ''; if (!$term_ids) { $terms = get_terms([ 'taxonomy' => $taxonomy, 'number' => max(1, min(4, (int) ($instance['auto_terms'] ?? 2))), 'orderby' => 'count', 'order' => 'DESC', 'hide_empty' => true, ]); if (is_wp_error($terms) || !$terms) { return function_exists('aksrt_get_ajax_null') ? aksrt_get_ajax_null(__('暂无分类', 'aksrt')) : '

' . esc_html__('暂无分类', 'aksrt') . '

'; } $term_ids = array_map(static fn($t) => (int) $t->term_id, $terms); } ob_start(); echo '
'; foreach ($term_ids as $term_id) { $term = get_term($term_id, $taxonomy); if (!$term || is_wp_error($term)) { continue; } $link = get_term_link($term); if (is_wp_error($link)) { continue; } $cover = self::term_cover_url($term); $views = self::term_views_sum((int) $term->term_id, $taxonomy); $more_badge = ' ' . esc_html((string) $views) . ''; $img_html = ''; if (function_exists('aksrt_graphic_card')) { $img_html = aksrt_graphic_card([ 'type' => '', 'class' => '', 'img' => $cover, 'alt' => $term->name, 'link' => [ 'url' => $link, 'target' => $target_blank ? '_blank' : '', ], 'lazy' => true, 'height_scale' => 70, 'mask_opacity' => 0, 'more' => $more_badge, ]); } $description = trim((string) $term->description); if ($description === '' && current_user_can('manage_categories')) { $edit = get_edit_term_link((int) $term->term_id, $taxonomy); $description = esc_html__('请在后台为该分类添加描述。', 'aksrt'); if ($edit) { $description .= ' ' . esc_html__('立即编辑', 'aksrt') . ''; } } else { $description = esc_html($description); } $posts_q = [ 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => $count, 'ignore_sticky_posts' => true, 'no_found_rows' => false, 'tax_query' => [[ 'taxonomy' => $taxonomy, 'field' => 'term_id', 'terms' => (int) $term->term_id, ]], ]; if ($orderby === 'views') { $posts_q['meta_key'] = 'aksrt_post_views'; $posts_q['orderby'] = 'meta_value_num'; $posts_q['order'] = 'DESC'; } elseif ($orderby === 'like') { $posts_q['meta_key'] = 'aksrt_post_likes'; $posts_q['orderby'] = 'meta_value_num'; $posts_q['order'] = 'DESC'; } elseif ($orderby === 'comment_count') { $posts_q['orderby'] = 'comment_count'; $posts_q['order'] = 'DESC'; } elseif ($orderby === 'modified') { $posts_q['orderby'] = 'modified'; $posts_q['order'] = 'DESC'; } else { $posts_q['orderby'] = 'date'; $posts_q['order'] = 'DESC'; } $q = new \WP_Query($posts_q); $found = (int) $q->found_posts; echo '
'; echo '
'; echo '
' . $img_html . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo '
'; echo '' . esc_html($term->name) . ''; echo '
' . $description . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo '
'; echo '' . esc_html(sprintf( /* translators: %s: article count */ __('%s篇文章', 'aksrt'), (string) $found )) . ''; echo '' . esc_html__('更多文章', 'aksrt') . ''; echo '
'; if ($q->have_posts()) { while ($q->have_posts()) { $q->the_post(); $pid = get_the_ID(); $title = get_the_title($pid); $meta = ''; if ($orderby === 'views') { $v = function_exists('aksrt_get_post_views') ? (int) aksrt_get_post_views($pid) : (int) get_post_meta($pid, 'aksrt_post_views', true); $meta = '' . esc_html((string) $v); } elseif ($orderby === 'like') { $v = function_exists('aksrt_get_post_likes') ? (int) aksrt_get_post_likes($pid) : (int) get_post_meta($pid, 'aksrt_post_likes', true); $meta = '' . esc_html((string) $v); } elseif ($orderby === 'comment_count') { $meta = '' . esc_html((string) get_comments_number($pid)); } else { $ts = $orderby === 'modified' ? get_the_modified_time('U', $pid) : get_the_time('U', $pid); $ago = human_time_diff((int) $ts, current_time('timestamp')); $meta = '' . esc_html($ago); } echo '
'; echo ''; echo '
' . $meta . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo '
'; } wp_reset_postdata(); } echo '
'; } echo '
'; return (string) ob_get_clean(); } /** * @param array $args * @param array $instance */ public function widget($args, $instance): void { $html = self::render_html($instance); if ($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 } echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** * @param array $instance */ public function form($instance): void { $taxs = get_taxonomies(['public' => true], 'objects'); ?>

$new_instance * @param array $old_instance * @return array */ public function update($new_instance, $old_instance): array { $orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'date')); if (!in_array($orderby, ['date', 'modified', 'views', 'like', 'comment_count'], true)) { $orderby = 'date'; } return [ 'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')), 'taxonomy' => sanitize_key((string) ($new_instance['taxonomy'] ?? 'category')), 'term_ids' => sanitize_text_field((string) ($new_instance['term_ids'] ?? '')), 'count' => max(1, min(12, (int) ($new_instance['count'] ?? 4))), 'columns' => max(1, min(3, (int) ($new_instance['columns'] ?? 1))), 'orderby' => $orderby, 'auto_terms' => max(1, min(4, (int) ($new_instance['auto_terms'] ?? 2))), 'target_blank' => !empty($new_instance['target_blank']) ? 1 : 0, ]; } }