WPAVATAR_PLUGIN_URL . 'assets/images/default-avatar.png',
'name' => __('默认头像', 'wpavatar')
];
}
if (file_exists(WPAVATAR_PLUGIN_DIR . 'assets/images/wapuu.png')) {
$avatars['wapuu'] = [
'url' => WPAVATAR_PLUGIN_URL . 'assets/images/wapuu.png',
'name' => __('文派Wapuu', 'wpavatar')
];
}
if (file_exists(WPAVATAR_PLUGIN_DIR . 'assets/images/wapuu-china.png')) {
$avatars['wapuu-china'] = [
'url' => WPAVATAR_PLUGIN_URL . 'assets/images/wapuu-china.png',
'name' => __('中国Wapuu', 'wpavatar')
];
}
if (file_exists(WPAVATAR_PLUGIN_DIR . 'assets/images/cravatar-default.png')) {
$avatars['cravatar'] = [
'url' => WPAVATAR_PLUGIN_URL . 'assets/images/cravatar-default.png',
'name' => __('Cravatar默认', 'wpavatar')
];
}
if (file_exists(WPAVATAR_PLUGIN_DIR . 'assets/images/cravatar-logo.png')) {
$avatars['cravatar-logo'] = [
'url' => WPAVATAR_PLUGIN_URL . 'assets/images/cravatar-logo.png',
'name' => __('Cravatar标志', 'wpavatar')
];
}
return $avatars;
}
public static function modify_profile_picture_description() {
return '' . __('您可以在初认头像修改您的资料图片', 'wpavatar') . '';
}
/**
* 预处理头像数据,根据配置决定是否强制使用MD5
*/
public static function pre_get_avatar_data($args, $id_or_email) {
if (is_null($args)) {
$args = [];
}
$email = self::get_email_from_id_or_email($id_or_email);
if (empty($email)) {
return $args;
}
// 检查是否为 Cravatar 线路,只有 Cravatar 线路才强制使用 MD5
$cdn_type = wpavatar_get_option('wpavatar_cdn_type', 'cravatar_route');
if ($cdn_type === 'cravatar_route') {
// Cravatar 线路强制使用 MD5
$args['hash_method'] = 'md5';
// 移除SHA256散列标记(如果存在)
if (isset($args['hash']) && $args['hash'] === 'sha256') {
unset($args['hash']);
}
} else {
// 尊重用户选择的哈希方法
$hash_method = wpavatar_get_option('wpavatar_hash_method', 'md5');
$args['hash_method'] = $hash_method;
}
// 确保我们有email_hash供后续使用,根据选择的哈希方法计算
if ($args['hash_method'] === 'sha256' && function_exists('hash')) {
$args['wpavatar_email_hash'] = hash('sha256', strtolower(trim($email)));
} else {
$args['wpavatar_email_hash'] = md5(strtolower(trim($email)));
}
// 添加超时属性
$timeout = wpavatar_get_option('wpavatar_timeout', 5);
$args['extra_attr'] = isset($args['extra_attr']) ? $args['extra_attr'] : '';
$args['extra_attr'] .= ' data-timeout="' . esc_attr($timeout) . '"';
return $args;
}
/**
* 直接替换头像URL,根据配置决定是否强制使用Cravatar和MD5哈希
*/
public static function get_avatar_url($url, $id_or_email) {
// 如果地址已经是我们支持的域名,则直接返回
$cdn_type = wpavatar_get_option('wpavatar_cdn_type', 'cravatar_route');
$cdn_domain = '';
if ($cdn_type === 'cravatar_route') {
$cdn_domain = wpavatar_get_option('wpavatar_cravatar_route', 'cravatar.com');
} elseif ($cdn_type === 'third_party') {
$cdn_domain = wpavatar_get_option('wpavatar_third_party_mirror', 'weavatar.com');
} elseif ($cdn_type === 'custom') {
$cdn_domain = wpavatar_get_option('wpavatar_custom_cdn', '');
if (empty($cdn_domain)) {
$cdn_domain = 'cravatar.com';
}
}
// 检查URL是否已经使用了支持的域名
if (strpos($url, $cdn_domain) !== false) {
return $url;
}
// 获取用户设置的哈希方法
$hash_method = wpavatar_get_option('wpavatar_hash_method', 'md5');
// 仅当使用Cravatar线路或者自定义CDN包含"cravatar"时,强制使用MD5
$force_md5 = ($cdn_type === 'cravatar_route' ||
($cdn_type === 'custom' && strpos(strtolower($cdn_domain), 'cravatar') !== false));
if ($force_md5) {
// 对于Cravatar,删除hash=sha256参数
$url = str_replace(['?hash=sha256', '&hash=sha256'], ['', ''], $url);
}
// 从URL提取邮箱哈希
$hash = '';
// 根据URL中是否包含SHA256或MD5哈希进行处理
if (preg_match('/\/avatar\/([a-f0-9]{64})/', $url, $matches)) {
// SHA256哈希
if ($force_md5) {
// 如果强制使用MD5,需要重新计算哈希
$email = self::get_email_from_id_or_email($id_or_email);
if (!empty($email)) {
$hash = md5(strtolower(trim($email)));
} else {
// 如果无法获取邮箱,则使用默认头像
return self::replace_avatar_url($url);
}
} else {
// 尊重SHA256
$hash = $matches[1];
}
} elseif (preg_match('/\/avatar\/([a-f0-9]{32})/', $url, $matches)) {
// MD5哈希
$hash = $matches[1];
}
if (empty($hash)) {
return self::replace_avatar_url($url);
}
// 构建新的头像URL
$new_url = 'https://' . $cdn_domain . '/avatar/' . $hash;
// 保留原始URL中的参数
$query_params = [];
$parsed_url = parse_url($url);
if (isset($parsed_url['query'])) {
parse_str($parsed_url['query'], $query_params);
}
// 如果使用SHA256并且不是强制使用MD5的情况,添加哈希参数
if ($hash_method === 'sha256' && !$force_md5 && strlen($hash) === 64) {
$query_params['hash'] = 'sha256';
}
// 构建完整URL
if (!empty($query_params)) {
$new_url .= '?' . http_build_query($query_params);
}
return $new_url;
}
/**
* 从ID或邮箱获取邮箱地址
*/
public static function get_email_from_id_or_email($id_or_email) {
$email = '';
if (is_numeric($id_or_email)) {
$user = get_user_by('id', (int)$id_or_email);
if ($user) {
$email = $user->user_email;
}
} elseif (is_string($id_or_email)) {
if (is_email($id_or_email)) {
$email = $id_or_email;
}
} elseif (is_object($id_or_email)) {
if ($id_or_email instanceof \WP_User) {
$email = $id_or_email->user_email;
} elseif ($id_or_email instanceof \WP_Post) {
$user = get_user_by('id', (int)$id_or_email->post_author);
if ($user) {
$email = $user->user_email;
}
} elseif ($id_or_email instanceof \WP_Comment) {
if (!empty($id_or_email->user_id)) {
$user = get_user_by('id', (int)$id_or_email->user_id);
if ($user) {
$email = $user->user_email;
}
} elseif (!empty($id_or_email->comment_author_email)) {
$email = $id_or_email->comment_author_email;
}
} elseif (isset($id_or_email->comment_ID)) {
$comment = get_comment($id_or_email->comment_ID);
if ($comment) {
if ($comment->user_id) {
$user = get_user_by('id', (int)$comment->user_id);
if ($user) {
$email = $user->user_email;
}
} else {
$email = $comment->comment_author_email;
}
}
}
}
return $email;
}
public static function replace_avatar_url($url) {
// 移除hash=sha256参数,因为Cravatar不支持
if (strpos($url, 'hash=sha256') !== false) {
$url = str_replace(['?hash=sha256', '&hash=sha256'], ['', ''], $url);
}
// 更换Gravatar域名为Cravatar域名
foreach (self::$gravatar_domains as $domain) {
if (strpos($url, $domain) !== false) {
$cdn_type = wpavatar_get_option('wpavatar_cdn_type', 'cravatar_route');
$cdn_domain = '';
if ($cdn_type === 'cravatar_route') {
$cdn_domain = wpavatar_get_option('wpavatar_cravatar_route', 'cravatar.com');
} elseif ($cdn_type === 'third_party') {
$cdn_domain = wpavatar_get_option('wpavatar_third_party_mirror', 'weavatar.com');
} elseif ($cdn_type === 'custom') {
$custom_cdn = wpavatar_get_option('wpavatar_custom_cdn', '');
if (!empty($custom_cdn)) {
$cdn_domain = $custom_cdn;
} else {
$cdn_domain = 'cravatar.com';
}
}
return str_replace($domain, $cdn_domain, $url);
}
}
return $url;
}
public static function add_seo_alt($avatar, $id_or_email, $size, $default, $alt) {
// 添加SEO友好的alt属性
$seo_alt = wpavatar_get_option('wpavatar_seo_alt');
if (!empty($seo_alt)) {
$user = false;
if (is_numeric($id_or_email)) {
$user = get_user_by('id', $id_or_email);
} elseif (is_object($id_or_email)) {
if (isset($id_or_email->user_id) && $id_or_email->user_id) {
$user = get_user_by('id', $id_or_email->user_id);
} elseif (isset($id_or_email->comment_author_email)) {
$user = (object) [
'display_name' => $id_or_email->comment_author
];
}
} elseif (is_string($id_or_email) && is_email($id_or_email)) {
$user = get_user_by('email', $id_or_email);
}
$alt_text = $user ? sprintf($seo_alt, $user->display_name) : __('用户头像', 'wpavatar');
$avatar = preg_replace('/alt=([\'"])[^\'"]*\\1/', "alt='$alt_text'", $avatar);
}
// 添加头像加载失败的备用显示
if (wpavatar_get_option('wpavatar_fallback_mode', 1)) {
$fallback_type = wpavatar_get_option('wpavatar_fallback_avatar', 'default');
$local_avatars = self::get_local_avatars();
if (isset($local_avatars[$fallback_type])) {
$fallback_url = $local_avatars[$fallback_type]['url'];
if (strpos($avatar, 'onerror') === false) {
$avatar = str_replace(' (time() - $cache_expire)) {
$cached_url = content_url(str_replace(WP_CONTENT_DIR, '', $cache_file));
return str_replace($url, esc_url($cached_url), $avatar);
}
if (self::cache_remote_avatar($url, $cache_file)) {
$cached_url = content_url(str_replace(WP_CONTENT_DIR, '', $cache_file));
return str_replace($url, esc_url($cached_url), $avatar);
}
return $avatar;
}
public static function get_avatar_hash($id_or_email) {
$email = Cravatar::get_email_from_id_or_email($id_or_email);
if (empty($email)) {
return '';
}
// 获取用户设置的哈希方法
$cdn_type = wpavatar_get_option('wpavatar_cdn_type', 'cravatar_route');
$hash_method = wpavatar_get_option('wpavatar_hash_method', 'md5');
// 检查是否需要强制使用MD5(针对Cravatar服务)
$force_md5 = false;
if ($cdn_type === 'cravatar_route') {
$force_md5 = true;
} elseif ($cdn_type === 'custom') {
$custom_cdn = wpavatar_get_option('wpavatar_custom_cdn', '');
if (strpos(strtolower($custom_cdn), 'cravatar') !== false) {
$force_md5 = true;
}
}
// 根据设置和条件选择哈希方法
if ($force_md5 || $hash_method === 'md5') {
return md5(strtolower(trim($email)));
} else {
// 使用SHA256
if (function_exists('hash')) {
return hash('sha256', strtolower(trim($email)));
} else {
// 如果不支持hash函数,回退到MD5
return md5(strtolower(trim($email)));
}
}
}
public static function get_cache_path($hash, $size) {
$dir = wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR);
$dir = trailingslashit($dir);
// Add site-specific directory if multisite
if (is_multisite()) {
$blog_id = get_current_blog_id();
$dir = $dir . 'site-' . $blog_id . '/';
}
wp_mkdir_p($dir);
return $dir . "{$hash}-{$size}.jpg";
}
public static function cache_remote_avatar($url, $dest) {
$dir = dirname($dest);
if (!file_exists($dir)) {
if (!wp_mkdir_p($dir)) {
error_log('WPAvatar: Failed to create cache directory: ' . $dir);
return false;
}
}
if (!is_writable($dir)) {
error_log('WPAvatar: Cache directory is not writable: ' . $dir);
return false;
}
$timeout = wpavatar_get_option('wpavatar_timeout', 5);
$response = wp_remote_get($url, [
'timeout' => $timeout,
'user-agent' => 'WPAvatar/' . WPAVATAR_VERSION . '; ' . home_url()
]);
if (is_wp_error($response)) {
error_log('WPAvatar: Error fetching avatar: ' . $response->get_error_message());
return false;
}
$response_code = wp_remote_retrieve_response_code($response);
if ($response_code !== 200) {
error_log('WPAvatar: Bad response code: ' . $response_code);
return false;
}
$image = wp_remote_retrieve_body($response);
if (empty($image)) {
error_log('WPAvatar: Empty image received');
return false;
}
$content_type = wp_remote_retrieve_header($response, 'content-type');
if (strpos($content_type, 'image/') !== 0) {
error_log('WPAvatar: Invalid content type: ' . $content_type);
return false;
}
$result = file_put_contents($dest, $image);
if ($result === false) {
error_log('WPAvatar: Failed to write cache file: ' . $dest);
return false;
}
@chmod($dest, 0644);
return true;
}
public static function cache_comment_avatar($comment_id, $comment_approved) {
if ($comment_approved !== 1 || !wpavatar_get_option('wpavatar_enable_cache', true)) {
return;
}
$comment = get_comment($comment_id);
if (!$comment || empty($comment->comment_author_email)) {
return;
}
$email = $comment->comment_author_email;
$size = wpavatar_get_option('wpavatar_shortcode_size', 96);
$avatar_url = get_avatar_url($email, ['size' => $size]);
$hash = self::get_avatar_hash($email);
if (empty($hash)) {
return;
}
$cache_file = self::get_cache_path($hash, $size);
self::cache_remote_avatar($avatar_url, $cache_file);
$retina_url = get_avatar_url($email, ['size' => $size * 2]);
$retina_cache_file = self::get_cache_path($hash, $size * 2);
self::cache_remote_avatar($retina_url, $retina_cache_file);
}
public static function cache_user_avatar($user_id) {
if (!wpavatar_get_option('wpavatar_enable_cache', true)) {
return;
}
$user = get_user_by('id', $user_id);
if (!$user) {
return;
}
$email = $user->user_email;
$size = wpavatar_get_option('wpavatar_shortcode_size', 96);
$avatar_url = get_avatar_url($email, ['size' => $size]);
$hash = self::get_avatar_hash($email);
if (empty($hash)) {
return;
}
$cache_file = self::get_cache_path($hash, $size);
self::cache_remote_avatar($avatar_url, $cache_file);
$retina_url = get_avatar_url($email, ['size' => $size * 2]);
$retina_cache_file = self::get_cache_path($hash, $size * 2);
self::cache_remote_avatar($retina_url, $retina_cache_file);
}
public static function purge_expired() {
$base_dir = wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR);
if (!file_exists($base_dir) || !is_dir($base_dir)) {
return;
}
$expire_days = wpavatar_get_option('wpavatar_cache_expire', 7);
$expire_time = time() - ($expire_days * DAY_IN_SECONDS);
// If multisite, purge site-specific directory
if (is_multisite()) {
$blog_id = get_current_blog_id();
$site_dir = trailingslashit($base_dir) . 'site-' . $blog_id . '/';
if (file_exists($site_dir) && is_dir($site_dir)) {
$files = glob($site_dir . '*.jpg');
if ($files) {
foreach ($files as $file) {
if (filemtime($file) < $expire_time) {
@unlink($file);
}
}
}
}
} else {
// For non-multisite, purge all files in the base directory
$files = glob(trailingslashit($base_dir) . '*.jpg');
if ($files) {
foreach ($files as $file) {
if (filemtime($file) < $expire_time) {
@unlink($file);
}
}
}
}
}
public static function schedule_purge() {
if (!wp_next_scheduled('wpavatar_purge_cache')) {
wp_schedule_event(time(), 'daily', 'wpavatar_purge_cache');
}
}
public static function check_cache_status() {
$base_dir = wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR);
// For multisite, check site-specific directory
if (is_multisite()) {
$blog_id = get_current_blog_id();
$site_dir = trailingslashit($base_dir) . 'site-' . $blog_id;
$stats = [
'path' => $site_dir,
'exists' => file_exists($site_dir) && is_dir($site_dir),
'writable' => is_writable($site_dir),
'file_count' => 0,
'size' => 0
];
if ($stats['exists']) {
$files = glob(trailingslashit($site_dir) . '*.jpg');
$stats['file_count'] = count($files ?: []);
foreach ($files ?: [] as $file) {
$stats['size'] += filesize($file);
}
$stats['size'] = size_format($stats['size']);
}
} else {
// For single site
$stats = [
'path' => $base_dir,
'exists' => file_exists($base_dir) && is_dir($base_dir),
'writable' => is_writable($base_dir),
'file_count' => 0,
'size' => 0
];
if ($stats['exists']) {
$files = glob(trailingslashit($base_dir) . '*.jpg');
$stats['file_count'] = count($files ?: []);
foreach ($files ?: [] as $file) {
$stats['size'] += filesize($file);
}
$stats['size'] = size_format($stats['size']);
}
}
ob_start();
?>