*/ 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 '

' . 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') || AKSRT_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: simple sidebar list echo '
    '; while ($query->have_posts()) { $query->the_post(); ?>
  • 'lazy', 'class' => 'fit-cover']); ?>
  • '; 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') ? AKSRT_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 AKSRT_Category_Block_Widget 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, ] ); } public function widget($args, $instance): void { $context = aksrt_home_widget_resolve_context($args); $category_id = $this->resolve_category_id($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')); $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 ); $show_more = ! empty($instance['show_more']); $query_args = [ 'post_type' => aksrt_home_widget_post_types(), 'post_status' => 'publish', 'posts_per_page' => $count, 'ignore_sticky_posts' => true, 'no_found_rows' => true, ]; if ($category_id > 0) { $query_args['cat'] = $category_id; } $query = new WP_Query($query_args); 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') ); aksrt_home_widget_render_post_grid($query, $layout, $columns, $context); 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, ]; } private function resolve_category_id(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; } } /** * Homepage category tabs widget. */ class AKSRT_Category_Tabs_Widget extends WP_Widget { public function __construct() { parent::__construct( 'aksrt_category_tabs', esc_html__('AKSRT 分类 Tabs', 'aksrt'), [ 'classname' => 'widget_aksrt_category_tabs', 'description' => esc_html__('多分类切换内容流,可挂载到首页内容挂载区。', 'aksrt'), 'customize_selective_refresh' => true, ] ); } public function widget($args, $instance): void { $context = aksrt_home_widget_resolve_context($args); $title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base); $title = $title !== '' ? $title : __('热门分类', 'aksrt'); $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 ); $show_more = ! empty($instance['show_more']); $tabs = $this->resolve_tabs($instance); echo $args['before_widget']; echo '
    '; if (empty($tabs)) { echo '

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

    '; echo '
    '; echo $args['after_widget']; return; } ?>

      $tab) : ?>
    $tab) { $query = new WP_Query([ 'post_type' => aksrt_home_widget_post_types(), 'post_status' => 'publish', 'posts_per_page' => $count, 'ignore_sticky_posts' => true, 'no_found_rows' => true, 'cat' => (int) $tab['id'], ]); ?>
    >
    '; echo $args['after_widget']; } public function form($instance): void { $title = (string) ($instance['title'] ?? ''); $category_ids = aksrt_home_widget_normalize_category_ids($instance['category_ids'] ?? [], 6); $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_ids' => aksrt_home_widget_normalize_category_ids($new_instance['category_ids'] ?? [], 6), '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, ]; } /** * Resolve configured category tabs. * * @param array $instance Widget instance. * @return array */ private function resolve_tabs(array $instance): array { $category_ids = aksrt_home_widget_normalize_category_ids($instance['category_ids'] ?? [], 6); if (empty($category_ids)) { $category_ids = aksrt_home_widget_fallback_category_ids(4); } $tabs = []; foreach ($category_ids as $category_id) { $term = get_term($category_id, 'category'); if (! $term || is_wp_error($term)) { continue; } $tabs[] = [ 'id' => (int) $category_id, 'name' => $term->name, 'link' => get_category_link($category_id), ]; } return $tabs; } } /** * Homepage category recommend cards widget. */ class AKSRT_Category_Recommend_Widget extends WP_Widget { public function __construct() { parent::__construct( 'aksrt_category_recommend', esc_html__('AKSRT 分类推荐卡片', 'aksrt'), [ 'classname' => 'widget_aksrt_category_recommend', 'description' => esc_html__('分类推荐卡片区块,可挂载到首页内容挂载区。', 'aksrt'), 'customize_selective_refresh' => true, ] ); } public function widget($args, $instance): void { $title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base); $title = $title !== '' ? $title : __('分类推荐', 'aksrt'); $category_ids = aksrt_home_widget_normalize_category_ids($instance['category_ids'] ?? [], 4); $posts_per_cat = max(1, min(8, (int) ($instance['posts_per_category'] ?? 5))); $columns = max(1, min(4, (int) ($instance['columns'] ?? 2))); $show_views = ! empty($instance['show_views']); $show_description = ! empty($instance['show_description']); $show_dates = ! empty($instance['show_dates']); $show_more = ! empty($instance['show_more']); if (empty($category_ids)) { $category_ids = aksrt_home_widget_fallback_category_ids(4); } $cards = $this->build_cards($category_ids, $posts_per_cat, $show_views, $show_description); echo $args['before_widget']; echo '
    '; aksrt_home_widget_render_header($title); if (empty($cards)) { echo '

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

    '; echo '
    '; echo $args['after_widget']; return; } $lazy = aksrt_home_widget_get_lazyload_args(); printf('
    ', (int) $columns); foreach ($cards as $card) { ?> '; echo ''; echo $args['after_widget']; } public function form($instance): void { $title = (string) ($instance['title'] ?? __('分类推荐', 'aksrt')); $category_ids = aksrt_home_widget_normalize_category_ids($instance['category_ids'] ?? [], 4); $posts_per_cat = (int) ($instance['posts_per_category'] ?? 5); $columns = (int) ($instance['columns'] ?? 2); $show_views = ! empty($instance['show_views']); $show_description = ! array_key_exists('show_description', $instance) || ! empty($instance['show_description']); $show_dates = ! array_key_exists('show_dates', $instance) || ! empty($instance['show_dates']); $show_more = ! array_key_exists('show_more', $instance) || ! empty($instance['show_more']); $categories = aksrt_home_widget_available_categories(); ?>

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

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

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

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

    sanitize_text_field($new_instance['title'] ?? ''), 'category_ids' => aksrt_home_widget_normalize_category_ids($new_instance['category_ids'] ?? [], 4), 'posts_per_category' => max(1, min(8, absint($new_instance['posts_per_category'] ?? 5))), 'columns' => max(1, min(4, absint($new_instance['columns'] ?? 2))), 'show_views' => ! empty($new_instance['show_views']) ? 1 : 0, 'show_description' => ! empty($new_instance['show_description']) ? 1 : 0, 'show_dates' => ! empty($new_instance['show_dates']) ? 1 : 0, 'show_more' => ! empty($new_instance['show_more']) ? 1 : 0, ]; } /** * Build category recommend cards. * * @param array $category_ids Category ids. * @param int $posts_per_cat Post count per category. * @param bool $show_views Whether to calculate views. * @param bool $show_description Whether to fetch description. * @return array> */ private function build_cards(array $category_ids, int $posts_per_cat, bool $show_views, bool $show_description): array { $cards = []; foreach ($category_ids as $category_id) { $term = get_term($category_id, 'category'); if (! $term || is_wp_error($term)) { continue; } $posts = get_posts([ 'post_type' => aksrt_home_widget_post_types(), 'cat' => $category_id, 'posts_per_page' => $posts_per_cat, 'post_status' => 'publish', 'orderby' => 'date', 'order' => 'DESC', 'no_found_rows' => true, ]); $post_items = []; foreach ($posts as $post) { $post_items[] = [ 'title' => get_the_title($post->ID), 'link' => get_permalink($post->ID), 'date' => function_exists('aksrt_time_ago') ? aksrt_time_ago($post->post_date) : get_the_date('Y-m-d', $post->ID), ]; } $cards[] = [ 'name' => $term->name, 'link' => get_category_link($category_id), 'cover' => class_exists('AKSRT_Settings') ? AKSRT_Settings::get_category_cover_url($category_id) : (get_template_directory_uri() . '/assets/img/placeholder.svg'), 'description' => $show_description && class_exists('AKSRT_Settings') ? AKSRT_Settings::get_category_recommend_desc($category_id) : ($show_description ? (string) $term->description : ''), 'count' => (int) $term->count, 'views' => $show_views && function_exists('aksrt_get_category_total_views') ? aksrt_get_category_total_views($category_id) : 0, 'posts' => $post_items, ]; } return $cards; } }