*/ 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\Core\PostMetaBox')) { return []; } $cover_images = (string) \AKSRT\Core\PostMetaBox::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\Settings')) { $thumb = \AKSRT\Settings\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\Settings') ? \AKSRT\Settings\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_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'; } }