From 88d798a5a5c6f9f6a1ea1722365dd7f28acbb42a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E6=B4=BE=E5=A4=87=E6=A1=88?= <130886204+modiqi@users.noreply.github.com> Date: Mon, 19 May 2025 03:26:25 +0800 Subject: [PATCH] =?UTF-8?q?v1.8.1=20=E6=B7=BB=E5=8A=A0=E5=A4=9A=E7=AB=99?= =?UTF-8?q?=E7=82=B9=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/admin.js | 178 +++++- includes/admin.php | 360 +++++++----- includes/compatibility.php | 106 ++++ includes/core.php | 794 +++++++++++++++++-------- includes/multisite.php | 1134 ++++++++++++++++++++++++++++++++++++ wpavatar.php | 281 ++++++++- 6 files changed, 2449 insertions(+), 404 deletions(-) create mode 100644 includes/compatibility.php create mode 100644 includes/multisite.php diff --git a/assets/js/admin.js b/assets/js/admin.js index fda06b3..aa4ac7a 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -9,6 +9,7 @@ jQuery(document).ready(function($) { return; } + // Tab navigation $('.wpavatar-tab').on('click', function() { var tab = $(this).data('tab'); if (!tab) return; @@ -21,7 +22,11 @@ jQuery(document).ready(function($) { if (tab === 'cache' && $('#cache-stats').is(':empty')) { setTimeout(function() { - $('#check-cache').trigger('click'); + if (wpavatar.is_network_admin === '1') { + $('#check-all-cache').trigger('click'); + } else { + $('#check-cache').trigger('click'); + } }, 300); } @@ -32,6 +37,7 @@ jQuery(document).ready(function($) { } }); + // CDN options management function updateCdnOptions() { var selectedType = $('input[name="wpavatar_cdn_type"]:checked').val(); @@ -53,23 +59,32 @@ jQuery(document).ready(function($) { if (value && value.toLowerCase().indexOf('cravatar') !== -1) { forceMd5HashMethod(true); } else { - // 非Cravatar服务默认选择SHA256,但不强制 - var currentHashMethod = $('input[name="wpavatar_hash_method"]:checked').val(); - if (!currentHashMethod) { - $('input[name="wpavatar_hash_method"][value="sha256"]').prop('checked', true); - } forceMd5HashMethod(false); } } function forceMd5HashMethod(force) { if (force) { + // Save the current user-selected hash method to restore later if needed + var currentMethod = $('input[name="wpavatar_hash_method"]:checked').val(); + if (currentMethod) { + $(this).data('previous-hash-method', currentMethod); + } + + // Force MD5 and disable SHA256 option $('input[name="wpavatar_hash_method"][value="md5"]').prop('checked', true); $('input[name="wpavatar_hash_method"][value="sha256"]').prop('disabled', true); $('.hash-method-notice').show(); } else { + // Restore disabled state, but don't change selection $('input[name="wpavatar_hash_method"][value="sha256"]').prop('disabled', false); $('.hash-method-notice').hide(); + + // Restore previous selection (if any) + var previousMethod = $(this).data('previous-hash-method'); + if (previousMethod && previousMethod === 'sha256') { + $('input[name="wpavatar_hash_method"][value="sha256"]').prop('checked', true); + } } } @@ -85,6 +100,7 @@ jQuery(document).ready(function($) { checkIfCravatarRelated($(this).val()); }); + // Cache management for single site $('#check-cache').on('click', function() { var $button = $(this); var $stats = $('#cache-stats'); @@ -165,6 +181,111 @@ jQuery(document).ready(function($) { }); }); + // Cache management for network admin + if (wpavatar.is_network_admin === '1') { + $('#check-all-cache').on('click', function() { + var $button = $(this); + var $stats = $('#cache-stats'); + + $button.prop('disabled', true).text(wpavatar_l10n.checking); + $stats.html('

' + wpavatar_l10n.checking_status + '

'); + + $.ajax({ + type: 'POST', + url: wpavatar.ajaxurl, + data: { + action: 'wpavatar_check_all_cache', + nonce: wpavatar.nonce + }, + success: function(response) { + if (response.success) { + $stats.html(response.data); + } else { + $stats.html('

' + (response.data || wpavatar_l10n.check_failed) + '

'); + } + }, + error: function() { + $stats.html('

' + wpavatar_l10n.request_failed + '

'); + }, + complete: function() { + $button.prop('disabled', false).text('查看所有站点缓存'); + } + }); + }); + + $('#purge-all-cache').on('click', function() { + var $button = $(this); + var $stats = $('#cache-stats'); + var $status = $('#wpavatar-status'); + + if (!confirm(wpavatar_l10n.confirm_purge)) { + return; + } + + $button.prop('disabled', true).text(wpavatar_l10n.purging); + $stats.html('

' + wpavatar_l10n.purging_cache + '

'); + + $.ajax({ + type: 'POST', + url: wpavatar.ajaxurl, + data: { + action: 'wpavatar_purge_all_cache', + nonce: wpavatar.nonce + }, + success: function(response) { + if (response.success) { + $status.removeClass('notice-error') + .addClass('notice-success') + .text(response.data) + .show() + .delay(3000) + .fadeOut(); + + setTimeout(function() { + $('#check-all-cache').trigger('click'); + }, 1000); + } else { + $status.removeClass('notice-success') + .addClass('notice-error') + .text(response.data || wpavatar_l10n.purge_failed) + .show(); + } + }, + error: function() { + $status.removeClass('notice-success') + .addClass('notice-error') + .text(wpavatar_l10n.request_failed) + .show(); + }, + complete: function() { + $button.prop('disabled', false).text('清空所有站点缓存'); + } + }); + }); + + // Network management controls + if ($('#select-all-options').length) { + $('#select-all-options').on('click', function() { + $('input[name="wpavatar_network_controlled_options[]"]').prop('checked', true); + }); + + $('#deselect-all-options').on('click', function() { + $('input[name="wpavatar_network_controlled_options[]"]').prop('checked', false); + }); + + $('#reset-default-options').on('click', function() { + if (confirm(wpavatar_l10n.confirm_reset)) { + var defaultOptions = ['wpavatar_enable_cravatar', 'wpavatar_cdn_type', 'wpavatar_cravatar_route', 'wpavatar_third_party_mirror', 'wpavatar_custom_cdn']; + + $('input[name="wpavatar_network_controlled_options[]"]').each(function() { + $(this).prop('checked', defaultOptions.indexOf($(this).val()) !== -1); + }); + } + }); + } + } + + // Form validation $('#wpavatar-basic-form, #wpavatar-cache-form, #wpavatar-advanced-form, #wpavatar-shortcodes-form').on('submit', function(e) { var formId = $(this).attr('id'); var $status = $('#wpavatar-status'); @@ -202,6 +323,28 @@ jQuery(document).ready(function($) { return true; }); + // For network import and bulk operations + if ($('#import-site-settings').length) { + $('#import-site-settings').on('click', function(e) { + if (!confirm(wpavatar_l10n.confirm_import)) { + e.preventDefault(); + return false; + } + return true; + }); + } + + if ($('#apply-to-all-sites').length) { + $('#apply-to-all-sites').on('click', function(e) { + if (!confirm('确定要将网络设置应用到所有站点吗?此操作将覆盖每个站点的现有设置。')) { + e.preventDefault(); + return false; + } + return true; + }); + } + + // Settings saved notification if (window.location.search.indexOf('settings-updated=true') > -1) { $('#wpavatar-status') .removeClass('notice-error') @@ -212,6 +355,29 @@ jQuery(document).ready(function($) { .fadeOut(); } + // Import settings notification + if (window.location.search.indexOf('imported=true') > -1) { + $('#wpavatar-status') + .removeClass('notice-error') + .addClass('notice-success') + .text('站点设置已成功导入到网络设置。') + .show() + .delay(3000) + .fadeOut(); + } + + // Applied to all sites notification + if (window.location.search.indexOf('applied=true') > -1) { + $('#wpavatar-status') + .removeClass('notice-error') + .addClass('notice-success') + .text('网络设置已成功应用到所有站点。') + .show() + .delay(3000) + .fadeOut(); + } + + // Set active tab var currentTab = ''; if (window.location.search.indexOf('tab=') > -1) { diff --git a/includes/admin.php b/includes/admin.php index ab1a9a7..441c08c 100644 --- a/includes/admin.php +++ b/includes/admin.php @@ -81,9 +81,32 @@ class Settings { return get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); } - $index_file = $value . 'index.php'; - if (!file_exists($index_file)) { - @file_put_contents($index_file, ' wp_create_nonce('wpavatar_admin_nonce'), 'ajaxurl' => admin_url('admin-ajax.php'), - 'cache_path' => get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR), + 'cache_path' => wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR), 'plugin_url' => WPAVATAR_PLUGIN_URL, 'assets_url' => WPAVATAR_PLUGIN_URL . 'assets/', + 'is_network_admin' => '0', + 'is_multisite' => is_multisite() ? '1' : '0', ]); wp_localize_script('wpavatar-admin', 'wpavatar_l10n', [ @@ -142,6 +167,17 @@ class Settings { } public static function render_settings_page() { + // Check for network control in multisite + if (is_multisite()) { + $network_enabled = get_site_option('wpavatar_network_enabled', 1); + $network_enforce = get_site_option('wpavatar_network_enforce', 0); + $network_controlled_options = get_site_option('wpavatar_network_controlled_options', array()); + + if (!is_array($network_controlled_options)) { + $network_controlled_options = explode(',', $network_controlled_options); + } + } + $active_tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'basic'; ?>
@@ -174,24 +210,66 @@ class Settings {

+ +
+

+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + + + +

+
+ +
@@ -218,7 +296,7 @@ class Settings { > > > @@ -252,21 +330,21 @@ class Settings { @@ -283,6 +361,26 @@ class Settings {

+ +
+

+ +
+ + +
+ + +
+ + + + + +

+
+ +

@@ -296,16 +394,22 @@ class Settings {
@@ -202,15 +280,15 @@ class Settings {

- > @@ -230,7 +308,7 @@ class Settings {
- > @@ -244,7 +322,7 @@ class Settings {
- + >


-

+

- + >

@@ -339,21 +447,47 @@ class Settings {

+ +
+

+ +
+ + +
+ + +
+ + + + + +

+
+ +
@@ -315,14 +419,18 @@ class Settings {
- + > + +

+

+
- + >

@@ -361,7 +495,7 @@ class Settings { +
- + >

@@ -374,7 +508,7 @@ class Settings {
$avatar) : ?> @@ -388,7 +522,7 @@ class Settings { : 'https://wpcy.com'; printf( - __('选择您喜欢的故障备用头像,如需智能线路切换,请使用%s。', 'wpavatar'), + __('选择您的故障备用头像,如需智能线路切换,请使用%s。', 'wpavatar'), sprintf( '%s', esc_url($wpcy_link), @@ -397,7 +531,7 @@ class Settings { ); ?>

-
@@ -407,11 +541,30 @@ class Settings {
-

+ +
+

+ +
+ + +
+ + +
+ + + + + +

+
+ +

@@ -447,29 +600,35 @@ class Settings {
- + >

- + >

- > @@ -603,106 +762,23 @@ class Settings { font-size: 12px; line-height: 1.3; } + + .wpavatar-network-notice { + margin: 0 0 20px; + padding: 10px 12px; + background: #f0f6fc; + border-left: 4px solid #72aee6; + } + .wpavatar-network-notice .dashicons-lock { + color: #72aee6; + margin-right: 5px; + } + .wpavatar-network-notice em { + display: block; + margin-top: 5px; + color: #666; + } -
-

- - - -
-

-

- -
- - - - - - - - - - - - - - - - - - -
- -

-
- -
- -
-

-
-
- -
-
-
-
- 'wpavatar-network', 'updated' => 'true'], network_admin_url('settings.php'))); - exit; - } -} diff --git a/includes/compatibility.php b/includes/compatibility.php new file mode 100644 index 0000000..5f10247 --- /dev/null +++ b/includes/compatibility.php @@ -0,0 +1,106 @@ +callbacks as $priority => $callbacks) { + foreach ($callbacks as $callback_key => $callback_data) { + if (is_array($callback_data['function']) && + is_object($callback_data['function'][0]) && + get_class($callback_data['function'][0]) === 'WenPai\\ChinaYes\\Service\\Super') { + + $method_name = $callback_data['function'][1]; + remove_filter($filter_name, [$callback_data['function'][0], $method_name], $priority); + } + } + } + } + } + } + + /** + * 重新初始化 WPAvatar 的 Cravatar 过滤器,使用更高的优先级 + */ + private static function reinitialize_wpavatar_filters() { + if (wpavatar_get_option('wpavatar_enable_cravatar', true)) { + // 使用高优先级再次添加过滤器 + add_filter('um_user_avatar_url_filter', ['\WPAvatar\Cravatar', 'replace_avatar_url'], 9999); + add_filter('bp_gravatar_url', ['\WPAvatar\Cravatar', 'replace_avatar_url'], 9999); + add_filter('user_profile_picture_description', ['\WPAvatar\Cravatar', 'modify_profile_picture_description'], 9999); + + // 确保 get_avatar_url 过滤器的优先级高于其他插件 + remove_filter('get_avatar_url', ['\WPAvatar\Cravatar', 'get_avatar_url'], 999); + add_filter('get_avatar_url', ['\WPAvatar\Cravatar', 'get_avatar_url'], 9999, 2); + } + } + + /** + * 管理界面兼容性通知 + */ + public static function admin_compatibility_notice() { + $screen = get_current_screen(); + if ($screen && $screen->id === 'settings_page_wpavatar-settings') { + echo '
'; + echo '

检测到文派叶子(WPCY.COM)插件,WPAvatar 生态组件兼容性补丁已生效,确保文派头像设置优先。

'; + echo '
'; + } + } +} diff --git a/includes/core.php b/includes/core.php index a71b5dd..3a949e2 100644 --- a/includes/core.php +++ b/includes/core.php @@ -19,12 +19,17 @@ class Cravatar { ]; public static function init() { - if (get_option('wpavatar_enable_cravatar', true)) { - add_filter('get_avatar_url', [__CLASS__, 'replace_avatar_url'], 1); + if (wpavatar_get_option('wpavatar_enable_cravatar', true)) { + // 使用高优先级过滤器来预处理头像数据 + add_filter('pre_get_avatar_data', [__CLASS__, 'pre_get_avatar_data'], 1, 2); + + // 直接过滤avatar_url,无论是否已经通过pre_get_avatar_data处理 + add_filter('get_avatar_url', [__CLASS__, 'get_avatar_url'], 999, 2); + + // 其他过滤器 add_filter('um_user_avatar_url_filter', [__CLASS__, 'replace_avatar_url'], 1); add_filter('bp_gravatar_url', [__CLASS__, 'replace_avatar_url'], 1); add_filter('user_profile_picture_description', [__CLASS__, 'modify_profile_picture_description'], 1); - add_filter('pre_get_avatar_data', [__CLASS__, 'pre_get_avatar_data'], 9, 2); add_filter('get_avatar', [__CLASS__, 'add_seo_alt'], 10, 5); } } @@ -74,12 +79,144 @@ class Cravatar { 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) { @@ -90,89 +227,58 @@ class Cravatar { $email = $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); + 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 (isset($id_or_email->comment_author_email)) { - $email = $id_or_email->comment_author_email; - } elseif (isset($id_or_email->user_email)) { - $email = $id_or_email->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; + } + } } } - if (empty($email)) { - return $args; - } - - // 确定使用的哈希方法 - $cdn_type = get_option('wpavatar_cdn_type', 'cravatar_route'); - $use_md5 = true; - - // Cravatar只支持MD5,如果使用Cravatar相关服务,强制使用MD5 - if ($cdn_type !== 'cravatar_route') { - // 检查第三方镜像或自定义CDN是否与Cravatar相关 - $third_party_mirror = get_option('wpavatar_third_party_mirror', ''); - $custom_cdn = get_option('wpavatar_custom_cdn', ''); - $is_cravatar_related = ( - strpos(strtolower($third_party_mirror), 'cravatar') !== false || - strpos(strtolower($custom_cdn), 'cravatar') !== false - ); - - if (!$is_cravatar_related) { - // 非Cravatar服务使用用户设置的哈希方法 - $hash_method = get_option('wpavatar_hash_method', 'sha256'); - $use_md5 = ($hash_method === 'md5'); - } - } - - // 检查WordPress版本,决定是否支持SHA256 - $wp_version = get_bloginfo('version'); - $use_sha256_support = version_compare($wp_version, '6.8', '>='); - - // 设置哈希方法 - if (!$use_md5 && $use_sha256_support) { - $args['hash_method'] = 'sha256'; - $args['hash'] = 'sha256'; - } else { - $args['hash_method'] = 'md5'; - - if (isset($args['hash']) && $args['hash'] === 'sha256') { - unset($args['hash']); - } - } - - // 计算邮箱地址的哈希值 - if (!isset($args['wpavatar_email_hash'])) { - if (!$use_md5) { - $args['wpavatar_email_hash'] = hash('sha256', strtolower(trim($email))); - } else { - $args['wpavatar_email_hash'] = md5(strtolower(trim($email))); - } - } - - // 设置超时属性 - $timeout = 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; + return $email; } - + public static function replace_avatar_url($url) { - // 遍历所有Gravatar域名,替换为Cravatar相关域名 + // 移除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 = get_option('wpavatar_cdn_type', 'cravatar_route'); + $cdn_type = wpavatar_get_option('wpavatar_cdn_type', 'cravatar_route'); $cdn_domain = ''; if ($cdn_type === 'cravatar_route') { - $cdn_domain = get_option('wpavatar_cravatar_route', 'cravatar.com'); + $cdn_domain = wpavatar_get_option('wpavatar_cravatar_route', 'cravatar.com'); } elseif ($cdn_type === 'third_party') { - $cdn_domain = get_option('wpavatar_third_party_mirror', 'weavatar.com'); + $cdn_domain = wpavatar_get_option('wpavatar_third_party_mirror', 'weavatar.com'); } elseif ($cdn_type === 'custom') { - $custom_cdn = get_option('wpavatar_custom_cdn', ''); + $custom_cdn = wpavatar_get_option('wpavatar_custom_cdn', ''); if (!empty($custom_cdn)) { $cdn_domain = $custom_cdn; } else { @@ -189,7 +295,7 @@ class Cravatar { public static function add_seo_alt($avatar, $id_or_email, $size, $default, $alt) { // 添加SEO友好的alt属性 - $seo_alt = get_option('wpavatar_seo_alt'); + $seo_alt = wpavatar_get_option('wpavatar_seo_alt'); if (!empty($seo_alt)) { $user = false; if (is_numeric($id_or_email)) { @@ -211,8 +317,8 @@ class Cravatar { } // 添加头像加载失败的备用显示 - if (get_option('wpavatar_fallback_mode', 1)) { - $fallback_type = get_option('wpavatar_fallback_avatar', 'default'); + 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])) { @@ -233,7 +339,7 @@ class Cache { add_action('init', [__CLASS__, 'setup_cache_dir']); add_action('init', [__CLASS__, 'schedule_purge']); - if (get_option('wpavatar_enable_cache', true)) { + if (wpavatar_get_option('wpavatar_enable_cache', true)) { add_filter('get_avatar_url', [__CLASS__, 'prepare_cache_url'], 99, 2); add_filter('get_avatar', [__CLASS__, 'serve_cached_avatar'], 20, 5); } @@ -245,12 +351,11 @@ class Cache { } public static function setup_cache_dir() { - $dir = get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); + $base_dir = wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); + $base_dir = rtrim($base_dir, '/\\') . '/'; - $dir = rtrim($dir, '/\\') . '/'; - - if (!file_exists($dir)) { - if (!wp_mkdir_p($dir)) { + if (!file_exists($base_dir)) { + if (!wp_mkdir_p($base_dir)) { add_settings_error( 'wpavatar_cache', 'cache_dir_error', @@ -261,7 +366,7 @@ class Cache { } } - if (!is_writable($dir)) { + if (!is_writable($base_dir)) { add_settings_error( 'wpavatar_cache', 'cache_dir_writable', @@ -271,12 +376,14 @@ class Cache { return false; } - $index_file = $dir . 'index.php'; + // Create index.php in base dir + $index_file = $base_dir . 'index.php'; if (!file_exists($index_file)) { @file_put_contents($index_file, ' (time() - $cache_expire)) { $cached_url = content_url(str_replace(WP_CONTENT_DIR, '', $cache_file)); return str_replace($url, esc_url($cached_url), $avatar); @@ -370,46 +510,51 @@ class Cache { } public static function get_avatar_hash($id_or_email) { - $email = ''; - - if (is_object($id_or_email)) { - if (isset($id_or_email->comment_author_email)) { - $email = $id_or_email->comment_author_email; - } elseif (isset($id_or_email->user_email)) { - $email = $id_or_email->user_email; - } - } elseif (is_numeric($id_or_email)) { - $user = get_user_by('id', $id_or_email); - $email = $user ? $user->user_email : ''; - } elseif (is_string($id_or_email) && is_email($id_or_email)) { - $email = $id_or_email; - } + $email = Cravatar::get_email_from_id_or_email($id_or_email); if (empty($email)) { return ''; } - $cdn_type = get_option('wpavatar_cdn_type', 'cravatar_route'); - $use_md5 = true; + // 获取用户设置的哈希方法 + $cdn_type = wpavatar_get_option('wpavatar_cdn_type', 'cravatar_route'); + $hash_method = wpavatar_get_option('wpavatar_hash_method', 'md5'); - // 确定使用的哈希方法 - if ($cdn_type !== 'cravatar_route' && - strpos(get_option('wpavatar_third_party_mirror', ''), 'cravatar') === false && - strpos(get_option('wpavatar_custom_cdn', ''), 'cravatar') === false) { - $hash_method = get_option('wpavatar_hash_method', 'md5'); - $use_md5 = ($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 (!$use_md5) { - return hash('sha256', strtolower(trim($email))); - } else { + // 根据设置和条件选择哈希方法 + 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 = get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); + $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"; } @@ -428,7 +573,7 @@ class Cache { return false; } - $timeout = get_option('wpavatar_timeout', 5); + $timeout = wpavatar_get_option('wpavatar_timeout', 5); $response = wp_remote_get($url, [ 'timeout' => $timeout, @@ -470,7 +615,7 @@ class Cache { } public static function cache_comment_avatar($comment_id, $comment_approved) { - if ($comment_approved !== 1 || !get_option('wpavatar_enable_cache', true)) { + if ($comment_approved !== 1 || !wpavatar_get_option('wpavatar_enable_cache', true)) { return; } @@ -480,7 +625,7 @@ class Cache { } $email = $comment->comment_author_email; - $size = get_option('wpavatar_shortcode_size', 96); + $size = wpavatar_get_option('wpavatar_shortcode_size', 96); $avatar_url = get_avatar_url($email, ['size' => $size]); @@ -493,14 +638,13 @@ class Cache { self::cache_remote_avatar($avatar_url, $cache_file); - // 缓存2x尺寸的头像,用于高分辨率显示器 $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 (!get_option('wpavatar_enable_cache', true)) { + if (!wpavatar_get_option('wpavatar_enable_cache', true)) { return; } @@ -510,7 +654,7 @@ class Cache { } $email = $user->user_email; - $size = get_option('wpavatar_shortcode_size', 96); + $size = wpavatar_get_option('wpavatar_shortcode_size', 96); $avatar_url = get_avatar_url($email, ['size' => $size]); @@ -523,29 +667,44 @@ class Cache { self::cache_remote_avatar($avatar_url, $cache_file); - // 缓存2x尺寸的头像,用于高分辨率显示器 $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() { - $dir = get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); - if (!file_exists($dir) || !is_dir($dir)) { + $base_dir = wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); + if (!file_exists($base_dir) || !is_dir($base_dir)) { return; } - $files = glob(trailingslashit($dir) . '*.jpg'); - if (!$files) { - return; - } - - $expire_days = get_option('wpavatar_cache_expire', 7); + $expire_days = wpavatar_get_option('wpavatar_cache_expire', 7); $expire_time = time() - ($expire_days * DAY_IN_SECONDS); - foreach ($files as $file) { - if (filemtime($file) < $expire_time) { - @unlink($file); + // 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); + } + } } } } @@ -557,134 +716,273 @@ class Cache { } public static function check_cache_status() { - $dir = get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); - $stats = [ - 'path' => $dir, - 'exists' => file_exists($dir) && is_dir($dir), - 'writable' => is_writable($dir), - 'file_count' => 0, - 'size' => 0 - ]; + $base_dir = wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); - if ($stats['exists']) { - $files = glob(trailingslashit($dir) . '*.jpg'); - $stats['file_count'] = count($files ?: []); - foreach ($files ?: [] as $file) { - $stats['size'] += filesize($file); - } - $stats['size'] = size_format($stats['size']); - } + // For multisite, check site-specific directory + if (is_multisite()) { + $blog_id = get_current_blog_id(); + $site_dir = trailingslashit($base_dir) . 'site-' . $blog_id; - ob_start(); - ?> -
-

-

-

-

-

-
- $site_dir, + 'exists' => file_exists($site_dir) && is_dir($site_dir), + 'writable' => is_writable($site_dir), + 'file_count' => 0, + 'size' => 0 + ]; -class Shortcode { - public static function init() { - add_shortcode('wpavatar', [__CLASS__, 'render_avatar']); - add_shortcode('wpavatar_username', [__CLASS__, 'render_username']); - add_filter('walker_nav_menu_start_el', [__CLASS__, 'menu_item_replace'], 10, 4); - } + 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 + ]; - public static function render_avatar($atts) { - $default_size = get_option('wpavatar_shortcode_size', 96); - $default_class = get_option('wpavatar_shortcode_class', 'wpavatar'); - $default_shape = get_option('wpavatar_shortcode_shape', 'square'); + 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']); + } + } - $atts = shortcode_atts([ - 'size' => $default_size, - 'user_id' => get_current_user_id(), - 'class' => $default_class, - 'email' => '', - 'shape' => $default_shape, - 'title' => '' - ], $atts); + 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 + ]; - 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 = ''; - } + $site_stats = []; - $avatar_args = [ - 'class' => implode(' ', $classes), - 'size' => intval($atts['size']), - 'extra_attr' => $style - ]; + if ($global_stats['exists']) { + // Find all site directories + $site_dirs = glob(trailingslashit($base_dir) . 'site-*', GLOB_ONLYDIR); + $global_stats['site_count'] = count($site_dirs ?: []); - if (!empty($atts['title'])) { - $avatar_args['extra_attr'] .= ' title="' . esc_attr($atts['title']) . '"'; - } + // 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; - 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(); - } + $size = 0; + foreach ($files ?: [] as $file) { + $size += filesize($file); + $global_stats['total_size'] += filesize($file); + } - public static function render_username($atts) { - $atts = shortcode_atts([ - 'user_id' => get_current_user_id(), - 'before' => '', - 'after' => '' - ], $atts); + // 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; + } + } - if (!empty($atts['user_id'])) { - $user = get_user_by('id', $atts['user_id']); - } else { - $user = wp_get_current_user(); - } + $site_stats[] = [ + 'id' => $blog_id, + 'name' => $blog_name ?: sprintf(__('站点 #%d', 'wpavatar'), $blog_id), + 'files' => $file_count, + 'size' => size_format($size) + ]; + } + } - $username = $user && $user->display_name ? $user->display_name : __('匿名用户', 'wpavatar'); - return $atts['before'] . $username . $atts['after']; - } + // 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; - 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; - } + $legacy_size = 0; + foreach ($legacy_files ?: [] as $file) { + $legacy_size += filesize($file); + $global_stats['total_size'] += filesize($file); + } - public static function generate_preview($user_id = 0, $shape = 'square', $size = 96) { - if (!$user_id) { - $user_id = get_current_user_id(); - } + $site_stats[] = [ + 'id' => 0, + 'name' => __('旧版缓存文件(非站点特定)', 'wpavatar'), + 'files' => $legacy_count, + 'size' => size_format($legacy_size) + ]; + } + } - $atts = [ - 'user_id' => $user_id, - 'shape' => $shape, - 'size' => $size, - 'class' => 'wpavatar-preview' - ]; + // Sort sites by file count (descending) + usort($site_stats, function($a, $b) { + return $b['files'] - $a['files']; + }); - return self::render_avatar($atts); - } -} + 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(); + } + + $username = $user && $user->display_name ? $user->display_name : __('匿名用户', 'wpavatar'); + return $atts['before'] . $username . $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); + } + } diff --git a/includes/multisite.php b/includes/multisite.php new file mode 100644 index 0000000..ffbc2b8 --- /dev/null +++ b/includes/multisite.php @@ -0,0 +1,1134 @@ + wp_create_nonce('wpavatar_admin_nonce'), + 'ajaxurl' => admin_url('admin-ajax.php'), + 'cache_path' => get_site_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR), + 'plugin_url' => WPAVATAR_PLUGIN_URL, + 'assets_url' => WPAVATAR_PLUGIN_URL . 'assets/', + 'is_network_admin' => is_network_admin() ? '1' : '0', + ]); + + wp_localize_script('wpavatar-admin', 'wpavatar_l10n', [ + 'checking' => __('检查中...', 'wpavatar'), + 'checking_status' => __('正在检查缓存状态...', 'wpavatar'), + 'check_failed' => __('检查失败,请重试', 'wpavatar'), + 'request_failed' => __('请求失败,请检查网络连接', 'wpavatar'), + 'check_cache' => __('检查缓存状态', 'wpavatar'), + 'confirm_purge' => __('确定要清空所有缓存头像吗?此操作无法撤销。', 'wpavatar'), + 'purging' => __('清空中...', 'wpavatar'), + 'purging_cache' => __('正在清空缓存...', 'wpavatar'), + 'purge_failed' => __('清空失败,请重试', 'wpavatar'), + 'purge_cache' => __('清空缓存', 'wpavatar'), + 'enter_custom_cdn' => __('请输入自定义CDN域名', 'wpavatar'), + 'enter_cache_path' => __('请输入缓存目录路径', 'wpavatar'), + 'settings_saved' => __('设置已成功保存。', 'wpavatar'), + 'confirm_import' => __('确定要从所选站点导入设置吗?此操作将覆盖当前的网络设置。', 'wpavatar'), + 'confirm_reset' => __('确定要重置所有控制选项吗?该操作会将所有站点恢复为各自的设置。', 'wpavatar') + ]); + } + + /** + * Add network menu + */ + public static function add_network_menu() { + add_submenu_page( + 'settings.php', + __('WPAvatar网络设置', 'wpavatar'), + __('WPAvatar', 'wpavatar'), + 'manage_network_options', + 'wpavatar-network', + [__CLASS__, 'render_network_page'] + ); + } + + /** + * Add network action links + */ + public static function add_network_action_links($links) { + $settings_link = '' . __('网络设置', 'wpavatar') . ''; + array_unshift($links, $settings_link); + return $links; + } + + /** + * Display network managed notice on site-level settings + */ + public static function network_managed_notice() { + $screen = get_current_screen(); + if (!$screen || $screen->id !== 'settings_page_wpavatar-settings') { + return; + } + + $network_controlled_options = get_site_option('wpavatar_network_controlled_options', self::$default_controlled_options); + if (!is_array($network_controlled_options)) { + $network_controlled_options = explode(',', $network_controlled_options); + } + + $controlled_count = count($network_controlled_options); + $option_count = 15; // Approximate total number of WPAvatar options + + if (get_site_option('wpavatar_network_enforce', 0)) { + echo '

' . + __('WPAvatar 插件正由网络管理员强制管理。所有设置将使用网络级别配置,任何更改将被忽略。如有疑问请联系网络管理员。', 'wpavatar') . + '

'; + } else if ($controlled_count > 0) { + echo '

' . + sprintf( + __('WPAvatar 插件的 %d 项设置由网络管理员控制。这些设置的更改将不会生效。', 'wpavatar'), + $controlled_count + ) . + '

'; + } + } + + /** + * Maybe remove site menu if network settings enforced + */ + public static function maybe_remove_site_menu() { + // If network settings completely override site settings + if (get_site_option('wpavatar_network_enforce', 0)) { + remove_submenu_page('options-general.php', 'wpavatar-settings'); + } + } + + /** + * Render network settings page + */ + public static function render_network_page() { + if (!current_user_can('manage_network_options')) { + wp_die(__('您没有足够权限访问此页面。', 'wpavatar')); + } + + // Display update message if settings were just updated + if (isset($_GET['updated']) && $_GET['updated'] == 'true') { + echo '

' . + __('网络设置已保存。', 'wpavatar') . + '

'; + } + + $active_tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'network'; + + ?> +
+

+ + + +

+ + + + +
+
+
+ + + + + + +
+
+ + +
+

+

+ +
+ + + + + + + + + + + + + + + + +
+ +

+
+ +

+
+ __('启用初认头像', 'wpavatar'), + 'wpavatar_cdn_type' => __('线路选择', 'wpavatar'), + 'wpavatar_cravatar_route' => __('Cravatar官方源', 'wpavatar'), + 'wpavatar_third_party_mirror' => __('第三方镜像', 'wpavatar'), + 'wpavatar_custom_cdn' => __('自定义CDN', 'wpavatar'), + 'wpavatar_hash_method' => __('头像哈希方法', 'wpavatar'), + 'wpavatar_timeout' => __('超时设置', 'wpavatar'), + 'wpavatar_enable_cache' => __('启用本地缓存', 'wpavatar'), + 'wpavatar_cache_path' => __('缓存目录', 'wpavatar'), + 'wpavatar_cache_expire' => __('缓存过期时间', 'wpavatar'), + 'wpavatar_seo_alt' => __('SEO替代文本', 'wpavatar'), + 'wpavatar_fallback_mode' => __('头像加载失败处理', 'wpavatar'), + 'wpavatar_fallback_avatar' => __('备用头像选择', 'wpavatar'), + 'wpavatar_shortcode_size' => __('默认头像大小', 'wpavatar'), + 'wpavatar_shortcode_class' => __('默认CSS类名', 'wpavatar'), + 'wpavatar_shortcode_shape' => __('默认头像形状', 'wpavatar'), + ]; + + // Group options by category + $option_groups = [ + 'basic' => [ + 'title' => __('基础设置', 'wpavatar'), + 'options' => [ + 'wpavatar_enable_cravatar', + 'wpavatar_cdn_type', + 'wpavatar_cravatar_route', + 'wpavatar_third_party_mirror', + 'wpavatar_custom_cdn', + 'wpavatar_hash_method', + 'wpavatar_timeout' + ] + ], + 'cache' => [ + 'title' => __('缓存控制', 'wpavatar'), + 'options' => [ + 'wpavatar_enable_cache', + 'wpavatar_cache_path', + 'wpavatar_cache_expire' + ] + ], + 'advanced' => [ + 'title' => __('高级设置', 'wpavatar'), + 'options' => [ + 'wpavatar_seo_alt', + 'wpavatar_fallback_mode', + 'wpavatar_fallback_avatar' + ] + ], + 'shortcodes' => [ + 'title' => __('头像简码', 'wpavatar'), + 'options' => [ + 'wpavatar_shortcode_size', + 'wpavatar_shortcode_class', + 'wpavatar_shortcode_shape' + ] + ] + ]; + + // Output options by group + echo '
'; + foreach ($option_groups as $group => $group_data) { + echo '
'; + echo '

' . esc_html($group_data['title']) . '

'; + + foreach ($group_data['options'] as $option) { + echo '
'; + } + + echo '
'; + } + echo '
'; + ?> +

+
+ + + +
+
+ +
+ +
+
+
+ + +
+

+

+ +
+ + + + + + + + + + + + + > + + + + > + + + + > + + + + + + + + + + + +
+ +

+
+
+
+ +
+ +

+
+ +

+
+ +

+
+
+ +

+

+
+ + +

+
+ +
+ +
+
+
+ + +
+

+

+ +
+

+
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + +
+ +

+
+ +

+
+ + +

+
+ +
+ +
+
+
+ + +
+

+

+ +
+ + + + + + + + + + + + + + + + +
+ +

+
+ +

+
+
+ $avatar) : + ?> + + +
+

+
+ +
+ +
+
+
+ + +
+

+

+ +
+

+
+
+

+ +
+
+

+ +
+
+

+ +
+
+

+
+ +
+ + + + + + + + + + + + + + + + +
+ +

+
+ +

+
+ +

+
+ +
+ +
+
+
+ + +
+

+

+ +
+

+

+ +
+ + + + +
+ +
+
+
+ +
+

+

+ +
+ + +

+

+ +
+ +
+
+
+
+
+
+ + + + 'wpavatar-network', + 'updated' => 'true' + ], network_admin_url('settings.php')); + + // Add the tab if it exists + if (!empty($current_tab)) { + $redirect_url = add_query_arg('tab', $current_tab, $redirect_url); + } + + wp_redirect($redirect_url); + exit; + } + + /** + * Import settings from a site to network settings + */ + public static function import_site_settings() { + if (!isset($_POST['import_blog_id']) || !current_user_can('manage_network_options')) { + wp_die(__('无效的请求或权限不足。', 'wpavatar')); + return; + } + + check_admin_referer('wpavatar_import_site_settings'); + + $blog_id = intval($_POST['import_blog_id']); + if ($blog_id <= 0) { + wp_die(__('无效的站点ID。', 'wpavatar')); + return; + } + + // Switch to the selected blog to get its settings + switch_to_blog($blog_id); + + // List of all options we want to import + $options_to_import = [ + 'wpavatar_enable_cravatar', + 'wpavatar_cdn_type', + 'wpavatar_cravatar_route', + 'wpavatar_third_party_mirror', + 'wpavatar_custom_cdn', + 'wpavatar_hash_method', + 'wpavatar_timeout', + 'wpavatar_enable_cache', + 'wpavatar_cache_path', + 'wpavatar_cache_expire', + 'wpavatar_seo_alt', + 'wpavatar_fallback_mode', + 'wpavatar_fallback_avatar', + 'wpavatar_shortcode_size', + 'wpavatar_shortcode_class', + 'wpavatar_shortcode_shape' + ]; + + // Copy options from site to network + foreach ($options_to_import as $option) { + $value = get_option($option); + if ($value !== false) { + update_site_option($option, $value); + } + } + + restore_current_blog(); + + // Redirect back with success message + wp_redirect(add_query_arg([ + 'page' => 'wpavatar-network', + 'tab' => 'tools', + 'imported' => 'true' + ], network_admin_url('settings.php'))); + + exit; + } + + /** + * Apply network settings to all sites + */ + public static function apply_to_all_sites() { + if (!current_user_can('manage_network_options')) { + wp_die(__('权限不足。', 'wpavatar')); + return; + } + + check_admin_referer('wpavatar_apply_to_all_sites'); + + // Get list of all sites + $sites = get_sites(array('number' => 500)); // Limit to 500 sites for performance + + // Get network-controlled options + $controlled_options = get_site_option('wpavatar_network_controlled_options', self::$default_controlled_options); + if (!is_array($controlled_options)) { + $controlled_options = explode(',', $controlled_options); + } + + // Apply settings to each site + foreach ($sites as $site) { + switch_to_blog($site->blog_id); + + // Apply all options in the controlled list + foreach ($controlled_options as $option) { + $network_value = get_site_option($option); + if ($network_value !== false) { + update_option($option, $network_value); + } + } + + // Ensure site cache directory exists + $cache_base = get_site_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); + $site_cache_dir = trailingslashit($cache_base) . 'site-' . $site->blog_id; + + if (!file_exists($site_cache_dir)) { + wp_mkdir_p($site_cache_dir); + + // Create index.php + $index_file = $site_cache_dir . '/index.php'; + if (!file_exists($index_file)) { + @file_put_contents($index_file, ' 'wpavatar-network', + 'tab' => 'tools', + 'applied' => 'true' + ], network_admin_url('settings.php'))); + + exit; + } +} + +// Add actions for tools +add_action('network_admin_edit_wpavatar_import_site_settings', ['\WPAvatar\Network', 'import_site_settings']); +add_action('network_admin_edit_wpavatar_apply_to_all_sites', ['\WPAvatar\Network', 'apply_to_all_sites']); diff --git a/wpavatar.php b/wpavatar.php index d495577..819a6d6 100644 --- a/wpavatar.php +++ b/wpavatar.php @@ -1,22 +1,24 @@ callbacks as $priority => $callbacks) { + foreach ($callbacks as $callback_key => $callback_data) { + if (is_array($callback_data['function']) && + is_object($callback_data['function'][0]) && + get_class($callback_data['function'][0]) === 'WenPai\\ChinaYes\\Service\\Super') { + + $method_name = $callback_data['function'][1]; + remove_filter($filter_name, [$callback_data['function'][0], $method_name], $priority); + } + } + } + } + } + + // 重新添加 WPAvatar 的过滤器,使用更高的优先级 + if (wpavatar_get_option('wpavatar_enable_cravatar', true)) { + // 移除原有优先级的过滤器 + remove_filter('get_avatar_url', ['WPAvatar\\Cravatar', 'get_avatar_url'], 999); + remove_filter('um_user_avatar_url_filter', ['WPAvatar\\Cravatar', 'replace_avatar_url'], 1); + remove_filter('bp_gravatar_url', ['WPAvatar\\Cravatar', 'replace_avatar_url'], 1); + + // 使用更高优先级重新添加 + add_filter('get_avatar_url', ['WPAvatar\\Cravatar', 'get_avatar_url'], 9999, 2); + add_filter('um_user_avatar_url_filter', ['WPAvatar\\Cravatar', 'replace_avatar_url'], 9999); + add_filter('bp_gravatar_url', ['WPAvatar\\Cravatar', 'replace_avatar_url'], 9999); + add_filter('user_profile_picture_description', ['WPAvatar\\Cravatar', 'modify_profile_picture_description'], 9999); + } + + // 添加管理界面通知 + add_action('admin_notices', function() { + $screen = get_current_screen(); + if ($screen && $screen->id === 'settings_page_wpavatar-settings') { + echo '
'; + echo '

检测到文派叶子(WPCY.COM)插件,WPAvatar 生态组件兼容性补丁已生效,确保文派头像设置优先。

'; + echo '
'; + } + }); + } + }, 5); + } +});