MigxPageConfigurator

Интеграция вёрстки и управление контентом
Версия 1.0.16-rc
Дата выпуска 07.08.2026
Загрузки 26
Просмотры 4 123
MigxPageConfigurator

Вступление

Компонент предназначен для повышения гибкости работы с контентом сайта. Позволяет ускорить интеграцию вёрстки с Modx Revolution.

Основные возможности:

  1. Автоматическое создание элементов сайта: шаблоны, ТВ.
  2. Автоматическая расстановка в вёрстке плейсхолдеров, вызовов сниппетов, чанков.
  3. Автоматическое создание файлов чанков и секций.
  4. Автоматическое заполнение контентом админки сайта.
  5. Централизованное редактирование вёрстки.
  6. Редактирование контента прямо на фронте (компонент mpcVisualEditor).
  7. Многоязычность через файлы лексиконов с переключением языка на лету.
  8. Встроенная ленивая загрузка изображений.
  9. Удобное управление контактами из админки.
  10. Декларативное управление настройками и сущностями из консоли (CLI).


Начало работы

Чтобы работать с компонентом было комфортнее рекомендую:

  1. Прочитать документацию на сайте https://docs.modx.pro (краткая справка и список изменений — в папке core/components/migxpageconfigurator/docs)
  2. Ознакомиться с примерами сущностей, которыми оперирует компонент, в папке 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]
,
[1]
, …), а не циклом, поэтому число элементов задаётся на нарезке и не меняется динамически (в т.ч. из фронт-редактора). С появлением произвольных имён полей секции (mpc 2.5.0) надобность отпала — вместо медиа-списка заводите нужное число обычных img/picture-полей с разными именами. Новые шаблоны на медиа-списки не делать; существующие продолжают работать.

Условный вывод. Для всех полей доступен условный вывод — для этого укажите полю атрибут 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 — флаг: подключить чанк из файла (
    {include "file:..."}
    ). Имя файла берётся из соседнего data-mpc-chunk.
  • data-mpc-parse — как include, но через parseChunk с параметрами; само значение атрибута — массив параметров (Fenom-литерал), имя чанка — из data-mpc-chunk.
  • data-mpc-attr — «отложенный» атрибут: строка
    data-mpc-attr="attr=val"
    целиком заменяется на
    attr=val
    . Нужен, чтобы Fenom/спецсимволы в атрибуте дошли до рендера невырезанными.

Примеры:

<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, где нужно указать тип контакта и расположение. Доступные типы:

  1. phone — телефон
  2. mail — email
  3. address — адрес
  4. social — соц. сеть
  5. map — карта
  6. worktime — время работы
  7. requisite — реквизит
  8. messenger — мессенджер

Контакты группируются по значению. Один контакт может иметь несколько мест размещения на странице (например в шапке и в подвале).
Расположение — это набор латинских символов, цифр и знака подчёркивания (например header и footer).
Так же для контакта можно указать ключ в атрибуте data-mpc-key. Ключ нужен для обращения к конкретному контакту. Ключ может содержать только латиницу, цифры и нижнее подчёркивание. Если ключ не указать, он будет сгенерирован автоматически. Ключ невозможно изменить из админки.

Данные контакта следует размещать в html элементах с атрибутами data-mpc-cfield. Доступны следующие поля контакта:

  1. caption — подпись
  2. 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.

1.0.16-rc

  • ИСПРАВЛЕНО: правка поля в окне MIGX могла не сохраняться — в конфиге лежало несколько tab'ов с пустым caption, они рисуются подряд, и в форме оказывалось несколько полей с одним name: правится верхнее, а сохраняется значение другого. Задваивал их сам мерджер апгрейда до появления позиционного сопоставления (MigxConfigMerger::mergeTabs) — по tab'у за апгрейд. Новых дублей он больше не создаёт, но накопленные оставлял в базе как «пользовательские tab'ы», и сами они не рассасывались.
  • ИЗМЕНЕНО: при мерже безымянный tab из базы, не несущий ни одного поля сверх уже собранных, отбрасывается — терять в нём нечего. Безымянный tab с собственными полями сохраняется как прежде: это пользовательский tab, а не наследие задвоения.
  • Итог: апгрейд пакета сам приводит задвоенные конфиги в порядок; отдельная чистка базы больше не нужна.

1.0.15-rc

  • ИСПРАВЛЕНО: настройка mpc_exclude_lexicons_filename не защищала картинки и видео — то есть ровно то, ради чего заведена. Список исключений читался только в ветке простых строковых полей (FieldWriter::shouldLexiconizeField), а медиа-ветка его не спрашивала вовсе: MediaLexiconMerger заводил ключи для src/srcset/alt/title безусловно. Расширение списка паттернов дыру не закрывало — до проверки просто не доходило дело.
  • ИСПРАВЛЕНО: ключ, попавший под exclude уже после того, как был заведён, продолжал принимать значения вечно. Существующий ключ переиспользовался без вопросов в трёх местах: resolveLeafKey (глубокий мерж picture/video/audio), mergeRecordWithLexicon (плоская img-запись) и ветка «ключ уже есть» в FieldWriter::applyLexiconToConfigValue.
  • ИЗМЕНЕНО: MediaLexiconMerger принимает третьим аргументом конструктора необязательный предикат fn(string $lexiconKey): bool — «ключ исключён» (FieldWriter передаёт LexiconManager::isExcluded). Предикат спрашивается и для нового ключа, и для существующего. Аргумент необязательный: без него поведение прежнее, внешние вызовы не ломаются.
  • Итог: поле под exclude не лексиконизируется ни при каких условиях — ключ не заводится, существующий не переиспользуется, путь остаётся в mpc_config литералом. Старые ключи вытесняются литералами по мере правок; массовой чистки лексиконов пакет не делает. Порт правки двойки 2.5.59-rc.

1.0.14-rc

  • ИСПРАВЛЕНО: при создании контекста копированием существующего (ядровой context/duplicate) копирование настроек пакета в новый контекст сыпало в лог ошибок «Duplicate entry '-mpc_default_language' for key 'PRIMARY'» — по записи на каждую настройку, которая в контексте уже есть от донора. copySystemSettingsToNewContext() делал безусловный newObject()->save(), то есть INSERT. Теперь существующие настройки пропускаются: значение донора (язык, список языков) осмысленно, а метод доносит только недостающие ключи. Порт правки двойки 2.5.58-rc.

1.0.13-rc

  • ИСПРАВЛЕНО: экспорт и импорт лексиконов не работали вовсе — процессоры остались на API openspout 3 (WriterEntityFactory, StyleBuilder, ReaderFactory::createFromType), тогда как в пакете стоит openspout 4, где этих классов нет. Любая выгрузка падала фаталом «class not found». Переведены на API 4: Row::fromValues, Style, ReaderFactory::createFromFile; ширины столбцов задаются на листе (в 3 это был метод writer'а на всю книгу).
  • ИСПРАВЛЕНО: импорт лексиконов терял привязку вкладки к файлу, если имя файла (alias ресурса) длиннее 31 символа — жёсткого лимита Excel на имя листа. Экспорт имя обрезал, импорт сравнивал строго и не узнавал вкладку; каждую приходилось назначать вручную. Хуже того, алиасы с общими первыми 31 символом ("...-v-moskve" / "...-v-spb") давали одно имя листа плюс порядковый суффикс _1/_2, зависящий от порядка обхода файлов — привязка была неоднозначна в принципе. Теперь имя вкладки считает одна чистая функция (общая для экспорта и импорта): короткий rid идёт как есть, длинный или с запрещёнными Excel символами превращается в «префикс + ~ + 7 hex sha1», ровно 31 символ.
  • НОВОЕ: all-in-one XLSX содержит скрытый служебный лист __mpc — точную карту «вкладка → файл лексикона». Импорт берёт адресата оттуда, поэтому переименование вкладки в Excel привязку не рушит. Ранее выгруженные файлы импортируются без переделки: резолв многоступенчатый (манифест → точный rid → Static → расчётное имя → старая обрезка, и только при единственном кандидате).
  • НОВОЕ: событие mpcOnSanitizeFileName — точка расширения правил именования файлов и папок (порт с ветки MODX 2). Работает во ВСЕХ потоках записи обоих пакетов: грабер вёрстки, папка секции грабера, загрузка в редакторе mpcVE, загрузка по внешней ссылке, создание и переименование в файловом менеджере редактора. Проекту со своими правилами имён больше не нужно патчить код пакета — достаточно плагина. Параметры: name, sanitized (дефолт пакета), kind (file|dir), extension, directory, context; своё имя возвращается через returnedValues['name'], пустой ответ = «дефолт устраивает». Описание и пример — в readme.
  • НОВОЕ: единая точка нормализации Migxpageconfigurator\Handlers\Support\ FileName — политика имён пакета, вызов события и ОБЯЗАТЕЛЬНЫЙ security-постфильтр после него. Имя, вернувшееся из плагина, всегда проходит зачистку: срез пути и '../', удаление управляющих символов, схлопывание точек (shell.php.jpg), лимит длины, блок-лист исполняемых расширений. Событие задаёт стиль имени, но не даёт обойти защиту. Блок-лист теперь один на оба пакета.
  • ИСПРАВЛЕНО: ссылка на скачанный по URL файл могла указывать не на тот файл. Ядро MODX 3 принимает дескрипторы загрузки ПО ЗНАЧЕНИЮ и отдаёт плагину на OnFileManagerBeforeUpload свою копию, а при upload_translit ещё и само зовёт filterPathSegment — переименование до пакета не доходило, и резолв финального URL искал файл под исходным именем. Теперь имя нормализуется ДО передачи ядру, а фактическое имя определяется по разнице снимков каталога.
  • ИСПРАВЛЕНО: транслитерация кириллицы зависела от наличия ext-intl ('наши' → nasi против nashi), а грабер дедупит файлы по имени: переезд на хостинг без intl задвоил бы медиатеку. Кириллица идёт по собственной таблице пакета.
  • НОВОЕ: событие mpcOnBeforeSaveExcel получает объект листа (sheet) до записи первой строки — позволяет настроить лист (например, заморозить шапку через SheetView). Порт с ветки MODX 2.

1.0.12-rc

  • ИСПРАВЛЕНО: произвольные лексиконы (data-mpc-lexicon="topic:key") в wrapper.tpl не граббились — вызов handleArbitraryLexicons был внутри блока «файл ≠ wrapper». Вынесли из wrapper-исключения: он от ресурса не зависит (топик в маркере), граббится для любого файла, включая обёртку. Гейт !fromPlugin сохранён.

1.0.11-rc

  • ИСПРАВЛЕНО: вложенные в include/parse-чанк чанки не заменялись на {include}/ {parse} в файле родителя (нарезались, но оставались сырой вёрсткой). Гейт в SectionFileWriter пропускал setIncludeChunks/setParseChunks, если сам элемент- чанк нёс data-mpc-include/-parse. Теперь обрабатываем всегда, корневой элемент пропускаем по имени чанка (self-skip).
  • EDIT-MODE: обёртку [data-mpc-unwrap] с маркером поля при mpc_edit_mode не снимаем (редактор не теряет адрес поля) + CSS [data-mpc-unwrap]{display:contents} в mpcVE. Прод-нарезка не изменилась.

1.0.10-rc

  • НОВОЕ: единый механизм скачивания медиа по URL — RemoteMediaIngestor (src/Handlers/Media). SSRF-гард, таймауты, лимит размера, content sniffing. На MODX 3 запись идёт НАТИВНЫМ uploadObjectsToContainer (flysystem пишет tmp и сам инвокает OnFileManagerUpload) — плагины-конвертеры срабатывают БЕЗ ручного события. Один сервис и для грабера вёрстки, и для редактора mpcVE.
  • ИЗМЕНЕНО: MediaDownloader переведён на RemoteMediaIngestor; дедуп учитывает целевой формат конвертера (thumbnailType источника).

1.0.9-rc

  • ИСПРАВЛЕНО: для listbox/option-поля внутри списка (MIGX) решение о лексиконизации/нормализации бралось по атрибутам каждого item, хотя поле определяется один раз. Теперь решение (тип опций, множественность, @SELECT) фиксируется по ПЕРВОМУ элементу поля и применяется ко всем items (значение остаётся своим). Кэш по пути схемы без индексов, сброс на границе секции. Каттер не затронут (режет по первому образцу). Порт с двойки. +1 тест.

1.0.8-rc

  • ОПТИМИЗАЦИЯ: request-scoped singleton Mpc для фронт-чтения (Mpc::instance()). Lexicon-модификаторы Fenom и сниппеты (getstaticsection/mpccontacts/ getparsedconfigpath) зовутся per-resource (десятки раз на каталоге) и каждый раз создавали new Mpc() с дорогим конструктором. Теперь один инстанс на запрос; инстанс ресурс-нейтрален, нарезка по-прежнему через отдельный new Mpc(). + кэш LexiconManager rid → идентификатор за запрос (alias: убирает запрос+invokeEvent на каждый вызов). Порт с двойки.

1.0.7-rc

  • ИСПРАВЛЕНО: для listbox/option-поля с динамическими опциями (data-mpc-values начинается с @SELECT) выбранное значение больше НЕ нормализуется — реальные значения @SELECT приходят из БД (id / алиас ресурса), и нормализация портила их (резала алиас). Значение @SELECT-поля грабится как есть; статические списки нормализуются по-прежнему. Порт с двойки. Handlers/Grabber/FieldValueExtractor.php.

1.0.6-rc

Темы оформления: один контент — разная вёрстка, переключение через настройки.

  • НОВОЕ: тема = оверрайд-слой вёрстки секций. Для секции рендер сначала ищет файл в подпапке темы (sections///.tpl), нет файла — базовая вёрстка sections/.tpl. Контент (mpc_config/лексиконы/медиа) темой НЕ затрагивается: меняется только резолв файла чанка на рендере. file_name в данных секции пишется как раньше (базовый путь) — тема накладывается на лету.
  • НОВОЕ: системные настройки mpc_theme (тема на весь сайт), mpc_theme_templates (JSON {"templateId":"тема"} — переопределение по шаблонам, приоритетнее глобалки), mpc_themes_subdir (папка тем внутри секций, дефолт _themes/).
  • НОВОЕ: wrapper (modTemplate.description = @FILE sections/wrapper.tpl) тоже темизируется с тем же fallback.
  • НОВОЕ: вёрстка темы ГЕНЕРИРУЕТСЯ нарезкой так же, как базовая — mpc cut --theme=: исходник читается из подпапки темы (templates///), выхлоп секций+wrapper пишется в sections// /. Запускается ТОЛЬКО Cutter (вёрстка), Grabber/Render пропускаются — контент (mpc_config/лексиконы/медиа) общий и не дублируется/не перетирается. Тема-оверрайд: в исходнике темы описывают только секции с иной разметкой, остальные на рендере берутся из базы (fallback). Mpc::process($file,$upd,$theme), Mpc::handleThemeFile, Cutter::setTheme, SectionFileWriter::setSectionsBase.
  • НОВОЕ: CLI mpc theme set [--template=ID] | clear [--template=ID] | status — переключение активной темы с чисткой parsed (для --template — только ресурсов шаблона). Смена настройки через админку чистит parsed автоматически (OnCacheUpdate).
  • БЕЗОПАСНОСТЬ: имя темы с разделителями/.. игнорируется (защита от выхода за пределы папки секций); путь вне sections/ (кастомный file_name) темой не трогается.
  • Render.php: resolveTheme/applyTheme/applyThemeToBinding/parseThemeTemplates + currentTheme; правки getSectionChunkBinding, getWrapperTpl, prepareResourceData. +10 unit-тестов (наложение темы, fallback, traversal, карта шаблонов, парсинг JSON).

1.0.5-rc

Админка лексиконов переведена с ExtJS на Vue 3 + PrimeVue из VueTools.

  • ИЗМЕНЕНО: CMP лексиконов (трёхпанельный грид языки/ресурсы/«ключ × язык» + экспорт XLSX/ZIP + импорт) переписан с ExtJS на Vue 3 + PrimeVue. Стек берётся из общего Import Map пакета VueTools (не бандлится) — бандл lexicons.min.js ~10 КБ. Контроллер index.class.php грузит ES-модуль + рантайм-проверка наличия VueTools (иначе понятное сообщение «установите VueTools»). Старый assets/.../js/mgr/ lexicons.js удалён. Процессоры lexicons/* без изменений.
  • ТРЕБУЕТ установленного пакета VueTools (modx-pro/vuetools) в менеджере.
  • Сборка фронта: Vite (assets/.../package.json, vite.config.js), externals vue/pinia/primevue/@vuetools.

1.0.4-rc

Порт с двойки: удаление настройки mpc_media_path + фикс дубля media-источника.

  • ИЗМЕНЕНО: удалена системная настройка mpc_media_path (в рантайме всегда пустая, реальный якорь базового пути медиа — basePath источника mpcMedia). Путь захардкожен в резолвере 9mediasource; грабер резолвит папки строго ОТНОСИТЕЛЬНО источника. Резолвер сносит старую настройку на install/upgrade. mpc_download_paths без функциональных изменений — описание уточнено.
  • ИСПРАВЛЕНО: резолвер 9mediasource на апгрейде плодил дубль media-источника, если источник был переименован (искал только по name). Теперь сначала по настройке mpc_media_source (id), затем по имени; новый создаётся только если не найден.

1.0.3-rc

  • НОВОЕ: сниппет mpcContacts — выборка контактов с фильтрацией по типу/плейсменту/ ключу (&type/&placement/&key) и рендером через чанк (&tpl); без &tpl — JSON. Параметры &outputSeparator/&limit/&toPlaceholder. Порт mpc2 2.5.46-rc.
  • ИЗМЕНЕНО: явный data-mpc-cfield="fvalue" грабится (href, иначе текст) и перекрывает авто-вставку fvalue=value (раньше fvalue в вёрстке игнорировался).

1.0.2-rc

  • ИСПРАВЛЕНО: в файлах чанков (data-mpc-chunk) отложенные плейсхолдеры ##…| lexicon} не разворачивались в {…}. Чанки вызываются через parse/include/сниппет и не проходят финальный ##{ Render (он обрабатывает только файлы секций), поэтому ## оставался сырым и лексиконный/отложенный плейсхолдер не резолвился. SectionFileWriter при записи файла чанка теперь применяет плоскую замену ##{ (не трогая { в data-mpc-* маркерах, иначе ломался бы data-mpc-res="{$id}"). Порт mpc2 2.5.45-rc.

1.0.1-rc

  • НОВОЕ: произвольные лексиконные ключи через маркер data-mpc-lexicon="topic:key" на НЕ-секционном элементе. На нарезке грабер вырезает содержимое тега (innerHtml, с инлайн-разметкой — после санитайза по mpc_allowed_tags) в файл топика {topic}.inc.php, а каттер ставит плейсхолдер {'key'|lexicon}. Топик опционален (без него — первый из новой настройки mpc_arbitrary_lexicon_topics). Ключ не привязан к секции/ресурсу/ТВ/контакту. Топики из настройки догружаются на рендере (Mpc::getLexiconFilenames), чтобы |lexicon резолвился на лайве.
  • НОВОЕ: настройка mpc_arbitrary_lexicon_topics (область mpc_lexicons) — список топиков для вырезки/скана произвольных ключей.
  • НОВОЕ: правка произвольного ключа из визуального редактора (FieldWriter type=lexicon + ArbitraryLexiconResolver): топик явный → только он, иначе скан списка; поиск текущий язык → дефолтный (создаём в текущем) → «Ключ не найден».
  • ВНИМАНИЕ: на элементе data-mpc-section атрибут data-mpc-lexicon сохраняет прежний смысл (префикс лексикона секции) — произвольные ключи только вне секций.

1.0.0-alpha

  • Первая сборка.

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