From 237fae0cd8c8ae94c6eb411e0f3cbe7f1635b540 Mon Sep 17 00:00:00 2001 From: Repellent Date: Mon, 6 Oct 2025 12:16:19 +0500 Subject: [PATCH] =?UTF-8?q?fix=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=D0=B0=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/templates/settings/settings_case.tpl | 2 +- class/class.settings.php | 417 +++++++++++++-------- inc/query_variants/query_manifest.php | 5 - 3 files changed, 260 insertions(+), 164 deletions(-) diff --git a/admin/templates/settings/settings_case.tpl b/admin/templates/settings/settings_case.tpl index 5f5199d..4d4c174 100644 --- a/admin/templates/settings/settings_case.tpl +++ b/admin/templates/settings/settings_case.tpl @@ -27,7 +27,7 @@ -
+
diff --git a/class/class.settings.php b/class/class.settings.php index bfe1866..2e0b4f1 100644 --- a/class/class.settings.php +++ b/class/class.settings.php @@ -60,181 +60,282 @@ } /** - * Переключает файлы запросов (UNLINK+COPY) и очищает связанные таблицы в БД. - * - * @param bool $use_safe_version TRUE для безопасной версии, FALSE для оригинальной. - * @return bool TRUE в случае успеха, FALSE в случае ошибки. - */ - private function _switchQueryFiles($use_safe_version) - { - // Получаем доступ к глобальному объекту базы данных - global $AVE_DB; - - // 1. Загружаем список файлов для замены (Манифест) - $manifest_path = BASE_DIR . '/inc/query_variants/query_manifest.php'; - - $manifest = @require $manifest_path; - - if (!is_array($manifest) || empty($manifest)) { - return false; - } - - // 2. Определяем исходную папку - $source_dir_name = $use_safe_version ? 'safe_files' : 'original_files'; - $source_base = BASE_DIR . '/inc/query_variants/' . $source_dir_name; - - $error_count = 0; - - // 3. ИТЕРАЦИЯ И КОПИРОВАНИЕ - foreach ($manifest as $relative_path) { - - $source_file = $source_base . '/' . $relative_path; - $target_file = BASE_DIR . '/' . $relative_path; - - if (!file_exists($source_file)) { - $error_count++; - continue; - } - - // 4. Создаем папку назначения, если ее нет - $target_dir = dirname($target_file); - if (!is_dir($target_dir)) { - @mkdir($target_dir, 0775, true); - } - - // 5. ГАРАНТИЯ ПЕРЕЗАПИСИ: Удаляем старый файл перед копированием - if (file_exists($target_file)) { - if (!@unlink($target_file)) { - $error_count++; - continue; - } - } - - // 6. Копируем - if (!@copy($source_file, $target_file)) { - $error_count++; - } - } - - // 7. ОЧИСТКА БАЗЫ ДАННЫХ - if ($error_count === 0) - { - // Используем константу PREFIX для определения префикса таблиц - $AVE_DB->query("DELETE FROM `" . PREFIX . "_request`"); - $AVE_DB->query("DELETE FROM `" . PREFIX . "_request_conditions`"); - } - - // 8. Возвращаем результат - return ($error_count === 0); + * Записывает события переключения файлов в специальный лог. + * + * @param string $message Сообщение для записи. + * @param string $level Уровень: 'ERROR', 'WARNING', 'INFO'. + */ +private function _logSwitchEvent($message, $level = 'INFO') +{ + // Абсолютный путь к папке логов. BASE_DIR должен быть определен. + $log_dir = BASE_DIR . '/tmp/logs'; + $log_file = $log_dir . '/query_switch.log'; + + // Форматируем сообщение + $timestamp = date('Y-m-d H:i:s'); + $log_entry = "[" . $timestamp . "] [" . $level . "] " . $message . "\n"; + + // Проверяем существование директории (и права, если есть возможность) + if (!is_dir($log_dir) || !is_writable($log_dir)) { + // Если лог-директория недоступна, пытаемся записать в системный лог CMS, если это возможно. + @reportLog("ОШИБКА ЛОГА: Не удалось записать событие. Директория '{$log_dir}' недоступна."); + return; } + // Безопасная запись в файл + @file_put_contents($log_file, $log_entry, FILE_APPEND | LOCK_EX); +} + +/** + * Переключает файлы запросов (UNLINK+COPY) и очищает связанные таблицы в БД. + * + * @param bool $use_safe_version Новое значение (использовать безопасную версию). + * @param bool $clear_db_flag Флаг: TRUE, если версия действительно изменилась и нужна очистка БД. + * @return bool TRUE в случае успеха, FALSE в случае ошибки. + */ +private function _switchQueryFiles($use_safe_version, $clear_db_flag) +{ + global $AVE_DB; + + $version_name = $use_safe_version ? 'Безопасная (Safe)' : 'Оригинальная (Original)'; + $this->_logSwitchEvent("=== Начат процесс переключения на версию: {$version_name}. ==="); + + $manifest_path = BASE_DIR . '/inc/query_variants/query_manifest.php'; + $manifest = @require $manifest_path; + + if (!is_array($manifest) || empty($manifest)) { + $this->_logSwitchEvent('ОШИБКА: Файл манифеста не загружен или пуст.', 'ERROR'); + return false; + } + + $source_dir_name = $use_safe_version ? 'safe_files' : 'original_files'; + $source_base = BASE_DIR . '/inc/query_variants/' . $source_dir_name; + $error_count = 0; + + // ИТЕРАЦИЯ И КОПИРОВАНИЕ + foreach ($manifest as $relative_path) { + $source_file = $source_base . '/' . $relative_path; + $target_file = BASE_DIR . '/' . $relative_path; + + if (!file_exists($source_file)) { + $error_count++; + $this->_logSwitchEvent("ОШИБКА ФАЙЛА: Отсутствует исходный файл: {$source_file}", 'ERROR'); + continue; + } + + $target_dir = dirname($target_file); + if (!is_dir($target_dir)) { + if (!@mkdir($target_dir, 0775, true)) { + $error_count++; + $this->_logSwitchEvent("ОШИБКА ФАЙЛА: Не удалось создать директорию: {$target_dir}", 'ERROR'); + } + } + + if (file_exists($target_file)) { + if (!@unlink($target_file)) { + $error_count++; + $this->_logSwitchEvent("ОШИБКА ФАЙЛА: Не удалось удалить старый файл: {$target_file}", 'ERROR'); + continue; + } + } + + if (!@copy($source_file, $target_file)) { + $error_count++; + $this->_logSwitchEvent("ОШИБКА ФАЙЛА: Не удалось скопировать файл: {$source_file}", 'ERROR'); + } + } + + // ЛОГИКА ОЧИСТКИ БАЗЫ ДАННЫХ + if ($error_count > 0) { + $this->_logSwitchEvent("ПРЕДУПРЕЖДЕНИЕ: Обнаружены ошибки копирования файлов ({$error_count} шт.). Очистка БД не будет выполнена.", 'WARNING'); + } + + if ($clear_db_flag) + { + $this->_logSwitchEvent("Очистка БД: Обнаружено фактическое переключение версии. Запуск DELETE FROM и ALTER TABLE.", 'INFO'); + + // 1. Очистка ДОЧЕРНЕЙ таблицы + if (@$AVE_DB->query("DELETE FROM `" . PREFIX . "_request_conditions`")) { + $this->_logSwitchEvent('Очистка `_request_conditions`: Успех.', 'INFO'); + } else { + $this->_logSwitchEvent('ОШИБКА БД: Не удалось выполнить DELETE FROM для `_request_conditions`.', 'ERROR'); + $error_count++; + } + + // 2. Очистка РОДИТЕЛЬСКОЙ таблицы + if (@$AVE_DB->query("DELETE FROM `" . PREFIX . "_request`")) { + $this->_logSwitchEvent('Очистка `_request`: Успех.', 'INFO'); + } else { + $this->_logSwitchEvent('ОШИБКА БД: Не удалось выполнить DELETE FROM для `_request`.', 'ERROR'); + $error_count++; + } + + // 3. Сброс СЧЕТЧИКА + if (@$AVE_DB->query("ALTER TABLE `" . PREFIX . "_request` AUTO_INCREMENT = 1")) { + $this->_logSwitchEvent('Сброс AUTO_INCREMENT: Успех.', 'INFO'); + } else { + $this->_logSwitchEvent('ОШИБКА БД: Не удалось выполнить ALTER TABLE AUTO_INCREMENT.', 'ERROR'); + $error_count++; + } + } else { + $this->_logSwitchEvent("Очистка БД: Переключение версии не обнаружено ({$version_name}). Пропуск очистки.", 'INFO'); + } + + $final_result = ($error_count === 0); + $this->_logSwitchEvent("=== Процесс завершен. Результат: " . ($final_result ? 'УСПЕХ' : 'СБОЙ') . " (Ошибок: {$error_count}). ===", $final_result ? 'INFO' : 'ERROR'); + + return $final_result; +} /** - * Метод отображения дополнительных настроек - * - */ - function settingsCase() - { - global $AVE_Template; + * Метод отображения дополнительных настроек + * + */ +function settingsCase() +{ + global $AVE_Template; - // Сохраняем настройки - if (isset($_REQUEST['more'])) - { - $set = '>> ЗАПУСКАЕМ БУФЕРИЗАЦИЮ ВЫВОДА для чистого JSON-ответа (AJAX) + if (isAjax()) { + ob_start(); + } + + // --- ИНИЦИАЛИЗАЦИЯ ДЛЯ ОБРАБОТКИ НЕ-AJAX ЗАПРОСОВ --- + // Получаем текущее значение константы из глобального пространства. + // Это необходимо для того, чтобы при клике "Сохранить" (не-AJAX) без изменения чекбокса + // значение REQUEST_USE_VERSION не сбрасывалось и не циклилось. + $current_constant_value = defined('REQUEST_USE_VERSION') ? (REQUEST_USE_VERSION ? '1' : '0') : '0'; + $request_use_version_value = $current_constant_value; + // ---------------------------------------------------- + + $set = ' $type) + { + foreach($type as $k => $v) + { + // ИЗВЛЕКАЕМ НОВОЕ ЗНАЧЕНИЕ ДЛЯ ПЕРЕКЛЮЧЕНИЯ ФАЙЛОВ ИЗ ФОРМЫ + if ($k == 'REQUEST_USE_VERSION') { + // Значение из формы всегда приоритетнее, даже если оно '0' (отмечено 'Нет') + $request_use_version_value = $v; + } - $request_use_version_value = '0'; + switch ($GLOBALS['CMS_CONFIG'][$key][$k]['TYPE']) + { + case 'bool' : + $v = $v ? 'true' : 'false'; + break; + case 'integer' : + $v = intval($v); + break; + case 'string' : + case 'dropdown' : + $v = "'" . add_slashes($v) . "'"; + break; + default : + $v = "'" . add_slashes($v) . "'"; + break; + } - foreach($_REQUEST['GLOB'] as $key => $type) - { - foreach($type as $k => $v) - { - // ИЗВЛЕКАЕМ ЗНАЧЕНИЕ ДЛЯ ПЕРЕКЛЮЧЕНИЯ ФАЙЛОВ - // Сохраняем значение переменной из формы до ее преобразования. - if ($k == 'REQUEST_USE_VERSION') { - $request_use_version_value = $v; - } - - switch ($GLOBALS['CMS_CONFIG'][$key][$k]['TYPE']) - { - case 'bool' : - $v = $v ? 'true' : 'false'; - break; + $set .= "\t" . "// " . $GLOBALS['CMS_CONFIG'][$key][$k]['DESCR'] . "\r\n"; + $set .= "\t" . "define('" . $k . "', " . $v . ");\r\n\r\n"; + } + } - case 'integer' : - $v = intval($v); - break; + $set .= '?>'; - case 'string' : - $v = "'" . add_slashes($v) . "'"; - break; + $result = file_put_contents(BASE_DIR . '/config/config.inc.php', $set); - case 'dropdown' : - $v = "'" . add_slashes($v) . "'"; - break; + if ($result > 0) + { + $new_query_setting = (bool)$request_use_version_value; + + // 2. Определяем, произошло ли фактическое переключение версий + $is_a_version_switch = ($new_query_setting !== $old_query_setting); - default : - $v = "'" . add_slashes($v) . "'"; - break; - } + // 3. Вызываем функцию, передавая НОВОЕ значение и ФЛАГ очистки + $switch_success = $this->_switchQueryFiles($new_query_setting, $is_a_version_switch); - $set .= "\t" . "// " . $GLOBALS['CMS_CONFIG'][$key][$k]['DESCR'] . "\r\n"; - $set .= "\t" . "define('" . $k . "', " . $v . ");\r\n\r\n"; - } - } + if (!$switch_success) { + reportLog('ВНИМАНИЕ: Конфигурация сохранена, но переключение файлов/очистка БД при вызове AJAX не удалось.'); + } + + $message = $AVE_Template->get_config_vars('SETTINGS_SAVED'); + $header = $AVE_Template->get_config_vars('SETTINGS_SUCCESS'); + $theme = 'accept'; + reportLog($AVE_Template->get_config_vars('SETTINGS_SAVE_DOP')); + } + else + { + $message = $AVE_Template->get_config_vars('SETTINGS_SAVED_ERR'); + $header = $AVE_Template->get_config_vars('SETTINGS_ERROR'); + $theme = 'error'; + } - $set .= '?>'; + if (isAjax()) + { + // ОЧИЩАЕМ ВЕСЬ ВЫВОД ПЕРЕД JSON + ob_end_clean(); + + echo json_encode(array( + 'message' => $message, + 'header' => $header, + 'theme' => $theme) + ); + } + else + { + // 1. Принудительно очищаем кэш PHP-файлов для главного конфига (если доступно) + // Это обходной путь, который может понадобиться, если сервер использует Opcache + if (function_exists('opcache_invalidate')) { + @opcache_invalidate(BASE_DIR . '/config/config.inc.php', true); + } + + // 2. Добавляем "соль" (cache buster) и принудительно сбрасываем состояние + $cache_bust = microtime(true); + $redirect_url = 'Location:index.php?do=settings&sub=case&cp=' . SESSION . '&t=' . $cache_bust . '#settings'; + + // 3. Добавляем заголовки, запрещающие кэширование прокси/сервером + header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); + header("Cache-Control: post-check=0, pre-check=0", false); + header("Pragma: no-cache"); - $result = file_put_contents(BASE_DIR . '/config/config.inc.php', $set); + $AVE_Template->assign('message', $message); + header($redirect_url); + } - if ($result > 0) - { - $new_query_setting = (bool)$request_use_version_value; - - // Вызываем функцию и сохраняем результат - $switch_success = $this->_switchQueryFiles($new_query_setting); + exit; + } + else + { + $AVE_Template->assign('CMS_CONFIG', $GLOBALS['CMS_CONFIG']); + $AVE_Template->assign('content', $AVE_Template->fetch('settings/settings_case.tpl')); + } +} - // Если переключение не удалось, записываем это в лог (но показываем пользователю успех) - if (!$switch_success) { - reportLog('ВНИМАНИЕ: Конфигурация сохранена, но переключение файлов при вызове AJAX не удалось (возможна блокировка файла).'); - } - - $message = $AVE_Template->get_config_vars('SETTINGS_SAVED'); - $header = $AVE_Template->get_config_vars('SETTINGS_SUCCESS'); - $theme = 'accept'; - reportLog($AVE_Template->get_config_vars('SETTINGS_SAVE_DOP')); - } - else - { - // ... (если не удалось записать config.inc.php) - $message = $AVE_Template->get_config_vars('SETTINGS_SAVED_ERR'); - $header = $AVE_Template->get_config_vars('SETTINGS_ERROR'); - $theme = 'error'; - } - - if (isAjax()) - { - echo json_encode(array( - 'message' => $message, - 'header' => $header, - 'theme' => $theme) - ); - } - else - { - $AVE_Template->assign('message', $message); - header('Location:index.php?do=settings&sub=case&cp=' . SESSION); - } - - exit; - // Выводим настройки - } - else - { - $AVE_Template->assign('CMS_CONFIG', $GLOBALS['CMS_CONFIG']); - $AVE_Template->assign('content', $AVE_Template->fetch('settings/settings_case.tpl')); - } - } /** * Метод записи настроек diff --git a/inc/query_variants/query_manifest.php b/inc/query_variants/query_manifest.php index 8df8f17..99dcffd 100644 --- a/inc/query_variants/query_manifest.php +++ b/inc/query_variants/query_manifest.php @@ -22,11 +22,6 @@ $query_files_manifest = [ // ------------------------------------ 'class/class.request.php', 'functions/func.parserequest.php', - - // ------------------------------------ - // Публичный шаблон (Templates) - // ------------------------------------ - 'templates/default/tpl/request/public.tpl', ]; return $query_files_manifest;