$args { * @type string $type style-1|style-2|style-3|style-4|''. * @type string $class Extra classes on .graphic. * @type string $img Image URL. * @type string $video Optional video URL (desktop). * @type string $alt Image alt. * @type array{url?:string,target?:string}|string $link Link. * @type string $text1 Primary text. * @type string $text2 Secondary text. * @type string $text3 Tertiary / badge text (HTML allowed for icon+count). * @type string $more Extra absolute HTML (badge etc). * @type bool $lazy Use the legacy lazySizes contract. * @type int|float $height_scale Padding-bottom % (0 = default CSS). * @type int|float $mask_opacity 0–100 overlay opacity. * @type string $hide pc|m|'' hide on device. * } * @param bool $echo Echo instead of return. */ function aksrt_graphic_card(array $args = [], bool $echo = false): string { $defaults = [ 'type' => '', 'hide' => '', 'class' => 'mb10', 'img' => '', 'video' => '', 'alt' => __('图片', 'aksrt'), 'link' => [ 'url' => '', 'target' => '', ], 'text1' => '', 'text2' => '', 'text3' => '', 'more' => '', 'lazy' => true, 'height_scale' => 0, 'mask_opacity' => 0, ]; $args = wp_parse_args($args, $defaults); $img_url = trim((string) $args['img']); $video_url = trim((string) $args['video']); if ($img_url === '' && $video_url === '') { return ''; } // Device visibility. $hide = sanitize_key((string) $args['hide']); if ($hide === 'pc' || $hide === 'm') { $is_mobile = function_exists('wp_is_mobile') ? wp_is_mobile() : false; if ((!$is_mobile && $hide === 'pc') || ($is_mobile && $hide === 'm')) { return ''; } } $type = sanitize_key((string) $args['type']); if ($type !== '' && !in_array($type, ['style-1', 'style-2', 'style-3', 'style-4'], true)) { // Accept bare "1"–"4". if (preg_match('/^[1-4]$/', $type)) { $type = 'style-' . $type; } else { $type = ''; } } $link = $args['link']; if (is_string($link)) { $link = ['url' => $link, 'target' => '']; } if (!is_array($link)) { $link = ['url' => '', 'target' => '']; } $link_url = esc_url(trim((string) ($link['url'] ?? ''))); $link_target = sanitize_text_field((string) ($link['target'] ?? '')); $lazy = !empty($args['lazy']); $media = ''; if ($video_url !== '' && !(function_exists('wp_is_mobile') && wp_is_mobile())) { if ($lazy && aksrt_is_lazy('lazy_other')) { $poster = $img_url !== '' ? ' data-poster="' . esc_url($img_url) . '"' : ''; $media = ''; } else { $poster = $img_url !== '' ? ' poster="' . esc_url($img_url) . '"' : ''; $media = ''; } } elseif ($img_url !== '') { $alt = (string) ($args['alt'] !== '' ? $args['alt'] : __('图片', 'aksrt')); if ($lazy) { $media = ''; } else { $media = '' . esc_attr($alt) . ''; } } if ($media === '') { return ''; } $mask_opacity = max(0, min(100, (int) $args['mask_opacity'])); $mask = $mask_opacity > 0 ? '
' : ''; // text1/2/3 may contain small trusted HTML (icons); sanitize lightly. $text1 = (string) $args['text1']; $text2 = (string) $args['text2']; $text3 = (string) $args['text3']; $allow = [ 'i' => ['class' => true, 'aria-hidden' => true], 'span' => ['class' => true, 'title' => true], 'item' => ['class' => true, 'data-toggle' => true, 'title' => true], 'b' => [], 'strong'=> [], 'em' => [], 'badge' => ['class' => true], ]; $text1_safe = $text1 !== '' ? wp_kses($text1, $allow) : ''; $text2_safe = $text2 !== '' ? wp_kses($text2, $allow) : ''; $text3_safe = $text3 !== '' ? wp_kses($text3, $allow) : ''; $more = (string) $args['more']; $more_safe = $more !== '' ? wp_kses($more, array_merge($allow, [ 'div' => ['class' => true, 'style' => true], 'badge' => ['class' => true], ])) : ''; $text = ''; if ($type === 'style-2') { $text = '
'; $text .= $text1_safe !== '' ? '
' . $text1_safe . '
' : ''; $text .= $text2_safe !== '' ? '
' . $text2_safe . '
' : ''; $text .= '
'; if ($text3_safe !== '') { $text .= '
' . $text3_safe . '
'; } } elseif ($type === 'style-3') { $text = $text1_safe !== '' ? '
' . $text1_safe . '
' : ''; $text .= '
'; $text .= $text2_safe !== '' ? '
' . $text2_safe . '
' : ''; $text .= $text3_safe !== '' ? '
' . $text3_safe . '
' : ''; $text .= '
'; } elseif ($type === 'style-4') { if ($text3_safe !== '') { $text = '
' . $text3_safe . '
'; } } else { // style-1 / default. $inner = $text1_safe !== '' ? '
' . $text1_safe . '
' : ''; $inner .= $text3_safe !== '' ? '
' . $text3_safe . '
' : ''; $inner .= $text2_safe !== '' ? '
' . $text2_safe . '
' : ''; $text = $inner !== '' ? '
' . $inner . '
' : ''; } $text .= $more_safe; $class = trim('graphic hover-zoom-img ' . (string) $args['class'] . ' ' . $type); $height_scale = max(0, (int) $args['height_scale']); $scale_attr = $height_scale > 0 ? ' style="padding-bottom:' . esc_attr((string) $height_scale) . '%!important;"' : ''; $html = ''; if ($link_url !== '') { $tgt = $link_target !== '' ? ' target="' . esc_attr($link_target) . '" rel="noopener noreferrer"' : ''; $html .= ''; } if ($type === 'style-4') { $html .= '
'; } $html .= '
'; $html .= $media; $html .= $mask; $html .= $text; if ($type === 'style-4') { $html .= '
'; // close .graphic early for style-4 body. $html .= '
'; $html .= $text1_safe !== '' ? '
' . $text1_safe . '
' : ''; $html .= $text2_safe !== '' ? '
' . $text2_safe . '
' : ''; $html .= '
'; // padding + main-shadow } else { $html .= ''; } if ($link_url !== '') { $html .= '
'; } if ($echo) { echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return ''; } return $html; } endif; if (!function_exists('aksrt_author_minicard')) : /** * Author mini card for user lists (legacy). * * @param int $user_id * @param string $class * @param bool $blur_bg */ function aksrt_author_minicard(int $user_id, string $class = '', bool $blur_bg = false): string { if ($user_id <= 0) { return ''; } $user = get_userdata($user_id); if (!$user) { return ''; } $avatar_url = function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user_id, ['size' => 96]) : get_avatar_url($user_id, ['size' => 96]); $home = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($user_id) : get_author_posts_url($user_id); $avatar_box = '
'; $name = function_exists('aksrt_get_user_name') ? aksrt_get_user_name([ 'id' => $user_id, 'class' => 'flex ac flex1', 'follow' => false, 'medal' => true, 'auth' => true, 'level' => true, ]) : '' . esc_html($user->display_name) . ''; // Follow control (separate from name, legacy places both in dt). // 服务端直接输出真实关注状态(legacy parity: zib_get_user_follow 恒渲染当前态)。 $follow = ''; $current = get_current_user_id(); if ($current !== $user_id) { $is_following = false; if (is_user_logged_in() && class_exists('\AKSRT\User\Follow')) { $is_following = \AKSRT\User\Follow::get_instance()->is_following($current, $user_id); } $follow_label = $is_following ? __('已关注', 'aksrt') : __('关注', 'aksrt'); $follow_title = $is_following ? __('点击取消关注', 'aksrt') : __('关注该用户', 'aksrt'); $follow_state = $is_following ? ' is-following' : ''; if (is_user_logged_in()) { $follow = ''; } else { $login = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(); $follow = ' ' . esc_html__('关注', 'aksrt') . ''; } } $desc = function_exists('aksrt_get_user_description') ? aksrt_get_user_description($user_id) : trim((string) get_user_meta($user_id, 'description', true)); $blur = ''; if ($blur_bg) { $blur = '
'; } return '
' . $blur . '
'; } endif;