387 lines
12 KiB
PHP
387 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* Shared post renderer helpers for archive, widget, and related lists.
|
|
*
|
|
* Centralizes card/list/mini rendering to avoid duplicating DOM contracts
|
|
* across category.php, search.php, widgets, and home modules.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
if (!function_exists('aksrt_renderer_defaults')) {
|
|
/**
|
|
* Base renderer config defaults.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
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<string,mixed> $config User config.
|
|
* @return array<string,mixed>
|
|
*/
|
|
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, '<span class="focus-color">$0</span>', $escaped);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_renderer_title_markup')) {
|
|
/**
|
|
* Resolve title markup, supporting highlighted search terms.
|
|
*
|
|
* @param array<string,mixed> $data Collected post data.
|
|
* @param array<string,mixed> $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 .= '<span class="focus-color"> ' . esc_html($subtitle) . '</span>';
|
|
}
|
|
|
|
return $title;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_renderer_excerpt_markup')) {
|
|
/**
|
|
* Resolve excerpt markup, supporting highlighted search terms.
|
|
*
|
|
* @param array<string,mixed> $data Collected post data.
|
|
* @param array<string,mixed> $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<string,mixed> $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_lazyload_config')) {
|
|
/**
|
|
* Small lazyload config subset for post cards.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
function aksrt_renderer_lazyload_config(): array {
|
|
if (class_exists('\AKSRT\Settings\Settings')) {
|
|
$lazy = \AKSRT\Settings\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';
|
|
}
|
|
}
|