Как загрузить?
<MigxPageConfigurator
Вступление
Компонент предназначен для повышения гибкости работы с контентом сайта. Позволяет ускорить интеграцию вёрстки с Modx Revolution.
Основные возможности:
- Автоматическое создание элементов сайта: шаблоны, ТВ.
- Автоматическая расстановка в вёрстке плейсхолдеров, вызовов сниппетов, чанков.
- Автоматическое создание файлов чанков и секций.
- Автоматическое заполнение контентом админки сайта.
- Централизованное редактирование вёрстки.
- Редактирование контента прямо на фронте (компонент mpcVisualEditor).
- Многоязычность через файлы лексиконов с переключением языка на лету.
- Встроенная ленивая загрузка изображений.
- Удобное управление контактами из админки.
- Декларативное управление настройками и сущностями из консоли (CLI).
Начало работы
Чтобы работать с компонентом было комфортнее рекомендую:
- Прочитать документацию на сайте https://docs.modx.pro (краткая справка и список изменений — в папке core/components/migxpageconfigurator/docs)
- Ознакомиться с примерами сущностей, которыми оперирует компонент, в папке core/components/migxpageconfigurator/examples
MPC 2.0
Служебная информация
К служебной информации можно отнести любые части шаблонов, которые есть на всех страницах и не зависят от ресурса (например фавикон, логотип, метрики и т.д.).
Служебная информация записывается в системные настройки или в настройки созданные с помощью компонента ClientConfig.
Если требуется записать настройку в конкретный контекст необходимо добавить атрибут data-mpc-ctx с указанием контекста или без значения, если нужно записать данные в текущий контекст.
Для обозначения служебной информации в шаблоне используется атрибут data-mpc-info с указанием ключа служебной информации. Из коробки доступны любые системные настройки.
Для добавления собственных ключей необходимо отредактировать конфигурацию MIGX с именем mpc_service_info.
Для служебной информации доступен вывод по условию. Чтобы задать условие используйте атрибут data-mpc-if, если не указывать условие — условием будет плейсхолдер поля.
Работа с секциями
Каждый шаблон состоит из секций. Секция может быть любым html тегом, но, как правило, это тег section или div.
Чтобы определить секцию нужно указать атрибут data-mpc-section с указанием ключа секции. Ключ секции должен содержать только латинские буквы, цифры и знаки подчёркивания (например test, main, header). Также для секции необходимо указать имя в атрибуте data-mpc-name. Имя необходимо для контент-менеджера, чтобы он по нему мог понять, что находится внутри. Если секция используется в нескольких шаблонах или несколько раз в одном шаблоне, то все копии следует отметить атрибутом data-mpc-copy, в значении рекомендую указывать название или путь к шаблону, в котором находится оригинал секции. Для копий необязательно использовать ту же разметку, что для оригинала.
Например это оригинал:
<section id="{$id}" data-mpc-section="third" data-mpc-name="Оригинал секции">
<div class="container">
<h1 data-mpc-field="title">Секция с простыми полями</h1>
<h2 data-mpc-field="subtitle">SubTitle</h2>
<div data-mpc-if="$content" data-mpc-field="content">
<p>Paragraph 1</p>
<p>Paragraph 2</p>
<p>Paragraph 3</p>
</div>
</div>
</section>Тогда копия может быть такой:
<section id="{$id}" data-mpc-section="third" data-mpc-name="Оригинал секции" data-mpc-copy="test.tpl">
<span data-mpc-field="title">Другой заголовок</span>
<span data-mpc-field="subtitle">Другой подзаголовок</span>
</section>Каждая секция состоит из набора полей, который определяется указанием атрибутов data-mpc-field.
Статичные секции. Секция может быть статической, т.е. отображаться с одинаковым контентом на разных страницах сайта. Чтобы сделать секцию статической, добавьте ей флаг data-mpc-static (без значения). Статичную секцию можно сделать обычной, а обычную статичной для отдельных ресурсов или для всех ресурсов с конкретным шаблоном. У статичных секций плейсхолдеры отложенные (символ
## вместо {), значения каскадятся от ресурса.Работа с полями секции
Поля бывают элементарные (img, picture, video, audio, title, subtitle, content, btn_text) и списочные (list_of_lists, list_images, list_triple, list_triple_pictures и т.д.). Разница между ними в том, что списочные поля состоят из других списочных и элементарных полей.
Начиная с mpc 2.5.0 имя поля может быть любым (произвольная латиница с цифрами и подчёркиванием), а не только из набора зарезервированных — тип такого поля задаётся атрибутом data-mpc-ftype (см. ниже).
DEPRECATED — медиа-списки (list_images, list_pictures, list_videos, list_audios): спец-имена для «массива однотипных медиа без ul/li». Появились, когда нужно было расставить картинки в разных местах, гдене подходил. Нарезаются ФИКСИРОВАННЫМИ слотами ({foreach},$list_images[0], …), а не циклом, поэтому число элементов задаётся на нарезке и не меняется динамически (в т.ч. из фронт-редактора). С появлением произвольных имён полей секции (mpc 2.5.0) надобность отпала — вместо медиа-списка заводите нужное число обычных img/picture-полей с разными именами. Новые шаблоны на медиа-списки не делать; существующие продолжают работать.[1]
Условный вывод. Для всех полей доступен условный вывод — для этого укажите полю атрибут data-mpc-if с указанием условия без оператора if.
Если атрибуту data-mpc-if не указать значение, то в качестве условия будет взят плейсхолдер поля. Например из такого шаблона:
<h2 data-mpc-field="subtitle" data-mpc-if>SubTitle</h2>получим вот такой результат:
{if $subtitle} <h2>SubTitle</h2> {/if}Limit и offset. Для списочных полей также доступно указание limit (data-mpc-lim) и offset (data-mpc-off).
Например такой шаблон:
<ul data-mpc-field="list_of_lists" data-mpc-lim="1" data-mpc-off="1">
<li data-mpc-item="">
<h5 data-mpc-field-1="title">Title1</h5>
<ul data-mpc-field-1="list_triple_img">
<li data-mpc-item-1>
<h5 data-mpc-field-2="title">Title12</h5>
<h6 data-mpc-field-2="subtitle">Subtitle12</h6>
<p data-mpc-field-2="content">Content12</p>
<img data-mpc-nolazy data-mpc-field-2="img" src="https://i.pinimg.com/736x/80/7d/2b/807d2b9987f0d35a1036b1597c3deb74.jpg" width="50" height="50" alt="Радуга">
</li>
</ul>
</li>
<li data-mpc-item="">
<h5 data-mpc-field-1="title">Title12</h5>
<ul data-mpc-field-1="list_triple_img">
<li data-mpc-item-1>
<h5 data-mpc-field-2="title">Title122</h5>
<h6 data-mpc-field-2="subtitle">Subtitle122</h6>
<p data-mpc-field-2="content">Content22</p>
<img data-mpc-field-2="img" src="https://i.pinimg.com/736x/f3/d9/2d/f3d92dcd1cd6f66daa196bfd255ac41d.jpg" width="50" height="50" alt="Радуга">
</li>
</ul>
</li>
</ul>преобразуется в такой чанк:
<ul>
{foreach $list_of_lists as $item1 index=$i1 last=$l1}
{set $c1 = 0}
{if $i1 >= 1 AND $c1 < 1}
{set $c1 = sum($c1, 1)}
<li>
<h5>{$item1.title}</h5>
<ul>
{foreach $item1.list_triple_img as $item2 index=$i2 last=$l2}
<li>
<h5>{$item2.title}</h5>
<h6>{$item2.subtitle}</h6>
<p>{$item2.content}</p>
<img src="{$item2.img[0].src}" width="{$item2.img[0].width}" height="{$item2.img[0].height}" alt="{$item2.img[0].alt}">
</li>
{/foreach}
</ul>
</li>
{/if}
{/foreach}
</ul>А пользователь увидит только второй элемент.
Максимум записей в списке. На контейнере списка можно ограничить число записей, которые контент-менеджер сможет добавить, атрибутом data-mpc-max с числом. Это пишется в migx-конфиг (maxRecords).
Имя, тип и подпись поля
Для автоматической генерации правильного поля в админке полю/TV можно дать дополнительные атрибуты:
- data-mpc-ftype — тип поля (имя MODX input-типа: listbox, number, date, checkbox, option и т.д.). Определяет также мультиопционность.
- data-mpc-fcap — подпись (caption) поля в админке.
- data-mpc-fdesc — описание поля в админке.
- data-mpc-values — список опций для listbox/option в формате migx
или выборкойПодпись==key||Подпись2==key2
.@SELECT ...
Например:
<select data-mpc-field="size" data-mpc-ftype="listbox" data-mpc-fcap="Размер" data-mpc-values="Маленький==s||Большой==l"></select>Поля ресурса и TV
Кроме полей секции компонент умеет работать напрямую с полями текущего ресурса и его TV.
- data-mpc-rfield — вывод поля ресурса. В значении — имя поля (pagetitle, longtitle, introtext и т.д.). На нарезке превращается в
(или ключ лексикона при включённой многоязычности), а контент грабится прямо в нативную колонку ресурса.{$resource.pagetitle} - data-mpc-tv — вывод TV ресурса. В значении — имя TV; если TV ещё нет, она будет создана автоматически (тип берётся из data-mpc-ftype, подпись/описание — из data-mpc-fcap/data-mpc-fdesc). Превращается в
.{$resource.tvs.subtitle} - data-mpc-res — флаг на поддереве: помечает, что внутри данные ЧУЖОГО ресурса. rfield/tv внутри такого блока каттер и грабер не трогают — контент туда пишет разработчик сам.
- data-mpc-rid + data-mpc-table — сменить источник значения поля: при data-mpc-table отличном от config поле читается из колонки ресурса с id из data-mpc-rid.
Например:
<h1 data-mpc-rfield="pagetitle">Заголовок страницы</h1>
<div data-mpc-tv="subtitle" data-mpc-ftype="textarea" data-mpc-fcap="Подзаголовок">Текст</div>Вставка элементов: сниппеты и чанки
В разметке можно сразу размещать вызовы сниппетов и подключение чанков.
- data-mpc-snippet — заменяет элемент на вызов сниппета. Значение
(пресет опционален). Префикс"имяСниппета|пресет"
— некэшируемый вызов.! - data-mpc-chunk — имя файла-чанка (используется вместе с data-mpc-include/data-mpc-parse); также помечает вложенный чанк для нарезки в отдельный файл.
- data-mpc-include — флаг: подключить чанк из файла (
). Имя файла берётся из соседнего data-mpc-chunk.{include "file:..."} - data-mpc-parse — как include, но через parseChunk с параметрами; само значение атрибута — массив параметров (Fenom-литерал), имя чанка — из data-mpc-chunk.
- data-mpc-attr — «отложенный» атрибут: строка
целиком заменяется наdata-mpc-attr="attr=val"
. Нужен, чтобы Fenom/спецсимволы в атрибуте дошли до рендера невырезанными.attr=val
Примеры:
<div data-mpc-snippet="pdoResources|news"></div>
<div data-mpc-include data-mpc-chunk="header.tpl"></div>
<div data-mpc-parse="['cls' => 'card']" data-mpc-chunk="card.tpl"></div>
<a data-mpc-attr="href={$resource.uri}">Ссылка</a>Дополнительные атрибуты вывода
- data-mpc-unwrap — флаг: вывести только содержимое (плейсхолдер/вызов), отбросив сам элемент-обёртку.
- data-mpc-symbol — переопределить первый символ Fenom-тега. По умолчанию
, для статичных секций{
. Пример:##
.data-mpc-symbol="##" - data-mpc-nolazy — флаг: отключить ленивую загрузку для конкретного изображения/фона.
Разметка текста в полях
По умолчанию любое значение поля при записи очищается от HTML (strip_tags). Какие теги разрешено сохранять — задаётся в системной настройке mpc_allowed_tags (через запятую). Пусто — вырезаются все теги.
Эта же настройка управляет тулбаром визуального редактора: кнопка форматирования показывается только для разрешённого тега. Чтобы появились кнопки ссылки/картинки, добавьте в настройку a и img. Дополнительные разрешённые атрибуты к безопасным дефолтам задаются в настройке mpcve_allowed_attrs.
Для разметки контента рекомендуется ограничиться набором: strong, em, u, s, ul/li, ol/li, blockquote, code, kbd, a.
Многоязычность (лексиконы)
Компонент умеет хранить значения полей не в самих полях, а в файлах лексиконов — это даёт перевод контента и переключение языка на лету без перенарезки.
- mpc_use_lexicons — главный переключатель: при включённом значения пишутся ключом в БД + переводом в файл лексикона.
- mpc_default_language — язык-источник (по умолчанию ru), из него берутся плейсхолдеры при синхронизации остальных языков.
- mpc_available_languages — все языки сайта (через запятую). При нарезке набор ключей всех неосновных языков приводится к текущему: новые ключи добавляются со значением-плейсхолдером, удалённые выкидываются.
- data-mpc-lexicon — на секции задаёт префикс ключей лексикона (он же мерж-ключ секции); пустое значение → имя секции. Различает оригинал и копию секции.
- data-mpc-translate — только для контактов: переопределяет список переводимых под-полей контакта (CSV), перекрывая настройку mpc_contact_lexicon_fields.
Работа с контактами и другой публичной информацией
Контакты сохраняются в ТВ с именем contacts у ресурса с шаблоном Контакты. Всё это задаётся в системных настройках.
Для добавления контактов используется атрибут data-mpc-contact, где нужно указать тип контакта и расположение. Доступные типы:
- phone — телефон
- mail — email
- address — адрес
- social — соц. сеть
- map — карта
- worktime — время работы
- requisite — реквизит
- messenger — мессенджер
Контакты группируются по значению. Один контакт может иметь несколько мест размещения на странице (например в шапке и в подвале).
Расположение — это набор латинских символов, цифр и знака подчёркивания (например header и footer).
Так же для контакта можно указать ключ в атрибуте data-mpc-key. Ключ нужен для обращения к конкретному контакту. Ключ может содержать только латиницу, цифры и нижнее подчёркивание. Если ключ не указать, он будет сгенерирован автоматически. Ключ невозможно изменить из админки.
Данные контакта следует размещать в html элементах с атрибутами data-mpc-cfield. Доступны следующие поля контакта:
- caption — подпись
- attributes — любые другие данные, например иконка
Важно: из-за особенностей работы компонента не используйте знак +, его можно заменить на %2B, но только не в контактах.
Так же в контактах не допускается использовать svg, эти теги просто не будут заменены на плейсхолдеры.
Генерация миниатюр
Компонент умеет генерировать миниатюры изображений с помощью сниппета pThumb. Сниппет устанавливается отдельно.
Вы можете указать свой в системной настройке mpc_thumb_snippet.
В системной настройке mpc_common_thumb_params можно указать параметры генерации миниатюр, ширина и высота подставятся из соответствующих атрибутов.
Если оставить эту настройку пустой, миниатюры генерироваться не будут. Отключить генерацию миниатюр для отдельного изображения можно добавив ему атрибут data-mpc-nothumb.
Через атрибут data-mpc-thumb можно задать индивидуальные параметры для конкретного изображения.
ВАЖНО: изображения в списках считаются одним целым, поэтому атрибуты data-mpc-nothumb и data-mpc-thumb следует указывать первому элементу, а применены они будут ко всем.
Для фоновых изображений (поле bg_img), которые заданы с помощью атрибута style, также доступна генерация миниатюр. При этом ширину и высоту следует указывать в атрибуте style.
ВАЖНО: каждое свойство должно заканчиваться знаком «;», иначе значение не будет считано.
Верная запись выглядит так:
<div class="container" data-mpc-field="bg_img" style="background-image: url('https://i.pinimg.com/736x/2b/d7/27/2bd7274a962e509da7dd8ed5b27549f7.jpg');height: 500px;width:1920px;"></div>Разворачивание SVG
Если в системной настройке указано значение атрибута и этот же атрибут указан тегу img, то при загрузке страницы тег img будет заменён на SVG из файла.
Картинки с атрибутом из системной настройки mpc_expand_attr игнорируются скриптом, который отвечает за ленивую загрузку.
Загрузка картинок
Если путь к картинке начинается с http и в системной настройке mpc_images_path указан путь к папке, то изображения будут загружены в эту папку при обработке шаблона.
К пути будет добавлено значение атрибута data-mpc-section, т.е. если в системной настройке указан путь /assets/images/ и картинки будут находиться в секции
data-mpc-section="first", то загружены они будут в папку /assets/images/first/.Как добавить поле в секцию и самостоятельно указать плейсхолдер?
Стандартные механизмы генерации плейсхолдеров достаточно универсальны, но всё же не покрывают 100% задач. Кроме того, кому-то может быть удобнее и привычнее расставлять плейсхолдеры и писать вызовы самостоятельно. В этом случае для создания полей в админке нужно внутри секции перечислить все необходимые поля, добавив им атрибут data-mpc-remove:
<section id="{$id}" data-mpc-section="first" data-mpc-name="Секция с простыми полями">
<span data-mpc-field="title" data-mpc-remove>Title</span>
<div class="container">
<h1>{$title}</h1>
</div>
</section>ВАЖНО: если вы обращаетесь к глобальным массивам
$_GET, $_SESSION, $_COOKIE или используете другие плейсхолдеры, которые будут доступны только непосредственно перед отдачей страницы пользователю, то начинать запись следует с ## вместо {. Например:<h1>Купить грибы в ##$.get.city}</h1>
##'msProducts' | snippet: ['parents' => 0, 'resource' => $.session.resources]} <!-- этот вызов полностью будет произведён перед отдачей на фронт -->
##'msProducts' | snippet: ['parents' => 0, 'resource' => $.session.resources, 'title' => '{$title}' ]} <!-- а в этот вызов будет передан параметр title, значение которому будет присвоено на этапе пререндера -->Визуальный редактор (mpcVisualEditor)
Размеченные компонентом страницы можно редактировать прямо на фронте — отдельным компонентом mpcVisualEditor. Редактор находит поля по тем же data-mpc-* маркерам и сохраняет правки по каждому полю отдельно.
Чтобы маркеры остались в готовых чанках (иначе редактору не за что зацепиться), на нарезке должна быть включена системная настройка mpc_edit_mode. Сам редактор подключается на фронт только когда одновременно
mpcve_active=1 И mpc_edit_mode=1.Для боевого деплоя mpc_edit_mode выключают и делают перенарезку — в файлы попадает чистый HTML без служебных атрибутов.
Управление из консоли (CLI)
Компонент умеет декларативно приводить админку к описанному в проектных манифестах состоянию — без ручного клика в админке. Тонкая обёртка — console/mpc, доступны группы команд: resources, plugins, configs, settings, clientconfig, packages, cut, cache, lexicon.
Подробности, флаги и формат манифестов — в core/components/migxpageconfigurator/console/README.md.
Системные события
mpcOnGetSectionFieldsValues — позволяет изменить получаемые из шаблона данные. Параметры:
- sectionKey — ключ секции, значение атрибута data-mpc-section
- fieldsValues — массив значений полей секции
- section — DOMElement секции
mpcOnHandleContact — позволяет изменить контактные данные. Параметры:
- contact — массив контактных данных, доступен как
$contact[0]
mpcOnBeforeDownloadFile — позволяет изменить имя файла перед загрузкой медиа (картинки/видео/аудио/прочее). Параметры:
- fileName — имя файла (без расширения); вернуть новое через
returnedValues['fileName'] - extension — расширение файла
- type — тип медиа (images/videos/audios/others)
- downloadPath — путь к папке загрузки внутри источника
- Grabber — экземпляр загрузчика
mpcOnBeforeRender — перед рендером ресурса. Параметры: resourceData (можно подменить через
returnedValues['resourceData']), Render.mpcOnBeforeParseConfig — перед разбором конфига секций. Параметры: sections (подмена через
returnedValues['sections']), Render.mpcOnGetSectionHtml — после сборки HTML секции при рендере. Параметры: section, html (подмена через
returnedValues['html']), Render.mpcOnGetNewHtml — при формировании нового HTML поля на нарезке. Параметры: fieldHTMLNew (подмена через
returnedValues['fieldHTMLNew']), PlaceholderProcessor.mpcOnFieldSave — после сохранения значения поля (в т.ч. из визуального редактора). Параметры: resourceId, address (уровень/адрес поля).
mpcOnGetLexiconKey — при вычислении ключа лексикона. Параметры: sectionLexiconPrefix, lexiconKey (подмена через
returnedValues['lexiconKey']), fieldName, Grabber.mpcOnImportLexiconValue — при импорте значения лексикона. Параметры: value (подмена через
returnedValues['value']).mpcOnGetResourceIdentifier — при вычислении идентификатора ресурса для ключей лексикона. Параметры: rid (подмена через
returnedValues['rid']), Grabber.mpcOnAddCellToExcel, mpcOnBeforeSaveExcel — хуки экспорта лексиконов в XLSX.
2.5.29-rc
Переключение языка, лексиконизация полей ресурса/TV из редактора, защита pagetitle.
- Переключение языка: имя cookie (mpc_lang_cookie_name) и домен (mpc_lang_cookie_domain) вынесены в системные настройки; JS-селектор и PHP теперь ставят cookie на один домен — фикс конфликта двух cookie на домене/поддомене. Список языков парсится с trim.
- Поля ресурса: pagetitle перенесён в protected (дефолты mpc_editable/protected_resource_fields) — через data-mpc-rfield больше не перезаписывается.
- Лексиконизация data-mpc-rfield/data-mpc-tv из редактора: при лексиконах в колонку/TV пишется ключ mpc_resource(tv), значение — в словарь (без per-resource гейта).
- Новый сервис LexiconSync: синхронизация ключей между языками + список непереведённых (.pending) — единый код для нарезки (LexiconManager::syncOtherLanguages) и редактора (FieldWriter). Перевод снимает ключ с pending, новый оригинал распространяется плейсхолдером по языкам.
- data-mpc-fcap/data-mpc-fdesc теперь работают и для TV (подпись/описание создаваемого TV).
- data-mpc-copy на чанке: файл не нарезается, только подключение (include/parse) — переиспользование чанка без зависимости от порядка/режима нарезки.
- Фон (bg_img): регулярка замены url() понимает любые кавычки/без и пробелы (раньше только одинарные при выключенном lazyload).
- Событие mpcOnFieldSave зарегистрировано (вызывалось, но не было в events.php); рассинхрон mpcOnBeforeDownloadImage → mpcOnBeforeDownloadFile исправлен.
- Каскад контентных полей ресурса от «типа страницы»: пустое поле наследует значение типа (whitelist mpc_editable_resource_fields).
- mpc_download_paths: подпапка по типу медиа относительно mpc_media_path. setup.options: + cookie языка / путь пресетов / лексиконные настройки.
- mpcVE: mpcve_active связан с mpc_edit_mode (предупреждение в лог, если редактор включён, но edit_mode выкл).
2.5.28-rc
Группировка системных настроек по областям + полная русификация.
- Все 49 системных настроек разложены по группам (area): пути, медиа, лексиконы, контакты, поля ресурса, общие — с префиксом mpc_ и русскими заголовками групп (areampc*), чтобы не путались с областями ядра/других пакетов.
- Лексикон настроек доведён: у каждой настройки есть название и описание (ru/en); заполнены ранее пустые описания (lazyload_enabled/expand_enabled/use_lexicons/available_languages/default_language/allowed_tags/allow_modx_tags).
2.5.27-rc
Чистка настроек и ретайр механизма create/.
- Аудит системных настроек: набор приведён к реально используемому в коде; на свежих установках лишние удаляются резолвером 6removesettings.php (image_extensions, service_info_tv_name, copy_config_tv_name, images_path, mime_to_ext, resource_lexicon_keys_path, path_to_create, thumb_format).
mpc_mime_to_ext(большой JSON в настройке) вынесена в файлelements/media/mime_to_ext.json; настройка теперьmpc_mime_to_ext_path(путь относительно core/). Убран дублировавшийся хардкод-JSON из Grabber.- Ретайр
mpc_resource_lexicon_keys_path+ файлаresource_lexicon_keys.inc.php+ ветки $_rlang в LexiconManager::createLexicons — роль закрыта автогенерацией ключей mpcresource* (ResourceFieldGrabber). mpc_exclude_fields_pathобъявлена в settings.php; Render читает путь относительно core/ (единый паттерн с mime/lexicon).mpc_thumb_formatретайрнута — была мёртвой (формат идёт изf=в mpc_common_thumb_params); убрано чтение в Cutter.- Ретайр механизма
create/: удаленыMpc::manageElement+ процессоры,console/mgr_elems.php, CLI-командаelements, настройкаmpc_path_to_create,examples/create/. Чем закрыто: TV → авто-провижнdata-mpc-tv; ресурсы →resources apply; сниппеты → файловые; плагины → новая CLI-группаplugins. - CLI: группа
eventsпереименована вpluginsи расширена —PluginsApply(заменилEventsApply) создаёт/обновляет плагин (код из файла, категория, static) и синхронизирует события. Манифестconsole/examples/plugins.example.php. Старый list-формат событий поддержан для совместимости.
2.5.26-rc
Фикс: дефолт настройки mpc_thumb_snippet.
- Установочный дефолт
mpc_thumb_snippetисправленpThumb→mpcThumb(встроенная обёртка над pThumb с проверкой существования файла). Прежнее значение указывало напрямую на pThumb в обход обёртки, расходилось с поставляемым сниппетомsnippet.mpcthumb.phpи тестами. Затрагивает только свежие установки. Файл:_build/elements/settings.php.
2.5.25-rc
Фикс: ресурсные лексиконы статичной секции перебивали page-types.
- Фикс каскада лексиконов. При сохранении ресурса (OnDocFormSave) секция, помеченная статичной, оставляла свои ключи в ресурсном lexicon-файле; на рендере resource-уровень перебивает type/page-types (Mpc::getLexiconFilenames грузит ресурсный файл последним) → значение «прилипало» к ресурсу после перевода секции в статику, игнорируя page-types. Теперь manageResourceLexicons вычищает ключи статичной секции (по её префиксу, array_diff_key симметрично filterByPrefix) из ресурсного массива перед перезаписью файла. Существующая порча лечится пересохранением ресурса. Полная перенарезка (mgr_tpl) багом не страдала. Файл: Plugins/OnDocFormSave.php.
2.5.24-rc
Setup-модалка при установке + кнопки копирования секций + чистка legacy.
- Модалка параметров при установке (build setup-options): использовать лексиконы, доступные языки, язык по умолчанию, путь к манифестам mpc CLI. Значения пишутся в системные настройки (resolvers/Asetupoptions.php, только при свежей установке).
- Кнопки «Дополнить / Перезаписать / Очистить секции» в тулбаре MIGX-грида mpc_config (замена TV-галочки copy_sections) — без патча vendored-migx. Грид обновляется без перезагрузки. FieldWriter::copySectionsFromType + processors/resource/copysections.
- mpc CLI: путь к манифестам в настройке mpc_manifests_path (резолв от core/), дефолты имён (apply без аргумента), обёртка console/mpc.
- TV img и copy_sections больше не создаются при установке (не нужны).
- Удалена настройка mpc_copy_config_tv_name (обслуживала убранную галочку).
- Фикс getContextSettingValue: context-настройка теперь override с fallback на глобальную (раньше затирала её на false) — глобальные настройки/модалка подхватываются на фронте.
- mpc CLI: работа с контекстными настройками (modContextSetting, per-key 'context') и ClientConfig (cgSetting/cgContextValue); settings/clientconfig list с фильтрами --namespace/--context/--group/--key, табличный вывод.
- Таблица mpc_tracked_fields теперь создаётся при установке через xPDO-схему (класс mpcTrackedField + 0schema-резолвер) вместо ленивого CREATE TABLE.
- Плагин: привязка OnBeforeDocFormSave добавлена в build-декларацию (нужна для админ-аудита правок ресурса).
2.5.23-rc
Сброс resource-кэша после нарезки (mgr_tpl) — переводы подхватываются без ручной очистки.
Баг: после mgr_tpl.php переводы не появлялись на сайте, пока вручную не сбросишь кэш из админки. Причина: грабер сбрасывает только lexicon_topics (LexiconManager::createLexicons), но НЕ resource-кэш — закэшированные страницы держат старые РЕЗОЛВНУТЫЕ значения лексикона (рендер выполняет {key|lexicon} и кэширует HTML). Mpc::process чистил кэш только при mpc_dev_mode, да и то лишь parsed-файлы (Render::clearCache), а не MODX resource-кэш.
Mpc::processв конце зовёт новыйrefreshSiteCache():cacheManager->refresh(['lexicon_topics' => [], 'resource' => ['contexts' => []]])— симметрично точечной записи редактора (FieldWriter::afterSave, который и так подхватывал переводы сразу). Контекст — инициализированный в mgr_tpl ($argv[1], по умолчанию web).Mpc::process— единственный вызов изconsole/mgr_tpl.php, так что сброс срабатывает ровно на пути перенарезки.- Регрессий нет: 290/290 тестов зелёные.
2.5.22-rc
Условный рендер булевых медиа-атрибутов + preload как строка (для редактора video/audio mpcVE).
HTML-boolean-атрибуты (controls/autoplay/loop/muted) включены самим ФАКТОМ присутствия — controls="0" всё равно включает контролы. Каттер рендерил их подстановкой значения (controls="{$rec.controls}"), из-за чего из фронт-редактора их нельзя было ВЫКЛЮЧИТЬ. Теперь рендерим условно.
Cutter\PlaceholderProcessor: булевы атрибуты (BOOL_ATTRS) вsetAttributesпомечаются сентинелом@@MPCBOOL@@, новыйrenderBoolAttrsпревращает его в{if $rec.attr}attr{/if}(firstSymbol{для обычных секций /##для static → резолвится на final-пассе). Так атрибут выводится только при truthy-значении;0его не печатает. Вызов — в концеsetMediaPlaceholder(булевы только на главном video/audio; source/img их не имеют). Лексиконизация/подстановка прочих атрибутов не затронута.Grabber\FieldValueExtractor::getMediaValue:preloadтеперь грабится какstring(перечислимый auto|metadata|none), а неboolean— иначе значение терялось (1/0) и рендерpreload="{$rec.preload}"давал мусор. Булевы (controls/autoplay/loop/muted) остаются 1/0 (presence) — совместимо с условным рендером ({if 1}→ есть,{if 0}→ нет), регрессии первого рендера нет.- ВАЖНО: чтобы булев атрибут стал управляемым из редактора, он должен присутствовать в разметке на нарезке (иначе
{if}не сгенерён — нечего включать/выключать). - Тесты:
PlaceholderProcessorTest::testSetPlaceholdersVideoBooleanAttrsConditionalRender(+условный рендер, нет сентинела/value-подстановки) — 290/290 зелёные.
2.5.21-rc
ЕДИНОЕ решение «лексиконизировать ли поле» для каттера, грабера и редактора.
Решение размазывалось и расходилось: config-поля каттер/грабер гейтили по content-type (shouldLexiconize/in_array), а rfield/TV — только по useLexicons + exclude, БЕЗ content-type. Итог: image-TV получал | lexicon (каттер) и ключ в колонке (грабер), хотя image нет в mpc_translated_content. Редактор же вообще решал по эвристике «есть ли ключ у соседей». Теперь ВСЕ идут через LexiconManager::shouldLexiconize($contentType, $field[, $parent]) (content-type ∈ mpc_translated_content + exclude_lexicons).
LexiconManager::contentTypeForTag($tag)— ЕДИНЫЙ маппинг тег→content-type (img/source/picture→image, video→video, audio→audio, иначе text). Один источник для каттера и грабера.Cutter::replaceResourceMarkers(rfield/TV):| lexiconставится только еслиshouldLexiconize(contentTypeForTag(tag), name). Было!isExcluded(name)без content-type.ResourceFieldGrabber::lexiconize(rfield/TV): тот же гейт. image-TV при настройке безimage→ путь в колонке, не ключ; exclude (pagetitle) сохранён.FieldWriter(редактор): решение тоже каноничное (shouldLexiconize) вместо прежней эвристикиfieldUsesLexiconKeys(удалена). content-type: config —inputTVtypeполя из mpc_base; TV —modTemplateVar.type; rfield —text. idx-less цепочка parentFieldName (как каттер); ключ для записи — idx-ful (getLexiconKeyForPath). Гейт применён и к rfield/TV-веткам редактора.- ВАЖНО: секции/ресурсы, нарезанные при ДРУГОЙ настройке
mpc_translated_content, рассинхронизированы с новым решением — нужна перенарезка (mgr_tpl). Отметить в документации пакета. - Тесты:
LexiconManagerTest(+contentTypeForTag),ResourceFieldGrabberTest(+image-TV не лексиконится; tdc-проп в фикстурах),FieldWriterTest(rfield/tv/скаляр гейтятся настройкой) — 289/289 (Unit+Snapshot) зелёные.
2.5.20-rc
Лексикон-ключи вложенных полей: единая КОНСТРУКЦИЯ parentFieldName (грабер+редактор).
- Раньше формат ЛИСТА ключа был единым (
LexiconManager::getLexiconKey), но КОНСТРУКЦИЯparentFieldNameдля вложенности (цепочка{field}/{field}_{idx}по уровням) жила только в грабере (ContentParser), а редактор (FieldWriter::makeLexiconKey) на вложенных полях бейлил в '' → значение писалось ЛИТЕРАЛОМ в конфиг → чанк{$field|lexicon}рендерил ПУСТО. Итог: у нового вложенного элемента картинка сохранялась (запись-ветка ключи генерит), а скалярные поля (title/subtitle/content) — нет. - Конструкция вынесена в
LexiconManager:appendLexiconParent($parent,$field,$idx)— один сегмент цепочки (грабер зовёт инкрементально при рекурсии);getLexiconKeyForPath($prefix,$path,$fieldName)— сворачивает путь и форматирует листом черезgetLexiconKey(редактор зовёт). Теперь ОБА идут через одну функцию — ключи не разъезжаются. ContentParser::parseHTMLпереключён наappendLexiconParent(вывод побайтово тот же — Snapshot-тесты грабера зелёные).FieldWriter::makeLexiconKey→getLexiconKeyForPath(вложенность любой глубины; убран бейл на path>1).- ПОБОЧНО исправлен ключ строк списка при idx>0: было
{prefix}_{field}_{name}_{idx}(неверно), стало{prefix}_{field}_{idx}_{name}_{idx}как у грабера (реальный лексикон:third_list_of_lists_1_title_1). ТестtestNewRowFieldGetsLexiconKeyприведён к верной схеме. FieldWriter::fieldUsesLexiconKeysРАСШИРЕН: ищет лексикон-ключ поля во ВСЕХ экземплярах списка (по всем индексам всех уровней, новый рекурсивныйlistFieldHasLexiconKey), а не только у соседей в КОНКРЕТНОЙ строке-списке. Без этого у НОВОГО верхнего элемента его вложенный список целиком пуст (deep-clone) → соседей-ключей нет → поле не лексиконизировалось → значение писалось литералом → после перезагрузки чанк{$f|lexicon}рендерил ПУСТО («значения сохраняются, но после reload пустые»). ТестtestNewNestedFieldGetsLexiconKeyFromOtherInstances.- Тесты:
LexiconManagerTest+2,FieldWriterTest+1 — 286/286 (Unit+Snapshot) зелёные.
2.5.19-rc
Row-операции по вложенным спискам (path) + deep-clone новой строки.
ConfigFieldWriter::mutateRows— поддержка ВЛОЖЕННЫХ списков:address.path= путь спуска к строке-контейнеру[{field,idx},…](НЕ включает целевой список),parentField= имя целевого списка в этой строке. ПереиспользуетmutateAtPath(декод JSON-строк по уровням). Для top-level списков path пуст (прежнее поведение). НовыйdescentPath()берёт путь ТОЛЬКО изaddress.path(для row-opsparentField+idx— не путь: idx тут операнд $fn). add/delete/move работают на любой глубине.ConfigFieldWriter::addRow— новая строка теперь deep-clone образца (blankRow/blankRows): скаляры → '', ВЛОЖЕННЫЕ списки → структура каждой строки СОХРАНЯЕТСЯ рекурсивно, значения чистятся. ВАЖНО: вложенный список внутри строкиContentParserхранит НАТИВНЫМ массивом (json-кодируется только верхний уровень поля), поэтому deep-clone ловит и нативные массивы строк-объектов, и JSON-строки (после правок через mutateAtPath). Иначе веткаis_array→[]затирала дочерний список нового элемента → «row not found in path for field img» при сохранении вложенного img. Прочие нативные массивы (sources, пустые) → [].looksLikeRows()детектит JSON-строку строк-объектов.- Тесты
ConfigFieldWriterTest: +6 (deep-clear строковой и нативной структуры, nested add/delete/move по path, ошибка пути, регрессия «img нового вложенного элемента») — 20/20 зелёные.
2.5.18-rc
Read-API лексикона: дамп карты key→value для показа ЗНАЧЕНИЙ в редакторе.
LexiconWriter::all(identifier)— полная картаkey=>valueлексикона ресурса (черезLexiconManager::getLexicons). Нужна визуальному редактору (mpcVE), чтобы панель скрытых полей показывала значение, а не ключ (иначе сохранение без правки записало бы сам ключ в значение — порча).FieldWriter::readLexicons(level, resourceId)— карта лексикона уровня (resource|global) для фронта; пусто, еслиmpc_use_lexiconsвыкл. Резолвит ресурс уровня (resolveLevelResource) + идентификатор файла. Read-only, без записи.
2.5.17-rc
Write-API: глубокий лексикон-мерж media-записи с sources (picture/video/audio).
FieldWriter::writeConfigField: record-значение с вложеннымimg(JSON-строка) и/или массивомsources→mergeMediaRecord(а не плоскийmergeRecordWithLexicon). Рекурсивно обрабатывает лексиконизируемые листья:img.src/alt/title,sources[k].srcset/src, верхнеуровневыеsrc/poster(video/audio). Существующий ключ → пишем значение в лексикон и сохраняем ключ; НОВЫЙ source/лист → генерим ключ единойgetLexiconKey({prefix}_{field}_source[_idx],_poster, …). Если новое значение РАВНО ключу — фронт прислал ключ (лист не меняли) → лексикон не трогаем (защита от затирания неизменённых источников отрендеренным thumb-URL). Хелперы:isMediaWithSources,mergeMediaRecord,mergeImgKeys,resolveLeafKey. ТестtestMergeMediaRecordPictureSources.- Версия пакета
2.5.16→2.5.17.
2.5.16-rc
Write-API: новая media-запись (картинка добавленной строки) получает лексикон-ключи.
FieldWriter::writeConfigField: для record-значения теперь различаем существующую запись (есть ключи →mergeRecordWithLexicon, как было) и НОВУЮ (recordHasLexiconKeys=false →newRecordWithLexiconKeys). Новая media-запись: для лексиконизируемых под-полей (src/srcset/alt/title) генерим ключи через единуюgetLexiconKey(idx=0 → без суффикса), пишем значения в лексикон, в запись кладём ключи;width/height— литералом. Безlexicon_prefixсекции → литерал. Нужно для загрузки картинки в только что добавленную строку media-списка (иначеsrc="{…|lexicon}"→ ''). Хелперы:sectionPrefix,recordHasLexiconKeys,newRecordWithLexiconKeys. ТестtestNewMediaRecordGetsLexiconKeys.- Версия пакета
2.5.15→2.5.16.
2.5.15-rc
Рефакторинг: генерация лексикон-ключа — ЕДИНАЯ функция (грабер + редактор).
LexiconManager::getLexiconKey→public static: единый источник формата ключа. Раньше редактор (FieldWriter::makeLexiconKey, 2.5.14) дублировал формат и РАСХОДИЛСЯ с грабером — клеил суффикс даже дляidx=0, тогда как грабер для idx=0 суффикс НЕ добавляет (gallery_list_imagesvs ошибочный…_0). На1-перенарезке это рассинхронило бы ключи редактора и грабера. ТеперьmakeLexiconKeyстроит толькоoptionsи зовёт общийgetLexiconKey. Вложенные новые поля (путь >1) ключ пока не генерят (редко). ТестtestGetLexiconKeyFormatфиксирует формат (idx=0 → без суффикса).- Версия пакета
2.5.14→2.5.15.
2.5.14-rc
Фикс: новое лексиконизируемое поле (напр. поле добавленной строки) сохраняется как ключ.
FieldWriter::writeConfigField: приuseLexicons, если поле НОВОЕ (текущее значение пустое/не-ключ) и реально лексиконится, редактор теперь ГЕНЕРИТ ключ лексикона (формат как у грабера:{prefix}_{parentField}_{field}_{idx}, prefix изlexicon_prefixсекции), пишет значение в лексикон ресурса, в конфиг кладёт КЛЮЧ. Раньше писался литерал → чанк{$field | lexicon}возвращал '' (модификатор на промахе ключа отдаёт ''), и значение «пропадало» после перезагрузки. Лексиконизируем только если поле лексиконится — сигнал: у соседних строк того же списка лежат ключи (fieldUsesLexiconKeys); иначе (литералы/нет соседей/top-level) — пишем литералом. Re-edit «застрявшего» литерала чинит его (заменяет на ключ). Тесты: +2 (testNewRowFieldGetsLexiconKey/…StaysLiteralWhenNotLexiconized).- Версия пакета
2.5.13→2.5.14.
2.5.13-rc
Write-API: запись/чтение поля строки ВЛОЖЕННОГО списка (путь любой глубины).
ConfigFieldWriter::setValue/getValue: адрес теперь поддерживаетpath— массив сегментов[{field, idx}, …]от секции к строке. Поле уровня 2 лежит на 2 уровня глубже (cfg[sec][L1][i][L2][j][field]) — раньше односегментный адрес писал в top-level поле (затирал одноимённое поле секции). НавигацияmutateAtPathдекодирует JSON-строки строк на каждом уровне.parentField+idx(1 уровень) — частный случай пути (back-compat). Тесты: +3 (set/get по пути, ошибка индекса).- Версия пакета
2.5.12→2.5.13.
2.5.12-rc
Write-API: структурные операции над строками списков (add/delete/move) для редактора.
ConfigFieldWriter:addRow/deleteRow/moveRow(PURE, черезmutateRows) — манипуляции массивом строк поля-списка в mpc_config. addRow добавляет пустую строку по структуре первой (MIGX_id=max+1); deleteRow/moveRow — splice + reindex. Значения строк (вкл. лексикон-ключи) едут ВМЕСТЕ со строкой → переводы не теряются при перестановке/удалении (orphan-ключ удалённой строки безвреден, чистится перенарезкой). Тесты: +4 (testAddRow…/testDeleteRow…/testMoveRow…).FieldWriter::writeRowOp(address)— обёртка: резолв уровня + load/save mpc_config + диспетч add|delete|move. Адрес: level/resourceId/section/parentField/op + idx (delete) / fromIdx,toIdx (move).- Версия пакета
2.5.11→2.5.12.
2.5.11-rc
Фикс: lexicon-мерж без updContent не тащит обратно ключи удалённых полей (orphan).
LexiconManager::createLexicons(preserve-ветка): сохраняем значение существующего перевода ТОЛЬКО для ключей, которые ещё есть в текущей нарезке (поле живо). Раньшеarray_merge($lexicons, $existing)возвращал в файл ключи удалённых полей — orphan-перевод оставался и при повторном добавлении поля маскировал новое значение (поле test_2 удалили→добавили с новым значением, а в лексикон писалось старое). Теперьforeachпо ключам нарезки: живое поле → значение сохраняем; удалённое → ключ выпадает; новое → значение из шаблона.- NB: уже «застрявший» orphan для ЖИВОГО поля чистится разовой перенарезкой с
1(или delete-поля + перенарезка) — отличить устаревший orphan от правки админа по состоянию файла нельзя. - Тест переименован/уточнён:
testCreateLexiconsPreservesLiveDropsOrphanWithoutOverwrite. - Версия пакета
2.5.10→2.5.11.
2.5.10-rc
Фикс: значения лексиконов в файле не перезаписываются без updContent (дополняет 2.5.9).
LexiconManager::createLexicons($allLexicons, bool $overwrite = true): безupdContentсуществующие переводы в lexicon-файле НЕ трогаются — дописываются только новые ключи (новых полей); значения админа сохраняются (array_merge($lexicons, $existing)— existing побеждает). СupdContent— прежнее поведение (шаблон перезаписывает, whitelist resource_lexicon_keys сохраняется). Раньшеarray_merge($tmp, $lexicons)всегда давал template-значениям перезаписать файл → переводы затирались приmgr_tplбез1.- Вызовы:
SectionProcessorпередаётupdContent;Grabber::createLexiconsпо умолчанию уважает$this->updContent. Контакты (ContactUpdater) уже гейтятся (handleContactsвыходит при!updContent) — без изменений. - Тесты:
testCreateLexiconsPreservesExistingWithoutOverwrite,testCreateLexiconsOverwritesWithFlag. - Версия пакета
2.5.9→2.5.10.
2.5.9-rc
Перенарезка без updContent = умный МЕРЖ конфига (а не «не трогать»). Уточняет 2.5.6.
SectionProcessor: значения секций теперь МЕРЖАТСЯ со старым конфигом (resource И static), а не перезаписываются/пропускаются.mergeSectionFields(grabbed, existing, reserved, overwrite):- без
updContent: служебные ключи (MIGX_id/id/section_name/…) — свежие из шаблона; контент-поле, что уже есть в конфиге → СОХРАНЯЕМ правку админа; новое поле → его контент из шаблона; поле, ушедшее из шаблона → удаляется; admin-поля настроек/стилей вне шаблона (hide_section/position/props…) — сохраняются. - с
updContent(1): шаблон перезаписывает контент (admin-поля настроек/стилей вне шаблона всё равно сохраняются).
- без
- Resource-конфиг теперь пишется ВСЕГДА (мерж со существующим по
MIGX_formname), а не только приupdContent. Static-блоки (updateStaticSectionValues) — мерж вместо голой замены. 2.5.6 (просто гейт по updContent) этим заменён. - Тесты:
mergeSectionFields(+2: preserve/overwrite),indexConfigBySection. - Версия пакета
2.5.8→2.5.9.
2.5.8-rc
Фикс: поля настроек/стилей mpc_base (inline_styles/class_names/…) не дублируются в контент-таб.
SectionProcessor::getSectionFields:data-mpc-fieldс именем, которое уже есть в mpc_base в НЕ-контентном табе (настройки/стили:inline_styles,class_names,css_file_path,props,hide_section,position…), больше НЕ добавляется в контент-таб секции — иначе появлялся дубль определения. Значение поля по-прежнему грабится (ContentParser), определение остаётся одно — в своём табе mpc_base. Реализация:collectReservedFieldNames(имена нон-контентных табов) + skip в getSectionFields. ТестtestCollectReservedFieldNames. Дополняет фикс 2.5.7 (там проверялся только контент-таб).- Версия пакета
2.5.7→2.5.8.
2.5.7-rc
Фикс: имя поля из mpc_base приоритетнее ftype (без дубликата определения).
SectionProcessor::makeFieldDef: проверка «имя уже есть в mpc_base → берём его определение» теперь БЕЗУСЛОВНА (раньше — только если не заданdata-mpc-ftype). Если поле с таким именем есть в mpc_base, ftype игнорируется (имя приоритетнее) — не плодим дубликат/конфликт. fcap/fdesc по-прежнему переопределяют подпись/описание. Тест:SectionProcessorTest::testNameInBaseBeatsFtype.- Версия пакета
2.5.6→2.5.7.
2.5.6-rc
Фикс: перенарезка БЕЗ updContent больше не перезаписывает контент статик-секций.
SectionProcessor::grabSection: граб значений статик-секции в конфиг статик-блоков (updateStaticSectionValues) теперь гейтитсяupdContent— как и resource-уровень (там записьmpc_configгейтилась, а статик-веткаsbpSectionValues— нет, поэтомуmgr_tpl web temp.tplбез1перезаписывал контент общих секций). БезupdContent: схема/вёрстка обновляются (createSectionConfig), значения сохраняются (sbp пишется обратно без изменений). С1— как раньше.- Версия пакета
2.5.5→2.5.6.
2.5.5-rc
Фикс фатала при перенарезке с лексиконами и нестандартным именем lexicon-файла.
LexiconManager::getResourceIdentifierById:$q->prepare()мог вернуть false (SQL не подготовился — напр.mpc_lexicon_filename_fieldуказывает на несуществующую колонку modResource), а код сразу звал$q->stmt->execute()→ «Call to a member function execute() on bool» (фатал на mgr_tpl/перенарезке). Теперь проверяемis_object($q->stmt)перед execute; при неудаче — фолбэк на числовой id +LOG_LEVEL_ERRORс подсказкой проверить настройку. Тест:LexiconManagerTest::testGetResourceIdentifierByIdFallsBackWhenPrepareFails. Латентный баг (не из 2.5.x), всплыл при включённых лексиконах сlexiconFilenameField != id.- Версия пакета
2.5.4→2.5.5.
2.5.4-rc
Публичный read-API конфига для редактора (mpcVE): правка СКРЫТЫХ полей.
FieldWriter::readConfig(level, resourceId)— read-only: возвращает декодированныйmpc_configуровня (resource/global/type). Нужен mpcVE для панели скрытых полей: поля, вырезанные из страницы (data-mpc-remove) или вспомогательные (напр.resources), DOM-маркера не имеют, но лежат в конфиге. ПереиспользуетresolveLevelResource+ имя TV (единый источник правды с записью); запись скрытого поля идёт прежним путёмwriteConfigField(address сparentField/idxдля под-полей строк). Тесты:FieldWriterTest(+3: decode секций, пустой конфиг, нет ресурса).- Версия пакета
2.5.3→2.5.4.
2.5.3-rc
Лексиконизация TV-полей (data-mpc-tv) — по аналогии с rfield, замыкает цепочку перевода TV (грабер → каттер → write-API). Отменяет ограничение из 2.5.1-rc «tv не трогается».
- Грабер (
ResourceFieldGrabber): TV-ветка теперь лексиконится так же, как rfield — в TV-значение пишется КЛЮЧmpc_resource_tv_, текст уходит в файл лексикона ресурса. Гейты те же:useLexicons+!isExcluded(поле изexcludeLexiconFields→ прямое значение в TV, без ключа). Методlexiconize()получил параметрkeyPrefix(rfield →mpc_resource_, tv →mpc_resource_tv_). - Отдельный неймспейс ключа
mpc_resource_tv_(а не общийmpc_resource_) — чтобы rfield и TV с ОДИНАКОВЫМ именем не делили один ключ лексикона (иначе перевод одного затирал бы другой). ТестtestRfieldAndTvSameNameUseDistinctKeysфиксирует это. - Каттер (
replaceResourceMarkers): получил параметрlexiconKeyPrefix; вызов для tv теперь сlexiconize=trueи префиксомmpc_resource_tv_→ рендерит{'mpc_resource_tv_' | lexicon}(без лексиконов / для excluded — прямое{$resource.tvs.}). - Write-API (
FieldWriter::writeTv): зеркалоwriteResourceField— в лексиконном режиме, если у TV есть ключmpc_resource_tv_, правка пишется в ЛЕКСИКОН (колонку/TV-значение не трогаем); иначе прямойsetTVValue(degradation, как у rfield). mpcVE-сторона (роутингtype:'tv'→ writeTv) уже была — фронт-редактирование TV теперь лексикон-aware без изменений в пакете mpcVE. LexiconManager/LexiconWriter/Render— без изменений (утилиты переиспользованы;$resource.tvsуже в скоупе рендера).- Тесты (+5, всего 255):
ResourceFieldGrabberTest(+3: tv-лексикон вкл, tv-exclude, rfield/tv без коллизии),CutterSnapshotTest(+1: tv →mpc_resource_tv_lexicon-форма),FieldWriterTest(+1: writeTv в лексикон при наличии ключа). Применяется ПЕРЕНАРЕЗКОЙ. - Версия пакета
2.5.2→2.5.3.
2.5.2-rc
Единый exclude для всех лексиконизируемых полей (включая rfield). Отменяет решение из 2.5.1-rc «отдельный exclude для rfield не вводим».
excludeLexiconFieldsтеперь действует и на поля ресурса (data-mpc-rfield), а не только на config-секции. Раньше rfield-путь лексиконизации (граберResourceFieldGrabber::lexiconize+ каттерreplaceResourceMarkers) гейтился ТОЛЬКО наuseLexiconsи игнорировал список исключений — поэтому лексиконились ВСЕ rfield, включаяpagetitle, который MODX читает из колонки напрямую (`/меню/крошки/дерево/поиск) и показывал бы там ключ вместо значения. Теперь оба места консистентно спрашиваютLexiconManager::isExcluded()(новый публичный фасад над приватнымisFieldExcluded): исключённое поле → грабер пишет в колонку ЗНАЧЕНИЕ (не ключ), каттер рендерит прямое{$resource.}(не| lexicon). Гейты rfield-лексиконизации:useLexicons+!isExcluded` (защищённые alias/uri/template по-прежнему отфильтрованы грабером раньше).pagetitleдобавлен в дефолтныйexcludeLexiconFields(services/exclude_lexicons.inc.php). Прочие нативные поля (menutitle/longtitle/description/…) — в project-specific exclude-файл (mpc_exclude_lexicons_filename) по необходимости (часто они как раз переводимы — в дефолт не кладём).- Тесты:
ResourceFieldGrabberTest(+testExcludedRfieldNotLexiconized: колонка=значение, лексикон пуст),CutterSnapshotTest(+testCutterExcludesNativeFieldsFromLexicon: pagetitle→{$resource.pagetitle}, content→| lexicon). Применяется ПЕРЕНАРЕЗКОЙ. - Версия пакета
2.5.1→2.5.2.
2.5.1-rc
Под фронт-редактирование (mpcVE): лексикон-aware запись полей, лексиконизация полей ресурса, кросс-ресурсные поля. Оставшийся роадмап редактора — core/components/mpcvisualeditor/docs/TODO.md.
- Лексикон-aware
FieldWriter::writeConfigField: в лексиконном режиме поле mpc_config хранит КЛЮЧ, а текст — в lexicon-файле. Теперь при записи через write-API: если ТЕКУЩЕЕ значение поля — ключ лексикона, новое значение пишется в лексикон (LexiconWriter), а ключ в конфиге сохраняется; для медиа-записи ([{src,alt,title,…}]) мерж по под-полям (лексиконныеsrc/alt/title→ их ключи обновляют лексикон, не-лексиконныеwidth/height→ литералом). Не-лексиконные значения иmpc_use_lexicons=0→ прямая запись в конфиг (прежнее поведение, деградирует само). Снята прежняя заглушкаlexiconized → not implemented. Фронт менять не нужно (текстовый и img-редакторы шлют литералы — бэкенд сам разруливает по наличию ключа). - Новый
LexiconWriter(services/custom/Handlers): точечный read-modify-write одной записи lexicon-файла{core}/{mpc_lexicon_path}/{culture}/{identifier}.inc.php(сохраняет остальные ключи; идентификатор/чтение/санитайз — изLexiconManager; culture =$_COOKIE['mpc_lang']?: cultureKey; cache-refresh best-effort). Тесты:LexiconWriterTest(has/set/escape/preserve),FieldWriterTest(+merge/isRecordValue). - Лексиконизация полей ресурса (
data-mpc-rfield). Раньше rfield по дизайну НЕ лексиконились (нативная колонка, рендер{$resource.field}) — поэтому не переводились. Теперь приmpc_use_lexicons=1цепочка замкнута в 3 местах: (1) грабер (ResourceFieldGrabber) кладёт в КОЛОНКУ ресурса КЛЮЧmpc_resource_(как config-поля хранят ключ в mpc_config), а значение — в лексикон-файл ресурса (в админке поле показывает ключ, не дубль значения). NB: нативные потребители колонки (MODX-`/меню/крошки/поиск) увидят ключ — для full-mpc сайтов, где заголовок рендерится через rfield, это ок; иначе заголовки рендерить через| lexicon; (2) каттер (handleResourceFields/replaceResourceMarkers) рендерит rfield как{'mpcresource' | lexicon}(без рантайм-фолбэка — как config-поля: есть лексикон →| lexicon, нет →{$resource.field}при выключенных лексиконах); (3)FieldWriter::writeResourceFieldпишет правку в лексикон-ключmpcresourceтекущего языка (колонку не трогает). Лексиконятся ВСЕ rfield (динамически из разметки; защищённые alias/uri/template отфильтрованы грабером, отдельный exclude для rfield не вводим).tvне трогается. Тесты:ResourceFieldGrabberTest(+2: лексикон вкл/выкл). Применяется ПЕРЕНАРЕЗКОЙ. Нюанс: нативная колонка не обновляется правкой перевода (MODX-меню/крошки берутpagetitle` напрямую) — фронт-контент через каттер показывает перевод. - Кросс-ресурсные поля:
data-mpc-res="{$id}"на ОБЁРТКЕ (поля ресурса, выводимого сниппетом — превью/аннотация статьи в листинге) → правка пишется в ТОТ ресурс. (1) strip-regex: +res. (2) Каттер (replaceResourceMarkers): если уdata-mpc-rfield/data-mpc-tvестьdata-mpc-resна самом элементе ИЛИ предке (hasAttribute+closest; DiDom closest не включает self) → каттер контент НЕ переписывает (continue), только маркеры остаются для редактора.handleResourceFieldsбежит по всему html ДО извлечения item-чанков, поэтому без этого он ломал бы авторскую разметку item-чанка сниппета — а рендер чужого ресурса (reslexicons +| lexicon, фолбэк) пишет автор, каттер этого не знает. (3)FieldWriter::writeResourceField— авто-роутинг цели: лексикон только если у целевого ресурса есть ключmpc_resource_(mpc-управляемый), иначе колонка (обычная статья из сниппета). (4) Грабер (ResourceFieldGrabber): поляdata-mpc-rfield/data-mpc-tvВНУТРИ обёрткиdata-mpc-resна грабе ИГНОРИРУЮТСЯ (isCrossResourceчерезclosest) — это разметка для редактора, их значения принадлежат другому ресурсу; иначе{$id}-разметка затирала бы поля текущего ресурс-типа. VE-часть (фронт читаетdata-mpc-res) — в пакете mpcVE. Применяется перенарезкой. - Тонкости (далее): поле, ПУСТОЕ при нарезке (нет ключа) и заполняемое в редакторе, пишется литералом (генерация нового ключа — отдельно); контактные/каскадные уровни проверить на сайте; рендер чужого ресурса через сниппет (reslexicons +
| lexicon+ фолбэк) пишет автор item-чанка. - Версия пакета
2.5.0→2.5.1.
2.5.0-rc
Фича «произвольные имена полей секции»: произвольное data-mpc-field имя теперь видно и редактируется в админке (S1 + S1.5 + S2 + GC). Фронтовая часть (mpcVisualEditor читает data-mpc-ftype для точного типа редактора) — в пакете mpcVE отдельно.
- [S1] Произвольные имена полей секции, видимые/редактируемые в админке (clone-by-type). Раньше каттер (
SectionProcessor::getSectionFields→deleteUndueFields) оставлял в конфиге секции ТОЛЬКО поля, заранее описанные вmpc_base— поле с произвольнымdata-mpc-fieldименем выкидывалось и в migx-форме админки не появлялось. Теперь имя разрешается в определение по ТИПУ-прототипу: тип задаётсяdata-mpc-ftype(имя прототипа вmpc_base), иначе дефолт по элементу (медиа-тегиimg/picture/video/audioи фон через inlinestyle→ свой типimg/picture/video/audio/bg_img; прочее →text). Существующие прототипы (img,list_*,picture,video,audio,bg_img) клонируются изmpc_baseс переименованием поля (sub-configconfigs/виджет сохраняются — списки/медиа/checkbox работают «из коробки»); скалярыtext/textarea/richtext/number/checkbox(Да==1)синтезируются в коде.caption=data-mpc-fcapлибо «{caption прототипа} (имя)»;description=data-mpc-fdesc. Если имя само совпало с прототипом иftypeне задан — берётся как есть (прежнее поведение). Новые authoring-атрибутыdata-mpc-ftype|fcap|fdescдобавлены в strip-regex каттера.mpc_baseНЕ менялся (чистка до реестра типов + миграция — S2; фронт читает ftype — S3). ТестSectionProcessorTest(puremakeFieldDef, +10 кейсов). Напоминание: применяется ПЕРЕНАРЕЗКОЙ секций (per-section конфиги пересобираются). - [S1.5] Динамические списки из произвольных имён. Если
data-mpc-field="X"— структурный список (естьdata-mpc-item), X нет вmpc_baseи не заданftype, каттер собирает row-конфиг migx ДИНАМИЧЕСКИ из полей строки (data-mpc-field-{уровень}, union по строкам с dedupe по имени) тем жеmakeFieldDefи сохраняет вmodx_migxпод детерминированным именемmpc_auto__(перенарезка апдейтит тот же конфиг); поле-список ссылается на него черезconfigs. Генеритсяformtabs+columns(картинки →this.renderImage, вложенные спискиshow_in_grid=0) + дефолтныйextended. Вложенные списки (data-mpc-field-2вdata-mpc-item-1и глубже) — РЕКУРСИВНО, что закрывает прежнее ограничение 2-го уровня. Предопределённыеmpc_list_*НЕ трогаются: имена изmpc_baseиftypeидут штатным путём (ничего на рабочем сайте не ломается). Медиа-списки формата «повторяющиеся теги без ul/li» (list_imagesи т.п.) — на прежних прототипах. GC осиротевших авто-конфигов: каждыйmpc_auto_*помечается владельцем (extended.mpc_owner= имя секции); при нарезке секции удаляются еёmpc_auto_*, не пересозданные в текущий проход (поле-список удалили/переименовали). Маркер-владелец исключает коллизии префиксов имён секций; чужие секции не трогаются (нарезка одной секции чистит только свои). Тесты:SectionProcessorTest(+4 кейса:autoListName,listFieldDef,buildColumns). - [S2] В
mpc_baseДОБАВЛЕНЫ прототипы скалярных типов:text(Текстовое поле),textarea(Текстовая область),richtext(Форматированный текст),checkbox(Флажок,Да==1),number(Число) — content-таб, pos 50–54. Ничего не удалялось (старые именованные поля целы → рабочий сайт не ломается). Теперьftype="text|textarea|richtext|checkbox|number"клонирует реальный прототип изmpc_base(синтез в коде остаётся фолбэком, если прототипа нет). Имена безmpc_-префикса (чистыйftype="text");MigxConfigMerger(union) добавит их в БД при апгрейде, кастомные поля сохранит. Для применения сида к БД БЕЗ переустановки — новая консольная командаconsole/mgr_configs.php sync(грузит сид, мержит черезMigxConfigMerger, сохраняет; как резолвер2migxconfigs). - Попутно: исправлен бутстрап
console/mgr_configs.php(MODX_CORE_PATHопределялся черезstr_replace('/','\\')→ бэкслэши, ломал require на Linux; приведён кdirname(__FILE__,4).'/'как вmgr_tpl.php). - Версия пакета
2.4.17→2.5.0.
2.4.17-rc
- Фикс регрессии 2.4.16: нормализатор
quoteSnippetParamValuesломал валидные выражения-параметры в скобках. Значение вида'input' => ('hero_bg_img' | lexicon)(mpcThumb,PlaceholderProcessor:524) начинается с(, а нормализатор: (1) не считал(за выражение и (2) читал значение до первой,без учёта вложенности/кавычек → обрезал и оборачивал('hero_bg_img'в кавычки →'('hero_bg_img'→Unexpected token 'hero_bg_img'на парсингеparsed/.tpl. Исправлено:readValueEndчитает ПОЛНОЕ значение с учётом вложенных() [] {}и строковых литералов;normalizeValueквотирует ТОЛЬКО простое голое слово (^[\p{L}\p{N}_.\/-]+$— резолв eager-плейсхолдера), а выражения ((…),$…,{…},@…), уже квотированное, числа/булево/null оставляет как есть; вложенные массивы нормализуются рекурсивно. Тесты:RenderTest(+4 кейса: скобочное выражение с внутренними кавычками, скобки+голое слово, выражение с операторами?:, и пр.). Перенарезка не требуется — фикс на рендере.


Последние обсуждения в сообществе MODX.pro