v1.8.1 添加多站点支持

This commit is contained in:
文派备案 2025-05-19 03:26:25 +08:00
parent 31a0254299
commit 88d798a5a5
6 changed files with 2449 additions and 404 deletions

View file

@ -9,6 +9,7 @@ jQuery(document).ready(function($) {
return; return;
} }
// Tab navigation
$('.wpavatar-tab').on('click', function() { $('.wpavatar-tab').on('click', function() {
var tab = $(this).data('tab'); var tab = $(this).data('tab');
if (!tab) return; if (!tab) return;
@ -21,7 +22,11 @@ jQuery(document).ready(function($) {
if (tab === 'cache' && $('#cache-stats').is(':empty')) { if (tab === 'cache' && $('#cache-stats').is(':empty')) {
setTimeout(function() { setTimeout(function() {
if (wpavatar.is_network_admin === '1') {
$('#check-all-cache').trigger('click');
} else {
$('#check-cache').trigger('click'); $('#check-cache').trigger('click');
}
}, 300); }, 300);
} }
@ -32,6 +37,7 @@ jQuery(document).ready(function($) {
} }
}); });
// CDN options management
function updateCdnOptions() { function updateCdnOptions() {
var selectedType = $('input[name="wpavatar_cdn_type"]:checked').val(); var selectedType = $('input[name="wpavatar_cdn_type"]:checked').val();
@ -53,23 +59,32 @@ jQuery(document).ready(function($) {
if (value && value.toLowerCase().indexOf('cravatar') !== -1) { if (value && value.toLowerCase().indexOf('cravatar') !== -1) {
forceMd5HashMethod(true); forceMd5HashMethod(true);
} else { } 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); forceMd5HashMethod(false);
} }
} }
function forceMd5HashMethod(force) { function forceMd5HashMethod(force) {
if (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="md5"]').prop('checked', true);
$('input[name="wpavatar_hash_method"][value="sha256"]').prop('disabled', true); $('input[name="wpavatar_hash_method"][value="sha256"]').prop('disabled', true);
$('.hash-method-notice').show(); $('.hash-method-notice').show();
} else { } else {
// Restore disabled state, but don't change selection
$('input[name="wpavatar_hash_method"][value="sha256"]').prop('disabled', false); $('input[name="wpavatar_hash_method"][value="sha256"]').prop('disabled', false);
$('.hash-method-notice').hide(); $('.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()); checkIfCravatarRelated($(this).val());
}); });
// Cache management for single site
$('#check-cache').on('click', function() { $('#check-cache').on('click', function() {
var $button = $(this); var $button = $(this);
var $stats = $('#cache-stats'); 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('<p>' + wpavatar_l10n.checking_status + '</p>');
$.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('<div class="error"><p>' + (response.data || wpavatar_l10n.check_failed) + '</p></div>');
}
},
error: function() {
$stats.html('<div class="error"><p>' + wpavatar_l10n.request_failed + '</p></div>');
},
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('<p>' + wpavatar_l10n.purging_cache + '</p>');
$.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) { $('#wpavatar-basic-form, #wpavatar-cache-form, #wpavatar-advanced-form, #wpavatar-shortcodes-form').on('submit', function(e) {
var formId = $(this).attr('id'); var formId = $(this).attr('id');
var $status = $('#wpavatar-status'); var $status = $('#wpavatar-status');
@ -202,6 +323,28 @@ jQuery(document).ready(function($) {
return true; 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) { if (window.location.search.indexOf('settings-updated=true') > -1) {
$('#wpavatar-status') $('#wpavatar-status')
.removeClass('notice-error') .removeClass('notice-error')
@ -212,6 +355,29 @@ jQuery(document).ready(function($) {
.fadeOut(); .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 = ''; var currentTab = '';
if (window.location.search.indexOf('tab=') > -1) { if (window.location.search.indexOf('tab=') > -1) {

View file

@ -81,10 +81,33 @@ class Settings {
return get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); return get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR);
} }
// If multisite, ensure the site-specific cache directory exists
if (is_multisite()) {
$blog_id = get_current_blog_id();
$site_cache_dir = trailingslashit($value) . 'site-' . $blog_id;
if (!file_exists($site_cache_dir)) {
if (!wp_mkdir_p($site_cache_dir)) {
add_settings_error(
'wpavatar_cache',
'cache_path_invalid',
__('无法创建站点缓存目录,请检查权限', 'wpavatar'),
'error'
);
return get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR);
}
}
$index_file = $site_cache_dir . '/index.php';
if (!file_exists($index_file)) {
@file_put_contents($index_file, '<?php // Silence is golden.');
}
} else {
$index_file = $value . 'index.php'; $index_file = $value . 'index.php';
if (!file_exists($index_file)) { if (!file_exists($index_file)) {
@file_put_contents($index_file, '<?php // Silence is golden.'); @file_put_contents($index_file, '<?php // Silence is golden.');
} }
}
return $value; return $value;
} }
@ -112,9 +135,11 @@ class Settings {
wp_localize_script('wpavatar-admin', 'wpavatar', [ wp_localize_script('wpavatar-admin', 'wpavatar', [
'nonce' => wp_create_nonce('wpavatar_admin_nonce'), 'nonce' => wp_create_nonce('wpavatar_admin_nonce'),
'ajaxurl' => admin_url('admin-ajax.php'), '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, 'plugin_url' => WPAVATAR_PLUGIN_URL,
'assets_url' => WPAVATAR_PLUGIN_URL . 'assets/', 'assets_url' => WPAVATAR_PLUGIN_URL . 'assets/',
'is_network_admin' => '0',
'is_multisite' => is_multisite() ? '1' : '0',
]); ]);
wp_localize_script('wpavatar-admin', 'wpavatar_l10n', [ wp_localize_script('wpavatar-admin', 'wpavatar_l10n', [
@ -142,6 +167,17 @@ class Settings {
} }
public static function render_settings_page() { 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'; $active_tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'basic';
?> ?>
<div class="wrap wpavatar-settings"> <div class="wrap wpavatar-settings">
@ -174,24 +210,66 @@ class Settings {
<h2><?php _e('基础设置', 'wpavatar'); ?></h2> <h2><?php _e('基础设置', 'wpavatar'); ?></h2>
<p class="wpavatar-section-desc"><?php _e('配置头像服务和CDN设置。', 'wpavatar'); ?></p> <p class="wpavatar-section-desc"><?php _e('配置头像服务和CDN设置。', 'wpavatar'); ?></p>
<?php if (is_multisite() && $network_enabled): ?>
<div class="wpavatar-network-notice">
<p>
<?php if (in_array('wpavatar_enable_cravatar', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('启用初认头像', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_cdn_type', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('线路选择', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_cravatar_route', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('Cravatar官方源', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_third_party_mirror', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('第三方镜像', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_custom_cdn', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('自定义CDN', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_hash_method', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('头像哈希方法', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_timeout', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('超时设置', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (array_intersect(['wpavatar_enable_cravatar', 'wpavatar_cdn_type', 'wpavatar_cravatar_route', 'wpavatar_third_party_mirror', 'wpavatar_custom_cdn', 'wpavatar_hash_method', 'wpavatar_timeout'], $network_controlled_options)): ?>
<em><?php _e('以上选项由网络管理员控制,您的更改将不会生效。', 'wpavatar'); ?></em>
<?php endif; ?>
</p>
</div>
<?php endif; ?>
<form method="post" action="options.php" id="wpavatar-basic-form"> <form method="post" action="options.php" id="wpavatar-basic-form">
<?php <?php
settings_fields('wpavatar_basic'); settings_fields('wpavatar_basic');
$enable_cravatar = get_option('wpavatar_enable_cravatar', 1); // Get option values using wpavatar_get_option instead of get_option
$cdn_type = get_option('wpavatar_cdn_type', 'cravatar_route'); $enable_cravatar = wpavatar_get_option('wpavatar_enable_cravatar', 1);
$cravatar_route = get_option('wpavatar_cravatar_route', 'cravatar.com'); $cdn_type = wpavatar_get_option('wpavatar_cdn_type', 'cravatar_route');
$third_party_mirror = get_option('wpavatar_third_party_mirror', 'weavatar.com'); $cravatar_route = wpavatar_get_option('wpavatar_cravatar_route', 'cravatar.com');
$custom_cdn = get_option('wpavatar_custom_cdn', ''); $third_party_mirror = wpavatar_get_option('wpavatar_third_party_mirror', 'weavatar.com');
$hash_method = get_option('wpavatar_hash_method', 'md5'); $custom_cdn = wpavatar_get_option('wpavatar_custom_cdn', '');
$timeout = get_option('wpavatar_timeout', 5); $hash_method = wpavatar_get_option('wpavatar_hash_method', 'md5');
$timeout = wpavatar_get_option('wpavatar_timeout', 5);
// Determine if fields should be disabled in multisite
$disabled_enable_cravatar = (is_multisite() && $network_enabled && in_array('wpavatar_enable_cravatar', $network_controlled_options)) ? 'disabled' : '';
$disabled_cdn_type = (is_multisite() && $network_enabled && in_array('wpavatar_cdn_type', $network_controlled_options)) ? 'disabled' : '';
$disabled_cravatar_route = (is_multisite() && $network_enabled && in_array('wpavatar_cravatar_route', $network_controlled_options)) ? 'disabled' : '';
$disabled_third_party_mirror = (is_multisite() && $network_enabled && in_array('wpavatar_third_party_mirror', $network_controlled_options)) ? 'disabled' : '';
$disabled_custom_cdn = (is_multisite() && $network_enabled && in_array('wpavatar_custom_cdn', $network_controlled_options)) ? 'disabled' : '';
$disabled_hash_method = (is_multisite() && $network_enabled && in_array('wpavatar_hash_method', $network_controlled_options)) ? 'disabled' : '';
$disabled_timeout = (is_multisite() && $network_enabled && in_array('wpavatar_timeout', $network_controlled_options)) ? 'disabled' : '';
?> ?>
<table class="form-table"> <table class="form-table">
<tr> <tr>
<th><?php _e('启用初认头像', 'wpavatar'); ?></th> <th><?php _e('启用初认头像', 'wpavatar'); ?></th>
<td> <td>
<label class="wpavatar-switch"> <label class="wpavatar-switch">
<input type="checkbox" name="wpavatar_enable_cravatar" value="1" <?php checked($enable_cravatar); ?>> <input type="checkbox" name="wpavatar_enable_cravatar" value="1" <?php checked($enable_cravatar); ?> <?php echo $disabled_enable_cravatar; ?>>
<span class="wpavatar-slider"></span> <span class="wpavatar-slider"></span>
<span class="wpavatar-switch-label"><?php _e('替换WordPress默认头像为Cravatar', 'wpavatar'); ?></span> <span class="wpavatar-switch-label"><?php _e('替换WordPress默认头像为Cravatar', 'wpavatar'); ?></span>
</label> </label>
@ -202,15 +280,15 @@ class Settings {
<th><?php _e('线路选择', 'wpavatar'); ?></th> <th><?php _e('线路选择', 'wpavatar'); ?></th>
<td> <td>
<label class="wpavatar-radio"> <label class="wpavatar-radio">
<input type="radio" name="wpavatar_cdn_type" value="cravatar_route" <?php checked($cdn_type, 'cravatar_route'); ?>> <input type="radio" name="wpavatar_cdn_type" value="cravatar_route" <?php checked($cdn_type, 'cravatar_route'); ?> <?php echo $disabled_cdn_type; ?>>
<span class="wpavatar-radio-label"><?php _e('Cravatar自选线路', 'wpavatar'); ?></span> <span class="wpavatar-radio-label"><?php _e('Cravatar自选线路', 'wpavatar'); ?></span>
</label><br> </label><br>
<label class="wpavatar-radio"> <label class="wpavatar-radio">
<input type="radio" name="wpavatar_cdn_type" value="third_party" <?php checked($cdn_type, 'third_party'); ?>> <input type="radio" name="wpavatar_cdn_type" value="third_party" <?php checked($cdn_type, 'third_party'); ?> <?php echo $disabled_cdn_type; ?>>
<span class="wpavatar-radio-label"><?php _e('第三方镜像', 'wpavatar'); ?></span> <span class="wpavatar-radio-label"><?php _e('第三方镜像', 'wpavatar'); ?></span>
</label><br> </label><br>
<label class="wpavatar-radio"> <label class="wpavatar-radio">
<input type="radio" name="wpavatar_cdn_type" value="custom" <?php checked($cdn_type, 'custom'); ?>> <input type="radio" name="wpavatar_cdn_type" value="custom" <?php checked($cdn_type, 'custom'); ?> <?php echo $disabled_cdn_type; ?>>
<span class="wpavatar-radio-label"><?php _e('自定义CDN', 'wpavatar'); ?></span> <span class="wpavatar-radio-label"><?php _e('自定义CDN', 'wpavatar'); ?></span>
</label> </label>
</td> </td>
@ -218,7 +296,7 @@ class Settings {
<tr class="cdn-option cravatar-route-option" <?php echo $cdn_type !== 'cravatar_route' ? 'style="display:none;"' : ''; ?>> <tr class="cdn-option cravatar-route-option" <?php echo $cdn_type !== 'cravatar_route' ? 'style="display:none;"' : ''; ?>>
<th><?php _e('Cravatar官方源', 'wpavatar'); ?></th> <th><?php _e('Cravatar官方源', 'wpavatar'); ?></th>
<td> <td>
<select name="wpavatar_cravatar_route" class="wpavatar-select"> <select name="wpavatar_cravatar_route" class="wpavatar-select" <?php echo $disabled_cravatar_route; ?>>
<option value="cravatar.cn" <?php selected($cravatar_route, 'cravatar.cn'); ?>><?php _e('默认线路 (cravatar.com)', 'wpavatar'); ?></option> <option value="cravatar.cn" <?php selected($cravatar_route, 'cravatar.cn'); ?>><?php _e('默认线路 (cravatar.com)', 'wpavatar'); ?></option>
<option value="cn.cravatar.com" <?php selected($cravatar_route, 'cn.cravatar.com'); ?>><?php _e('中国 (cn.cravatar.com)', 'wpavatar'); ?></option> <option value="cn.cravatar.com" <?php selected($cravatar_route, 'cn.cravatar.com'); ?>><?php _e('中国 (cn.cravatar.com)', 'wpavatar'); ?></option>
<option value="hk.cravatar.com" <?php selected($cravatar_route, 'hk.cravatar.com'); ?>><?php _e('香港 (hk.cravatar.com)', 'wpavatar'); ?></option> <option value="hk.cravatar.com" <?php selected($cravatar_route, 'hk.cravatar.com'); ?>><?php _e('香港 (hk.cravatar.com)', 'wpavatar'); ?></option>
@ -230,7 +308,7 @@ class Settings {
<tr class="cdn-option third-party-option" <?php echo $cdn_type !== 'third_party' ? 'style="display:none;"' : ''; ?>> <tr class="cdn-option third-party-option" <?php echo $cdn_type !== 'third_party' ? 'style="display:none;"' : ''; ?>>
<th><?php _e('第三方镜像', 'wpavatar'); ?></th> <th><?php _e('第三方镜像', 'wpavatar'); ?></th>
<td> <td>
<select name="wpavatar_third_party_mirror" class="wpavatar-select"> <select name="wpavatar_third_party_mirror" class="wpavatar-select" <?php echo $disabled_third_party_mirror; ?>>
<option value="weavatar.com" <?php selected($third_party_mirror, 'weavatar.com'); ?>><?php _e('WeAvatar (weavatar.com)', 'wpavatar'); ?></option> <option value="weavatar.com" <?php selected($third_party_mirror, 'weavatar.com'); ?>><?php _e('WeAvatar (weavatar.com)', 'wpavatar'); ?></option>
<option value="libravatar.org" <?php selected($third_party_mirror, 'libravatar.org'); ?>><?php _e('Libravatar (libravatar.org)', 'wpavatar'); ?></option> <option value="libravatar.org" <?php selected($third_party_mirror, 'libravatar.org'); ?>><?php _e('Libravatar (libravatar.org)', 'wpavatar'); ?></option>
<option value="gravatar.loli.net" <?php selected($third_party_mirror, 'gravatar.loli.net'); ?>><?php _e('Loli镜像 (gravatar.loli.net)', 'wpavatar'); ?></option> <option value="gravatar.loli.net" <?php selected($third_party_mirror, 'gravatar.loli.net'); ?>><?php _e('Loli镜像 (gravatar.loli.net)', 'wpavatar'); ?></option>
@ -244,7 +322,7 @@ class Settings {
<tr class="cdn-option custom-cdn-option" <?php echo $cdn_type !== 'custom' ? 'style="display:none;"' : ''; ?>> <tr class="cdn-option custom-cdn-option" <?php echo $cdn_type !== 'custom' ? 'style="display:none;"' : ''; ?>>
<th><?php _e('自定义CDN', 'wpavatar'); ?></th> <th><?php _e('自定义CDN', 'wpavatar'); ?></th>
<td> <td>
<input type="text" name="wpavatar_custom_cdn" value="<?php echo esc_attr($custom_cdn); ?>" class="regular-text wpavatar-input"> <input type="text" name="wpavatar_custom_cdn" value="<?php echo esc_attr($custom_cdn); ?>" class="regular-text wpavatar-input" <?php echo $disabled_custom_cdn; ?>>
<p class="description"><?php _e('输入自定义CDN域名例如cdn.example.com', 'wpavatar'); ?></p> <p class="description"><?php _e('输入自定义CDN域名例如cdn.example.com', 'wpavatar'); ?></p>
</td> </td>
</tr> </tr>
@ -252,21 +330,21 @@ class Settings {
<th><?php _e('头像哈希方法', 'wpavatar'); ?></th> <th><?php _e('头像哈希方法', 'wpavatar'); ?></th>
<td> <td>
<label class="wpavatar-radio"> <label class="wpavatar-radio">
<input type="radio" name="wpavatar_hash_method" value="md5" <?php checked($hash_method, 'md5'); ?>> <input type="radio" name="wpavatar_hash_method" value="md5" <?php checked($hash_method, 'md5'); ?> <?php echo $disabled_hash_method; ?>>
<span class="wpavatar-radio-label"><?php _e('MD5 (Cravatar默认)', 'wpavatar'); ?></span> <span class="wpavatar-radio-label"><?php _e('MD5 (Cravatar默认)', 'wpavatar'); ?></span>
</label><br> </label><br>
<label class="wpavatar-radio"> <label class="wpavatar-radio">
<input type="radio" name="wpavatar_hash_method" value="sha256" <?php checked($hash_method, 'sha256'); ?>> <input type="radio" name="wpavatar_hash_method" value="sha256" <?php checked($hash_method, 'sha256'); ?> <?php echo $disabled_hash_method; ?>>
<span class="wpavatar-radio-label"><?php _e('SHA256 (Gravatar默认)', 'wpavatar'); ?></span> <span class="wpavatar-radio-label"><?php _e('SHA256 (Gravatar默认)', 'wpavatar'); ?></span>
</label> </label>
<p class="description"><?php _e('选择头像邮箱的哈希方法Cravatar目前使用MD5一般Gravatar镜像均为SHA256', 'wpavatar'); ?></p> <p class="description"><?php _e('选择头像邮箱的哈希方法Cravatar目前使用MD5一般Gravatar镜像均为SHA256', 'wpavatar'); ?></p>
<p class="description hash-method-notice" style="color: #d63638; <?php echo $cdn_type !== 'cravatar_route' ? 'display:none;' : ''; ?>"><?php _e('注意使用Cravatar服务时哈希方法将仅使用MD5', 'wpavatar'); ?></p> <p class="description hash-method-notice" style="color: #d63638; <?php echo $cdn_type !== 'cravatar_route' ? 'display:none;' : ''; ?>"><?php _e('注意使用Cravatar服务时哈希方法将仅使用MD5', 'wpavatar'); ?> <a href="https://cravatar.com/docs" target="_blank" rel="noopener noreferrer"><?php _e('进一步了解↗', 'wpavatar'); ?></a></p>
</td> </td>
</tr> </tr>
<tr> <tr>
<th><?php _e('超时设置', 'wpavatar'); ?></th> <th><?php _e('超时设置', 'wpavatar'); ?></th>
<td> <td>
<input type="number" name="wpavatar_timeout" value="<?php echo esc_attr($timeout); ?>" min="1" max="30" class="small-text wpavatar-input"> <input type="number" name="wpavatar_timeout" value="<?php echo esc_attr($timeout); ?>" min="1" max="30" class="small-text wpavatar-input" <?php echo $disabled_timeout; ?>>
<?php _e('秒', 'wpavatar'); ?> <?php _e('秒', 'wpavatar'); ?>
<p class="description"><?php _e('头像请求的最大等待时间,超过后将使用备用头像', 'wpavatar'); ?></p> <p class="description"><?php _e('头像请求的最大等待时间,超过后将使用备用头像', 'wpavatar'); ?></p>
</td> </td>
@ -283,6 +361,26 @@ class Settings {
<h2><?php _e('缓存控制', 'wpavatar'); ?></h2> <h2><?php _e('缓存控制', 'wpavatar'); ?></h2>
<p class="wpavatar-section-desc"><?php _e('管理头像缓存设置和操作。', 'wpavatar'); ?></p> <p class="wpavatar-section-desc"><?php _e('管理头像缓存设置和操作。', 'wpavatar'); ?></p>
<?php if (is_multisite() && $network_enabled): ?>
<div class="wpavatar-network-notice">
<p>
<?php if (in_array('wpavatar_enable_cache', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('启用本地缓存', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_cache_path', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('缓存目录', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_cache_expire', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('缓存过期时间', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (array_intersect(['wpavatar_enable_cache', 'wpavatar_cache_path', 'wpavatar_cache_expire'], $network_controlled_options)): ?>
<em><?php _e('以上选项由网络管理员控制,您的更改将不会生效。', 'wpavatar'); ?></em>
<?php endif; ?>
</p>
</div>
<?php endif; ?>
<div class="wpavatar-stats-card"> <div class="wpavatar-stats-card">
<h3><?php _e('缓存统计', 'wpavatar'); ?></h3> <h3><?php _e('缓存统计', 'wpavatar'); ?></h3>
<div id="cache-stats" class="cache-stats-wrapper"></div> <div id="cache-stats" class="cache-stats-wrapper"></div>
@ -296,16 +394,22 @@ class Settings {
<?php <?php
settings_fields('wpavatar_cache'); settings_fields('wpavatar_cache');
$enable_cache = get_option('wpavatar_enable_cache', 1); // Get option values using wpavatar_get_option instead of get_option
$cache_path = get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); $enable_cache = wpavatar_get_option('wpavatar_enable_cache', 1);
$cache_expire = get_option('wpavatar_cache_expire', 7); $cache_path = wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR);
$cache_expire = wpavatar_get_option('wpavatar_cache_expire', 7);
// Determine if fields should be disabled in multisite
$disabled_enable_cache = (is_multisite() && $network_enabled && in_array('wpavatar_enable_cache', $network_controlled_options)) ? 'disabled' : '';
$disabled_cache_path = (is_multisite() && $network_enabled && in_array('wpavatar_cache_path', $network_controlled_options)) ? 'disabled' : '';
$disabled_cache_expire = (is_multisite() && $network_enabled && in_array('wpavatar_cache_expire', $network_controlled_options)) ? 'disabled' : '';
?> ?>
<table class="form-table"> <table class="form-table">
<tr> <tr>
<th><?php _e('启用本地缓存', 'wpavatar'); ?></th> <th><?php _e('启用本地缓存', 'wpavatar'); ?></th>
<td> <td>
<label class="wpavatar-switch"> <label class="wpavatar-switch">
<input type="checkbox" name="wpavatar_enable_cache" value="1" <?php checked($enable_cache); ?>> <input type="checkbox" name="wpavatar_enable_cache" value="1" <?php checked($enable_cache); ?> <?php echo $disabled_enable_cache; ?>>
<span class="wpavatar-slider"></span> <span class="wpavatar-slider"></span>
<span class="wpavatar-switch-label"><?php _e('缓存头像到本地服务器', 'wpavatar'); ?></span> <span class="wpavatar-switch-label"><?php _e('缓存头像到本地服务器', 'wpavatar'); ?></span>
</label> </label>
@ -315,14 +419,18 @@ class Settings {
<tr> <tr>
<th><?php _e('缓存目录', 'wpavatar'); ?></th> <th><?php _e('缓存目录', 'wpavatar'); ?></th>
<td> <td>
<input type="text" name="wpavatar_cache_path" value="<?php echo esc_attr($cache_path); ?>" class="regular-text wpavatar-input"> <input type="text" name="wpavatar_cache_path" value="<?php echo esc_attr($cache_path); ?>" class="regular-text wpavatar-input" <?php echo $disabled_cache_path; ?>>
<?php if (is_multisite()): ?>
<p class="description"><?php printf(__('确保目录可写,当前站点将创建子目录:%s', 'wpavatar'), trailingslashit($cache_path) . 'site-' . get_current_blog_id()); ?></p>
<?php else: ?>
<p class="description"><?php _e('确保目录可写,建议路径:/wp-content/uploads/cravatar', 'wpavatar'); ?></p> <p class="description"><?php _e('确保目录可写,建议路径:/wp-content/uploads/cravatar', 'wpavatar'); ?></p>
<?php endif; ?>
</td> </td>
</tr> </tr>
<tr> <tr>
<th><?php _e('缓存过期时间', 'wpavatar'); ?></th> <th><?php _e('缓存过期时间', 'wpavatar'); ?></th>
<td> <td>
<input type="number" name="wpavatar_cache_expire" value="<?php echo esc_attr($cache_expire); ?>" min="1" max="30" class="small-text wpavatar-input"> <input type="number" name="wpavatar_cache_expire" value="<?php echo esc_attr($cache_expire); ?>" min="1" max="30" class="small-text wpavatar-input" <?php echo $disabled_cache_expire; ?>>
<?php _e('天', 'wpavatar'); ?> <?php _e('天', 'wpavatar'); ?>
<p class="description"><?php _e('头像缓存的有效期,过期后将重新获取', 'wpavatar'); ?></p> <p class="description"><?php _e('头像缓存的有效期,过期后将重新获取', 'wpavatar'); ?></p>
</td> </td>
@ -339,21 +447,47 @@ class Settings {
<h2><?php _e('高级设置', 'wpavatar'); ?></h2> <h2><?php _e('高级设置', 'wpavatar'); ?></h2>
<p class="wpavatar-section-desc"><?php _e('配置头像的SEO和备用方案。', 'wpavatar'); ?></p> <p class="wpavatar-section-desc"><?php _e('配置头像的SEO和备用方案。', 'wpavatar'); ?></p>
<?php if (is_multisite() && $network_enabled): ?>
<div class="wpavatar-network-notice">
<p>
<?php if (in_array('wpavatar_seo_alt', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('SEO替代文本', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_fallback_mode', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('头像加载失败处理', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_fallback_avatar', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('备用头像选择', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (array_intersect(['wpavatar_seo_alt', 'wpavatar_fallback_mode', 'wpavatar_fallback_avatar'], $network_controlled_options)): ?>
<em><?php _e('以上选项由网络管理员控制,您的更改将不会生效。', 'wpavatar'); ?></em>
<?php endif; ?>
</p>
</div>
<?php endif; ?>
<form method="post" action="options.php" id="wpavatar-advanced-form"> <form method="post" action="options.php" id="wpavatar-advanced-form">
<?php <?php
settings_fields('wpavatar_advanced'); settings_fields('wpavatar_advanced');
$seo_alt = get_option('wpavatar_seo_alt', '%s的头像'); // Get option values using wpavatar_get_option instead of get_option
$fallback_mode = get_option('wpavatar_fallback_mode', 1); $seo_alt = wpavatar_get_option('wpavatar_seo_alt', '%s的头像');
$fallback_avatar = get_option('wpavatar_fallback_avatar', 'default'); $fallback_mode = wpavatar_get_option('wpavatar_fallback_mode', 1);
$fallback_avatar = wpavatar_get_option('wpavatar_fallback_avatar', 'default');
$local_avatars = \WPAvatar\Cravatar::get_local_avatars(); $local_avatars = \WPAvatar\Cravatar::get_local_avatars();
// Determine if fields should be disabled in multisite
$disabled_seo_alt = (is_multisite() && $network_enabled && in_array('wpavatar_seo_alt', $network_controlled_options)) ? 'disabled' : '';
$disabled_fallback_mode = (is_multisite() && $network_enabled && in_array('wpavatar_fallback_mode', $network_controlled_options)) ? 'disabled' : '';
$disabled_fallback_avatar = (is_multisite() && $network_enabled && in_array('wpavatar_fallback_avatar', $network_controlled_options)) ? 'disabled' : '';
?> ?>
<table class="form-table"> <table class="form-table">
<tr> <tr>
<th><?php _e('SEO替代文本', 'wpavatar'); ?></th> <th><?php _e('SEO替代文本', 'wpavatar'); ?></th>
<td> <td>
<input type="text" name="wpavatar_seo_alt" value="<?php echo esc_attr($seo_alt); ?>" class="regular-text wpavatar-input"> <input type="text" name="wpavatar_seo_alt" value="<?php echo esc_attr($seo_alt); ?>" class="regular-text wpavatar-input" <?php echo $disabled_seo_alt; ?>>
<p class="description"><?php _e('头像的ALT文本%s将被替换为用户名', 'wpavatar'); ?></p> <p class="description"><?php _e('头像的ALT文本%s将被替换为用户名', 'wpavatar'); ?></p>
</td> </td>
</tr> </tr>
@ -361,7 +495,7 @@ class Settings {
<th><?php _e('头像加载失败处理', 'wpavatar'); ?></th> <th><?php _e('头像加载失败处理', 'wpavatar'); ?></th>
<td> <td>
<label class="wpavatar-switch"> <label class="wpavatar-switch">
<input type="checkbox" name="wpavatar_fallback_mode" value="1" <?php checked($fallback_mode); ?>> <input type="checkbox" name="wpavatar_fallback_mode" value="1" <?php checked($fallback_mode); ?> <?php echo $disabled_fallback_mode; ?>>
<span class="wpavatar-slider"></span> <span class="wpavatar-slider"></span>
<span class="wpavatar-switch-label"><?php _e('启用备用头像', 'wpavatar'); ?></span> <span class="wpavatar-switch-label"><?php _e('启用备用头像', 'wpavatar'); ?></span>
</label> </label>
@ -374,7 +508,7 @@ class Settings {
<div class="default-avatar-options"> <div class="default-avatar-options">
<?php foreach ($local_avatars as $key => $avatar) : ?> <?php foreach ($local_avatars as $key => $avatar) : ?>
<label> <label>
<input type="radio" name="wpavatar_fallback_avatar" value="<?php echo esc_attr($key); ?>" <?php checked($fallback_avatar, $key); ?>> <input type="radio" name="wpavatar_fallback_avatar" value="<?php echo esc_attr($key); ?>" <?php checked($fallback_avatar, $key); ?> <?php echo $disabled_fallback_avatar; ?>>
<img src="<?php echo esc_url($avatar['url']); ?>" alt="<?php echo esc_attr($avatar['name']); ?>" width="48" height="48"> <img src="<?php echo esc_url($avatar['url']); ?>" alt="<?php echo esc_attr($avatar['name']); ?>" width="48" height="48">
<span class="avatar-option-name"><?php echo esc_html($avatar['name']); ?></span> <span class="avatar-option-name"><?php echo esc_html($avatar['name']); ?></span>
</label> </label>
@ -388,7 +522,7 @@ class Settings {
: 'https://wpcy.com'; : 'https://wpcy.com';
printf( printf(
__('选择您喜欢的故障备用头像,如需智能线路切换,请使用%s。', 'wpavatar'), __('选择您的故障备用头像,如需智能线路切换,请使用%s。', 'wpavatar'),
sprintf( sprintf(
'<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>', '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>',
esc_url($wpcy_link), esc_url($wpcy_link),
@ -407,11 +541,30 @@ class Settings {
</form> </form>
</div> </div>
<div class="wpavatar-section" id="wpavatar-section-shortcodes" style="<?php echo $active_tab !== 'shortcodes' ? 'display: none;' : ''; ?>"> <div class="wpavatar-section" id="wpavatar-section-shortcodes" style="<?php echo $active_tab !== 'shortcodes' ? 'display: none;' : ''; ?>">
<h2><?php _e('简码设置', 'wpavatar'); ?></h2> <h2><?php _e('简码设置', 'wpavatar'); ?></h2>
<p class="wpavatar-section-desc"><?php _e('配置头像简码的默认参数和预览效果。', 'wpavatar'); ?></p> <p class="wpavatar-section-desc"><?php _e('配置头像简码的默认参数和预览效果。', 'wpavatar'); ?></p>
<?php if (is_multisite() && $network_enabled): ?>
<div class="wpavatar-network-notice">
<p>
<?php if (in_array('wpavatar_shortcode_size', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('默认头像大小', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_shortcode_class', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('默认CSS类名', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (in_array('wpavatar_shortcode_shape', $network_controlled_options)): ?>
<span class="dashicons dashicons-lock"></span> <?php _e('默认头像形状', 'wpavatar'); ?><br>
<?php endif; ?>
<?php if (array_intersect(['wpavatar_shortcode_size', 'wpavatar_shortcode_class', 'wpavatar_shortcode_shape'], $network_controlled_options)): ?>
<em><?php _e('以上选项由网络管理员控制,您的更改将不会生效。', 'wpavatar'); ?></em>
<?php endif; ?>
</p>
</div>
<?php endif; ?>
<div class="wpavatar-preview-container"> <div class="wpavatar-preview-container">
<h3><?php _e('头像预览', 'wpavatar'); ?></h3> <h3><?php _e('头像预览', 'wpavatar'); ?></h3>
<div class="wpavatar-preview-wrapper"> <div class="wpavatar-preview-wrapper">
@ -447,29 +600,35 @@ class Settings {
<?php <?php
settings_fields('wpavatar_shortcodes'); settings_fields('wpavatar_shortcodes');
$shortcode_size = get_option('wpavatar_shortcode_size', 96); // Get option values using wpavatar_get_option instead of get_option
$shortcode_class = get_option('wpavatar_shortcode_class', 'wpavatar'); $shortcode_size = wpavatar_get_option('wpavatar_shortcode_size', 96);
$shortcode_shape = get_option('wpavatar_shortcode_shape', 'square'); $shortcode_class = wpavatar_get_option('wpavatar_shortcode_class', 'wpavatar');
$shortcode_shape = wpavatar_get_option('wpavatar_shortcode_shape', 'square');
// Determine if fields should be disabled in multisite
$disabled_shortcode_size = (is_multisite() && $network_enabled && in_array('wpavatar_shortcode_size', $network_controlled_options)) ? 'disabled' : '';
$disabled_shortcode_class = (is_multisite() && $network_enabled && in_array('wpavatar_shortcode_class', $network_controlled_options)) ? 'disabled' : '';
$disabled_shortcode_shape = (is_multisite() && $network_enabled && in_array('wpavatar_shortcode_shape', $network_controlled_options)) ? 'disabled' : '';
?> ?>
<table class="form-table"> <table class="form-table">
<tr> <tr>
<th><?php _e('默认头像大小', 'wpavatar'); ?></th> <th><?php _e('默认头像大小', 'wpavatar'); ?></th>
<td> <td>
<input type="number" name="wpavatar_shortcode_size" value="<?php echo esc_attr($shortcode_size); ?>" min="16" max="512" class="small-text wpavatar-input"> <input type="number" name="wpavatar_shortcode_size" value="<?php echo esc_attr($shortcode_size); ?>" min="16" max="512" class="small-text wpavatar-input" <?php echo $disabled_shortcode_size; ?>>
<p class="description"><?php _e('简码默认头像大小(像素)', 'wpavatar'); ?></p> <p class="description"><?php _e('简码默认头像大小(像素)', 'wpavatar'); ?></p>
</td> </td>
</tr> </tr>
<tr> <tr>
<th><?php _e('默认CSS类名', 'wpavatar'); ?></th> <th><?php _e('默认CSS类名', 'wpavatar'); ?></th>
<td> <td>
<input type="text" name="wpavatar_shortcode_class" value="<?php echo esc_attr($shortcode_class); ?>" class="regular-text wpavatar-input"> <input type="text" name="wpavatar_shortcode_class" value="<?php echo esc_attr($shortcode_class); ?>" class="regular-text wpavatar-input" <?php echo $disabled_shortcode_class; ?>>
<p class="description"><?php _e('简码生成的头像默认CSS类', 'wpavatar'); ?></p> <p class="description"><?php _e('简码生成的头像默认CSS类', 'wpavatar'); ?></p>
</td> </td>
</tr> </tr>
<tr> <tr>
<th><?php _e('默认头像形状', 'wpavatar'); ?></th> <th><?php _e('默认头像形状', 'wpavatar'); ?></th>
<td> <td>
<select name="wpavatar_shortcode_shape" class="wpavatar-select"> <select name="wpavatar_shortcode_shape" class="wpavatar-select" <?php echo $disabled_shortcode_shape; ?>>
<option value="square" <?php selected($shortcode_shape, 'square'); ?>><?php _e('方形', 'wpavatar'); ?></option> <option value="square" <?php selected($shortcode_shape, 'square'); ?>><?php _e('方形', 'wpavatar'); ?></option>
<option value="rounded" <?php selected($shortcode_shape, 'rounded'); ?>><?php _e('圆角方形', 'wpavatar'); ?></option> <option value="rounded" <?php selected($shortcode_shape, 'rounded'); ?>><?php _e('圆角方形', 'wpavatar'); ?></option>
<option value="circle" <?php selected($shortcode_shape, 'circle'); ?>><?php _e('圆形', 'wpavatar'); ?></option> <option value="circle" <?php selected($shortcode_shape, 'circle'); ?>><?php _e('圆形', 'wpavatar'); ?></option>
@ -603,106 +762,23 @@ class Settings {
font-size: 12px; font-size: 12px;
line-height: 1.3; 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;
}
</style> </style>
<?php <?php
} }
} }
class Network {
public static function init() {
if (is_network_admin()) {
add_action('network_admin_menu', [__CLASS__, 'add_network_menu']);
add_action('network_admin_edit_wpavatar_network', [__CLASS__, 'save_network_settings']);
}
}
public static function add_network_menu() {
add_submenu_page(
'settings.php',
__('WPAvatar网络设置', 'wpavatar'),
__('WPAvatar', 'wpavatar'),
'manage_network_options',
'wpavatar-network',
[__CLASS__, 'render_network_page']
);
}
public static function render_network_page() {
?>
<div class="wrap wpavatar-settings">
<h1><?php esc_html_e('WPAvatar网络设置', 'wpavatar'); ?></h1>
<div id="wpavatar-network-status" class="notice" style="display:none; margin-top: 10px;"></div>
<div class="wpavatar-card">
<h2><?php _e('网络范围设置', 'wpavatar'); ?></h2>
<p class="wpavatar-section-desc"><?php _e('配置多站点网络的WPAvatar设置。', 'wpavatar'); ?></p>
<form method="post" action="edit.php?action=wpavatar_network" id="wpavatar-network-form">
<?php wp_nonce_field('wpavatar_network_settings'); ?>
<table class="form-table">
<tr>
<th><?php _e('启用网络范围设置', 'wpavatar'); ?></th>
<td>
<label class="wpavatar-switch">
<input type="checkbox" name="wpavatar_network_enabled" value="1" <?php checked(get_site_option('wpavatar_network_enabled', 1)); ?>>
<span class="wpavatar-slider"></span>
<span class="wpavatar-switch-label"><?php _e('在所有站点启用WPAvatar', 'wpavatar'); ?></span>
</label>
<p class="description"><?php _e('启用后WPAvatar将在网络中的所有站点上生效', 'wpavatar'); ?></p>
</td>
</tr>
<tr>
<th><?php _e('默认CDN类型', 'wpavatar'); ?></th>
<td>
<select name="wpavatar_network_cdn_type" class="wpavatar-select">
<option value="cravatar_route" <?php selected(get_site_option('wpavatar_network_cdn_type', 'cravatar_route'), 'cravatar_route'); ?>><?php _e('Cravatar线路', 'wpavatar'); ?></option>
<option value="third_party" <?php selected(get_site_option('wpavatar_network_cdn_type', 'cravatar_route'), 'third_party'); ?>><?php _e('第三方镜像', 'wpavatar'); ?></option>
<option value="custom" <?php selected(get_site_option('wpavatar_network_cdn_type', 'cravatar_route'), 'custom'); ?>><?php _e('自定义CDN', 'wpavatar'); ?></option>
</select>
</td>
</tr>
<tr>
<th><?php _e('默认Cravatar线路', 'wpavatar'); ?></th>
<td>
<select name="wpavatar_network_cravatar_route" class="wpavatar-select">
<option value="cravatar.com" <?php selected(get_site_option('wpavatar_network_cravatar_route', 'cravatar.com'), 'cravatar.com'); ?>><?php _e('默认线路 (cravatar.com)', 'wpavatar'); ?></option>
<option value="cn.cravatar.com" <?php selected(get_site_option('wpavatar_network_cravatar_route', 'cravatar.com'), 'cn.cravatar.com'); ?>><?php _e('中国大陆 (cn.cravatar.com)', 'wpavatar'); ?></option>
<option value="hk.cravatar.com" <?php selected(get_site_option('wpavatar_network_cravatar_route', 'cravatar.com'), 'hk.cravatar.com'); ?>><?php _e('香港 (hk.cravatar.com)', 'wpavatar'); ?></option>
<option value="en.cravatar.com" <?php selected(get_site_option('wpavatar_network_cravatar_route', 'cravatar.com'), 'en.cravatar.com'); ?>><?php _e('国际 (en.cravatar.com)', 'wpavatar'); ?></option>
</select>
</td>
</tr>
<tr>
<th><?php _e('哈希方法', 'wpavatar'); ?></th>
<td>
<p class="description" style="color: #d63638;"><?php _e('注意使用Cravatar服务时哈希方法将强制使用MD5', 'wpavatar'); ?></p>
</td>
</tr>
</table>
<div class="wpavatar-submit-wrapper">
<button type="submit" class="button button-primary"><?php _e('保存设置', 'wpavatar'); ?></button>
</div>
</form>
</div>
</div>
<?php
}
public static function save_network_settings() {
check_admin_referer('wpavatar_network_settings');
update_site_option('wpavatar_network_enabled', isset($_POST['wpavatar_network_enabled']) ? 1 : 0);
if (isset($_POST['wpavatar_network_cdn_type'])) {
update_site_option('wpavatar_network_cdn_type', sanitize_text_field($_POST['wpavatar_network_cdn_type']));
}
if (isset($_POST['wpavatar_network_cravatar_route'])) {
update_site_option('wpavatar_network_cravatar_route', sanitize_text_field($_POST['wpavatar_network_cravatar_route']));
}
wp_redirect(add_query_arg(['page' => 'wpavatar-network', 'updated' => 'true'], network_admin_url('settings.php')));
exit;
}
}

106
includes/compatibility.php Normal file
View file

@ -0,0 +1,106 @@
<?php
/**
* WPAvatar 兼容性修复
*
* 用于处理与 WP-China-Yes 插件的兼容性问题
*/
namespace WPAvatar;
class Compatibility {
/**
* 初始化兼容性修复
*/
public static function init() {
// 在所有插件加载后、主题初始化前运行
add_action('after_setup_theme', [__CLASS__, 'handle_wp_china_yes_compatibility'], 5);
}
/**
* 处理与 WP-China-Yes 插件的兼容性
*/
public static function handle_wp_china_yes_compatibility() {
// 检查 WP-China-Yes 插件是否激活
if (self::is_wp_china_yes_active()) {
// 移除 WP-China-Yes 的头像替换过滤器
self::remove_wp_china_yes_filters();
// 重新初始化 WPAvatar 的 Cravatar 功能,使用更高的优先级
self::reinitialize_wpavatar_filters();
// 可选:添加管理界面通知
if (is_admin()) {
add_action('admin_notices', [__CLASS__, 'admin_compatibility_notice']);
}
}
}
/**
* 检查 WP-China-Yes 插件是否激活
*/
private static function is_wp_china_yes_active() {
return class_exists('WenPai\\ChinaYes\\Service\\Super') ||
defined('CHINA_YES_VERSION') ||
function_exists('WenPai\\ChinaYes\\get_settings');
}
/**
* 移除 WP-China-Yes 的头像替换过滤器
*/
private static function remove_wp_china_yes_filters() {
// 找到可能的 Super 类实例
global $wp_filter;
$filters_to_check = [
'get_avatar_url',
'um_user_avatar_url_filter',
'bp_gravatar_url',
'user_profile_picture_description',
'avatar_defaults'
];
foreach ($filters_to_check as $filter_name) {
if (isset($wp_filter[$filter_name])) {
foreach ($wp_filter[$filter_name]->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 '<div class="notice notice-info is-dismissible">';
echo '<p>检测到文派叶子WPCY.COM插件WPAvatar 生态组件兼容性补丁已生效,确保文派头像设置优先。</p>';
echo '</div>';
}
}
}

View file

@ -19,12 +19,17 @@ class Cravatar {
]; ];
public static function init() { public static function init() {
if (get_option('wpavatar_enable_cravatar', true)) { if (wpavatar_get_option('wpavatar_enable_cravatar', true)) {
add_filter('get_avatar_url', [__CLASS__, 'replace_avatar_url'], 1); // 使用高优先级过滤器来预处理头像数据
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('um_user_avatar_url_filter', [__CLASS__, 'replace_avatar_url'], 1);
add_filter('bp_gravatar_url', [__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('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); add_filter('get_avatar', [__CLASS__, 'add_seo_alt'], 10, 5);
} }
} }
@ -74,12 +79,144 @@ class Cravatar {
return '<a href="https://cravatar.com" target="_blank" rel="noopener">' . __('您可以在初认头像修改您的资料图片', 'wpavatar') . '</a>'; return '<a href="https://cravatar.com" target="_blank" rel="noopener">' . __('您可以在初认头像修改您的资料图片', 'wpavatar') . '</a>';
} }
/**
* 预处理头像数据根据配置决定是否强制使用MD5
*/
public static function pre_get_avatar_data($args, $id_or_email) { public static function pre_get_avatar_data($args, $id_or_email) {
if (is_null($args)) { if (is_null($args)) {
$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 = ''; $email = '';
if (is_numeric($id_or_email)) { if (is_numeric($id_or_email)) {
$user = get_user_by('id', (int)$id_or_email); $user = get_user_by('id', (int)$id_or_email);
if ($user) { if ($user) {
@ -90,89 +227,58 @@ class Cravatar {
$email = $id_or_email; $email = $id_or_email;
} }
} elseif (is_object($id_or_email)) { } elseif (is_object($id_or_email)) {
if (isset($id_or_email->user_id) && $id_or_email->user_id) { if ($id_or_email instanceof \WP_User) {
$user = get_user_by('id', $id_or_email->user_id); $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) { if ($user) {
$email = $user->user_email; $email = $user->user_email;
} }
} elseif (isset($id_or_email->comment_author_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; $email = $id_or_email->comment_author_email;
} elseif (isset($id_or_email->user_email)) {
$email = $id_or_email->user_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;
} }
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 { } else {
$args['hash_method'] = 'md5'; $email = $comment->comment_author_email;
}
if (isset($args['hash']) && $args['hash'] === 'sha256') { }
unset($args['hash']);
} }
} }
// 计算邮箱地址的哈希值 return $email;
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;
} }
public static function replace_avatar_url($url) { 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) { foreach (self::$gravatar_domains as $domain) {
if (strpos($url, $domain) !== false) { 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 = ''; $cdn_domain = '';
if ($cdn_type === 'cravatar_route') { 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') { } 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') { } elseif ($cdn_type === 'custom') {
$custom_cdn = get_option('wpavatar_custom_cdn', ''); $custom_cdn = wpavatar_get_option('wpavatar_custom_cdn', '');
if (!empty($custom_cdn)) { if (!empty($custom_cdn)) {
$cdn_domain = $custom_cdn; $cdn_domain = $custom_cdn;
} else { } else {
@ -189,7 +295,7 @@ class Cravatar {
public static function add_seo_alt($avatar, $id_or_email, $size, $default, $alt) { public static function add_seo_alt($avatar, $id_or_email, $size, $default, $alt) {
// 添加SEO友好的alt属性 // 添加SEO友好的alt属性
$seo_alt = get_option('wpavatar_seo_alt'); $seo_alt = wpavatar_get_option('wpavatar_seo_alt');
if (!empty($seo_alt)) { if (!empty($seo_alt)) {
$user = false; $user = false;
if (is_numeric($id_or_email)) { if (is_numeric($id_or_email)) {
@ -211,8 +317,8 @@ class Cravatar {
} }
// 添加头像加载失败的备用显示 // 添加头像加载失败的备用显示
if (get_option('wpavatar_fallback_mode', 1)) { if (wpavatar_get_option('wpavatar_fallback_mode', 1)) {
$fallback_type = get_option('wpavatar_fallback_avatar', 'default'); $fallback_type = wpavatar_get_option('wpavatar_fallback_avatar', 'default');
$local_avatars = self::get_local_avatars(); $local_avatars = self::get_local_avatars();
if (isset($local_avatars[$fallback_type])) { if (isset($local_avatars[$fallback_type])) {
@ -233,7 +339,7 @@ class Cache {
add_action('init', [__CLASS__, 'setup_cache_dir']); add_action('init', [__CLASS__, 'setup_cache_dir']);
add_action('init', [__CLASS__, 'schedule_purge']); 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_url', [__CLASS__, 'prepare_cache_url'], 99, 2);
add_filter('get_avatar', [__CLASS__, 'serve_cached_avatar'], 20, 5); add_filter('get_avatar', [__CLASS__, 'serve_cached_avatar'], 20, 5);
} }
@ -245,12 +351,11 @@ class Cache {
} }
public static function setup_cache_dir() { 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($base_dir)) {
if (!wp_mkdir_p($base_dir)) {
if (!file_exists($dir)) {
if (!wp_mkdir_p($dir)) {
add_settings_error( add_settings_error(
'wpavatar_cache', 'wpavatar_cache',
'cache_dir_error', 'cache_dir_error',
@ -261,7 +366,7 @@ class Cache {
} }
} }
if (!is_writable($dir)) { if (!is_writable($base_dir)) {
add_settings_error( add_settings_error(
'wpavatar_cache', 'wpavatar_cache',
'cache_dir_writable', 'cache_dir_writable',
@ -271,12 +376,14 @@ class Cache {
return false; return false;
} }
$index_file = $dir . 'index.php'; // Create index.php in base dir
$index_file = $base_dir . 'index.php';
if (!file_exists($index_file)) { if (!file_exists($index_file)) {
@file_put_contents($index_file, '<?php // Silence is golden.'); @file_put_contents($index_file, '<?php // Silence is golden.');
} }
$htaccess_file = $dir . '.htaccess'; // Create .htaccess in base dir
$htaccess_file = $base_dir . '.htaccess';
if (!file_exists($htaccess_file)) { if (!file_exists($htaccess_file)) {
$htaccess_content = "# Prevent directory listing\n"; $htaccess_content = "# Prevent directory listing\n";
$htaccess_content .= "Options -Indexes\n"; $htaccess_content .= "Options -Indexes\n";
@ -288,11 +395,45 @@ class Cache {
@file_put_contents($htaccess_file, $htaccess_content); @file_put_contents($htaccess_file, $htaccess_content);
} }
// If multisite, create site-specific directory
if (is_multisite()) {
$blog_id = get_current_blog_id();
$site_dir = $base_dir . 'site-' . $blog_id . '/';
if (!file_exists($site_dir)) {
if (!wp_mkdir_p($site_dir)) {
add_settings_error(
'wpavatar_cache',
'cache_dir_error',
__('无法创建站点缓存目录,请检查权限', 'wpavatar'),
'error'
);
return false;
}
}
if (!is_writable($site_dir)) {
add_settings_error(
'wpavatar_cache',
'cache_dir_writable',
__('站点缓存目录不可写,请检查权限', 'wpavatar'),
'error'
);
return false;
}
// Create index.php in site dir
$site_index_file = $site_dir . 'index.php';
if (!file_exists($site_index_file)) {
@file_put_contents($site_index_file, '<?php // Silence is golden.');
}
}
return true; return true;
} }
public static function prepare_cache_url($url, $id_or_email) { public static function prepare_cache_url($url, $id_or_email) {
if (!get_option('wpavatar_enable_cache', true)) { if (!wpavatar_get_option('wpavatar_enable_cache', true)) {
return $url; return $url;
} }
@ -307,7 +448,7 @@ class Cache {
} }
public static function serve_cached_avatar($avatar, $id_or_email, $size, $default, $alt) { public static function serve_cached_avatar($avatar, $id_or_email, $size, $default, $alt) {
if (!get_option('wpavatar_enable_cache', true)) { if (!wpavatar_get_option('wpavatar_enable_cache', true)) {
return $avatar; return $avatar;
} }
@ -320,25 +461,24 @@ class Cache {
$is_avatar_url = false; $is_avatar_url = false;
$cdn_type = get_option('wpavatar_cdn_type', 'cravatar_route'); $cdn_type = wpavatar_get_option('wpavatar_cdn_type', 'cravatar_route');
if ($cdn_type === 'cravatar_route') { if ($cdn_type === 'cravatar_route') {
$cdn_domain = get_option('wpavatar_cravatar_route', 'cravatar.com'); $cdn_domain = wpavatar_get_option('wpavatar_cravatar_route', 'cravatar.com');
if (strpos($url, $cdn_domain) !== false) { if (strpos($url, $cdn_domain) !== false) {
$is_avatar_url = true; $is_avatar_url = true;
} }
} elseif ($cdn_type === 'third_party') { } 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');
if (strpos($url, $cdn_domain) !== false) { if (strpos($url, $cdn_domain) !== false) {
$is_avatar_url = true; $is_avatar_url = true;
} }
} elseif ($cdn_type === 'custom') { } elseif ($cdn_type === 'custom') {
$cdn_domain = get_option('wpavatar_custom_cdn', ''); $cdn_domain = wpavatar_get_option('wpavatar_custom_cdn', '');
if (!empty($cdn_domain) && strpos($url, $cdn_domain) !== false) { if (!empty($cdn_domain) && strpos($url, $cdn_domain) !== false) {
$is_avatar_url = true; $is_avatar_url = true;
} }
} }
// 检查是否为Gravatar URL
if (!$is_avatar_url) { if (!$is_avatar_url) {
foreach (Cravatar::$gravatar_domains as $domain) { foreach (Cravatar::$gravatar_domains as $domain) {
if (strpos($url, $domain) !== false) { if (strpos($url, $domain) !== false) {
@ -355,7 +495,7 @@ class Cache {
$hash = self::get_avatar_hash($id_or_email); $hash = self::get_avatar_hash($id_or_email);
$cache_file = self::get_cache_path($hash, $size); $cache_file = self::get_cache_path($hash, $size);
$cache_expire = get_option('wpavatar_cache_expire', 7) * DAY_IN_SECONDS; $cache_expire = wpavatar_get_option('wpavatar_cache_expire', 7) * DAY_IN_SECONDS;
if (file_exists($cache_file) && filemtime($cache_file) > (time() - $cache_expire)) { if (file_exists($cache_file) && filemtime($cache_file) > (time() - $cache_expire)) {
$cached_url = content_url(str_replace(WP_CONTENT_DIR, '', $cache_file)); $cached_url = content_url(str_replace(WP_CONTENT_DIR, '', $cache_file));
return str_replace($url, esc_url($cached_url), $avatar); return str_replace($url, esc_url($cached_url), $avatar);
@ -370,46 +510,51 @@ class Cache {
} }
public static function get_avatar_hash($id_or_email) { public static function get_avatar_hash($id_or_email) {
$email = ''; $email = Cravatar::get_email_from_id_or_email($id_or_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;
}
if (empty($email)) { if (empty($email)) {
return ''; 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');
// 确定使用的哈希方法 // 检查是否需要强制使用MD5针对Cravatar服务
if ($cdn_type !== 'cravatar_route' && $force_md5 = false;
strpos(get_option('wpavatar_third_party_mirror', ''), 'cravatar') === false && if ($cdn_type === 'cravatar_route') {
strpos(get_option('wpavatar_custom_cdn', ''), 'cravatar') === false) { $force_md5 = true;
$hash_method = get_option('wpavatar_hash_method', 'md5'); } elseif ($cdn_type === 'custom') {
$use_md5 = ($hash_method === 'md5'); $custom_cdn = wpavatar_get_option('wpavatar_custom_cdn', '');
if (strpos(strtolower($custom_cdn), 'cravatar') !== false) {
$force_md5 = true;
}
} }
if (!$use_md5) { // 根据设置和条件选择哈希方法
if ($force_md5 || $hash_method === 'md5') {
return md5(strtolower(trim($email)));
} else {
// 使用SHA256
if (function_exists('hash')) {
return hash('sha256', strtolower(trim($email))); return hash('sha256', strtolower(trim($email)));
} else { } else {
// 如果不支持hash函数回退到MD5
return md5(strtolower(trim($email))); return md5(strtolower(trim($email)));
} }
} }
}
public static function get_cache_path($hash, $size) { 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); $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); wp_mkdir_p($dir);
return $dir . "{$hash}-{$size}.jpg"; return $dir . "{$hash}-{$size}.jpg";
} }
@ -428,7 +573,7 @@ class Cache {
return false; return false;
} }
$timeout = get_option('wpavatar_timeout', 5); $timeout = wpavatar_get_option('wpavatar_timeout', 5);
$response = wp_remote_get($url, [ $response = wp_remote_get($url, [
'timeout' => $timeout, 'timeout' => $timeout,
@ -470,7 +615,7 @@ class Cache {
} }
public static function cache_comment_avatar($comment_id, $comment_approved) { 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; return;
} }
@ -480,7 +625,7 @@ class Cache {
} }
$email = $comment->comment_author_email; $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]); $avatar_url = get_avatar_url($email, ['size' => $size]);
@ -493,14 +638,13 @@ class Cache {
self::cache_remote_avatar($avatar_url, $cache_file); self::cache_remote_avatar($avatar_url, $cache_file);
// 缓存2x尺寸的头像用于高分辨率显示器
$retina_url = get_avatar_url($email, ['size' => $size * 2]); $retina_url = get_avatar_url($email, ['size' => $size * 2]);
$retina_cache_file = self::get_cache_path($hash, $size * 2); $retina_cache_file = self::get_cache_path($hash, $size * 2);
self::cache_remote_avatar($retina_url, $retina_cache_file); self::cache_remote_avatar($retina_url, $retina_cache_file);
} }
public static function cache_user_avatar($user_id) { public static function cache_user_avatar($user_id) {
if (!get_option('wpavatar_enable_cache', true)) { if (!wpavatar_get_option('wpavatar_enable_cache', true)) {
return; return;
} }
@ -510,7 +654,7 @@ class Cache {
} }
$email = $user->user_email; $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]); $avatar_url = get_avatar_url($email, ['size' => $size]);
@ -523,32 +667,47 @@ class Cache {
self::cache_remote_avatar($avatar_url, $cache_file); self::cache_remote_avatar($avatar_url, $cache_file);
// 缓存2x尺寸的头像用于高分辨率显示器
$retina_url = get_avatar_url($email, ['size' => $size * 2]); $retina_url = get_avatar_url($email, ['size' => $size * 2]);
$retina_cache_file = self::get_cache_path($hash, $size * 2); $retina_cache_file = self::get_cache_path($hash, $size * 2);
self::cache_remote_avatar($retina_url, $retina_cache_file); self::cache_remote_avatar($retina_url, $retina_cache_file);
} }
public static function purge_expired() { public static function purge_expired() {
$dir = get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); $base_dir = wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR);
if (!file_exists($dir) || !is_dir($dir)) { if (!file_exists($base_dir) || !is_dir($base_dir)) {
return; return;
} }
$files = glob(trailingslashit($dir) . '*.jpg'); $expire_days = wpavatar_get_option('wpavatar_cache_expire', 7);
if (!$files) {
return;
}
$expire_days = get_option('wpavatar_cache_expire', 7);
$expire_time = time() - ($expire_days * DAY_IN_SECONDS); $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) { foreach ($files as $file) {
if (filemtime($file) < $expire_time) { if (filemtime($file) < $expire_time) {
@unlink($file); @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() { public static function schedule_purge() {
if (!wp_next_scheduled('wpavatar_purge_cache')) { if (!wp_next_scheduled('wpavatar_purge_cache')) {
@ -557,23 +716,48 @@ class Cache {
} }
public static function check_cache_status() { public static function check_cache_status() {
$dir = get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); $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 = [ $stats = [
'path' => $dir, 'path' => $site_dir,
'exists' => file_exists($dir) && is_dir($dir), 'exists' => file_exists($site_dir) && is_dir($site_dir),
'writable' => is_writable($dir), 'writable' => is_writable($site_dir),
'file_count' => 0, 'file_count' => 0,
'size' => 0 'size' => 0
]; ];
if ($stats['exists']) { if ($stats['exists']) {
$files = glob(trailingslashit($dir) . '*.jpg'); $files = glob(trailingslashit($site_dir) . '*.jpg');
$stats['file_count'] = count($files ?: []); $stats['file_count'] = count($files ?: []);
foreach ($files ?: [] as $file) { foreach ($files ?: [] as $file) {
$stats['size'] += filesize($file); $stats['size'] += filesize($file);
} }
$stats['size'] = size_format($stats['size']); $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(); ob_start();
?> ?>
@ -587,9 +771,123 @@ class Cache {
<?php <?php
return ob_get_clean(); return ob_get_clean();
} }
}
class Shortcode { public static function check_all_cache_status() {
$base_dir = wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR);
$global_stats = [
'path' => $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();
?>
<div class="network-cache-stats">
<h4><?php _e('全局缓存统计', 'wpavatar'); ?></h4>
<p><?php printf(__('缓存根目录: %s', 'wpavatar'), esc_html($global_stats['path'])); ?></p>
<p><?php printf(__('目录存在: %s', 'wpavatar'), $global_stats['exists'] ? __('是', 'wpavatar') : __('否', 'wpavatar')); ?></p>
<p><?php printf(__('目录可写: %s', 'wpavatar'), $global_stats['writable'] ? __('是', 'wpavatar') : __('否', 'wpavatar')); ?></p>
<p><?php printf(__('站点缓存目录数: %d', 'wpavatar'), $global_stats['site_count']); ?></p>
<p><?php printf(__('总缓存文件数: %d', 'wpavatar'), $global_stats['total_files']); ?></p>
<p><?php printf(__('总缓存大小: %s', 'wpavatar'), size_format($global_stats['total_size'])); ?></p>
<?php if (!empty($site_stats)): ?>
<h4><?php _e('站点缓存详情', 'wpavatar'); ?></h4>
<table class="widefat striped" style="margin-top: 10px;">
<thead>
<tr>
<th><?php _e('站点', 'wpavatar'); ?></th>
<th><?php _e('文件数', 'wpavatar'); ?></th>
<th><?php _e('大小', 'wpavatar'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($site_stats as $stat): ?>
<tr>
<td><?php echo esc_html($stat['name']); ?></td>
<td><?php echo esc_html($stat['files']); ?></td>
<td><?php echo esc_html($stat['size']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
}
}
class Shortcode {
public static function init() { public static function init() {
add_shortcode('wpavatar', [__CLASS__, 'render_avatar']); add_shortcode('wpavatar', [__CLASS__, 'render_avatar']);
add_shortcode('wpavatar_username', [__CLASS__, 'render_username']); add_shortcode('wpavatar_username', [__CLASS__, 'render_username']);
@ -597,9 +895,9 @@ class Shortcode {
} }
public static function render_avatar($atts) { public static function render_avatar($atts) {
$default_size = get_option('wpavatar_shortcode_size', 96); $default_size = wpavatar_get_option('wpavatar_shortcode_size', 96);
$default_class = get_option('wpavatar_shortcode_class', 'wpavatar'); $default_class = wpavatar_get_option('wpavatar_shortcode_class', 'wpavatar');
$default_shape = get_option('wpavatar_shortcode_shape', 'square'); $default_shape = wpavatar_get_option('wpavatar_shortcode_shape', 'square');
$atts = shortcode_atts([ $atts = shortcode_atts([
'size' => $default_size, 'size' => $default_size,
@ -687,4 +985,4 @@ class Shortcode {
return self::render_avatar($atts); return self::render_avatar($atts);
} }
} }

1134
includes/multisite.php Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,22 +1,24 @@
<?php <?php
/** /**
* Plugin Name: WPAvatar * Plugin Name: WPAvatar
* Version: 1.6.3 * Version: 1.8.1
* Plugin URI: https://wpavatar.com/download * Plugin URI: https://wpavatar.com/download
* Description: Replace Gravatar with Cravatar, a perfect replacement of Gravatar in China. * Description: Replace Gravatar with Cravatar, a perfect replacement of Gravatar in China.
* Author: WPfanyi * Author: WPfanyi
* Author URI: https://wpfanyi.com/ * Author URI: https://wpfanyi.com/
* Text Domain: wpavatar * Text Domain: wpavatar
* Domain Path: /languages * Domain Path: /languages
* Network: true
*/ */
defined('ABSPATH') || exit; defined('ABSPATH') || exit;
define('WPAVATAR_VERSION', '1.6.3'); define('WPAVATAR_VERSION', '1.8.0');
define('WPAVATAR_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('WPAVATAR_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('WPAVATAR_PLUGIN_URL', plugin_dir_url(__FILE__)); define('WPAVATAR_PLUGIN_URL', plugin_dir_url(__FILE__));
define('WPAVATAR_CACHE_DIR', WP_CONTENT_DIR . '/uploads/cravatar'); define('WPAVATAR_CACHE_DIR', WP_CONTENT_DIR . '/uploads/cravatar');
// Create necessary plugin directories if they don't exist
if (!file_exists(WPAVATAR_PLUGIN_DIR . 'assets')) { if (!file_exists(WPAVATAR_PLUGIN_DIR . 'assets')) {
wp_mkdir_p(WPAVATAR_PLUGIN_DIR . 'assets'); wp_mkdir_p(WPAVATAR_PLUGIN_DIR . 'assets');
} }
@ -55,19 +57,31 @@ if (!file_exists(WPAVATAR_PLUGIN_DIR . 'includes')) {
} }
} }
// Include core files
require_once WPAVATAR_PLUGIN_DIR . 'includes/core.php'; require_once WPAVATAR_PLUGIN_DIR . 'includes/core.php';
require_once WPAVATAR_PLUGIN_DIR . 'includes/admin.php'; require_once WPAVATAR_PLUGIN_DIR . 'includes/admin.php';
require_once WPAVATAR_PLUGIN_DIR . 'includes/multisite.php';
// Register AJAX actions
add_action('wp_ajax_wpavatar_purge_cache', 'wpavatar_purge_cache_ajax'); add_action('wp_ajax_wpavatar_purge_cache', 'wpavatar_purge_cache_ajax');
function wpavatar_purge_cache_ajax() { function wpavatar_purge_cache_ajax() {
check_ajax_referer('wpavatar_admin_nonce', 'nonce'); check_ajax_referer('wpavatar_admin_nonce', 'nonce');
// Check user capability
if (!current_user_can('manage_options')) { if (!current_user_can('manage_options')) {
wp_send_json_error(__('权限不足', 'wpavatar')); wp_send_json_error(__('权限不足', 'wpavatar'));
return; return;
} }
$dir = get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR); // Get cache directory path
$dir = wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR);
// Add blog-specific directory if multisite
if (is_multisite()) {
$blog_id = get_current_blog_id();
$dir = trailingslashit($dir) . 'site-' . $blog_id . '/';
}
$dir = rtrim($dir, '/\\') . '/'; $dir = rtrim($dir, '/\\') . '/';
if (!file_exists($dir) || !is_dir($dir)) { if (!file_exists($dir) || !is_dir($dir)) {
@ -91,6 +105,60 @@ function wpavatar_purge_cache_ajax() {
wp_send_json_success(sprintf(__('已清空 %d 个缓存文件', 'wpavatar'), $count)); wp_send_json_success(sprintf(__('已清空 %d 个缓存文件', 'wpavatar'), $count));
} }
add_action('wp_ajax_wpavatar_purge_all_cache', 'wpavatar_purge_all_cache_ajax');
function wpavatar_purge_all_cache_ajax() {
check_ajax_referer('wpavatar_admin_nonce', 'nonce');
// Check user capability for network admin
if (!current_user_can('manage_network_options')) {
wp_send_json_error(__('权限不足', 'wpavatar'));
return;
}
// Get base cache directory path
$base_dir = wpavatar_get_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR);
$base_dir = rtrim($base_dir, '/\\') . '/';
if (!file_exists($base_dir) || !is_dir($base_dir)) {
wp_send_json_error(__('缓存目录不存在', 'wpavatar'));
return;
}
$count = 0;
// Get all site cache directories
$site_dirs = glob($base_dir . 'site-*', GLOB_ONLYDIR);
if ($site_dirs) {
foreach ($site_dirs as $site_dir) {
$files = glob($site_dir . '/*.jpg');
if ($files) {
foreach ($files as $file) {
if (file_exists($file)) {
if (@unlink($file)) {
$count++;
}
}
}
}
}
}
// Also check legacy non-site specific files
$legacy_files = glob($base_dir . '*.jpg');
if ($legacy_files) {
foreach ($legacy_files as $file) {
if (file_exists($file)) {
if (@unlink($file)) {
$count++;
}
}
}
}
wp_send_json_success(sprintf(__('已清空所有站点的 %d 个缓存文件', 'wpavatar'), $count));
}
add_action('wp_ajax_wpavatar_check_cache', 'wpavatar_check_cache_ajax'); add_action('wp_ajax_wpavatar_check_cache', 'wpavatar_check_cache_ajax');
function wpavatar_check_cache_ajax() { function wpavatar_check_cache_ajax() {
check_ajax_referer('wpavatar_admin_nonce', 'nonce'); check_ajax_referer('wpavatar_admin_nonce', 'nonce');
@ -104,9 +172,24 @@ function wpavatar_check_cache_ajax() {
wp_send_json_success($result); wp_send_json_success($result);
} }
add_action('wp_ajax_wpavatar_check_all_cache', 'wpavatar_check_all_cache_ajax');
function wpavatar_check_all_cache_ajax() {
check_ajax_referer('wpavatar_admin_nonce', 'nonce');
if (!current_user_can('manage_network_options')) {
wp_send_json_error(__('权限不足', 'wpavatar'));
return;
}
$result = \WPAvatar\Cache::check_all_cache_status();
wp_send_json_success($result);
}
add_action('plugins_loaded', function () { add_action('plugins_loaded', function () {
// Load text domain for translations
load_plugin_textdomain('wpavatar', false, dirname(plugin_basename(__FILE__)) . '/languages'); load_plugin_textdomain('wpavatar', false, dirname(plugin_basename(__FILE__)) . '/languages');
// Create default avatar if it doesn't exist
$default_avatar = WPAVATAR_PLUGIN_DIR . 'assets/images/default-avatar.png'; $default_avatar = WPAVATAR_PLUGIN_DIR . 'assets/images/default-avatar.png';
if (!file_exists($default_avatar)) { if (!file_exists($default_avatar)) {
$placeholder = WPAVATAR_PLUGIN_DIR . 'assets/images/placeholder-avatar.png'; $placeholder = WPAVATAR_PLUGIN_DIR . 'assets/images/placeholder-avatar.png';
@ -127,6 +210,7 @@ add_action('plugins_loaded', function () {
} }
} }
// Fetch Cravatar logo if it doesn't exist
$cravatar_logo = WPAVATAR_PLUGIN_DIR . 'assets/images/cravatar-logo.png'; $cravatar_logo = WPAVATAR_PLUGIN_DIR . 'assets/images/cravatar-logo.png';
if (!file_exists($cravatar_logo) && function_exists('file_get_contents')) { if (!file_exists($cravatar_logo) && function_exists('file_get_contents')) {
$logo_url = 'https://cn.cravatar.com/avatar/00000000000000000000000000000000?d=cravatar_logo'; $logo_url = 'https://cn.cravatar.com/avatar/00000000000000000000000000000000?d=cravatar_logo';
@ -137,25 +221,49 @@ add_action('plugins_loaded', function () {
} }
} }
// Initialize multisite network support first
if (is_multisite()) { if (is_multisite()) {
\WPAvatar\Network::init(); \WPAvatar\Network::init();
} }
// Initialize core components
\WPAvatar\Core::init(); \WPAvatar\Core::init();
\WPAvatar\Cravatar::init(); \WPAvatar\Cravatar::init();
\WPAvatar\Cache::init(); \WPAvatar\Cache::init();
\WPAvatar\Shortcode::init(); \WPAvatar\Shortcode::init();
// Initialize admin settings (will be conditionally disabled in multisite if network managed)
if (is_admin()) { if (is_admin()) {
\WPAvatar\Settings::init(); // Check if we should initialize settings in site admin
$should_init_settings = true;
if (is_multisite() && get_site_option('wpavatar_network_enforce', 0) && !is_network_admin()) {
$should_init_settings = false;
} }
if ($should_init_settings) {
\WPAvatar\Settings::init();
}
}
// Filter text to replace Gravatar references with Cravatar
add_filter('gettext', 'wpavatar_replace_gravatar_text', 20, 3); add_filter('gettext', 'wpavatar_replace_gravatar_text', 20, 3);
add_filter('ngettext', 'wpavatar_replace_gravatar_text_plural', 20, 4); add_filter('ngettext', 'wpavatar_replace_gravatar_text_plural', 20, 4);
}); });
/**
* Replace Gravatar text with Cravatar in translations
*
* @param string $translated_text Translated text
* @param string $text Original text
* @param string $domain Text domain
* @return string Modified translated text
*/
function wpavatar_replace_gravatar_text($translated_text, $text, $domain) { function wpavatar_replace_gravatar_text($translated_text, $text, $domain) {
if (!get_option('wpavatar_enable_cravatar', 1)) { // Get enable_cravatar setting
$enable_cravatar = wpavatar_get_option('wpavatar_enable_cravatar', 1);
if (!$enable_cravatar) {
return $translated_text; return $translated_text;
} }
@ -179,8 +287,20 @@ function wpavatar_replace_gravatar_text($translated_text, $text, $domain) {
return $translated_text; return $translated_text;
} }
/**
* Replace Gravatar text with Cravatar in plural translations
*
* @param string $translated_text Translated text
* @param string $single Singular text
* @param string $plural Plural text
* @param int $number Number for plural form
* @return string Modified translated text
*/
function wpavatar_replace_gravatar_text_plural($translated_text, $single, $plural, $number) { function wpavatar_replace_gravatar_text_plural($translated_text, $single, $plural, $number) {
if (!get_option('wpavatar_enable_cravatar', 1)) { // Get enable_cravatar setting
$enable_cravatar = wpavatar_get_option('wpavatar_enable_cravatar', 1);
if (!$enable_cravatar) {
return $translated_text; return $translated_text;
} }
@ -202,7 +322,39 @@ function wpavatar_replace_gravatar_text_plural($translated_text, $single, $plura
return $translated_text; return $translated_text;
} }
/**
* Helper function to get the correct option based on multisite status
*
* @param string $option_name Option name
* @param mixed $default Default value
* @return mixed Option value
*/
function wpavatar_get_option($option_name, $default = false) {
if (is_multisite()) {
// Check if network settings are enabled
if (get_site_option('wpavatar_network_enabled', 1)) {
// Check if this option is controlled by network
$network_controlled_options = get_site_option('wpavatar_network_controlled_options', array());
// Convert to array if it's a string (for backward compatibility)
if (!is_array($network_controlled_options)) {
$network_controlled_options = explode(',', $network_controlled_options);
}
// If this option is controlled by network or network enforces all settings
if (in_array($option_name, $network_controlled_options) || get_site_option('wpavatar_network_enforce', 0)) {
return get_site_option($option_name, $default);
}
}
}
// Default to site option
return get_option($option_name, $default);
}
// Register activation hook
register_activation_hook(__FILE__, function() { register_activation_hook(__FILE__, function() {
// Set default options for single site
add_option('wpavatar_enable_cravatar', 1); add_option('wpavatar_enable_cravatar', 1);
add_option('wpavatar_cdn_type', 'cravatar_route'); add_option('wpavatar_cdn_type', 'cravatar_route');
add_option('wpavatar_cravatar_route', 'cravatar.com'); add_option('wpavatar_cravatar_route', 'cravatar.com');
@ -223,13 +375,16 @@ register_activation_hook(__FILE__, function() {
add_option('wpavatar_shortcode_class', 'wpavatar'); add_option('wpavatar_shortcode_class', 'wpavatar');
add_option('wpavatar_shortcode_shape', 'square'); add_option('wpavatar_shortcode_shape', 'square');
// Create cache directory
wp_mkdir_p(WPAVATAR_CACHE_DIR); wp_mkdir_p(WPAVATAR_CACHE_DIR);
// Create index.php to prevent directory listing
$index_file = rtrim(WPAVATAR_CACHE_DIR, '/\\') . '/index.php'; $index_file = rtrim(WPAVATAR_CACHE_DIR, '/\\') . '/index.php';
if (!file_exists($index_file)) { if (!file_exists($index_file)) {
@file_put_contents($index_file, '<?php // Silence is golden.'); @file_put_contents($index_file, '<?php // Silence is golden.');
} }
// Create .htaccess to configure cache
$htaccess_file = rtrim(WPAVATAR_CACHE_DIR, '/\\') . '/.htaccess'; $htaccess_file = rtrim(WPAVATAR_CACHE_DIR, '/\\') . '/.htaccess';
if (!file_exists($htaccess_file)) { if (!file_exists($htaccess_file)) {
$htaccess_content = "# Prevent directory listing\n"; $htaccess_content = "# Prevent directory listing\n";
@ -242,17 +397,127 @@ register_activation_hook(__FILE__, function() {
@file_put_contents($htaccess_file, $htaccess_content); @file_put_contents($htaccess_file, $htaccess_content);
} }
// Set default options for multisite
if (is_multisite()) { if (is_multisite()) {
// Network settings
add_site_option('wpavatar_network_enabled', 1); add_site_option('wpavatar_network_enabled', 1);
add_site_option('wpavatar_network_cdn_type', 'cravatar_route'); add_site_option('wpavatar_network_enforce', 0);
add_site_option('wpavatar_network_cravatar_route', 'cravatar.com');
// Define default network controlled options
$default_controlled = array(
'wpavatar_enable_cravatar',
'wpavatar_cdn_type',
'wpavatar_cravatar_route',
'wpavatar_third_party_mirror',
'wpavatar_custom_cdn'
);
add_site_option('wpavatar_network_controlled_options', $default_controlled);
// Copy regular options to network options
foreach ([
'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'
] as $option_name) {
add_site_option($option_name, get_option($option_name));
} }
// If current site is not the main site, create site-specific cache dir
if (!is_main_site()) {
$blog_id = get_current_blog_id();
$cache_dir = trailingslashit(WPAVATAR_CACHE_DIR) . 'site-' . $blog_id;
wp_mkdir_p($cache_dir);
$index_file = $cache_dir . '/index.php';
if (!file_exists($index_file)) {
@file_put_contents($index_file, '<?php // Silence is golden.');
}
}
}
// Schedule daily cache purge
if (!wp_next_scheduled('wpavatar_purge_cache')) { if (!wp_next_scheduled('wpavatar_purge_cache')) {
wp_schedule_event(time(), 'daily', 'wpavatar_purge_cache'); wp_schedule_event(time(), 'daily', 'wpavatar_purge_cache');
} }
}); });
// Register deactivation hook
register_deactivation_hook(__FILE__, function() { register_deactivation_hook(__FILE__, function() {
// Clear scheduled events
wp_clear_scheduled_hook('wpavatar_purge_cache'); wp_clear_scheduled_hook('wpavatar_purge_cache');
}); });
// 确保 WPAvatar 插件已激活
add_action('plugins_loaded', function() {
if (class_exists('WPAvatar\\Cravatar')) {
// 添加兼容性过滤器,确保 WPAvatar 的优先级高于 WP-China-Yes
add_action('after_setup_theme', function() {
// 检查 WP-China-Yes 插件是否激活
if (class_exists('WenPai\\ChinaYes\\Service\\Super') || defined('CHINA_YES_VERSION')) {
// 移除 WP-China-Yes 的头像相关过滤器
global $wp_filter;
$filters_to_check = [
'get_avatar_url',
'um_user_avatar_url_filter',
'bp_gravatar_url',
'user_profile_picture_description',
'avatar_defaults'
];
foreach ($filters_to_check as $filter_name) {
if (isset($wp_filter[$filter_name])) {
foreach ($wp_filter[$filter_name]->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 '<div class="notice notice-info is-dismissible">';
echo '<p>检测到文派叶子WPCY.COM插件WPAvatar 生态组件兼容性补丁已生效,确保文派头像设置优先。</p>';
echo '</div>';
}
});
}
}, 5);
}
});