Files
2026-07-27 12:58:44 +03:00

472 lines
15 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/*
|--------------------------------------------------------------------------------------
| AVE.cms
|--------------------------------------------------------------------------------------
| @package AVE.cms
| @file adminx/modules/Settings/Schema.php
| @author AVE.cms <support@ave-cms.ru>
| @copyright 2007-2026 (c) AVE.cms
| @link https://ave-cms.ru
| @version 3.3
*/
namespace App\Adminx\Settings;
defined('BASEPATH') || die('Direct access to this location is not allowed.');
use App\Adminx\Support\AdminLocale;
use App\Common\SystemTables;
use App\Frontend\ThemeSettings;
/**
* Описание системных настроек, переносимых из legacy source settings в
* key/value App\Common\Settings. Один источник для UI, validation и мигратора.
*/
class Schema
{
public static function groups()
{
$groups = array(
'access' => array(
'label' => 'Доступ к сайту',
'icon' => 'ti ti-lock-access',
'tile_bg' => 'var(--amber-100)',
'tile_fg' => 'var(--amber-600)',
'description' => 'Публичный доступ и временное закрытие сайта на время разработки.',
'sort_order' => 5,
),
'site' => array(
'label' => 'Сайт',
'icon' => 'ti ti-world',
'tile_bg' => 'var(--blue-100)',
'tile_fg' => 'var(--blue-600)',
'description' => 'Название, страна, дата и системные страницы.',
'sort_order' => 10,
),
'mail' => array(
'label' => 'Почта',
'icon' => 'ti ti-mail',
'tile_bg' => 'var(--green-100)',
'tile_fg' => 'var(--green-600)',
'description' => 'Отправитель, транспорт и шаблон письма.',
'sort_order' => 20,
),
'pagination' => array(
'label' => 'Пагинация',
'icon' => 'ti ti-list-numbers',
'tile_bg' => 'var(--violet-100)',
'tile_fg' => 'var(--violet-600)',
'description' => 'Шаблоны постраничной навигации по умолчанию.',
'sort_order' => 30,
),
'breadcrumbs' => array(
'label' => 'Хлебные крошки',
'icon' => 'ti ti-route',
'tile_bg' => 'var(--cyan-100)',
'tile_fg' => 'var(--cyan-600)',
'description' => 'Шаблоны цепочки навигации документа.',
'sort_order' => 40,
),
);
return $groups;
}
public static function definitions()
{
$definitions = array(
'site_access_mode' => array(
'label' => 'Режим публичного доступа',
'type' => 'select',
'group' => 'access',
'options' => array(
'public' => 'Сайт открыт',
'development' => 'Режим разработки',
),
'default' => 'public',
'public_shell' => false,
'sort_order' => 10,
),
'site_development_title' => array(
'label' => 'Заголовок закрытой страницы',
'type' => 'string',
'group' => 'access',
'default' => 'Сайт готовится к запуску',
'max' => 160,
'public_shell' => false,
'depends' => array('site_access_mode' => 'development'),
'sort_order' => 20,
),
'site_development_message' => array(
'label' => 'Сообщение посетителям',
'type' => 'text',
'group' => 'access',
'default' => 'Мы завершаем настройку сайта. Пожалуйста, зайдите немного позже.',
'max' => 1000,
'public_shell' => false,
'depends' => array('site_access_mode' => 'development'),
'sort_order' => 30,
),
'site_name' => array(
'label' => 'Название сайта',
'type' => 'string',
'group' => 'site',
'required' => true,
'max' => 255,
'sort_order' => 10,
),
'default_country' => array(
'label' => 'Страна по умолчанию',
'type' => 'select',
'group' => 'site',
'options' => self::countryOptions(),
'default' => 'RU',
'sort_order' => 20,
),
'date_format' => array(
'label' => 'Формат даты',
'type' => 'select',
'group' => 'site',
'options' => array(
'%d.%m.%Y' => '09.07.2026',
'%d %B %Y' => '09 июля 2026',
'%A, %d.%m.%Y' => 'Четверг, 09.07.2026',
'%A, %d %B %Y' => 'Четверг, 09 июля 2026',
),
'default' => '%d.%m.%Y',
'sort_order' => 30,
),
'time_format' => array(
'label' => 'Формат даты и времени',
'type' => 'select',
'group' => 'site',
'options' => array(
'%d.%m.%Y, %H:%M' => '09.07.2026, 12:30',
'%d %B %Y, %H:%M' => '09 июля 2026, 12:30',
'%A, %d.%m.%Y (%H:%M)' => 'Четверг, 09.07.2026 (12:30)',
'%A, %d %B %Y (%H:%M)' => 'Четверг, 09 июля 2026 (12:30)',
),
'default' => '%d.%m.%Y, %H:%M',
'sort_order' => 40,
),
'use_doctime' => array(
'label' => 'Учитывать дату публикации документа',
'type' => 'bool',
'group' => 'site',
'default' => true,
'sort_order' => 50,
),
'page_not_found_id' => array(
'label' => 'ID страницы 404',
'type' => 'int',
'group' => 'site',
'default' => 2,
'min' => 1,
'sort_order' => 60,
),
'message_forbidden' => array(
'label' => 'Текст запрета доступа',
'type' => 'text',
'group' => 'site',
'sort_order' => 70,
),
'hidden_text' => array(
'label' => 'Текст скрытого блока',
'type' => 'text',
'group' => 'site',
'sort_order' => 80,
),
'mail_from_name' => array(
'label' => 'Имя отправителя',
'type' => 'string',
'group' => 'mail',
'max' => 255,
'sort_order' => 10,
),
'mail_from' => array(
'label' => 'Email отправителя',
'type' => 'email',
'group' => 'mail',
'max' => 255,
'sort_order' => 20,
),
'mail_type' => array(
'label' => 'Транспорт',
'type' => 'select',
'group' => 'mail',
'options' => array(
'mail' => 'PHP mail()',
'smtp' => 'SMTP',
'sendmail' => 'Sendmail',
),
'default' => 'mail',
'sort_order' => 30,
),
'mail_host' => array(
'label' => 'SMTP-сервер',
'type' => 'string',
'group' => 'mail',
'max' => 255,
'depends' => array('mail_type' => 'smtp'),
'sort_order' => 40,
),
'mail_port' => array(
'label' => 'SMTP-порт',
'type' => 'int',
'group' => 'mail',
'default' => 25,
'min' => 1,
'max' => 65535,
'depends' => array('mail_type' => 'smtp'),
'sort_order' => 50,
),
'mail_smtp_encrypt' => array(
'label' => 'SMTP-шифрование',
'type' => 'select',
'group' => 'mail',
'options' => array('' => 'Без шифрования', 'ssl' => 'SSL', 'tls' => 'TLS'),
'depends' => array('mail_type' => 'smtp'),
'sort_order' => 60,
),
'mail_smtp_login' => array(
'label' => 'SMTP-логин',
'type' => 'string',
'group' => 'mail',
'max' => 255,
'depends' => array('mail_type' => 'smtp'),
'sort_order' => 70,
),
'mail_smtp_pass' => array(
'label' => 'SMTP-пароль',
'type' => 'password',
'group' => 'mail',
'max' => 255,
'sensitive' => true,
'depends' => array('mail_type' => 'smtp'),
'sort_order' => 80,
),
'mail_sendmail_path' => array(
'label' => 'Путь к sendmail',
'type' => 'string',
'group' => 'mail',
'default' => '/usr/sbin/sendmail',
'max' => 255,
'depends' => array('mail_type' => 'sendmail'),
'sort_order' => 90,
),
'mail_word_wrap' => array(
'label' => 'Перенос строк',
'type' => 'int',
'group' => 'mail',
'default' => 50,
'min' => 0,
'max' => 1000,
'sort_order' => 100,
),
'mail_content_type' => array(
'label' => 'Тип письма',
'type' => 'select',
'group' => 'mail',
'options' => array('text/plain' => 'Text', 'text/html' => 'HTML'),
'default' => 'text/plain',
'sort_order' => 110,
),
'mail_new_user' => array(
'label' => 'Письмо новому пользователю',
'type' => 'text',
'group' => 'mail',
'default' => "Здравствуйте, %NAME%!\n\nВаш аккаунт на сайте %HOST% создан. Теперь вы можете войти, используя данные регистрации.",
'sort_order' => 120,
),
'mail_signature' => array(
'label' => 'Подпись письма',
'type' => 'text',
'group' => 'mail',
'default' => "С уважением,\nкоманда сайта.",
'sort_order' => 130,
),
) + self::navigationDefinitions();
return $definitions;
}
public static function legacyKeys()
{
return array_keys(self::definitions());
}
public static function definition($key)
{
$defs = self::definitions();
return isset($defs[$key]) ? $defs[$key] : null;
}
public static function groupedFields()
{
$groups = self::groups();
$defs = self::definitions();
foreach ($groups as $code => $group) {
$groups[$code]['label'] = AdminLocale::translateMarkup($group['label']);
$groups[$code]['description'] = AdminLocale::translateMarkup($group['description']);
$groups[$code]['fields'] = array();
}
foreach ($defs as $key => $definition) {
$group = isset($definition['group']) ? $definition['group'] : 'site';
if (!isset($groups[$group])) {
$groups[$group] = array(
'label' => $group,
'icon' => 'ti ti-settings',
'description' => '',
'sort_order' => 100,
'fields' => array(),
);
}
$definition['key'] = $key;
$definition['value'] = Model::value($key, isset($definition['default']) ? $definition['default'] : '');
$definition = self::localizeDefinition($definition);
if (isset($definition['type']) && $definition['type'] === 'section_order') {
$definition['order_items'] = ThemeSettings::sectionOrder(
$definition['value'],
isset($definition['options']) ? $definition['options'] : array()
);
}
$groups[$group]['fields'][] = $definition;
}
uasort($groups, function ($a, $b) {
return (int) $a['sort_order'] < (int) $b['sort_order'] ? -1 : 1;
});
foreach ($groups as $code => $group) {
usort($groups[$code]['fields'], function ($a, $b) {
$sa = isset($a['sort_order']) ? (int) $a['sort_order'] : 100;
$sb = isset($b['sort_order']) ? (int) $b['sort_order'] : 100;
if ($sa === $sb) {
return strcmp($a['key'], $b['key']);
}
return $sa < $sb ? -1 : 1;
});
}
return $groups;
}
protected static function localizeDefinition(array $definition)
{
foreach (array('label', 'description', 'hint') as $key) {
if (isset($definition[$key]) && is_string($definition[$key])) {
$definition[$key] = AdminLocale::translateMarkup($definition[$key]);
}
}
if (isset($definition['options']) && is_array($definition['options'])) {
foreach ($definition['options'] as $value => $label) {
if (is_string($label)) {
$definition['options'][$value] = AdminLocale::translateMarkup($label);
}
}
}
return $definition;
}
protected static function countryOptions()
{
$fallback = array('RU' => 'Россия');
try {
$table = SystemTables::table('countries');
$exists = \DB::query('SHOW TABLES LIKE %s', $table)->getValue();
if (!$exists) {
return $fallback;
}
$rows = \DB::query(
'SELECT country_code, country_name FROM ' . $table . " WHERE country_status = '1' ORDER BY country_name ASC"
)->getAll();
$out = array();
foreach ((array) $rows as $row) {
$out[(string) $row['country_code']] = (string) $row['country_name'];
}
return !empty($out) ? $out : $fallback;
} catch (\Throwable $e) {
return $fallback;
}
}
protected static function navigationDefinitions()
{
$pagination = array(
'navi_box' => array('Контейнер пагинации', '<ul class="pagination">%s</ul>'),
'start_label' => array('Ссылка «Первая»', 'Первая'),
'end_label' => array('Ссылка «Последняя»', 'Последняя'),
'separator_label' => array('Метка пропуска страниц', '…'),
'next_label' => array('Ссылка «Следующая»', '»'),
'prev_label' => array('Ссылка «Предыдущая»', '«'),
'total_label' => array('Метка общего количества', 'Страница %d из %d'),
'link_box' => array('Контейнер ссылки', '<li>%s</li>'),
'total_box' => array('Контейнер итога', '<span>%s</span>'),
'active_box' => array('Контейнер активной страницы', '<li class="active">%s</li>'),
'separator_box' => array('Контейнер разделителя', '<li>%s</li>'),
);
$breadcrumbs = array(
'bread_box' => array('Контейнер хлебных крошек', '<ol class="breadcrumb">%s</ol>'),
'bread_separator' => array('Разделитель хлебных крошек', '<li aria-hidden="true">/</li>'),
'bread_link_box' => array('Контейнер ссылки крошки', '<li>%s</li>'),
'bread_link_template' => array('Шаблон ссылки крошки', '<a href="[link]">[name]</a>'),
'bread_self_box' => array('Контейнер текущей страницы', '<li aria-current="page">%s</li>'),
);
$defs = array();
$sort = 10;
foreach ($pagination as $key => $definition) {
$defs[$key] = array(
'label' => $definition[0],
'type' => 'string',
'group' => 'pagination',
'default' => $definition[1],
'sort_order' => $sort,
);
$sort += 10;
}
$sort = 10;
foreach ($breadcrumbs as $key => $definition) {
$defs[$key] = array(
'label' => $definition[0],
'type' => 'string',
'group' => 'breadcrumbs',
'default' => $definition[1],
'sort_order' => $sort,
);
$sort += 10;
}
foreach (array(
'bread_show_main' => 'Показывать главную',
'bread_show_host' => 'Показывать хост',
'bread_separator_use' => 'Использовать разделитель',
'bread_link_box_last' => 'Последняя крошка как ссылка',
) as $key => $label) {
$defs[$key] = array(
'label' => $label,
'type' => 'bool',
'group' => 'breadcrumbs',
'default' => true,
'sort_order' => $sort,
);
$sort += 10;
}
return $defs;
}
}