325 lines
11 KiB
PHP
325 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* Unified frontend avatar helpers.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
if (!function_exists('aksrt_resolve_avatar_user_id')) {
|
|
/**
|
|
* Resolve a WordPress user ID from an avatar subject.
|
|
*
|
|
* @param mixed $subject User ID, WP_User, comment, post, or email.
|
|
*/
|
|
function aksrt_resolve_avatar_user_id($subject): int {
|
|
if ($subject instanceof WP_User) {
|
|
return (int) $subject->ID;
|
|
}
|
|
if ($subject instanceof WP_Post) {
|
|
return (int) $subject->post_author;
|
|
}
|
|
if ($subject instanceof WP_Comment) {
|
|
return (int) $subject->user_id;
|
|
}
|
|
if (is_object($subject) && isset($subject->user_id)) {
|
|
return (int) $subject->user_id;
|
|
}
|
|
if (is_numeric($subject)) {
|
|
return max(0, (int) $subject);
|
|
}
|
|
if (is_string($subject) && is_email($subject)) {
|
|
$user = get_user_by('email', $subject);
|
|
return $user instanceof WP_User ? (int) $user->ID : 0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_resolve_avatar_email')) {
|
|
/**
|
|
* Resolve an email address from an avatar subject.
|
|
*
|
|
* @param mixed $subject User ID, WP_User, comment, post, or email.
|
|
*/
|
|
function aksrt_resolve_avatar_email($subject): string {
|
|
if ($subject instanceof WP_User) {
|
|
return sanitize_email((string) $subject->user_email);
|
|
}
|
|
if ($subject instanceof WP_Comment && is_email((string) $subject->comment_author_email)) {
|
|
return sanitize_email((string) $subject->comment_author_email);
|
|
}
|
|
if (is_object($subject) && isset($subject->comment_author_email) && is_email((string) $subject->comment_author_email)) {
|
|
return sanitize_email((string) $subject->comment_author_email);
|
|
}
|
|
if (is_string($subject) && is_email($subject)) {
|
|
return sanitize_email($subject);
|
|
}
|
|
|
|
$user_id = aksrt_resolve_avatar_user_id($subject);
|
|
$user = $user_id > 0 ? get_userdata($user_id) : false;
|
|
return $user instanceof WP_User ? sanitize_email((string) $user->user_email) : '';
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_get_user_feature_settings')) {
|
|
/**
|
|
* Return the canonical user media and signature settings.
|
|
*
|
|
* @return array{avatar_upload_s:bool,avatar_external:string,avatar_default_img:string,user_cover_upload_s:bool,user_cover_img:string,user_desc_std:string}
|
|
*/
|
|
function aksrt_get_user_feature_settings(): array {
|
|
if (class_exists('\AKSRT\Settings\Settings')) {
|
|
return \AKSRT\Settings\Settings::get_user_feature_settings();
|
|
}
|
|
|
|
return [
|
|
'avatar_upload_s' => true,
|
|
'avatar_external' => '',
|
|
'avatar_default_img' => get_theme_file_uri('assets/img/avatar-default.png'),
|
|
'user_cover_upload_s' => true,
|
|
'user_cover_img' => get_theme_file_uri('assets/img/user_t.jpg'),
|
|
'user_desc_std' => '这家伙很懒,什么都没有写...',
|
|
];
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_get_default_avatar_url')) {
|
|
/**
|
|
* Return the bundled avatar used when a user has no custom image.
|
|
*/
|
|
function aksrt_get_default_avatar_url(): string {
|
|
$settings = aksrt_get_user_feature_settings();
|
|
return (string) apply_filters(
|
|
'aksrt_default_avatar_url',
|
|
(string) $settings['avatar_default_img']
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_get_external_avatar_url')) {
|
|
/**
|
|
* Return the configured external avatar for a subject.
|
|
*
|
|
* @param mixed $subject User subject.
|
|
*/
|
|
function aksrt_get_external_avatar_url($subject, int $size = 96): string {
|
|
$settings = aksrt_get_user_feature_settings();
|
|
$hosts = [
|
|
'gravatar' => 'https://www.gravatar.com/avatar/',
|
|
'weavatar' => 'https://weavatar.com/avatar/',
|
|
'cravatar' => 'https://cn.cravatar.com/avatar/',
|
|
'geekzu' => 'https://sdn.geekzu.org/avatar/',
|
|
'loli' => 'https://gravatar.loli.net/avatar/',
|
|
'sep' => 'https://cdn.sep.cc/avatar/',
|
|
'v2ex' => 'https://cdn.v2ex.com/gravatar/',
|
|
];
|
|
$host = $hosts[(string) $settings['avatar_external']] ?? '';
|
|
$email = aksrt_resolve_avatar_email($subject);
|
|
if ($host === '' || $email === '') {
|
|
return '';
|
|
}
|
|
|
|
$size = max(1, min(2048, $size));
|
|
return add_query_arg([
|
|
's' => $size,
|
|
'd' => aksrt_get_default_avatar_url(),
|
|
], $host . hash('sha256', strtolower(trim($email))));
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_get_custom_avatar_url')) {
|
|
/**
|
|
* Return the locally uploaded avatar URL.
|
|
*/
|
|
function aksrt_get_custom_avatar_url(int $user_id, int $size = 96): string {
|
|
if ($user_id <= 0) {
|
|
return '';
|
|
}
|
|
|
|
$attachment_id = (int) get_user_meta($user_id, 'aksrt_avatar_attachment_id', true);
|
|
if ($attachment_id <= 0) {
|
|
return '';
|
|
}
|
|
|
|
$attachment = get_post($attachment_id);
|
|
if (!$attachment instanceof WP_Post || $attachment->post_type !== 'attachment' || (int) $attachment->post_author !== $user_id || !wp_attachment_is_image($attachment_id)) {
|
|
return '';
|
|
}
|
|
|
|
$image = wp_get_attachment_image_url($attachment_id, [$size, $size]);
|
|
return is_string($image) ? $image : '';
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_get_avatar_url')) {
|
|
/**
|
|
* Resolve the canonical avatar URL used by all AKSRT frontend surfaces.
|
|
*
|
|
* @param mixed $subject User subject accepted by get_avatar_url().
|
|
* @param array|int $args WordPress avatar args or a numeric size.
|
|
*/
|
|
function aksrt_get_avatar_url($subject, $args = []): string {
|
|
if (is_numeric($args)) {
|
|
$args = ['size' => (int) $args];
|
|
}
|
|
$args = is_array($args) ? $args : [];
|
|
$size = max(16, min(512, (int) ($args['size'] ?? 96)));
|
|
$user_id = aksrt_resolve_avatar_user_id($subject);
|
|
$custom = aksrt_get_custom_avatar_url($user_id, $size);
|
|
if ($custom !== '') {
|
|
return $custom;
|
|
}
|
|
$external = aksrt_get_external_avatar_url($subject, $size);
|
|
if ($external !== '') {
|
|
return $external;
|
|
}
|
|
return aksrt_get_default_avatar_url();
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_get_avatar_html')) {
|
|
/**
|
|
* Render a canonical avatar image.
|
|
*
|
|
* @param mixed $subject User subject.
|
|
* @param int $size Pixel size.
|
|
* @param array<string,mixed> $attrs Additional safe image attributes.
|
|
*/
|
|
function aksrt_get_avatar_html($subject, int $size = 96, array $attrs = []): string {
|
|
$size = max(16, min(512, $size));
|
|
$url = aksrt_get_avatar_url($subject, ['size' => $size]);
|
|
if ($url === '') {
|
|
return '';
|
|
}
|
|
|
|
$defaults = [
|
|
'class' => 'avatar avatar-' . $size . ' photo',
|
|
'alt' => '',
|
|
'width' => (string) $size,
|
|
'height' => (string) $size,
|
|
];
|
|
$attrs = array_merge($defaults, $attrs);
|
|
unset($attrs['loading']);
|
|
$class = (string) ($attrs['class'] ?? $defaults['class']);
|
|
unset($attrs['class']);
|
|
$html_attrs = '';
|
|
foreach ($attrs as $name => $value) {
|
|
$name = sanitize_key((string) $name);
|
|
if ($name === '' || $value === false || $value === null) {
|
|
continue;
|
|
}
|
|
$html_attrs .= ' ' . $name . '="' . esc_attr((string) $value) . '"';
|
|
}
|
|
return '<img' . aksrt_get_lazy_attr('lazy_avatar', $url, $class, aksrt_get_default_avatar_url()) . $html_attrs . '>';
|
|
}
|
|
}
|
|
|
|
add_filter('get_avatar_data', static function (array $args, $subject): array {
|
|
$args['url'] = aksrt_get_avatar_url($subject, ['size' => (int) ($args['size'] ?? 96)]);
|
|
$args['found_avatar'] = true;
|
|
return $args;
|
|
}, 20, 2);
|
|
|
|
if (!function_exists('aksrt_get_avatar_box')) {
|
|
/**
|
|
* legacy parity: 头像框(span.avatar-img + VIP 角标 + 可选主页链接)。
|
|
*
|
|
* @param mixed $id_or_email 用户 ID / WP_User / 邮箱。
|
|
* @param string $class 外层 span 类(avatar-img avatar-lg 等)。
|
|
* @param bool $link 是否包一层用户主页链接。
|
|
* @param bool $vip 是否叠加 VIP 角标图。
|
|
*/
|
|
function aksrt_get_avatar_box($id_or_email, string $class = 'avatar-img', bool $link = true, bool $vip = true): string {
|
|
$user_id = aksrt_resolve_avatar_user_id($id_or_email);
|
|
$subject = $user_id > 0 ? $user_id : $id_or_email;
|
|
$img = aksrt_get_avatar_html($subject, 80, ['class' => 'avatar']);
|
|
|
|
$badge = '';
|
|
if ($vip && $user_id > 0) {
|
|
// 禁封角标覆盖 VIP 角标位(参考主题 user_avatar_badge 过滤器覆盖语义)。
|
|
$ban_badge = function_exists('aksrt_get_user_ban_avatar_badge')
|
|
? aksrt_get_user_ban_avatar_badge($user_id)
|
|
: '';
|
|
if ($ban_badge !== '') {
|
|
$badge = $ban_badge;
|
|
} elseif (function_exists('aksrt_get_user_vip_level') && aksrt_get_user_vip_level($user_id) > 0) {
|
|
$icon_url = '';
|
|
if (class_exists('\AKSRT\Settings\Settings')) {
|
|
$vip_settings = \AKSRT\Settings\Settings::get_payment_vip_settings();
|
|
$level = aksrt_get_user_vip_level($user_id);
|
|
$icon_url = esc_url((string) ($vip_settings['levels'][$level]['img_icon'] ?? ''));
|
|
}
|
|
if ($icon_url !== '') {
|
|
$badge_text = function_exists('aksrt_get_vip_badge_text') ? aksrt_get_vip_badge_text($user_id) : 'VIP';
|
|
$badge = '<img class="avatar-badge" src="' . $icon_url . '" title="' . esc_attr($badge_text) . '" alt="' . esc_attr($badge_text) . '">';
|
|
}
|
|
}
|
|
}
|
|
|
|
$html = '<span class="' . esc_attr($class) . '">' . $img . $badge . '</span>';
|
|
if ($link && $user_id > 0) {
|
|
$home = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($user_id) : get_author_posts_url($user_id);
|
|
$html = '<a href="' . esc_url($home) . '">' . $html . '</a>';
|
|
}
|
|
return $html;
|
|
}
|
|
}
|
|
|
|
add_action('user_edit_form_tag', static function (): void {
|
|
echo ' enctype="multipart/form-data"';
|
|
});
|
|
|
|
if (!function_exists('aksrt_is_avatar_upload_enabled')) {
|
|
/**
|
|
* Whether local avatar uploads are enabled by attachment policy.
|
|
*/
|
|
function aksrt_is_avatar_upload_enabled(): bool {
|
|
$settings = aksrt_get_user_feature_settings();
|
|
return !empty($settings['avatar_upload_s']);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_is_user_cover_upload_enabled')) {
|
|
function aksrt_is_user_cover_upload_enabled(): bool {
|
|
$settings = aksrt_get_user_feature_settings();
|
|
return !empty($settings['user_cover_upload_s']);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_get_user_cover_url')) {
|
|
function aksrt_get_user_cover_url(int $user_id): string {
|
|
$custom = $user_id > 0 ? esc_url_raw((string) get_user_meta($user_id, 'aksrt_background_image', true)) : '';
|
|
if ($custom !== '') {
|
|
return $custom;
|
|
}
|
|
$settings = aksrt_get_user_feature_settings();
|
|
return (string) $settings['user_cover_img'];
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_get_user_description')) {
|
|
function aksrt_get_user_description(int $user_id): string {
|
|
$description = $user_id > 0 ? trim((string) get_user_meta($user_id, 'description', true)) : '';
|
|
if ($description !== '') {
|
|
return $description;
|
|
}
|
|
$settings = aksrt_get_user_feature_settings();
|
|
return (string) $settings['user_desc_std'];
|
|
}
|
|
}
|
|
|
|
if (!function_exists('aksrt_delete_owned_avatar_attachment')) {
|
|
/**
|
|
* Delete an avatar attachment only when it belongs to the target user.
|
|
*/
|
|
function aksrt_delete_owned_avatar_attachment(int $attachment_id, int $user_id): bool {
|
|
$attachment = $attachment_id > 0 ? get_post($attachment_id) : null;
|
|
if (!$attachment instanceof WP_Post || $attachment->post_type !== 'attachment' || (int) $attachment->post_author !== $user_id) {
|
|
return false;
|
|
}
|
|
return (bool) wp_delete_attachment($attachment_id, true);
|
|
}
|
|
}
|