298 lines
17 KiB
PHP
298 lines
17 KiB
PHP
<?php
|
||
|
||
namespace AKSRT\WidgetSystem\Renderers;
|
||
|
||
use AKSRT\WidgetSystem\RendererHelpers;
|
||
use AKSRT\WidgetSystem\PostQuery;
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
/** Render the 17 general and decorative widgets. */
|
||
final class GeneralRenderer {
|
||
public static function render(string $slug, array $instance, array $context = []): string {
|
||
$method = 'render_' . $slug;
|
||
return is_callable([self::class, $method]) ? (string) self::$method($instance, $context) : '';
|
||
}
|
||
|
||
private static function render_search(array $i): string {
|
||
$action = esc_url(home_url('/'));
|
||
$placeholder = esc_attr((string) $i['placeholder']);
|
||
$style = sanitize_html_class((string) $i['search_style']);
|
||
return '<form class="aksrt-widget-search aksrt-widget-search--' . $style . '" role="search" method="get" action="' . $action . '"><label><span class="screen-reader-text">' . esc_html__('搜索:', 'aksrt') . '</span><input type="search" name="s" placeholder="' . $placeholder . '" autocomplete="off"></label><button type="submit" aria-label="' . esc_attr__('搜索', 'aksrt') . '"><i class="fa fas fa-search" aria-hidden="true"></i><span>' . esc_html__('搜索', 'aksrt') . '</span></button></form>';
|
||
}
|
||
|
||
private static function render_posts_toc(array $i): string {
|
||
if (!is_singular('post')) {
|
||
return '';
|
||
}
|
||
return '<nav class="aksrt-widget-toc' . (!empty($i['collapsed']) ? ' is-collapsed' : '') . '" data-min-headings="' . (int) $i['min_headings'] . '" data-scroll-offset="' . (int) $i['offset'] . '" aria-label="' . esc_attr__('文章目录', 'aksrt') . '"><button class="aksrt-widget-toc__toggle" type="button" aria-expanded="' . (empty($i['collapsed']) ? 'true' : 'false') . '"><span>' . esc_html__('文章目录', 'aksrt') . '</span><i class="fa fas fa-chevron-down" aria-hidden="true"></i></button><ol class="aksrt-widget-toc__list"></ol><p class="aksrt-widget-toc__empty">' . esc_html__('正在生成目录…', 'aksrt') . '</p></nav>';
|
||
}
|
||
|
||
private static function render_quote(array $i): string {
|
||
$quotes = RendererHelpers::lines((string) $i['quotes']);
|
||
$text = $quotes ? $quotes[array_rand($quotes)] : __('保持好奇,持续创造。', 'aksrt');
|
||
return '<figure class="aksrt-widget-quote" data-source="' . esc_attr((string) $i['source']) . '" data-interval="' . (int) $i['interval'] . '"><i class="fa fas fa-quote-left" aria-hidden="true"></i><blockquote>' . esc_html($text) . '</blockquote><button type="button" aria-label="' . esc_attr__('换一句', 'aksrt') . '"><i class="fa fas fa-sync-alt" aria-hidden="true"></i></button></figure>';
|
||
}
|
||
|
||
private static function render_icon_cards(array $i): string {
|
||
return self::icon_cards($i, false);
|
||
}
|
||
|
||
private static function render_icon_cover_cards(array $i): string {
|
||
return self::icon_cards($i, true);
|
||
}
|
||
|
||
private static function icon_cards(array $instance, bool $cover): string {
|
||
$pc = max(1, (int) ($instance['pc_row'] ?? 4));
|
||
$mobile = max(1, (int) ($instance['m_row'] ?? 2));
|
||
$cards = (array) ($instance['cards'] ?? []);
|
||
$is_row = count($cards) > 1;
|
||
$row_class = 'col-sm-' . max(1, (int) (12 / $pc));
|
||
if ($mobile > 1) $row_class .= ' col-xs-' . max(1, (int) (12 / $mobile));
|
||
$items = '';
|
||
foreach ($cards as $card) {
|
||
if (!is_array($card)) {
|
||
continue;
|
||
}
|
||
$icon_size = !$cover && !empty($instance['size']) ? 16 + (int) $instance['size'] : ($cover ? 25 : 0);
|
||
$class = $cover ? (!empty($instance['show_widget_bg']) ? 'padding-10' : 'zib-widget mb10') : (!empty($instance['show_widget_bg']) ? 'box-body nopw-sm' : 'mb20');
|
||
$args = [
|
||
'icon' => $card['icon'] ?? '',
|
||
'customize_icon' => $card['customize_icon'] ?? '',
|
||
'icon_size' => $icon_size,
|
||
'icon_radius' => !empty($card['icon_radius']) || ($cover && !empty($instance['icon_radius4'])),
|
||
'icon_color' => $card['icon_color'] ?? '',
|
||
'icon_custom_color' => $card['icon_custom_color'] ?? '',
|
||
'icon_class' => $card['icon_class'] ?? '',
|
||
'title' => $card['title'] ?? '',
|
||
'desc' => $card['desc'] ?? '',
|
||
'link' => $card['link'] ?? [],
|
||
'class' => $class,
|
||
];
|
||
$item = '';
|
||
if ($cover && function_exists('aksrt_icon_cover_card')) {
|
||
$item = aksrt_icon_cover_card($args);
|
||
} elseif (!$cover && function_exists('aksrt_icon_card')) {
|
||
$item = aksrt_icon_card($args);
|
||
} else {
|
||
$item = '';
|
||
}
|
||
$items .= $is_row ? '<div class="' . esc_attr($row_class) . '">' . $item . '</div>' : $item;
|
||
}
|
||
if ($items === '') return '';
|
||
$inner = $is_row ? '<div class="row gutters-5">' . $items . '</div>' : $items;
|
||
if ($cover) return '<div class="' . ($instance['show_widget_bg'] ?? false ? 'zib-widget padding-10' : 'mb10') . '">' . $inner . '</div>';
|
||
return '<div class="clearfix">' . (!empty($instance['show_widget_bg']) ? '<div class="zib-widget nobottom notop">' : '') . $inner . (!empty($instance['show_widget_bg']) ? '</div>' : '') . '</div>';
|
||
}
|
||
|
||
private static function render_video(array $i): string {
|
||
$url = esc_url((string) $i['url']);
|
||
if ($url === '') {
|
||
return RendererHelpers::empty_state(__('请配置视频地址', 'aksrt'));
|
||
}
|
||
$ratio = (string) $i['ratio'];
|
||
if (aksrt_is_lazy('lazy_other')) {
|
||
$video_attr = ' class="lazyload" src="" data-src="' . $url . '"';
|
||
$poster_attr = !empty($i['poster']) ? ' data-poster="' . esc_url((string) $i['poster']) . '"' : '';
|
||
} else {
|
||
$video_attr = ' src="' . $url . '"';
|
||
$poster_attr = !empty($i['poster']) ? ' poster="' . esc_url((string) $i['poster']) . '"' : '';
|
||
}
|
||
return '<div class="aksrt-widget-video aksrt-widget-video--' . esc_attr(str_replace(':', '-', $ratio)) . '" data-video-url="' . $url . '" data-video-poster="' . esc_url((string) $i['poster']) . '" data-autoplay="' . (!empty($i['autoplay']) ? '1' : '0') . '" data-muted="' . (!empty($i['muted']) ? '1' : '0') . '"><video controls playsinline preload="metadata"' . $video_attr . $poster_attr . (!empty($i['autoplay']) ? ' autoplay' : '') . (!empty($i['muted']) ? ' muted' : '') . '></video><p class="aksrt-widget-media-error" role="alert" aria-live="assertive" hidden>' . esc_html__('视频加载失败,请稍后重试。', 'aksrt') . '</p></div>';
|
||
}
|
||
|
||
private static function render_embed(array $i): string {
|
||
if (empty($i['url'])) {
|
||
return RendererHelpers::empty_state(__('请配置嵌入地址', 'aksrt'));
|
||
}
|
||
$src_attr = aksrt_is_lazy('lazy_other')
|
||
? ' class="lazyload" src="" data-src="' . esc_url((string) $i['url']) . '"'
|
||
: ' src="' . esc_url((string) $i['url']) . '"';
|
||
return '<div class="aksrt-widget-embed" style="--aksrt-embed-ratio:' . esc_attr((string) (float) $i['ratio']) . '%"><iframe' . $src_attr . ' referrerpolicy="strict-origin-when-cross-origin"' . (!empty($i['allow_fullscreen']) ? ' allowfullscreen' : '') . ' title="' . esc_attr__('嵌入内容', 'aksrt') . '"></iframe></div>';
|
||
}
|
||
|
||
private static function render_media_cards(array $i): string {
|
||
return self::media_grid((array) $i['cards']);
|
||
}
|
||
|
||
private static function media_grid(array $items, string $class = ''): string {
|
||
$html = '<div class="aksrt-widget-grid aksrt-widget-media-cards ' . esc_attr($class) . '">';
|
||
foreach ($items as $item) {
|
||
if (is_array($item)) {
|
||
$html .= RendererHelpers::card_media($item);
|
||
}
|
||
}
|
||
return $html . '</div>';
|
||
}
|
||
|
||
private static function render_partners(array $i): string {
|
||
$items = '';
|
||
foreach ((array) $i['items'] as $item) {
|
||
if (is_array($item)) {
|
||
$items .= RendererHelpers::card_media($item, 'aksrt-partner');
|
||
}
|
||
}
|
||
return '<div class="aksrt-widget-partners" style="--aksrt-partner-rows:' . (int) $i['rows_pc'] . ';--aksrt-partner-rows-m:' . (int) $i['rows_mobile'] . ';--aksrt-partner-speed:' . (int) $i['speed'] . 's"><div class="aksrt-widget-partners__track">' . $items . $items . '</div></div>';
|
||
}
|
||
|
||
private static function render_combo_info(array $i): string {
|
||
$html = '<div class="aksrt-widget-grid aksrt-widget-combo">';
|
||
foreach ((array) $i['items'] as $item) {
|
||
if (!is_array($item)) {
|
||
continue;
|
||
}
|
||
$tags = '';
|
||
foreach (preg_split('/\s*[,,]\s*/', (string) ($item['tags'] ?? '')) ?: [] as $tag) {
|
||
if ($tag !== '') {
|
||
$tags .= '<span>' . esc_html($tag) . '</span>';
|
||
}
|
||
}
|
||
$html .= '<article class="aksrt-combo-card">' . RendererHelpers::card_media($item) . ($tags ? '<div class="aksrt-combo-card__tags">' . $tags . '</div>' : '') . '</article>';
|
||
}
|
||
return $html . '</div>';
|
||
}
|
||
|
||
private static function render_text_title(array $i): string {
|
||
return '<div class="aksrt-widget-text-title is-' . esc_attr((string) $i['alignment']) . '"><h2>' . nl2br(esc_html((string) $i['heading'])) . '</h2><p>' . nl2br(esc_html((string) $i['description'])) . '</p></div>';
|
||
}
|
||
|
||
private static function render_buttons(array $i): string {
|
||
$padding_pc = in_array((string) ($i['pc_padding'] ?? 'large'), ['small', 'medium', 'large'], true) ? (string) $i['pc_padding'] : 'large';
|
||
$padding_m = in_array((string) ($i['m_padding'] ?? 'medium'), ['small', 'medium', 'large'], true) ? (string) $i['m_padding'] : 'medium';
|
||
$align = in_array((string) ($i['align'] ?? 'center'), ['left', 'center', 'right'], true) ? (string) $i['align'] : 'center';
|
||
$style = '';
|
||
foreach (['pc_width' => '--pc-width', 'm_width' => '--m-width', 'pc_gap' => '--pc-gap', 'm_gap' => '--m-gap'] as $key => $property) {
|
||
if (!empty($i[$key]) && is_numeric($i[$key])) $style .= $property . ':' . (float) $i[$key] . 'px;';
|
||
}
|
||
$button_class = 'but' . (!empty($i['radius']) ? ' radius' : '') . (!empty($i['hollow']) ? ' hollow' : '');
|
||
$buttons = '';
|
||
foreach ((array) ($i['items'] ?? []) as $index => $item) {
|
||
if (!is_array($item) || trim((string) ($item['text'] ?? '')) === '') continue;
|
||
$link = RendererHelpers::link((array) ($item['link'] ?? []));
|
||
$url = $link['url'] !== '' ? $link['url'] : 'javascript:void(0)';
|
||
if (!empty($i['go_link']) && $link['url'] !== '' && function_exists('aksrt_go_link')) $url = esc_url(aksrt_go_link($link['url'], true));
|
||
$target = $link['target'] === '_blank' ? ' target="_blank" rel="noopener noreferrer"' : '';
|
||
$color = sanitize_html_class((string) ($item['color'] ?? ''));
|
||
$item_class = trim($button_class . ' ' . $color);
|
||
$icon_left = !empty($item['icon_left']) ? self::icon_markup((string) $item['icon_left']) : '';
|
||
$icon_right = !empty($item['icon_right']) ? '<span class="ml6">' . self::icon_markup((string) $item['icon_right']) . '</span>' : '';
|
||
$animation = '';
|
||
$attr = '';
|
||
if (!empty($i['obs_animation'])) {
|
||
$animation = ' obs-animate ani-' . sanitize_html_class((string) $i['obs_animation']);
|
||
$attr = ' data-animation="' . esc_attr((string) $i['obs_animation']) . '" style="--delay:' . (float) $index * 0.15 . 's;"' . (!empty($i['animation_repeat']) ? ' data-animation-repeat="true"' : '');
|
||
}
|
||
$buttons .= '<div class="' . trim($animation) . '"' . $attr . '><a class="' . esc_attr($item_class) . '" href="' . $url . '"' . $target . '>' . $icon_left . wp_kses_post((string) $item['text']) . $icon_right . '</a></div>';
|
||
}
|
||
if ($buttons === '') return '';
|
||
return '<div class="widget-buttons mb20 pc-size-' . esc_attr($padding_pc) . ' m-size-' . esc_attr($padding_m) . '"><div class="widget-buttons-wrap flex hh align-' . esc_attr($align) . '" style="' . esc_attr($style) . '">' . $buttons . '</div></div>';
|
||
}
|
||
|
||
private static function render_layout_gap(array $i): string {
|
||
return '<div class="aksrt-widget-gap" aria-hidden="true" style="--aksrt-gap:' . (float) $i['pc_gap'] . 'px;--aksrt-gap-m:' . (float) $i['m_gap'] . 'px"></div>';
|
||
}
|
||
|
||
private static function render_tag_cloud(array $i): string {
|
||
$args = ['taxonomy' => (string) $i['taxonomy'], 'number' => (int) $i['number'], 'hide_empty' => true];
|
||
if ($i['orderby'] !== 'rand') {
|
||
$args['orderby'] = (string) $i['orderby'];
|
||
$args['order'] = 'DESC';
|
||
}
|
||
$terms = get_terms($args);
|
||
if (is_wp_error($terms) || !$terms) {
|
||
return RendererHelpers::empty_state(__('暂无标签', 'aksrt'));
|
||
}
|
||
if ($i['orderby'] === 'rand') {
|
||
shuffle($terms);
|
||
}
|
||
$html = '<div class="aksrt-widget-tag-cloud">';
|
||
foreach (array_slice($terms, 0, (int) $i['number']) as $term) {
|
||
$url = get_term_link($term);
|
||
if (!is_wp_error($url)) {
|
||
$html .= '<a href="' . esc_url($url) . '">' . esc_html($term->name) . (!empty($i['show_count']) ? '<small>' . (int) $term->count . '</small>' : '') . '</a>';
|
||
}
|
||
}
|
||
return $html . '</div>';
|
||
}
|
||
|
||
private static function render_recent_comments(array $i): string {
|
||
$comments = get_comments(['status' => 'approve', 'number' => (int) $i['count'], 'user__not_in' => RendererHelpers::ids((string) $i['exclude_users']), 'post__not_in' => RendererHelpers::ids((string) $i['exclude_posts'])]);
|
||
if (!$comments) {
|
||
return RendererHelpers::empty_state(__('暂无评论', 'aksrt'));
|
||
}
|
||
$html = '<ul class="aksrt-widget-comments">';
|
||
foreach ($comments as $comment) {
|
||
$html .= '<li><a class="aksrt-widget-comments__avatar" href="' . esc_url(get_comment_link($comment)) . '">' . (function_exists('aksrt_get_avatar_html') ? aksrt_get_avatar_html($comment, 48) : get_avatar($comment, 48)) . '</a><div><strong>' . esc_html(get_comment_author($comment)) . '</strong><p>' . esc_html(wp_trim_words(wp_strip_all_tags((string) $comment->comment_content), 18)) . '</p>' . (!empty($i['show_source']) ? '<a class="aksrt-widget-comments__source" href="' . esc_url(get_comment_link($comment)) . '">' . esc_html(get_the_title((int) $comment->comment_post_ID)) . '</a>' : '') . '</div></li>';
|
||
}
|
||
return $html . '</ul>';
|
||
}
|
||
|
||
private static function render_links(array $i): string {
|
||
$html = '<div class="aksrt-widget-links aksrt-widget-links--' . esc_attr((string) $i['style']) . '">';
|
||
foreach (array_slice((array) $i['links'], 0, (int) $i['limit']) as $item) {
|
||
$link = RendererHelpers::link((array) ($item['link'] ?? []));
|
||
if ($link['url']) {
|
||
$html .= '<a href="' . $link['url'] . '"' . RendererHelpers::link_attrs($link) . '><strong>' . esc_html((string) ($item['title'] ?? $link['text'])) . '</strong><small>' . esc_html((string) ($item['description'] ?? '')) . '</small></a>';
|
||
}
|
||
}
|
||
return $html . '</div>';
|
||
}
|
||
|
||
private static function render_notice(array $i): string {
|
||
$source = (string) ($i['source'] ?? '');
|
||
$lists = (array) ($i['img_ids'] ?? []);
|
||
$posts_query = false;
|
||
if ($source === 'posts' && class_exists(PostQuery::class)) {
|
||
$query_args = array_merge((array) ($i['posts_opts'] ?? []), ['count' => (int) ($i['count'] ?? 8), 'paginate' => '']);
|
||
$posts_query = PostQuery::build($query_args, 1);
|
||
$lists = [];
|
||
if ($posts_query && method_exists($posts_query, 'have_posts')) {
|
||
while ($posts_query->have_posts()) {
|
||
$posts_query->the_post();
|
||
$subtitle = class_exists('AKSRT\\Core\\PostMetaBox') ? \AKSRT\Core\PostMetaBox::get_meta_value(get_the_ID(), 'subtitle', '') : '';
|
||
$title = trim(wp_strip_all_tags(get_the_title())) . trim(wp_strip_all_tags((string) $subtitle));
|
||
if (!empty($i['desc_s'])) {
|
||
$description = class_exists('AKSRT\\Core\\PostMetaBox') ? \AKSRT\Core\PostMetaBox::get_meta_value(get_the_ID(), 'description', '') : '';
|
||
if (trim((string) $description) === '') $description = get_the_excerpt();
|
||
$title .= ': ' . wp_html_excerpt(wp_strip_all_tags((string) $description), 160, '...');
|
||
}
|
||
$lists[] = ['title' => $title, 'href' => get_permalink()];
|
||
}
|
||
wp_reset_postdata();
|
||
}
|
||
}
|
||
$msg_icon = ($posts_query && !empty($i['msg_icon'])) ? self::notice_icon((string) $i['msg_icon']) : '';
|
||
$slides = '';
|
||
$blank = !empty($i['blank']) ? ' target="_blank" rel="noopener noreferrer"' : '';
|
||
foreach ($lists as $notice) {
|
||
if (!is_array($notice) || trim((string) ($notice['title'] ?? '')) === '') continue;
|
||
$href = !empty($notice['href']) ? esc_url((string) $notice['href']) : 'javascript:void(0);';
|
||
$icon = !empty($notice['icon']) ? self::notice_icon((string) $notice['icon']) : '';
|
||
$title = wp_kses_post((string) ($notice['title'] ?? ''));
|
||
$slides .= '<div class="swiper-slide notice-slide"><a class="flex ac"' . $blank . ' href="' . $href . '">' . $icon . '<span class="ml6 text-ellipsis"><span class="">' . $title . '</span></span></a></div>';
|
||
}
|
||
$class = 'swiper-bulletin mb20 ' . sanitize_html_class((string) ($i['color'] ?? 'c-blue')) . (!empty($i['radius']) ? ' radius' : ' main-radius') . ($posts_query ? ' has-posts flex ac' : '');
|
||
$html = '<div class="new-swiper" data-interval="' . ((int) ($i['interval'] ?? 3) * 1000) . '" data-direction="vertical" data-loop="true" data-autoplay="1"><div class="swiper-wrapper">' . $slides . '</div></div>';
|
||
return '<div class="' . esc_attr($class) . '">' . $msg_icon . $html . '</div>';
|
||
}
|
||
|
||
private static function notice_icon(string $icon): string {
|
||
return '<span class="relative bulletin-icon flex jc shrink0">' . self::icon_markup($icon) . '</span>';
|
||
}
|
||
|
||
private static function icon_markup(string $icon): string {
|
||
$icon = trim($icon);
|
||
if ($icon === '') return '';
|
||
if (str_starts_with($icon, 'customsvg-')) {
|
||
return wp_kses(substr($icon, 10), [
|
||
'svg' => ['class' => true, 'viewbox' => true, 'viewBox' => true, 'width' => true, 'height' => true, 'fill' => true, 'xmlns' => true, 'aria-hidden' => true],
|
||
'use' => ['href' => true, 'xlink:href' => true],
|
||
'path' => ['d' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true],
|
||
]);
|
||
}
|
||
if (str_starts_with($icon, 'zibsvg-')) return '<svg class="icon" aria-hidden="true"><use xlink:href="#icon-' . esc_attr(substr($icon, 7)) . '"></use></svg>';
|
||
if (!str_contains($icon, 'fa ') && str_contains($icon, 'fa-')) $icon = 'fa ' . $icon;
|
||
return '<i class="' . esc_attr($icon) . '" aria-hidden="true"></i>';
|
||
}
|
||
}
|