mirror of
https://github.com/avecms/AVE.cms.git
synced 2026-08-01 08:45:44 +00:00
176 lines
64 KiB
PHP
176 lines
64 KiB
PHP
<?php
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------------------
|
||
| AVE.cms
|
||
|--------------------------------------------------------------------------------------
|
||
| @package AVE.cms
|
||
| @file adminx/Support/SectionHelp.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\Support;
|
||
|
||
defined('BASEPATH') || die('Direct access to this location is not allowed.');
|
||
|
||
/** Contextual help shown on Adminx section pages. */
|
||
class SectionHelp
|
||
{
|
||
public static function forPath($path)
|
||
{
|
||
$path = '/' . trim((string) $path, '/');
|
||
$moduleHelp = self::moduleDefinition($path);
|
||
if ($moduleHelp !== null) {
|
||
return $moduleHelp;
|
||
}
|
||
|
||
foreach (self::definitions() as $definition) {
|
||
if (preg_match($definition['pattern'], $path)) {
|
||
unset($definition['pattern']);
|
||
return $definition;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
protected static function moduleDefinition($path)
|
||
{
|
||
foreach (\App\Common\ModuleManager::all() as $module) {
|
||
$extension = isset($module['admin_extension']) && is_array($module['admin_extension']) ? $module['admin_extension'] : array();
|
||
$url = isset($extension['url']) ? '/' . trim((string) $extension['url'], '/') : '';
|
||
$help = isset($extension['help']) && is_array($extension['help']) ? $extension['help'] : array();
|
||
if ($url === '' || !$help || ($path !== $url && strpos($path, $url . '/') !== 0)) {
|
||
continue;
|
||
}
|
||
|
||
$title = trim(isset($help['title']) ? (string) $help['title'] : (isset($module['name']) ? (string) $module['name'] : ''));
|
||
$description = trim(isset($help['description']) ? (string) $help['description'] : '');
|
||
$items = array();
|
||
foreach (isset($help['items']) && is_array($help['items']) ? $help['items'] : array() as $item) {
|
||
$item = trim((string) $item);
|
||
if ($item !== '') {
|
||
$items[] = $item;
|
||
}
|
||
}
|
||
|
||
if ($title === '' || $description === '' || !$items) {
|
||
return null;
|
||
}
|
||
|
||
return array(
|
||
'title' => AdminLocale::translateMarkup($title),
|
||
'description' => AdminLocale::translateMarkup($description),
|
||
'items' => array_map(array(AdminLocale::class, 'translateMarkup'), $items),
|
||
'note' => AdminLocale::translateMarkup(trim(isset($help['note']) ? (string) $help['note'] : '')),
|
||
'doc' => trim(isset($help['doc']) ? (string) $help['doc'] : ''),
|
||
);
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
protected static function definitions()
|
||
{
|
||
return array(
|
||
self::item('#^/documents/redirects$#', 'Редиректы документов', 'Управляет старыми адресами страниц и перенаправляет посетителей на актуальные документы.', array('Ищите редирект по старому URL, названию или ID документа.', 'Коды 301/308 подходят для постоянного переноса, 302/307 — для временного.', 'Перед удалением убедитесь, что старый адрес больше не используется во внешних ссылках.'), 'Каждый старый URL должен быть уникальным и вести на существующий документ.'),
|
||
self::item('#^/documents/views$#', 'Статистика просмотров', 'Показывает посещаемость документов по дням и помогает находить наиболее востребованные страницы.', array('Период влияет только на подневную статистику.', 'Общий счётчик документа хранится отдельно и не очищается вместе с дневными данными.', 'Очистка статистики необратима и предназначена для обслуживания.'), 'Для бизнес-аналитики используйте внешнюю систему аналитики; здесь хранится внутренняя статистика CMS.'),
|
||
self::item('#^/documents/(?:create|\d+/edit)$#', 'Редактор документа', 'Создаёт и изменяет страницу сайта: её адрес, публикацию, SEO и значения полей выбранной рубрики.', array('Набор полей и их группы задаются рубрикой.', 'Изменение alias может автоматически создать редирект со старого URL.', 'Ctrl+S сохраняет изменения и оставляет редактор открытым; ревизии позволяют вернуться к прошлой версии.'), 'Смена рубрики существующего документа запрещена без отдельной миграции полей.'),
|
||
self::item('#^/documents#', 'Документы', 'Основной реестр страниц и товарных карточек сайта, построенных на рубриках и их полях.', array('Фильтруйте документы по рубрике, состоянию и названию.', 'Публикацию, копирование и удаление можно выполнять из списка или пакетно.', 'Удалённый документ сначала попадает в корзину и только затем может быть удалён окончательно.'), 'Документ №1 и системная страница 404 защищены от окончательного удаления.', 'content/documents'),
|
||
|
||
self::item('#^/catalog/products/shipping$#', 'Доставка и упаковка', 'Показывает готовность товарных данных к расчёту доставки и позволяет редактировать грузовые места без открытия документа.', array('Каждое грузовое место хранит количество, вес и три габарита.', 'Расчёт включается отдельно и использует только полностью заполненную упаковку.', 'Фильтр помогает быстро найти товары без размеров или с выключенным расчётом.'), 'Вес и размеры указываются для одного грузового места; количество умножает итоговые показатели.'),
|
||
self::item('#^/catalog/products(?:/.*)?$#', 'Товары каталога', 'Показывает товарные документы в быстром индексе каталога: цены, остатки, изображения и категории.', array('Переиндексация обновляет проекцию товара из полей документа.', 'Редактор товара остаётся редактором документа, но открывается из каталога.', 'Фильтры списка не меняют данные товара.'), 'После изменения каталоговых полей индекс обычно обновляется автоматически.'),
|
||
self::item('#^/catalog/quick-edit$#', 'Быстрое заполнение товаров', 'Позволяет массово уточнять основные товарные данные прямо в таблице без открытия каждого документа.', array('Изменённая ячейка сохраняется отдельно по AJAX.', 'Фильтры помогают отобрать товары без артикула, цены или обязательных признаков.', 'Состояние строки показывает публикацию и доступность товара.'), 'Используйте редактор документа для полей, которых нет в таблице быстрого заполнения.'),
|
||
self::item('#^/catalog/variant-groups(?:/.*)?$#', 'Группы вариантов', 'Объединяет отдельные товары с собственными URL и артикулами в одну карточку публичного листинга.', array('Основной вариант представляет группу до выбора цвета или исполнения.', 'Порядок вариантов определяет порядок переключателей на публичной карточке.', 'Выключенный вариант остаётся документом, но не предлагается покупателю.'), 'Каждый вариант должен сохранять собственные цену, остаток, URL и артикул.'),
|
||
self::item('#^/catalog/card-templates$#', 'Карточки товаров', 'Управляет Twig-разметкой и CSS товарной карточки отдельно для разных мест публичного сайта.', array('Черновик можно проверять на живых данных товара без публикации.', 'Контексты назначают представление главной странице, каталогу и другим листингам.', 'Ревизии сохраняют историю черновиков и опубликованных версий.'), 'Публичный сайт меняется только после публикации представления и включения нативного режима нужного контекста.'),
|
||
self::item('#^/catalog/filter-templates$#', 'Шаблоны фильтров', 'Управляет Twig-разметкой, CSS и клиентским поведением фасетов товарного каталога.', array('Алгоритм выборки остаётся в каталоге, а шаблон отвечает за представление.', 'Черновик можно проверить до публикации.', 'Теги шаблона выводят название фильтра, варианты и рассчитанные количества товаров.'), 'После публикации проверьте выбор, сброс и повторное применение нескольких фильтров.'),
|
||
self::item('#^/catalog/\d+/\d+$#', 'Конструктор каталога', 'Связывает поле типа catalog с деревом разделов, документами, полями и публичными фильтрами.', array('Разделы задают структуру каталога и набор доступных полей.', 'Условия фильтров определяют SQL-ограничения публичной выборки.', 'Порядок полей и фильтров меняется перетаскиванием и сохраняется сразу.'), 'Изменения структуры влияют на редактор документов и публичный каталог.'),
|
||
self::item('#^/catalog#', 'Каталог', 'Управляет структурами каталогов, товарным индексом, фильтрами и связью с документами.', array('Каждый каталог создаётся для конкретного поля рубрики типа catalog.', 'Товары являются документами и не дублируются в отдельной основной таблице.', 'Для вариантов одного товара используются группы вариантов.'), 'Начинайте с выбора каталога, затем настраивайте разделы, поля и фильтры.', 'content/catalog'),
|
||
|
||
self::item('#^/rubrics(?:/.*)?$#', 'Рубрики и поля', 'Определяет типы документов: набор полей, группы, шаблоны вывода, права и код жизненного цикла.', array('Поля являются отдельными сущностями со своим типом, настройками и шаблоном.', 'Группы организуют длинные формы документов и не влияют на публичный URL.', 'Код до/после сохранения выполняется при изменении документа и требует проверки синтаксиса.'), 'Удаляйте рубрики и поля только после проверки связанных документов и шаблонов.', 'content/rubrics'),
|
||
self::item('#^/themes#', 'Темы публичного сайта', 'Управляет файлами оформления, подключениями CSS/JavaScript и способом сборки публичной страницы.', array('Файлы темы находятся в templates/<код темы> и проходят проверку пути и расширения.', 'Во вкладке «Настройки» выбирается нативная сборка AVE.cms или Twig-оболочка темы.', 'Текстовые файлы получают ревизии; тему можно экспортировать или импортировать безопасным ZIP-пакетом.'), 'Ассеты и переопределения темы работают в обоих режимах сборки.', 'content/themes'),
|
||
self::item('#^/templates#', 'Шаблоны страниц', 'Хранит внешнюю HTML/PHP-оболочку сайта, в которую подставляется содержимое документа и системные теги.', array('Шаблон №1 является системным и не удаляется.', 'Сохранение обновляет БД, файловый кеш и создаёт ревизию.', 'Палитра тегов вставляет зарегистрированные блоки, навигации и значения страницы.'), 'Ошибка PHP в основном шаблоне может нарушить весь публичный сайт — используйте проверку перед сохранением.', 'content/templates'),
|
||
self::item('#^/blocks#', 'Блоки', 'Единый реестр HTML-фрагментов, текста и исполняемой логики для шаблонов и документов.', array('Редактор выбирается отдельно для каждого блока: PHP, HTML, rich text или обычный текст.', 'Основной публичный вызов использует тег sysblock; старый тег block поддерживается как синоним.', 'Группы организуют список и не влияют на публичный вывод.'), 'PHP включайте только для блоков, которым действительно нужна серверная логика.', 'content/blocks'),
|
||
self::item('#^/navigation#', 'Навигация', 'Создаёт меню сайта: шаблоны уровней, пункты, вложенность, условия показа и связи с документами.', array('Каждая навигация имеет собственный тег для вставки в шаблон.', 'Пункты можно сортировать и переносить между уровнями.', 'Связанный документ позволяет не вводить URL вручную.'), 'После изменения структуры очищается кеш соответствующей навигации.', 'content/navigation'),
|
||
self::item('#^/requests#', 'Запросы', 'Формирует выборки документов по рубрикам и полям, а затем выводит их через шаблоны элементов и списка.', array('Условия определяют, какие документы попадут в результат.', 'Основной и постраничный шаблоны отвечают только за представление.', 'External/AJAX-флаги разрешают прямой вызов запроса и требуют осторожности.'), 'Проверяйте запрос после изменения условий: он может использоваться сразу в нескольких страницах.', 'content/requests'),
|
||
self::item('#^/media#', 'Медиа и файлы', 'Управляет файлами в uploads: загрузкой, папками, изображениями, превью и конвертацией WebP.', array('Сетка и список показывают одни и те же физические файлы.', 'Редактор изображений создаёт производные файлы для crop и resize.', 'При удалении исходника удаляются его превью и связанная WebP-копия.'), 'Не размещайте служебные PHP-файлы в uploads; раздел предназначен только для пользовательских медиа.', 'media'),
|
||
|
||
self::item('#^/system/orders#', 'Заказы и корзина', 'Обрабатывает заказы, состояния оплаты и доставки, шаблоны корзины и брошенные сессии покупателей.', array('Карточка заказа фиксирует товары, цены и выбранные варианты на момент оформления.', 'Способы оплаты настраиваются во вкладке «Оплата», реквизиты провайдеров — во вкладке «Gateways».', 'Брошенные корзины очищаются по сроку хранения и доступны для диагностики.'), 'Отмена и возврат могут влиять на платёжный шлюз — проверяйте итоговый статус операции.'),
|
||
self::item('#^/system/customers#', 'Пользователи сайта', 'Управляет публичными аккаунтами, регистрацией, OAuth-гейтами и полями профиля.', array('Email-регистрация, восстановление доступа и создание аккаунта после заказа входят в ядро.', 'Дополнительные поля независимо подключаются к регистрации и личному кабинету.', 'Подключённые OAuth-гейты открывают ту же публичную сессию, что и обычная форма входа.', 'Отключение аккаунта запрещает вход, но сохраняет связанные данные.'), 'Email остаётся основным идентификатором; дополнительные способы входа поставляются отдельными модулями.'),
|
||
self::item('#^/content/contacts/forms/\d+$#', 'Конструктор формы', 'Настраивает поля, шаблон формы, получателей, письмо и поведение после отправки.', array('Теги полей доступны в шаблоне формы и письма.', 'Получатели задаются по одному на строку.', 'История обращений хранит отправленные значения и ответы менеджеров.'), 'После изменения шаблона отправьте тестовое обращение и проверьте письмо.'),
|
||
self::item('#^/content/contacts#', 'Формы и обращения', 'Управляет публичными формами обратной связи и журналом сообщений посетителей.', array('Форма состоит из полей, шаблонов и почтовых настроек.', 'История позволяет менять статус обращения и отвечать посетителю.', 'Копирование формы создаёт независимый набор настроек.'), 'Проверяйте адреса получателей и настройки системной почты перед публикацией формы.'),
|
||
self::item('#^/content/rss#', 'RSS-каналы', 'Публикует документы выбранной рубрики в XML-ленте по настраиваемому адресу /rss/{alias}.', array('Заголовок и описание можно брать из полей рубрики.', 'Количество элементов ограничивает размер выдаваемой ленты.', 'Префикс /rss/ зарезервирован и не может использоваться документами.'), 'После изменения алиаса обновите внешние подписки и проверьте XML из списка.'),
|
||
self::item('#^/content/feeds#', 'Товарные фиды', 'Формирует управляемые XML/YML-выгрузки товаров для партнеров и торговых площадок.', array('Категории определяют товарную выборку, исключения применяются после дочерних разделов.', 'Поля XML и характеристики связываются с индексом каталога или полями товарной рубрики.', 'Предпросмотр не меняет кеш, а пересоздание атомарно публикует новый файл.'), 'Не меняйте закрепленные legacy URL у действующих партнеров; редактируйте связанную конфигурацию фида.'),
|
||
self::item('#^/content/price-import#', 'Импорт цен', 'Обновляет цены товаров из XLSX после обязательного предварительного просмотра изменений.', array('Сначала загрузите файл или получите его по настроенной HTTPS-ссылке.', 'Выберите лист и сформируйте предварительный просмотр.', 'Применение изменяет только подтверждённые строки и записывает результат в историю.'), 'Не применяйте импорт, если предварительный просмотр показывает неожиданные товары или пустые цены.'),
|
||
|
||
self::item('#^/users#', 'Пользователи', 'Управляет сотрудниками, которые могут входить в панель управления, их ролями и активностью.', array('Роль определяет доступные разделы и действия.', 'Отключённый пользователь не может войти, но сохраняется в журнале действий.', 'Нельзя отключить или удалить собственную активную учётную запись.'), 'Публичные аккаунты находятся в отдельном разделе «Пользователи сайта».', 'administration/users'),
|
||
self::item('#^/roles#', 'Роли и права', 'Определяет набор разрешений для администраторов и разграничивает доступ к функциям системы.', array('Права сгруппированы по модулям и назначаются роли целиком.', 'Системная роль admin всегда имеет полный доступ.', 'Роль с назначенными пользователями нельзя удалить.'), 'После изменения роли права применяются при следующей проверке доступа пользователя.'),
|
||
self::item('#^/settings/main$#', 'Основные настройки', 'Управляет публичным доступом, параметрами сайта, почты, документов и вывода.', array('Режим разработки возвращает посетителям временную страницу 503 и закрывает динамические маршруты от индексации.', 'Сотрудник с отдельным правом продолжает видеть документы, модули и API.', 'Поля сгруппированы по назначению, а тип значения проверяется перед сохранением.', 'Секреты устанавливаемых модулей не должны храниться в общих настройках.'), 'Перед открытием сайта переключите публичный доступ и проверьте страницу в отдельном браузере без авторизации.'),
|
||
self::item('#^/settings/interface$#', 'Настройка интерфейса', 'Определяет порядок и видимость пунктов левого меню и виджетов дашборда.', array('Перетаскивание меняет порядок только для панели управления.', 'Скрытие пункта не отменяет право роли и не блокирует прямой URL.', 'Виджеты установленных модулей появляются в списке автоматически.'), 'Доступ к функциям всегда ограничивайте через роли и права, а не только через видимость меню.'),
|
||
self::item('#^/settings/security$#', 'Подтверждение критических действий', 'Управляет повторным запросом пароля перед изменением исполняемого кода и установкой модулей.', array('Подтверждение действует пять минут для текущего пользователя и IP-адреса.', 'Отключение повторного пароля не отменяет проверку прав роли и CSRF-токена.', 'Настройка распространяется на блоки, шаблоны, рубрики, запросы, навигацию и другие операции с кодом.'), 'По умолчанию подтверждение выключено; на рабочем сайте его можно включить как дополнительный защитный слой.'),
|
||
self::item('#^/settings/constants$#', 'Системные константы', 'Хранит типизированные значения конфигурации, доступные ядру и публичному сайту.', array('Тип определяет допустимый редактор и формат сохранённого значения.', 'Перед добавлением проверьте, нет ли уже настройки с тем же назначением.', 'Удаляйте константу только после поиска её использования в коде и шаблонах.'), 'Название константы является программным контрактом и не должно меняться без миграции.'),
|
||
self::item('#^/settings/paginations$#', 'Шаблоны пагинации', 'Настраивает HTML постраничной навигации для документов и сохранённых запросов.', array('Базовый шаблон защищён от удаления.', 'Теги страниц, текущего состояния и ссылок подставляются во время рендера.', 'После изменения проверьте первую, среднюю и последнюю страницы списка.'), 'Шаблон должен сохранять доступность ссылок и понятное обозначение текущей страницы.'),
|
||
self::item('#^/settings/maintenance$#', 'Обслуживание', 'Очищает адресные кеши и накопленные служебные данные AVE.cms.', array('Каждая кнопка работает только со своим источником данных.', 'Очистка кеша не удаляет документы и настройки.', 'Ревизии и статистика просмотров удаляются необратимо.'), 'Перед очисткой данных, которые нельзя восстановить из документов, создайте резервную копию.'),
|
||
self::item('#^/settings/diagnostics$#', 'Диагностика системы', 'Проверяет PHP, расширения, доступ к каталогам, почту, данные модулей и готовность публичной части.', array('Проверки не отправляют письма и не выполняют внешние операции.', 'Красные состояния нужно устранить до публикации.', 'Тяжёлые проверки могут кратковременно кешироваться.'), 'Диагностика ядра не заменяет отдельную проверку подключённых модулей и внешних сервисов.'),
|
||
self::item('#^/settings/files$#', 'Системные файлы', 'Даёт контролируемый редактор разрешённых PHP-файлов конфигурации и пользовательских функций.', array('Список файлов ограничен системой и не является файловым менеджером.', 'Перед сохранением выполняется проверка PHP-синтаксиса.', 'Ошибка в таком файле может повлиять и на панель, и на публичный сайт.'), 'Используйте этот раздел для небольших проектных дополнений; крупную функциональность оформляйте модулем.'),
|
||
self::item('#^/settings/benchmark$#', 'Производительность', 'Измеряет скорость PHP, базы данных, файловых операций и кеша на текущем сервере.', array('Результаты нужны для сравнения одного окружения во времени, а не разных проектов.', 'Повторяйте тест после обновления PHP, БД или инфраструктуры.', 'Тест создаёт кратковременную нагрузку и сохраняет историю запусков.'), 'Оценивайте не только общий балл, но и конкретный медленный этап.'),
|
||
self::item('#^/settings#', 'Настройки системы', 'Собирает общие параметры AVE.cms, интерфейс, константы, пагинацию, обслуживание и диагностику.', array('Каждый подраздел имеет собственный URL и назначение.', 'Типизированные значения проверяются перед сохранением.', 'Настройки устанавливаемых модулей хранятся вместе с соответствующим модулем.'), 'Меняйте только параметры, назначение которых понятно и проверено на текущем сайте.', 'administration/settings'),
|
||
self::item('#^/events#', 'Системные события', 'Объединяет аудит действий, ошибки, SQL-журнал, 404 и статистику внешних переходов.', array('Цвет и состояние помогают отличать успешные операции, предупреждения и ошибки.', 'Переходы группируются по посетителю, странице входа и источнику; открытые IP и произвольные query-параметры не сохраняются.', 'Фильтры и CSV-экспорт работают отдельно для каждого источника.'), 'Очистка журнала необратима и не исправляет причину ошибки.'),
|
||
self::item('#^/security/ip-blocks#', 'IP-блокировки', 'Управляет ручными блокировками адресов, а автоматические ограничения выполняет RateLimiter.', array('Блокировка может быть постоянной или иметь дату окончания.', 'Причина видна администраторам и записывается в аудит.', 'Разблокировка возвращает доступ сразу.'), 'Не блокируйте адрес reverse proxy без проверки реального клиентского IP.'),
|
||
self::item('#^/database#', 'База данных', 'Показывает рабочие таблицы AVE.cms, выполняет обслуживание, применяет миграции ядра и управляет полными резервными копиями.', array('OPTIMIZE применяется только к выбранной таблице или ко всей рабочей схеме после подтверждения.', 'Применение миграций не восстанавливает данные: это отдельная операция развития структуры ядра.', 'Новые дампы используют {{prefix}}, поэтому могут восстанавливаться с другим dbpref.', 'Перед восстановлением система проверяет файл и автоматически создаёт страховочную копию.'), 'Восстановление полностью заменяет таблицы текущей установки; рабочую копию дополнительно храните вне сервера.', 'database'),
|
||
self::item('#^/kanban$#', 'Канбан', 'Организует личные рабочие карточки по колонкам и показывает доску на дашборде.', array('Карточки и порядок сохраняются без перезагрузки.', 'Колонки можно настроить под собственный процесс.', 'Каждый сотрудник работает со своей доской.'), 'Не используйте канбан как журнал системных ошибок: для них предназначены события и Todo.'),
|
||
self::item('#^/notes$#', 'Заметки', 'Хранит личные текстовые заметки сотрудника и закрепляет важные записи на дашборде.', array('Заметки видны только их владельцу.', 'Закрепление влияет на порядок выдачи.', 'Быстрое создание доступно из шапки, если вклад модуля включён.'), 'Не храните в заметках пароли, токены и другие секреты.'),
|
||
self::item('#^/reminders$#', 'Напоминания', 'Хранит личные задачи с датой и выводит просроченные пункты в общем колокольчике.', array('Срок определяет состояние напоминания.', 'Выполнение сохраняется сразу.', 'Просроченный счётчик формируется отдельно для текущего сотрудника.'), 'Удаление напоминания необратимо; выполненный пункт можно вернуть в работу.'),
|
||
self::item('#^/notfound$#', 'Ошибки 404', 'Показывает несуществующие публичные адреса и помогает создать документ или редирект.', array('Сначала проверьте источник переходов и частоту запроса.', 'Для старого адреса существующей страницы создайте постоянный редирект.', 'Случайные сканирующие URL можно удалить из журнала без создания страницы.'), 'Редирект должен вести на действительно соответствующий документ, а не всегда на главную.'),
|
||
self::item('#^/seo-audit$#', 'SEO-аудит', 'Проверяет документы на технические и редакционные проблемы, влияющие на поисковое представление.', array('Фильтры помогают сначала разобрать критичные и массовые проблемы.', 'Оценка документа складывается из нескольких независимых проверок.', 'Исправления выполняются в редакторе документа и проявляются после повторного аудита.'), 'Автоматическая оценка является подсказкой и не заменяет проверку содержания страницы.', 'modules/seo-audit'),
|
||
self::item('#^/todo$#', 'Todo', 'Хранит личный список замечаний и следующих действий для текущего администратора.', array('Задачу можно создать из выпадающего списка в шапке на любой странице панели управления.', 'Чекбокс сразу сохраняет состояние без перезагрузки.', 'Каждый пользователь видит только собственные задачи.'), 'Удаление задачи необратимо; выполненные пункты можно вернуть в работу обычным чекбоксом.'),
|
||
self::item('#^/modules/search#', 'Поиск по сайту', 'Индексирует опубликованные документы и формирует обычную страницу результатов и JSON для живого поиска.', array('Области позволяют отдельно искать по товарам, новостям, статьям и другим наборам рубрик.', 'Алгоритм определяет разбор строки и вес точного совпадения, заголовка и содержимого.', 'Штатный HTML можно заменить шаблонами модуля или существующим запросом документов.'), 'После изменения рубрик или индексируемых полей выполните полную переиндексацию.', 'modules/search'),
|
||
self::item('#^/modules/commerceml#', 'Обмен с 1С', 'Принимает CommerceML по защищённому адресу и сопоставляет товары 1С с документами товарной рубрики.', array('Для каждой интеграции создайте отдельный профиль с логином, паролем и рубрикой.', 'Поля внешнего ID, артикула, цены и остатка выбираются из полей назначенной рубрики.', 'Пробный XML можно загрузить из списка профилей и проверить результат в журнале обменов.'), 'Создание неизвестных товаров выключено по умолчанию: сначала проверьте сопоставление на существующих документах.'),
|
||
self::item('#^/modules/experiments#', 'A/B-тесты', 'Распределяет посетителей между вариантами и собирает показы и конверсии без смешивания вариантов в общем кеше.', array('Код эксперимента используется в шаблонном теге, API и атрибутах отслеживания конверсии.', 'Вес вариантов задаёт распределение внутри выбранного охвата аудитории.', 'Черновик не участвует в публичном выводе; перед запуском проверьте каждый вариант.'), 'Изменение цены штатным вариантом влияет только на представление и не меняет сумму заказа.'),
|
||
self::item('#^/modules/popups#', 'Всплывающие окна', 'Управляет кампаниями поп-апов, условиями показа, A/B-вариантами и собранными заявками.', array('Сценарий определяет момент показа: задержку, прокрутку или попытку ухода.', 'Пути включения и исключения принимают по одному шаблону на строку; символ * заменяет произвольную часть.', 'HTML вариантов контролируется проектом, а заявки обрабатываются на отдельной вкладке.'), 'Проверьте кампанию на тестовом URL и мобильном экране до включения автоматической вставки.'),
|
||
self::item('#^/modules/reviews#', 'Отзывы', 'Собирает оценки и отзывы к документам, позволяет модерировать публикацию и отвечать от имени компании.', array('Состояние определяет, попадёт ли отзыв в публичный список.', 'Признак подтверждённой покупки устанавливается только после проверки заказа.', 'Ответ компании сохраняется вместе с отзывом и выводится его публичным шаблоном.'), 'Удаление отзыва необратимо; спорный материал безопаснее сначала отклонить.', 'modules/reviews'),
|
||
self::item('#^/modules/scheduler#', 'Планировщик', 'Собирает фоновые задачи установленных модулей в единый реестр, запускает их по расписанию и хранит журнал.', array('Защищённый URL добавляется в планировщик хостинга с интервалом в одну минуту.', 'Cron-выражение и активность каждой задачи меняются прямо в таблице.', 'Ручной запуск использует тот же обработчик и попадает в общий журнал.'), 'После смены секретного URL обновите задание на хостинге: старый адрес сразу перестанет работать.'),
|
||
self::item('#^/modules/search-analytics#', 'Поисковые запросы', 'Показывает реальные поисковые фразы посетителей, частоту запросов и случаи пустой выдачи.', array('Фильтр по области разделяет спрос по новостям, товарам, статьям и другим настроенным областям.', 'Состояние и заметка позволяют отмечать разобранные запросы и планировать новый контент.', 'Показатель «Результатов сейчас» помогает проверить, исправлена ли нулевая выдача.'), 'Очистка истории удаляет аналитические данные, но не затрагивает поисковый индекс.'),
|
||
self::item('#^/modules/system-health#', 'Мониторинг здоровья', 'Проверяет доступность базы данных, диска, фоновых задач и заданных внешних HTTPS-сервисов.', array('Текущий снимок показывает независимый результат каждой инфраструктурной проверки.', 'История помогает отличить разовый сбой от повторяющейся проблемы.', 'Для внешнего сервиса задаются ожидаемый HTTP-статус и ограниченный таймаут.'), 'Мониторинг фиксирует рабочее состояние, но не заменяет приёмочный чек-лист перед запуском сайта.'),
|
||
self::item('#^/modules/traffic-analytics#', 'Источники трафика', 'Группирует входящие переходы по UTM-меткам, источникам, кампаниям и посадочным страницам.', array('Период и фильтры одинаково применяются к сводке, таблицам и CSV-экспорту.', 'UTM-конструктор собирает ссылку в браузере и не сохраняет введённые значения.', 'Один вход посетителя на ту же страницу за день агрегируется без хранения IP.'), 'Перед очисткой выбранного периода выгрузите CSV, если данные нужны для отчётности.'),
|
||
self::item('#^/modules/antispam#', 'Антиспам', 'Настраивает профили защиты публичных форм и показывает журнал отклонённых отправок.', array('Профиль объединяет лимиты, проверку скрытого поля и анализ содержимого для конкретной формы.', 'Журнал объясняет, какое правило остановило запрос, и помогает скорректировать чувствительность.', 'Доверенные исключения применяйте только к контролируемым интеграциям.'), 'Не ослабляйте общий профиль из-за одного ложного срабатывания: создайте отдельный профиль для нужной формы.'),
|
||
self::item('#^/modules/interactions#', 'Взаимодействия', 'Хранит общий журнал оценок, реакций и голосов, который используют прикладные модули.', array('Канал определяет допустимые действия и типы объектов.', 'Агрегаты ускоряют публичный вывод счётчиков и рейтингов.', 'Пересчёт восстанавливает агрегаты из исходного журнала действий.'), 'Обычно этот раздел не вставляется в шаблон напрямую: публичный интерфейс предоставляют рейтинги, опросы и комментарии.'),
|
||
self::item('#^/modules/ratings#', 'Рейтинги', 'Добавляет на документы и другие разрешённые объекты настраиваемую шкалу оценок.', array('Документы включают страницы, товары, новости и статьи, построенные на рубриках.', 'Тег [mod_rating] использует текущий документ; явная цель задаётся типом и ID.', 'Кнопки тегов вставляют доступные значения в HTML-шаблон рейтинга.'), 'Голоса и итоговые значения хранятся в модуле «Взаимодействия».'),
|
||
self::item('#^/modules/comments#', 'Комментарии', 'Управляет древовидными обсуждениями, премодерацией, антиспамом и публичными шаблонами.', array('Разрешите только те типы объектов, на которых действительно нужны обсуждения.', 'Шаблоны списка, комментария и формы имеют собственные наборы тегов.', 'Публикация, скрытие и отметка спама выполняются из общего журнала.'), 'Оценка комментариев использует отдельный канал модуля «Взаимодействия».'),
|
||
self::item('#^/modules/polls#', 'Опросы', 'Создаёт одиночные и множественные голосования с расписанием, доступом и управляемыми результатами.', array('Для публикации вставьте [mod_poll:ID] в документ, блок или шаблон.', 'Архив опросов доступен по настраиваемому публичному URL.', 'Шаблоны опроса, варианта, результата и архива имеют разные наборы тегов.'), 'Удаление опроса также удаляет варианты ответа и собранные голоса.'),
|
||
self::item('#^/modules/galleries#', 'Галереи', 'Собирает изображения медиатеки в упорядоченные сетки и слайдеры с собственными публичными URL.', array('Файлы остаются в медиатеке; галерея хранит только связи, подписи и порядок.', 'Тег [mod_gallery:ID] выводит коллекцию в документе, блоке или шаблоне.', 'Отдельные шаблоны управляют оболочкой галереи и разметкой одного изображения.'), 'Удаление галереи не удаляет исходные файлы и созданные для них превью.'),
|
||
self::item('#^/modules/banners#', 'Баннеры', 'Управляет рекламными местами, ротацией изображений, расписанием и внутренней статистикой переходов.', array('Стабильный код места используется в теге [mod_banner:КОД].', 'Вес влияет только на стратегию «По весу»; равномерная ротация выбирает материал с меньшим числом показов.', 'Изображения выбираются из общей медиатеки и не удаляются вместе с баннером.'), 'Нулевой лимит означает отсутствие ограничения; сброс статистики необратим.'),
|
||
self::item('#^/modules/faq#', 'Вопросы и ответы', 'Собирает частые вопросы в независимые коллекции с публичной страницей, тегом, API и Schema.org.', array('Стабильный код используется в теге [mod_faq:КОД] и публичном URL.', 'Ответ оформляется в rich-text редакторе, а вопросы сортируются перетаскиванием.', 'JSON-LD включает только опубликованные вопросы опубликованной коллекции.'), 'После изменения базового URL обновите ссылки в шаблонах и проверьте, что новый адрес не занят документом.'),
|
||
self::item('#^/modules/related#', 'Похожие материалы', 'Подбирает связанные документы по смыслу, ручным связям или стабильному кольцевому порядку.', array('Профиль задаёт рубрики, признаки, веса, лимит и способ вывода.', 'Встроенный шаблон подходит для новой разметки, существующий запрос — для готовых карточек сайта.', 'Тег [mod_related:КОД] использует текущий документ, а API принимает его ID явно.'), 'После установки и массового изменения контента перестройте индекс на отдельной вкладке.'),
|
||
self::item('#^/modules/demo-site#', 'Демо-сайт', 'Устанавливает и обслуживает демонстрационный набор рубрик, документов, навигации и шаблонов.', array('Предпросмотр показывает состав пакета до изменения данных.', 'Повторное применение использует ledger пакета и не должно создавать дубли.', 'Удаление затрагивает только сущности, созданные этим пакетом.'), 'Перед применением к сайту с собственным контентом сделайте резервную копию базы и публичных шаблонов.'),
|
||
self::item('#^/modules/site-readiness#', 'Готовность сайта', 'Проверяет базовые настройки, документы, шаблоны, модули и окружение перед публикацией сайта.', array('Профиль определяет глубину и набор проверяемых подсистем.', 'Красные результаты требуют исправления, предупреждения нужно оценивать в контексте проекта.', 'Проверка ничего не исправляет и не меняет публичные данные.'), 'Запускайте полный профиль после изменения структуры сайта, модулей или серверного окружения.'),
|
||
self::item('#^/modules/vk-oauth#', 'Вход через VK ID', 'Подключает VK ID как дополнительный способ входа в существующий публичный аккаунт.', array('Redirect URL из настроек должен точно совпадать с адресом в приложении VK.', 'Внешний профиль связывается с общим аккаунтом сайта, а не создаёт отдельную сессию.', 'Секрет приложения хранится только в настройках gateway.'), 'Проверяйте вход и привязку на тестовом аккаунте после смены домена или ключей приложения.'),
|
||
self::item('#^/modules/yandex-oauth#', 'Вход через Яндекс', 'Подключает Яндекс ID как дополнительный способ входа в существующий публичный аккаунт.', array('Callback URL из настроек должен точно совпадать с адресом OAuth-приложения.', 'Полученный профиль связывается с общим аккаунтом сайта.', 'Client secret не должен попадать в публичный шаблон или журнал.'), 'После смены домена обновите callback в панели Яндекса и настройках модуля.'),
|
||
self::item('#^/modules/smsc-auth#', 'Вход по SMS', 'Подключает SMSC для регистрации и беспарольного входа по подтверждённому номеру телефона.', array('API-ключ хранится в закрытом хранилище секретов.', 'Код одноразовый, ограничен по времени и числу попыток.', 'Телефон относится к общей учётной записи, поэтому отдельный профиль для SMS не создаётся.'), 'Перед включением на сайте отправьте тестовое SMS и проверьте вход существующего и нового пользователя.', 'modules/smsc-auth'),
|
||
self::item('#^/modules/yookassa#', 'YooKassa', 'Настраивает платёжный gateway YooKassa для оформления и контроля оплаты заказов.', array('Идентификатор магазина и секрет применяются только серверной частью.', 'Callback принимает изменения статуса от платёжной системы.', 'Тестовый и рабочий режимы должны использовать соответствующие ключи.'), 'Перед рабочим запуском проведите полный тест оплаты, отмены и повторного callback.'),
|
||
self::item('#^/modules/psb#', 'ПСБ / СФР', 'Настраивает отдельный платёжный сценарий для товаров, доступных к оплате электронным сертификатом.', array('Признак доступности и код ТСР задаются в данных товара.', 'Gateway показывается только для подходящего состава заказа.', 'Ключи и callback используются только серверной частью.'), 'Проверьте смешанную корзину и отказ платежа до включения способа на публичном сайте.'),
|
||
self::item('#^/modules/file-security#', 'Безопасность файлов', 'Проверяет целостность исходников по SHA-256, ищет подозрительные конструкции и защищает загрузки.', array('Сначала сформируйте эталон на заведомо проверенной версии проекта.', 'Находка эвристики является причиной проверить файл, а не доказательством заражения.', 'В карантин автоматически можно переместить только загрузки и временные вложения; системные файлы исправляются вручную.'), 'Не принимайте изменённый файл в эталон, пока не проверили причину изменения и его содержимое.'),
|
||
self::item('#^/modules#', 'Модули', 'Устанавливает и обновляет расширения сайта, их таблицы, шаблоны и состояние подключения.', array('Системные функции не отображаются как устанавливаемые модули.', 'Update применяет предусмотренную модулем схему обновления.', 'Uninstall может удалить данные модуля, если это заложено его manifest.'), 'Проверяйте совместимость версии и резервную копию перед обновлением модуля.', 'modules'),
|
||
self::item('#^/system/document-jobs#', 'Операции с документами', 'Запускает управляемые пакетные задания над документами и хранит подробный журнал выполнения.', array('Сначала проверьте PHP-синтаксис задания.', 'Runner выполняет работу пошагово с lock и отображает прогресс.', 'Остановленное задание можно проанализировать по журналу.'), 'Большие задания запускайте небольшими пакетами и контролируйте ошибки после каждого шага.'),
|
||
self::item('#^/system/console#', 'PHP-консоль', 'Выполняет административные PHP-сниппеты в ограниченном дочернем процессе для диагностики и обслуживания.', array('Сниппеты можно сохранять для повторного использования.', 'Перед запуском выполняется проверка синтаксиса.', 'Время, память и размер вывода ограничены.'), 'Консоль выполняет код с доступом к системе — используйте её только для проверенных операций.', 'administration/console'),
|
||
self::item('#^/system/updates#', 'Обновления AVE.cms', 'Устанавливает подписанные патчи ядра с проверкой локальных изменений, резервной копией и откатом.', array('Источник использует отдельный публичный RSA-ключ владельца AVE.cms.', 'Подготовка патча не меняет файлы до отдельного подтверждения установки.', 'Незавершённое задание продолжается с сохранённого этапа после повторного открытия раздела.'), 'Не удаляйте storage/updates и не копируйте полную сборку поверх сайта во время активного обновления.', 'administration/updates'),
|
||
self::item('#^/(?:dashboard)?$#', 'Дашборд', 'Показывает общее состояние AVE.cms, последние документы и виджеты установленных модулей.', array('Состав и порядок виджетов меняются в настройках интерфейса.', 'Последние документы и системные показатели дают быстрый переход в профильный раздел.', 'Модульные виджеты появляются только у пользователей с соответствующим правом.'), 'Используйте дашборд как отправную точку, а подробные данные смотрите в соответствующих разделах.', 'administration/dashboard'),
|
||
);
|
||
}
|
||
|
||
protected static function item($pattern, $title, $description, array $items, $note, $doc = '')
|
||
{
|
||
return array(
|
||
'pattern' => $pattern,
|
||
'title' => AdminLocale::translateMarkup($title),
|
||
'description' => AdminLocale::translateMarkup($description),
|
||
'items' => array_map(array(AdminLocale::class, 'translateMarkup'), $items),
|
||
'note' => AdminLocale::translateMarkup($note),
|
||
'doc' => (string) $doc,
|
||
);
|
||
}
|
||
}
|