*/ function aksrt_renderer_defaults(): array { return [ 'context' => 'archive', 'mode' => 'list', 'columns' => 1, 'style' => 'default', 'card_style' => 'standard', 'heading_bold' => true, 'show_excerpt' => true, 'excerpt_words' => 36, 'show_meta' => true, 'show_author' => true, 'show_date' => true, 'show_views' => true, 'show_comments' => true, 'show_likes' => false, 'show_badges' => true, 'show_tags' => true, 'tag_limit' => 3, 'category_limit' => 1, 'show_thumb' => true, 'thumb_ratio' => '4:3', 'thumb_fit' => 'cover', 'thumb_lazy' => true, 'thumb_if_has' => false, 'target_blank' => false, 'title_html' => '', 'excerpt_html' => '', 'badge_show' => true, 'show_resource_badge' => true, 'multi_thumb_enabled' => true, 'multi_thumb_limit' => 4, 'multi_thumb_min' => 2, 'list_show_type' => 'separate', 'list_img_position' => 'left', 'number_offset' => 0, 'show_number' => false, 'extra_class' => '', 'highlight_query' => '', ]; } } if (!function_exists('aksrt_renderer_config')) { /** * Merge renderer config with context-aware defaults. * * @param array $config User config. * @return array */ function aksrt_renderer_config(array $config = []): array { $cfg = wp_parse_args($config, aksrt_renderer_defaults()); $cfg['mode'] = in_array($cfg['mode'], ['list', 'card', 'mini', 'grid-cms'], true) ? $cfg['mode'] : 'list'; $cfg['columns'] = max(1, min(6, (int) $cfg['columns'])); $cfg['excerpt_words'] = max(0, (int) $cfg['excerpt_words']); $cfg['tag_limit'] = max(0, (int) $cfg['tag_limit']); $cfg['category_limit'] = max(0, (int) $cfg['category_limit']); $cfg['multi_thumb_limit'] = max(1, min(6, (int) $cfg['multi_thumb_limit'])); $cfg['multi_thumb_min'] = max(1, min(6, (int) $cfg['multi_thumb_min'])); if ($cfg['mode'] === 'mini') { $cfg['show_excerpt'] = false; $cfg['show_badges'] = false; $cfg['show_tags'] = false; $cfg['show_author'] = false; $cfg['show_comments'] = false; } if (in_array((string) $cfg['style'], ['default', 'null'], true)) { $cfg['style'] = ''; } $cfg['list_img_position'] = in_array(sanitize_key((string) ($cfg['list_img_position'] ?? 'left')), ['left', 'right'], true) ? sanitize_key((string) ($cfg['list_img_position'] ?? 'left')) : 'left'; $cfg['list_show_type'] = in_array(sanitize_key((string) ($cfg['list_show_type'] ?? 'separate')), ['separate', 'no_margin'], true) ? sanitize_key((string) ($cfg['list_show_type'] ?? 'separate')) : 'separate'; return $cfg; } } if (!function_exists('aksrt_renderer_sanitize_inline_html')) { /** * Sanitize small inline HTML snippets used by render config overrides. * * @param string $html Raw HTML. * @return string */ function aksrt_renderer_sanitize_inline_html(string $html): string { return wp_kses($html, [ 'span' => ['class' => true, 'aria-hidden' => true], 'em' => ['class' => true], 'strong' => ['class' => true], 'i' => ['class' => true, 'aria-hidden' => true], 'small'=> ['class' => true], ]); } } if (!function_exists('aksrt_renderer_highlight_text')) { /** * Highlight plain text query matches using a safe span wrapper. * * @param string $text Plain text. * @param string $query Highlight query. * @return string */ function aksrt_renderer_highlight_text(string $text, string $query): string { $text = wp_strip_all_tags($text); $query = trim($query); if ($query === '' || $text === '') { return esc_html($text); } $words = preg_split('/\s+/u', $query) ?: []; $words = array_values(array_unique(array_filter(array_map('trim', $words), static function ($word) { return $word !== '' && mb_strlen($word) >= 2; }))); if (empty($words)) { return esc_html($text); } $pattern = '/' . implode('|', array_map(static function ($word) { return preg_quote($word, '/'); }, $words)) . '/iu'; $escaped = esc_html($text); return (string) preg_replace($pattern, '$0', $escaped); } } if (!function_exists('aksrt_renderer_title_markup')) { /** * Resolve title markup, supporting highlighted search terms. * * @param array $data Collected post data. * @param array $cfg Renderer config. * @return string */ function aksrt_renderer_title_markup(array $data, array $cfg): string { if (!empty($cfg['title_html'])) { return aksrt_renderer_sanitize_inline_html((string) $cfg['title_html']); } $title = aksrt_renderer_highlight_text((string) $data['title'], (string) $cfg['highlight_query']); $subtitle = trim((string) ($data['subtitle'] ?? '')); if ($subtitle !== '') { $title .= ' ' . esc_html($subtitle) . ''; } return $title; } } if (!function_exists('aksrt_renderer_excerpt_markup')) { /** * Resolve excerpt markup, supporting highlighted search terms. * * @param array $data Collected post data. * @param array $cfg Renderer config. * @return string */ function aksrt_renderer_excerpt_markup(array $data, array $cfg): string { if (!empty($cfg['excerpt_html'])) { return aksrt_renderer_sanitize_inline_html((string) $cfg['excerpt_html']); } return aksrt_renderer_highlight_text((string) $data['excerpt'], (string) $cfg['highlight_query']); } } if (!function_exists('aksrt_renderer_curated_images')) { /** * Resolve curated cover images from post meta. * * @param int $post_id Post ID. * @param int $limit Maximum items. * @return string[] */ function aksrt_renderer_curated_images(int $post_id, int $limit = 6): array { $images = []; $limit = max(1, min(8, $limit)); if (!class_exists('AKSRT_Post_Meta_Box')) { return []; } $cover_images = (string) AKSRT_Post_Meta_Box::get_meta_value($post_id, 'cover_images', ''); if ($cover_images === '') { return []; } $cover_ids = array_filter(array_map('absint', preg_split('/[\s,]+/', $cover_images) ?: [])); foreach ($cover_ids as $attachment_id) { $url = (string) wp_get_attachment_image_url($attachment_id, 'medium_large'); if ($url !== '') { $images[] = esc_url_raw($url); } } return array_slice(array_values(array_unique($images)), 0, $limit); } } if (!function_exists('aksrt_renderer_attachment_images')) { /** * Resolve gallery/content images for list cards. * * @param int $post_id Post ID. * @param int $limit Maximum items. * @return string[] */ function aksrt_renderer_attachment_images(int $post_id, int $limit = 6): array { $images = []; $limit = max(1, min(8, $limit)); if (function_exists('aksrt_get_post_gallery_images')) { $images = aksrt_get_post_gallery_images($post_id, $limit); } if (function_exists('aksrt_get_first_content_image_url')) { $first_content_image = (string) aksrt_get_first_content_image_url($post_id); if ($first_content_image !== '') { $images[] = esc_url_raw($first_content_image); } } return array_slice(array_values(array_unique(array_filter($images))), 0, $limit); } } if (!function_exists('aksrt_renderer_random_fallback_image')) { /** * Pick a deterministic fallback image when "random cover" is enabled. * * @param int $post_id Post ID. * @return string */ function aksrt_renderer_random_fallback_image(int $post_id): string { $candidates = [ get_template_directory_uri() . '/assets/img/person_background.png', get_template_directory_uri() . '/assets/img/placeholder.svg', ]; if (class_exists('AKSRT_Settings')) { $thumb = AKSRT_Settings::get_post_thumb_settings(); $spare = $thumb['spare_images'] ?? []; if (is_array($spare) && !empty($spare)) { foreach ($spare as $url) { if (is_string($url) && $url !== '') { array_unshift($candidates, $url); } } } } $candidates = array_values(array_unique(array_filter(array_map('esc_url_raw', $candidates)))); if (empty($candidates)) { return ''; } $index = $post_id % count($candidates); return $candidates[$index] ?? $candidates[0]; } } if (!function_exists('aksrt_renderer_resolve_images')) { /** * Resolve primary + gallery images for a post based on card settings. * * @param int $post_id Post ID. * @param array $cfg Renderer config. * @return array{primary:string,gallery:string[],has_thumb:bool,has_real:bool} */ function aksrt_renderer_resolve_images(int $post_id, array $cfg): array { $thumb_url = get_the_post_thumbnail_url($post_id, 'medium_large'); $curated_images = !empty($cfg['thumb_if_has']) || !empty($cfg['multi_thumb_enabled']) ? aksrt_renderer_curated_images($post_id, max(4, (int) $cfg['multi_thumb_limit'])) : []; $content_images = !empty($cfg['multi_thumb_enabled']) ? aksrt_renderer_attachment_images($post_id, max(4, (int) $cfg['multi_thumb_limit'])) : []; $gallery = []; if (!empty($curated_images)) { $gallery = array_merge($gallery, $curated_images); } if (!empty($content_images)) { $gallery = array_merge($gallery, $content_images); } $gallery = array_values(array_unique(array_filter($gallery))); $primary = is_string($thumb_url) && $thumb_url !== '' ? esc_url_raw($thumb_url) : ''; $has_real = $primary !== ''; if ($primary === '' && !empty($curated_images[0])) { $primary = $curated_images[0]; $has_real = true; } if ($primary === '' && !empty($content_images[0])) { $primary = $content_images[0]; $has_real = true; } $thumb_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_post_thumb_settings() : []; $allow_fallback = !empty($thumb_settings['rand_thumb']) || !empty($cfg['thumb_if_has']); if ($primary === '' && $allow_fallback) { $primary = aksrt_renderer_random_fallback_image($post_id); } return [ 'primary' => $primary, 'gallery' => $gallery, 'has_thumb' => $primary !== '', 'has_real' => $has_real, ]; } } if (!function_exists('aksrt_renderer_lazyload_config')) { /** * Small lazyload config subset for post cards. * * @return array */ function aksrt_renderer_lazyload_config(): array { if (class_exists('AKSRT_Settings')) { $lazy = AKSRT_Settings::get_lazyload_config(); return [ 'enabled' => !empty($lazy['enabled']) && !empty($lazy['posts_thumb_enabled']), 'placeholder_url' => (string) ($lazy['placeholder_url'] ?? ''), 'blur' => !empty($lazy['blur']), ]; } return [ 'enabled' => false, 'placeholder_url' => '', 'blur' => false, ]; } } if (!function_exists('aksrt_renderer_thumb_ratio_value')) { /** * Convert ratio tokens like 4:3 to CSS aspect ratio values. * * @param string $ratio Ratio token. * @return string */ function aksrt_renderer_thumb_ratio_value(string $ratio): string { $ratio = trim($ratio); if (preg_match('/^(\d+)\s*:\s*(\d+)$/', $ratio, $matches)) { $w = max(1, (int) $matches[1]); $h = max(1, (int) $matches[2]); return $w . ' / ' . $h; } return '4 / 3'; } } if (!function_exists('aksrt_renderer_collect_post_data')) { /** * Aggregate the runtime data needed by all renderer modes. * * @param array $cfg Renderer config. * @return array */ function aksrt_renderer_collect_post_data(array $cfg): array { $post_id = (int) get_the_ID(); if ($post_id <= 0) { return []; } $lazy = aksrt_renderer_lazyload_config(); $lazy['enabled'] = !empty($lazy['enabled']) && !empty($cfg['thumb_lazy']); $images = aksrt_renderer_resolve_images($post_id, $cfg); $gallery_images = array_slice($images['gallery'], 0, $cfg['multi_thumb_limit']); $is_mult_thumb = !empty($cfg['multi_thumb_enabled']) && count($gallery_images) >= (int) $cfg['multi_thumb_min'] && in_array($cfg['mode'], ['list', 'card', 'grid-cms'], true); $author_id = (int) get_post_field('post_author', $post_id); $categories = get_the_category($post_id) ?: []; $tags = get_the_tags($post_id) ?: []; $custom_excerpt = class_exists('AKSRT_Post_Meta_Box') ? (string) AKSRT_Post_Meta_Box::get_meta_value($post_id, 'custom_excerpt', '') : ''; $custom_subtitle = (string) get_post_meta($post_id, '_subtitle', true); $excerpt_source = $custom_excerpt !== '' ? $custom_excerpt : (string) get_the_excerpt($post_id); if ($excerpt_source === '') { $excerpt_source = (string) get_post_field('post_content', $post_id, 'raw'); } $excerpt = wp_trim_words(wp_strip_all_tags($excerpt_source), $cfg['excerpt_words'], '...'); $resource_meta = function_exists('aksrt_get_resource_meta') ? aksrt_get_resource_meta($post_id) : []; $resource_strip = function_exists('aksrt_resource_meta_strip') ? aksrt_resource_meta_strip($resource_meta) : []; return [ 'post_id' => $post_id, 'permalink' => get_permalink($post_id), 'title' => get_the_title($post_id), 'subtitle' => $custom_subtitle, 'excerpt' => $excerpt, 'img_url' => $images['primary'], 'has_img' => !empty($images['has_thumb']), 'has_real_img' => !empty($images['has_real']), 'gallery_images' => $gallery_images, 'is_mult_thumb' => $is_mult_thumb, 'is_video_cover' => !empty(get_post_meta($post_id, '_aksrt_featured_video', true)) || !empty(get_post_meta($post_id, '_video_url', true)), 'author_id' => $author_id, 'avatar_url' => get_avatar_url($author_id, ['size' => 40]), 'author_name' => get_the_author(), 'categories' => $categories, 'tags' => $tags, 'comment_count' => (int) get_comments_number($post_id), 'views' => function_exists('aksrt_get_post_views') ? (int) aksrt_get_post_views($post_id) : 0, 'likes' => function_exists('aksrt_get_post_likes') ? (int) aksrt_get_post_likes($post_id) : 0, 'time_ago' => human_time_diff((int) get_the_time('U', $post_id), current_time('timestamp')), 'resource_badge' => function_exists('aksrt_resource_card_badge_text') ? (string) aksrt_resource_card_badge_text($post_id) : '', 'resource_strip' => $resource_strip, 'is_resource' => function_exists('aksrt_is_resource_post') ? aksrt_is_resource_post($post_id) : false, 'is_sticky' => is_sticky($post_id), 'lazy' => $lazy, 'thumb_ratio_css' => aksrt_renderer_thumb_ratio_value((string) $cfg['thumb_ratio']), 'thumb_ratio_key' => (string) $cfg['thumb_ratio'], ]; } } if (!function_exists('aksrt_renderer_article_attrs')) { /** * Build the article wrapper attributes. * * @param string[] $classes CSS classes. * @param array $data Collected post data. * @return string */ function aksrt_renderer_article_attrs(array $classes, array $data): string { $classes[] = 'thumb-ratio--' . str_replace(':', '-', (string) $data['thumb_ratio_key']); return sprintf( 'class="%s" style="%s"', esc_attr(implode(' ', array_unique(array_filter($classes)))), esc_attr('--aksrt-thumb-ratio:' . $data['thumb_ratio_css']) ); } } if (!function_exists('aksrt_renderer_thumb')) { /** * Render the primary thumbnail. * * @param array $data Collected post data. * @param array $cfg Renderer config. * @return string */ function aksrt_renderer_thumb(array $data, array $cfg): string { if (!$cfg['show_thumb'] || empty($data['has_img'])) { return ''; } if (!empty($data['is_mult_thumb']) && in_array($cfg['mode'], ['card', 'grid-cms'], true)) { return aksrt_renderer_mult_thumb($data); } $lazy = $data['lazy']; $img = (string) $data['img_url']; $title = (string) $data['title']; $href = (string) $data['permalink']; $is_sticky = !empty($data['is_sticky']); $has_real_img = !empty($data['has_real_img']); if ($has_real_img && !empty($lazy['enabled'])) { $img_html = '' . '' . esc_attr($title) . ''; } else { $img_html = '' . esc_attr($title) . ''; } $sticky_html = ($is_sticky && !empty($cfg['badge_show'])) ? '' . esc_html__('置顶', 'aksrt') . '' : ''; $format_badge = ''; if (!empty($data['is_mult_thumb']) && !empty($data['gallery_images'])) { $format_badge = '' . esc_html(count($data['gallery_images'])) . ''; } elseif (!empty($data['is_video_cover'])) { $format_badge = ''; } $empty_class = $has_real_img ? '' : ' item-thumbnail--fallback'; $target_attr = !empty($cfg['target_blank']) ? ' target="_blank"' : ''; if ($cfg['mode'] === 'list') { $graphic_class = 'post-graphic'; if (($cfg['list_img_position'] ?? 'left') === 'right') { $graphic_class .= ' order1'; } return '
' . $img_html . '' . $sticky_html . $format_badge . '
'; } return '
' . $img_html . '' . $sticky_html . $format_badge . '
'; } } if (!function_exists('aksrt_renderer_mult_thumb')) { /** * Render the multi-image strip used by list cards. * * @param array $data Collected post data. * @return string */ function aksrt_renderer_mult_thumb(array $data): string { if (empty($data['is_mult_thumb']) || empty($data['gallery_images'])) { return ''; } $lazy = $data['lazy']; $html = ''; return $html; } } if (!function_exists('aksrt_renderer_badges')) { /** * Render taxonomy and resource badges. * * @param array $data Collected post data. * @param array $cfg Renderer config. * @return string */ function aksrt_renderer_badges(array $data, array $cfg): string { $items = []; if (!empty($cfg['show_resource_badge']) && $data['resource_badge'] !== '') { $items[] = '' . esc_html($data['resource_badge']) . ''; } if (!empty($cfg['show_badges']) && !empty($cfg['badge_show']) && $cfg['category_limit'] > 0) { foreach (array_slice($data['categories'], 0, $cfg['category_limit']) as $category) { $items[] = ' ' . esc_html($category->name) . ''; } } if (!empty($cfg['show_tags']) && $cfg['tag_limit'] > 0) { foreach (array_slice($data['tags'], 0, $cfg['tag_limit']) as $tag) { $items[] = '#' . esc_html($tag->name) . ''; } } $topics = taxonomy_exists('topics') ? wp_get_post_terms((int) $data['post_id'], 'topics') : []; if (!is_wp_error($topics) && !empty($topics)) { foreach (array_slice($topics, 0, 2) as $topic) { $items[] = ' ' . esc_html($topic->name) . ''; } } if (!empty($data['is_resource'])) { $items[] = 'VIP'; } if (empty($items)) { return ''; } return '
' . implode('', $items) . '
'; } } if (!function_exists('aksrt_renderer_resource_strip')) { /** * Render compact resource meta chips. * * @param array $data Collected post data. * @return string */ function aksrt_renderer_resource_strip(array $data): string { if (empty($data['resource_strip']) || !is_array($data['resource_strip'])) { return ''; } $html = '
    '; foreach ($data['resource_strip'] as $item) { $html .= '
  • ' . esc_html((string) $item) . '
  • '; } $html .= '
'; return $html; } } if (!function_exists('aksrt_renderer_meta')) { /** * Render the meta bar. * * @param array $data Collected post data. * @param array $cfg Renderer config. * @return string */ function aksrt_renderer_meta(array $data, array $cfg): string { if (!$cfg['show_meta']) { return ''; } $format_number = function_exists('aksrt_format_number') ? 'aksrt_format_number' : 'strval'; if ($cfg['mode'] === 'mini') { $items = []; if (!empty($cfg['show_date'])) { $items[] = '' . esc_html($data['time_ago']) . ''; } if (!empty($cfg['show_views'])) { $items[] = ' ' . esc_html($format_number($data['views'])) . ''; } if (empty($items)) { return ''; } return '
' . implode('', $items) . '
'; } $left = []; if (!empty($cfg['show_author'])) { $left[] = '' . esc_html($data['author_name']) . ''; } if (!empty($cfg['show_date'])) { $left[] = '' . esc_html($data['time_ago']) . ''; } $right = []; if (!empty($cfg['show_comments'])) { $right[] = ' ' . esc_html((string) $data['comment_count']) . ''; } if (!empty($cfg['show_views'])) { $right[] = ' ' . esc_html($format_number($data['views'])) . ''; } if (!empty($cfg['show_likes'])) { $right[] = ' ' . esc_html($format_number($data['likes'])) . ''; } if (empty($left) && empty($right)) { return ''; } $html = '
'; if (!empty($left)) { $html .= '
' . implode('', $left) . '
'; } if (!empty($right)) { $html .= '
' . implode('', $right) . '
'; } $html .= '
'; return $html; } } if (!function_exists('aksrt_renderer_list')) { /** * Render a horizontal list card. * * @param array $data Collected post data. * @param array $cfg Renderer config. * @return string */ function aksrt_renderer_list(array $data, array $cfg): string { $classes = ['posts-item', 'list', 'ajax-item', 'posts-item--' . ($cfg['card_style'] ?: 'standard')]; if ($cfg['style'] !== '' && $cfg['style'] !== 'default') { $classes[] = $cfg['style']; } if (!empty($data['is_mult_thumb'])) { $classes[] = 'mult-thumb'; } if ($cfg['context'] === 'home') { $classes[] = 'posts-item--home-list'; } if (empty($data['has_img'])) { $classes[] = 'no-thumb'; } if (($cfg['list_show_type'] ?? 'separate') === 'no_margin') { $classes[] = 'no-margin'; $classes[] = 'no_margin'; } $thumb = aksrt_renderer_thumb($data, $cfg); $mult = aksrt_renderer_mult_thumb($data); if ($cfg['card_style'] === 'text' || ($cfg['card_style'] === 'thumb_if_has' && empty($data['has_img']))) { $thumb = ''; $mult = ''; $classes[] = 'no-thumb'; $classes[] = 'posts-item--text'; } $title_markup = aksrt_renderer_title_markup($data, $cfg); $excerpt_markup = aksrt_renderer_excerpt_markup($data, $cfg); $badges = aksrt_renderer_badges($data, $cfg); $meta = aksrt_renderer_meta($data, $cfg); $target_attr = !empty($cfg['target_blank']) ? ' target="_blank"' : ''; $heading_cls = !empty($cfg['heading_bold']) ? 'item-heading item-heading--bold' : 'item-heading'; $title_html = '

' . $title_markup . '

'; $excerpt_html = $cfg['show_excerpt'] && $excerpt_markup !== '' ? '

' . $excerpt_markup . '

' : ''; $html = '
'; if ($cfg['style'] === 'style2') { $html .= $title_html; $html .= '
'; $html .= $thumb; $html .= '
'; $html .= $mult ?: $excerpt_html; $html .= $badges; $html .= $meta; $html .= '
'; } else { $html .= $thumb; $html .= '
'; $html .= $title_html; $html .= $mult ?: $excerpt_html; $html .= '
' . $badges . $meta . '
'; $html .= '
'; } $html .= '
'; return $html; } } if (!function_exists('aksrt_renderer_card')) { /** * Render a vertical card or CMS resource card. * * @param array $data Collected post data. * @param array $cfg Renderer config. * @return string */ function aksrt_renderer_card(array $data, array $cfg): string { $classes = ['posts-item', 'card', 'ajax-item', 'posts-item--' . ($cfg['card_style'] ?: 'standard')]; if ($cfg['style'] !== '' && $cfg['style'] !== 'default') { $classes[] = $cfg['style']; } if ($cfg['context'] === 'home') { $classes[] = 'posts-item--home'; } if ($cfg['mode'] === 'grid-cms') { $classes[] = 'posts-item--cms'; } if (!empty($data['is_mult_thumb'])) { $classes[] = 'mult-thumb'; } if (empty($data['has_img'])) { $classes[] = 'no-thumb'; } $thumb = aksrt_renderer_thumb($data, $cfg); $badges = aksrt_renderer_badges($data, $cfg); $resource_strip = aksrt_renderer_resource_strip($data); $meta = aksrt_renderer_meta($data, $cfg); $title_markup = aksrt_renderer_title_markup($data, $cfg); $excerpt_markup = aksrt_renderer_excerpt_markup($data, $cfg); $html = '
'; $html .= $thumb; $html .= '
'; $target_attr = !empty($cfg['target_blank']) ? ' target="_blank"' : ''; $heading_cls = !empty($cfg['heading_bold']) ? 'item-heading item-heading--bold' : 'item-heading'; $html .= '

' . $title_markup . '

'; if ($cfg['show_excerpt'] && $excerpt_markup !== '') { $html .= '

' . $excerpt_markup . '

'; } $html .= $badges; if ($cfg['mode'] === 'grid-cms') { $html .= $resource_strip; } $html .= $meta; $html .= '
'; return $html; } } if (!function_exists('aksrt_renderer_mini')) { /** * Render a compact mini card. * * @param array $data Collected post data. * @param array $cfg Renderer config. * @return string */ function aksrt_renderer_mini(array $data, array $cfg): string { $classes = ['posts-mini']; if (!empty($cfg['extra_class'])) { $classes[] = $cfg['extra_class']; } if (empty($data['has_img'])) { $classes[] = 'posts-mini--no-thumb'; } $number_html = ''; if ($cfg['show_number'] && $cfg['number_offset'] > 0) { $badge_colors = ['c-red', 'c-yellow', 'c-orange', 'c-blue', 'c-green']; $index = $cfg['number_offset'] - 1; $color = $badge_colors[$index] ?? ''; $number_html = '' . (int) $cfg['number_offset'] . ''; } $thumb_html = ''; $target_attr = !empty($cfg['target_blank']) ? ' target="_blank"' : ''; if ($cfg['show_thumb'] && !empty($data['has_img'])) { $lazy = $data['lazy']; $thumb_img = !empty($lazy['enabled']) && !empty($data['has_real_img']) ? '' . esc_attr($data['title']) . '' : '' . esc_attr($data['title']) . ''; $thumb_html = '' . $thumb_img . ''; } $heading_class = 'item-heading' . (!empty($cfg['heading_bold']) ? ' item-heading--bold' : '') . (!empty($data['has_img']) ? ' text-ellipsis-2' : ' text-ellipsis'); $title_html = '

' . $number_html . '' . aksrt_renderer_title_markup($data, $cfg) . '

'; $html = '
'; $html .= $thumb_html; $html .= '
'; $html .= $title_html; $html .= aksrt_renderer_meta($data, $cfg); $html .= '
'; return $html; } } if (!function_exists('aksrt_render_post')) { /** * Render one post item inside a loop. * * @param array $config Renderer config overrides. * @return string */ function aksrt_render_post(array $config = []): string { $cfg = aksrt_renderer_config($config); $data = aksrt_renderer_collect_post_data($cfg); if (empty($data)) { return ''; } switch ($cfg['mode']) { case 'card': case 'grid-cms': return aksrt_renderer_card($data, $cfg); case 'mini': return aksrt_renderer_mini($data, $cfg); case 'list': default: return aksrt_renderer_list($data, $cfg); } } } if (!function_exists('aksrt_render_post_list')) { /** * Render a query loop with the unified renderer. * * @param WP_Query|null $query Posts query. Falls back to global loop. * @param array $config Renderer config overrides. * @param bool $echo Whether to echo output. * @return string */ function aksrt_render_post_list(?WP_Query $query = null, array $config = [], bool $echo = true): string { $cfg = aksrt_renderer_config($config); $wrapper_classes = ['posts-row', 'ajaxpager']; if ($cfg['context'] === 'home') { $wrapper_classes[] = 'aksrt-homepage__posts'; $wrapper_classes[] = 'post-grid'; $wrapper_classes[] = 'post-grid--home'; } elseif ($cfg['mode'] !== 'mini') { $wrapper_classes[] = 'post-grid'; $wrapper_classes[] = 'post-grid--' . $cfg['mode']; } if (!empty($config['wrapper_class']) && is_string($config['wrapper_class'])) { foreach (preg_split('/\s+/', trim($config['wrapper_class'])) as $extra_class) { $extra_class = sanitize_html_class($extra_class); if ($extra_class !== '') { $wrapper_classes[] = $extra_class; } } } $wrapper_attr = 'class="' . esc_attr(implode(' ', array_unique($wrapper_classes))) . '"'; if ($cfg['columns'] > 1) { $wrapper_attr .= ' style="--aksrt-widget-columns:' . (int) $cfg['columns'] . '"'; } $data_view = in_array($cfg['mode'], ['card', 'grid-cms'], true) ? 'card' : $cfg['mode']; // Homepage panel already provides .posts-row.ajaxpager — allow callers to skip nesting. $skip_wrapper = !empty($config['skip_wrapper']); $html = ''; if ($query instanceof WP_Query) { if (!$query->have_posts()) { $html = '

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

'; } else { if (!$skip_wrapper) { $html .= '
'; } $number = $cfg['number_offset']; while ($query->have_posts()) { $query->the_post(); if ($cfg['mode'] === 'mini' && $cfg['show_number']) { $number++; $config['number_offset'] = $number; $cfg = aksrt_renderer_config($config); } $html .= aksrt_render_post($cfg); } if (!$skip_wrapper) { $html .= '
'; } } wp_reset_postdata(); } else { if (!have_posts()) { $html = '

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

'; } else { if (!$skip_wrapper) { $html .= '
'; } while (have_posts()) { the_post(); $html .= aksrt_render_post($cfg); } if (!$skip_wrapper) { $html .= '
'; } } } if ($echo) { echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return ''; } return $html; } } if (!function_exists('aksrt_render_mini_list')) { /** * Convenience wrapper for mini lists. * * @param WP_Query $query Posts query. * @param array $config Renderer config overrides. * @param bool $echo Whether to echo output. * @return string */ function aksrt_render_mini_list(WP_Query $query, array $config = [], bool $echo = true): string { $config['mode'] = 'mini'; return aksrt_render_post_list($query, $config, $echo); } } if (!function_exists('aksrt_render_placeholder')) { /** * Render skeleton placeholders for loading states. * * @param string $mode list|card|mini. * @param int $count Number of placeholders. * @return string */ function aksrt_render_placeholder(string $mode = 'list', int $count = 3): string { $count = max(1, min(12, $count)); $html = ''; if ($mode === 'card') { $item = '
' . '

' . '

' . '
'; $html = str_repeat($item, $count); } elseif ($mode === 'mini') { $item = '
' . '
' . '
'; $html = str_repeat($item, $count); } else { $item = '
' . '

' . '

' . '
'; $html = str_repeat($item, $count); } return $html; } }