Files
wordpress-theme-aksrt/inc/helpers/aksrt-icons.php
T
kate522 fe870bfc19 refactor(icons): replace local brand SVGs with FA5 classes
Unify social/OAuth/contact icons on the global Font Awesome 5.15.4 pack,
add aksrt_render_social_links for author hero, enqueue FA on admin SPA,
and delete assets/img/icons.
2026-07-17 10:05:27 +08:00

292 lines
8.5 KiB
PHP

<?php
/**
* AKSRT Theme — Brand / contact icons (Font Awesome 5)
*
* Short-key → FA5 class map, escaped <i> markup, contact option labels,
* and public social-link renderer. Local SVG inventory removed.
*
* @package AKSRT
* @version 3.4.0
*/
defined('ABSPATH') || exit;
// ============================================================
// PHP 7.x 兼容辅助函数(替代 PHP 8.0+ 的 match 表达式)
// ============================================================
if (!function_exists('aksrt_parse_country_list')):
/**
* 解析 GeoIP 屏蔽国家列表字符串为数组
*
* @param string $raw 逗号分隔的国家代码
* @return array 大写国家代码数组
*/
function aksrt_parse_country_list($raw) {
if (empty($raw)) return array();
$parts = explode(',', wp_unslash($raw));
$result = array();
foreach ($parts as $p) {
$trimmed = trim($p);
if ($trimmed !== '') {
$result[] = strtoupper($trimmed);
}
}
return array_values(array_filter($result));
}
endif;
// ============================================================
// Brand icon map (FA 5.15.4 free)
// ============================================================
if (!function_exists('aksrt_brand_icon_map')) :
/**
* Short key → FA5 class string (allowlisted).
*
* @return array<string, string>
*/
function aksrt_brand_icon_map(): array {
static $map = null;
if ($map !== null) {
return $map;
}
// FA5 free has no bilibili/gitee brands — use solid stand-ins.
$map = [
'github' => 'fab fa-github',
'google' => 'fab fa-google',
'x' => 'fab fa-twitter',
'twitter' => 'fab fa-twitter',
'email' => 'fas fa-envelope',
'envelope' => 'fas fa-envelope',
'wechat' => 'fab fa-weixin',
'weixin' => 'fab fa-weixin',
'qq' => 'fab fa-qq',
'weibo' => 'fab fa-weibo',
'telegram' => 'fab fa-telegram-plane',
'discord' => 'fab fa-discord',
'bilibili' => 'fas fa-tv',
'linkedin' => 'fab fa-linkedin-in',
'facebook' => 'fab fa-facebook-f',
'youtube' => 'fab fa-youtube',
'instagram' => 'fab fa-instagram',
'web' => 'fas fa-globe',
'globe' => 'fas fa-globe',
'gitee' => 'fas fa-code-branch',
'baidu' => 'fas fa-paw',
'alipay' => 'fab fa-alipay',
];
return $map;
}
endif;
if (!function_exists('aksrt_normalize_icon_key')) :
/**
* Normalize short key or full FA class string to a short map key.
*
* Examples: 'fab fa-github' | 'fabfa-github' | 'fa-github' | 'github' | 'weixin' → 'github' | 'wechat'
*
* @param string $name Raw icon key or FA class string.
* @return string Short key (may be empty).
*/
function aksrt_normalize_icon_key(string $name): string {
$name = trim($name);
if ($name === '') {
return '';
}
// Strip style prefixes: fab/fas/far + optional space or hyphen.
$name = preg_replace('/^(fab|fas|far)[\s-]*/i', '', $name);
$name = preg_replace('/^fa[\s-]*/i', '', (string) $name);
$name = trim((string) $name, "- \t\n\r\0\x0B");
$name = strtolower($name);
// Aliases → canonical keys used by the map / contact options.
$aliases = [
'weixin' => 'wechat',
'weixingzh' => 'wechat',
'twitter' => 'x',
'envelope' => 'email',
'globe' => 'web',
];
if (isset($aliases[$name])) {
return $aliases[$name];
}
return $name;
}
endif;
if (!function_exists('aksrt_brand_icon_class')) :
/**
* Resolve a short key or FA class string to an allowlisted FA5 class.
*
* @param string $name Short key or full FA class string.
* @return string e.g. 'fab fa-github', or '' if unknown.
*/
function aksrt_brand_icon_class(string $name): string {
$raw = trim($name);
if ($raw === '') {
return '';
}
// Pass through already-valid full FA class strings (footer contact_more).
if (preg_match('/^(fa[sbr]|fa)\s+fa-[\w-]+(?:\s+fa-[\w-]+)*$/i', $raw)) {
// Prefer mapped short key when strip matches our catalog (normalizes weixin etc.).
$key = aksrt_normalize_icon_key($raw);
$map = aksrt_brand_icon_map();
if ($key !== '' && isset($map[$key])) {
return $map[$key];
}
// Allowlisted style + glyph only — collapse whitespace, lower style prefix.
$parts = preg_split('/\s+/', strtolower($raw));
$parts = array_values(array_filter((array) $parts));
return implode(' ', $parts);
}
$key = aksrt_normalize_icon_key($raw);
if ($key === '') {
return '';
}
$map = aksrt_brand_icon_map();
return $map[$key] ?? '';
}
endif;
if (!function_exists('aksrt_brand_icon')) :
/**
* Escaped FA <i> markup for a brand/contact icon.
*
* @param string $name Short key or FA class string.
* @param array|string $extra_classes Optional extra classes.
* @return string Safe HTML or empty string when unknown.
*/
function aksrt_brand_icon(string $name, $extra_classes = []): string {
$class = aksrt_brand_icon_class($name);
if ($class === '') {
return '';
}
if (is_string($extra_classes) && $extra_classes !== '') {
$extra_classes = preg_split('/\s+/', $extra_classes) ?: [];
}
if (is_array($extra_classes) && $extra_classes !== []) {
$extra = array_filter(array_map('sanitize_html_class', $extra_classes));
if ($extra !== []) {
$class .= ' ' . implode(' ', $extra);
}
}
return '<i class="' . esc_attr($class) . '" aria-hidden="true"></i>';
}
endif;
// ============================================================
// 图标 key → 显示名称
// ============================================================
if (!function_exists('aksrt_icon_key_to_name')) :
function aksrt_icon_key_to_name($key) {
$key = aksrt_normalize_icon_key((string) $key);
$map = array(
'github' => 'GitHub', 'google' => 'Google', 'x' => 'X',
'email' => '邮箱', 'wechat' => '微信', 'qq' => 'QQ',
'weibo' => '微博', 'telegram' => 'Telegram',
'discord' => 'Discord', 'bilibili' => 'Bilibili',
'linkedin' => 'LinkedIn', 'facebook' => 'Facebook',
'youtube' => 'YouTube', 'instagram' => 'Instagram',
'web' => '网站',
'gitee' => 'Gitee',
'baidu' => 'Baidu',
'alipay' => 'Alipay',
);
return isset($map[$key]) ? $map[$key] : ucwords(str_replace('-', ' ', $key));
}
endif;
// ============================================================
// 联系方式图标选项(仅保留选定的 15 个)
// ============================================================
if (!function_exists('aksrt_contact_icon_options')) :
function aksrt_contact_icon_options($selected = '') {
$icons = array(
'github' => 'GitHub',
'google' => 'Google',
'x' => 'X',
'email' => '邮箱',
'wechat' => '微信',
'qq' => 'QQ',
'weibo' => '微博',
'telegram' => 'Telegram',
'discord' => 'Discord',
'bilibili' => 'Bilibili',
'linkedin' => 'LinkedIn',
'facebook' => 'Facebook',
'youtube' => 'YouTube',
'instagram' => 'Instagram',
'web' => '网站',
);
$selected = aksrt_normalize_icon_key((string) $selected);
$html = '<option value="">—</option>';
foreach ($icons as $key => $label) {
$sel = $key === $selected ? ' selected' : '';
$html .= '<option value="' . esc_attr($key) . '"' . $sel . '>' . esc_html($label) . '</option>';
}
return $html;
}
endif;
// ============================================================
// Public social links (author hero / profile)
// ============================================================
if (!function_exists('aksrt_render_social_links')) :
/**
* Echo a list of public contact links with FA icons.
*
* @param array<int, array{name?:string,url?:string,icon?:string,title?:string,label?:string}> $contacts
*/
function aksrt_render_social_links(array $contacts): void {
$links = [];
foreach ($contacts as $contact) {
if (!is_array($contact)) {
continue;
}
$url = isset($contact['url']) ? (string) $contact['url'] : '';
$icon = isset($contact['icon']) ? (string) $contact['icon'] : '';
if ($url === '' || $icon === '') {
continue;
}
$icon_html = aksrt_brand_icon($icon);
if ($icon_html === '') {
$icon_html = '<i class="fas fa-globe" aria-hidden="true"></i>';
}
$label = (string) ($contact['name'] ?? $contact['title'] ?? $contact['label'] ?? '');
if ($label === '') {
$label = aksrt_icon_key_to_name($icon);
}
$links[] = [
'url' => $url,
'label' => $label,
'icon' => $icon_html,
];
}
if ($links === []) {
return;
}
echo '<div class="profile-social-links">';
foreach ($links as $link) {
printf(
'<a class="social-icon-link" href="%1$s" target="_blank" rel="noopener noreferrer" title="%2$s" aria-label="%2$s">%3$s</a>',
esc_url($link['url']),
esc_attr($link['label']),
$link['icon'] // pre-escaped via aksrt_brand_icon / fixed globe fallback
);
}
echo '</div>';
}
endif;