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(); ?>

$base_dir, 'exists' => file_exists($base_dir) && is_dir($base_dir), 'writable' => is_writable($base_dir), 'site_count' => 0, 'total_files' => 0, 'total_size' => 0 ]; $site_stats = []; if ($global_stats['exists']) { // Find all site directories $site_dirs = glob(trailingslashit($base_dir) . 'site-*', GLOB_ONLYDIR); $global_stats['site_count'] = count($site_dirs ?: []); // Check each site directory foreach ($site_dirs ?: [] as $site_dir) { $blog_id = intval(str_replace(trailingslashit($base_dir) . 'site-', '', $site_dir)); if ($blog_id > 0) { $files = glob($site_dir . '/*.jpg'); $file_count = count($files ?: []); $global_stats['total_files'] += $file_count; $size = 0; foreach ($files ?: [] as $file) { $size += filesize($file); $global_stats['total_size'] += filesize($file); } // Try to get blog name $blog_name = ''; if (function_exists('get_blog_details')) { $blog_details = get_blog_details($blog_id); if ($blog_details) { $blog_name = $blog_details->blogname; } } $site_stats[] = [ 'id' => $blog_id, 'name' => $blog_name ?: sprintf(__('站点 #%d', 'wpavatar'), $blog_id), 'files' => $file_count, 'size' => size_format($size) ]; } } // Also check for legacy non-site specific files $legacy_files = glob(trailingslashit($base_dir) . '*.jpg'); $legacy_count = count($legacy_files ?: []); if ($legacy_count > 0) { $global_stats['total_files'] += $legacy_count; $legacy_size = 0; foreach ($legacy_files ?: [] as $file) { $legacy_size += filesize($file); $global_stats['total_size'] += filesize($file); } $site_stats[] = [ 'id' => 0, 'name' => __('旧版缓存文件(非站点特定)', 'wpavatar'), 'files' => $legacy_count, 'size' => size_format($legacy_size) ]; } } // Sort sites by file count (descending) usort($site_stats, function($a, $b) { return $b['files'] - $a['files']; }); ob_start(); ?>

$default_size, 'user_id' => get_current_user_id(), 'class' => $default_class, 'email' => '', 'shape' => $default_shape, 'title' => '' ], $atts); if (empty($atts['user_id']) && empty($atts['email'])) { $atts['user_id'] = get_current_user_id(); } $classes = [$atts['class']]; if ($atts['shape'] === 'circle') { $classes[] = 'avatar-circle'; $style = 'style="border-radius: 50%; overflow: hidden;"'; } elseif ($atts['shape'] === 'rounded') { $classes[] = 'avatar-rounded'; $style = 'style="border-radius: 8px; overflow: hidden;"'; } else { $classes[] = 'avatar-square'; $style = ''; } $avatar_args = [ 'class' => implode(' ', $classes), 'size' => intval($atts['size']), 'extra_attr' => $style ]; if (!empty($atts['title'])) { $avatar_args['extra_attr'] .= ' title="' . esc_attr($atts['title']) . '"'; } ob_start(); if (!empty($atts['email'])) { echo get_avatar($atts['email'], intval($atts['size']), 'default', '', $avatar_args); } else { echo get_avatar($atts['user_id'], intval($atts['size']), 'default', '', $avatar_args); } return ob_get_clean(); } public static function render_username($atts) { $atts = shortcode_atts([ 'user_id' => get_current_user_id(), 'before' => '', 'after' => '' ], $atts); if (!empty($atts['user_id'])) { $user = get_user_by('id', $atts['user_id']); } else { $user = wp_get_current_user(); } // 检查用户是否已登录且有显示名称,如果没有则返回空字符串 if (!$user || !$user->ID || empty($user->display_name)) { return ''; } return $atts['before'] . $user->display_name . $atts['after']; } public static function menu_item_replace($item_output, $item, $depth, $args) { if (strpos($item_output, '{wpavatar}') !== false) { $item_output = str_replace('{wpavatar}', do_shortcode('[wpavatar shape="circle" size="32"]'), $item_output); } if (strpos($item_output, '{wpavatar_username}') !== false) { $item_output = str_replace('{wpavatar_username}', do_shortcode('[wpavatar_username]'), $item_output); } return $item_output; } public static function generate_preview($user_id = 0, $shape = 'square', $size = 96) { if (!$user_id) { $user_id = get_current_user_id(); } $atts = [ 'user_id' => $user_id, 'shape' => $shape, 'size' => $size, 'class' => 'wpavatar-preview' ]; return self::render_avatar($atts); } }