Updated plugin version to 1.9.4 in main files and readme. Enhanced security by adding proper escaping to all URL outputs in the admin interface to address an XSS vulnerability. Updated changelog and stable tag in readme to reflect these changes.
487 lines
17 KiB
PHP
487 lines
17 KiB
PHP
<?php
|
|
/**
|
|
* Plugin Name: WPAvatar
|
|
* Version: 1.9.4
|
|
* Plugin URI: https://wpavatar.com/download
|
|
* Description: Replace Gravatar with Cravatar, a perfect replacement of Gravatar in China.
|
|
* Author: WPfanyi
|
|
* Author URI: https://wpfanyi.com/
|
|
* Text Domain: wpavatar
|
|
* Domain Path: /languages
|
|
* Network: true
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
define('WPAVATAR_VERSION', '1.9.4');
|
|
define('WPAVATAR_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
|
define('WPAVATAR_PLUGIN_URL', plugin_dir_url(__FILE__));
|
|
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')) {
|
|
wp_mkdir_p(WPAVATAR_PLUGIN_DIR . 'assets');
|
|
}
|
|
|
|
if (!file_exists(WPAVATAR_PLUGIN_DIR . 'assets/css')) {
|
|
wp_mkdir_p(WPAVATAR_PLUGIN_DIR . 'assets/css');
|
|
|
|
$css_file = WPAVATAR_PLUGIN_DIR . 'admin.css';
|
|
if (file_exists($css_file)) {
|
|
copy($css_file, WPAVATAR_PLUGIN_DIR . 'assets/css/admin.css');
|
|
}
|
|
}
|
|
|
|
if (!file_exists(WPAVATAR_PLUGIN_DIR . 'assets/js')) {
|
|
wp_mkdir_p(WPAVATAR_PLUGIN_DIR . 'assets/js');
|
|
|
|
$js_file = WPAVATAR_PLUGIN_DIR . 'admin.js';
|
|
if (file_exists($js_file)) {
|
|
copy($js_file, WPAVATAR_PLUGIN_DIR . 'assets/js/admin.js');
|
|
}
|
|
}
|
|
|
|
if (!file_exists(WPAVATAR_PLUGIN_DIR . 'assets/images')) {
|
|
wp_mkdir_p(WPAVATAR_PLUGIN_DIR . 'assets/images');
|
|
}
|
|
|
|
// 检查必要的插件目录是否存在
|
|
if (!file_exists(WPAVATAR_PLUGIN_DIR . 'includes')) {
|
|
wp_mkdir_p(WPAVATAR_PLUGIN_DIR . 'includes');
|
|
|
|
if (file_exists(WPAVATAR_PLUGIN_DIR . 'core.php')) {
|
|
copy(WPAVATAR_PLUGIN_DIR . 'core.php', WPAVATAR_PLUGIN_DIR . 'includes/core.php');
|
|
}
|
|
|
|
if (file_exists(WPAVATAR_PLUGIN_DIR . 'admin.php')) {
|
|
copy(WPAVATAR_PLUGIN_DIR . 'admin.php', WPAVATAR_PLUGIN_DIR . 'includes/admin.php');
|
|
}
|
|
|
|
// 创建营销组件文件
|
|
if (file_exists(WPAVATAR_PLUGIN_DIR . 'marketing.php')) {
|
|
$marketing_content = file_get_contents(WPAVATAR_PLUGIN_DIR . 'marketing.php');
|
|
file_put_contents(WPAVATAR_PLUGIN_DIR . 'includes/marketing.php', $marketing_content);
|
|
}
|
|
}
|
|
|
|
// 包含核心文件
|
|
require_once WPAVATAR_PLUGIN_DIR . 'includes/core.php';
|
|
require_once WPAVATAR_PLUGIN_DIR . 'includes/admin.php';
|
|
require_once WPAVATAR_PLUGIN_DIR . 'includes/multisite.php';
|
|
require_once WPAVATAR_PLUGIN_DIR . 'includes/marketing.php';
|
|
require_once WPAVATAR_PLUGIN_DIR . 'includes/wpcy-compatibility.php';
|
|
|
|
// Register AJAX actions
|
|
add_action('wp_ajax_wpavatar_purge_cache', 'wpavatar_purge_cache_ajax');
|
|
function wpavatar_purge_cache_ajax() {
|
|
check_ajax_referer('wpavatar_admin_nonce', 'nonce');
|
|
|
|
// Check user capability
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(__('权限不足', 'wpavatar'));
|
|
return;
|
|
}
|
|
|
|
// 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, '/\\') . '/';
|
|
|
|
if (!file_exists($dir) || !is_dir($dir)) {
|
|
wp_send_json_error(__('缓存目录不存在', 'wpavatar'));
|
|
return;
|
|
}
|
|
|
|
$files = glob($dir . '*.jpg');
|
|
$count = 0;
|
|
|
|
if ($files) {
|
|
foreach ($files as $file) {
|
|
if (strpos($file, $dir) === 0 && file_exists($file)) {
|
|
if (@unlink($file)) {
|
|
$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');
|
|
function wpavatar_check_cache_ajax() {
|
|
check_ajax_referer('wpavatar_admin_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_send_json_error(__('权限不足', 'wpavatar'));
|
|
return;
|
|
}
|
|
|
|
$result = \WPAvatar\Cache::check_cache_status();
|
|
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 () {
|
|
// Load text domain for translations
|
|
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';
|
|
if (!file_exists($default_avatar)) {
|
|
$placeholder = WPAVATAR_PLUGIN_DIR . 'assets/images/placeholder-avatar.png';
|
|
|
|
if (file_exists($placeholder)) {
|
|
copy($placeholder, $default_avatar);
|
|
} else {
|
|
$avatar_image = imagecreatetruecolor(96, 96);
|
|
$bg_color = imagecolorallocate($avatar_image, 238, 238, 238);
|
|
$text_color = imagecolorallocate($avatar_image, 68, 68, 68);
|
|
|
|
imagefill($avatar_image, 0, 0, $bg_color);
|
|
imagestring($avatar_image, 5, 30, 40, 'Avatar', $text_color);
|
|
|
|
wp_mkdir_p(dirname($default_avatar));
|
|
imagepng($avatar_image, $default_avatar);
|
|
imagedestroy($avatar_image);
|
|
}
|
|
}
|
|
|
|
// Fetch Cravatar logo if it doesn't exist
|
|
$cravatar_logo = WPAVATAR_PLUGIN_DIR . 'assets/images/cravatar-logo.png';
|
|
if (!file_exists($cravatar_logo) && function_exists('file_get_contents')) {
|
|
$logo_url = 'https://cn.cravatar.com/avatar/00000000000000000000000000000000?d=cravatar_logo';
|
|
$logo_data = @file_get_contents($logo_url);
|
|
if ($logo_data) {
|
|
wp_mkdir_p(dirname($cravatar_logo));
|
|
@file_put_contents($cravatar_logo, $logo_data);
|
|
}
|
|
}
|
|
|
|
// Initialize multisite network support first
|
|
if (is_multisite()) {
|
|
\WPAvatar\Network::init();
|
|
}
|
|
|
|
// Initialize compatibility layer
|
|
\WPAvatar\Compatibility::init();
|
|
|
|
// Initialize core components
|
|
\WPAvatar\Core::init();
|
|
\WPAvatar\Cravatar::init();
|
|
\WPAvatar\Cache::init();
|
|
\WPAvatar\Shortcode::init();
|
|
|
|
// Initialize Marketing component
|
|
\WPAvatar\Marketing::init();
|
|
|
|
// Initialize admin settings (will be conditionally disabled in multisite if network managed)
|
|
if (is_admin()) {
|
|
// 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('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) {
|
|
// Get enable_cravatar setting
|
|
$enable_cravatar = wpavatar_get_option('wpavatar_enable_cravatar', 1);
|
|
|
|
if (!$enable_cravatar) {
|
|
return $translated_text;
|
|
}
|
|
|
|
$current_screen = function_exists('get_current_screen') ? get_current_screen() : null;
|
|
$screen_id = $current_screen ? $current_screen->id : '';
|
|
|
|
$is_discussion_page = ($screen_id === 'options-discussion' ||
|
|
(isset($_GET['page']) && $_GET['page'] === 'discussion'));
|
|
|
|
$is_comments_page = ($screen_id === 'edit-comments' ||
|
|
$screen_id === 'comment');
|
|
|
|
$is_profile_page = ($screen_id === 'profile' ||
|
|
$screen_id === 'user-edit');
|
|
|
|
if ($is_discussion_page || $is_comments_page || $is_profile_page) {
|
|
$translated_text = str_replace('Gravatar', 'Cravatar', $translated_text);
|
|
$translated_text = str_replace('gravatar', 'cravatar', $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) {
|
|
// Get enable_cravatar setting
|
|
$enable_cravatar = wpavatar_get_option('wpavatar_enable_cravatar', 1);
|
|
|
|
if (!$enable_cravatar) {
|
|
return $translated_text;
|
|
}
|
|
|
|
$current_screen = function_exists('get_current_screen') ? get_current_screen() : null;
|
|
$screen_id = $current_screen ? $current_screen->id : '';
|
|
|
|
$is_relevant_page = ($screen_id === 'options-discussion' ||
|
|
$screen_id === 'edit-comments' ||
|
|
$screen_id === 'comment' ||
|
|
$screen_id === 'profile' ||
|
|
$screen_id === 'user-edit' ||
|
|
(isset($_GET['page']) && $_GET['page'] === 'discussion'));
|
|
|
|
if ($is_relevant_page) {
|
|
$translated_text = str_replace('Gravatar', 'Cravatar', $translated_text);
|
|
$translated_text = str_replace('gravatar', 'cravatar', $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() {
|
|
// Set default options for single site
|
|
add_option('wpavatar_enable_cravatar', 1);
|
|
add_option('wpavatar_cdn_type', 'cravatar_route');
|
|
add_option('wpavatar_cravatar_route', 'cravatar.com');
|
|
add_option('wpavatar_third_party_mirror', 'weavatar.com');
|
|
add_option('wpavatar_custom_cdn', '');
|
|
add_option('wpavatar_hash_method', 'md5');
|
|
add_option('wpavatar_timeout', 5);
|
|
|
|
add_option('wpavatar_enable_cache', 1);
|
|
add_option('wpavatar_cache_path', WPAVATAR_CACHE_DIR);
|
|
add_option('wpavatar_cache_expire', 15);
|
|
|
|
add_option('wpavatar_seo_alt', '%s的头像');
|
|
add_option('wpavatar_fallback_mode', 1);
|
|
add_option('wpavatar_fallback_avatar', 'default');
|
|
|
|
add_option('wpavatar_shortcode_size', 96);
|
|
add_option('wpavatar_shortcode_class', 'wpavatar');
|
|
add_option('wpavatar_shortcode_shape', 'square');
|
|
|
|
// 添加营销组件默认设置
|
|
add_option('wpavatar_commenters_count', 15);
|
|
add_option('wpavatar_commenters_size', 45);
|
|
add_option('wpavatar_users_count', 15);
|
|
add_option('wpavatar_users_size', 40);
|
|
|
|
// Create cache directory
|
|
wp_mkdir_p(WPAVATAR_CACHE_DIR);
|
|
|
|
// Create index.php to prevent directory listing
|
|
$index_file = rtrim(WPAVATAR_CACHE_DIR, '/\\') . '/index.php';
|
|
if (!file_exists($index_file)) {
|
|
@file_put_contents($index_file, '<?php // Silence is golden.');
|
|
}
|
|
|
|
// Create .htaccess to configure cache
|
|
$htaccess_file = rtrim(WPAVATAR_CACHE_DIR, '/\\') . '/.htaccess';
|
|
if (!file_exists($htaccess_file)) {
|
|
$htaccess_content = "# Prevent directory listing\n";
|
|
$htaccess_content .= "Options -Indexes\n";
|
|
$htaccess_content .= "# Cache images for one week\n";
|
|
$htaccess_content .= "<IfModule mod_expires.c>\n";
|
|
$htaccess_content .= "ExpiresActive On\n";
|
|
$htaccess_content .= "ExpiresByType image/jpeg \"access plus 1 week\"\n";
|
|
$htaccess_content .= "</IfModule>\n";
|
|
@file_put_contents($htaccess_file, $htaccess_content);
|
|
}
|
|
|
|
// Set default options for multisite
|
|
if (is_multisite()) {
|
|
// Network settings
|
|
add_site_option('wpavatar_network_enabled', 1);
|
|
add_site_option('wpavatar_network_enforce', 0);
|
|
|
|
// 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',
|
|
// 营销组件设置
|
|
'wpavatar_commenters_count',
|
|
'wpavatar_commenters_size',
|
|
'wpavatar_users_count',
|
|
'wpavatar_users_size'
|
|
] 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')) {
|
|
wp_schedule_event(time(), 'daily', 'wpavatar_purge_cache');
|
|
}
|
|
});
|
|
|
|
// Register deactivation hook
|
|
register_deactivation_hook(__FILE__, function() {
|
|
// Clear scheduled events
|
|
wp_clear_scheduled_hook('wpavatar_purge_cache');
|
|
});
|