Файл configuration.php в Cotonti
Файл /system/configuration.php в Cotonti
Файл configuration.php в Cotonti CMF играет ключевую роль в управлении конфигурационными параметрами системы. Он содержит набор функций для работы с конфигурационными значениями и их обработки. Рассмотрим основные моменты, которые можно выделить из кода:
-
Импорт конфигурационных значений: Функции, такие как
cot_config_import, используются для получения и фильтрации значений конфигурации из различных источников (например, из глобальных массивов$_POST,$_GET, и других). Они обеспечивают безопасный способ импорта данных, применяя фильтры и дополнительные правила обработки. -
Обновление конфигурационных значений: Функция
cot_config_update_optionsотвечает за обновление значений конфигурации в базе данных. Она позволяет фильтровать и валидировать входные данные, используя как стандартные, так и кастомные фильтры. Это позволяет системным администраторам и пользователям менять параметры конфигурации через интерфейс, который потом обновляется в базе данных. -
Генерация HTML элементов для конфигурации: Функция
cot_config_inputгенерирует соответствующие HTML-элементы (такие как текстовые поля, выпадающие списки, радиокнопки и другие) для ввода и отображения конфигурационных значений в админ-панели. Этот процесс адаптируется под тип данных, заданный в конфигурации. -
Заголовки и подсказки для конфигурационных опций: Функции
cot_config_titlesиcot_config_selecttitlesотвечают за отображение заголовков и подсказок для различных параметров конфигурации. Они используют локализованные строки для отображения информации на разных языках. -
Обработка кастомных и динамических фильтров: В коде предусмотрены возможности для применения кастомных фильтров через динамическое выполнение функций (например, с помощью
call_user_func_array). Это даёт гибкость при работе с различными типами данных, которые могут быть уникальными для каждого сайта или расширения.
В целом, этот файл служит центральным местом для обработки, обновления и отображения конфигурационных значений, что позволяет системам и плагинам гибко настраиваться через админ-панель или другие интерфейсы.
Назначение файла: Этот файл отвечает за управление конфигурацией системы или плагинов. Он облегчает изменение настроек, контроль за правильностью данных и отображение соответствующих элементов управления на сайте.
/**
* Configuration Management API
*
* @package API - Configuration
* @copyright (c) Cotonti Team
* @license https://github.com/Cotonti/Cotonti/blob/master/License.txt
*/
defined('COT_CODE') or die('Wrong URL'); // Проверка, что файл был вызван корректно, иначе выводится ошибка и завершение работы
/**
* Generic text configuration. Is displayed as textarea. Contains text.
* Is used by default.
*/
const COT_CONFIG_TYPE_TEXT = 0; // Константа для текстовой конфигурации, отображаемой как поле ввода (textarea), используется по умолчанию
/**
* A string, max length is 255 chars. Is displayed as a single line of input.
* The list of variants is ignored for this type.
*/
const COT_CONFIG_TYPE_STRING = 1; // Константа для строковой конфигурации, максимальная длина 255 символов, отображается как поле ввода одной строки
/**
* Selection from the list of possible variants. Is displayed as a dropdown.
*/
const COT_CONFIG_TYPE_SELECT = 2; // Константа для выбора из списка возможных вариантов, отображаемого в виде выпадающего списка
/**
* Radio yes/no selection.
*/
const COT_CONFIG_TYPE_RADIO = 3; // Константа для выбора радио-кнопок "да/нет"
/**
* Callback function type
*/
const COT_CONFIG_TYPE_CALLBACK = 4; // Константа для типа конфигурации с функцией обратного вызова
/**
* Hidden config. It is actually a text string, but it is not displayed anywhere
*/
const COT_CONFIG_TYPE_HIDDEN = 5; // Константа для скрытой конфигурации, которая представляет собой строку текста, но не отображается нигде
/**
* Visual separator/fieldset
*/
const COT_CONFIG_TYPE_SEPARATOR = 6; // Константа для визуального разделителя или области формы
/**
* Integer range
*/
const COT_CONFIG_TYPE_RANGE = 7; // Константа для диапазона целых чисел
/**
* Custom type.
*/
const COT_CONFIG_TYPE_CUSTOM = 8; // Константа для пользовательского типа конфигурации
/**
* Генерирует поле ввода для целого числа
*
* @param array $cfg_var Данные конфигурационной переменной
* @param string $min Минимально допустимое значение, полученное из параметров обратного вызова
* @param string $max Максимально допустимое значение, полученное из параметров обратного вызова
* @return string Код поля ввода
*/
function cot_config_type_int($cfg_var, $min='', $max='')
{
$name = $cfg_var['config_name']; // Получаем имя конфигурационной переменной
$value = $cfg_var['config_value']; // Получаем значение конфигурационной переменной
if (!empty($min) && !empty($max)){ // Если заданы минимальное и максимальное значения
$placeholder = "$min - $max"; // Заполняем подсказку диапазоном значений
} elseif(!empty($min)) { // Если задано только минимальное значение
$placeholder = cot_rc('adm_int_min', array('value'=>$min)); // Подсказка для минимального значения
} elseif(!empty($min)) { // Этот блок дублирует условие выше, вероятно, ошибка (мин не проверяется дважды)
$placeholder = cot_rc('adm_int_max', array('value'=>$max)); // Подсказка для максимального значения
}
return cot_inputbox('text', $name, $value, array('placeholder' => $placeholder )); // Генерируем HTML-элемент ввода с подсказкой
}
/**
* Фильтрует значение как целое число в пределах от Min до Max.
* Используется как функция обратного вызова для фильтрации пользовательского ввода типа конфигурации
* @see также тип COT_CONFIG_TYPE_CUSTOM
*
* @param string $new_value Значение, введённое пользователем
* @param array $cfg_var Данные конфигурационной переменной
* @param string $min Минимально допустимое значение, полученное из параметров обратного вызова
* @param string $max Максимально допустимое значение, полученное из параметров обратного вызова
* @param bool $skip_warnings Если установлено, предупреждения не отображаются
* @return int|NULL Отфильтрованное целое значение или NULL, если значение не может быть отфильтровано / недопустимо
*/
function cot_config_type_int_filter($new_value, $cfg_var, $min = '', $max = '', $skip_warnings = false)
{
$not_filtered = $new_value; // Сохраняем исходное значение, введённое пользователем
$var_name = $cfg_var['config_name']; // Получаем имя конфигурационной переменной
list($title, $hint) = cot_config_titles($var_name, $cfg_var['config_text']); // Получаем название и подсказку для конфигурационной переменной
$fix_msg = ''; // Сообщение о фиксированном значении
$not_num = false; // Флаг для проверки, является ли значение числом
// Проверяем, является ли введённое значение числом
if (!is_numeric($new_value)) {
$not_num = true; // Если не число, устанавливаем флаг
} else {
$new_value = floor($new_value); // Округляем значение до целого
if (!empty($min) && $new_value < $min) { // Если значение меньше минимального
$new_value = $min; // Устанавливаем минимальное значение
$fix_msg = '. '.cot_rc('adm_set') . cot_rc('adm_int_min', array('value' => $min)); // Сообщение о применении минимального значения
}
if (!empty($max) && $new_value > $max) { // Если значение больше максимального
$new_value = $max; // Устанавливаем максимальное значение
$fix_msg = '. ' . cot_rc('adm_set') . cot_rc('adm_int_max', array('value' => $max)); // Сообщение о применении максимального значения
}
}
// Уведомление для пользователя о неправильном значении
$invalid_value_msg = cot_rc('adm_invalid_input', array('value' => $not_filtered, 'field_name' => $title ));
if (!$skip_warnings && ($fix_msg || $not_num)) { // Если не пропущены предупреждения и есть ошибка или фиксированное значение
cot_message($invalid_value_msg . $fix_msg, $not_num ? 'error' : 'warning', $var_name); // Выводим сообщение об ошибке или предупреждение
}
return $not_num ? null : (int) $new_value; // Если значение не числовое, возвращаем NULL, иначе возвращаем целое значение
}
/**
* Регистрирует несколько конфигурационных записей за один раз.
*
* Пример:
* <code>
* $config_options = array(
* array(
* 'name' => 'disable_test', // Название опции
* 'type' => COT_CONFIG_TYPE_RADIO, // Тип опции
* 'default' => '0' // Значение по умолчанию
* ),
* array(
* 'name' => 'test_selection', // Название опции
* 'type' => COT_CONFIG_TYPE_SELECT, // Тип опции
* 'default' => '20', // Значение по умолчанию
* 'variants' => '5,10,15,20,25,30,35,40,50' // Возможные значения для SELECT
* ),
* array(
* 'name' => 'test_value', // Название опции
* 'type' => COT_CONFIG_TYPE_STRING, // Тип опции
* 'default' => 'something' // Значение по умолчанию
* ),
* array(
* 'name' => 'not_visible', // Название опции
* 'type' => COT_CONFIG_TYPE_HIDDEN, // Тип опции
* 'default' => 'test23' // Значение по умолчанию
* )
* );
*
* cot_config_add('test', $config_options, 'module'); // Регистрируем конфигурацию
* </code>
*
* @param string $name Название расширения (код)
* @param array $options Ассоциативный массив конфигурационных записей.
* Каждая запись массива имеет следующие ключи:
* 'name' => Название опции, буквенно-цифровое, с разрешёнными знаками _
* Должно быть уникальным для модуля/плагина
* 'type' => Тип опции, см. константы COT_CONFIG_TYPE_*
* 'default' => Значение по умолчанию, по умолчанию пустая строка
* 'variants' => Список возможных значений через запятую (без пробелов),
* Только для опций типа SELECT.
* 'order' => Строка, определяющая позицию опции в списке,
* Например, '04'. Или будет автоматически присвоена, если не указана
* 'text' => Текстовое описание. Обычно опускается и хранится в языковых файлах
* @param mixed $is_module Флаг, указывающий, является ли это конфигурацией модуля или плагина
* @param string $category Код категории структуры. Только для конфигурационных опций по категориям
* @param string $donor Название расширения для внедрения конфигурации от одного расширения в другое
* @return bool Статус операции
* @global CotDB $db
*/
function cot_config_add($name, $options, $is_module = false, $category = '', $donor = '')
{
global $db, $cfg, $db_config;
$cnt = count($options); // Получаем количество опций
if (is_bool($is_module)) // Проверяем, является ли $is_module булевым значением
{
$type = $is_module ? 'module' : 'plug'; // Если $is_module истина, тип 'module', иначе 'plug'
}
else
{
$type = !in_array($is_module, array('plug', 'core')) ? 'module' : $is_module; // Если $is_module не 'plug' или 'core', тип 'module'
}
// Проверяем аргументы
if (!$cnt) // Если количество опций равно 0, возвращаем false
{
return false;
}
// Строим SQL-запрос
$option_set = array(); // Массив для хранения конфигурационных записей
for ($i = 0; $i < $cnt; $i++) // Проходим по всем опциям
{
$opt = $options[$i]; // Получаем текущую опцию
$option_set[] = array( // Добавляем опцию в массив
'config_owner' => $type, // Владелец конфигурации
'config_cat' => $name, // Категория конфигурации
'config_subcat' => $category, // Подкатегория конфигурации
'config_order' => isset($opt['order']) ? $opt['order'] : str_pad($i, 2, 0, STR_PAD_LEFT), // Порядок конфигурации
'config_name' => $opt['name'], // Название конфигурации
'config_type' => (int) $opt['type'], // Тип конфигурации
'config_value' => isset($opt['default']) ? $opt['default'] : '', // Значение конфигурации по умолчанию
'config_default' => isset($opt['default']) ? $opt['default'] : '', // Значение по умолчанию
'config_variants' => isset($opt['variants']) ? $opt['variants'] : '', // Возможные варианты значений
'config_text' => isset($opt['text']) ? $opt['text'] : '', // Текстовое описание конфигурации
'config_donor' => $donor // Расширение-донора для конфигурации
);
}
$ins_cnt = $db->insert($db_config, $option_set); // Выполняем вставку в базу данных
return $ins_cnt == $cnt; // Возвращаем результат операции
}
/**
* Внедряет заданные опции в конфигурацию модуля, если их там ещё нет.
* Используется плагинами, которые расширяют поведение модуля и нуждаются в настройках для конкретного модуля или категории.
*
* @global array $cfg Конфигурация
* @param string $module_name Код целевого модуля
* @param array $options Массив внедряемых опций, описанных в cot_config_add()
* @param bool $into_struct Флаг, указывающий, что конфигурационные опции должны быть внедрены в конфигурацию категорий модуля, а не в конфигурацию самого модуля
* @param string $donor Название расширения для внедрения конфигурации от одного расширения в другое
* @return int Количество опций, которые были действительно внедрены
* @see cot_config_add()
*/
function cot_config_implant($module_name, $options, $into_struct, $donor)
{
global $cfg; // Используем глобальный массив конфигурации
$category = $into_struct ? '__default' : ''; // Если флаг $into_struct установлен, то категория будет '__default', иначе пустая строка
$add_options = array(); // Массив для хранения опций, которые нужно добавить
foreach ($options as $opt) // Проходим по всем опциям
{
// Проверяем, нужно ли добавлять опцию в конфигурацию
if (!$into_struct && !isset($cfg[$module_name][$opt['name']]) || $into_struct && !isset($cfg[$module_name]['cat___default'][$opt['name']]))
{
$add_options[] = $opt; // Добавляем опцию в массив, если её нет в конфигурации
}
}
// Внедряем опции с помощью функции cot_config_add
return cot_config_add($module_name, $add_options, 'module', $category, $donor);
}
/**
* Проверяет, есть ли уже внедрённые записи конфигурации.
*
* @param string $acceptor Имя модуля-акцептора
* @param string $donor Имя расширения-донора
* @return bool TRUE, если найдено внедрение записей конфигурации, FALSE если нет
* @global CotDB $db
*/
function cot_config_implanted($acceptor, $donor)
{
global $db, $db_config; // Используем глобальные переменные базы данных и таблицы конфигурации
// Выполняем SQL-запрос, чтобы проверить, есть ли записи с данным владельцем, категорией и донором
return $db->query("SELECT COUNT(*) FROM $db_config WHERE config_owner = 'module' AND config_cat = ? AND config_donor = ?", array($acceptor, $donor))->fetchColumn() > 0;
}
/**
* Загружает структуру конфигурации из базы данных в массив
*
* @param string $name Код расширения
* @param mixed $is_module TRUE, если это модуль, FALSE, если плагин
* @param string $category Код категории структуры. Только для опций конфигурации, относящихся к категории
* @param string $donor Имя расширения для внедрения конфигурации из расширения в расширение
* @return array Структура опций конфигурации
* @see cot_config_add()
* @global CotDB $db
*/
function cot_config_load($name, $is_module = false, $category = '', $donor = '')
{
global $db, $db_config; // Используем глобальные переменные базы данных и таблицы конфигурации
$options = array(); // Массив для хранения опций конфигурации
// Определяем тип конфигурации (модуль или плагин)
if (is_bool($is_module))
{
$type = $is_module ? 'module' : 'plug'; // Если это логическое значение, присваиваем 'module' или 'plug'
}
else
{
$type = !in_array($is_module, array('plug', 'core')) ? 'module' : $is_module; // Если это строка, то выбираем 'module' или указанное значение
}
// Формируем SQL-запрос для извлечения конфигурационных данных
$query = "SELECT config_name, config_type, config_value,
config_default, config_variants, config_order
FROM $db_config WHERE config_owner = ? AND config_cat = ? AND config_subcat = ? AND config_donor = ?";
$params = array($type, $name, $category, $donor); // Параметры для запроса
$res = $db->query($query, $params); // Выполняем запрос
while ($row = $res->fetch()) // Проходим по всем строкам результата
{
// Добавляем данные каждой строки в массив $options
$options[] = array(
'name' => $row['config_name'], // Имя опции
'type' => $row['config_type'], // Тип опции
'order' => $row['config_order'], // Порядок опции
'value' => $row['config_value'], // Текущее значение
'default' => $row['config_default'], // Значение по умолчанию
'variants' => $row['config_variants'] // Возможные варианты для SELECT
);
}
$res->closeCursor(); // Закрываем курсор результата
return $options; // Возвращаем массив опций конфигурации
}
/**
* Обновляет свойства конфигурационной карты в базе данных для заданных опций
*
* @param string $name Код расширения
* @param array $options Записи конфигурации
* @param bool|string $isModule TRUE, если модуль, FALSE, если плагин
* @param string $category Код категории структуры. Только для опций конфигурации, относящихся к категории
* @param string $donor Имя расширения для внедрения конфигурации из расширения в расширение
* @return int Количество обновленных записей
* @global CotDB $db
*/
function cot_config_modify($name, $options, $isModule = false, $category = '', $donor = '')
{
global $db_config; // Используем глобальную переменную для таблицы конфигурации
// Определяем тип конфигурации (модуль или плагин)
if (is_bool($isModule)) {
$type = $isModule ? 'module' : 'plug'; // Если логическое значение, присваиваем 'module' или 'plug'
} else {
$type = !in_array($isModule, array('plug', 'core')) ? 'module' : $isModule; // Если строковое значение, то выбираем 'module' или указанный тип
}
$affected = 0; // Переменная для подсчета количества обновленных записей
// Условие для SQL-запроса
$where = 'config_owner = ? AND config_cat = ? AND config_name = ? AND config_donor = ?';
// Если категория не пуста, добавляем условие для категории
if (!empty($category)) {
$where .= ' AND config_subcat = ?';
}
// Проходим по всем переданным опциям
foreach ($options as $opt) {
$config_name = (string) $opt['name']; // Получаем имя конфигурации
unset($opt['name']); // Убираем имя из массива опций
$opt_row = array(); // Массив для хранения обновленных данных
// Перебираем ключи и значения в текущей опции
foreach ($opt as $key => $val) {
$opt_row['config_' . $key] = $val; // Добавляем данные с префиксом 'config_'
}
// Формируем параметры для SQL-запроса
$params = empty($category) ?
array($type, $name, $config_name, $donor) : array($type, $name, $config_name, $donor, $category);
// Выполняем обновление и увеличиваем счетчик обновленных записей
$affected += Cot::$db->update($db_config, $opt_row, $where, $params);
}
// Возвращаем количество обновленных записей
return $affected;
}
/**
* Разбирает массив записей конфигурации файла установки в представление в виде массива
*
* @param array $info_cfg Записи конфигурации файла установки
* @return array Опции конфигурации
*/
function cot_config_parse($info_cfg)
{
$options = array(); // Массив для хранения опций конфигурации
if (is_array($info_cfg)) // Проверяем, является ли переданный параметр массивом
{
foreach ($info_cfg as $i => $x) // Перебираем элементы массива конфигурации
{
$line = explode(':', $x); // Разделяем строку на части по символу ":"
$line[1] = trim($line[1]); // Убираем пробелы вокруг второго элемента
if (is_array($line) && !empty($line[1]) && !empty($i)) // Проверяем, что строка правильно разделена
{
// В зависимости от типа конфигурации, присваиваем соответствующее значение
switch ($line[1])
{
case 'string': // Тип конфигурации string
$line['Type'] = COT_CONFIG_TYPE_STRING;
break;
case 'select': // Тип конфигурации select
$line['Type'] = COT_CONFIG_TYPE_SELECT;
break;
case 'radio': // Тип конфигурации radio
$line['Type'] = COT_CONFIG_TYPE_RADIO;
break;
case 'callback': // Тип конфигурации callback
$line['Type'] = COT_CONFIG_TYPE_CALLBACK;
break;
case 'hidden': // Тип конфигурации hidden
$line['Type'] = COT_CONFIG_TYPE_HIDDEN;
break;
case 'separator': // Тип конфигурации separator
$line['Type'] = COT_CONFIG_TYPE_SEPARATOR;
break;
case 'range': // Тип конфигурации range
$line['Type'] = COT_CONFIG_TYPE_RANGE;
break;
case 'custom': // Тип конфигурации custom
$line['Type'] = COT_CONFIG_TYPE_CUSTOM;
break;
default: // Если тип не найден, по умолчанию текстовый тип
$line['Type'] = COT_CONFIG_TYPE_TEXT;
break;
}
// Добавляем опцию в массив конфигураций
$options[] = array(
'name' => $i, // Имя опции
'order' => trim($line[0]), // Порядок опции (убираем пробелы)
'type' => $line['Type'], // Тип опции
'variants' => $line[2], // Возможные значения
'default' => $line[3], // Значение по умолчанию
'text' => trim($line[4]) // Описание опции
);
}
}
}
return $options; // Возвращаем массив с опциями конфигурации
}
/**
* Отменяет регистрацию опции(й) конфигурации.
*
* @param string $name Код расширения
* @param mixed $is_module Флаг, указывающий, является ли конфигурация для модуля или плагина
* @param mixed $option Имя одной опции конфигурации в виде строки.
* Или передайте массив имен опций, чтобы удалить их сразу. Если пусто или не указано,
* все опции выбранного модуля/плагина будут удалены.
* @param string $category Код категории структуры. Только для опций конфигурации, относящихся к категориям
* @param string $donor Имя расширения для внедрения конфигурации из одного расширения в другое
* @return int Количество удалённых опций
* @global CotDB $db
*/
function cot_config_remove($name, $is_module = false, $option = '', $category = '', $donor = null)
{
global $db, $db_config;
// Определяем тип конфигурации: модуль или плагин
if (is_bool($is_module))
{
$type = $is_module ? 'module' : 'plug'; // Если $is_module равно true, выбираем 'module', иначе 'plug'
}
else
{
$type = !in_array($is_module, array('plug', 'core')) ? 'module' : $is_module; // Если тип не 'plug' или 'core', выбираем 'module'
}
// Формируем базовое условие для удаления, выбирая конфигурацию по типу и названию
$where = "config_owner = '$type' AND config_cat = " . $db->quote($name); // Условие для владельца конфигурации и категории
if (!empty($category))
{
$where .= " AND config_subcat = " . $db->quote($category); // Если категория не пуста, добавляем условие для подкатегории
}
if (!is_null($donor))
{
$where .= " AND config_donor = " . $db->quote($donor); // Если donor задан, добавляем условие для имени расширения
}
// Если передан массив опций, строим условие для нескольких опций
if (is_array($option))
{
$cnt = count($option); // Считаем количество опций в массиве
if ($cnt == 1)
{
$option = $option[0]; // Если массив состоит из одной опции, выбираем её
}
else
{
$where .= " AND config_name IN ("; // Строим условие для нескольких опций
for ($i = 0; $i < $cnt; $i++)
{
if ($i > 0)
$where .= ','; // Добавляем запятую между опциями
$where .= $db->quote($option[$i]); // Добавляем каждую опцию в условие
}
$where .= ')';
unset($option); // Убираем переменную $option, так как мы её обработали
}
}
// Если передана одна опция, добавляем её в условие
if (!empty($option))
{
$where .= " AND config_name = " . $db->quote($option); // Условие для одной опции
}
// Выполняем удаление записей и возвращаем количество удалённых опций
return $db->delete($db_config, $where); // Удаляем записи из таблицы конфигураций
}
/**
* Обновляет значения конфигурации
*
* Пример:
* <code>
* $config_values = array(
* 'disable_test' => '0',
* 'hidden_test' => 'test45',
* );
*
* cot_config_set('test', $config_values, true);
* </code>
*
* @param string $name Название расширения, к которому принадлежит конфигурация
* @param array $options Массив параметров в виде 'имя параметра' => 'значение параметра'
* @param bool|string $isModule Флаг, указывающий, является ли это конфигурацией для модуля или плагина
* @param string $category Код категории структуры. Только для конфигурационных опций на основе категорий
* @return int Количество обновленных записей
* @global CotDB $db
*/
function cot_config_set($name, $options, $isModule = false, $category = '')
{
global $db_config; // Получаем доступ к глобальной переменной с настройками базы данных
if (is_bool($isModule)) { // Проверка, является ли $isModule логическим значением
$type = $isModule ? 'module' : 'plug'; // Если $isModule истинно, то это модуль, иначе плагин
} else {
$type = !in_array($isModule, array('plug', 'core')) ? 'module' : $isModule; // Если $isModule не плагин или ядро, то это модуль
}
$upd_cnt = 0; // Инициализируем счетчик обновленных записей
$where = 'config_owner = ? AND config_cat = ? AND config_name = ?'; // Начальная часть запроса для выбора конфигурации по владельцу, категории и имени
if (!empty($category)) { // Если категория не пуста
$where .= ' AND config_subcat = ?'; // Добавляем к запросу условие по подкатегории
if ($category != '__default') { // Если категория не равна '__default'
$default_options = cot_config_load($name, $isModule, '__default'); // Загружаем конфигурацию по умолчанию для указанного расширения
}
$structure_val = array(); // Инициализируем массив для значений структуры
} else {
$structure_val = cot_config_list($type, $name, '__default'); // Загружаем список конфигурации для модуля или плагина
}
$category_tmp = $category; // Сохраняем временно значение категории
foreach ($options as $key => $val) { // Для каждого параметра в массиве опций
if (isset($structure_val[$key]) && is_array($structure_val[$key])) { // Если параметр есть в структуре и он является массивом
$where_sub = ' AND config_subcat = ?'; // Добавляем подкатегорию в условие
$category = '__default'; // Устанавливаем категорию как '__default'
} else {
$where_sub = ''; // Если параметр не массив, то подкатегория не нужна
$category = $category_tmp; // Восстанавливаем исходное значение категории
}
if (empty($category) || $category == '__default' || $val != $default_options[$key]) { // Если категория пуста, категория по умолчанию или значение параметра отличается от дефолтного
$params = empty($category) ? [$type, $name, (string) $key] : [$type, $name, (string) $key, $category]; // Формируем параметры для запроса
$upd_cnt += Cot::$db->update($db_config, ['config_value' => $val], $where . $where_sub, $params); // Выполняем обновление в базе данных и увеличиваем счетчик
}
}
return $upd_cnt; // Возвращаем количество обновленных записей
}
/**
* Обновляет существующие конфигурационные карты, удаляя устаревшие опции, добавляя новые
* опции и изменяя те, которые требуют обновления.
*
* @param string $name Код расширения
* @param array $options Конфигурационные опции
* @param bool $is_module TRUE для модулей, FALSE для плагинов
* @param string $category Код категории структуры. Только для конфигураций на основе категорий
* @param string $donor Имя расширения для внедрения конфигурации от одного расширения к другому
* @return int Количество затронутых записей
*/
function cot_config_update($name, $options, $is_module = false, $category = '', $donor = '')
{
$affected = 0; // Инициализация переменной для подсчета затронутых записей
$old_options = cot_config_load($name, $is_module, $category, $donor); // Загружаем старые конфигурационные опции
// Находим и удаляем опции, которых больше не существует
$remove_opts = array(); // Массив для хранения опций, которые нужно удалить
foreach ($old_options as $old_opt) // Проходим по старым опциям
{
$keep = false; // Флаг, указывающий, следует ли сохранять текущую опцию
foreach ($options as $opt) // Проходим по новым опциям
{
if ($opt['name'] == $old_opt['name']) // Если имя опции совпадает с именем старой опции
{
$keep = true; // Устанавливаем флаг на true, если опция должна быть сохранена
break; // Прерываем цикл
}
}
if (!$keep && $old_opt['type'] != COT_CONFIG_TYPE_HIDDEN) // Если опцию не нужно сохранять и она не скрытая
{
$remove_opts[] = $old_opt['name']; // Добавляем имя опции в список на удаление
}
}
if (count($remove_opts) > 0) // Если есть опции для удаления
{
$affected += cot_config_remove($name, $is_module, $remove_opts, $category, $donor); // Удаляем опции и увеличиваем счетчик
}
// Находим новые опции и опции, которые были изменены
$new_options = array(); // Массив для новых опций
$upd_options = array(); // Массив для измененных опций
foreach ($options as $opt) // Проходим по новым опциям
{
$existed = false; // Флаг, указывающий, существует ли уже такая опция
foreach ($old_options as $old_opt) // Проходим по старым опциям
{
if ($opt['name'] == $old_opt['name']) // Если имя новой опции совпадает с именем старой опции
{
$changed = array_diff($opt, $old_opt); // Находим различия между новой и старой опцией
if (count($changed) > 0) // Если есть различия
{
// Значения для измененных опций сбрасываются на значение по умолчанию
// только если тип и значение по умолчанию изменились
if ($opt['type'] != $old_opt['type'] && $opt['default'] != $old_opt['default'])
{
$opt['value'] = $opt['default']; // Сбрасываем значение опции на значение по умолчанию
}
$upd_options[] = $opt; // Добавляем измененную опцию в массив для обновления
}
$existed = true; // Устанавливаем флаг, что опция существует
break; // Прерываем цикл
}
}
if (!$existed) // Если опция не существует
{
$new_options[] = $opt; // Добавляем опцию в массив новых опций
}
}
if (count($new_options) > 0) // Если есть новые опции
{
$affected += cot_config_add($name, $new_options, $is_module, $category); // Добавляем новые опции и увеличиваем счетчик
}
if (count($upd_options) > 0) // Если есть измененные опции
{
$affected += cot_config_modify($name, $upd_options, $is_module, $category); // Обновляем измененные опции и увеличиваем счетчик
}
return $affected; // Возвращаем количество затронутых записей
}
/**
* Сброс значения конфигурации
*
* @param string $name Имя расширения, к которому относится конфигурация
* @param string $option Имя опции
* @param mixed $is_module Флаг, указывающий, является ли конфигурация для модуля или плагина
* @param string $category Код категории структуры. Только для конфигураций на основе категорий
* @return int Количество обновленных записей
* @global CotDB $db
*/
function cot_config_reset($name, $option, $is_module = false, $category = '')
{
global $db, $db_config;
// Определяем тип конфигурации, если флаг is_module является булевым
if (is_bool($is_module))
{
$type = $is_module ? 'module' : 'plug'; // Если is_module true, то тип 'module', иначе 'plug'
}
else
{
$type = !in_array($is_module, array('plug', 'core')) ? 'module' : $is_module; // Если is_module не является 'plug' или 'core', то тип 'module'
}
// Если указана категория
if (!empty($category))
{
// Удаляем запись конфигурации для заданной категории
$db->delete($db_config, "config_name = ? AND config_owner = ? AND config_cat = ?
AND config_subcat = ?", array($option, $type, $name, $category));
}
else
{
// Если категория не указана, сбрасываем значение конфигурации на значение по умолчанию
$db->query("UPDATE $db_config SET config_value = config_default
WHERE config_name = ? AND config_owner = ? AND config_cat = ? AND (config_subcat = '' OR config_subcat IS NULL OR config_subcat = '__default')", array($option, $type, $name));
}
}
/**
* Получает конфигурации из базы данных
* @param string $owner Владелец ('core', 'plug', 'module')
* @param string $cat Код расширения (страница, форумы и т. д.) или подтип ядра (меню, главное, производительность и т. д.)
* @param string $subcat Категория для модулей, если она существует
* @return array Массив с конфигурациями
*/
function cot_config_list($owner, $cat, $subcat = "")
{
global $db, $db_config;
// Формируем массив условий для выборки
$where = array(
'type' => "config_type != '" . COT_CONFIG_TYPE_HIDDEN . "'", // Исключаем конфигурации с типом COT_CONFIG_TYPE_HIDDEN
'owner' => "config_owner = '" . $db->prep($owner) . "'", // Условие для владельца конфигурации
'cat' => "config_cat = '" . $db->prep($cat) . "'", // Условие для категории конфигурации
'subcat' => empty($subcat) ? "(config_subcat = '' OR config_subcat IS NULL OR config_subcat = '__default')" : "(config_subcat = '" . $db->prep($subcat) . "' OR config_subcat = '__default')" // Условие для подкатегории, если она существует
);
// Собираем все условия в строку
$where_query = implode(" AND ", $where);
// Пытаемся выбрать все строки, индексируя их по имени конфигурации
$sql = $db->query("SELECT * FROM $db_config WHERE $where_query ORDER BY config_subcat ASC, config_order ASC, config_name ASC");
$rs = $sql->fetchAll(PDO::FETCH_ASSOC); // Получаем все строки как ассоциативный массив
$rowset = array(); // Массив для основной конфигурации
$rowset_default = array(); // Массив для конфигурации по умолчанию
// Перебираем все строки
foreach ($rs as $row)
{
$keyx = $row['config_name']; // Ключ для текущей конфигурации
// Если конфигурация по умолчанию
if ($row['config_subcat'] == "__default")
{
$rowset_default[$keyx] = $row; // Добавляем в массив конфигурации по умолчанию
}
else
{
$rowset[$keyx] = $row; // Добавляем в основной массив конфигурации
}
}
// Сливаем массивы для правильного отображения
if (!empty($subcat))
{
// Если подкатегория задана, сливаем значения конфигураций по умолчанию с основной конфигурацией
foreach ($rowset_default as $key => $row)
{
if (!empty($rowset[$key]))
{
$rowset[$key]['config_subdefault'] = $row['config_value']; // Добавляем значение по умолчанию в основную конфигурацию
}
}
$rowset = array_merge($rowset_default, $rowset); // Сливаем массивы
}
else
{
// Если подкатегория не задана, просто объединяем основной массив и массив конфигураций по умолчанию
$rowset = $rowset + $rowset_default;
}
unset($rs); // Освобождаем память от переменной с результатами запроса
return $rowset; // Возвращаем результат
}
/**
* Импортирует данные для значений конфигурации из внешнего источника
*
* @param string|array $name Имя значения или массив имен для списка значений
* @param string $source Тип источника
* @param string $filter Тип фильтра
* @param string $defvalue Значение по умолчанию для отфильтрованных данных
* @see cot_import()
* @return mixed Отфильтрованное значение или массив значений
*/
function cot_config_import($name, $source = 'POST', $filter = 'NOC', $defvalue = null)
{
global $cot_import_filters;
// Если имя не указано, возвращаем null
if (!$name) {
return null;
}
// Если имя не является массивом, преобразуем его в массив
if (!is_array($name)) {
$name = array($name);
$single_value = true; // Устанавливаем флаг, что значение одно
}
$res = array(); // Массив для хранения результатов
// Проходим по каждому имени в массиве
foreach ($name as $idx => $var_name) {
// Определяем тип фильтра для текущего значения
$filter_type = (is_array($filter)) ? ($filter[$var_name] ? $filter[$var_name] : ($filter[$idx] ? $filter[$idx] : 'NOC')) : $filter;
// Импортируем значение без фильтра
$not_filtered = cot_import($var_name, $source, 'NOC');
// Импортируем значение с фильтром
$value = cot_import($var_name, $source, $filter_type);
// Дополнительная фильтрация по имени переменной
if (
!empty($cot_import_filters[$var_name]) &&
is_array($cot_import_filters[$var_name]) &&
sizeof($cot_import_filters[$var_name])
) {
// Применяем фильтр для текущего значения
$value = cot_import($value, 'DIRECT', $var_name);
}
// Если значение является некорректным
if (is_null($value)) {
// Оформляем ошибку с выводом значений
$value_to_show = (in_array($filter_type, array('INT', 'NUM', 'TXT', 'ALP')))
? htmlspecialchars(cot_cutstring(strip_tags($not_filtered), 15)) // Обрезаем строку до 15 символов
: '';
// Получаем заголовок для поля
list($field_title) = cot_config_titles($var_name);
// Сообщение об ошибке
$error_msg = cot_rc('adm_invalid_input', array('value' => $value_to_show, 'field_name' => $field_title));
// Если задано значение по умолчанию
if (!is_null($defvalue)) {
// Присваиваем значение по умолчанию, если оно задано
$value = !is_array($defvalue) ? $defvalue : (isset($defvalue[$var_name]) ? $defvalue[$var_name] : (isset($defvalue[$idx]) ? $defvalue[$idx] : null));
// Добавляем в сообщение информацию о значении по умолчанию
$error_msg .= $value_to_show ? '. '.cot_rc('adm_set_default', htmlspecialchars(strip_tags($value))) : '';
}
// Выводим сообщение об ошибке
cot_message($error_msg, 'error', $var_name);
}
// Добавляем значение в результат
$res[$var_name] = $value;
}
// Если значение одно, возвращаем его, иначе возвращаем массив
return $single_value ? $value : $res;
}
/**
* Сохраняет обновленные значения конфигурации в базе данных
*
* @param string $name Имя расширения или раздела, к которому относится конфигурация
* @param array $optionslist Список опций, возвращаемый функцией cot_config_list()
* @param mixed $is_module Флаг, указывающий, является ли это конфигурацией модуля или плагина
* @param string $update_new_only Обновлять только измененные значения
* @param string $source Источник импортированных данных
* @return boolean|number Количество обновленных значений
*/
function cot_config_update_options($name, &$optionslist, $is_module=false, $update_new_only = true, $source = 'POST')
{
global $cot_import_filters;
// Если optionslist не является массивом, возвращаем false
if (!is_array($optionslist)) return false;
$new_options = array(); // Массив для хранения новых опций
// Проходим по всем опциям конфигурации
foreach ($optionslist as $cfg_name => $cfg_var)
{
// Если это визуальный разделитель или поле без значения, пропускаем
if ($cfg_var['config_type'] == COT_CONFIG_TYPE_SEPARATOR) continue;
$filtered = FALSE; // Флаг для фильтрации
$builtin_filter = FALSE; // Флаг для встроенной фильтрации
// Получаем данные из источника (например, POST) без фильтрации
$data = $raw_input = cot_import($cfg_name, $source, 'NOC');
// Проверяем, если конфигурация имеет пользовательский тип
$custom_type = ($cfg_var['config_type'] == COT_CONFIG_TYPE_CUSTOM)
&& $cfg_var['config_variants']
&& preg_match('#^(\w+)\((.*?)\)$#', $cfg_var['config_variants'], $mt);
// Если пользовательский тип найден, пытаемся применить фильтрацию через пользовательскую функцию
if ($custom_type)
{
$custom_func = $mt[1]; // Имя пользовательской функции
$custom_filter_func = $custom_func . '_filter'; // Имя функции фильтрации
// Если функция фильтрации существует
if (function_exists($custom_filter_func))
{
$callback_params = preg_split('#\s*,\s*#', $mt[2]);
// Преобразуем параметры, убирая кавычки
if (count($callback_params) > 0 && !empty($callback_params[0]))
{
for ($i = 0; $i < count($callback_params); $i++)
{
$callback_params[$i] = str_replace(array("'", '"'), array('', ''), $callback_params[$i]);
}
}
/**
* Фильтрует значение с помощью пользовательской функции
* @param string $data Значение, введенное пользователем
* @param array $cfg_var Данные конфигурации
* ... другие параметры для функции
* @return NULL|mixed Отфильтрованное значение или NULL, если значение не может быть отфильтровано.
* @see cot_config_type_int_filter() как пример
*/
$filtered = call_user_func_array(
$custom_filter_func,
array_merge(array(&$raw_input, $cfg_var), $callback_params)
);
}
else // Если пользовательской функции нет, пробуем встроенные фильтры
{
// Последняя часть имени функции может быть типом встроенного фильтра
list($base_filter) = array_reverse(explode('_', strtoupper($custom_func)));
// Проверяем, если это встроенный фильтр
if (in_array(strtoupper($base_filter), array('INT', 'BOL', 'PSW', 'ALP', 'TXT', 'NUM'))
|| sizeof($cot_import_filters[$base_filter])
)
{
$filtered = cot_config_import($cfg_name, $source, $base_filter);
$builtin_filter = true;
}
}
}
// Если фильтрация не прошла (значение не отфильтровано)
if (is_null($filtered))
{
// Если это встроенная фильтрация, то присваиваем пустое значение
$optionslist[$cfg_name]['config_value'] = $builtin_filter ? '' : $raw_input;
}
else
{
// Если фильтрация прошла успешно, присваиваем отфильтрованное значение
if (false !== $filtered) $data = $filtered;
// Если это массив, сериализуем его
if (is_array($data)) $data = serialize($data);
// Если данные изменились или не обновляем только новые, добавляем опцию для обновления
if ($data != $cfg_var['config_value'] || !$update_new_only)
$new_options[$cfg_name] = $data;
// Обновляем значение в списке опций
$optionslist[$cfg_name]['config_value'] = $data;
}
}
// Если есть новые опции для обновления, применяем их
return (sizeof($new_options)) ? cot_config_set($name, $new_options, $is_module) : 0;
}
/**
* Возвращает HTML-форму ввода для конфигурации
* @param array $cfg_var Массив с параметрами конфигурационной переменной
* @return string HTML-код для ввода данных конфигурации
*/
function cot_config_input($cfg_var)
{
$name = $cfg_var['config_name']; // Имя конфигурационной переменной
$type = $cfg_var['config_type']; // Тип конфигурационной переменной
$value = $cfg_var['config_value']; // Значение конфигурационной переменной
$options = $cfg_var['config_variants']; // Опции для переменной (если есть)
$config_input = ''; // Переменная для хранения HTML-кода ввода
$split_re = '#\s*,\s*#'; // Регулярное выражение для разделения опций по запятой
// В зависимости от типа конфигурации формируем соответствующий HTML-код для ввода
switch ($type) {
case COT_CONFIG_TYPE_STRING:
// Для строкового типа конфигурации создаем текстовое поле ввода
$config_input = cot_inputbox('text', $name, $value);
break;
case COT_CONFIG_TYPE_SELECT:
// Для типа SELECT создаем выпадающий список
if (!empty($options)) {
$params = preg_split($split_re, $options); // Разделяем опции по запятой
$params_titles = cot_config_selecttitles($name, $params); // Получаем заголовки для опций
}
$config_input = (is_array($params)) ? cot_selectbox($value, $name, $params, $params_titles, false) : cot_inputbox('text', $name, $value);
break;
case COT_CONFIG_TYPE_RADIO:
global $L;
// Для типа RADIO создаем радиокнопки
if (!empty($options)) {
// Расширяем радиокнопки, используя список пользовательских значений
$params = preg_split($split_re, $options);
$params_titles = cot_config_selecttitles($name, $params);
if (empty($value)) $value = $cfg_var['config_default']; // Если значение пустое, используем значение по умолчанию
} else {
// Старый стиль с двумя вариантами: "Да" и "Нет"
$params = array(1, 0);
$params_titles = array(Cot::$L['Yes'], Cot::$L['No']);
}
$config_input = cot_radiobox($value, $name, $params, $params_titles, '', ' ');
break;
case COT_CONFIG_TYPE_RANGE:
// Для диапазона чисел создаем выпадающий список с заданным диапазоном значений
$range = preg_split($split_re, $options); // Разделяем диапазон
$params = range($range[0], $range[1], empty($range[2]) ? 1 : $range[2]); // Генерируем массив значений диапазона
$config_input = cot_selectbox($value, $name, $params, $params, false);
break;
case COT_CONFIG_TYPE_CUSTOM:
// Для пользовательских типов пытаемся вызвать соответствующую фильтрующую функцию
if ((preg_match('#^(\w+)\((.*?)\)$#', $options, $mt) && function_exists($mt[1])))
{
$callback_params = preg_split($split_re, $mt[2]);
if (count($callback_params) > 0 && !empty($callback_params[0]))
{
for ($i = 0; $i < count($callback_params); $i++)
{
$callback_params[$i] = str_replace(array("'", '"'), array('', ''), $callback_params[$i]);
}
}
$config_input = call_user_func_array($mt[1], array_merge(array($cfg_var), $callback_params));
}
else
{
// Если функции нет, используем текстовое поле
$config_input = cot_inputbox('text', $name, $value);
}
break;
case COT_CONFIG_TYPE_CALLBACK:
// Для типа CALLBACK вызываем функцию для получения значений
if ((preg_match('#^(\w+)\((.*?)\)$#', $options, $mt) && function_exists($mt[1])))
{
$callback_params = preg_split($split_re, $mt[2]);
if (count($callback_params) > 0 && !empty($callback_params[0]))
{
for ($i = 0; $i < count($callback_params); $i++)
{
$callback_params[$i] = str_replace("'", '', $callback_params[$i]);
$callback_params[$i] = str_replace('"', '', $callback_params[$i]);
}
$params = call_user_func_array($mt[1], $callback_params);
}
else
{
$params = call_user_func($mt[1]);
}
// Проверяем, если это ассоциативный массив (значение => название)
$assoc = (range( 0, count($params) -1 ) != array_keys( $params ));
if ($assoc)
{
$assoc_titles = array_values($params);
$params = array_keys($params);
}
$params_titles = cot_config_selecttitles($name, $params);
if ($assoc && $params_titles == $params) $params_titles = $assoc_titles;
$config_input = cot_selectbox($value, $name, $params, $params_titles, false);
}
break;
case COT_CONFIG_TYPE_HIDDEN:
case COT_CONFIG_TYPE_SEPARATOR:
// Если тип скрытый или разделитель, не создаем форму ввода
break;
default :
// По умолчанию используем текстовое поле для многострочного ввода
$config_input = cot_textarea($name, $value, 8, 56);
break;
}
return $config_input; // Возвращаем HTML-код для ввода
}
/**
* Возвращает заголовок и подсказку для опции конфигурации
* @param string $name Имя конфигурационной переменной
* @param string $text Текст, который будет использоваться, если нет заголовка
* @return array Массив с заголовком и подсказкой
*/
function cot_config_titles($name, $text = '')
{
global $L; // Глобальный массив локализаций
// Если подсказка для данной конфигурации не задана, инициализируем её пустой строкой
if (!isset(Cot::$L['cfg_' . $name . '_hint'])) {
Cot::$L['cfg_' . $name . '_hint'] = '';
}
// Проверяем, если заголовок конфигурации существует и это массив (с заголовком и подсказкой)
if (isset(Cot::$L['cfg_' . $name]) && is_array(Cot::$L['cfg_' . $name])) {
// Если подсказка не установлена, то берем её из массива (если она существует)
Cot::$L['cfg_' . $name . '_hint'] = (isset(Cot::$L['cfg_' . $name][1]) && !isset(Cot::$L['cfg_' . $name . '_hint'])) ?
Cot::$L['cfg_' . $name][1] : Cot::$L['cfg_' . $name . '_hint'];
// Заголовок конфигурации устанавливаем как первый элемент массива
Cot::$L['cfg_' . $name] = Cot::$L['cfg_' . $name][0];
}
// Если текст не пустой, то его экранируем (для безопасности), иначе используем имя конфигурации
$text = !empty($text) ? htmlspecialchars($text) : $name;
// Заголовок - это либо локализованное значение, либо текст, если локализации нет
$title = isset(Cot::$L['cfg_' . $name]) ? Cot::$L['cfg_' . $name] : $text;
// Возвращаем массив из заголовка и подсказки
return array($title, Cot::$L['cfg_' . $name . '_hint']);
}
/**
* Вспомогательная функция для генерации заголовков для выбора.
* @param string $name Текущее имя конфигурации
* @param array $params Массив параметров конфигурации
* @return array Заголовки для выбора
*/
function cot_config_selecttitles($name, $params)
{
// Если заголовки параметров не определены, возвращаем оригинальный массив параметров
if (!isset(Cot::$L['cfg_' . $name . '_params'])) {
return $params;
}
// Загружаем локализованные параметры
$langParam = Cot::$L['cfg_' . $name . '_params'];
// Если локализованные параметры - это строка, преобразуем её в массив
if (!is_array($langParam)) {
$langParam = preg_split('#\s*,\s*#', $langParam);
// Проверяем, если первый элемент выглядит как ассоциативный ключ:значение
if (preg_match('#^[\w-]+\s*:#', $langParam[0])) {
// Поддержка ассоциативных массивов
$temp = [];
foreach ($langParam as $item) {
// Разбираем элементы в формате ключ:значение
if (preg_match('/^(?P<key>[\w-]+)\s*:\s*(?P<value>.*)$/', $item, $matches)) {
$temp[$matches['key']] = $matches['value'];
}
}
// Если есть ассоциативный массив, заменяем оригинальный
if (count($temp) > 0) {
$langParam = $temp;
}
}
}
// Массив для заголовков параметров
$cfgParamsTitles = [];
// Массив ключей локализованных параметров
$langParamsKeys = array_keys($langParam);
// Проверяем, если локализованные параметры - это числовой массив
if (isset($langParamsKeys[0]) && is_numeric($langParamsKeys[0])) {
// Если числовой массив, просто используем его
$cfgParamsTitles = $langParam;
} else {
// Если ассоциативный массив, сопоставляем значения
foreach ($params as $val) {
if (isset($langParam[$val])) {
// Если значение параметра есть в локализованных параметрах, добавляем его
$cfgParamsTitles[] = $langParam[$val];
} else {
// Иначе добавляем оригинальное значение
$cfgParamsTitles[] = $val;
}
}
}
// Возвращаем массив заголовков
return $cfgParamsTitles;
}
Комментарии (0)
Автор контента
Оффлайн
Administrator
Последняя авторизация: 05.07.2026 18:32
- Страница размещена: 24.01.2025 11:11
- Последнее обновление: 24.01.2025 11:47