449 lines
17 KiB
PHP
449 lines
17 KiB
PHP
<?php
|
||
/**
|
||
* Plugin Name: HTACCESS Manager
|
||
* Plugin URI: https://lhcy.org
|
||
* Description: 根据需求生成.htaccess重定向规则
|
||
* Version: 1.0
|
||
* Author: 林海草原
|
||
* Text Domain: htaccess-manager
|
||
* License: Private
|
||
*
|
||
*/
|
||
|
||
// 防止直接访问
|
||
if (!defined('ABSPATH')) {
|
||
exit;
|
||
}
|
||
|
||
class HTACCESS_Manager {
|
||
|
||
private $options;
|
||
|
||
public function __construct() {
|
||
$this->options = get_option('htaccess_manager_options');
|
||
add_action('admin_menu', array($this, 'add_admin_menu'));
|
||
add_action('admin_init', array($this, 'admin_init'));
|
||
add_action('admin_notices', array($this, 'admin_notices'));
|
||
register_activation_hook(__FILE__, array($this, 'activate'));
|
||
register_deactivation_hook(__FILE__, array($this, 'deactivate'));
|
||
register_uninstall_hook(__FILE__, array('HTACCESS_Manager', 'uninstall'));
|
||
}
|
||
|
||
public function activate() {
|
||
// 创建must-use插件目录
|
||
$mu_plugins_dir = WP_CONTENT_DIR . '/mu-plugins';
|
||
if (!file_exists($mu_plugins_dir)) {
|
||
wp_mkdir_p($mu_plugins_dir);
|
||
}
|
||
|
||
// 生成初始选项
|
||
if (false === $this->options) {
|
||
$default_options = array(
|
||
'redirect_type' => 'www_to_root',
|
||
'is_subdomain' => false,
|
||
'auto_update' => true
|
||
);
|
||
update_option('htaccess_manager_options', $default_options);
|
||
}
|
||
|
||
$this->update_htaccess();
|
||
$this->create_mu_plugin();
|
||
}
|
||
|
||
public function deactivate() {
|
||
// 从.htaccess中安全移除本插件的规则
|
||
$this->safe_remove_htaccess_rules();
|
||
|
||
// 清理mu-plugin
|
||
$mu_plugin_file = WP_CONTENT_DIR . '/mu-plugins/htaccess-helper.php';
|
||
if (file_exists($mu_plugin_file)) {
|
||
unlink($mu_plugin_file);
|
||
}
|
||
}
|
||
|
||
public static function uninstall() {
|
||
// 删除选项
|
||
delete_option('htaccess_manager_options');
|
||
|
||
// 从.htaccess中安全移除规则
|
||
self::safe_remove_htaccess_rules_static();
|
||
|
||
// 清理mu-plugin
|
||
$mu_plugin_file = WP_CONTENT_DIR . '/mu-plugins/htaccess-helper.php';
|
||
if (file_exists($mu_plugin_file)) {
|
||
unlink($mu_plugin_file);
|
||
}
|
||
}
|
||
|
||
public function add_admin_menu() {
|
||
add_options_page(
|
||
'HTACCESS 管理',
|
||
'HTACCESS 管理',
|
||
'manage_options',
|
||
'htaccess-manager',
|
||
array($this, 'admin_page')
|
||
);
|
||
}
|
||
|
||
public function admin_init() {
|
||
register_setting('htaccess_manager', 'htaccess_manager_options');
|
||
|
||
add_settings_section(
|
||
'htaccess_manager_section',
|
||
'重定向设置',
|
||
array($this, 'section_callback'),
|
||
'htaccess_manager'
|
||
);
|
||
|
||
add_settings_field(
|
||
'is_subdomain',
|
||
'是否为二级域名',
|
||
array($this, 'is_subdomain_callback'),
|
||
'htaccess_manager',
|
||
'htaccess_manager_section'
|
||
);
|
||
|
||
add_settings_field(
|
||
'redirect_type',
|
||
'重定向类型',
|
||
array($this, 'redirect_type_callback'),
|
||
'htaccess_manager',
|
||
'htaccess_manager_section'
|
||
);
|
||
|
||
add_settings_field(
|
||
'auto_update',
|
||
'自动更新',
|
||
array($this, 'auto_update_callback'),
|
||
'htaccess_manager',
|
||
'htaccess_manager_section'
|
||
);
|
||
}
|
||
|
||
public function section_callback() {
|
||
echo '<p>配置您的网站重定向规则。HTTPS重定向是强制的。</p>';
|
||
}
|
||
|
||
public function is_subdomain_callback() {
|
||
$value = isset($this->options['is_subdomain']) ? $this->options['is_subdomain'] : false;
|
||
?>
|
||
<label>
|
||
<input type="checkbox" name="htaccess_manager_options[is_subdomain]" value="1" <?php checked(1, $value); ?> />
|
||
我的网站使用二级域名(如:sub.domain.com)
|
||
</label>
|
||
<p class="description">如果启用,将只强制HTTPS,不处理www和根域名之间的跳转。</p>
|
||
<?php
|
||
}
|
||
|
||
public function redirect_type_callback() {
|
||
$value = isset($this->options['redirect_type']) ? $this->options['redirect_type'] : 'www_to_root';
|
||
?>
|
||
<fieldset>
|
||
<label>
|
||
<input type="radio" name="htaccess_manager_options[redirect_type]" value="www_to_root" <?php checked('www_to_root', $value); ?> <?php echo isset($this->options['is_subdomain']) && $this->options['is_subdomain'] ? 'disabled' : ''; ?> />
|
||
www跳转到根域名(www.example.com → example.com)
|
||
</label><br>
|
||
<label>
|
||
<input type="radio" name="htaccess_manager_options[redirect_type]" value="root_to_www" <?php checked('root_to_www', $value); ?> <?php echo isset($this->options['is_subdomain']) && $this->options['is_subdomain'] ? 'disabled' : ''; ?> />
|
||
根域名跳转到www(example.com → www.example.com)
|
||
</label>
|
||
</fieldset>
|
||
<?php
|
||
}
|
||
|
||
public function auto_update_callback() {
|
||
$value = isset($this->options['auto_update']) ? $this->options['auto_update'] : true;
|
||
?>
|
||
<label>
|
||
<input type="checkbox" name="htaccess_manager_options[auto_update]" value="1" <?php checked(1, $value); ?> />
|
||
设置更改时自动更新.htaccess文件
|
||
</label>
|
||
<?php
|
||
}
|
||
|
||
public function admin_page() {
|
||
?>
|
||
<div class="wrap">
|
||
<h1>HTACCESS 管理</h1>
|
||
|
||
<form action="options.php" method="post">
|
||
<?php
|
||
settings_fields('htaccess_manager');
|
||
do_settings_sections('htaccess_manager');
|
||
submit_button('保存设置');
|
||
?>
|
||
</form>
|
||
|
||
<hr>
|
||
|
||
<div class="card">
|
||
<h2>当前生成的规则</h2>
|
||
<textarea readonly style="width: 100%; height: 200px; font-family: monospace; background: #f6f6f6;"><?php echo esc_textarea($this->generate_htaccess_rules()); ?></textarea>
|
||
<p>
|
||
<button type="button" class="button button-secondary" onclick="copyToClipboard(this)">复制规则</button>
|
||
<span id="copy-message" style="margin-left: 10px; color: green; display: none;">已复制到剪贴板!</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2>手动更新</h2>
|
||
<p>如果自动更新失败,您可以手动复制上面的规则到您的.htaccess文件中。</p>
|
||
<form method="post">
|
||
<?php wp_nonce_field('manual_update', 'htaccess_nonce'); ?>
|
||
<input type="submit" name="manual_update" class="button button-primary" value="手动更新.htaccess文件">
|
||
<input type="submit" name="manual_remove" class="button button-secondary" value="从.htaccess中移除规则" onclick="return confirm('确定要从.htaccess文件中移除所有HTACCESS Manager规则吗?')">
|
||
</form>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2>当前.htaccess内容预览</h2>
|
||
<?php
|
||
$htaccess_file = ABSPATH . '.htaccess';
|
||
if (file_exists($htaccess_file)) {
|
||
$content = file_get_contents($htaccess_file);
|
||
echo '<textarea readonly style="width: 100%; height: 300px; font-family: monospace; background: #f0f0f0;">' . esc_textarea($content) . '</textarea>';
|
||
} else {
|
||
echo '<p>.htaccess文件不存在。</p>';
|
||
}
|
||
?>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
function copyToClipboard(button) {
|
||
const textarea = document.querySelector('textarea');
|
||
textarea.select();
|
||
document.execCommand('copy');
|
||
|
||
const message = document.getElementById('copy-message');
|
||
message.style.display = 'inline';
|
||
setTimeout(() => {
|
||
message.style.display = 'none';
|
||
}, 2000);
|
||
}
|
||
|
||
// 动态禁用/启用重定向类型选项
|
||
document.querySelector('input[name="htaccess_manager_options[is_subdomain]"]').addEventListener('change', function() {
|
||
var radios = document.querySelectorAll('input[name="htaccess_manager_options[redirect_type]"]');
|
||
radios.forEach(function(radio) {
|
||
radio.disabled = this.checked;
|
||
}.bind(this));
|
||
});
|
||
</script>
|
||
<?php
|
||
|
||
// 处理手动更新
|
||
if (isset($_POST['manual_update']) && check_admin_referer('manual_update', 'htaccess_nonce')) {
|
||
$this->update_htaccess();
|
||
echo '<div class="notice notice-success is-dismissible"><p>.htaccess文件已更新!</p></div>';
|
||
}
|
||
|
||
// 处理手动移除
|
||
if (isset($_POST['manual_remove']) && check_admin_referer('manual_update', 'htaccess_nonce')) {
|
||
$this->safe_remove_htaccess_rules();
|
||
echo '<div class="notice notice-success is-dismissible"><p>已从.htaccess文件中移除所有HTACCESS Manager规则!</p></div>';
|
||
}
|
||
}
|
||
|
||
public function admin_notices() {
|
||
if (isset($_GET['settings-updated']) && $_GET['settings-updated']) {
|
||
if (isset($this->options['auto_update']) && $this->options['auto_update']) {
|
||
$this->update_htaccess();
|
||
echo '<div class="notice notice-success is-dismissible"><p>设置已保存并更新了.htaccess文件!</p></div>';
|
||
} else {
|
||
echo '<div class="notice notice-success is-dismissible"><p>设置已保存!请手动更新.htaccess文件。</p></div>';
|
||
}
|
||
}
|
||
}
|
||
|
||
private function generate_htaccess_rules() {
|
||
$rules = array();
|
||
$rules[] = 'RewriteEngine On';
|
||
$rules[] = '';
|
||
|
||
if (isset($this->options['is_subdomain']) && $this->options['is_subdomain']) {
|
||
// 二级域名情况,只强制HTTPS
|
||
$rules[] = '# 强制HTTPS重定向(二级域名)';
|
||
$rules[] = 'RewriteCond %{HTTPS} off';
|
||
$rules[] = 'RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]';
|
||
} else {
|
||
// 主域名情况,处理www和HTTPS
|
||
$rules[] = '# 同时处理 HTTPS 和 WWW 重定向';
|
||
|
||
if (isset($this->options['redirect_type']) && $this->options['redirect_type'] === 'root_to_www') {
|
||
// 根域名跳转到www
|
||
$rules[] = 'RewriteCond %{HTTPS} off [OR]';
|
||
$rules[] = 'RewriteCond %{HTTP_HOST} ^' . preg_quote(parse_url(home_url(), PHP_URL_HOST), '.') . '$ [NC]';
|
||
$rules[] = 'RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]';
|
||
$rules[] = 'RewriteRule ^ https://www.%1%{REQUEST_URI} [L,R=301]';
|
||
} else {
|
||
// www跳转到根域名(默认)
|
||
$rules[] = 'RewriteCond %{HTTPS} off [OR]';
|
||
$rules[] = 'RewriteCond %{HTTP_HOST} ^www\. [NC]';
|
||
$rules[] = 'RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]';
|
||
$rules[] = 'RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]';
|
||
}
|
||
}
|
||
|
||
return implode("\n", $rules);
|
||
}
|
||
|
||
private function update_htaccess() {
|
||
$htaccess_file = ABSPATH . '.htaccess';
|
||
|
||
if (!file_exists($htaccess_file)) {
|
||
// 如果.htaccess文件不存在,创建它
|
||
touch($htaccess_file);
|
||
}
|
||
|
||
if (is_writable($htaccess_file)) {
|
||
$new_rules = $this->generate_htaccess_rules();
|
||
|
||
// 读取现有内容
|
||
$content = file_get_contents($htaccess_file);
|
||
|
||
// 定义标记
|
||
$start_marker = '# BEGIN HTACCESS Manager';
|
||
$end_marker = '# END HTACCESS Manager';
|
||
|
||
// 检查是否已有我们的规则
|
||
if (strpos($content, $start_marker) !== false) {
|
||
// 替换现有规则
|
||
$pattern = '/' . preg_quote($start_marker, '/') . '.*?' . preg_quote($end_marker, '/') . '/s';
|
||
$replacement = $start_marker . "\n" . $new_rules . "\n" . $end_marker;
|
||
$content = preg_replace($pattern, $replacement, $content);
|
||
} else {
|
||
// 在文件开头添加新规则(在WordPress规则之前)
|
||
$wp_start_marker = '# BEGIN WordPress';
|
||
if (strpos($content, $wp_start_marker) !== false) {
|
||
$replacement = $start_marker . "\n" . $new_rules . "\n" . $end_marker . "\n\n" . $wp_start_marker;
|
||
$content = str_replace($wp_start_marker, $replacement, $content);
|
||
} else {
|
||
// 如果没有WordPress标记,直接添加到文件开头
|
||
$content = $start_marker . "\n" . $new_rules . "\n" . $end_marker . "\n\n" . $content;
|
||
}
|
||
}
|
||
|
||
// 清理多余的空行
|
||
$content = preg_replace("/(\r\n|\r|\n){3,}/", "\n\n", $content);
|
||
|
||
file_put_contents($htaccess_file, $content);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private function safe_remove_htaccess_rules() {
|
||
$htaccess_file = ABSPATH . '.htaccess';
|
||
|
||
if (file_exists($htaccess_file) && is_writable($htaccess_file)) {
|
||
$content = file_get_contents($htaccess_file);
|
||
|
||
// 备份原始内容
|
||
$backup_content = $content;
|
||
|
||
// 只移除我们自己的规则标记和内容
|
||
$start_marker = '# BEGIN HTACCESS Manager';
|
||
$end_marker = '# END HTACCESS Manager';
|
||
|
||
$pattern = '/' . preg_quote($start_marker, '/') . '.*?' . preg_quote($end_marker, '/') . '\s*\n?/s';
|
||
$content = preg_replace($pattern, '', $content);
|
||
|
||
// 清理多余的空行
|
||
$content = preg_replace("/(\r\n|\r|\n){3,}/", "\n\n", $content);
|
||
$content = trim($content) . "\n";
|
||
|
||
// 只有在内容确实发生变化且不为空时才写入
|
||
if ($content !== $backup_content && !empty(trim($content))) {
|
||
file_put_contents($htaccess_file, $content);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public static function safe_remove_htaccess_rules_static() {
|
||
$htaccess_file = ABSPATH . '.htaccess';
|
||
|
||
if (file_exists($htaccess_file) && is_writable($htaccess_file)) {
|
||
$content = file_get_contents($htaccess_file);
|
||
|
||
// 备份原始内容
|
||
$backup_content = $content;
|
||
|
||
// 只移除我们自己的规则标记和内容
|
||
$start_marker = '# BEGIN HTACCESS Manager';
|
||
$end_marker = '# END HTACCESS Manager';
|
||
|
||
$pattern = '/' . preg_quote($start_marker, '/') . '.*?' . preg_quote($end_marker, '/') . '\s*\n?/s';
|
||
$content = preg_replace($pattern, '', $content);
|
||
|
||
// 清理多余的空行
|
||
$content = preg_replace("/(\r\n|\r|\n){3,}/", "\n\n", $content);
|
||
$content = trim($content) . "\n";
|
||
|
||
// 只有在内容确实发生变化且不为空时才写入
|
||
if ($content !== $backup_content && !empty(trim($content))) {
|
||
file_put_contents($htaccess_file, $content);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private function create_mu_plugin() {
|
||
$mu_plugin_content = '<?php
|
||
/**
|
||
* Must-use plugin for HTACCESS Manager
|
||
* Auto-generated file - DO NOT EDIT
|
||
*/
|
||
|
||
defined(\'ABSPATH\') or die();
|
||
|
||
class HTACCESS_Manager_Helper {
|
||
|
||
public static function safe_remove_rules() {
|
||
$htaccess_file = ABSPATH . \'.htaccess\';
|
||
|
||
if (file_exists($htaccess_file) && is_writable($htaccess_file)) {
|
||
$content = file_get_contents($htaccess_file);
|
||
$backup_content = $content;
|
||
|
||
// 只移除HTACCESS Manager的规则
|
||
$start_marker = \'# BEGIN HTACCESS Manager\';
|
||
$end_marker = \'# END HTACCESS Manager\';
|
||
|
||
$pattern = \'/\' . preg_quote($start_marker, \'/\') . \'.*?\' . preg_quote($end_marker, \'/\') . \'\\s*\\n?/s\';
|
||
$content = preg_replace($pattern, \'\', $content);
|
||
|
||
// 清理多余空行
|
||
$content = preg_replace("/(\\r\\n|\\r|\\n){3,}/", "\\n\\n", $content);
|
||
$content = trim($content) . "\\n";
|
||
|
||
// 只有在内容变化且不为空时才写入
|
||
if ($content !== $backup_content && !empty(trim($content))) {
|
||
file_put_contents($htaccess_file, $content);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 在主插件被禁用时清理规则
|
||
add_action(\'deactivated_plugin\', function($plugin) {
|
||
if ($plugin === \'htaccess-manager/htaccess-manager.php\') {
|
||
HTACCESS_Manager_Helper::safe_remove_rules();
|
||
}
|
||
});
|
||
';
|
||
|
||
$mu_plugin_file = WP_CONTENT_DIR . '/mu-plugins/htaccess-helper.php';
|
||
file_put_contents($mu_plugin_file, $mu_plugin_content);
|
||
}
|
||
}
|
||
|
||
new HTACCESS_Manager();
|