Что исправлено
- •Ошибки программы обновления: сбой записи файлов и
version.json, патчи и предпросмотр изменений не поддерживаются, репозиторий недоступен и успешное подключение.
- •Страница обновления: ошибка ответа сервера, «N файлов изменено · N коммитов», готово, применение, задание зарегистрировано, превышено время ожидания и откат завершён.
- •Уведомления о регистрации задания: сообщение о том, что задание на обновление, патч или откат поставлено в очередь.
- •Привилегированный обработчик (cron) теперь оставляет свои сообщения о ходе работы, а также уведомления об успехе и неудаче на правильном языке.
Страница журнала изменений (`/changelog`)
- •Относительное время вроде «5 months ago» существовало только на корейском и английском; теперь оно есть на 13 языках.
Сообщение о пустом списке, пометка «Перевод ИИ», примечание «нет перевода — показано на KO» и «показаны только последние N» тоже переведены.
- •Язык страницы теперь определяется языком, который сайт действительно использует, а не только значением сессии (
?lang=, сессия, cookie, язык по умолчанию).
Раньше при первом визите страница переключалась на корейский, из-за чего корейские надписи попадали на японские и английские сайты.
- •Длинные идентификаторы и пути (текст без пробелов) больше не вылезают за карточку и не уходят под кнопку установки справа.
Полезно знать
- •У привилегированного обработчика нет вошедшего пользователя, а значит, и языка. Он пишет сообщения на языке сайта по умолчанию.
(Раньше он не запускал систему переводов, поэтому ключи вроде updater.write_failed могли выводиться как есть. Теперь обработчик её запускает.)
- •Серверные журналы (
error_log) и отладочные журналы для разработчиков читают операторы, поэтому они оставлены без изменений.
- •На уже установленном сайте обновление выполняет старая, уже установленная программа обновления, поэтому эти улучшения впервые проявятся при обновлении после перехода на 2.8.2.
v2.8.1
2026-10-12 · 7 дн. назад
Усиленная многоязычность (название приложения, данные для поиска, `?lang=`) · обновления защищают собственные изменения ядра на сайте
Многоязычность
- •Название и описание приложения в «Добавить на главный экран» и в окне установки теперь отображаются на языке посетителя. Раньше сохранённое значение (например, японское) выводилось как есть.
Переводы хранятся в rzx_translations под ключами вроде pwa.front.name. manifest.php теперь лишь передаёт управление официальному выводу.
- •Структурированные данные (JSON-LD): сначала используются переводы значений настроек. Исправлены второй пункт «хлебных крошек», в который попадало ещё и название сайта, и «Home», который на всех языках отображался по-английски.
- •Виджет статистики: число и единица измерения (
suffix) теперь принимают значения по языкам, как и подпись. Одиночное значение работает так же, как раньше.
- •Экран офлайн-режима и последнее сообщение service worker теперь доступны на корейском, японском и английском.
Версия кэша service worker повышена с v5 до v6, чтобы уже установленные устройства получили новый экран.
- •«Ничего не найдено» в поиске сообщений было жёстко прописано по-корейски; теперь оно есть на 13 языках.
Поисковые системы и ИИ
- •Выводятся
<meta property="og:locale"> и альтернативные языки (og:locale:alternate). При публикации в соцсетях это сообщает, на каком языке страница.
- •Публичные системные страницы, формируемые кодом, теперь попадают в
sitemap.xml (раньше их там не было).
- •Первая строка
llms.txt (описание сайта) отображается на языке посетителя.
- •Настройка robots для каждой страницы (в панели администратора было поле ввода, но её нигде не читали) теперь действительно применяется.
Исправленные проблемы
- •Значение
?lang= принимается только после сверки со списком поддерживаемых языков. Раньше неподдерживаемое значение оставалось в cookie на год,
и при последующем открытии этого языка посетителя могло переключить на этот экран против его воли. Переключатель языка следует тому же правилу.
- •Конструктор виджетов: переводы для языков, у которых на экране нет поля ввода, стирались при каждом сохранении.
- •Поиск сообщений сообщества: имена хранятся в зашифрованном виде, поэтому ни одно имя не давало результатов. Теперь сначала выполняется расшифровка, затем сравнение.
Обновления защищают собственные изменения ядра на сайте
- •Если указать пути в
storage/custom/update-preserve.txt, по одному в строке (# — комментарий, папки допускаются), обновление не будет перезаписывать эти файлы.
Используйте это, чтобы защитить то, что вы внесли прямо в файлы ядра, например правило входа, специфичное для сайта. Абсолютные пути и .. не принимаются,
а если файл не удаётся прочитать, обновление не останавливается и продолжается со списком защиты по умолчанию.
- •Полезно знать: сторона, выполняющая обновление, уже должна иметь эту функцию, чтобы ею воспользоваться. Она действует начиная с обновления после перехода на 2.8.1.
Что изменилось
В платёжном модуле для каждого провайдера уже был отдельный файл, но сведения о том, «какие провайдеры существуют», были прописаны в нескольких местах кода.
Чтобы добавить провайдера, приходилось менять платёжный менеджер, страницу настроек и вебхук.
- •Поместите папку (
gateway.json и файл реализации) в rzxlib/Modules/Payment/Gateways/, и ядро распознает её само.
Выбор провайдера, поля ключей и адрес вебхука на странице настроек администратора создаются из этой спецификации.
- •Сайт по-прежнему выбирает на странице настроек одного провайдера (как и раньше). Это не функция одновременного использования нескольких провайдеров.
- •Способ добавления провайдера описан в
docs/PAYMENT_GATEWAYS.md.
Square
- •Поддерживаются оплата картой, возвраты и запросы, сохранённые карты (для регулярных списаний) и вебхуки (с проверкой подписи).
- •Форма ввода карты проходит аутентификацию держателя (SCA) и при неудаче показывает причину на экране. 13 языков.
- •На странице настроек администратора появилась кнопка «Проверка соединения». В тестовом режиме она выполняет небольшой платёж, запрос и возврат,
чтобы убедиться, что ключи заданы верно. Реальные деньги не движутся, а в боевом (live) режиме проверка не запускается.
- •Пока вы не выбрали Square, для сайтов ничего не меняется.
Существующие провайдеры (PAY.JP и Stripe) остаются прежними
- •Файлы реализации двух провайдеров не затронуты. Старые настройки (один ключ, v2, v3) читаются как раньше.
- •Даже если файлы спецификаций по какой-то причине не удаётся прочитать, страховка обеспечивает создание PAY.JP и Stripe прежним способом.
- •На реальных настройках работающих сайтов и множестве синтетических настроек мы сравнили старый и новый код и подтвердили, что они выбирают того же провайдера, тот же режим и те же ключи
(scripts/test-payment-parity.php).
Чтение платёжных настроек для каждого сайта
Когда в одной базе данных для каждого сайта хранится отдельная платёжная настройка, платёжный модуль теперь читает настройку своего сайта.
Раньше не было определено, какая именно настройка будет выбрана. Если у сайта нет собственной настройки, он работает как раньше.
Полезно знать
- •Добавлен общий адрес вебхука (
/api/webhook-payment.php?gateway=…). Существующие адреса вебхуков PAY.JP и Stripe не изменились.
- •Список провайдеров на странице настроек сохраняет прежний порядок; провайдеры, добавленные через спецификацию, следуют за ними.
В чём была проблема
Карта сайта (sitemap.xml) и руководство для ИИ (llms.txt) содержали только то, что известно ядру.
Чтобы плагин добавил свои страницы, оставалось только напрямую править файлы ядра, а эта правка
пропадала при каждом обновлении. Кроме того, ядро угадывало, как читать таблицу плагинов, поэтому в некоторых местах всё незаметно расходилось.
- •Объекты недвижимости выводились только по отдельному адресу (
/properties-sitemap.xml); если никто не ссылался на этот адрес,
поисковые системы не могли его найти.
- •Товары маркета ни разу не попадали в карту сайта из-за запроса, искавшего несуществующий столбец.
Ошибка глоталась, поэтому никто не знал.
- •Некоторые сайты выводили адреса заведений в неверном виде, из-за чего индексировались адреса, ведущие на несуществующие страницы.
Плагины теперь публикуют свои страницы сами
- •Если плагин указывает
seo в своём plugin.json, ядро читает это и объединяет с картой сайта и руководством для ИИ.
Это работает так же, как для виджетов, макетов и модулей интранета, и при совпадении адресов побеждает ядро.
- •Адреса для нескольких языков и пометки языка добавляет ядро. Плагину достаточно указать адреса.
- •Один неисправный плагин больше не обрушивает всю карту сайта. Пропадает только доля этого плагина.
- •Адреса отключённых плагинов исчезают автоматически: страницы отключённого плагина всё равно не открываются.
Китайские альтернативные адреса игнорировались
Карта сайта писала пометку языка как zh_CN, а по стандарту нужно zh-CN, поэтому Google полностью игнорировал эту строку.
Сами страницы были написаны правильно, так что один и тот же сайт говорил в двух местах разное.
Теперь альтернативные адреса на упрощённом и традиционном китайском распознаются правильно.
Руководство для ИИ по языкам
- •
llms.txt существовал только на языке по умолчанию. Теперь он выдаёт языковые версии, например ?lang=en.
Названия и описания выводятся на этом языке, а в начале руководства указано, какая это версия.
- •На многоязычных сайтах движки ответов ИИ теперь могут представлять этот сайт пользователям каждого языкового региона.
Сотрудники могут видеть собственную кадровую карточку (интранет)
- •Кадровую карточку, которую видели только кадровики, теперь может просматривать и сам сотрудник. Доступ через меню профиля вверху.
- •Она доступна только для просмотра. Процесс, в котором вы подаёте заявку на исправление, а кадровик её утверждает, находится в подготовке.
- •Оценки не видны самому сотруднику. То же относится к поощрениям и взысканиям. Из прикреплённых документов показывается только список.
- •Нельзя открыть чужую карточку, изменив адресную строку: чья это карточка, определяется только данными входа.
- •Сотрудник также может проверить, кто просматривал его кадровые данные.
Использование экранов форума в другой оболочке
Все ссылки на экране форума строятся из одного значения ($boardUrl), но ядро всегда его перезаписывало.
Поэтому тем, кто использует форум внутри административного экрана, например интранета, приходилось заменять уже готовую страницу посимвольно.
Стоило ядру хоть немного изменить вид ссылки, всё незаметно расходилось.
- •Если вызывающая сторона заранее задаёт базовый адрес, используется это значение. Если нет, всё как раньше.
- •Ссылка с шестерёнкой (настройки форума) тоже определяется в одном месте. Раньше список, FAQ, вопросы и ответы и тема ссылок строили каждый свою.
Макеты можно хранить внутри плагина
- •Макеты ищутся сначала в
skins/layouts/ ядра, а если там нет, в plugins/{id}/skins/layouts/ плагина.
Это то же правило, что для виджетов и модулей интранета, и при совпадении имён побеждает ядро.
- •Макеты плагинов также появляются в выборе макета в администрировании.
- •До сих пор макет, предназначенный только для плагина, приходилось класть в папку ядра. Обновление его не удаляет,
но если позже ядро выпустит макет с тем же именем, он будет перезаписан, а при переносе плагина на другой сайт папку приходилось переносить вручную.
Плагины сами объявляют модули интранета
Список для экранов интранета (левое меню и пути входа) существовал только в одном файле настроек ядра.
Чтобы плагин добавил свой модуль, нужно было править файл ядра, и эта правка пропадала при обновлении.
- •Если плагин указывает
intranet_module в своём plugin.json, ядро читает это и объединяет. Это работает так же, как для меню и системных страниц.
- •В качестве значения можно указать массив напрямую или путь к файлу, например
"data/intranet-module.php".
- •При совпадении имён побеждает ядро. Плагин не может перехватить модуль ядра и изменить его поведение по умолчанию.
- •Все пять мест в ядре, читавших этот список, переведены на новый способ. Даже если обновление перезапишет файл настроек, модули плагинов сохранятся.
Японские эры (和暦) при вводе дат
- •На японских экранах, если в поле даты выбрать эру (令和, 平成, 昭和, 大正, 明治) и ввести номер года, год по григорианскому календарю заполняется автоматически.
Не нужно каждый раз пересчитывать значение, которое помнят по эре, например дату рождения.
- •Поле отображения дня недели остаётся как есть.
Настройка макета форума не отражалась на экране
- •Даже если в администрировании выбирали макет для каждого форума, на экране показывался макет сайта по умолчанию.
Место, где сохраняли, и место, где читали, были разными.
- •Исправление: при сохранении форума записывается и та настройка, которую читает экран.
Существующие форумы выглядят так же, как сейчас, до повторного сохранения (внешний вид не меняется внезапно).
Сообщения не сохранялись при написании
- •При сохранении сообщения столбец заголовка пропускался, поэтому на сайтах, где этот столбец ещё есть, сохранение завершалось ошибкой.
На экране появлялось только «Unexpected end of JSON input».
- •Исправление: если столбец есть, он тоже заполняется. Сайты, удалившие столбец, работают как прежде.
Поиск по форуму
- •Поиск смотрел в старый столбец, поэтому на сайтах, где его удалили, весь список переставал открываться, как только вводился поисковый запрос.
- •У сайтов, сохранивших столбец, тоже была проблема. При редактировании сообщения столбец не обновлялся, поэтому поиск находил только текст до правки.
- •Исправление: поиск идёт по таблице переводов. Теперь он находит просматриваемый язык, а также переведённое содержимое.
Сообщения не были видны в списках, виджетах и «Моей странице»
- •Виджет с вкладками направлений деятельности и виджет-сетка форума всегда показывали пустой список (ошибка глоталась внутри).
- •В «Моих комментариях» на «Моей странице» комментарии форума отсутствовали полностью.
- •У темы форума со ссылками заголовок всегда был пустым.
- •Исправление: все они теперь читают заголовок и текст из таблицы переводов. Миниатюры снова находятся по тексту.
Перевод ИИ и новая установка
- •Мы исправили перевод сообщений ИИ, который завершался ошибкой при чтении исходного текста, из-за чего все задания на перевод проваливались.
- •Мы исправили случай, когда при новой установке заголовок и текст на исходном языке примеров сообщений не попадали в таблицу переводов, и заголовок казался пустым.
Прочее
- •Мы исправили случай, когда список не открывался при сортировке форума «по рекомендациям» (обращение к несуществующему столбцу).
Чужой текст попадал в окно уведомления как есть
Общее окно уведомления в админке (модальное окно результата) вставляло в страницу заголовки меню, названия скинов и сообщения сервера без изменений.
Если в название вписать что-то вроде <script>, это могло выполниться, когда другой администратор открывал этот экран (хранимый XSS).
- •Исправлено: весь текст окна (заголовок, содержимое, кнопка, строка подтверждения) перед вставкой преобразуется так, чтобы отображаться обычными символами.
- •Сообщение при сбое подключения к базе данных теперь берётся из перевода и проходит то же преобразование.
Кнопки окна на любом языке выводились по — корейски
- •«Подтвердить» и «Отмена» были жёстко записаны по-корейски в JavaScript. Теперь они выводятся на просматриваемом языке.
Сервер готовит формулировки текущего языка, а скрипт читает эти значения.
Тексты админки на 13 языках
Корейские строки, зашитые в экраны оформления (скин), меню и настроек страницы, заменены ключами перевода и заполнены для 13 языков.
- •Скины: результаты переименования, копирования и удаления; подтверждение «файлы и настройки скина удаляются безвозвратно»; результат загрузки изображений
- •Меню: уведомление, что системная страница удаляется только из меню; подтверждение «связанная страница (раздел) тоже будет удалена»
- •Настройки страницы: сообщения о типе файла, неудачном сохранении и повторяющемся URL
Приватные записи просматривались из списка
Запись, помеченная как приватная, всё равно показывала заголовок, обложку и анонс в списке. Скрыт был только текст.
- •Исправлено: в списках тоже заголовок и превью видят только автор, администратор или тот, кто ввёл пароль.
- •Закрыта и возможность подсмотреть содержимое по адресу вложения — вложения теперь проходят ту же проверку.
- •Заголовки предыдущей и следующей записи на странице чтения также скрыты.
- •Виджеты, которые берут списки (карточки форума, сетки, вкладки), исключают приватные записи прямо в запросе.
Первый экран появлялся слишком поздно
На телефоне первые буквы показывались с задержкой. Причина — шрифты.
- •CSS шрифтов (Google Fonts 152 КБ + Pretendard 29 КБ) блокировал отрисовку.
Теперь страница рисуется системными шрифтами, а веб-шрифты подставляются по мере загрузки.
- •Pretendard загружается только на корейских экранах. На японских первым идёт японский шрифт, поэтому скачивались лишь фрагменты и отбрасывались.
- •Две небольшие таблицы стилей (размер текста, тело форума) встроены прямо в страницу вместо отдельного файла.
- •При первом заходе кэш экрана (service worker) заставлял читать документ дважды — это убрано.
- •Расчёт ширины горизонтального меню отложен до полной загрузки страницы (раньше он «подвешивал» её на 0,5 с).
Чтобы поисковые роботы и ИИ переходили по языковым страницам
- •Ссылки переключения языка ведут на настоящие адреса (
?lang=…). Раньше это были JavaScript-ссылки, по которым роботы не переходили.
Панель управления
- •В списке страниц одна и та же страница выводилась по строке на каждый язык; теперь строки объединены в одну.
- •Изменение только заголовка меню стирало переводы на другие языки. Теперь переводы очищаются лишь при смене связанной страницы.
- •Отложенная публикация блога с прошедшим временем сохранялась как есть; теперь выводится предупреждение, а статус не меняется (13 языков).
Уведомления и формулировки
- •Тема уведомлений о сообщениях сообщества была жёстко задана на корейском. Теперь она уходит на языке получателя.
- •Текст ссылки в полосе уведомления о cookie был слишком тёмным — стал светлее.
Внутренняя уборка
- •Удалены папки представлений админки, случайно продублированные (
admin/site/site, admin/components/components).
Реально используются исходные папки. Сборка дистрибутива также их исключает.
Закрытые разделы попадали в карту сайта
В карту сайта попадали даже адреса разделов, которые видят только администраторы (например, ящики входящих заявок).
Поисковые роботы получали по этим адресам только 403 (доступ запрещён), а само существование закрытых разделов становилось известно.
- •Исправлено: в карту попадают только разделы, доступные незарегистрированным посетителям. Критерий — разделы, у которых права на просмотр списка и записей установлены в «Все».
- •Также исключаются секретные записи и разделы, у которых в настройках включено исключение из поиска (noindex).
- •Страница с тем же именем, что и раздел, оценивается по разделу, поскольку адрес первым занимает раздел.
- •Многоязычные страницы тоже включаются в карту сайта.
- •Страницы, которые не нужно включать на конкретном сайте, указываются в настройке
sitemap_exclude. llms.txt следует той же настройке.
Отправленные письма попадали в спам или возвращались
- •В письмах не было времени отправки (Date) и уникального идентификатора сообщения (Message-ID), поэтому принимающие серверы легко относили их к спаму.
Теперь оба заголовка добавляются, а текст письма отправляется в base64. Это также предотвращает обрезку длинных строк и обрыв текста из-за «.» в начале строки.
- •Если адрес получателя находился в том же домене, что и отправитель (например, копия на адрес компании), почтовый ящик этого домена отклонял письмо как подделку.
Только в этом случае письмо теперь отправляется с адреса учётной записи SMTP, а ответы по-прежнему приходят на исходный адрес.
Усилены настройки SEO и поиска с ИИ
- •В настройки SEO администратора добавлены поля сведений об операторе (структурированные данные Organization). Пустые поля не выводятся.
- •Теперь действительно используются настройки SEO отдельных страниц, а когда одно и то же содержимое доступно по нескольким адресам, задаётся канонический адрес (canonical).
- •Добавлено поле для кода подтверждения сайта Bing. Поиск ChatGPT использует индекс Bing.
- •Описания обрезаются по ширине отображения в результатах поиска — в конце предложения, а не посередине.
Упорядочен показ языков
- •Страница 404 всегда показывалась на корейском. Теперь она выводится на языке просмотра (японский, корейский или английский; для остальных языков — английский).
- •Исправлено «только что» в колокольчике уведомлений, которое было жёстко задано на корейском.
- •На японских страницах в первую очередь используются японские шрифты. Помощник шрифтов для каждого языка загружается всегда, поэтому страницы без макета (например, страницы общего доступа) больше не останавливаются из-за шрифта.
- •В списке «Предпочитаемый язык» в личном кабинете больше не показываются языки, для которых нет текстов.
- •При регистрации язык просматриваемой страницы сохраняется как предпочитаемый (раньше это поле оставалось пустым). Письма для сброса пароля тоже отправляются на этом языке.
Ввод и вход
- •Даты, например дата рождения, теперь вводятся в три поля: год, месяц и день. Раньше отображение зависело от языка браузера.
- •Исправлено: в некоторых оформлениях при входе и восстановлении пароля пропускалась проверка reCAPTCHA.
Оплата
- •Для каждой платёжной системы тестовые и рабочие ключи хранятся отдельно. Тестовый режим достаточно просто выбрать, и ключи больше не теряются при переключении.
Виджеты
- •Кнопку виджета hero теперь можно открывать в новом окне.
- •Повторяющиеся элементы в конструкторе виджетов тоже можно заполнять для каждого языка (кнопка с глобусом).
Сборка
- •Файлы конфигурации (.env) и их резервные копии никогда не включаются в дистрибутив, как бы они ни назывались.
- •Резервные файлы, оставшиеся после редактирования (
.bak, в том числе с датой после имени), тоже не включаются.
- •Из дистрибутива убраны скрипты, которые используются только для работы головного офиса (синхронизация сайтов, хостинг, почтовый сервер, обновление условий и т. п.), а также cron-задачи, нужные только плагинам, которых нет в дистрибутиве. Скрипты, нужные сайтам клиентов, — например, обработчики обновления, установки и перевода — по-прежнему входят в дистрибутив.
- •После создания zip-архива выполняется повторная проверка; если в нём есть файлы конфигурации или резервные копии, публикация останавливается.
- •Тестовые скрипты больше не хранят пароли в файле, а берут их из настроек.
После обновления название и дата выпуска версии не менялись
Росло только число, а заголовок оставался от предыдущей версии.
На экране администратора выводилось что-то вроде «2.7.9 · Японские шрифты · уведомление о продлении договора…», и номер с названием не совпадали.
Глядя только на экран, нельзя было понять, что именно получено.
- •Причина:
version.json — файл, который намеренно не перезаписывается, чтобы сохранить индивидуальные настройки каждого клиента.
Поэтому программа обновления заполняет его сама, но подставляла только номер и время, а заголовок и дату выпуска оставляла.
- •Исправление: заголовок и дата выпуска новой версии теперь читаются из полученного пакета и тоже применяются.
При частичных обновлениях без пакета они читаются из списка распространения (manifest.json).
Индивидуальные настройки клиентов (github и другие) сохраняются как есть.
Внимание — даже после перехода на эту версию экран может по-прежнему показывать старое название. Ведь обновление выполняет
программа обновления той версии, которая установлена сейчас.
Корректно это будет отображаться начиная с версии, следующей за той, что получила 2.7.10.
Проверка сборки
Поломка конструктора в 2.7.8 (исправлена в 2.7.9) спокойно прошла регрессионные тесты.
Дело в том, что существующие проверки смотрели лишь на то, «есть ли файл · содержится ли строка».
- •Теперь скрипт, попадающий в конструктор, действительно создаётся и проверяется на синтаксис (
node --check).
- •Если есть код, который вставляет многострочный файл как есть в строку JS, сборка блокируется с указанием номера строки.
- •Мы проверили это на файле, воспроизводящем 2.7.8, и убедились, что проверка действительно его отлавливает.
Симптомы
- •В палитре не видно ни одного виджета
- •Уже размещённые виджеты не рисуются на холсте — видео, изображения и прочее будто исчезли
- •Сохранить не удаётся
Причина
В 2.7.8 мы свели объявления шрифтов в одно место, partials/fonts.php. Но место, где создаётся предпросмотр конструктора, находилось
внутри одной строки JS‑строки, и 33‑строчное объявление шрифтов попало туда как есть.
В тот момент, когда строка разорвалась, строка JS оборвалась и весь скрипт конструктора умер ещё до того, как был прочитан.
Это тот тип сбоя, который убивает файл целиком, поэтому он выглядит не как «некоторые функции ведут себя странно», а как конструктор, который вообще ничего не делает.
В 2.7.5 и ниже этой проблемы нет.
Как мы это исправили
Определения шрифтов остались в том же едином месте, fonts.php, а только при вставке в конструктор вывод сворачивается в одну строку и обрабатываются кавычки.
Место, где правят шрифты, по-прежнему одно.
Шрифт по умолчанию теперь ориентирован на японский
До сих пор шрифт по умолчанию был ориентирован на корейский. Основные пользователи находятся в Японии, но экраны выглядели неуклюже,
а при смене языка шрифт не менялся, поэтому японский текст набирался корейским шрифтом.
- •Значения по умолчанию теперь такие: минчо для заголовков и Pretendard JP для основного текста.
- •На экране настроек языка можно указать шрифт для каждого языка отдельно. Если не указать, для языка подставится подходящее значение по умолчанию.
- •То же правило действует на всех экранах администратора, участника и фронтенда, а также во всех темах (default, modern, minimal, shop).
Уведомление о продлении договора и окончании бесплатного периода
Бывало, что об истечении договора узнавали уже после того, как оно прошло. Теперь по мере приближения даты продления вас предупредят заранее.
- •Предстоящие продления и окончание бесплатного предоставления выводятся уведомлением до наступления даты.
- •Работает от одной строки cron (
cron.d-voscms-contract-notice), а в инструкции по установке добавлен способ её регистрации.
Оплата картой
Платежи, прошедшие аутентификацию японских эмитентов карт (3-D Secure), проходили аутентификацию, но списание отклонялось.
- •PAY.jp блокирует использование токена, созданного для аутентификации, если оставить его как есть. После прохождения аутентификации сервер должен сделать завершающий вызов,
только тогда токен можно использовать для списания, и этого одного шага не хватало.
- •Мы также открыли способ заранее проверять, сохранились ли данные клиента на стороне платёжной компании, — это предотвращает сбои при попытке списания с удалённого клиента.
Колокольчик уведомлений и правила возврата на всех языках
- •В колокольчике уведомлений в шапке такие тексты, как «Уведомлений нет» и «минут назад», были жёстко заданы по-корейски. Теперь они меняются по языку.
- •Мы привели в порядок оставшиеся фиксированные формулировки в тексте правил возврата.
Бухгалтерия
В книги японской компании попадали сделки в вонах, и итоги не сходились.
- •Для документов в валюте, отличной от валюты книг (иены), проводки не создаются. Запись остаётся, но в книги не попадает.
Наведение порядка в дистрибутиве
- •Резервные файлы, возникавшие при редактировании, уходили в дистрибутив (имена вида
имя.php.bak.число).
Правило исключения отсеивало только .bak и пропускало имена с добавленным числом. Теперь блокируются и .bak., .orig, *~.
- •Торговые материалы сервиса THEVOS AI (прайс-лист, расчёт наборов, cron сверки платежей) убраны из дистрибутива.
Всё, что их читает, уже было убрано, поэтому в дистрибутиве это были файлы, которыми никто не пользовался.
«Требуется миграция БД»
039_ai_knowledge_base создаёт таблицы ИИ для консультаций магазинов NaitoWork X,
но уходил в дистрибутив как есть. Мало того, последняя строка этого файла была
ALTER TABLE rzx_shop_inquiries ADD COLUMN IF NOT EXISTS ai_meta ...
rzx_shop_inquiries — таблица, которую создаёт плагин vos-store.
В дистрибутиве vos-store нет — ядро пыталось изменить таблицу несуществующего плагина.
Поэтому при каждой новой установке: создаются 4 таблицы и всё падает на последней строке → поскольку до конца дело не дошло,
запись «применено» не делается → баннер висит постоянно, а нажатие снова падает на том же месте.
Мы полностью убрали ИИ‑консультацию (RAG) из дистрибутива:
039 · rzxlib/Core/AI/{Knowledge,Embedding,Generate} ·
scripts/{ai-kb-ingest,ai-rag-test,cron-ai-consult,hanesbot-ingest}.php
ИИ‑перевод (rzxlib/Core/AI/Translation) остаётся. Это движок платного плагина vos-ai,
и он следует тому же правилу, что бухгалтерия и хостинг: «движок лежит в ядре, а функцию открывает плагин».
«1 плагин не прошёл проверку (vos
Ядро хранит список «поставляемых плагинов, работающих без лицензии», и он устарел.
Что код считал поставляемым | vos-autoinstall · vos-marketplace |
Что реально поставляется | vos-autoinstall · vos-community |
vos-marketplace выпал из дистрибутива, а его место занял vos-community,
но только этот список не поправили. Мы поставили плагин, а потом выдали предупреждение, потому что не знали, что он поставляемый.
Чтобы такое не повторилось
Причина в том, что список поставляемых записан в двух местах —
в списке исключений скрипта сборки и в LicenseStatus::BUNDLE_PLUGINS.
В сборку добавлен шаг сверки. Если они расходятся, zip не создаётся и сборка останавливается.
Это не то, что должны помнить люди.
На форумах не было видно кнопки написания
В Q&A и FAQ даже у тех, кому разрешено писать, не было кнопки. Причин было две.
- •Администраторов блокировало — при проверке права на запись сначала выяснялось, участник ли это, и только потом смотрели администраторов.
Администраторов нет в users, поэтому они застревали на первом же барьере. Мы поменяли порядок, чтобы администраторов пропускали первыми.
- •Если не было ни одной записи, кнопка тоже пропадала — кнопка находилась внутри списка,
и при переключении на экран «записей нет» она исчезала вместе с ним. Тот, кто хотел написать первым, не мог написать.
Мы вынесли кнопку за пределы списка.
- •FAQ даже смотрел прямо на
admin_id — игнорируя право записи из настроек форума.
Заголовки и подзаголовки форумов отображались на другом языке
Корейский экран, на котором лишь подзаголовок выводился по-японски.
- •Переводы вообще не читались. Заголовок и подзаголовок сохраняются в настройках темы как одна строка,
а переводы лежат отдельно в rzx_translations. Экран просто выводил сохранённую строку.
- •Не было разделения по форумам. Ключ перевода был
skin_config.subtitle_text, поэтому Q&A и FAQ
делили одну и ту же ячейку. Правка одного меняла другой.
Мы разделили его на board.{номер форума}.{пункт}.
- •Основная часть рисовалась до определения языка. Ядро сначала создаёт основную часть, а шапку добавляет позже,
но язык определялся в шапке. Мы перенесли определение на сторону основной части, чтобы оно происходило первым.
При установке пропускались миграции ядра
install-core.php вообще не смотрел в database/migrations.
На только что установленном сайте нескольких таблиц не было с самого начала, и позже, при включении соответствующей функции, всё падало.
Миграции, которые молча пропускались (продолжение 2.7.4)
В 2.7.4 были обработаны однострочные комментарии (--), но многострочные комментарии (/ /) остались.
Точка с запятой внутри такого блока склеивала файл в один кусок, так что не выполнялось ни одной строки,
а запись всё равно помечалась как «применено». Мы подтвердили реальный ущерб — в одной из установок не хватало сразу 9 таблиц.
-- или /* внутри строки комментарием не являются. Мы удаляем комментарии, считая кавычки.
Прочее
- •Шапка магазина — в самый верх выпадающего списка категорий добавлено «Все категории».
Тем, кто хотел зайти и осмотреться, было некуда идти.
- •Конструктор виджетов — редактирование повторяющихся элементов (
repeater) вынесено в отдельный файл.
- •Боковая панель интранета — текст бухгалтерии теперь ищется во всех трёх ветках (салон, интернет-магазин, обычная компания).
Из-за того что смотрели только одну, при другой бухгалтерии в меню выводился сырой ключ.
- •Моя страница — наведён порядок на панели.
- •Отчёт о платных плагинах — проверка лицензии теперь отправляет и включённые платные плагины.
Установленные, но не включённые, не отправляются — деньги привязаны только к включённым.
Страницы только для головного офиса уходили в дистрибутиве
config/system-pages.php попал в дистрибутив целиком, поэтому в «Системных страницах» администрирования установки клиента
появилось шесть пунктов меню, на которые можно нажать, но за которыми ничего нет.
📥 downloads | Загрузки дистрибутива — распространяет только головной офис |
📋 changelog | Это история продукта головного офиса |
📧 contact | Клиенты делают его сами с помощью виджета (это не системная страница) |
💼 recruit · 💃 casts | Только для портала ночного бизнеса (NaitoWork X) |
🎟 coupons | Предоставляет плагин маркетплейса |
- •Добавлен шаг сборки
[4e/8] (strip-system-pages.php). После удаления синтаксис проверяется через php -l —
если этот файл сломается, весь экран администрирования упадёт.
- •В дистрибутиве остаются восемь системных страниц: home · terms · privacy · data-policy ·
refund-policy · hosting-terms · tokushoho · funds-settlement
Системные страницы плагинов
- •Текст
site.pages.blog добавлен на 13 языков.
Даже если плагин регистрирует системную страницу, при отсутствии текста в ядре site.pages.blog показывается на экране как есть.
- •(Исправление собственного
plugin.json у vos-blog выходит через маркетплейс — это плагин, не входящий в дистрибутив)
Уже установленные сайты
Если в «Системных страницах» администрирования видны неиспользуемые пункты, удалите соответствующий блок из config/system-pages.php.
Содержимое страниц (rzx_page_contents) не удаляется — оно лишь исчезает из списка.
① Миграции, начинающиеся с комментария, пропускались целиком
- •Комментарий в
DatabaseMigrator::splitSql() гласил «разделять после удаления комментариев», но
на самом деле разделение шло только по точкам с запятой.
- •Если первая строка SQL-файла —
-- описание, весь файл превращается в один фрагмент,
который задерживается на str_starts_with($stmt, '--') в части выполнения, и ни одна инструкция не выполняется.
- •Когда выполняется 0 инструкций, ошибок тоже 0, поэтому это считается
success и записывается как применённое.
Раз запись есть, оно больше не запускается, так что таблица остаётся отсутствующей навсегда.
- •Ущерб затрагивал все файлы, начинающиеся с
--. Измерено: из 9 файлов 6 выполнили 0 инструкций, а 2 — частично.
- •Теперь перед разделением удаляются построчные комментарии
-- и # и блоки / /.
-- внутри строки не затрагивается (если бы значение вроде 'a -- b' обрезалось, данные были бы испорчены).
② Новая установка вообще не запускала инкрементные миграции
- •Скрипт установки запускал только
migrations/core и migrations/migrations
и пропускал верхний уровень database/migrations/*.sql. Программа обновления, наоборот, отслеживает только верхний уровень.
- •Поэтому при новой установке 034–042 отсутствовали с самого начала и оставались пустыми до следующего релиза.
- •Теперь при установке запускается и верхний уровень, и он записывается как применённый (чтобы первое обновление не запускало те же самые повторно).
Уже установленные сайты
Нажмите один раз Выполнить миграции в Админ → Настройки → Обновление.
Ошибочно записанные не будут запущены снова, поэтому, если таблица пуста, это можно проверить так.
SELECT migration FROM rzx_migrations; -- записано
SHOW TABLES LIKE 'rzx_home_notices'; -- на самом деле
Что убрано из дистрибутива
- •
vos-cp (панель управления услугами) и vos-shopping (интернет-магазин) попадали в дистрибутив заодно.
Оба — недавно созданные плагины, которых просто не было в списке исключений.
- •
vos-cp — панель для собственных услуг THEVOS. Нет причин для её присутствия в установке клиента
- •
vos-shopping ещё в разработке (v0.1.0), а правило гласит, что плагины покупаются на маркетплейсе
- •
vos-review тоже убран — по той же причине (указание владельца).
- •В дистрибутив входят только два плагина:
vos-community и vos-autoinstall.
Всё остальное получают на маркетплейсе.
Что добавлено в ядро
- •Двухуровневая шапка магазина (
skins/layouts/shop) — выпадающий список всех категорий, поиск, предпросмотр корзины и
меню аватара после входа. В самом верху выпадающего списка категорий мы поместили «Все категории», чтобы был путь назад
- •Накопленные баллы, баланс магазина и уровень участника показываются в «Моей странице». Названия берутся из заданных вами значений
- •Поддержка виджетов плагинов — если у плагина есть папка
widgets/, конструктор ядра распознаёт её автоматически
- •Общий компонент поиска адреса по почтовому индексу — форма заказа и сведения об участнике используют один и тот же
- •Колокольчик уведомлений в верхней панели администрирования
- •Наведён порядок в группах меню консоли администратора (
config/intranet-modules.php)
- •Поддержка повторяющихся полей (repeater) в конструкторе виджетов
Что исправлено
- •Проблема, при которой многоязычные поля ввода перезаписывали исходные данные
- •Ошибка в записи книги баланса магазина
- •Проблема, при которой обновление дистрибутива превращало страницы хостинга в 404
Что проверить на уже установленных копиях
На сайтах, установленных по старому дистрибутиву и получавших только обновления, остаются папки неиспользуемых плагинов
(обновление не удаляет папки плагинов). Всё, что в Админ → Плагины отображается как не установлено,
можно удалить из plugins/ вместе с папкой.
Changed
- •Единая маршрутизация URL заявок на услуги —
/service/hosting, /service/mail и /service/domain → на /service/order через 302 (пока не готовы отдельные страницы заявок, посетителей направляют на интегрированную страницу заявки, а позже её можно заменить отдельными страницами).
Notes
- •Исправления ядра из 2.7.1 (многоязычный hreflang, предотвращение тихих сбоев программы обновления, объединение тарифов
/pricing с /service/order) уже были корректно включены в пакет 2.7.1. 2.7.2 — это пересборка, добавляющая сверху наведение порядка в маршрутизации заявок на услуги.
- •Дистрибутив в принципе включает только чистое ядро (платные и отраслевые плагины исключены). Сайты головного офиса (voscms.com и thevos.jp) не являются объектами самообновления дистрибутива; они развёртываются из исходного кода/git.
SEO
- •Многоязычный hreflang — самоссылающийся canonical для каждой страницы + автоматический hreflang для поддерживаемых языков, индексация по языкам через
?lang=xx, нормализация 301 для ?lang языка по умолчанию на обычный URL и расширенный sitemap.
Fixed
- •Исправлены тихие сбои программы обновления — сбои записи файлов ядра или version.json теперь обрабатываются как исключения. Когда применение не удаётся из-за нехватки прав и тому подобного, вместо ложного «SUCCESS» вы получаете явную ошибку и автоматический откат.
- •Предотвращён сбой 500 на странице тарифов, где многоязычное поле передавалось в htmlspecialchars как массив.
Changed
- •Расширен переключатель reCAPTCHA v3 для отдельных форм (blog_comment и другие).
- •Вкладка «Уведомления консоли владельца» в настройках отображается только при активном vos-store.
- •Страница тарифов
/pricing объединена с /service/order (301), а тариф «Система без ограничений» доступен только администратору.
Состав дистрибутива
- •Дистрибутив сокращён до чистого ядра — включены только 3 плагина ядра (
vos-autoinstall, vos-community, vos-review). Отраслевые плагины (ночной бизнес / салон / POS), виджеты и тема (story) исключены полностью. Универсальные функции вроде проверки возраста и баннера cookie относятся к ядру (rzxlib), поэтому включены.
Added/Changed (виджеты ядра)
- •location-map — поддерживает встраивание без ключа (
map_embed), убирая зависимость от ключа API, и связывает язык карты с locale
- •practice-tabs (вкладки направлений деятельности) — новый встроенный виджет
- •Глобальная настройка ширины страницы (
widget_page_width) — унифицирует ширину страниц виджетов/документов
Виджеты, специфичные для сайта и не входящие в комплект (bundled:false) (hero-slideshow, overlap-cards, office-intro, page-hero, content-head, consult-cta, board-grid, clients-logos, тема профиля форума), в дистрибутив ядра не включены.
Removed
- •Виджеты предоплаты (
prepaid-charge, prepaid-pass, prepaid-purchase) — исключены из дистрибутива, так как vos-prepaid не входит в комплект
Added (настройки и ядро)
- •Шлюз проверки возраста повышен до общей настройки ядра
- •Регулировка прозрачности фона баннера согласия на cookie
- •Изображения логотипа для каждого языка
- •Банковские реквизиты в сведениях о компании + уведомление о банковском переводе
- •Уведомление «Принимаемые карты» на экране оплаты (значки брендов) + настройка администратора
Fixed
- •Локализация заголовков и подзаголовков страниц на язык посетителя (page-scoped db_trans) и исправление того, что content_width не применялся
- •Множество исправлений ошибок в виджетах/администрировании/страницах и дополнения на 13 языков
Added
- •Автоматическая очистка устаревших файлов — обновление, перезаписывающее файлы из zip полного пакета, не может удалить файлы, убранные из исходников, поэтому в установленных дистрибутивах оставались остатки (orphan) (например, монолитный
settings.php, разделённый и удалённый при рефакторинге, оставался в старых установках). Мы добавили механизм на основе явного списка для их очистки.
- •
config/obsolete-files.php — накопительный список путей для удаления (установки, пропустившие несколько версий, очищаются за один раз).
- •Сразу после
applyUpdate() программы обновления cleanupObsoleteFiles() безопасно выполняет unlink файлов из списка. Он никогда не трогает защищаемые цели (.env, storage и т. д.), выходы за пределы пути и каталоги.
- •Первая запись:
resources/views/admin/settings.php.
Fixed
- •Ссылка «Системные настройки» на панели — ссылка в быстрых действиях указывала на устаревший путь
/settings, который был выделен и удалён, и поэтому вела на неверную (резервную) страницу. Исправлено на точку входа настроек /settings/general. (Упущено после сборки 2.6.22.)
Fixed
- •Баннер «Требуется миграция БД» на панели — CSRF token invalid — кнопка повторной попытки не отправляла CSRF-токен на
/update-api.php (run_migrations), поэтому запрос отклонялся с 419. Теперь $_SESSION['csrf_token'] гарантируется и отправляется и в заголовке, и в теле.
- •Баннер не исчезал даже после выполнения миграций —
DatabaseMigrator::recordMigration предполагал столбец batch и префикс patch_, поэтому в схеме установленного типа (migration/version/executed_at) INSERT записи терпел неудачу, а префикс к тому же сбивал сопоставление имён файлов. Исправлено: имя файла записывается как есть, в соответствии с реальными столбцами. (Это была причина того, что при обновлении до 2.6.20 миграции оставляли лишь журнал «OK» и не применялись.)
Fixed
- •Необработанные ключи в боковой панели страниц ядра, таких как управление правами администратора — поскольку макет интранета включался только по route_prefixes из
config/intranet-modules.php, страница ядра /staff/admins на сайте без соответствующего плагина (например, без установленного vos-salon) отображалась с боковой панелью интранета salon, а ключи salon.intranet. и salon.section. показывались как необработанный текст. Исправлено.
- •Добавлена общая вспомогательная функция
resolve_intranet_module() — интранет включается только если lang-ресурсы модуля (плагин) действительно установлены; иначе откат к admin-sidebar ядра.
- •Дублировавшаяся логика определения в
_head.php / admin-sidebar.php объединена в вспомогательную функцию. На сайтах с установленным плагином (salon, store и т. д.) поведение не меняется.
Fixed
- •Ошибка сохранения уровня баллов — исправлен сбой с ошибкой
Unknown column 'group_id' при сохранении уровня в модуле баллов администратора. Причиной был конфликт схем point_levels/member_points между core/004 и migrations/019(v190) (CREATE TABLE IF NOT EXISTS в 019 не мог добавить новые столбцы в существующую таблицу).
- •миграция
034_point_system_schema_reconcile (верхний уровень, применяется автоматически при обновлении):
- •
point_levels — добавлен group_id, задано значение по умолчанию для name и уникальный индекс по level (чтобы работал upsert).
- •
member_points — добавлены point/balance/total_accumulated/level.
- •Всё аддитивно (без потери данных), без влияния на существующие столбцы и потребителей (например, vos-prepaid).
Added
- •3 виджета покупки — интегрированный (
prepaid-purchase, показываемое на выбор: всё / только пополнение / только абонементы) + только для пополнения (prepaid-charge) + только для абонементов (prepaid-pass). Один общий компонент purchase-widget.php обслуживает все 3 виджета (нулевое дублирование), ветвление выполняется фильтром $ppOnly.
- •Фоновое изображение товара — загрузка перетаскиванием (а также по щелчку) в модальном окне добавления/изменения товара в админке, а фон применяется в «Моей странице» и на карточках виджетов. Миграция 003
image_url.
- •Многоязычный ввод названия товара + перевод ИИ — повторно используется общий
multilang-modal (кнопка с глобусом), сохранение на основе rzx_translations, а отображение на языке посетителя через prepaid_name(). Перевод ИИ повторно использует конечную точку ядра /api/translations.
- •В форму оплаты добавлено поле ввода имени владельца карты (передаётся в
createToken PAY.jp).
- •Выбор клиента при ручном пополнении — вместо прямого ввода user_id теперь поиск с автодополнением по имени/телефону/e-mail (действие
customer_search). Список состояния клиентов тоже показывает имя + e-mail клиента вместо хэша user_id.
- •Переключатель активности — флажок «товар активен» заменён переключателем.
Changed
- •Блок покупки в 4 колонки — сетка карточек товаров адаптивная 2→3→4 (
lg:grid-cols-4), а фиксированная ширина виджета max-w-3xl убрана → подстраивается под ширину контейнера размещения.
Fixed
- •Высота палитры и мегаменю конструктора виджетов — высота строки конструктора зафиксирована как
calc(100vh - 88px), поэтому левые категории и выпадающее меню автоматически занимают всю высоту экрана, а наложение элементов мегаменю устранено.
Added
- •Проверка link в
api/broadcast.php — если схема отсутствует, https:// добавляется в начало автоматически (example.com → https://example.com); если значение не проходит filter_var FILTER_VALIDATE_URL, возвращается 400 с ошибкой field: 'link'; всё, что длиннее 500 символов, отклоняется.
- •Проверка на стороне клиента в реальном времени —
type="url", inputmode="url", и сразу при вводе:
- •Нет схемы → контур emerald + ✓ + подсказка «При отправке будет дополнено автоматически: …»
- •В хосте нет точки (.) или сломан формат → контур red + ⚠ + «Неверный формат URL»
- •Всё в порядке → контур emerald + ✓ + «Это допустимый URL»
- •Клиент тоже дополняет значение перед отправкой → гарантируется совпадение предпросмотра и фактически отправленного текста.
Added
- •Заголовки разделов: ✉ Составление сообщения / 📡 Каналы отправки — визуальная группировка + пояснительный текст.
- •Счётчики символов (заголовок
0/255, текст 0/2000) — автоматические цвета: amber от 70 %, red от 90 %.
- •Блок предпросмотра сообщения (пунктирная рамка) — в реальном времени показывает, как заголовок, текст и ссылка вместе будут выглядеть в LINE + счётчик общего числа символов
n/5000 (red выше 4500).
- •Вспомогательный текст для каждого поля:
- •Заголовок: «Отображается и в почтовом ящике участника, и в уведомлении LINE»
- •Текст: «Если написать в тексте URL, он автоматически сокращается и для него ведётся учёт кликов»
- •Ссылка: «Автоматически добавляется в конец текста через пустую строку. Если написать её ещё и в тексте, она появится дважды, будьте внимательны»
- •Значок состояния справа от поля ссылки (
absolute) — ✓ / ⚠ отображаются сразу.
Added
🔗 Правила проверки URL (новый 5-й раздел):
- •Должен начинаться с
http:// / https:// → превращается в токен учёта кликов
- •Отсутствующая схема дополняется автоматически
- •Хосты без точки или со сломанным форматом отклоняются
- •Ограничение в 500 символов
- •Блок с примерами:
✓ https://voscms.com/event / ✓ example.com/page → дополнено автоматически / ✗ asdfasdf (нет точки в хосте)
Notes
- •Прод (
voscms-com) и salon синхронизированы. API, интерфейс и i18n действуют сразу.
- •i18n на 13 языков:
community.broadcast.guide_url_, section_message, section_channels*, subject_hint, body_hint, link_hint, link_{invalid,autofixed,valid}, preview_{title,empty}.
Added
- •Изменение макета
/theadmin/community/broadcast — прежняя единая карточка разделена на lg:grid-cols-3. Основная форма отправки занимает левые 2 колонки (lg:col-span-2), а в правой 1 колонке появился новый блок-справка (<aside>, sticky top-4).
- •Содержание справки из 6 разделов:
1. 📅 Что такое отправка по расписанию? — автоматическая отправка в будущее время, удобна для ночи, выходных и мероприятий
2. 🛠 Как пользоваться — 4 шага (выбрать адресатов → отметить LINE → отметить расписание → отправить), интерфейс с нумерованными значками
3. ⏱ Точность отправки — объяснение 5-минутного интервала cron + блок с примером (10:00 → 10:00–10:05)
4. 🆚 Сразу или по расписанию — различия в поведении двух режимов в виде карточек
5. 📋 Проверка ожидания в очереди — кнопка-ссылка на /history?status=queued
6. ⚠ Предупреждения — минимум на 30 секунд вперёд, внутри приложения/push расписание недоступно, на хостинге без cron не работает, отмена из интерфейса не поддерживается
- •Индиго-градиентный фон (
from-indigo-50 to-blue-50) — блок-справка визуально отделяется от основной карточки и при этом мягко с ней сочетается.
- •i18n на 13 языков — около 20 ключей
community.broadcast.guide_* (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW).
Added
- •На
/mypage/settings добавлена карточка «Предпочитаемый язык» — select из 14 вариантов (автоматически / 13 языков). При сохранении выполняется UPDATE rzx_users.locale, а $_SESSION['locale'] применяется сразу.
- •Хук Auth::loginUser — при входе, если есть
$user['locale'], а locale сессии пуст, он заполняется автоматически. Если пользователь изменил его вручную во время сессии, это значение сохраняется с приоритетом.
- •i18n на 13 языков —
auth.settings.language.{title,description,label,auto,hint,save,success,error_invalid,error_save} (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW).
- •После изменения интерфейс со следующей страницы отображается на языке участника, а если администратор задал в правиле автоматической отправки
messages_by_locale, этому участнику по LINE отправляется соответствующий текст.
Added
3 новые карточки в верхней части /theadmin/community/broadcast/history (после существующих 4 карточек):
1. Общий коэффициент кликов (CTR) — SUM(rzx_broadcast_clicks.click_count) / SUM(rzx_line_broadcasts.recipient_count WHERE status='sent') × 100. Крупный текст цвета emerald.
2. A/B-тест (последние 30 дней) — для отправок с variant IS NOT NULL получатели, клики и CTR по каждому variant. Variant с более высоким CTR выделяется цветом emerald.
3. Распределение по locale (последние 30 дней) — только для отправок с source='auto_rule' AND messages_by_locale IS NOT NULL locale определяется сравнением ключей JSON messages_by_locale правила с первыми 30 символами текста broadcast → получатели/клики/CTR по каждому locale. Сортировка через ksort.
- •i18n на 13 языков:
community.broadcast.stat_{ctr_title,ctr_breakdown,variant_title,variant_hint,locale_title,locale_hint,empty}.
Notes
- •Деревья
voscms-com и salon синхронизированы. Новых столбцов БД нет (все они уже вошли в миграции 008/009/010 этапа Step 4).
- •Логика сопоставления locale сравнивает первые 30 символов текста — если текст после подстановки переменных совпадает, он учитывается в той же группе locale. Если текст слишком короткий или целиком заполняется подстановкой переменных, процент совпадений может снизиться (в будущем можно добавить столбец locale прямо в строку broadcast).
Added
- •миграция 010 —
rzx_auto_broadcast_rules.messages_by_locale JSON NULL + rzx_users.locale VARCHAR(10) NULL + индекс idx_locale.
- •AutoBroadcaster::lineUserIdsWithLocale — запрашивает пары userId LINE и locale участника. Среды без столбца
users.locale откатываются к lineUserIds.
- •AutoBroadcaster::pickLocaleMessage — приоритет: user.locale участника → locale сайта по умолчанию (rzx_settings.locale) → первый ключ JSON (если всё не подошло, null → возврат к тексту A).
- •AutoBroadcaster::fire — если есть
messages_by_locale, для каждого участника используется текст, соответствующий его locale; иначе A/B-variant; а если и его нет, одиночный A. Когда locale, variant и значение по умолчанию дают одинаковый текст, они отправляются как одна группа через multicast (эффективность).
- •Модальное окно правил администратора — «Многоязычная маршрутизация включена» и прямой ввод JSON, раскрываемые через
<details>. Отображается в виде pretty-print. Проверка invalid_locale_json.
- •i18n на 13 языков —
community.automation.locale_{toggle,placeholder,hint} + fields.messages_by_locale + error.invalid_locale_json (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW).
Notes
- •Этапы 1–4 инфраструктуры отправки в LINE завершены (2.6.9–2.6.15):
1. Step 1 — CTA · utility stack · ручная отправка · audience по уровням · множественный выбор (7872c4b, 7c94a55, c4a3325)
2. Step 2 — Автоматическая классификация групп участников (b24979c)
3. Step 3 — Правила автоматической отправки по событиям (60d47cd) + cron отложенной отправки (8cf02ad)
4. Step 4 — Отслеживание кликов (79c99a84) + A/B-тест (86e19b0b) + многоязычная маршрутизация (эта)
- •Миграция 010 применена на проде (
voscms.com). users.locale — новый столбец: у всех существующих участников он NULL (используется locale сайта по умолчанию → то же поведение, что и раньше).
- •Интерфейс «Моей страницы», где участник сам задаёт предпочитаемый locale, — следующий этап.
- •Рекомендуемые записи cron:
/5 /usr/bin/php8.3 /var/www/voscms-com/scripts/process-scheduled-broadcasts.php, 0 3 /usr/bin/php8.3 /var/www/voscms-com/scripts/recalculate-member-grades.php.
Added
- •миграция 009 —
rzx_auto_broadcast_rules.message_template_b TEXT NULL, rzx_line_broadcasts.variant CHAR(1) NULL, rzx_broadcast_clicks.variant CHAR(1) NULL + индекс (source_rule_id, variant).
- •AutoBroadcaster::fire — если
message_template_b не пуст, userId LINE делятся поровну 50:50 на A/B по crc32 mod 2. Для каждого variant история отправки заранее вставляется (INSERT, logQueued) → wrap токена → multicast → finalize. Один и тот же пользователь при повторных отправках всегда получает один и тот же variant (согласованность хэша).
- •Модальное окно правил администратора — опция «A/B-тест включён» и поле ввода variant B, раскрываемые через
<details>. По умолчанию простая одиночная отправка, а A/B — явное включение.
- •ClickTracker — передаёт метаданные variant. Если столбец broadcast_clicks.variant существует, он заполняется при INSERT. Если нет, применяется резервный вариант (совместимость со средами, где миграция 009 не применена).
- •Резервный вариант logQueued — работает и в средах без столбца variant (когда миграция 009 не применена).
- •i18n на 13 языков —
community.automation.ab_{toggle,placeholder,hint} (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW).
Notes
- •Три оператора ALTER из миграции 009 выполнены на проде (
voscms.com) по отдельности. У существующих правил message_template_b=NULL (поведение одиночной отправки сохраняется).
- •Редактирование правила → ввод variant B в раскрывающейся области «A/B-тест включён» → достаточно сохранить, и со следующего срабатывания A/B-распределение происходит автоматически.
Added
- •миграция 008 (
rzx_broadcast_clicks) — token (8 символов base62, UNIQUE), broadcast_id, source_rule_id, original_url (2048), click_count, last_clicked_at, last_user_agent. Для той же комбинации (broadcast_id, original_url) токен используется повторно.
- •Класс
ClickTracker — wrap($pdo, $siteId, $body, $meta) извлекает из текста URL http/https регулярным выражением → INSERT или повторное использование → заменяет их на /t/{token}. consume($pdo, $token, $ua) превращает токен в исходный URL и увеличивает click_count.
- •Маршрут
/t/{token} (index.php:330) — вход в маршрутизатор на регулярных выражениях. ClickTracker::consume → редирект 302 на исходный URL, а если его нет, 404. Проверка токена URL ([A-Za-z0-9]{4,16}).
- •Интеграция с правилами автоматической отправки —
AutoBroadcaster::fire() непосредственно перед отправкой работает в порядке logQueued → получение broadcast_id → ClickTracker::wrap → multicast → finalizeBroadcast. Связь токена и broadcast_id гарантирована.
- •Интеграция с ручной отправкой — в
api/broadcast.php wrap выполняется сразу после сборки текста для немедленной отправки. В scripts/process-scheduled-broadcasts.php wrap выполняется непосредственно перед отправкой из очереди (broadcast_id известен).
- •Настройка домена отслеживания — приоритет у
rzx_settings.click_track_domain, иначе APP_URL. Возможен короткий домен (например, s.voscms.com).
Notes
- •Миграция 008 применена на проде (
voscms.com).
- •Интеграционный тест wrap/consume: 2 URL в тексте → вставлено (INSERT) 2 токена по 8 символов → при вызове consume возвращается исходный URL, а click_count корректно увеличивается.
Added
- •миграция 007 (
rzx_line_broadcasts): столбцы scheduled_at DATETIME NULL и processed_at DATETIME NULL плюс индекс (status, scheduled_at). scheduled_at NULL = отправить сразу (как раньше), будущее время = в очередь.
- •
scripts/process-scheduled-broadcasts.php — точка входа cron. Берёт 50 записей с status='queued' AND scheduled_at <= NOW() AND processed_at IS NULL → сразу блокирует их через processed_at = NOW() (транзакция) → разбирает audience (all / grade_N, как в broadcast.php) → multicast LineMessenger порциями по 500 → обновляет status по результату. Рекомендуемая периодичность cron: /5 *.
- •Интерфейс broadcast.php: флажок
[Запланировать отправку] + поле datetime-local. При установке флажка значение по умолчанию — текущее время + 1 час (локальное время пользователя). Если флажок не установлен или поле пусто, отправка немедленная, как раньше. Планировать можно только канал LINE (внутри приложения и push остаются немедленными).
- •api/broadcast.php: обрабатывает POST-параметр
scheduled_at. Для будущего времени (> NOW + 30s) при send_line он без вызова API LINE только вставляет (INSERT) rzx_line_broadcasts (status='queued', scheduled_at) и возвращает в ответе scheduled: true, scheduled_at.
- •i18n на 13 языков:
community.broadcast.schedule_{label,enable,hint,success} (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW).
Fixed
- •Столбцы INSERT исправлены в соответствии с реальной схемой
rzx_line_broadcasts (title, body, audience varchar(20), recipient_count, status enum(queued|sent|failed)). Раньше INSERT использовал несуществующие имена столбцов (message_text, recipients_count, sent_count, failed_count, audience_value, partial), и даже резервный вариант терпел неудачу, поэтому история автоматической отправки отсутствовала.
Notes
- •Миграция 007 применена на проде (
voscms.com). Dry-run подтвердил, что pickup равен 0 записей.
- •Деревья прода (
voscms-com) и salon синхронизированы. .env исключён.
- •Регистрация cron — решение стороны эксплуатации хостинга (при необходимости
/5 * /usr/bin/php8.3 /var/www/voscms-com/scripts/process-scheduled-broadcasts.php).
Added
- •Таблица правил автоматической отправки (
rzx_auto_broadcast_rules) — миграция 005_auto_broadcast_rules.sql. Правила изолированы по site_id, со столбцами trigger_type / trigger_filter (JSON) / audience (all|grade_X|custom) / message_template / is_enabled / fired_count / last_fired_at.
- •Диспетчер AutoBroadcaster (
rzxlib/Core/Notification/AutoBroadcaster.php) — статическая точка входа AutoBroadcaster::fire($triggerType, $ctx). Сопоставление с активными правилами → подстановка переменных шаблона ({{title}}, {{url}}, {{author}}, {{board_name}}, {{site_name}}) → разбор audience (та же логика, что в broadcast.php; для уровня is_default участники с NULL включаются автоматически) → поиск userId LINE через rzx_user_social_accounts → multicast порциями по 500 → запись истории в rzx_line_broadcasts → обновление статистики правила.
- •Хук автоматической отправки для новых сообщений форума (api-posts.php:184) — сразу после INSERT сообщения (кроме приватных) вызывается
AutoBroadcaster::fire('board_post', [post_id, board_id, title, url, author, board_name]). Даже если отправка не удалась, ответ на сообщение не затрагивается.
- •Страница администратора для управления правилами (
/theadmin/community/automation) — интерфейс в виде сетки карточек. CRUD правил + переключатель вкл/выкл + цветной значок для каждого триггера (board_post=blue, review_created=amber, reservation_created=green). Триггер / фильтр (JSON) / целевой уровень / шаблон сообщения редактируются в модальном окне.
- •Столбцы отслеживания источника в rzx_line_broadcasts — миграция
006_line_broadcasts_add_source.sql. source ('manual' | 'auto_rule') + source_rule_id (INT) + индекс. История автоматической отправки отличима от ручной в той же таблице.
- •Маршруты и миграции зарегистрированы в plugin.json — маршрут
community/automation, а также миграции 004/005/006 после 003.
- •i18n на 13 языков — все ключи
community.automation.* (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW). Подписи триггеров, поля, подсказки, сообщения об успехе/ошибке и шаблоны по умолчанию.
Notes
- •Миграции 005/006 применены на проде (
voscms.com). Правила пока не зарегистрированы.
- •Деревья прода (
voscms-com) и salon синхронизированы. .env исключён.
- •Step 3-B (отложенная отправка
scheduled_at + cron) — следующий этап.
Added
- •Автоматическое назначение уровня по умолчанию при регистрации (
rzxlib/Core/Auth/Auth.php::register()) — сразу после регистрации в rzx_member_grades ищется уровень с is_default=1, и grade_id нового участника заполняется немедленно. На сайтах без таблицы member_grades (устаревших или ненастроенных) пропускается молча через try-catch. Существующих участников с NULL заполняют отдельным скриптом пересчёта или кнопкой.
- •CLI-скрипт пересчёта уровней (
scripts/recalculate-member-grades.php) — на основе суммы платежей из rzx_payments и числа бронирований из rzx_reservations (только если таблица существует) он обходит уровни в порядке sort_order DESC и сопоставляет самый высокий уровень, который можно выполнить. Уровни без заданных правил (min_spent=0 AND min_reservations=0) автоматически пропускаются — кандидатами на повышение являются только уровни с явными правилами. Участники, не удовлетворяющие условиям, возвращаются на уровень по умолчанию.
- •Параметры:
--dry-run (имитация без реального UPDATE), --user-id=UUID (только конкретный участник).
- •Пример cron:
0 3 * /usr/bin/php8.3 /var/www/voscms/scripts/recalculate-member-grades.php
- •Кнопка администратора «Пересчитать сейчас» (
/theadmin/members/groups) — в панель действий добавлена кнопка изумрудного оттенка. При нажатии: окно подтверждения → AJAX action=recalculate_grades → уведомление о результате (цель/повышено/понижено/новые) → автоматическое обновление. Поскольку та же логика, что и в скрипте, включена прямо в код, это работает даже в средах без прав PHP CLI.
- •i18n на 13 языков — новые ключи
members.groups.recalculate* (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW): recalculate, recalculate_hint, confirm_recalculate, recalculate_stat.{total,upgraded,downgraded,fallback}, success.recalculated, error.{no_grades,no_default_grade}.
Notes
- •На проде (
voscms.com) проверены --dry-run → реальный запуск → повторный запуск после доработки правил. Все 5 участников с grade_id NULL корректно получили Normal (по умолчанию). У VIP/Gold/Silver нет правил, поэтому они остаются на 0 участников, пока правила не будут добавлены (так задумано).
- •Деревья прода (
voscms-com) и salon синхронизированы. .env исключён.
Added
- •Компонент CTA добавления друга LINE для участников (
resources/views/components/line-friend-cta.php) — 5 variants (card / button / bar / side-tab / bubble) × 6 позиций (bottom-right/bottom-left/bottom-center/top-right/middle-right/middle-left) × 3 размера, пользовательские цвета, задержка показа и «запомнить закрытие» на 24 часа (localStorage).
- •Изменяемый интерфейс настроек — новый раздел «CTA добавления друга LINE» в нижней части карточки канала LINE в
/theadmin/settings/message-api. Basic ID, переключатель включения, радиокнопки variant, выбор position, размер, выбор цвета и ввод delay.
- •Автоматический показ в 5 макетах — автоматически подключается из
skins/layouts/{default,modern,minimal,modern-copy-260413}/footer.php. На страницах оплаты, заявки на услугу и процесса установки он автоматически пропускается.
Added
- •Новый компонент
resources/views/components/utility-floating-stack.php — шаблон дизайна cameron_plugin с thevos.jp (квадратный stack 80×80 у правого края + зазор 1 px + акцентный цвет для каждой иконки). Позицию, размер и цвет может настроить оператор.
- •Оба режима сосуществуют —
utility_stack_mode = separate (по умолчанию, каждый CTA отдельно) / unified (PWA + LINE показываются вместе одним stack). В интегрированном режиме автоматический показ 5 variants CTA LINE отключён, а автоматические триггеры модального окна установки PWA (баннер через setTimeout/DOMContentLoaded) тоже отключены; окно открывается только щелчком по иконке PWA в stack.
- •Естественное расширение в будущем — по шаблону регистрации элементов (канал Kakao, WhatsApp, подписка на рассылку, переключатель тёмного режима и т. д.) другие элементы можно добавить в stack, просто добавив ключи настроек.
- •Гарантированный автоматический перенос строк — встроенный CSS
word-break: break-all; overflow-wrap: anywhere; max-width: 72px обходит ограничение ширины дочерних flex-элементов Tailwind. Подписи CJK тоже естественно переносятся внутри блока.
- •i18n на 13 языков — группа
line_cta. (22 ключа) и группа utility_stack. (12 ключей) в resources/lang/{13 locale}/messaging.php. Для японского и китайского пробелы на смысловых границах (например, «アプリとして インストール») гарантируют естественные места переноса.
Added
- •
LineMessenger::broadcast(messages) + broadcastText(text) — рассылает сразу всем друзьям (до 5 объектов сообщений; лимит отправки LINE расходуется на число получателей × число сообщений).
- •
LineMessenger::multicast(userIds[], messages) — отправляет на указанные userId (не более 500) (инфраструктура групповой отправки).
- •Улучшение интерфейса
/theadmin/community/broadcast — добавлен флажок «Отправить также друзьям LINE» (зелёный баннер). Независимо от push-рассылки автоматически отправляет друзьям LINE через multicast/broadcast.
- •Рассылка по уровню участника —
rzx_member_grades загружается динамически, и радиоварианты по уровням появляются автоматически (цветная точка + slug + счётчик в реальном времени). Формат audience — grade_<id>.
- •Участники с
grade_id IS NULL автоматически включаются в уровень с is_default=1 — даже на сайтах, где уровень при регистрации автоматически не назначается, участники с NULL естественно входят в подсчёт и рассылку для уровня по умолчанию.
- •Таблица истории рассылок
rzx_line_broadcasts — изоляция по site_id, method (broadcast/multicast), audience, title/body, recipient_count, line_request_id, api_status/error, status. При отправке строка автоматически добавляется (INSERT).
- •При
audience=all выбирается broadcast, в остальных случаях multicast, автоматически — собирает userId LINE целевых пользователей с user_social_accounts.line_friend=1 и отправляет порциями по 500.
- •Единая ширина — в broadcast.php
max-w-3xl → max-w-7xl (как на других страницах администрирования).
- •i18n на 13 языков — добавлены ключи
send_line_label/desc и audience_grade_group.
Changed
- •
resources/views/admin/dashboard.php — комментарий в заголовке, запасное значение pageTitle и баннер уведомления об обновлении RezlyX v... исправлены на «VosCMS».
- •
resources/views/admin/partials/admin-topbar.php — исправлен комментарий в заголовке.
Fixed
- •
storage/.update_cache хранил старый кэш current: "2.6.4", поэтому даже после обновления до v2.6.8 на панели по-прежнему показывался баннер «Есть новое обновление» — после удаления кэша он пересчитывается автоматически при следующем запросе.
- •Накопленные изменения dev → прод не были синхронизированы — помимо этой сессии, многие файлы, такие как install-core.php (патчи дефектов № 1–№ 7) и version.json, так и не синхронизировались с продом. dev синхронизирован с продом за один раз через rsync (342 файла изменено + 196 новых, 6,3 МБ, без пользовательских данных,
.env, storage/, uploads/, vendor/ и .git/).
Documentation
- •
docs/LINE_FRIEND_CTA_DESIGN_GUIDE.md — добавлены Part 3 (фактическая реализация интегрированного Utility Floating Stack), Part 4 (фактическая реализация эксплуатации рассылки Фазы 1), Part 5 (сценарии внешних пользователей — анализ ценности рассылки с множественным выбором) и Part 6 (дорожная карта Фаз 2–4). С примерно 631 до 920 строк.
Regression Test
- •Проверяется наличие
utility-floating-stack.php и 5 partial-файлов variants line-friend-cta.
- •Проверяется наличие методов
LineMessenger::broadcast/multicast.
- •Проверяется наличие файла миграции
plugins/vos-community/migrations/004_line_broadcasts.sql.
- •Проверяется наличие ключей
messaging.utility_stack. + community.broadcast.send_line_ на 13 языках.
- •Автоматически проверяется отсутствие остатков RezlyX в dashboard.php / admin-topbar.php.
Fixed
- •Консолидация таблицы лицензий vos-market — 6 API-файлов запрашивали неправильную таблицу
{$pfx}vos_licenses, вызывая "Server error" 500 при каждом платеже. Вся проверка лицензий теперь направлена в единый мастер vcs_licenses (управляемый vos-license-server).
- •Удаление дыры безопасности неверифицированной регистрации в мастере в
sync.php — Предыдущий код UPSERT-ил в vos_licenses без проверки vos_key, позволяя кому угодно автоматически регистрировать произвольные ключи лицензий в мастере через простой POST. Отслеживание теперь осуществляется существующим rzx_mkt_sync_reports (per-item).
- •
migrations/007 устарел — Неправильно спроектированная миграция теперь является no-op (SELECT 1), с причиной и историей изменений в комментариях.
Added
- •4 карточки статистики — Отслеживаемые сайты / Сайты с нарушениями / Многодоменные лицензии / Неактивные 30+ дней.
- •Вкладка нарушений (отображение на уровне товара) — С первого взгляда видите, какой платный виджет установлен без лицензии на каком домене. Отображаются цена, версия, vos_key и последнее посещение.
- •Вкладка нескольких доменов — Случаи, когда один лицензионный ключ сообщается с нескольких доменов.
- •Вкладка неактивных — Сайты без сигнала синхронизации более 30 дней.
- •Уточнённое определение нарушений —
unknown_product и бесплатные элементы исключены. Только платные товары маркетплейса в нелицензированном состоянии считаются нарушениями.
- •Белый список собственных доменов — Домены оператора, хранящиеся как JSON-массив в
settings.mkt_owner_domains, автоматически исключаются из подсчётов.
- •i18n на 13 языках — Боковая панель, карточки, вкладки и подсказки полностью переведены.
Changed
- •
settings/system/updates — Карточки установленной и последней версии исправлены на "VosCMS vX.Y.Z".
- •
updates/ajax.php — Fallback имени БД и User-Agent исправлены.
Regression Test
- •Автоматически проверяет отсутствие остатков SQL
vos_licenses в 6 файлах vos-market
- •Обнаруживает регрессию дыры безопасности INSERT в
sync.php
- •Проверяет сохранение no-op состояния
migrations/007
- •Автоматически проверяет отсутствие остатков RezlyX в 3 файлах экрана обновлений
Fixed
- •Автоматическое заполнение
site_url — Мастер установки записывал введённый пользователем siteUrl только в APP_URL файла .env и не записывал в rzx_settings.site_url базы данных, поэтому все функции нового сайта, зависящие от site_url (автоматическая установка, SEO, ссылки в письмах и т. д.), ломались. Исправлено добавлением site_url в массив $settings в install-core.php.
- •Автоматическая регистрация в
rzx_site_domains — Мастер установки не добавлял текущий домен установки в таблицу site_domains, поэтому SiteRouter не распознавал домен. Теперь установленный домен автоматически ВСТАВЛЯЕТСЯ как primary для site_id=1.
- •Автоматическая запись
MARKET_API_URL в .env — При новой установке в .env не было адреса API маркета, и InstallerService не находил, куда обращаться. Теперь по умолчанию автоматически записывается https://market.voscms.com/api/market (работа с единым мастером лицензий).
- •Опечатка в имени таблицы API установки маркета —
vos-market/views/api/item/install.php при проверке лицензии обращался к несуществующей таблице rzx_vos_licenses, из-за чего все автоматические установки отвечали «Server error» (HTTP 500). Исправлено на настоящую таблицу vcs_licenses (мастер лицензий, фиксированное имя без префикса).
- •Автоматическая настройка прав на каталоги widgets/plugins/themes — После установки пользователь PHP-FPM не мог писать в эти каталоги, и автоматическая установка молча завершалась неудачей. Теперь мастер установки автоматически выдаёт setgid и запись для группы (группа запуска PHP определяется автоматически, чтобы учитывать разнообразие внешних сред).
- •Многоступенчатый резерв в
install-handler.php — В мультисайтовой среде запрос site_url выполнялся с LIMIT 1 без фильтра по site_id и был нестабилен. Трёхступенчатый резерв SiteRouter → откат к site_id=1 → $_SERVER['HTTP_HOST'] гарантирует распознавание домена в любой среде.
Added
- •
InstallerService::installTheme() — Читает theme.json товара с type=theme и рекурсивно устанавливает plugin/widget/skin из requires. Гарантирует проверку на 13 языков и идемпотентность (уже установленные элементы пропускаются).
- •
runPostInstall — Сразу после установки темы автоматически запускает seed-файлы post_install/settings.json / boards.json / pages.json, за один раз автоматически настраивая страницы, размещение виджетов и базовые параметры.
- •Обязательные 13 языков (
REQUIRED_LOCALES) — Многоязычные объекты (name, description, title страницы, config.title виджета и т. д.) проходят проверку только при наличии всех 13 ключей. О недостающих локалях выдаётся предупреждение.
- •Единый мастер
vcs_licenses — Вся выдача и проверка лицензий управляется через боевой маркет (market.voscms.com). Среды dev и test используют тот же мастер.
Added
- •Автоматическая проверка этих шести дефектов — Всего 13 пунктов, включая логику seed мастера установки, права на каталоги, API маркета, доступность сервера лицензий, ветку theme в InstallerService и многоступенчатый резерв install-handler.
- •Автоматический хук в build-voscms.sh — Вызывается автоматически в конце сборки. Если возникает хотя бы один FAIL, код возврата отличен от 0 и выпуск блокируется. Если тот же дефект повторится в следующей сборке, он будет пойман сразу.
Documentation
- •
docs/theme-package/THEME_PACKAGE_SPEC.md — Определяет стандарт пакета тем VosCMS (theme.json). Содержит пример интегрированной темы Salon, подробную схему post_install и правило 13 языков.
Fixed
- •Резервные копии обновления: хранится только одна на версию —
Backup::createFull() теперь перед созданием копии удаляет существующую копию той же версии. Это устраняет проблему, когда при повторных обновлениях или откатах на одной версии в списке копий накапливались дубликаты этой версии (сохраняется только последнее состояние непосредственно перед обновлением).
Fixed
- •Восстановлен отсутствовавший
update-api.php в дистрибутиве — Скрипт сборки ошибочно относил update-api.php, бэкенд обновления, к «dev-файлам» и удалял его из дистрибутива. В результате вкладка обновления во всех установленных дистрибутивах не работала и показывала «File not found». Файл больше не исключается из сборки и теперь корректно входит в дистрибутив.
- •Исправлено нераспознавание сессии входа (Unauthorized) —
update-api.php смотрел путь хранения сессий по умолчанию (/var/lib/php/sessions); теперь он использует тот же storage/sessions, что и index.php. Это устраняет ошибку Unauthorized, которая появлялась даже при входе администратором.
Changed
- •Обновления Core используют только собственный манифест дистрибутива как единственный канал — Из
update-api.php удалены остатки тегов GitHub, токена и диагностики (перекрыт путь, по которому check перезаписывал результат манифеста тегом GitHub). В маркетплейсе нет Core (только плагины).
- •Усилена безопасность — В
update-api.php добавлены проверка CSRF и проверка прав мастера (владельца).
- •Приведён в порядок список сохранения при обновлении —
skins и config исключены из списка сохранения. Поскольку applyUpdate только копирует, скины ядра и config из zip дистрибутива обновляются, а пользовательские файлы клиентов, которых нет в zip, сохраняются.
Changed
- •Убрана дублирующая кнопка «Проверить обновления» — при открытии страницы последняя версия проверяется автоматически.
- •Исправлено исчезновение списка резервных копий при временной ошибке → раздел сохраняется и показывается пустым.
- •При ошибке вместо необработанных сообщений вроде
Unauthorized показывается уведомление «Сейчас вы используете последнюю версию».
Added
- •Раздел
um на 13 языках добавлен — все локали vos-community/lang/{locale}.php (ko·en·ja·de·es·fr·id·mn·ru·tr·vi·zh_CN·zh_TW): follow·following·message·block·unblock·report·report_about·report_reason/detail/placeholder·reason_spam/harassment/inappropriate/other·report_submit/sent/failed·msg_title/placeholder/send/sent/failed·block/unblock_confirm·block/unblock_done·cancel·failed·network_error
- •Полная i18n
user-mention-menu.php — весь жёстко закодированный корейский в PHP-HTML заменён на __('community.um.*'); строки времени выполнения JS выводятся как объект PHP через UM_LANG = <?= json_encode(...) ?>, помощник umT(key, name) подставляет заполнитель :name
- •Меню упоминаний разрешено для гостей — переработана структура защиты в
community-overlays.php: пути admin/API полностью заблокированы, тогда как user-mention-menu включается даже для гостей (подписка/жалоба ведут к входу); защита действий страницы и username остаётся только для авторизованных
Changed
- •
boardPreloadUsers(array $rows) добавлена (board/_init.php) — массово загружает список user_id одним IN-запросом, общий через кэш $GLOBALS['_boardUserCache']
- •
boardAuthorMention() расширена — добавлены атрибуты data-user-username·data-user-avatar (точно связывает URL истории /story/{username}, показывает изображение аватара). Предпочитает HIT кэша предзагрузки, при промахе использует ленивый одиночный запрос
- •
board/list.php·board/read.php·skins/board/qna/_list-full.php — вызывают boardPreloadUsers() перед рендерингом для предотвращения N+1 запросов
Fixed
- •Парсер отображения расширен (
resources/views/system/changelog/index.php) — добавлен рендеринг блоков кода (``), таблиц (|...|), markdown-ссылок (text), подзаголовков #### и горизонтальных линий (---`). Устраняет отображение прежних версий (2.4.1·2.3.8·2.3.7 и т. д.) с этим синтаксисом как сырого markdown
- •Поверсионный locale-фолбэк — изменено с выборки всего списка на одном языке на поверсионную основу (
текущая локаль → en → ko). Теперь каждая версия отображается на любом языке, а ещё не переведённые версии показывают значок резервного языка
- •Выравнивание границ блоков (
rzxlib/Core/Changelog/ChangelogParser.php) — предотвращено поглощение заголовка ## [Unreleased] без даты предыдущей версией (напр. 1.5.0) путём разделения блоков на каждом ## [
Added
- •Миграция 056 таблица
dist_downloads (site_id·version·ip·user_agent·referrer·utm_*·is_bot·created_at)
- •Эндпоинт загрузки
api/dist-download.php — вместо статической прямой ссылки /download/*.zip запросы идут через него, чтобы фиксировать статистику по версиям / по дням / по источнику (referrer·UTM), а затем 302-редирект. UA ботов считаются отдельно; белый список версий (защита от path-traversal)
- •Страница статистики в админке
resources/views/admin/dist-downloads.php (только мастер, 13 языков) — сводные карточки (всего/сегодня/7д/30д), столбцы по версиям, динамика по дням (30д), Топ-10 источников, UTM. Собственные SVG/CSS-графики (без внешних библиотек)
- •Ссылка загрузки виджета
download-hero переведена на эндпоинт подсчёта
- •Добавлено меню админки «Статистика загрузок» (config/admin-menu.php, только мастер)
Added
- •
hero-cta-text — верхняя текстовая область (печать). Значения по умолчанию совпадают с рабочим содержимым hero-cta2 (13 языков); пользовательские настройки в приоритете
- •
hero-cta-button — кнопка загрузки (автоопределение последней версии + подсчёт загрузок, без настройки) плюс необязательная кнопка маркетплейса (одна кнопка, если оставить пустой)
- •Подписи и значения по умолчанию формы настроек на 13 языках (поддержка объектов
getLabel в конструкторе)
- •Цвет/фон кнопки загрузки приведены в соответствие с hero-cta2 (#2563eb)
Added
- •Перетащите карточку из палитры на холст, чтобы добавить виджет (группа
pull:'clone' SortableJS + замена реальным блоком в onAdd холста). Работает вместе с добавлением по клику и сортировкой ↑↓/за ручку
- •Флайаут остаётся открытым во время перетаскивания + подсветка зоны сброса (
wb-drop-active)
Changed
- •Убран отступ обёртки страницы между виджетами (
type-widget.php py-2 → 0)
- •Стабилизирован синхронный ИИ-перевод комментариев — nginx
fastcgi_read_timeout 150с (vhost форума) · PHP-FPM request_terminate_timeout 150 · set_time_limit 150, OllamaProvider keep_alive 30 мин (модель остаётся в памяти, устраняя холодные загрузки)
- •Добавлены кнопки копирования для Webhook URL и Redirect URI на вкладках Message API и Социальный вход (13 языков, с запасным вариантом для небезопасного контекста)
Added
- •Миграция 036 — колонки
withholding_tax · consumption_tax · tax_basis в partner_earnings / payouts
- •Миграция 037 — колонки
fx_rate · fx_amount · fx_currency в payouts (запись переводов с конвертацией в USD)
- •Миграция 038 — колонка
vcs_developers.paypal_email
- •Хелпер
rzxlib/Core/Helpers/MarketTax::calculate() — стандартная японская модель вычетов
· Цена продажи включает налог (税込); маркетплейс напрямую перечисляет НДС в NTA
· freelance → удержание 10,21% / invoice_eligible → налоговый вычет на покупку / no_invoice → НДС не уплачивается
· Без категории или без данных → принудительный freelance (консервативно: недоплата возвращается, переплата невозместима)
- •Скрипт задним числом
scripts/market-tax-backfill.php — пересчёт существующих earnings/payouts (защищает status='paid', поддерживает --dry-run)
- •Отображение расчёта в 5 частях: выручка → комиссия → вычет НДС → удержание (freelance) → чистая выплата
- •Новые ключи в
rzx_mkt_settings: min_payout_amount · consumption_tax_rate · default_commission_rate (settings в приоритете везде; жёстко прописанный fallback удалён)
Added
- •Модал расчёта в
/theadmin/market/partners/payouts — prompt() × 2 + alert() → единый модал
· JOIN с vcs_developers для автозаполнения банка/отделения/владельца счёта/номера счёта/типа бизнеса/T-номера (prefill; PII enc: автодешифрование)
· Автоматический расчёт 5 частей + чипы правовой основы Японии (freelance_withholding и др.) + ссылки на статьи (§204, система счетов-фактур)
· Toast-уведомление на странице; закрытие модала и автоматическая перезагрузка
- •Автоматическая конвертация в USD для счетов вне страны работы (Миграция 037)
· Автоматический курс Frankfurter API (кэш storage TTL 24 ч) + ручное переопределение администратором (TTS банка)
· Автоматический выбор method=paypal
· Для иностранцев: карточка банковского счёта скрыта, карточка e-mail PayPal отображается (bank_country == site_country)
- •Указание суммы расчёта разработчиком (
/mypage/developer/earnings)
· Весь баланс → ввод суммы + кнопка полной суммы + валидация минимальной суммы
· Колонки 5 частей для истории продаж, информационный блок расчёта (применяемый налоговый закон по типу бизнеса)
- •Конвертация в USD и курс валюты как вспомогательная информация в истории выплат (экран администратора и разработчика)
- •Замена графика тенденций продаж на SVG гистограмму (устраняет проблему видимости полос div)
Added
- •Политика блокировки на основе
tax_review_status (профиль разработчика)
· not_submitted / rejected → редактируемо
· pending / approved → блокировка редактирования (замок + предупреждение)
· rejected → редактируемо + подсказка «при сохранении войдёт в очередь проверки»
· Серверная защита: HTTP 423 Locked (блокирует обход через интерфейс)
- •
update.php частичное обновление в безопасном режиме — динамический UPDATE только отправленных полей (ранее: полная перезапись с риском NULL для неотправленных полей)
- •Ссылка на официальный сайт верификации (экран верификации администратора + профиль разработчика)
· 🇯🇵 Сайт публикации номера юридического лица NTA (https://www.houjin-bangou.nta.go.jp/)
· 🇯🇵 Сайт публикации квалифицированных эмитентов счетов-фактур NTA (https://www.invoice-kohyo.nta.go.jp/regno-search/)
· 🇰🇷 Проверка статуса регистрации компании на Hometax Кореи
· Кнопка копирования в один клик рядом с номером компании/T-номером
Added
- •Новая регистрация (
/mypage/developer) — выбор гражданства перемещён в верхнюю часть формы, с руководством по сравнению со страной работы
· country == site_country → 🏠 внутренняя форма (тип бизнеса / банковский счёт)
· country != site_country → 🌐 иностранная форма (автоматический freelance + обязательный e-mail PayPal)
· Геттер isDomestic Alpine.js, глобальная константа OPERATING_COUNTRY
- •Редактирование профиля (
/mypage/developer/profile) — та же структура (правило [feedback_admin_user_parity])
· Иностранцы: раздел компании скрыт; расчётная информация = только e-mail PayPal
· Гражданство заблокировано (для изменения обратитесь к администратору), чип режима вверху
- •Серверная валидация API регистрации — e-mail PayPal обязателен для иностранцев, принудительный freelance, значения компании/счёта игнорируются
Added
- •Автоматическая замена токенов в
page.php — применяется глобально ко всем системным страницам
· {{COMPANY_NAME}} {{COMPANY_ADDRESS}} {{COMPANY_PHONE}} {{COMPANY_EMAIL}} {{COMPANY_REG_NUMBER}} {{COMPANY_REPRESENTATIVE}}
· {{SITE_NAME}} {{SITE_URL}} {{DOMAIN}}
· Автоматическое сопоставление из settings business_ или site_ администратора
· Пользовательские page_title / page_subtitle также проходят замену токенов
- •Приоритет
{{DOMAIN}} — official_domain (официальный домен для условий и политик) → хост site_url
· Даже на сервере разработки vos.21ces.com текст условий отображает voscms.com
Added
- •Полная замена
terms / privacy в rzx_page_contents (по 13 строк) — условия платформы бронирования RezlyX → условия свободного программного обеспечения VosCMS
- •Пользовательское соглашение из 18 статей: цель · определения · публикация и пересмотр · дополнительные нормы · заключение договора · конфиденциальность · уведомления · обязанности компании · обязанности пользователя · предоставление услуг · изменения · ограничения использования · лицензия (бесплатное использование) · владение и хранение данных · авторские права на публикации · отказ от ответственности (AS-IS) · возмещение убытков · разрешение споров
- •Политика конфиденциальности из 10 статей: цель обработки · категории · сроки хранения · передача и поручение третьим лицам · права субъектов данных · меры безопасности · cookies · ответственный за защиту · возмещение при нарушениях (PPC) · изменения
- •Японское право + исключительная юрисдикция Районного суда Фукуоки, со ссылками на APPI §33·§34·§35
- •Данные компании используют токены → одно редактирование в административной панели автоматически синхронизирует условия и политику
- •Японская версия является официальным текстом; официальные переводы на родном языке для 13 языков
- •Карточка информации о компании — только 3 пункта: официальное название, адрес и телефон (представитель/имя удалены во избежание пересмотра условий при изменении регистрации)
- •Скрипты сохранения:
scripts/voscms-terms-update.php · scripts/voscms-privacy-update.php
Fixed
- •
menus-api.php в /theadmin/site/menus отставал от vos на 99 строк, что приводило к пропуску валидации и перевода при сохранении/редактировании меню
- •Синхронизация menus-api.php из vos → market:
· mn_link_target_exists() — меню-ярлыки (ссылки) сохраняются только после проверки существования целевой страницы (системная страница · rzx_page_contents)
· Защита от повторного использования ID меню — автоматическая очистка устаревших rzx_translations.menu_item.{id}.* для новых ID
· Метод INSERT для переводов: INSERT IGNORE → INSERT … ON DUPLICATE KEY UPDATE (разрешает перезапись)
· При изменении title·url: полная очистка старых переводов + автоматическое переоформление переводов при совпадении с системной страницей (config/system-pages.php)
- •Добавлено 4 файла
admin/site/menus*.php в INCLUDES sync-to-market.sh
Fixed
- •Отсутствующая таблица
rzx_contact_messages → добавлена миграция 041_create_contact_messages.sql (IF NOT EXISTS, без влияния на существующий vos)
- •Двойная компоновка в
contact-messages.php — _head.php уже выводил sidebar+topbar, но view включал их повторно напрямую, что давало дублирование <body> и вложенный main. Приведено в соответствие с шаблоном vos (заголовок = _head 1 раз, подвал = _foot 1 раз)
Added
- •Миграция 040
040_create_developer_platform.sql — одновременное создание 7 таблиц: vcs_developers + vcs_developer_earnings/payouts/activity/notes + vcs_review_queue + rzx_mp_items (IF NOT EXISTS, финальная схема после применения ALTER 030~038)
· vcs_developers на vos.21ces.com существовал только в дампе без миграции CREATE TABLE — исправляет сбой ALTER 030 на сайтах, созданных позднее
- •Синхронизация
mypage/profile-sidebar.php — запрос rzx_mkt_partners (старый) → запрос vcs_developers (новый) (интеграция активности разработчика в личном кабинете)
- •Патч маршрутов
index.php market (вручную, избегая полного sync):
· /developer/ → /mypage/developer/ редирект 301 (маппинг login/dashboard/my-items/submit/earnings/profile/logout)
· Маршруты mypage/developer/{register|items|items/new|earnings|profile} + mypage/developer/items/{N} + mypage/developer/items/{N}/edit
- •Добавлены миграция 040 + profile-sidebar.php в INCLUDES
sync-to-market.sh
- •Автоматическое резервное копирование БД market (
storage/backups/market-YYYYMMDD-HHMMSS/market_vos.sql) перед применением миграции 040
Added
- •Условия market.21ces.com следуют модели маркетплейса, управляемого третьей стороной, отдельно от условий свободного ПО VosCMS
- •Условия из 18 статей (с отражением правил работы маркетплейса):
· Ст. 8: договор купли-продажи заключается между покупателем и разработчиком; компания выступает посредником в оплате и доставке
· Ст. 9: четыре исключения возврата для цифровых продуктов (до загрузки · несоответствие описания 7 дней · подтверждение вредоносного ПО · системная ошибка) — выполняет требования раскрытия ст. 15-2 японского Закона об особых коммерческих сделках
· Ст. 10: без явного процента комиссии (ссылка на «тарифы, опубликованные отдельно»; позволяет корректировать ставки без пересмотра условий)
· Ст. 10: японское удержание 10,21% согласно ст. 204 Закона о подоходном налоге; для держателей T-номера НДС маркетплейс перечисляет напрямую в NTA; счета вне страны работы → перевод через PayPal в USD
· Ст. 14: приоритет индивидуальных условий разработчика — кроме операций платформы (оплата, расчёт, процедура возврата)
· Ст. 17: предел возмещения убытков: сумма комиссий/расчётов за предыдущие 6 месяцев
· Ст. 18: японское право + исключительная юрисдикция Районного суда Фукуоки
- •Политика конфиденциальности из 12 статей (с точки зрения трёхстороннего маркетплейса):
· Категории данных — общие / покупатели / разработчики (общие) / резиденты Японии / иностранные резиденты / доставка: 6 категорий
· Сроки хранения — участники 1 год · транзакции 7 лет · расчёты/удержания 7 лет · верификация личности 5 лет · логи 6 месяцев · запросы 3 года
· Обработчики данных — Stripe · KOMOJU · PayPal · Frankfurter · SendGrid · NTA явно указаны
· Международные передачи — США (PayPal/Stripe/SendGrid) · ЕС (обменные курсы) · другие страны (местный шлюз/банк) явно указаны
- •Скрипты:
scripts/market-terms-update.php · scripts/market-privacy-update.php (UPSERT — сначала UPDATE; INSERT если строка отсутствует)
- •БД market обновлена (terms/privacy, по 13 строк в rzx_page_contents), многоязычные заголовки и подзаголовки page-i18n также применены
Added
- •Исправление отсутствующего маппинга настроек скина (
page_title · page_subtitle) — значения, введённые на экране настроек, не отображались на странице
- •Создан отсутствующий парциал
_page-title-bg.php (устраняет предупреждение include в nginx error log)
- •Добавлена опция color в
title_bg_type + поле title_bg_color — автоматический fallback на цвет при отсутствии изображения/видео
- •Удалён fallback белого блока при
content_bg=transparent (устраняет узкий вид контента max-w-5xl внутри карточки)
- •CSS-переменная
--page-content-width генерируется автоматически — компоненты, включаемые извне, такие как панель действий «Об этой странице» (vos-community), выравниваются по той же ширине, что и content_width страницы
Fixed
- •PDOException на странице расчётов — удалена ссылка на несуществующую колонку
p.currency в rzx_mkt_partners
- •Невидимый график тенденций продаж — полосы div с высотой в % отображались почти в 0px в зависимости от среды → заменено на график на основе SVG viewBox (гарантирована минимальная видимость 4px)
Added
- •Экран детальной информации об элементе на странице разработчика (
/mypage/developer/items/{id}) — статистика, история релизов, тенденции загрузок
- •Экран редактирования элемента разработчика (
/mypage/developer/items/{id}/edit) — тот же интерфейс с 3 вкладками, что и items-new, существующие значения предзаполняются, загрузка нового ZIP-файла происходит в отдельном потоке
- •Новый API:
/api/developer/item-update — обновляет name(JSON), short/desc(JSON), tags(JSON), price, license, repo/demo URL, зависимости, минимальную версию, icon, screenshots
- •Новый API:
/api/developer/request-payout — запрос на выплату
- •Новый API:
/api/developer/update-bank — обновить собственный расчётный счёт
- •Новый API:
/api/developer/issue-reply — опубликовать ответ на Q&A (собственный канал ответов разработчика)
- •Экран детальной информации о разработчике в админке (
/theadmin/developers/show) — карточка компании/счёта/документов + режим проверки документов (одобрение/отклонение)
- •Верификация почты при регистрации и входе (
/verify-email) расширена + поле company сохранено в Auth::register
- •Добавлены официальные ключи перевода на 13 языков (
show_edit_btn, items_edit_*, items_back_detail, api_updated и др.)
Added
- •Формы отправки отзывов, issues и Q&A доступны прямо в виджетах (переход на отдельную страницу не требуется)
- •Отправлять могут только покупатели — не-покупатели видят форму, но при отправке получают уведомление
- •Ответы на Q&A поддерживают треды (ответы на ответы) — миграция БД 035 (
parent_reply_id)
- •Новые API:
/plugins/vos-market/views/api/item/post-issue.php, post-reply.php, post-review.php
- •Core
rzxlib/Core/Helpers/Notifications.php — хелпер уведомлений (в паре с миграцией БД 034)
Added
- •Полная поддержка 13 языков применена ко всем экранам:
/theadmin/market/licenses, sync, orders, settings, submissions, partners/payouts, items
- •Экран настроек маркетплейса — ярлык "Партнёр" унифицирован до "Разработчик" (только отображаемый текст; имена переменных сохранены для совместимости)
- •Ввод названия элемента маркетплейса использует общий многоязычный модал ввода —
name_json редактируется на 13 языках одновременно
- •
plugins/vos-market/lang/* — новый языковой пул на уровне плагина
- •
resources/lang/*/marketplace.php — все 13 языков переведены на родной язык (соответствует правилу полного перевода i18n)
Added
- •
033_developer_notes_activity.sql — заметки и журнал активности по каждому разработчику
- •
034_notifications.sql — таблица rzx_notifications (уведомления пользователей)
- •
035_issue_reply_thread.sql — добавлена колонка rzx_mkt_issue_replies.parent_reply_id (тредированные ответы)
Fixed
- •
/Market?item={slug} отсутствует slug — исправлено в items-show через выделенный запрос к одному столбцу
- •Имя столбца
icon_url в API обновления элемента исправлено на фактическое имя icon
- •Пустая страница настроек маркетплейса — применена отсутствующая миграция 005 для
rzx_mkt_settings
Added
- •VARCHAR(12)
users.username + UsernameValidator (не менее 6 символов, ограничение спецсимволов)
- •Модальное окно-шлюз при первом входе после авторизации (AJAX
/api/username-set)
- •Полные переводы на 13 языков (английский fallback удалён)
Added
- •
/profile/{uuid} → /story/{username} редирект 301
- •Метка контекстного меню: «Просмотреть профиль» → «Просмотреть историю»
Added
- •Плагин vos-story, выделенный из ядра — посвящён
/story/{username}
- •Шапка в стиле Facebook + 5 фиксированных вкладок (Все / Информация / Фото / Подписчики / Подписки)
- •CRUD записей + SNS-лента (
vos_story_posts, body_format = text/html)
- •Переключатель TEXT/EDIT + интеграция Summernote
- •Прикрепление изображений (несколько) + сетка вкладки Фото
- •Система лайков + комментариев
- •Система скинов Story (
skins/story/default) + токен на пользователя (/mypage/story)
- •Страница политики администратора (
/admin/vos-story/policy)
- •Панель действий страницы: i18n + позиция (конец основного контента) + токен ширины контента
- •Сетка 2:1 (только область контента) — правая сторона: реклама + друзья (вкладки Подписчики/Подписки)
- •Кнопки действий в шапке — только значки ([Настройки моей истории] / [Редактировать профиль] / [Подписаться])
Added
- •D-1: Боковая панель из 4 меню (Панель управления / История / Аккаунт / Сообщения)
- •D-2-A: Новая панель управления — 8 карточек состояния + навигация из 8 вкладок + вкладка информации об аккаунте
- •D-2-B: Вкладки Закладки / Входящие / Друзья (каждая со ссылкой «Смотреть все»)
- •D-2-C: Вкладки Сохранённое / Мои записи / Мои комментарии (доска + Story + страница объединены)
- •D-2-D: Управление автовходом (remember tokens)
Added
- •Общий помощник
rzx_mirror_external_images() — cURL + защита от SSRF + изменение размера через GD
- •E-1: Захват снапшота закладки + поглощение внешних изображений
- •E-2: Поглощение при создании записи Story (колонка source в story_attachments)
- •E-3: Поглощение при создании записи на доске
- •E-4: Поглощение при создании контента страницы
- •Маркировка изображений:
data-source-original, data-source-mirrored
Added
- •Боковая панель управления хостингом — последний элемент [Настройки], ведущий к 4 вкладкам страницы заявки на услугу (general/domain/hosting/addons)
- •Политика возврата → Системные настройки → Общие → отдельная карточка (изолировано от налоговых настроек)
- •Хранение в одном ключе
rzx_settings.refund_policy
- •Ключи
settings.refund.* для 13 языков
Added
- •Миграция 009: столбцы
page_contents.created_by / updated_by
- •Автоматически записывается на всех экранах редактирования страниц (admin/customer/system)
Added
- •Подсчёт записей — распознаёт отдельную структуру
rzx_translations для записей доски (board_post.{id}.title)
Added
- •Настройки администратора для квоты хранилища на хостинг / порога предупреждения / политики (block/notify/none)
- •Кольцевая диаграмма использования в личном кабинете
- •Фоновый монитор
scripts/db-quota-monitor.php
Added
- •Кнопка переустановки VosCMS — вкладка хостинга в панели администратора
- •Действие изменения [Статуса] хостинга + журнал
- •Форма редактирования «Настройки подключения к серверу» удалена (предотвращает ненужное раскрытие данных)
Fixed
- •Сетевая ошибка scrap
add_scrap — конфликт require файла API vos-story
- •Карточка закладки — контур + значки удаления/редактирования (корзина)
- •Несоответствие между количеством записей и списком (отсутствующий фильтр по статусу)
- •Удалено совпадение по nick_name (предотвращает ошибочную идентификацию seed-данных с user_id равным NULL)
- •Позиция меню Story в личном кабинете — top → main (автоматическое распознавание плагина)
- •Значок меню Story в формате ядра (полный тег
<path>)
- •Отсутствие столбца key в settings SELECT вкладки дополнений (два раздела скрыты)
- •Действие hosting_status 500 — место определения вспомогательной функции
- •Панель действий страницы — токен ширины контента применён
- •Ошибка, при которой существующие записи исчезают при публикации новой записи (немедленное обновление ленты)
Added
- •Детали хостинга → Вкладка «Домен» → Кнопка
+ Добавить домен → модальное окно
- •Варианты: бесплатный поддомен / новая регистрация / подключение имеющегося домена
- •Поиск WHOIS (
/api/domain-check.php повторно использован) + отображение цены по TLD
- •Оплата: сохранённая карта (повторное использование клиента PAY.JP хостинга) или новая карта (PAY.JP Elements)
- •Автоматическое разделение НДС 10% (промежуточная сумма / налог / итого)
- •При завершении оплаты — добавление в
metadata.added_domains[] суба хостинга (отдельный саб не создаётся)
- •Автоматический сеть возврата средств — при сбое обработки БД сумма PAY.JP автоматически возвращается
Added
- •Дата начала~истечения + кнопка проверки (DNS A/CNAME → совпадение с нашей зоной или диапазоном IP Cloudflare)
- •Переключатель авторедактирования + кнопка запроса продления (по домену)
- •registrar_pending / manual_attach_pending: кнопка проверки → значок «В обработке»
- •Фиолетовый значок «Добавленный домен»
Added
- •Боковая панель управления хостингом: добавлен новый 2-й пункт
- •Единый список: sub type='domain' + hosting sub.added_domains[] — всё отображается в хронологическом порядке
- •Карточки статистики (всего/в обработке/завершено) + фильтры (статус/опция/поиск)
- •Действия: [Отметить как зарегистрированное] / [Обработать отмену] (при отмене: возврат PAY.JP + INSERT строки возврата)
- •Автоматическая расшифровка имени пользователя
Added
- •Боковая панель управления хостингом: добавлен новый 3-й пункт
- •Журнал транзакций на основе
rzx_payments — карточки статистики доходов/возвратов/чистого дохода/налога
- •Автоматический расчёт суммы без НДС для японской налоговой декларации (цена с НДС → база = total × 100/110, налог = total × 10/110)
- •Таблица тенденций за последние 12 месяцев + таблица журнала транзакций + фильтры/пагинация
Added
- •Вкладка дополнений личного кабинета: раздел «Доступно для подачи заявки» (бизнес-почта исключается автоматически)
- •Действия по цене:
- •Бесплатно (поддержка установки): уведомление администратора + order_logs
- •Смета (настройка): «Запросить смету»
- •Фиксированная повторяющаяся цена (годовая техподдержка): модальное окно оплаты картой (
_addon-pay-modal.php)
- •При завершении оплаты саб дополнения создаётся автоматически + запись в payments
- •Совпадение метки уже активного саба: кнопка заявки автоматически деактивируется
- •Строка install_info: кнопки [Перейти в админ / Перейти на сайт] справа
Added
- •Схема БД: UNIQUE
uk_payment_order удалён → UNIQUE uk_payment_key (charge_id) заменён
- •Разрешено несколько строк оплаты на один заказ (хостинг + домен + дополнение накапливаются)
- •При возврате: оплаченная строка не изменяется, отдельный INSERT строки возврата (status='refund', бухгалтерская целостность)
- •Японское 7-летнее обязательство по хранению + стандарт урегулирования споров мерчанта PAY.JP выполнены
Added
- •composer require minishlink/web-push
- •Помощник отправки push-уведомлений на основе VAPID для admin/supervisor
- •Автоматическая очистка истёкших endpoint-ов + автоматическая запись в rzx_push_messages
- •Точки вызова: pay_domain (оплата домена завершена), service-order (запрос регистрации домена), request_addon
Added
- •Автоматическое совпадение поддомена нашей зоны (распознаётся wildcard CNAME)
- •Совпадение 15 CIDR-диапазонов IPv4 Cloudflare (при ответе через прокси)
- •Ответ «already_refunded» обрабатывается как успех (когда PAY.JP уже вернул средства)
Added
- •Подзапрос суммирует итоги
rzx_payments (paid − refunded)
- •Накопленное отображение базовой оплаты хостинга + добавления домена + оплаты дополнения
Added
- •
admin_cancel_addon — status саба='cancelled' + возврат через PG + INSERT строки возврата
- •Маршрутизация PG: на основе столбца
gateway строки оплаты (безопасно даже после смены PG)
Removed
- •В соответствии с японским 7-летним обязательством по хранению + политикой урегулирования споров PAY.JP все кнопки «удалить» в UI администратора удалены
- •Значок корзины + adminDeleteAddon JS + admin_delete_addon API + 4 метки на 13 языках — всё удалено
- •Только «Отменить» — строки данных никогда не удаляются (DELETE)
Fixed
- •Конфликт двоеточия-префикса в плейсхолдерах Translator —
[':count' => N] → ['count' => N] исправлено массово
- •
metadata[refund_reason] в PayjpGateway::refund() отклонён PAY.JP → заменено на refund_reason
- •Проверка прав администратора в service-manage.php —
role === 'admin' → role IN ('admin','supervisor') исправлено для 5 действий
- •Вкладка домена заказа услуги: после возврата карточка учёта показывает отрицательное число → логика SQL исправлена (раздельное суммирование status='paid' / 'refund')
Added
- •ssh.21ces.com Cloudflare Tunnel зарегистрирован (Public Hostname: SSH → localhost:22)
- •Внешний ПК:
cloudflared access ssh --hostname ssh.21ces.com или ProxyCommand в ~/.ssh/config
- •Сторона сервера: PermitRootLogin no, ключ thevos зарегистрирован
Added
HostingProvisioner.php installVoscms() теперь последовательно вызывает install-core.php step 3 → step 4 после zip-развёртывания (ранее только step 2):
- •Новый
runInstallCoreSteps() — обход сессии через POST-фолбек _db + проверка маркера .installed
- •Новый
callInstallCore() — HTTP loopback (порт 80, с использованием SSL-edge Cloudflare; обработка отсутствия origin-сертификата при rate limit)
- •Новый
appendHostingEnv() — повторное внедрение переменных HOSTING_* после того, как install-core.php перезаписал .env
- •Автоматическое удаление welcome.html после успешной установки (чтобы index.php VosCMS был приоритетным)
- •Платёжный поток service-order.php:567-576 — при успешной установке автоматически записываются
install_completed_at и install_admin_url в метаданные addon (триггер отображения вкладки в личном кабинете)
Added
admin/service-orders/detail.php:175-260 + partials/addon.php:
- •В аддоне «Поддержка установки» вкладки дополнительных услуг теперь две кнопки:
- •Установить VosCMS (фиолетовая) — отображается, если
install_info есть, а install_completed_at отсутствует
- •Страница администратора (зелёная) — открывает URL администратора в новой вкладке после установки
- •API-действие
admin_run_voscms_install — прямой вызов HostingProvisioner.installVoscms(), фолбек учётных данных БД из metadata.server.db или .env клиента
- •При успехе: запись метаданных addon + запись
voscms_installed в order_log
Added
customer/mypage/service-partials/hosting.php:
- •🟢 Кнопка Резервное копирование сайта —
service-manage.php?action=request_backup (новая)
- •mysqldump → дамп БД + zip-упаковка (public_html, .env, sql)
- •Подписанный URL для скачивания (HMAC, истекает через 10 минут) — api/backup-download.php новый
- •Автоматическая очистка резервных копий старше 7 дней
- •🟣 Кнопка Открыть phpMyAdmin —
https://pma.voscms.com/?lang=<user_locale> в новой вкладке
- •vhost nginx: pma.voscms.com.conf новый (автоматическая маршрутизация через wildcard Cloudflare Tunnel
*.voscms.com)
- •Вход с собственными учётными данными БД пользователя
- •Отображение информации о подключении к БД (хост / имя БД / пользователь / пароль с переключателем)
- •i18n на 13 языков:
btn_site_backup, btn_open_phpmyadmin, confirm_site_backup, backup_in_progress, backup_ready, f_password, password_notice, f_protocol
Added
customer/mypage/service-partials/_setup-monitor.php:
- •Разделение этапов: настройка хостинга → автоматическая установка VosCMS
- •Заказы
system_imported (хостинг, зарегистрированный самим администратором, напр. vos.21ces.com) активируются немедленно — мониторинговый блок не отображается
- •Новый i18n на 13 языков:
setup_step_voscms_install
Changed
Дополнения к rsync exclude:
- •5 внутренних плагинов (не должны попадать на сайты клиентов хостинга):
- •
vos-developer, vos-hosting, vos-license-server, vos-market, vos-salon
- •7 миграций SQL для reservation (клиенты хостинга не устанавливают систему бронирования):
- •
001_create_reservation_tables.sql, 011_seed_service_translations.sql, 012_seed_service_translations_p2.sql, 013_add_source_to_reservations.sql, 014_add_payment_status_to_reservations.sql, 015_create_reservation_core.sql (новый), 016_alter_reservation_services.sql, 024_reservations_bundle_columns.sql
Дополнения к удаляемым dev-файлам:
- •Директория
admin/ (остаток offline.html) — пустая директория вызывает 403 из-за directory indexing nginx; только root /offline.html используется sw.js
- •Базовое кодирование →
obfuscate.php (старый ionCube EVAL вызывал зависание при исполнении; ionCube только при USE_IONCUBE=1)
Changed
Разделение структуры, при которой 001_create_base_tables.sql создавал и базовые, и reservation-таблицы в одном файле:
- •Изменён:
001_create_base_tables.sql — только 5 ключевых таблиц (rzx_admins, rzx_users, rzx_member_grades, rzx_settings, rzx_migrations) + базовые seeds (member_grades по умолчанию, settings по умолчанию)
- •Новый:
015_create_reservation_core.sql — 9 таблиц бронирования (rzx_categories, rzx_services, rzx_service_options, rzx_reservations, rzx_payments, rzx_point_transactions, rzx_business_hours, rzx_holidays, rzx_staff_positions, rzx_staff) + seeds бронирования (business_hours, point_*)
При сборке дистрибутива хостинга 015 исключается → rzx_reservations не создаётся → $hasReservations = false в admin/dashboard.php:25-27 → календарь автоматически не отображается.
Сайты, использующие систему бронирования (voscms.com, RezlyX и др.), применяют оба SQL-файла → изменений нет.
Changed
config/templates/nginx-vhost.conf.tpl + HostingProvisioner addHttpsToVhost():
- •Ранее: порт 80 (редирект HTTPS) + порт 443 (реальный контент) → бесконечная петля редиректа с Cloudflare Tunnel + редиректом 80→443
- •Теперь: единый объединённый серверный блок 80/443, одинаковый контент, редиректов нет
- •
fastcgi_read_timeout 60s → 300s (предотвращает таймаут nginx при массовых INSERT)
Changed
- •
provision-hosting.php (CLI-инструмент для бэкфилла): автоматическое обновление метаданных подписки хостинга (hosting_provisioned, server.{ftp,db,env}) — ранее выполнялся только _autoProvisionHosting из service-order.php
- •Автоматическое удаление 5 внутренних плагинов + директории admin/
Added
Новый scripts/install-cli.php:
- •Бэкфилл автоматического вызова install-core.php после provisioning хостинга
- •Учётные данные БД: приоритет — метаданные subscription, фолбек —
.env docroot клиента
- •Использование:
sudo -u www-data php scripts/install-cli.php --order=<ORDER>
- •При успехе: автоматическая запись
install_completed_at в метаданные addon + действие voscms_installed в order_logs
Fixed
- •Отсутствующий вызов
_autoProvisionHosting() в ветке оплаты картой — добавлен
- •
install_info.admin_pw в метаданных оплаты отображался открытым текстом → унификация паттерна toggle (👁) на стороне личного кабинета и администратора
Migration Notes
- •Внешние клиенты хостинга: дополнительных действий не требуется. Сборка
voscms-2.4.3.zip использует чистый zip.
- •Основной сайт (voscms.com): изменений нет (оба SQL-файла применяются).
- •Существующие клиенты хостинга (dwhahn и др.): рекомендуется deprovision + reprovision для чистой переустановки (применить новый zip после разделения).
- •Ручная очистка:
/etc/nginx/sites-available/pma.voscms.com.conf добавлен — деплоить напрямую на основную систему (вне rsync).
Added
Вызывается сразу после завершения оплаты в service-order.php:
- •Новая функция
_autoProvisionHosting($pdo, $prefix, $orderId, $orderNumber) — выполняется вне транзакции оплаты (целостность БД изолирована; заказ не затрагивается при сбое)
- •Только
domain_option = 'free' настраивается немедленно (поддомен Cloudflare — DNS-авторегистрация уже завершена)
- •
new (ожидание смены NS после покупки домена) / existing (ожидание смены NS существующего домена) — в режиме deferred — администратор запускает отдельно в момент активации домена
- •Если в аддоне 'install' есть
install_info, файлы voscms деплоятся + .env создаётся автоматически (автоматизация пошагового сессионного потока install-core.php (шаги 2–5) требует полного переосмысления установочного пайплайна voscms — отложено на Фазу 5; после оплаты пользователь открывает https://<домен>/install.php → проходит мастер настройки с предзаполненными в .env данными БД)
- •Данные ftp/db/env автоматически сохраняются в
metadata.server подписки на хостинг → автоматически отображаются в вкладке хостинга в личном кабинете (UI изменений не требуется)
- •Действия order_logs:
hosting_provisioned / hosting_provision_skipped / hosting_provision_deferred / hosting_provision_failed
Порядок вызова: commit оплаты → _autoProvisionHosting() → _autoProvisionMailDomain()
Added
$actionLabels на странице детального просмотра сервисного заказа администратора (detail.php:498-528) расширены с 8 до 31 записи. Исправляет отображение сырых кодов действий (например, mail_account_added) в журнале активности.
Новые сопоставления действий:
- •Почта:
mail_account_added/deleted, mail_address_changed, mail_provisioned/_skipped/_failed, mail_domain_migrated/activated, bizmail_upgrade_request
- •Хостинг:
hosting_provisioned, hosting_provision_skipped/deferred/failed, hosting_deprovisioned
- •Дополнения:
admin_add_storage_addon, admin_delete_addon, storage_addon_paid/requested
- •Система:
customer_notified, setup_email_sent, server_info_updated, bulk_extended, manual_capacity_period_fix
Цветовая семантика действий: green (завершено/положительно), amber (ожидание/предупреждение), red (ошибка/удаление), blue (информация/уведомление), violet (admin/особое), zinc (нейтральный).
23 i18n-ключа — services.admin_orders.act_* — добавлены для ko/ja/en (страница оператора). Остальные 10 языков используют английский как fallback.
Changed
При автопровизионинге хостинга автоустановка VosCMS выполняет только деплой файлов + автосоздание .env. Автоматизация пошагового сессионного потока install-core.php (шаги 2–5) требует полного переосмысления установочного пайплайна — отложено на Фазу 5. После оплаты пользователь открывает https://<домен>/install.php → проходит мастер настройки (данные БД предзаполнены в .env).
Возвращаемое значение installVoscms() изменено — теперь возвращает install_url + сообщение note.
Added
HostingProvisioner.php — Основной класс для автоматической настройки при заказе хостинга. Транзакция из 9 шагов (откат в обратном порядке при сбое):
- •Создание пользователя Linux (
vos_<order_number>, в пределах 32 символов, с сохранением регистра)
- •Структура директорий:
/var/www/customers/<order>/{public_html,logs,tmp} (chmod 750/2775/750/1770)
- •Дисковая квота (
setquota -u <user> <soft> <hard>, hard = soft × 1.1)
- •Проверка конфликта директорий: отказ при конфликте с существующим nginx vhost /
reserved_subdomains
- •Монтирование 1,9 ТБ SSD
/var/www (ext4 + noatime + usrquota,grpquota)
- •Центральный sudoers (
/etc/sudoers.d/voscms-hosting-provisioner) — www-data выполняет root-команды (useradd/setquota/nginx и т.д.) без пароля (NOPASSWD)
- •Ограничение шаблона аргументов на команду (
vos_, /var/www/customers/, *.conf) — проходит проверку visudo
CLI-инструмент scripts/provision-hosting.php:
php provision-hosting.php SVC260428AE7476 hotel.com 1GB
php provision-hosting.php --order SVC260428AE7476 # автоматический запрос к БД
php provision-hosting.php --deprovision SVC260428AE7476 hotel.com
php provision-hosting.php --install-voscms <order> <domain> <capacity>
Шаблоны config/templates/:
- •
nginx-vhost.conf.tpl — HTTP + расположение ACME-challenge + FPM-сокет на пользователя
- •
welcome.html.tpl — приветственная страница активации
Added
Каждый хостинг запускает PHP под своим пользователем (предотвращает межсайтовые атаки) + SSL выдаётся автоматически даже за Cloudflare-прокси.
config/templates/php-fpm-pool.conf.tpl:
- •
user = vos_<order> / group = www-data — изоляция по UID
- •
listen = /var/run/php/<order>.sock — сокет на пользователя
- •
pm = ondemand — экономия памяти для хостингов с малым трафиком
- •
php_admin_value[open_basedir] — доступ ограничен директорией сайта + /tmp + /usr/share/php
- •
disable_functions — exec, passthru, shell_exec, system, proc_open, popen, pcntl_exec
- •
session.save_path / upload_tmp_dir — изолированы в директорию пользователя
- •env: HOSTING_ORDER / HOSTING_USER / HOSTING_DOMAIN
SSL Let's Encrypt — Cloudflare DNS-01 challenge:
- •Плагин
python3-certbot-dns-cloudflare
- •
/etc/letsencrypt/cloudflare/credentials.ini (root 0600, CLOUDFLARE_API_TOKEN из .env)
- •Выдаётся независимо от Cloudflare-прокси / внешней доступности
- •Серверный блок HTTPS добавляется автоматически (TLS 1.2/1.3, сильный шифр, HSTS, X-Frame-Options, X-Content-Type-Options)
- •Автоматический редирект HTTP → HTTPS 301
- •
certbot.timer обновляется автоматически дважды в день (уже активен)
Added
Изоляция БД + пользователя на каждый заказ хостинга.
- •Аккаунт MySQL
hosting_admin (создаётся однократно, ALL PRIVILEGES + GRANT OPTION) — только для управления БД
- •Имя БД/пользователя =
vos_<order> (совпадает с именем Linux-пользователя)
- •Пароль: 32 hex случайный (
bin2hex(random_bytes(16)))
- •
GRANT ALL PRIVILEGES ON vos_<order>.* TO vos_<order>@localhost — доступ только к собственной БД
- •Проверка изоляции: обращение к другой БД хостинга / voscms_prod возвращает Access denied 1044
Автоустановка VosCMS (installVoscms() — запускается при --install-voscms или наличии install_info в addon):
- •Распаковывает последний
voscms-dist/voscms-X.Y.Z.zip → public_html
- •Автоматически генерирует
.env: APP_KEY=base64:<32 байта> / данные БД / HOSTING_ORDER внедрён
- •Права: vos_:www-data, .env chmod 640
- •Безголовый вызов
install-core.php (текущая проблема с 301-редиректом, будет решена в Фазе 4)
Verified
provision DEMO260429FULL demo07.21ces.com 1GB --install-voscms:
- •✅ Пользователь
vos_DEMO260429FULL (изоляция UID, группа www-data)
- •✅ Дисковая квота 1 ГБ soft / 1,05 ГБ hard
- •✅ PHP-FPM pool собственный сокет
- •✅ nginx vhost (HTTP + HTTPS, HSTS)
- •✅ SSL Let's Encrypt (
/etc/letsencrypt/live/demo07.21ces.com/)
- •✅ MySQL БД
vos_DEMO260429FULL + пользователь (изоляция подтверждена)
- •✅ Файлы VosCMS 2.3.9 + .env развёрнуты
deprovision: Все ресурсы удалены чисто (пользователь/home/БД/FPM pool/nginx/SSL).
Added
Вместо отраслевого стандарта «anniversary» (от даты регистрации до той же даты год спустя) тарификация переведена на календарное закрытие конца месяца (с 1-го числа следующего месяца по последний день месяца истечения). Пользователи, зарегистрировавшиеся в одном месяце, имеют одинаковую дату истечения, что упрощает пакетную обработку cron и закрытие бухгалтерии. Следует японскому стандарту хостинга (月末締め翌月1日請求).
- •БД: добавлен столбец
rzx_subscriptions.billing_start DATETIME NULL (дата начала регулярной тарификации)
- •Помощники:
_proratedFirstAmount (месячная единица ÷ 30 × оставшиеся дни), _calendarBillingStart (1-е следующего месяца), _calendarExpires (последний день 23:59:59)
- •Форма заказа:
hosting_prorated автоматически добавляется к позициям оплаты хостинга (напр., вступление 2/4, 12 мес. ¥1 000/мес. → первый месяц ¥967 + 12 обычных месяцев ¥12 000 = ¥12 967)
- •Домены сохраняют тарификацию «anniversary» (цикл регистрации 1 год); хостинг, почта, обслуживание и надстройки используют календарную тарификацию
- •Обработка
-1 second → -1 day в service-order.php также унифицирована
- •Дополнительные услуги (увеличение веб-пространства) также используют ту же пропорциональную оплату + сумму обычных N месяцев, автоматически синхронизированную с датой истечения хостинга
Added
Настройка Postfix + Dovecot + Rspamd + Roundcube + OpenDKIM + Let's Encrypt на ConoHa VPS (133.117.72.149). mail-tester 10/10, FCRDNS проверен. Автоматическая настройка домена при оформлении заказа.
- •Автоматическая регистрация DNS (
CloudflareDns.php) — A / MX / SPF / DKIM / DMARC 5 записей за раз
- •Генерация ключа DKIM — mx1 SSH
rspamadm dkim_keygen (выделенный ключ ed25519 storage/keys/mailsync_ed25519, владелец www-data, 0700)
- •3-режимный провижнинг (
MailDomainProvisioner.php): free (поддомен мгновенно) / new (домен куплен, активация администратором) / existing (инструкции по смене NS). Машина состояний: existing_pending / new_pending / active
- •Пароль: SHA512-CRYPT (
{SHA512-CRYPT}$6$...) — совместимый с Dovecot односторонний хеш. Скрипт миграции для старых паролей enc: (функция encrypt): scripts/migrate-mail-passwords.php
- •Cron синхронизации с mx1 (
scripts/mail-sync-to-mx1.php) — voscms_prod → mx1 mail_lookup virtual_users каждые 5 минут. Немедленный триггер (triggerMailSyncToMx1()) для фоновой синхронизации сразу после почтовых действий
- •Автоматическая регистрация cron:
/etc/cron.d/voscms-mail-sync
- •Уведомления: HTML-шаблоны почты для администратора (требуется провижнинг) / клиента (почта готова) (
MailNotifier.php)
Added
Управление базовой почтой / бизнес-почтой в связке с хостингом. Исправлена проблема двойного отображения, когда один и тот же адрес хранился и в подписке на почту, и в метаданных хостинга (единый источник истины: почтовая подписка).
- •Кнопка веб-почты — GET-параметр
?_user=адрес Roundcube для предзаполнения имени пользователя, target="_blank". Приоритет URL: metadata.mail_server.webmail_url → env WEBMAIL_URL → по умолчанию https://mail.voscms.com/
- •Кнопка смены email — для исправления опечаток. Домен фиксирован; меняется только локальная часть. Синхронизируются все метаданные mail + hosting одного заказа
- •Логика дедупликации (
mail.php) — _seenAddresses выводит каждый адрес только один раз, приоритет у подписки на почту
- •Преобразование virtual hosting → mail в service-detail.php выполняется только при отсутствии реальной почтовой подписки (обратная совместимость)
- •service-order.php больше не сохраняет hosting metadata.mail_accounts при новой регистрации (почтовая подписка — единственный источник)
Added
Покупка дополнительного хранилища с оплатой во вкладке дополнительных услуг личного кабинета. Регистрация клиента PAY.JP + chargeCustomer.
- •Модальное окно: выбор ёмкости (1 ГБ / 3 ГБ / 5 ГБ) + ввод данных карты (payjp.js v2 Elements)
- •Зарегистрировать карту / оплатить другой картой / вернуться к зарегистрированной карте — переключение
- •Автоматическое определение истёкшей/отклонённой карты (
expired_card, card_declined, invalid_* и другие коды ошибок PAY.JP) → форма карты открывается автоматически + информационный баннер
- •Добавлено поле ввода имени владельца карты (
addon-card-holder) — сохраняется как метаданные клиента, не передаётся в токен (PAY.JP v2 Elements не поддерживает опцию name)
- •Паттерн Portal:
document.body.appendChild() для выхода из контейнеров с transform и перекрытия всего viewport (z-[9999])
- •При неудачном списании после создания нового клиента: автоматический возврат + удаление клиента (предотвращение «сирот»)
- •hosting metadata.extra_storage[] накапливается; шкала хранилища во вкладке хостинга показывает суммарный объём
Added
Структурированы 14 статей (определения, заявление, тарифы, автоматическое продление, расторжение, сервис, резервное копирование, запрещённые действия, приостановка, техобслуживание, конфиденциальность, ответственность, изменения, юрисдикция) с опорой на стандартные условия японских хостинг-провайдеров, таких как StarServer. Указан порядок календарной тарификации + пропорциональный расчёт за первый месяц + возврат средств за неиспользованные месяцы при расторжении.
- •Регистрация системной страницы:
config/system-pages.php slug hosting-terms, 🖥️ индиго
- •Маппинг редактора страниц:
pages-document.php — pageMetaMap['hosting-terms']
- •Заполнение БД: ja (основной) / ko / en — 3 языка (rzx_page_contents)
- •i18n:
site.pages.hosting_terms + hosting_terms_edit (editor_title/desc)
Added
- •4 вкладки всегда видимы (detail.php) — hosting/domain/mail/addon. При отсутствии данных отображается сообщение о пустом состоянии (empty_*)
- •Вкладка дополнительных услуг — кнопка
+ Добавить хранилище (модальное окно бесплатного предоставления администратором; выбор ёмкости мгновенно создаёт активную addon subscription, billing_amount=0, expires=дата истечения хостинга) + кнопка корзины/удаления для каждого элемента (мягкое удаление status=cancelled, удаление из hosting metadata.extra_storage[])
- •Список — улучшенное отображение заявителя (index.php) — заказы системных администраторов с ролью
supervisor/admin отображаются как user.name + фиолетовый подзаголовок Суперадмин: [email protected] (обходит ошибку расшифровки устаревшего зашифрованного applicant_name вида FKzGx9SE7xG/2)
- •Кнопка создания заказа (
+ New Order) — только интерфейс, функциональность будет добавлена позже
- •i18n 13 языков:
role_supervisor, role_admin, btn_create_order, empty_, btn_add_storage_addon, btn_delete_addon, modal_add_storage_, confirm_, alert_ и более 20 ключей
Added
- •
subdomain-check.php — приоритет БД (reserved_subdomains) + запасной вариант через API Cloudflare. JS checkSubdomain() обращается к серверу и отражает результат
- •Серверная повторная валидация в service-order.php непосредственно перед оплатой — предотвращает обход на стороне клиента
- •Автоматический импорт
reserved_subdomains — 11 существующих записей из зоны Cloudflare (vos/bestatech/bt/dev/ec430/ec431/salon/ssh/test/thevos/ai.21ces.com)
- •Исправление бага INSERT IGNORE в меню (menus-api.php):
- •
add_menu_item: очистить устаревшие переводы сразу после INSERT + перезаписать через INSERT ... ON DUPLICATE KEY UPDATE
- •
update_menu_item: обнаружить изменения title/url, удалить все 13 старых переводов + повторно зарегистрировать, если системная страница
- •
rename_menu_item: очистить старые переводы при изменении заголовка
- •Исправлена регрессия, при которой переводы старого меню (id=38, «Введение в VosCMS») указывали на новое меню условий хостинга
Added
Построен полный цикл: заказ услуги → завершение оплаты → автоматический провижнинг почтового домена → уведомление клиента.
- •В service-order.php добавлен вызов
_autoProvisionMailDomain() — выполняется вне транзакции оплаты (изоляция целостности БД)
- •БД:
rzx_reserved_subdomains (zone, subdomain, record_type, reserved_by, reason) — кеш доступности
- •Ключ DKIM генерируется автоматически, сохраняется в metadata.mail_provision как mode/origin/domain/zone/subdomain/provisioned_at
- •Ветвление UI по режиму провижнинга (service-detail.php):
pending / new_pending / existing_pending — вкладки скрыты, отображается только мониторинг (_setup-monitor.php); вкладки видны только при состоянии active
Fixed
$arr['key'] ?? [] в PHP возвращает временное значение, поэтому &$ref не влияет на исходный массив. change_mail_password / change_mail_address возвращали успешные ответы, но UPDATE метаданных не давал эффекта — изменения пароля и адреса молча не применялись.
- •Переход на шаблон прямого обновления по индексу:
foreach ($arr as $i => $item) { $arr[$i]['k'] = ... }
- •Сохранение нового почтового пароля в service-order.php также изменено с
encrypt() (двусторонний) на mail_password_hash() (SHA512-CRYPT)
Fixed
- •Яркий флуоресцентный зелёный в сводке оплаты → более сдержанный цвет:
text-green-400 → text-green-600 dark:text-green-400 (7 мест)
- •Отсутствующий ключ i18n в сообщении о доступности поддомена (
order.php маппинг PHP→JS i18n + унификация заполнителя :fqdn)
- •Уточнён расчёт даты истечения:
+N months -1 second (тот же день) → +N months -1 day (ровно на 1 день раньше) → окончательно унифицировано на календарный метод
Database
- •Добавлен
rzx_subscriptions.billing_start DATETIME NULL (dev + prod)
- •Таблица
rzx_reserved_subdomains (кеш коллизий зоны Cloudflare)
- •
rzx_page_contents заполнен данными hosting-terms (ja/ko/en)
Added
Весь текст в 6 областях (домен, хостинг, дополнения, заявитель, оплата, резюме) и на странице завершения оплаты переведён на 13 языков. Текст алертов/модальных окон на стороне клиента внедряется из PHP по паттерну var I18N = {...} + t(key, replace).
- •Расположение: plugins/vos-hosting/lang/{13 locales}/services.php —
services.order.* более 200 ключей
- •Детали услуги на Моей странице:
services.detail.* более 70 ключей, service-detail.php
- •Управление заказами в админке:
services.admin_orders.* более 115 ключей, admin/service-orders/
- •Корейские метки, хранящиеся в БД (тарифные планы хостинга, обслуживание, дополнения), маппятся по ID настройки через closure
_localizeLabel($sub) и применяется db_trans() → единая обработка на Моей странице и в админке
Added
При нажатии кнопки оплаты детали заказа (домен/план/дополнения/заявитель/условия) проверяются и отображается сводный модал → пользователь подтверждает → оплата продолжается. Предотвращает ошибки, например регистрацию пустого поддомена.
- •order.js —
showOrderConfirmModal(), closeOrderConfirmModal(), confirmAndPay()
- •Улучшено удаление оставшихся слоёв модала после завершения оплаты (удаление DOM + сброс overflow body/documentElement)
Added
При отметке "Поддержка установки" (_id='install') в дополнениях появляется форма для ввода ID администратора/e-mail/пароля/имени сайта. Пароль скрыт + переключатель иконки глаза + уведомление "изменить после установки". Значения хранятся как install_info в метаданных подписки и отображаются на Моей странице и в админке.
- •Ввод: _addons.php
- •Отображение: Моя страница/admin partials/addon.php (блок с синим фоном + переключатель пароля)
Added
Заметка, привязанная к вкладке дополнений, перенесена на уровень заказа (rzx_orders.admin_notes). Всегда отображается в виде текстового поля из 7 строк над журналом активности справа, независимо от наличия дополнений. Добавлено действие AJAX update_order_memo.
Added
Введено поле section (top/main/bottom, по умолчанию main) для визуального разделения основных меню и меню плагинов. Между зонами автоматически вставляется разделитель; если в зоне нет отображаемых меню, вся зона включая разделитель пропускается.
Боковая панель администратора:
- •top: Панель управления / Управление пользователями / Управление сайтом / Автоустановщик (
vos-autoinstall)
- •main: Лицензия VosCMS (
vos-license-manager) / Управление хостингом (vos-hosting)
- •bottom: Плагины / Настройки
- •Изменения: config/admin-menu.php, admin-sidebar.php
Боковая панель Моей страницы:
- •top: Панель управления / Профиль / Сообщения
- •main: Услуги (
vos-hosting добавляется автоматически) — накапливается автоматически по мере добавления записей другими плагинами
- •bottom: Настройки / Пароль / Удаление аккаунта / Выход
- •Изменения: config/mypage-menu.php, mypage-sidebar.php
Регистрация меню плагина: поля section + position в записях menus.admin / menus.mypage файла plugin.json задают зону. Меню services перенесено из core mypage-menu.php → vos-hosting/plugin.json (автоматическая регистрация).
Added
API валидации лицензии и API портала разработчиков находились в ядре, что означало их раскрытие на всех развёрнутых сайтах voscms. Оба API вынесены в новый плагин, предназначенный только для головного офиса.
- •
vos-license-server (scope: hq_only): перенесено 7 лицензионных эндпоинтов (check, register, verify, stats, updates, register-plugin, _init)
- •
vos-developer (scope: hq_only): перенесено 7 эндпоинтов портала разработчиков (register, login, submit, my-items, earnings, update-version, _init)
- •
api/_plugin_dispatch.php — обёртка ядра проверяет rzx_plugins.is_active перед диспетчеризацией; возвращает 404 если неактивен
- •Ядро
/api/license/.php и /api/developer/.php сохраняет только обёртку (внешняя совместимость сохранена)
- •
/api/notices.php с env-вайтлистом HQ_HOSTS добавлен (отвечает только с домена HQ voscms.com)
- •Скрипт сборки (
build-voscms.sh) уже исключает api/license, api/developer, api/notices.php и vos-license-manager
Fixed
- •Баг +1 день при истечении срока действия хостинг-контракта (
+12 months → +12 months -1 second) — применено в 4 местах: subscription/free/paid/bank
- •Корпоративная почта: нельзя добавить 2 и более (селектор захватывал кнопку X)
- •Сводка заказа: дополнительное хранилище не агрегировалось (отсутствует атрибут
name у select хранилища)
- •Метка тарифного плана хостинга не локализована — решено сопоставлением
service_hosting_plans._id с db_trans()
Added
_init.php теперь напрямую запрашивает rzx_translations для board.{id}.{title|description|header_content|footer_content|seo_keywords|seo_description} (текущая локаль → en → источник). На любой странице — list/read/write/settings — $board[$field] автоматически заполняется переведённым значением. Кнопка «← Назад» на странице настроек форума также автоматически переводится через тот же механизм.
Сид на 13 языков для title + header_content форума обращений (id=6) — HTML-структура сохранена, переводится только текст, значки этапов обработки также на каждом языке. Скрипты сида: scripts/seed_issue_i18n.php + scripts/seed_issue_i18n_data.php.
Added
Перевод определений (метка/описание/варианты/значение по умолчанию) rzx_board_extra_vars на 13 языков. 4 поля × 13 = 52 строки.
- •
ExtraVarRenderer::renderAll($vars, $values, $mode, $boardId) — добавлен аргумент boardId, applyDefTranslations() загружает все переводы переменных одним запросом.
- •
ExtraVarRenderer::getOptionLabel($boardId, $varName, $value) — поиск переведённой метки одного варианта для представлений списка (статический кэш).
- •
renderStatusBadge($value, $size, $displayLabel = null) — цвет использует сопоставление источника (ko), переводится только текст → цвет одинаков во всех языках (получено = розовый, решено = изумрудный).
- •select/radio/checkbox: шаблон
value=источник / label=перевод. При сохранении публикации всегда сохраняется значение источника, обеспечивая совместимость данных.
- •Обновления вызывающего кода:
read.php, write.php, _list-row.php, _list-card/_list-webzine/_list-gallery скина по умолчанию.
Сид на 13 языков для дополнительной переменной status форума обращений: scripts/seed_ev_status_i18n.php.
Added
API extra_var_get возвращает переведённые значения для текущей локали в виде объекта localized через db_trans(). JS-модал отображает localized.title || ev.title с приоритетом → в японском режиме при открытии модала автоматически показывается «処理ステージ».
extra_var_update ветвится по локали:
- •Исходная локаль (ko) → обновить всю исходную строку (существующее поведение)
- •Неисходная локаль (ja/en/…) → в источник сохраняются только мета (
var_type/права/3 флажка); 4 поля перевода (title/description/options/default_value) сохраняются в rzx_translations под текущей локалью → целостность источника гарантирована
Отображаемые имена в списке дополнительных переменных также используют db_trans('board_ev.{boardId}.{varName}.title').
Added
Раздел «Права на редактирование» (метка/варианты выпадающего списка/подсказка) модала редактирования дополнительных переменных вынесен в языковые файлы на 13 языков. Новые ключи: site.boards.ev_permission, ev_perm_all, ev_perm_member, ev_perm_admin, ev_permission_help.
Added
Двунаправленная нормализация поля «Варианты» в модале редактирования дополнительных переменных:
- •Загрузка модала: конвертирует JSON-массив из БД (
["a","b","c"]) в формат переноса строк для отображения в textarea (в соответствии с инструкциями)
- •API сохранения: хелпер
ev_normalize_options() принимает ввод с переносом строк или JSON и последовательно сохраняет как JSON-массив. Исправлен баг, при котором ввод с переносом строк ранее сохранялся как NULL.
Added
Два новых скина для паттернов контента помимо обращений/объявлений.
FAQ (skins/board/faq/) — публикация = вопрос, тело = ответ. Включает строку поиска, фильтр категорий и пагинацию. Переключение отображения области заголовка, многоязычный заголовок/подзаголовок, поддержка фона (изображение/видео/нет), настройка прозрачности наложения. v1.0.1.
Q&A (skins/board/qna/) — публикация = вопрос (заголовок+тело), комментарий = ответ. Значок «без ответа», первый элемент раскрыт автоматически, отображение комментариев с сортировкой по дереву (parent_id/depth). Ответы на ответы отображаются с отступом + меткой «↳ Ответить» (верхний уровень = «А.»). Встроенная форма редактирования/ответа (редактировать может только автор/администратор, отвечать — только с правом perm_comment). v1.0.0.
Общее для обоих скинов: значок шестерёнки настроек администратора размещён внизу справа от строки поиска (отделён от области заголовка). boardSkinFile() работает с приоритетом _list-full.php.
Added
В api-comments.php добавлено действие update. Редактировать может только comment.user_id == текущий пользователь или администратор. После валидации количества символов обновляет content + updated_at.
Added
Токен {year} в поле copyright настроек макета автоматически заменяется текущим годом при рендеринге. БД сохраняет оригинал (с {year}), поэтому ручное обновление никогда не требуется. Применено к: default/minimal/modern/base-footer.php. Описание copyright в 4 файлах layout.json расширено на 13 языков с инструкциями по использованию {year} на каждом родном языке.
Также исправлен баг, при котором $_lc['copyright'] игнорировался в подвалах default/minimal — теперь значение настройки макета действительно рендерится.
Added
Сид на 13 языков для menu_item.38.title (О VosCMS / About VosCMS / VosCMSについて / Über VosCMS / Acerca de VosCMS / À propos de VosCMS / Tentang VosCMS / VosCMS-ийн тухай / О VosCMS / VosCMS Hakkında / Giới thiệu VosCMS / 关于 VosCMS / 關於 VosCMS).
Added
По одному примеру на каждый этап обработки + 1 существующий решённый = итого 4. Перевод заголовка/содержания каждой публикации на 13 языков (104 строки).
| Этап | Заголовок (ko) | Статус |
|---|
| Получено | [Баг] Кнопка X мобильного меню-гамбургера не работает | Новое |
| На проверке | [Запрос функции] Уведомление по e-mail автору публикации при написании комментария | Новое |
| В работе | [Производительность] Улучшить скорость первоначальной загрузки страницы детала маркетплейса | Новое |
| Решено | Прикреплённое в публикации форума изображение вставлено в тело... | Существующее |
Удалена существующая публикация 34 (тестовая публикация с накопленными инлайн-стилями Tailwind).
Fixed
customer/board/list.php выполнял require хелпера rzx_admin_icons на строке 167, но include скин-файла на строке 161 выходил через return — вызывая ошибку 500 при вызове rzx_admin_icons() в скинах FAQ/Q&A. require_once перемещён выше include скина.
Changed
Удалены оставшиеся ссылки на старое название /marketplace. Исправлен баг, при котором форма поиска отправляла GET на /marketplace и вызывала перенаправление на дашборд (уход со страницы при изменении поиска/сортировки/фильтров). Все файлы browse.php, item-detail.php, item-submit.php, purchases.php и assets/js/marketplace.js переведены на /autoinstall.
Added
В Шаге 4 install.php под полем ввода пути администратора добавлена жёлтая подсказка, предварительный просмотр URL в реальном времени и раскрывающийся гид «Почему стоит изменить это?». Три причины на понятном языке: автоматические попытки взлома (перебор), сканирование уязвимостей и ограничения сильных паролей. Применено для всех 13 языков.
Added
На этапе завершения установки в install.php плагин vos-autoinstall автоматически регистрируется и активируется (прямой вызов PluginManager::install()). Пользователи могут сразу использовать маркетплейс после входа в панель администратора без отдельной установки.
Added
Добавлена поддержка параметра ?locale=xx для market.21ces.com/api/market/catalog и /item. Многоязычный JSON для name, description, short_description, cat_name возвращается сервером как единая строка (резервный вариант: запрошенный locale → en → первое значение). Вся дублирующая логика json_decode + locale fallback на стороне VosCMS удалена (browse.php, item-detail.php, _components/item-card.php).
Added
TTL файлового кэша ответов API маркетплейса интегрирован в настройки PluginManager. Жёстко заданные 30 минут → по умолчанию 5 минут, опции 1/5/10/30/60 минут. Раздел кэша маркетплейса в https://vos.21ces.com/theadmin/settings/general и /theadmin/autoinstall/settings читают и пишут одну и ту же настройку с автоматической синхронизацией. Кнопка немедленного обновления кэша добавлена на страницу AutoInstall и страницу общих настроек. При изменении TTL существующий кэш автоматически инвалидируется.
Added
Сразу после заголовка отображается та же таблица, что и в маркетплейс-администраторе (тип данных / краткое описание / slug / Product Key / минимальные версии VosCMS и PHP / партнёр / цена / лицензия). Соотношение контента слева:справа изменено с 2:1 на 3:1 для расширения области контента.
Added
Кнопка «Написать отзыв» в верхнем правом углу вкладки отзывов на странице деталей. Модальное окно ввода звёздного рейтинга / псевдонима / текста → API VosCMS → ретранслируется на маркетплейс POST /api/market/item/review. Лицензионный ключ VosCMS + домен прикрепляются автоматически, ограничение: одна отправка с домена, покупатели автоматически получают is_verified=1. Отзывы хранятся в БД маркетплейса (rzx_mkt_reviews) со статусом status='pending' и публикуются после одобрения оператора. Новый endpoint GET /api/market/item/reviews?slug=... возвращает только одобренные отзывы. Домен отображается частично замаскированным (mys***.com).
Added
Предыдущий флажок Только бесплатные → расширен до 3-позиционного переключателя Все / Бесплатные / Платные. В API каталога маркета добавлен параметр paid=1 (i.price>0). Вынесен из строки поиска, перемещён перед область переключателя вида, интегрирован с селектором сортировки в панель инструментов заголовка.
Changed
Рамка поля поиска border-2 border-emerald-300, зелёный фон за левым значком поиска, кнопка поиска bg-emerald-500. Строка поиска упрощена — оставлен только ввод ключевого слова; сортировка и фильтры перенесены в заголовок тела.
Changed
| Статус | Отображение |
|---|
| Бесплатно | Установить + Скачать |
| Платно (не куплено) | Купить (модальное окно оплаты PAY.JP маркетплейса) |
| Платно (куплено) | Установить + Скачать |
| Уже установлено | «Установлено» |
Та же ветка применена ко всем 4 представлениям страницы списка (список/вебзин/карточка/сетка). Набор купленных slug запрашивается один раз при входе на страницу и переиспользуется. Оплата обрабатывается маркетплейсом через POST /api/market/item/purchase с биллингом PAY.JP + выдачей лицензии.
Changed
- •Переключатель стиля вида: текстовые метки убраны → только иконки +
title / aria-label (доступность сохранена)
- •Значок типа: светлый фон
bg-<color>-100 → тёмный полупрозрачный bg-zinc-900/85 backdrop-blur-sm + цветной текст (улучшенная читаемость поверх изображений)
- •Раздел скриншотов: перемещён с сразу после заголовка → под контент вкладок, счётчик отображается как
(n) аналогично маркетплейсу
- •
dark:prose-invert на странице деталей не работал → плагин typography добавлен в CDN Tailwind
- •Область описания страницы деталей: тот же подход, что у маркетплейс-администратора — HTML-разметка рендерится как есть, удаляются только опасные элементы типа
<script>/<iframe>/<style>/onXX
Fixed
- •Форма карты PAY.JP не принимает ввод: заблаговременно смонтирована в скрытом состоянии, отчего iframe рисовался с размером 0px. Исправлено ленивой инициализацией + монтированием через
requestAnimationFrame
- •Отсутствие миниатюры виджета: 28 виджетов зарегистрированы в БД маркетплейса, но в
/var/www/market/widgets/ скопированы только 12 бесплатных. Синхронизированы 14 недостающих. К API catalog/item добавлено преобразование в абсолютный URL для screenshots + автоматическая фильтрация несуществующих файлов
- •Предупреждение о неавторизованном bundle-плагине:
vos-autoinstall попадал в unauthorizedPlugins. В LicenseStatus.php добавлена константа BUNDLE_PLUGINS = ['vos-autoinstall', 'vos-marketplace'], фильтрация применена в active()/fromCache()/canUsePlugin(). То же самое применено к серверу лицензий verify.php
- •Возможность дублирования виджетов: сидинг
install.php использовал только INSERT IGNORE, но в rzx_page_widgets нет ограничения UNIQUE, поэтому дубликаты были возможны. Изменено на DELETE FROM page_widgets WHERE page_slug='home' перед INSERT (ограничение UNIQUE в БД не добавлено, так как намеренные дубликаты существуют для случаев типа виджетов досок)
Migration Notes
- •Меню/маршруты/миграции плагина
vos-autoinstall автоматически активируются при новых установках (существующие установки нужно активировать один раз из панели администратора)
- •TTL кэша API маркетплейса по умолчанию: 30 минут → 5 минут (для более быстрого отражения изменений цен/контента)
- •Настройка плагина
vos-autoinstall.cache_ttl является новой
Changed
- •
browse.php: переход с запроса к локальной БД на прямой API-вызов к market.21ces.com/api/market/catalog
- •
item-detail.php: переход с локальной БД на API-вызовы /api/market/item и /api/market/item/versions
- •
CatalogClient.php: базовый URL API изменён с /api/v1 → /api/market, поле ответа items[] → data[] приведено в соответствие
Fixed
Проблема: В блоке применения макета в market.21ces.com/index.php отсутствовала обработка случая $__noLayout = true, из-за чего все маршруты API плагина (api/market/*) возвращали HTTP 200 + пустой ответ.
Исправление: В конец index.php добавлено elseif ($__pageFile && $__noLayout) { include $__pageFile; }. api/market/catalog, api/market/item, api/market/updates/check и другие теперь работают корректно.
Added
В plugins/vos-market/views/api/catalog.php добавлены параметры sort, free и featured, расширены поля ответа (sale_price, rating_avg, is_featured и др.).
Added
Вспомогательная функция mpApiFetch(): минимизирует запросы к серверу маркетплейса с помощью файлового кеша с TTL 30 минут. Отображает баннер об ошибке при неудачном ответе API.
Fixed
Проблема: В AdminRouter после создания экземпляра $licenseClient = new LicenseClient() и попытки автоматической регистрации обновление $_ENV['LICENSE_KEY'] не отражалось во внутреннем $this->licenseKey экземпляра — поэтому вызов check() в рамках того же запроса по-прежнему возвращал unregistered.
Changed
- •При входе в
check(), если $this->licenseKey пуст, немедленно вызывать /api/license/register
- •При успехе: обновить
$this->licenseKey + обновить $_ENV['LICENSE_KEY'] + сохранить файл .env, затем перейти к шагу verify
- •Возвращать
unregistered только при сбое (нет ответа от сервера)
- •Новый метод
saveKeyToEnv(): заменяет пустую строку LICENSE_KEY= (случай неудачи install.php) реальным ключом; пропускает, если действующий ключ уже существует
Эффект
- •Пользователи, установившие вручную без install.php → лицензия регистрируется автоматически при первом входе в администраторскую панель
- •Пользователи, обновившиеся со старой версии (нет LICENSE_KEY) → автоматическая регистрация при первом входе в администраторскую панель после обновления
- •Сообщение панели управления «Лицензия не зарегистрирована» теперь отображается только когда сам сервер лицензий недоступен
Fixed
В seed-файле database/seeds/legal_pages.php переменные Tailwind CSS (--tw-scale-x, --tw-pan-x и др., 400+) были встроены в атрибут style="" каждого тега <h1>/<h2>/<p>. При каждой установке это загрязнение распространялось в БД, раздувая страницы terms и privacy (в частности, privacy/ko достигла 128KB; норма — 2KB).
- •
database/seeds/legal_pages.php: 442KB → 131KB (уменьшение на 70%, синтаксис корректен)
- •Очищено 26 уже загрязнённых строк в БД разработки: 1,04MB → 56KB (удалено 988KB, −95%)
- •Новый
scripts/clean-inline-styles.php — инструмент массовой очистки style="... --tw-* ..." (пригоден для повторного использования в случае рецидива)
Changed
CSS-файл .board-content содержал правила, принудительно преобразующие каждую страницу документа, рассматривая «h2 как заголовок карточки, а h2+p как тело карточки». Это нарушало структуру hero на страницах, оформленных напрямую с помощью Tailwind (например, Brand).
- •Удалено 103 строки правил блока карточек (149 → 45 строк)
- •Оставшиеся правила: базовая типография (p/h·/blockquote/code/ul/ol/table), карточка OG-ссылки, переопределение текста в тёмном режиме
- •Результат: восстановлен оригинальный дизайн страницы
Brand (h2 с синей левой линией, центрированный hero и т. д.)
- •Для юридических документов (terms/privacy и т. д.) стандартной типографии
<h1><h2><p> вполне достаточно — стиль карточек был излишним украшением
Changed
Предпосылки: В версии 2.3.1 содержимое страниц было разделено на rzx_page_contents для корейского оригинала и rzx_translations для остальных языков, а в 2.3.2 добавлен отдельный путь сохранения в редакторе. Однако повторный анализ архитектуры показал, что обе таблицы имеют практически идентичную структуру (slug/key + locale + content), при этом порождая следующие издержки:
- •Корейский оригинал дублируется в обеих таблицах (для зеркалирования цепочки fallback db_trans)
- •Логика сохранения в редакторе требует ветвления ko vs. не-ko
- •Отображение статуса locale в интерфейсе управления страницами требует JOIN двух таблиц
- •Исходное обоснование («единая очередь конвейера ИИ-перевода») имеет мало практической ценности, поскольку длинное тело страницы HTML (10 КБ) и короткие строки интерфейса (20 байт) требуют разных рабочих процессов (разбиение на фрагменты, сохранение тегов и т.д.)
Восстановленная структура: единая таблица rzx_page_contents, одна строка на locale (изначальный дизайн)
Migration
- •
rzx_translations.page.{slug}.title/content × 7 slug × 12 не-ko locale (168 строк) → восстановлено в rzx_page_contents
- •Все ключи
page.* удалены из rzx_translations (234 строки)
- •Цели: Brand, terms, privacy, refund-policy, tokushoho, funds-settlement, data-policy
- •Применено к базам данных разработки и продакшена
Changed
- •
resources/views/admin/site/pages-edit-content.php — ветвление ko/не-ko удалено, простой UPSERT
- •
resources/views/admin/site/pages-document.php — аналогично
- •
resources/views/customer/page.php — db_trans('page.*.title/content') удалён, данные читаются напрямую из page_contents (цепочка fallback locale → en → ko сохранена)
- •
resources/views/admin/site/pages.php — заголовки списка страниц также читаются напрямую из page_contents.title
Kept
rzx_translations сохраняется — продолжает использоваться для:
- •Форумов (
board_post.{id}.title/content)
- •Строк интерфейса (
menu.*, метки настроек)
- •SEO-мета страниц (
page.{slug}.browser_title|meta_keywords|meta_description) — короткие строки, здесь уместно
Принцип: короткие строки интерфейса/мета → rzx_translations; длинный текст тела страницы → rzx_page_contents, разделение хранилищ по характеристикам каждого типа данных.
Changed
После миграции в 2.3.1 структуры сохранения содержимого страниц на оригинальный ko (rzx_page_contents) + DB переводов (rzx_translations), редактор администратора продолжал INSERT/UPDATE всех локалей только в rzx_page_contents — это приводило к несоответствию с форматом миграции и сбоям запросов db_trans() на стороне рендеринга. Этот выпуск устраняет проблему.
Правила ветвления (исходная локаль = $_ENV['DEFAULT_LOCALE'] ?? 'ko'):
| locale | место сохранения |
|---|
ko (исходная) | rzx_page_contents + зеркалирование в rzx_translations (резерв для db_trans()) |
en/ja/zh_CN/... | только rzx_translations (page.{slug}.title, page.{slug}.content) |
При сохранении неисходной локали, если исходная строка в rzx_page_contents отсутствует, автоматически создаётся stub-строка (для сохранения связей меню/настроек). is_active применяется единообразно к исходной строке.
Changed
При открытии страницы редактирования неисходной локали:
1. Сначала найти page.{slug}.title/content + locale из rzx_translations
2. Если не найдено — откат к устаревшей строке rzx_page_contents (совместимость со страницами, пропущенными при миграции)
Для исходной локали используется только rzx_page_contents, как и раньше.
Changed
- •Удаление страницы: также удалить
rzx_translations WHERE lang_key LIKE 'page.{slug}.%'
- •Изменение slug: массовый UPDATE
page.{old_slug}. → page.{new_slug}. в rzx_translations.lang_key
Changed
Прежний подход к хранению строки с полным содержимым на locale в rzx_page_contents унифицирован в метод оригинал ko + БД переводов (rzx_translations). Обеспечивается структурная согласованность с форматом хранения досок (board_post.*).
7 целевых страниц: Brand, terms, privacy, refund-policy, tokushoho, funds-settlement, data-policy
Before:
rzx_page_contents
├── Brand / ko (полный контент)
├── Brand / en (полный контент)
├── ... (13 строк locale)
After:
rzx_page_contents
└── Brand / ko (1 оригинальная строка)
rzx_translations
├── page.Brand.title × 13 locale (включая ko)
└── page.Brand.content × 13 locale
Результаты миграции:
- •
rzx_page_contents: 7 slug × 12 строк не-ko = 84 строки удалено
- •
rzx_translations: 7 slug × 13 locale × (title + content) = 182 строки перенесено
- •Применено к БД разработки и продакшена, проверка рендеринга пройдена (корректное отображение по locale для ko/en и др.)
Changed
- •Кнопка «Новая страница» в заголовке удалена — была нефункциональным плейсхолдером. Страницы создаются через управление меню как единственный источник истины.
- •Значок корзины 🗑 + обработчик AJAX + JS
deleteUserPage() удалены — страницы очищаются при удалении меню, поэтому самостоятельный путь удаления страниц не нужен. Предотвращает поломку меню из-за ошибочных удалений.
- •Сохранены: иконки настройки ⚙ · редактирование ✏ · просмотр 👁
Fixed
- •Ошибка загрузки изображения в
Настройки > Сайт > Изображение логотипа: move_uploaded_file() молча завершался неудачей из-за отсутствия подкаталога storage/logos/. Добавлено автоматическое создание подкаталога в обработчик загрузки + создан каталог на серверах разработки и продакшена (www-data:www-data, 775).
- •Восстановлен приоритет логотипа макета: временно инвертировался в «настройки сайта первыми», затем восстановлен в макет > сайт как запасной вариант согласно требованию «логотип для конкретного макета». Если логотип не задан в макете, автоматически используется логотип сайта.
- •Затрагивает:
skins/layouts/{default, minimal, modern, modern-copy-260413}/header.php и footer.php
Fixed
- •Для дублированных slug
bi-guide / logo-guide оставлена только первоначально созданная строка (id=186, 187), дубликаты id=193, 194 удалены
- •Применено к разработке и продакшену. Баг двойного INSERT в API меню уже исправлен в v2.3.0
Added
Ресурсы логотипа созданы в /var/www/_upload/ — загружать в продакшн по мере необходимости:
Иконка (только знак)
- •
voscms-logo-color-{256,512,1024}.png · цветная (deep·ocean·sky·ocean + центр deep)
- •
voscms-logo-mono-{256,512,1024}.png · sky однотонный + градиент прозрачности 1/0.7/0.48/0.7
Горизонтальный словесный знак (для шапки) — соотношение 820×256
- •
voscms-wordmark-{128h,256h,512h}.png · светлый (Vos deep + CMS серый)
- •
voscms-wordmark-dark-{128h,256h,512h}.png · тёмный (Vos sky + CMS slate-300)
Вертикальный словесный знак (для иконки приложения / OG-изображения) — соотношение 360×420
- •
voscms-stacked-light-{256h,512h,1024h}.png
- •
voscms-stacked-dark-{256h,512h,1024h}.png
Все SVG-оригиналы сохранены. Установленный librsvg2-bin предоставляет CLI rsvg-convert. Проблема с поддержкой цветов OKLCH решена с помощью hex-конвертированной версии (voscms-logo-mono-hex.svg).
Added
- •Таблица БД
rzx_changelog — независимые записи по версии × locale, отслеживание translation_source (original/ai/manual) + source_hash
- •Парсер
RzxLib\Core\Changelog\ChangelogParser — стандарт Keep a Changelog + белый список внутренних разделов (Infrastructure/Internal/Refactor/Chore/Docs/Test — приватные)
- •Импортер
ChangelogImporter — идемпотентное слияние, защита ручных переводов, обнаружение изменений через content_hash
- •CLI-скрипт
scripts/import-changelog.php — параметры --file --locale --dry-run --silent, поддержка SemVer pre-release (2.1.1-hotfix)
- •Интерфейс перевода
RzxLib\Core\Translate\{TranslatorInterface, NullTranslator, TranslatorFactory} — активируется изменением driver в config/translator.php после готовности GPU Gemma 4 (ai.21ces.com)
- •Системная страница
/changelog зарегистрирована (config/system-pages.php), фронтенд-представление resources/views/system/changelog/index.php
- •Специальный скин
skins/page/changelog/skin.json — стили карточек (filled/outlined/flat), цвета по разделам, значок версии, формат даты, отображение относительного времени, переключатель видимости внутренних разделов
- •Страница редактирования
/changelog/edit выделена — предпросмотр загрузки MD, матрица покрытия многоязычностью, переключение и удаление по версиям, кнопка AI-перевода (скоро)
- •Автоматизация деплоя — шаг
1.5/5 Changelog import добавлен в deploy-voscms.sh: автоматическое обнаружение и слияние ko + 12 locale .md-файлов
- •Ключ перевода
site.pages.changelog добавлен для 13 locale
Changed
- •Порядок вкладок изменён:
[Основная информация] [Права доступа] [Дополнительно] [Скин] — системные страницы могут добавлять собственные вкладки в конец
- •Схема
settings_tabs введена (config/system-pages.php) — системные страницы объявляют массив вкладок верхнего уровня; каждый элемент рендерится динамически через key/label/icon/view. Устаревший нижний include settings_view удалён
- •Принцип установлен: Настройки = внешний вид + функциональность / Редактирование = данные — интерфейсы управления данными (загрузка, версии, перевод) вынесены в
/changelog/edit
- •Стратегия сохранения MERGE — обработчик
save_settings объединяется с существующими настройками. Данные других вкладок не удаляются, даже если вкладки разделены
- •JS
saveSettings() — отправляет только поля, присутствующие в DOM. Безопасно, когда некоторые поля не отрендерены в разделённых вкладках
- •Положение карточки layout/skin — возвращена на вкладку «Основная информация» (с учётом обратной связи). Вкладка Skin отвечает исключительно за переменные skin.json
- •Дублирование ширины страницы устранено — поле
page_width удалено со вкладки «Основная информация» (консолидировано в content_width скина)
- •Упрощение вторичных подвкладок в service/order —
general/domain/hosting/addons из config/service-settings-tabs.php перенесены в settings_tabs и повышены до вкладок верхнего уровня
Changed
- •
/{slug}/settings и /{slug}/edit теперь распознают config/system-pages.php — ранее запрашивалась только БД rzx_page_contents, из-за чего системные страницы возвращали 404. Теперь порядок запроса: БД → config
- •Поле
edit_view добавлено — прямое указание пути к файлу представления редактирования для системных страниц. При клике на /{slug}/edit представление подключается напрямую (устранён редирект через админ)
- •Фолбэк pages-settings.php — системные страницы без записи в БД теперь формируют
$pageData из конфига, предотвращая редирект на список управления страницами
- •URL иконки администратора унифицирован —
/{slug}/settings и /{slug}/edit используют стандартный фронтенд-URL
Fixed
- •
menus-api.php: исправлена отсутствующая проверка дубликатов перед INSERT для типа страницы external — предотвращает появление дублирующихся строк в rzx_page_contents при двойном нажатии «Сохранить меню»
- •Исправлена пропущенная очистка
page_contents external-типа + widget при удалении меню — предотвращает появление осиротевших записей
Fixed
- •Фон заголовка (
title_bg_type, title_bg_image, title_bg_video, title_bg_overlay, title_text_color), show_title, show_breadcrumb, custom_css, custom_header_html, custom_footer_html — все стандартные переменные скина страницы теперь отражены в публичном представлении
- •Парциал
_page-title-bg.php теперь принимает переменную $_contentWidth — область заголовка следует ширине контента
- •Иконка администратора отделена от заголовка — размещена независимо в правом верхнем углу области контента; всегда видима, даже при
show_title=0
- •Добавлен верхний отступ 20px над областью заголовка (баланс фонового макета)
Added
- •Ключ перевода
site.pages.contact добавлен для 13 locale (ko/en/ja/zh_CN/zh_TW/de/es/fr/id/mn/ru/tr/vi) — устранена ранее допущенная пропажа
- •Руководство по системе управления страницами
docs/PAGE_ADMIN_GUIDE.md — документирует структуру вкладок, схему, маршрутизацию, права доступа и методы расширения
Added
- •Поля для каждого элемента: число, суффикс, метка (многоязычная), иконка и цвет
- •В конструкторе виджетов администратора поддерживается добавление/редактирование/удаление элементов (новый интерфейс редактирования для типа
stat_items)
- •16 типов иконок (users, chart, star, heart, check-circle, clock, calendar, globe, shield, lightning, trophy, cart, dollar, building, chat) + 10 цветов
- •Анимация подсчёта при входе в область просмотра — IntersectionObserver + easeOutCubic 1.4s
- •Считаются только числовые значения (строки вида
24/7 отображаются как есть), форматирование с запятыми и десятичными знаками сохраняется
- •
widget.json v1.1.0 → v1.2.0
Fixed
- •Исправлена проблема, при которой
admin_name сохранялся с префиксом enc: и отображался без расшифровки
- •Исправлена проблема, при которой запрос аватара неверно выполнял LEFT JOIN с несуществующей таблицей
staff — теперь напрямую запрашивает rzx_users.profile_image в соответствии с унифицированной структурой admin_id = users.id
- •Относительные пути аватаров автоматически дополняются префиксом baseUrl
Added
- •Кнопки
[Получить миниатюру] [Получить скриншот] автоматически вставляются рядом с каждым полем <input type="url"> (включая расширенную переменную типа url) на всех досках
- •Миниатюра: нативный рендер 1280×960 → вывод 800×600 (первый экран)
- •Скриншот: нативный рендер 1280×полная_страница → вывод 800×уменьшенный (полная прокрутка страницы, реальная высота)
- •Пошаговый прогресс-бар —
Подключение к серверу → Загрузка страницы → Захват экрана → Генерация изображения → Сохранение файла → Ожидание ответа
- •Автоматическое назначение захваченной миниатюры главным изображением — помечается как
board_files.is_primary=1, виджет board-showcase использует её приоритетно
- •Новый маршрут
/board/api/url-capture?type={thumbnail|screenshot}&url=... + прокси api-url-capture.php
- •В
/board/api/files добавлено действие set_primary — назначает один из существующих вложений главным изображением
Changed
- •Запрос изображений вложений:
ORDER BY is_primary DESC, id ASC — захваченные миниатюры отображаются первыми
- •При создании записи параметр
primary_file_pos передаётся серверу для указания индекса главного файла
v2.2.0
2026-04-18 · 6 мес. назад
Разделение Marketplace
Changed
- •Разделение продукта Marketplace: функциональность, которую совместно использовал плагин
plugins/vos-marketplace/, выделена в самостоятельный продукт → github.com/THEVOSjp/Marketplace новый репозиторий, независимое развёртывание на market.21ces.com
- •Плагин сохраняет только «пользовательский интерфейс для клиентов (поиск, покупка, установка, лицензирование)» — данные, продавцы и расчёты полностью переданы серверу marketplace
- •Точки интеграции API на стороне Core обновлены
Added
- •Модальное окно после выбора языка — предупреждение о необходимости резервного копирования
.env + БД + storage/uploads/ и уведомление о том, что при потере APP_KEY расшифровка невозможна
- •Двухэтапное подтверждение: флажок + кнопка подтверждения (безусловная установка запрещена)
- •Полные переводы добавлены для всех 13 языков (
backup_title/body/item_env/db/uploads/warn/check/confirm/cancel)
Added
- •
scripts/register-widgets-to-marketplace.php — массовая регистрация 28 файловых виджетов в rzx_mp_items
Changed
- •
plugins/vos-marketplace/src/CatalogClient.php — URL API по умолчанию изменён на https://market.21ces.com/api/v1, путь fetchItem() обновлён, формат запроса checkUpdates() обновлён
- •
plugins/vos-marketplace/plugin.json — значение по умолчанию marketplace_api_url обновлено
- •
rzxlib/Core/Modules/WidgetLoader.php — URL миниатюры теперь содержит кеш-бастер ?v={mtime} (для обработки иммутабельного кеша Cloudflare)
Fixed
- •
widgets/feature-tour/widget.json — очистка полей
- •Обновлены миниатюры некоторых виджетов (staff, testimonials, feature-tour, shop-map, shop-ranking, stylebook)
v2.1.5
2026-04-17 · 6 мес. назад
Added
- •contact-info (v0.1.0) — контактная карточка (адрес/телефон/email/часы работы + изображение + маленькая карта)
- •location-map (v0.1.0) — горизонтальная карта (Google Maps + информация в 3 колонках внизу, настраиваемая ширина)
Added
- •Полный рефакторинг страницы отправки элемента — 3 вкладки (основная информация / релиз / продажи)
- •Лицензия (GPL/LGPL/BSD/MIT/CC/PD/коммерческая), URL репозитория, URL демо, зависимые плагины, баннерное изображение
- •История релизов, информация о продажах (переключатель публичный/платный, цена со скидкой/дата окончания), улучшения UX
Added
- •Меню разработчика (ID:20) на 13 языках, description/typing_words Hero CTA дополнены на 12 языках
Changed
- •Уточнено различие имён виджетов (карта / контактная карточка), добавлены fullWidthTypes в
page.php
- •contact-form категория → только метка (код удалён, настройки виджета редактируемы)
Fixed
- •board-contents/board-showcase: file_type → mime_type
- •boards-api: отсутствующий is_shown_in_list + ограничение JSON опций
- •contact-form POST: полная очистка буфера ob
- •Hero CTA: двойное экранирование
\n в typing_words/description
DB Changes
- •rzx_mp_items: добавлены license, repo_url, demo_url
- •rzx_board_extra_vars: добавлен is_shown_in_list
- •rzx_contact_messages: новая таблица
v2.1.4
2026-04-17 · 6 мес. назад
Added
- •Виджет download-hero — страница загрузки программного обеспечения (прямая интеграция с version.json, 4 встроенных языка, ключевые функции / системные требования / история версий)
- •Страница /downloads — системная страница типа widget, переводы меню на 13 языков,
is_system=1
- •Виджет contact-form (v0.1.0) — виджет частной формы обратной связи 1:1 (доступен для гостей, выбор категории, защита от спама через honeypot, отправка через AJAX)
- •Виджет contact-info — виджет отображения контактной информации
- •Виджет location-map — виджет карты местоположения
- •Общий помощник rzx_send_mail() — новая функция отправки почты по SMTP в
rzxlib/Core/Helpers/mail.php
- •Таблица rzx_contact_messages — таблица БД для хранения обращений
- •Страница /contact — системная страница типа widget, переводы меню на 13 языков
Fixed
- •Защита системных страниц от удаления — контент страницы/виджеты сохраняются при удалении системной страницы из меню (удаляется только пункт меню)
- •
menus-api.php: двойная проверка через DB is_system + slug в config/system-pages.php
- •
menus-js.php: при удалении системной страницы показывается уведомление «Будет удалено только из меню»
- •Многоязычные меню системных страниц — при добавлении системной страницы в меню автоматически регистрируются 13-языковые метки из ключей перевода
site.pages.*
- •Перевод страницы загрузок — добавлены переводы
site.pages.downloads для 13 языков
Changed
- •Hero главной страницы — кнопка Download → прямая загрузка по адресу
/download/voscms-latest.zip, версия обновлена до v2.1.1
- •Auth.php — сначала вызывает общий помощник
rzx_send_mail(), затем при необходимости sendSmtpMail
- •index.php — добавлена автозагрузка помощника
mail.php
v2.1.3
2026-04-17 · 6 мес. назад
Changed
- •Вкладка Хостинг — сводная карточка + прогресс-бар использования + таблица подключения FTP/БД (автоматически отображается при настройке администратором)
- •Вкладка Домен — карточка домена + основная настройка + таблица информации об именных серверах (автоматически отображается при настройке администратором)
- •Вкладка Почта — информация о почтовом сервере (IMAP/SMTP) + карточка по каждому аккаунту + встроенная смена пароля
- •Вкладка Техобслуживание — информационная карточка + запланированная дата + история работ (автоматически отображается при записи администратором)
- •Вкладка Дополнительных услуг — единый макет карточек
- •Единая информационная структура в соответствии с партиалом администратора (администратор вводит в metadata → автоматически отображается в личном кабинете)
Changed
- •Область журнала активности уменьшена с соотношения 1/3 до 1/5
- •Удалено жёстко заданное значение NS по умолчанию на вкладке домена администратора (подготовка к интеграции API Cloudflare)
Changed
- •Рабочий процесс подготовки полностью переработан: сначала поддомен → затем подключение купленного домена
- •Правило идентификатора подготовки: имя поддомена или SLD купленного домена
- •Задействован Cloudflare wildcard-туннель (добавление A-записи поддомена не требуется)
- •Изменена конфигурация дисков сервера 2: NVMe 256 ГБ + SSD 2 ТБ + HDD 8 ТБ (трёхдисковая конфигурация)
v2.1.2
2026-04-16 · 6 мес. назад
Added
- •Система классификации услуг — добавлен столбец
service_class (recurring/one_time/free), completed_at дата завершения для разовых услуг
- •Детали услуги на странице «Мой профиль» —
/mypage/services/{order} управление вкладками по типу (хостинг/домен/почта/обслуживание/дополнительные услуги)
- •API управления услугами —
api/service-manage.php (переключатель автопродления, запрос продления, настройка основного домена, смена пароля почты)
- •Управление заказами в администраторской панели —
/admin/service-orders список заказов + страница деталей, модал статуса разовых услуг (принято/в обработке/на удержании/отменено/завершено)
- •Меню администратора — меню «Заказы услуг» добавлено в боковую панель (position: 65)
Added
- •phone-display — компонент отображения номера телефона (серверный хелпер
format_phone()), без зависимости от JS
- •format_phone() — PHP-функция форматирования международных номеров телефонов (Корея/Япония/США/Китай/Великобритания/Франция/Германия/Австралия и др.)
- •Настройка бесплатного домена — управление списком бесплатных поддоменов в настройках администратора (
service_free_domains), поддержка выбора нескольких доменов
Fixed
- •Отсутствующие подписки на бесплатные услуги — бесплатные заказы не создавали записи подписки для домена/дополнений/обслуживания и т. д. → рефакторинг для массового создания всех услуг
- •Согласие с условиями в секции оплаты — флажок условий скрывался вместе с секцией оплаты при бесплатных заказах → вынесен за пределы секции
- •Определение isFree в updateSubmitButton —
btn.closest('form') NULL → исправлено с помощью document.querySelector
- •Индикатор активного пункта боковой панели администратора — неопределённый
$adminPath делал все меню неактивными → добавлено объявление переменной
- •Аватар на странице «Мой профиль» — отсутствующие
use Auth + $user = Auth::user() в services.php/service-detail.php
- •Проверка разового дополнения — флаг
one_time в настройках администратора теперь корректно отражается в service_class при оформлении заказа
- •Начальный статус разовой услуги — изменено на
pending (принято) вместо active
Changed
- •Рефакторинг API заказов — код создания подписок для 3 платёжных путей (бесплатно/карта/банковский перевод) консолидирован в общую функцию
$subscriptionData + _insertSubscriptions()
- •UI списка услуг — встроенное раскрытие → ссылка на карточку + отдельная страница деталей
- •Отображение номера телефона —
format_phone() применена к профилю/списку участников/списку сотрудников
- •Динамизация бесплатного домена — хардкод
.21ces.net → ссылка на значение из настроек администратора
v2.1.1-hotfix
2026-04-16 · 6 мес. назад
Fixed
- •Ошибки 404 в меню подвала — отсутствующие данные страниц юридических документов (terms, privacy, refund-policy, data-policy, tokushoho, funds-settlement) в таблице
rzx_page_contents → восстановлены из тестовой БД
- •Сбой загрузки изображений/видео в конструкторе виджетов — исправлена проблема, при которой POST
FormData от фронтенд-обёртки (page-widget-builder.php) не передавался AJAX-обработчику (добавлено условие загрузки файла + обработка ob_end_clean/exit)
- •Права доступа к директории загрузки — владелец
storage/uploads/widgets/ изменён на www-data для предоставления PHP-FPM прав на запись
Added
- •Многоязычное меню заявки на услугу — добавлены переводы
menu_item.19.title для 13 языков в таблицу rzx_translations
- •Многоязычный виджет Hero CTA — применены переводы на 13 языков для title, subtitle и текста кнопки баннера hero страницы Dev
- •Предпросмотр статуса загрузки — добавлена визуальная обратная связь при загрузке изображений/видео: в процессе (спиннер), успех (галочка) и ошибка (сообщение об ошибке)
v2.1.1
2026-04-12 · 6 мес. назад
Added
- •Экран выбора языка на Шаге 0 — выбор из 13 языков до начала установки; весь интерфейс отображается на выбранном языке с этого момента
- •Файлы перевода установки разделены —
resources/lang/install.php (13 языков × 45 строк)
- •Уровень участника на нескольких языках — Normal/Silver/Gold/VIP автоматически сгенерированы на 13 языках
Added
- •config/admin-menu.php — основное меню администратора вынесено в конфигурационный файл (сохраняется при обновлениях ядра)
- •admin-sidebar.php рефакторинг — 731 строка жёстко прописанного кода → ~200 строк динамического рендерера (на основе config + plugin.json)
- •vos-shop plugin.json — жёстко прописанное меню перенесено в формат
menus.admin
Added
- •Выдвижное меню — мобильное: панель переключения аватар+имя → левый выдвижной оверлей (300ms ease-out)
- •Затемнённый фон с плавным появлением — закрыть кликом по фону или кнопкой X; прокрутка body заблокирована
Added
- •force_locale — принудительно применять язык по умолчанию (параметр
?lang=xx позволяет пользователю изменить)
- •default_language — ключ конфигурации install.php ↔ Translator унифицирован
- •Параметр URL —
?lang=xx определяется первым, сохраняется в сессии/cookie
Fixed
- •Скин FAQ не применён — SQL миграции выполнял чистый
INSERT для форума по умолчанию до install.php, из-за чего INSERT IGNORE молча игнорировался; исправлено
- •Отсутствие source_locale в таблице translations — столбец отсутствовал в миграции ядра → db_trans() полностью не работал → информация пользователя/меню/i18n не отображалась; исправлено
- •Индикатор активности в боковой панели вкладки настроек — добавлен атрибут
route_prefix, чтобы меню настроек оставалось активным на всех под-маршрутах настроек
- •Ошибка остаточной сессии при установке — при чистой установке без .env старая PHP-сессия приводила к пропуску Шага 0; исправлено
- •nginx 403 — конфликт между физической директорией admin/ и маршрутом исправлен (удалено
try_files $uri/)
- •Жёстко прописанная версия в скрипте обфускации — obfuscate.php теперь читает версию динамически из version.json
Changed
- •Многоязычный вид виджета grid/board-contents — добавлен поиск по rzx_translations (текущий locale → резервный английский)
- •Категории отраслей сайта — расширены с 15 до 26 (добавлены: корпоративный, торговый центр, юридический, бухгалтерский, IT, медиа и т.д.; 13 языков)
- •Настройки виджета grid — render.php секции grid изменён на нативный тип ячейки (
board-list); заголовок на 13 языках
- •Выбор языка на Шаге 4 — расширен с 5 до 13 языков; предзаполнен языком установки
- •Выбор часового пояса — расширен до 12 часовых поясов, охватывающих регионы 13 языков
v1.26.0
2026-04-01 · 7 мес. назад
Added
- •Пакетные (комплектные) услуги в 3 колонки вверху экрана выбора услуг — янтарная цветовая тема для визуального отличия от индивидуальных услуг
- •Значок SET, ярлык скидки мероприятия, названия включённых услуг, цена пакета, зачёркнутая оригинальная цена, отображение размера скидки
- •Выбор пакета автоматически выбирает все включённые услуги (цена рассчитывается как цена пакета)
- •Услуги в пакете нельзя включать/отключать по отдельности (предотвращает неверный расчёт цены)
- •Режим назначения: таблица
rzx_staff_bundles для фильтрации пакетов по сотрудникам
- •Параметр
bundles передаётся на страницу подтверждения
- •Многоязычная поддержка: ключи перевода киоска, связанные с пакетами, добавлены для 13 языков
Added
- •Кнопки переключения вида — переключатель
[Месяц] [Неделя] [День] в верхней панели (активное состояние: синий)
- •Недельный вид — левая вертикальная ось = день недели, верхняя горизонтальная ось = временная ось
- •Горизонтальная прокрутка, блоки бронирования отображаются в виде временных полос
- •Алгоритм дорожки: обнаруживает пересечение времени и автоматически размещает ниже, высота строки переменная
- •Левая боковая панель ↔ вертикальная прокрутка сетки синхронизированы
- •Дневной вид — горизонталь = временная ось, вертикаль = дорожки (тот же метод, что и в недельном виде)
- •Временной диапазон на основе рабочего времени — на основе настроек
business_hour_start/business_hour_end; автоматически расширяется для бронирований вне диапазона
- •Передача времени по клику на пустую ячейку — в недельном/дневном виде: X-координата клика → расчёт времени в единицах 15 минут → автозаполнение времени начала в модале бронирования
- •Верхняя навигационная панель зафиксирована — применены
overflow-x-hidden + sticky; остаётся в области просмотра при горизонтальной прокрутке
Added
- •Администрирование > Настройки > Общие — добавлен интерфейс настройки времени начала/конца рабочего дня
- •Ключи настроек БД
business_hour_start, business_hour_end
- •Применяются к диапазону времени по умолчанию в недельном/дневном виде календаря
Fixed
- •Отсутствующие поля POS
add-service — добавлена передача start_time, end_time, staff_id, designation_fee, notes (ранее: время на стороне сервера зашито в код, сотрудник/заметки отсутствовали)
- •Предотвращение выхода end_time за полночь — лимит
% 24 → min(23:59) применён в ReservationHelper, API start-service и calcEnd() формы бронирования
- •Обработка перехода через полночь в определении использования POS —
CustomerBasedAdapter обрабатывает end_time < start_time как переход через полночь
- •Валидация пакета бронирования — исправлено: создание бронирования возможно даже при выборе только пакета (без отдельных услуг)
Changed
- •Переименование вкладок: Приём / Список приёма / Бронирования
- •Список приёма: источники
walk_in, pos, kiosk, admin — отображаются все статусы
- •Бронирования: источники
phone, online — отображаются все статусы
- •Порядок сортировки: Ожидание → Подтверждено → В процессе → Завершено → Неявка → Отменено; внутри одного статуса по времени
- •Строки завершено/отменено/неявка: отображаются с
opacity-50, показывается значок без назначения
Changed
- •Значок завершено: изменён с серого → на зелёный
- •Значки отменено/неявка добавлены (скрыты, если количество 0)
Added
- •Нижняя часть карточки: добавлена кнопка неявки для карточек в статусе ожидания (иконка запрета), кнопка отмены изменена на красный
- •Детальный модал: кнопка действия неявки отображается для статусов
pending и confirmed
- •Групповая массовая неявка: добавлена функция
POS.noShowAllServices()
Fixed
- •Навигация назад в деталях бронирования —
<a href="/reservations"> → history.back(), возврат к предыдущей странице
- •Многоязычность: ключи переключения вида календаря (месяц/неделя/день), пакет, рабочее время добавлены для 13 языков
v1.25.0
2026-04-01 · 7 мес. назад
Architecture
- •POS выделен в независимую структуру плагина
- •
resources/views/admin/reservations/pos-*.php → перемещено в директорию resources/views/admin/pos/
- •Функция POS автоматически включается/отключается в зависимости от наличия директории
pos/ (проверка is_dir())
- •Боковое меню: меню POS автоматически скрывается при отсутствии директории
pos/
- •Маршрутизация
index.php: маршруты POS отключаются при отсутствии директории pos/
- •
pos/_init.php → делегирует к общему _init.php резервирований (общие DB, аутентификация, i18n)
- •API-эндпоинты остаются в
reservations/_api.php (обратная совместимость гарантирована)
Added
- •Единый платёжный модал — одновременное использование баллов + наличных + карты (комбинированная оплата)
- •Баллы: запрос баланса, ручной ввод или использование всей суммы
- •Наличные: ввод суммы
- •Карта: ввод суммы + кнопка автозаполнения «использовать остаток»
- •Рассрочка: единовременная оплата / выбор от 2 до 12 месяцев
- •Автоматический расчёт итога, сдачи и недостатка в реальном времени
- •Для оплаты картой Stripe наличные/баллы обрабатываются только после успешной оплаты картой (безопасно при отмене)
- •Модал деталей услуги — отображение баллов
- •Баланс баллов участника всегда отображается в области заголовка (показывается даже при нулевом балансе)
- •Модал деталей услуги — улучшение бейджа сотрудника
- •Бейдж «назначен/указан» отображается перед именем сотрудника (ранее: под именем)
- •Перемещение кнопок чека/печати
- •Перемещены выше раздела с деталями клиента, чек (слева) + печать (справа)
- •Улучшение функции печати
- •Печатает содержимое текущего модала деталей услуги вместо открытия внешней страницы
Changed
- •Макет карточек — на основе grid → карточки фиксированного размера с flex-wrap
- •Фиксированный размер карточки: по умолчанию 320×240px (small: 260×200, large: 380×280)
- •Размер карточек не меняется в зависимости от ширины экрана; автоматический перенос
- •Модал оплаты картой — расширен до ширины 1030px, высоты 800px
Fixed
- •Ошибка cross-origin API устранена
- •
$baseUrl был жёстко привязан к APP_URL из .env, что вызывало сбои fetch с других хостов/HTTPS
- •Панель управления: URL fetch изменён на относительный путь (
/update-api.php)
- •
_init.php резервирований: $baseUrl изменён на пустую строку, чтобы все fetch использовали относительные пути
- •Проблема смешанного контента полностью решена в среде прокси Cloudflare HTTPS
- •Ошибка выполнения миграции БД — «Failed to fetch» устранена (проблема cross-origin)
- •Баг предварительного списания наличных при отмене комбинированной оплаты Stripe
- •Раньше: наличные обрабатывались первыми → при отмене оплаты картой наличные уже были списаны
- •Теперь: наличные/баллы обрабатываются только после успешной оплаты картой Stripe
i18n
- •
pos_pay_after_points — сумма после применения баллов
- •
pos_pay_proceed — перейти к оплате
- •
pos_pay_remainder — использовать весь остаток
- •
pos_pay_installment — рассрочка
- •
pos_pay_lump_sum — единовременная оплата
- •
pos_pay_months — месяцев
- •
pos_pay_total_pay — итого
- •
pos_pay_short — недостаток
- •
show_receipt — чек
- •
show_print — печать
v1.18.0
2026-03-29 · 7 мес. назад
Added
- •Многоязычная функция форматирования дат —
formatReservationDate($dateString, $locale)
- •Поддержка 13 языков (ko, en, ja, zh_CN, zh_TW, de, es, fr, id, mn, ru, tr, vi)
- •Автоматическая локализация даты и дня недели на странице деталей бронирования
- •Резерв: неподдерживаемые локали → английский формат
Changed
- •Многоязычная обработка страницы деталей бронирования
- •Названия сервиса, сотрудника и пакета локализованы (на основе таблицы translations в БД)
- •Интегрирована функция форматирования дат
- •Метка статуса платежа локализована (поле payment_status)
- •Многоязычная поддержка статуса платежа
- •Раздел
payment добавлен во все языковые файлы (11 языков)
- •Переводы для: неоплачено, оплачено, частично оплачено, возвращено
Fixed
- •Страница деталей бронирования: имена сотрудника и сервиса, которые ранее не локализовались, теперь отображаются корректно
- •Метка статуса платежа: устранена проблема, при которой она отображалась только на корейском
- •Многоязычная обработка унифицирована на основе функции
_tr()
Layout & UI
- •Ширина страниц унифицирована (max-w-5xl → max-w-7xl)
- •Страницы доски (список, чтение, написание): max-w-5xl → max-w-7xl
- •Внешняя страница (page.php): обновлены значение по умолчанию и настройки БД
- •Детали бронирования / поиск бронирования / страницы сотрудников: обновлены настройки БД
- •Результат: ширина всех страниц теперь совпадает со страницей бронирования
Documentation
- •В COMPONENT_MANUAL.md добавлена подробная документация для
formatReservationDate()
- •Поддерживаемые языки и таблица форматов
- •Примеры использования и внутренняя структура
- •Примечания и связанные функции
v1.16.0
2026-03-23 · 7 мес. назад
Added
- •Мобильная нижняя панель меню — Персонал / Бронирования / Главная / Войти (или Мой кабинет) / Регистрация / Ещё, с подсветкой текущей страницы
- •Мобильная панель «Ещё» — главное меню + Мой кабинет / Выйти
- •Реальное применение настроек макета/скина — site_layout, site_page_skin, site_board_skin, site_member_skin → отражено в рендеринге
- •Параметр no_layout — только контент без макета (для предпросмотра)
- •Добавлены ключи перевода — common.nav.staff, common.nav.more (13 языков)
Fixed
- •Применён макет к страницам участников (login/register/forgot/reset): дублирующийся заголовок удалён путём извлечения
<main> скина
- •Исправлена ошибка повторного объявления ImageCropper/ProfilePhotoCropper в скине участников
- •Исправлена проблема include_once загрузчика меню: отсутствие меню base-header при загрузке внутри closure скина
- •
apple-mobile-web-app-capable устарел → mobile-web-app-capable (8 файлов)
- •Кнопки входа/регистрации в шапке скрыты на мобильных устройствах (заменены нижней панелью меню)
- •Поддержка iOS safe-area для нижней панели меню
Changed
- •Страницы участников переведены с собственного макета на стандартный (login/register/forgot/reset)
- •Нижний отступ подвала удалён (мобильная панель меню использует фиксированное позиционирование)
v1.15.3
2026-03-22 · 7 мес. назад
Added
- •Панель детальных настроек макета — AJAX-загрузка формы настроек на основе SkinConfigRenderer
- •Настройки меню макета — раздел menus в layout.json → автогенерация выпадающего списка sitemaps из БД
- •Поддержка вкладок в SkinConfigRenderer — добавление свойства
tab в vars автоматически генерирует UI вкладок
- •Рефакторинг SkinConfigRenderer — единый метод
renderVar(), разделение renderFormFlat/renderFormWithTabs, общий renderDependsOnJs
- •Расширения layout.json — menus (GNB/FNB), 14 vars (шапка/логотип/цвет/подвал/пользовательский), 13 языков
- •Интеграция предпросмотра макета — выбор "Отключить" показывает предпросмотр с ?no_layout=1
Fixed
- •Запрос меню управления макетом: исправлено имя таблицы
rzx_menus → rzx_sitemaps
- •Настройка ширины центральной панели + whitespace-nowrap для ссылок действий
Pending
- •Применение значений настроек макета/скина к фактическому рендерингу страниц (следующая сессия)
v1.15.2
2026-03-22 · 7 мес. назад
Added
- •Страница управления макетами — трёхпанельный макет: левая — предпросмотр+меню, правая — список скинов, панель детальных настроек
- •Для каждого скина: миниатюра + ссылки действий (детальные настройки / создать копию / удалить)
- •Опции «Отключить» / «Установить другой скин»
- •Сохранение по 4 категориям: макет, страница, доска, участник (
site_layout, site_page_skin, site_board_skin, site_member_skin)
Changed
- •«Управление дизайном» → переименовано в «Управление макетами» (13 языков: admin.php + site.php)
v1.15.1
2026-03-22 · 7 мес. назад
Changed
- •15 страниц управления сайтом → применён общий заголовок
_head.php
- •Доски: boards, boards-create, boards-edit, boards-trash
- •Меню: menus
- •Дизайн: design
- •Страницы: pages, pages-settings, pages-edit-content, pages-document, pages-compliance, pages-widget-builder
- •Виджеты: widgets, widgets-create, widgets-marketplace
- •Страницы в режиме embed (boards-edit, pages-settings, pages-edit-content) применяют
_head.php только вне режима embed
- •Полная унификация заголовка и макета всех страниц администратора завершена
v1.15.0
2026-03-22 · 7 мес. назад
Added
- •Модуль системных виджетов — 4 виджета: staff (представление персонала), booking (оформление записи), lookup (поиск записи), cta001 (баннер призыва к записи)
- •Системные страницы — staff, booking, lookup зарегистрированы в БД (is_system=1) + маршрутизация (
/staff, /booking, /lookup)
- •Рендеринг страниц на основе виджетов — страницы staff/booking/lookup рендерят виджеты при их размещении, при отсутствии — возврат к устаревшему рендерингу
- •Автоматическая регистрация виджетов в БД — вызов
syncToDatabase() автоматически регистрирует виджеты при добавлении директории виджета
- •Категория виджетов
system — переведена на 13 языков (widgets.categories + widget_builder.cat)
- •Унификация списка страниц — цикличный рендеринг системных страниц + единый набор иконок шестерёнки/редактирования/предпросмотра
- •Унификация заголовка страницы администратора —
$pageSubTitle + $pageSubDesc (общий вывод через _head.php)
- •Добавлены ключи перевода — staff.admins, staff.attendance.report/stats, points.description и др.
Fixed
- •
page_slug = 'home' жёстко прописан в конструкторе виджетов → динамическая обработка через $_GET['slug']
- •AJAX save/preview конструктора виджетов с жёстко прописанным
home → динамический $pageSlug
- •URL предпросмотра конструктора виджетов → динамический
$pageSlug (фиксированная главная → каждая страница)
- •Исправлено отсутствие require WidgetLoader в widget preview_widget
- •Путь к изображению в виде карточки услуги (префикс
/storage/)
- •Ширина настроек киоска
max-w-3xl → 100% унифицирована
- •Заголовок POS «Управление записями» → переименован в «POS»
Changed
- •10 страниц управления персоналом + 4 страницы управления участниками → применён общий заголовок
_head.php
- •Страницы киоска/бандла с применённым
_head.php
- •Языковые настройки киоска grid → flex-wrap адаптивный
- •Список страниц: системные страницы рендерятся в цикле на основе данных
v1.14.1
2026-03-22 · 7 мес. назад
Added
- •Вкладка скина страницы — основная информация о скине + расширенные переменные (на основе SkinConfigRenderer)
- •Скин для всех типов страниц — настройки скина применяются к document/widget/external одинаково
- •Система фона заголовка — фон изображение/видео + наложение + цвет текста (общий partial
_page-title-bg.php)
- •Система depends_on — декларативная зависимость между полями в skin.json, рендерер автоматически генерирует JS (поддержка checkbox/radio)
- •Загрузка файла скина — загрузка изображений/видео через multipart (
storage/skins/page/{skin}/)
- •Карточный выбор макета/скина — изменён на UI карточек с миниатюрами во вкладке информации о модуле
- •SEO-система — SEO-помощник, шаблон заголовка, OG/Twitter мета-теги, извлечение контента
- •Загрузка OG Image — ввод URL + загрузка файла + предпросмотр
- •Общее модальное окно результата — единый UI модального окна успеха/ошибки сохранения (поддержка 13 языков)
- •Карточное представление сервиса — карточка с полноэкранным фоном + кнопка переключения таблица/карточки
- •Перетаскивание для сортировки сервисов — изменение порядка + сохранение в БД в обоих представлениях
- •Назначение/выбор сотрудника для записи — 2 кнопки (назначить/выбрать) в деталях записи
- •UI маршрута записи — телефонная запись / посещение в офисе / онлайн
- •Настройки телефонной записи — переключатель включения/отключения в основных настройках сервиса
- •Заголовки безопасности — X-Frame-Options + CSP frame-ancestors
- •PAGE_SYSTEM.md — документация по разработке системы страниц
- •PRD_OrderSystem.md — плановый документ системы заказов за столами
- •PRD_KioskSystem.md — плановый документ системы киоска
Fixed
- •Настройка favicon из БД применена (фронт / admin / скин макет — 4 места)
- •URL миграции дашборда
window.location.origin → $baseUrl (среды с подкаталогами)
- •URL API режима embed настроек страниц → путь admin указан напрямую
- •i18n модального окна результата: использует сообщения locale при рендеринге страницы вместо ответа API
- •Запасной вариант пустого значения скина во вкладке скина (
"" → default)
- •Проблема двойного потребления
php://input в pages-settings.php решена
Changed
- •Киоск / настройки киоска / bundle-страницы → применён общий заголовок
_head.php
- •Сохранение настроек скина JSON → передача FormData (multipart) (поддержка загрузки файлов)
- •Параметр
$baseUrl добавлен в SkinConfigRenderer (предпросмотр изображений/видео)
- •Переводы скинов на 13 языках (skin.json + site.php + common.php)
v1.13.0
2026-03-21 · 7 мес. назад
Added
- •Система страниц завершена — автоматическое создание страниц при создании страниц типа документ/виджет/внешние из меню
- •Динамический рендерер страниц —
page.php (документ→HTML, виджет→WidgetRenderer, внешняя→iframe/include)
- •Настройки среды страниц — 3 вкладки (основные / макет & скин / SEO), сохраняется в JSON
page_config_{slug}
- •Редактирование содержимого страниц — редактирование заголовка и содержимого по языкам, изменение slug, типа, удаление
- •Динамический список управления страницами — автозагрузка пользовательских страниц из БД, иконки и значки по типу
- •Иконки администратора на фронте — шестерёнка (настройки) + редактирование (контент) отображаются отдельно
- •iframe внешней страницы — при ошибке загрузки показывается подсказка «Открыть в новом окне»
- •Внутренние PHP/HTML-файлы — рендерятся внутри макета RezlyX через include
Fixed
- •URL ярлыка меню
#menu_{id} → сохраняет реальный URL меню
- •Редактирование меню отображает ту же структуру UI ярлыка, что и при создании
- •Сохранение меню учитывает URL ярлыка (
formMenuType → основа видимости панели)
- •Переводы меню добавлены для 13 языков (12 пунктов меню × 13 языков)
- •Исправлен путь ключа перевода редактора документов страницы (
admin.site.pages. → site.pages.)
Changed
- •В DB
rzx_page_contents добавлен столбец page_type (document/widget/external)
- •При удалении меню теперь удаляются связанная страница + данные переводов
v1.12.5
2026-03-21 · 7 мес. назад
Fixed
- •Исправлен URL-ярлык меню — теперь сохраняется реальный URL меню вместо
#menu_{id}
v1.12.4
2026-03-21 · 7 мес. назад
Fixed
- •Тип списка настроек скина не применялся — исправлена проблема, при которой кеш localStorage перезаписывал настройки скина
- •При запрете переключения стилей используются только настройки скина; при разрешении приоритет отдаётся localStorage
- •Устранена двойная отправка скрытых полей checkbox (состояние «снято» теперь обрабатывается в JS)
v1.12.3
2026-03-20 · 7 мес. назад
Fixed
- •Исправлены отсутствующие ключи перевода доски для английского/японского (source_locale, reply, style, board_settings и др.)
- •Применён многоязычный fallback для названий категорий (locale → en → оригинал)
- •Многоязычная поддержка основного текста списка доски (предпросмотр вебзина/карточек/галереи)
v1.12.2
2026-03-20 · 7 мес. назад
Fixed
- •Многоязычный резервный текст списка досок — переведённый текст применён в превью вебзин/карточек/галереи (локаль → en → оригинал)
- •Отсутствующие файлы скина в продакшене — создан каталог
skins/default/board/ через FTP и загружены skin.json/миниатюра
v1.12.1
2026-03-20 · 7 мес. назад
Fixed
- •Настройки скина не отображаются в продакшене — полная синхронизация файлов переорганизована
- •Структура файлов CLAUDE.md + BOARD_SYSTEM.md полностью обновлена
v1.12.0
2026-03-20 · 7 мес. назад
Added
- •Система настройки скинов завершена — динамическое создание форм на основе skin.json + применение к реальным доскам
- •Группировка по разделам (основное / список / ссылки / дизайн / фон заголовка / пользовательское)
- •Добавлены типы ввода radio и video
- •Загрузка файлов изображений/видео (FormData multipart)
- •Функция удаления изображений/видео
- •Информационная таблица скина (автор / URL / e-mail / дата / версия / миниатюра)
- •Изображение/видео фона заголовка — настройки типа фона, высоты, прозрачности оверлея и цвета текста
- •4 стиля макета списка — список / веб-журнал / галерея / карточка; файлы скинов разделены + поддержка переопределения
- •Галерея: полноразмерное изображение cover + анимация слайда при наведении
- •Веб-журнал: миниатюра + заголовок + превью + карточка с мета-информацией
- •Объявления вынесены в отдельный верхний список (_list-notices.php)
- •Настройки скина применены — primary_color, border_radius, posts_per_row, allow_style_switch, show_category, связанная доска, Font Awesome, пользовательский CSS/HTML
- •Автоматическое слияние значений по умолчанию skin.json — значения по умолчанию из skin.json применяются даже без сохранения в БД
Fixed
- •skin_config не сохранялся в БД (действие update в boards-api.php не включало skin_config)
- •Сохранение скина изменено с URLSearchParams → FormData (multipart) для поддержки загрузки файлов
- •Добавлена поддержка автоматического парсинга массива
$_POST['skin_config']
v1.11.3
2026-03-20 · 7 мес. назад
Fixed
- •Применён многоязычный fallback для заголовков предыдущей/следующей записи на странице детального просмотра (locale → en → оригинал)
v1.11.2
2026-03-20 · 7 мес. назад
Fixed
- •Многоязычный резервный заголовок списка досок — поиск переводов по всем локалям (ограничение только корейским удалено), английский резерв с учётом исходного языка
v1.11.1
2026-03-20 · 7 мес. назад
Fixed
- •Улучшено определение одиночных URL для OG-карточек — теперь алгоритм поднимается до ближайшего блочного родителя, благодаря чему URL, обёрнутые в строчные теги
<font> / <span>, обрабатываются корректно
v1.11.0
2026-03-20 · 7 мес. назад
Added
- •Авто-ссылка URL — автоматически обнаруживает URL в теле/редакторе записи доски → конвертирует в теги
<a>
- •Карточка предпросмотра OG-ссылки — карточка в формате SNS-баннера (миниатюра + заголовок + описание + домен) генерируется автоматически
- •Редактор: ввести/вставить URL + Enter → вызов OG API → вставка карточки
- •Тело: автономная URL-ссылка → автоматически конвертируется в OG-карточку
- •Модуль OgFetcher — извлечение метаданных OG/Twitter Card, favicon, автоматическая обработка кодировки
- •OG API — эндпоинт
/board/api/og
- •board-autolink.js — унифицированный JS авто-ссылки + OG-карточки (глобально загружается из base-header)
v1.10.2
2026-03-20 · 7 мес. назад
Fixed
- •Выравнивание изображений в редакторе/контенте — переопределение
img { display: block } из Tailwind preflight
- •Создан общий CSS
board-content.css (объединены отображения редактора и контента)
- •Глобально подключён из
base-header.php (все страницы фронтенда)
- •В
multilang-modal.php добавлены глобальные стили редактора (все экземпляры Summernote в админке)
- •Удалён класс
prose из тела доски; инлайн-стили редактора теперь сохраняются на 100%
Changed
- •В CLAUDE.md задокументированы правила стилей тела доски (класс
board-content обязателен)
v1.10.1
2026-03-20 · 7 мес. назад
Fixed
- •Исправлена ошибка, при которой при добавлении категории доски в многоязычном модале загружались данные предыдущей категории (генерируется уникальный временный langKey)
- •Добавлено значение по умолчанию 3 для
comment_max_depth в миграцию 021
v1.10.0
2026-03-20 · 7 мес. назад
Added
- •Расширенные переменные доски — модуль компонента ExtraVarRenderer (15 типов: рендеринг input/display по типу)
- •Многоязычность расширенных переменных — независимое хранение значений расширенных переменных по языкам (интеграция rzx_translations, цепочка резервирования)
- •Раздельное многоязычное хранение записей доски — при редактировании на исходном языке сохраняется напрямую в board_posts; другие языки сохраняют переводы в rzx_translations
- •Цепочка резервного чтения записей доски — отображает заголовок/тело/расширенные переменные в порядке: текущий локаль → en → оригинал
- •Вложенные ответы — поддержка вложенных комментариев глубиной 3 уровня, рекурсивная сортировка деревом, встроенная форма ответа
- •Лайк/дизлайк комментария — кнопка лайка на каждый комментарий + API
- •Доступ к настройкам доски с фронтенда — значок шестерёнки администратора → /board/{slug}/settings фронтенд-макет
- •Ссылка на заголовок в администрировании доски — клик по заголовку в списке досок администратора переходит к этой доске
Fixed
- •Проверка прав администратора в API доски:
$currentUser['role'] → $_SESSION['admin_id'] (обновлено 4 файла)
- •Уведомление на исходном языке отображается только автору/администратору
- •Ссылка вкладки в режиме встраивания настроек доски поддерживает URL фронтенда
Changed
- •Добавлен JSON-столбец
extra_vars в БД rzx_board_posts
- •Значение по умолчанию
comment_max_depth изменено с 0 → 3
- •Унифицирована панель действий с комментариями (иконки лайк/дизлайк/ответить/удалить)
v1.9.4
2026-03-20 · 7 мес. назад
Added
- •Данные форумов по умолчанию — миграции форумов «Объявления», «Вопросы и ответы», «FAQ» и «Свободный форум» (020_default_boards_v193.sql)
v1.9.3
2026-03-20 · 7 мес. назад
Added
- •Сброс групп участников — инициализация данными по умолчанию (7 групп + переводы на 13 языков)
- •Сброс настроек баллов — восстановление значений конфигурации / баллов уровней / связей с группами по умолчанию (баланс участников не затрагивается)
- •Многоязычные группы участников — общий компонент
rzx_multilang_input() применён для имени группы / описания привилегий
- •Перетаскивание групп участников — сортировка карточек через SortableJS + автосохранение
Fixed
- •Отображение перевода карточки группы участников по локали (вид карточки + модал редактирования)
- •Исправлено повреждение данных многоязычного перевода групп участников (повторное сохранение UTF-8 через PHP)
- •Исправлено имя столбца в таблице настроек модуля баллов (board_name → title)
- •Устранён конфликт сортировки в списке участников с баллами (единая кодировка utf8mb4_unicode_ci)
- •Миграция производственной БД (019_point_system_v190.sql)
v1.9.0
2026-03-20 · 7 мес. назад
Added
- •Система управления бонусами (баллами) — 3-вкладочная структура (Основные настройки / Настройки по модулям / Список баллов участников)
- •3-полевая структура баллов — point (баллы активности), balance (платёжные бонусы), total_accumulated (накопленные баллы, основа для расчёта уровня)
- •Весовой коэффициент накопленных баллов — отдельные весовые коэффициенты для оплаты и активности, преимущественный рост уровня для платящих клиентов
- •Обмен баллов — конвертация баллов → бонусы (коэффициент, единица, минимальные баллы настраиваются)
- •17 категорий начисления/списания баллов — та же структура, что в Rhymix (возврат при удалении, срок действия, уведомления исключены)
- •Настройки по модулям — индивидуальная конфигурация 15 пунктов баллов для каждой доски
- •Список баллов участников — 4 столбца (баллы/бонусы/накопленные/уровень), поиск, индивидуальное редактирование
- •Баллы уровня — расчёт по формуле, динамическая регулировка максимального уровня, привязка к группам
- •Документация системы баллов — POINT_SYSTEM.md подробный проектный документ
- •Перетаскивание группы участников — изменение порядка карточек с помощью SortableJS + автосохранение
- •Многоязычность групп участников — общий компонент
rzx_multilang_input() применён к названию группы/описанию преимуществ
- •Seed-данные групп участников — 7 групп × 13 языков многоязычных данных перевода
Changed
- •Добавлены столбцы
balance, total_accumulated в таблицу rzx_member_points
- •Изменён тип
rzx_point_levels.group_id с INT на CHAR(36) (совместимо с UUID)
- •Приведены коллации
rzx_point_levels и rzx_member_points к единому utf8mb4_unicode_ci
- •Обновлён макет страницы комплекта (
sidebar-main-content → паттерн панели управления flex-1 ml-64)
- •Исправлена единица валюты страницы комплекта
$config['currency'] → $siteSettings['service_currency']
- •Продолжительность услуги изменена с select → input number + datalist
- •В CLAUDE.md задокументированы правила интерфейса многоязычного ввода (обязательное использование rzx_multilang_input)
v1.8.3
2026-03-20 · 7 мес. назад
Added
- •Страница детального управления бандлом — редактирование основной информации о бандле, загрузка обложки, редактирование состава услуг, привязка сотрудников, переключение активен/неактивен
- •Скидка по акции — цена акции для каждого бандла, период (начало/конец), настройка метки; автоматически активируется в период проведения
- •Дизайн карточек в списке бандлов — сетка карточек, изображение в заголовке, значок акции/скидки, закреплённая нижняя панель действий
- •Фоновое изображение рекомендуемого пакета — изображение обложки + градиентное наложение на карточках бандла на странице детальной информации о сотруднике для клиента
- •Прямой ввод продолжительности услуги — select → числовой ввод (5–480 мин, шаг 5 минут) + список рекомендаций datalist
Changed
- •В DB
rzx_service_bundles добавлены столбцы: event_price, event_start, event_end, event_label
- •В API списка бандлов добавлены
total_duration и столбцы акции
- •В файлы перевода (ko, en, ja) добавлены ключи детального управления бандлами + скидки по акции
v1.8.1
2026-03-19 · 7 мес. назад
Added
- •Страница настроек POS — размер карточки, отображение изображения/цены/телефона, прозрачность, вкладка по умолчанию, автообновление, звук уведомления, обязательное назначение сотрудника, автоназначение
- •Настройки POS применены — размер карточки/изображение/цена/телефон/прозрачность/автообновление/вкладка по умолчанию отражаются в POS
- •Автоматическое назначение сотрудника — при активации в настройках POS автоматически назначает сотрудника при регистрации на киоске (способный выполнить услугу + наименьшее количество записей на тот же день)
- •Бейдж сотрудника на карточке POS — индикатор назначен/не назначен; кнопка продолжения деактивирована при отсутствии назначения
- •Фоновое изображение услуги в POS — изображение основной услуги в качестве фона в шапке + клиентской зоне модального окна детали услуги
- •Сотрудник is_visible — управляет видимостью в списке; скрытые сотрудники исключены из записей/POS/киоска
- •Баннер карточки сотрудника — баннерное изображение + значок статуса на карточке сотрудника в панели администратора
- •Автоназначение на киоске — регистрация с staff_id=NULL; назначение выполняется администратором в POS
Fixed
- •Назначение сотрудника в POS: список услуг немедленно обновляется после назначения (предотвращение кэша)
- •Исправлен параметр API назначения сотрудника (reservation_id → reservation_ids[])
- •Исправлено множество отсутствующих таблиц/столбцов в продакшене (миграция 018 усилена)
- •Добавлен маршрут выхода администратора
- •Размещение маршрута предаутентификации киоска
v1.7.0
2026-03-18 · 7 мес. назад
Added
- •Базовая система макетов —
layouts/base-header.php + base-footer.php, автоматическое применение ob_start в index.php, общая шапка/подвал для всех клиентских страниц
- •Фронтенд доски объявлений — страницы списка, детали, написания/редактирования, редактор Summernote, прикрепление файлов перетаскиванием
- •Короткие URL доски — доступ к
/free, /notice/3 и т.д. без префикса /board/; автоматическое сопоставление slug из БД
- •Страница поиска записей — короткий URL
/lookup, поиск по номеру записи / e-mail / телефону
- •Страница деталей записи —
/booking/detail/{номер}, пузырь приветствия сотрудника, информация об услуге/оплате/отмене
- •Страница отмены записи —
/booking/cancel/{номер}, ввод причины отмены
- •Детали записи в личном кабинете —
/mypage/reservations/{id}, боковая панель + подробная информация
- •Многоязычная доска —
original_locale (язык первого черновика) + source_locale (текущий язык контента), цепочка запасных вариантов (source → en → original)
- •Приветствия сотрудников —
greeting_before (до процедуры) + greeting_after (после процедуры), многоязычные, отображаются в зависимости от статуса записи
- •Выпадающее меню профиля администратора — фото/инициалы + имя + ссылки на главную / личный кабинет / выход
- •Ссылка администратора в заголовке клиента — при входе под учётной записью администратора в меню отображается ссылка на панель администратора
Changed
- •Единый макет для всех клиентских страниц (индивидуальные HTML-структуры удалены, LayoutManager применяется автоматически)
- •Условие отображения категорий доски изменено с
show_category на hide_categories
- •URL доски изменён с
/board/free на короткий URL /free по умолчанию
- •«Admin» в верхней панели администратора заменён реальным именем + фото профиля
Fixed
- •Отсутствующий столбец
rzx_users.role → определение администратора теперь по $_SESSION['admin_id']
- •Отсутствующий столбец
rzx_users.nick_name → используется name + расшифровка через Encryption
- •
rzx_staff.profile_image → исправлено на имя столбца avatar
- •Преобразование типа int→string для
category_id в модели Service
- •Поиск записей по e-mail/телефону использует поиск по открытому тексту (данные без шифрования)
v1.6.1
2026-03-18 · 7 мес. назад
Added
- •6 вкладок администрирования форума — Основные настройки, Управление категориями, Дополнительные переменные, Права доступа, Дополнительные настройки, Скин
- •Консолидация вкладки основных настроек — Основная информация + SEO + Выбор макета + Выбор скина + Отображение + Список + Расширенные (сворачиваемые/разворачиваемые)
- •Общие компоненты — 8 файлов
components/board/section-*.php (basic, seo, layout-select, skin-select, display, list, advanced, js), переиспользуемые на страницах создания/редактирования
- •Компонент многоязычного ввода — функция
rzx_multilang_input(), режимы текста/редактора, встроенная кнопка глобуса
- •Расширенное управление категориями — древовидная структура (parent_id), модал редактирования (название/цвет шрифта/описание/разрешённые группы/раскрыть/категория по умолчанию), перетаскиваемая сортировка SortableJS, динамическая загрузка уровней участников из БД
- •Вкладка дополнительных переменных — таблица
rzx_board_extra_vars, 15 типов ввода (включая text_multilang, textarea_multilang, textarea_editor), многоязычная поддержка, перетаскиваемая сортировка
- •Вкладка прав доступа — Права доступа (all/member/grade:*/admin_staff/admin), администратор модуля (общий поиск пользователей, фото профиля, область прав), информация о уведомлениях по почте
- •Вкладка дополнительных настроек — Интегрированный форум, документы (история/рекомендовать/пожаловаться), комментарии (количество/глубина/одобрение/рекомендовать/пожаловаться), WYSIWYG-редактор (матрица прав), файлы (группа загрузки), фиды (RSS публично/авторское право)
- •Система скинов — формат
skin.json, SkinConfigRenderer, поддержка "multilang": true, карточка информации о скине
- •Система макетов — формат
layout.json, выбор карточкой в основных настройках
- •Общий поиск пользователей — компонент
user-search.php, шифрование/дешифрование, фото профиля, переиспользуемый везде
- •Администратор модуля — таблица
rzx_board_admins, права публикации/комментариев/настроек, подписка на уведомления
- •Расширение расширенных настроек — исключить администратора из анонимности, ограничение Data URL, злоупотребление Unicode, защита публикаций/комментариев (количество комментариев/ответов/период), защита супер-администратора, адрес электронной почты отправителя
- •UI переключателей — все элементы да/нет преобразованы в переключатели (основные + дополнительные настройки)
- •Ключи переводов — около 300 ключей, 13 языков
Fixed
- •Ошибка SQL в корзине — привязка
LIMIT ? строка → целочисленный литерал
- •Summernote не рендерится — загрузка jQuery/Summernote JS + инициализация после отображения модала
- •Ошибка сопоставления — явное указание COLLATE utf8mb4_unicode_ci при JOIN board_admins
- •Свернуть/развернуть не работает — функция
toggleSection определена первой в <head>
Changed
- •Отдельные вкладки (список/расширенные) → объединены во вкладку основных настроек со сворачиванием/разворачиванием
- •Выбор скина → перенесён во вкладку основных настроек; вкладка скина показывает только настройки
- •Почта администратора → изменена на "Адрес электронной почты отправителя" в расширенных настройках
- •Администратор модуля → перенесён во вкладку прав доступа
- •"Показывать категорию в списке" → удалено из настроек категорий, делегировано в настройки по скину
- •"Показывать категорию в списке" → удалено из настроек категорий, делегировано в настройки по скину
- •Группы разрешённых авторов → динамически загружаются из таблицы
rzx_member_grades вместо хардкода
v1.6.0
2026-03-17 · 7 мес. назад
Added
- •Многоязычная система резервного перехода: встроенная цепочка fallback в
db_trans() (настроенный язык → en → язык по умолчанию → source_locale → любой перевод)
- •Многоязычная кнопка в настройках доски: кнопка многоязычности (глобус) добавлена к 6 полям во вкладке основных настроек
- •Многоязычный fallback во фронтовом представлении доски: заголовок, описание, SEO, header/footer многоязычные применены в
_init.php
- •Многоязычный fallback виджета: улучшена обработка пустой строки в
WidgetRenderer::t(), tBtn()
- •Добавлены ключи перевода PWA (13 языков:
admin.pwa.update_available, update, later)
Fixed
- •Проблема пустой карточки виджета Features: оператор
?? обрабатывал пустую строку "" как допустимое значение → исправлено с помощью fallback на основе !empty()
- •Ошибка Mixed Content в продакшене:
APP_URL http→https исправлен + автоматическая коррекция протокола JS
v1.5.2
2026-03-17 · 7 мес. назад
Added
- •Удаление услуги из истории услуг POS (кнопка удаления + API + немедленное обновление DOM)
- •Добавлены ключи перевода удаления услуги на 13 языках
v1.5.0
2026-03-17 · 7 мес. назад
Added
- •Система регистрации на киоске: подтверждение услуги → данные клиента → создание записи → выдача номера очереди
- •Значок доступности сотрудника: показывает занят/доступен в реальном времени при выборе сотрудника на киоске
- •Модуль международных телефонных номеров: компонент phone-input применён в форме регистрации на киоске
- •Сид с примерами данных записей:
database/seed_reservations.php (10 записей: POS/киоск/онлайн)
- •Вынесены общие файлы киоска (
_styles.php, _bg.php, _scripts.php)
- •Добавлено 11 ключей перевода киоска на 13 языках
Changed
- •Выбор услуги на киоске: фильтрация по навыкам сотрудника (режим назначения)
- •Вкладки категорий киоска выровнены по центру
Fixed
- •Невозможность входа администратора в продакшене (повреждение хеша пароля — проблема подстановки переменной
$ в bash)
- •Стабилизация системы обновлений: расширен список сохранения, проверка работоспособности, синхронизация версии БД
- •Объединение единого источника версии (
version.json → index.php, автоматическая синхронизация БД)