*/ function aksrt_home_widget_allowed_layouts(): array { return [ 'card' => __('卡片', 'aksrt'), 'list' => __('列表', 'aksrt'), 'grid-cms' => __('资源网格', 'aksrt'), ]; } } if (!function_exists('aksrt_home_widget_post_types')) { /** * Post types used by homepage widgets. * * @return array */ function aksrt_home_widget_post_types(): array { return ['post']; } } if (!function_exists('aksrt_home_widget_available_categories')) { /** * Return categories for widget admin forms. * * @return array */ function aksrt_home_widget_available_categories(): array { $categories = get_categories([ 'taxonomy' => 'category', 'hide_empty' => false, 'orderby' => 'name', 'order' => 'ASC', ]); return is_array($categories) ? $categories : []; } } if (!function_exists('aksrt_home_widget_fallback_category_ids')) { /** * Pick top categories as fallback when widget instance has no selection. * * @param int $limit Maximum category ids to return. * @return array */ function aksrt_home_widget_fallback_category_ids(int $limit = 1): array { $limit = max(1, min(8, $limit)); $terms = get_terms([ 'taxonomy' => 'category', 'orderby' => 'count', 'order' => 'DESC', 'number' => $limit, 'hide_empty' => true, 'fields' => 'ids', ]); if (is_wp_error($terms) || !is_array($terms)) { return []; } return array_values(array_filter(array_map('absint', $terms))); } } if (!function_exists('aksrt_home_widget_normalize_category_ids')) { /** * Normalize selected category ids. * * @param mixed $raw_ids Raw ids from widget instance. * @param int $limit Maximum ids to keep. * @return array */ function aksrt_home_widget_normalize_category_ids($raw_ids, int $limit = 4): array { $ids = array_map('absint', (array) $raw_ids); $ids = array_values(array_filter($ids, static fn($id) => $id > 0)); if ($limit > 0) { $ids = array_slice($ids, 0, $limit); } return $ids; } } if (!function_exists('aksrt_home_widget_resolve_layout')) { /** * Sanitize requested widget post layout. * * @param mixed $layout Layout value. * @param string $default Default layout. * @return string */ function aksrt_home_widget_resolve_layout($layout, string $default = 'card'): string { $allowed = array_keys(aksrt_home_widget_allowed_layouts()); $layout = is_string($layout) ? $layout : $default; return in_array($layout, $allowed, true) ? $layout : $default; } } if (!function_exists('aksrt_home_widget_resolve_context')) { /** * Resolve widget area context from sidebar args. */ function aksrt_home_widget_resolve_context(array $args): string { if (function_exists('aksrt_widget_get_area_context')) { return aksrt_widget_get_area_context($args); } return 'homepage'; } } if (!function_exists('aksrt_home_widget_effective_layout')) { /** * Compact rich homepage widgets when placed into a sidebar area. */ function aksrt_home_widget_effective_layout(string $layout, string $context): string { if ($context === 'sidebar' && $layout !== 'list') { return 'list'; } return $layout; } } if (!function_exists('aksrt_home_widget_effective_columns')) { /** * Sidebar widgets should stay single-column. */ function aksrt_home_widget_effective_columns(int $columns, string $context): int { if ($context === 'sidebar') { return 1; } return max(1, min(4, $columns)); } } if (!function_exists('aksrt_home_widget_render_header')) { /** * Render a compact Zibll-like section header. * * @param string $title Section title. * @param string $more_link Optional more link. * @param string $more_text Optional more text. * @return void */ function aksrt_home_widget_render_header(string $title, string $more_link = '', string $more_text = ''): void { ?>

have_posts()) { echo function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无内容', 'aksrt')) : '

' . esc_html__('暂无内容', 'aksrt') . '

'; wp_reset_postdata(); return; } if ($context === 'sidebar') { $use_homepage_mini = function_exists('aksrt_is_fixed_homepage_request') && aksrt_is_fixed_homepage_request() && function_exists('aksrt_render_mini_list') && (!class_exists('\AKSRT\Settings\Settings') || \AKSRT\Settings\Settings::homepage_sidebar_mini_enabled()); if ($use_homepage_mini && function_exists('aksrt_render_mini_list')) { echo '
'; aksrt_render_mini_list($query, ['context' => 'sidebar', 'show_thumb' => true]); echo '
'; return; } // Fallback: shared mini list (lazy thumbs + meta). if (function_exists('aksrt_render_mini_list')) { echo '
'; aksrt_render_mini_list($query, ['context' => 'sidebar', 'show_thumb' => true]); echo '
'; return; } echo '
    '; $ph = get_template_directory_uri() . '/assets/img/placeholder.svg'; while ($query->have_posts()) { $query->the_post(); $thumb = get_the_post_thumbnail_url(get_the_ID(), 'thumbnail'); echo '
  • '; if ($thumb) { echo ''; } echo '
    '; echo '' . esc_html(get_the_title()) . ''; echo '' . esc_html(human_time_diff(get_the_time('U'), current_time('timestamp')) . __('前', 'aksrt')) . ''; echo '
  • '; } echo '
'; wp_reset_postdata(); return; } // Homepage / content area: delegate to shared renderer $layout = aksrt_home_widget_resolve_layout($layout, 'card'); $columns = max(1, min(4, $columns)); if (function_exists('aksrt_render_post_list')) { aksrt_render_post_list($query, [ 'mode' => $layout === 'grid-cms' ? 'grid-cms' : $layout, 'context' => 'widget', 'columns' => $columns, ], true); } else { // Fallback: use template part $previous_layout = get_query_var('aksrt_post_card_layout', ''); $previous_scene = get_query_var('aksrt_post_card_scene', ''); set_query_var('aksrt_post_card_layout', $layout); set_query_var('aksrt_post_card_scene', 'home'); $classes = [ 'aksrt-home-widget__post-grid', 'aksrt-home-widget__post-grid--' . $layout, 'post-grid', 'post-grid--' . $layout, ]; printf( '
', esc_attr(implode(' ', $classes)), (int) $columns ); while ($query->have_posts()) { $query->the_post(); get_template_part('templates/parts/content', 'post-card'); } echo '
'; set_query_var('aksrt_post_card_layout', $previous_layout); set_query_var('aksrt_post_card_scene', $previous_scene); } wp_reset_postdata(); } } if (!function_exists('aksrt_home_widget_get_lazyload_args')) { /** * Lazyload settings reused by category recommend widget. * * @return array{enabled: bool, placeholder_url: string} */ function aksrt_home_widget_get_lazyload_args(): array { $config = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_lazyload_config() : ['enabled' => false, 'placeholder_url' => '']; return [ 'enabled' => ! empty($config['enabled']), 'placeholder_url' => ! empty($config['placeholder_url']) ? (string) $config['placeholder_url'] : get_template_directory_uri() . '/assets/img/placeholder.svg', ]; } } /** * Homepage category block widget. */ class CategoryBlockWidget extends \WP_Widget { public function __construct() { parent::__construct( 'aksrt_category_block', esc_html__('AKSRT 分类块', 'aksrt'), [ 'classname' => 'widget_aksrt_category_block', 'description' => esc_html__('指定分类的最新内容区块,可挂载到首页内容挂载区。', 'aksrt'), 'customize_selective_refresh' => true, ] ); } /** * @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, string $context = 'homepage'): array { $page = max(1, $page); $category_id = self::resolve_category_id_static($instance); $count = max(1, min(12, (int) ($instance['count'] ?? 6))); $columns = aksrt_home_widget_effective_columns((int) ($instance['columns'] ?? 3), $context); $layout = aksrt_home_widget_effective_layout( aksrt_home_widget_resolve_layout($instance['layout'] ?? 'card', 'card'), $context ); $paginate = sanitize_key((string) ($instance['paginate'] ?? 'none')); if (!in_array($paginate, ['ajax', 'none', 'number'], true)) { $paginate = 'none'; } $query_args = [ 'post_type' => aksrt_home_widget_post_types(), 'post_status' => 'publish', 'posts_per_page' => $count, 'paged' => $page, 'ignore_sticky_posts' => true, ]; if ($paginate === 'none') { $query_args['no_found_rows'] = true; } if ($category_id > 0) { $query_args['cat'] = $category_id; } $query = new \WP_Query($query_args); ob_start(); aksrt_home_widget_render_post_grid($query, $layout, $columns, $context); $html = (string) ob_get_clean(); if ($html === '' && $page === 1) { $html = function_exists('aksrt_get_widget_null') ? aksrt_get_widget_null(__('暂无内容', 'aksrt')) : '

' . esc_html__('暂无内容', 'aksrt') . '

'; } $max_pages = max(1, (int) $query->max_num_pages); $total = (int) $query->found_posts; $has_more = in_array($paginate, ['ajax', 'number'], true) && $page < $max_pages; $base_qs = [ 'count' => $count, 'cat' => $category_id, 'columns' => $columns, 'layout' => $layout, 'context' => $context, 'paginate' => $paginate, ]; $rest_base = add_query_arg($base_qs, rest_url('aksrt/v1/widgets/category-block')); $paginate_html = ''; if ($paginate === 'ajax' && $has_more) { $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 = '
'; } return compact('html', 'paginate_html', 'has_more', 'page', 'max_pages', 'total'); } public function widget($args, $instance): void { $context = aksrt_home_widget_resolve_context($args); $category_id = self::resolve_category_id_static($instance); $term = $category_id > 0 ? get_term($category_id, 'category') : null; $title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base); $title = $title !== '' ? $title : (($term && ! is_wp_error($term)) ? $term->name : __('精选分类', 'aksrt')); $show_more = ! empty($instance['show_more']); $paginate = sanitize_key((string) ($instance['paginate'] ?? 'none')); if (!in_array($paginate, ['ajax', 'none', 'number'], true)) { $paginate = 'none'; } $instance['paginate'] = $paginate; $payload = self::query_payload($instance, 1, $context); echo $args['before_widget']; echo '
'; aksrt_home_widget_render_header( (string) $title, ($show_more && $term && ! is_wp_error($term)) ? (string) get_category_link($category_id) : '', __('查看分类', 'aksrt') ); $rest = rest_url('aksrt/v1/widgets/category-block'); echo '
'; echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $payload['paginate_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo ''; echo '
'; echo '
'; echo $args['after_widget']; } public function form($instance): void { $title = (string) ($instance['title'] ?? ''); $category_id = (int) ($instance['category_id'] ?? 0); $count = (int) ($instance['count'] ?? 6); $columns = (int) ($instance['columns'] ?? 3); $layout = aksrt_home_widget_resolve_layout($instance['layout'] ?? 'card', 'card'); $show_more = ! empty($instance['show_more']); $categories = aksrt_home_widget_available_categories(); ?>

id="get_field_id('show_more')); ?>" name="get_field_name('show_more')); ?>" value="1">

sanitize_text_field($new_instance['title'] ?? ''), 'category_id' => absint($new_instance['category_id'] ?? 0), 'count' => max(1, min(12, absint($new_instance['count'] ?? 6))), 'columns' => max(1, min(4, absint($new_instance['columns'] ?? 3))), 'layout' => aksrt_home_widget_resolve_layout($new_instance['layout'] ?? 'card', 'card'), 'show_more' => ! empty($new_instance['show_more']) ? 1 : 0, 'paginate' => in_array(sanitize_key((string) ($new_instance['paginate'] ?? 'none')), ['none', 'ajax', 'number'], true) ? sanitize_key((string) ($new_instance['paginate'] ?? 'none')) : 'none', ]; return $business; } public static function resolve_category_id_static(array $instance): int { $category_id = absint($instance['category_id'] ?? 0); if ($category_id > 0) { return $category_id; } $fallback = aksrt_home_widget_fallback_category_ids(1); return $fallback[0] ?? 0; } private function resolve_category_id(array $instance): int { return self::resolve_category_id_static($instance); } }