MigxPageConfigurator

Интеграция вёрстки и управление контентом
Версия 2.5.66-rc
Дата выпуска 02.09.2026
Загрузки 27
Просмотры 4 289
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.

2.5.66-rc

  • ИСПРАВЛЕНО: узлы-объявления [data-mpc-remove] иногда не вырезались из секции и уезжали на фронт как готовая вёрстка. Удаление устроено как str_replace по сериализации DiDom, но к моменту вызова removeHiddenPlaceholders предыдущий проход (setPlaceholders) уже вписал Fenom прямо в позицию имени тега — ‹source{if $source.media} media="..."{/if} ...›. DiDom при ре-сериализации выбрасывает такой мусорный «атрибут», строка расходится с исходной, и замена промахивается: чаще всего страдали ‹picture› с несколькими ‹source›. Добавлен фолбэк: уцелевшие узлы вырезаются по самому тексту — от открывающего тега до парного закрывающего с учётом вложенности одноимённых тегов и void-элементов. Узел без парного закрывающего не трогается.

2.5.64-rc

  • ИСПРАВЛЕНО: установка пакета обнуляла политику доступа «Administrator». Резолвер прав (_build/resolvers/7permissions.php) читал поле data политики через json_decode(), хотя оно объявлено с phptype=json и xPDO отдаёт его уже массивом: декодирование массива возвращает null, поэтому политика сохранялась с одним лишь правом mpc_view, теряя все остальные (в MODX 2.8 их 181). Сайты этого обычно не замечали — админы работают под sudo или под другой политикой, — но любому не-sudo пользователю с политикой «Administrator» переставал открываться даже вход в админку. Теперь данные политики читаются как массив, а set() кодирует их сам.
  • ИСПРАВЛЕНО: право mpc_view исчезало из политики при первом же сохранении политики через админку. Оно выдавалось политике, но не заводилось как modAccessPermission в её шаблоне, а админка при сохранении пишет только права, известные шаблону, и молча вычищает остальные. Резолвер теперь сначала регистрирует mpc_view в шаблоне политики (шаблон берётся у самой политики, а не по имени — на сайтах с переименованным шаблоном поиск по имени промахивался), и лишь затем выдаёт право. При удалении пакета запись из шаблона убирается. ⚠️ Уже испорченную политику пакет не восстанавливает: он не знает, какие права там были. Штатный набор берётся из transport ядра MODX (core/packages/core/modAccessPolicy) либо с исправной копии сайта.

2.5.63-rc

  • ИСПРАВЛЕНО: лексиконные поля внутри файла чанка (data-mpc-chunk) выводились пустыми. В чанк каттер писал eager-форму ключа — выражение в одинарных кавычках, которое Fenom не интерполирует: у чанка нет своего eager-пасса, его тело компилируется целиком в момент parse/include, поэтому ключ оставался литералом и модификатор lexicon возвращал пустую строку. Теперь для файла чанка выражение откладывается целиком, как в статичных секциях внутри foreach: в чанк уходит переменная с модификатором lexicon, она резолвится в рантайме, когда лексикон уже загружен. Практический эффект: разметку mpcVE (data-mpc-field / data-mpc-item / data-mpc-field-N) теперь можно ставить и в кастомном выводе, вынесенном в чанк, — тексты остаются на месте, а поля становятся редактируемыми с фронта. Существующая вёрстка не затрагивается: перенарезка всех шаблонов проекта до и после правки дала нулевой дифф.

2.5.62-rc

  • ИСПРАВЛЕНО: правка через визуальный редактор уходила в чужой язык. Язык записи брался из cookie mpc_lang как есть, без проверки. На мультиконтекстном сайте cookie остаётся от предыдущего поддомена, поэтому правка чешской страницы (контекст с cultureKey=cs) ложилась в lexicon/en/: витрина изменений не показывала, а английский текст оказывался затёрт чешским. Теперь язык записи — cultureKey текущего контекста, а cookie учитывается, только если mpc её реально применяет (mpc_set_language_on_request=1) и её значение разрешено настройкой mpc_available_languages этого контекста — то есть ровно теми же правилами, по которым язык выбирается на витрине. Касается и полей ресурса/TV, и пер-полевой правки контактов.
  • ИСПРАВЛЕНО: язык по умолчанию (mpc_default_language) и список языков (mpc_available_languages) при записи читались только из глобальных настроек, без контекстного переопределения. На сайте, где у каждого поддомена свой язык по умолчанию, из-за этого неверно решалось, синхронизировать ли правку в остальные языки.

2.5.61-rc

  • ИСПРАВЛЕНО: атрибут media у тега source внутри video/audio не попадал в разметку секции. Шаблон строки {foreach} каттер строит по ОДНОМУ образцу — последнему source — и ставит плейсхолдеры только на те атрибуты, что есть на нём. У видео последний source это десктопный fallback без media, поэтому media выпадал из вывода для всех строк цикла, хотя грабер пишет его в данные каждой строки. Теперь набор атрибутов образца сводится по всем source элемента. У picture баг не проявлялся: там media обычно есть у всех source.
  • ИЗМЕНЕНО: необязательные атрибуты source (media и всё, что есть не у каждого source в вёрстке) выводятся условно — {if $source.media}media="..."{/if}. Пустой media="" это пустой media-query list, то есть «подходит всегда», и такой source перехватывал бы выбор у следующих.

2.5.60-rc

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

2.5.59-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.58-rc

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

2.5.57-rc

  • ИСПРАВЛЕНО: импорт лексиконов терял привязку вкладки к файлу, если имя файла (alias ресурса) длиннее 31 символа — жёсткого лимита Excel на имя листа. Экспорт имя обрезал, импорт сравнивал строго и не узнавал вкладку; каждую приходилось назначать вручную. Хуже того, алиасы с общими первыми 31 символом ("...-v-moskve" / "...-v-spb") давали одно имя листа плюс порядковый суффикс _1/_2, зависящий от порядка обхода файлов — то есть привязка была неоднозначна в принципе.
  • ИЗМЕНЕНО: имя вкладки считает одна чистая функция LexiconImport::sheetNameFor, общая для экспорта и импорта. Короткий rid идёт как есть; длинный или с запрещёнными Excel символами (: \ / ? * [ ]) превращается в «префикс + ~ + 7 hex sha1 от полного rid» — ровно 31 символ, детерминированно, без коллизий.
  • НОВОЕ: all-in-one XLSX содержит скрытый служебный лист __mpc — точную карту «вкладка → файл лексикона» (колонки sheet | rid). Импорт берёт адресата оттуда, поэтому переименование вкладки в Excel привязку не рушит. Лист скрытый, в данные не попадает и вручную выбрать его для импорта нельзя.
  • ИЗМЕНЕНО: резолв цели при импорте стал многоступенчатым — манифест (если файл лексикона ещё на месте) → точное совпадение с rid → Static → имя, посчитанное тем же sheetNameFor → старая выгрузка с простой обрезкой до 31 символа. Последняя ступень срабатывает, только если кандидат ровно один: при общем префиксе у двух алиасов вкладка честно уходит в ручной ремап, а не привязывается наугад. Ранее выгруженные файлы импортируются без переделки.

2.5.56-rc

  • НОВОЕ: событие mpcOnSanitizeFileName — точка расширения правил именования файлов и папок. Работает во ВСЕХ потоках записи обоих пакетов: грабер вёрстки, папка секции грабера, загрузка в редакторе mpcVE, загрузка по внешней ссылке, создание и переименование в файловом менеджере редактора. Проекту со своими правилами имён больше не нужно патчить код пакета — достаточно плагина. Параметры: name, sanitized (дефолт пакета), kind (file|dir), extension, directory, context; своё имя возвращается через returnedValues['name'], пустой ответ = «дефолт устраивает». Описание и пример — в readme.
  • НОВОЕ: единая точка нормализации MpcServices\Handlers\Support\FileName — политика имён пакета, вызов события и ОБЯЗАТЕЛЬНЫЙ security-постфильтр после него. До этого политика была задублирована в трёх местах с разным поведением (MediaDownloader, mpcVE MediaLibrary, mpcVE FileManagerHandler); теперь дубли сняты, все потоки ходят в одну точку, блок-лист исполняемых расширений — один на оба пакета. Имя, вернувшееся из плагина, всегда проходит зачистку: срез пути и '../', удаление управляющих символов, схлопывание точек (shell.php.jpg), лимит длины, блок-лист. Событие задаёт стиль имени, но не даёт обойти защиту.
  • ИЗМЕНЕНО: политика имён по умолчанию приведена к naming convention — kebab-case для файлов, snakecase для папок ('' в имени файла теперь уходит в дефис), ограничение длины базы 100 символов, предсказуемое fallback-имя вместо пустого. Затрагивает только НОВЫЕ файлы: ранее загруженные не переименовываются, чтобы не порвать ссылки в уже сохранённом контенте.
  • ИЗМЕНЕНО: транслитерация кириллицы больше не зависит от наличия ext-intl — сначала собственная таблица, intl только для остальной диакритики. Раньше результат отличался на серверах с intl и без ('наши' → 'nasi' против 'nashi'), а грабер дедупит уже скачанные файлы по имени: переезд на хостинг без intl тихо задвоил бы медиатеку.
  • ИСПРАВЛЕНО: RemoteMediaIngestor::store() игнорировал имя, изменённое плагином на OnFileManagerBeforeUpload, — вызывал нативное событие, но писал в createObject() свою переменную и от неё же резолвил финальный URL. Плагин, переименовывающий файл, не работал, а ссылка могла указывать не на тот файл. Теперь имя берётся из дескриптора после события и проходит единую точку.

2.5.55-rc

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

2.5.54-rc

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

2.5.53-rc

  • НОВОЕ: единый механизм скачивания медиа по URL — класс RemoteMediaIngestor (Handlers/Media). SSRF-гард, таймауты и лимит размера, content sniffing, запись в источник + РУЧНОЙ вызов нативных событий файлового менеджера (OnFileManagerBeforeUpload/OnFileManagerUpload). Используется и грабером вёрстки, и редактором mpcVE (вставил ссылку → скачалось).
  • ИЗМЕНЕНО: грабер MediaDownloader переведён на RemoteMediaIngestor. Теперь при нарезке срабатывают проектные плагины-конвертеры (напр. migxresizeonupload: jpg→webp), как при ручной загрузке. Дедуп учитывает целевой формат конвертера (thumbnailType источника) — повторная нарезка не качает заново.
  • НОВОЕ: настройка mpc_fire_upload_event (вкл/выкл нативных событий при скачивании по URL; default 1) — отключить, если конвертер в CLI нежелателен.

2.5.52-rc

  • ИСПРАВЛЕНО: для listbox/option-поля ВНУТРИ списка (MIGX) решение о лексиконизации и нормализации значения принималось по атрибутам КАЖДОГО элемента списка (data-mpc-values/data-mpc-ftype текущего item). Поле же определяется один раз — последующие items не должны его переопределять. Теперь решение (тип опций, множественность, @SELECT-динамика) фиксируется по ПЕРВОМУ элементу поля и применяется ко всем items (значение innerHtml остаётся своим у каждого). Решение кэшируется по пути схемы без индексов строк и сбрасывается на границе секции. Каттер не затронут — он и так режет по первому образцу item. Файлы: Handlers/Grabber/FieldValueExtractor.php, Handlers/Grabber/ContentParser.php. +1 тест (491 зелёный).

2.5.51-rc

  • ОПТИМИЗАЦИЯ: request-scoped singleton Mpc для фронт-чтения. Lexicon-модификаторы Fenom (reslexicons/lexiconsarr) и сниппеты (getstaticsection/mpccontacts/ getparsedconfigpath) зовутся per-resource (десятки раз на каталоге) и каждый раз создавали new Mpc() — а конструктор поднимает Grabber/Cutter/Render (пресеты, сэмплы, чтение конфигов). Теперь общий инстанс на запрос (Mpc::instance()). Инстанс ресурс-нейтрален (ресурс везде передаётся аргументом / берётся per-call из $modx-›resource / переустанавливается в Render::prepareResourceData), нарезка по-прежнему через отдельный new Mpc(). + кэш LexiconManager: rid → идентификатор лексикона за запрос (при lexicon_filename_field=alias убирает запрос+invokeEvent на каждый per-resource вызов). Файлы: Mpc.php, Handlers/Grabber/LexiconManager.php, Plugins/pdoToolsOnFenomInit.php, elements/snippets/snippet.{getstaticsection, mpccontacts,getparsedconfigpath}.php.

2.5.50-rc

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

2.5.49-rc

Темы оформления: один контент — разная вёрстка, переключение через настройки. Порт фичи с mpc3 (1.0.6-rc).

  • НОВОЕ: тема = оверрайд-слой вёрстки секций. Для секции рендер сначала ищет файл в подпапке темы (sections/‹mpc_themes_subdir›/‹тема›/‹секция›.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 ‹file|all› --theme=‹name›: исходник из templates/‹subdir›/‹name›/, выхлоп секций+wrapper в sections/‹subdir›/‹name›/. Запускается ТОЛЬКО Cutter, Grabber/Render пропускаются — контент общий, не дублируется/не перетирается. Mpc::process($f,$upd,$theme), handleThemeFile, Cutter::setTheme, SectionFileWriter::setSectionsBase.
  • НОВОЕ: CLI mpc theme set ‹name› [--template=ID] | clear [--template=ID] | status — переключение с чисткой parsed (для --template — только ресурсов шаблона). Смена настройки через админку чистит parsed автоматически (OnCacheUpdate).
  • БЕЗОПАСНОСТЬ: имя темы с разделителями/.. игнорируется; путь вне sections/ (кастомный file_name) темой не трогается.
  • Render: resolveTheme/applyTheme/applyThemeToBinding/parseThemeTemplates + currentTheme; правки getSectionChunkBinding/getWrapperTpl/prepareResourceData. +10 unit-тестов (488 зелёных).

2.5.48-rc

  • ИСПРАВЛЕНО: резолвер 9mediasource на апгрейде плодил дубль media-источника, если источник был переименован (искал только по name='mpcMedia'). Теперь сначала ищет по настройке mpc_media_source (id), затем по имени — новый создаётся, только если не найден никак.

2.5.47-rc

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

2.5.46-rc

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

2.5.45-rc

  • ИСПРАВЛЕНО: разворот ##{ в файлах чанков (введён в 2.5.44) кодировал { внутри data-mpc-* атрибутов в { (побочный эффект convertStaticHashToBrace) и ломал data-mpc-res="{$id}" — его {$id} должен резолвиться pdoResources при per-row рендере чанка (кросс-ресурс правка в редакторе). Теперь для чанков плоская замена ##{, не трогающая { в маркерах.

2.5.44-rc

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

2.5.43-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 сохраняет прежний смысл (префикс лексикона секции) — произвольные ключи только вне секций.

2.5.42-rc

  • ИЗМЕНЕНО: нарезка (mpc cut) больше не рапортует ложный успех, если резать было нечего. Раньше Cli::cut печатал «Нарезка выполнена» всегда — даже когда файл не найден/пуст. Теперь Mpc::process возвращает сводку (processed/ok/failed/messages), handleFile не гоняет cutter/render по отсутствующему файлу, а CLI рапортует успех только если реально нарезан хотя бы один файл (иначе — ошибка с причиной и ненулевой код выхода). Успешный вывод показывает число нарезанных файлов; для all при наличии — число с ошибками.

2.5.41-rc

  • ИСПРАВЛЕНО: резолвер 9mediasource создавал медиа-источник mpcMedia с пустым basePath (= корень сайта) → файл-менеджер mpcVE показывал весь сайт (core/, конфиги, manager/) — неудобно и небезопасно. Теперь источник заскоуплен на папку медиа (basePath/baseUrl = mpc_media_path, по умолчанию assets/components/migxpageconfigurator/media/), а mpc_media_path обнуляется при создании (путь живёт в источнике, иначе задвоился бы). Существующие установки не трогаются (источник пересоздаётся только если его нет).

2.5.40-rc

Управление установкой языка пакетом на OnHandleRequest.

  • НОВОЕ: настройка-выключатель mpc_set_language_on_request (по умолчанию Да). Вкл — пакетный плагин OnHandleRequest как раньше зовёт setLanguageSettings (cultureKey + cookie языка). Выкл — пакет язык не трогает; проект делает это сам (например, после switchContext), пакетная установка не мешает.
  • НОВОЕ: событие mpcOnBeforeSetLanguageSettings — вызывается в setLanguageSettings, передаёт available[]/default/cookieName. Плагин проекта через returnedValues может изменить набор языков, язык по умолчанию, имя cookie или пропустить установку (skip=true) — тонкий контроль без полного отключения пакета.

2.5.39-rc

  • НОВОЕ: настройка mpc_contacts_page_alias — псевдоним ресурса контактов. Пусто → как раньше (создаётся ресурс с псевдонимом contacts). Указан существующий ресурс → работаем с ним, шаблон НЕ меняем (если шаблона нет — создаётся «Контакты»); указан несуществующий → создаётся ресурс на шаблоне «Контакты». Создание ресурса контактов перенесено из vehicle в resolver (4systemsettings) — без лишнего ресурса-сироты при кастомном псевдониме. Дефолтный шаблон нового ресурса контактов — «Контакты».
  • НОВОЕ: тип-зависимая лексиконизация контактов. Настройка mpc_contact_lexicon_fields теперь принимает синтаксис «тип:поле» наряду с «поле»: запись «поле» переводит его у всех типов контактов, «тип:поле» — только у контакта этого типа (phone/email/social/address/...). Пример: «caption, address:value, address:fvalue» — caption у всех + значение только у адреса (соцсети/телефон не переводятся). Старые значения («caption» / «caption,value,...») работают без изменений.
  • ИСПРАВЛЕНО: per-блочный атрибут data-mpc-translate на контакте не действовал на вывод — грабер писал ключ в лексикон, но Cutter игнорировал атрибут и не ставил | lexicon (плейсхолдер отдавал сырое значение). Теперь Cutter учитывает data-mpc-translate симметрично граберу (единая точка решения Base::isContactFieldTranslatable).
  • ИЗМЕНЕНО: окно настроек при установке (setup-options) больше не показывается при ОБНОВЛЕНИИ пакета (только при первой установке) и ограничено по высоте (скролл внутри), чтобы не вылезать за пределы экрана.
  • ИСПРАВЛЕНО (упаковка): в пакет попадал только UI-лексикон default.inc.php. Раньше сборка паковала папку lexicon/ целиком → в дистрибутив утекали пер-сайтовые контент-лексиконы сборочного стенда (демо-телефоны/адреса контактов, переводы статик-блоков), которые грузились на каждой странице. Контент-лексиконы — артефакт грабера, генерируются на нарезке per-сайт.

2.5.38-rc

  • ИСПРАВЛЕНО (критично): установка/обновление пакета роняла сайт с Fatal error "Failed opening required '.../myclabs/deep-copy/.../deep_copy.php'". Причина: composer-генератор прописывал в files-autoload dev-зависимости (myclabs/deep-copy, phpunit Functions.php), которые сборка физически исключает из пакета; files-autoload грузится eagerly при vendor/autoload.php → require несуществующего файла → Fatal. Теперь _build/build.php перед упаковкой приводит services/vendor к боевому виду (composer dump-autoload --no-dev, с детерминированным fallback-вычищением files-autoload, если composer на сборочном хосте недоступен).
  • ИСПРАВЛЕНО: после установки/обновления ./console/mpc падал с "Permission denied" (MODX transport не сохраняет executable-бит, файл ставится как 0644). Добавлен resolver, восстанавливающий 0755 на console/mpc при install/upgrade.
  • ИСПРАВЛЕНО: data-mpc-unwrap не снимал обёртку чанка, если внутри был data-mpc-attr (особенно заметно при mpc_edit_mode — блок-обёртка целиком оставалась в файле чанка). Причина: замена data-mpc-attr оставляла голый {$attr} в позиции HTML-атрибута, на котором DiDom-ре-сериализация в unwrapBlock спотыкалась и str_replace промахивался. Порядок в SectionFileWriter::putToFile изменён: unwrapBlock → потом data-mpc-attr.

2.5.37-rc

  • Логирование (MpcServices\Helpers\Logging) переведено с файлов на mxLogger. Делегирует записи сервису mxLogger (если установлен); иначе молчит и один раз предупреждает в журнале. Затронуты все 3 точки создания логгера (Mpc, Handlers\Base, Processors\Base) — им передан $modx.
  • Новые настройки mpc_debug (вкл/выкл, включено из коробки) и mpc_log_level (минимальный уровень debug|info|warning|error, по умолчанию error; единый приёмник mxLogger, включено из коробки). Консистентно с mxlogger.min_level. Захардкожены в _build/elements/settings.php.
  • Тэги: базовые migxpageconfigurator + mpc; process_uid воронки по сессии. Числовые уровни Logging (DEBUG/WARN/ERROR) маппятся на debug/warning/error mxLogger. Сигнатура Logging::write сохранена (+ опциональный $tags). Файловый лог (logs/*.txt) убран.

2.5.36-rc

Единый image-конвейер: TV-картинки (data-mpc-tv на img) проходят обрезку/реформат и lazy-загрузку.

  • Cutter::replaceResourceMarkers: data-mpc-tv / data-mpc-rfield на ‹img› теперь проходит image-конвейер (thumb + lazy) симметрично секциям (setImgPlaceholder) и контактам (handleContacts) — раньше шёл сырым src мимо обрезки (единственное image-поле вне конвейера; причина была в РОУТИНГЕ, не в «нет размеров»). Ветка «реформат без кропа» (commonThumbParams, напр. q=90&f=webp): размеры w/h не извлекаются — значение TV это путь-строка, а не migx-структура; кроп по атрибутам тега отложен. Уважает data-mpc-nothumb / data-mpc-nolazy / data-mpc-thumb. Лексикон-форма TV остаётся без thumb (перевод по КЛЮЧУ ≠ обёртка ЗНАЧЕНИЯ в getThumb).
  • Тесты: 439 зелёных (+ testCutterRunsTvImageThroughThumbPipeline: TV-img → mpcThumb-вызов + data-lazy + заглушка).

2.5.35-rc

Контакты (язык/мёрж/иконки), лексиконы, настройки ClientConfig, секции, списки.

  • ContactUpdater: пер-полевая правка value больше не затирает caption/attributes (пополевой мёрж плейсмента); безключевой контакт получает ckey после присвоения data-mpc-key; image-поле icon (отдельная иконка) + class-fallback для attributes (иконка-классом: пустой элемент с class).
  • Язык контактов: правка из редактора пишет перевод в ТЕКУЩИЙ язык (cookie mpc_lang), а не в дефолтный (Grabber::handleContactsHtml + LexiconManager skipLexiconSync) — раньше правка en меняла ru и раскидывалась по всем языкам.
  • Cutter::handleContacts: пустой не-value cfield с class → плейсхолдер в атрибут class.
  • LexiconSync::syncKey: перевод нового поля (rfield/tv) не утекает в дефолтный язык — плейсхолдер берётся из дефолта, не из текущего; дефолт не попадает в pending.
  • InformationUpdater: эффективное значение + target (context/system) по факту контекстной записи; ClientConfig читается напрямую (cgSetting/cgContextValue — getOption в API не резолвит); запись в глобальную ClientConfig чистит ПУСТЫЕ cgContextValue (перекрывали дефолт пустотой → fallback на странице).
  • SectionProcessor: position секции заполняется при нарезке (порядок вёрстки), existing/drag/-1 в приоритете (рендер сортирует секции по position, -1 вверху).
  • ConfigFieldWriter: список не пустеет до 0 — последняя строка очищается как образец (иначе пустой список нельзя наполнить заново из редактора).
  • FieldWriter: writeRowOp inherit-aware (наследуемая секция → диалог копирования вместо «section not found»); снимок поля-списка для отката row-операций из истории (restoreRows); color/colorpicker content-type; icon в выводе контактов (Render).
  • Тесты: 438 зелёных (+ deleteLastRow, syncKey-leak, snapshot class-fallback).

2.5.34-rc

Контакты: ckey-identity, обёртка caption/attributes + unwrap; settingMeta; авторасширение cut.

  • ContactUpdater: при мёрже identity контакта = ckey (а не value). Смена значения keyed-контакта обновляет запись, а не плодит ДУБЛЬ с тем же ключом (был баг и в нарезке, и в правке из редактора).
  • Cutter::handleContacts: caption/attributes/value по умолчанию ОБОРАЧИВАЮТ плейсхолдер в свой элемент (раньше текстовый caption/attributes заменялся голым плейсхолдером → терялась обёртка ‹span class› и маркер data-mpc-cfield не доживал до edit-mode). data-mpc-unwrap на cfield → голый плейсхолдер (опт-ин, для value — только без href).
  • InformationUpdater::settingMeta(key, ctx) — значение + xtype настройки ИЗ БД (для типизированного редактора настроек mpcVisualEditor).
  • Base::getFileContent — авто-добавление расширения (cut wrapper находит wrapper.tpl), берётся из mpc_tpl_file_extension. CLI больше не «успех» при отсутствии файла без расширения.

2.5.33-rc

Пер-полевая запись контакта по HTML-фрагменту (для редактора).

  • Grabber::handleContactsHtml(html) + Mpc::saveContact(html) — прогон ОДНОГО контакт-фрагмента (data-mpc-contact + data-mpc-cfield) через ContactUpdater. Переиспользуется фронт-редактором mpcVisualEditor (contact/save) для правки контакта со страницы: вся логика identity (по data-mpc-key)/лексиконов/мёрж в TV «Контакты» — та же, что при нарезке. + сброс кэша.

2.5.32-rc

Публичный write-API служебных настроек (для редактора).

  • InformationUpdater::saveSetting(key, value, ctx) — переиспользуемая запись значения служебной настройки (data-mpc-info) в системную/контекстную/ClientConfig-настройку, с blacklist защищённых. Вынесена из handleInformation (грабер теперь зовёт её же). Используется фронт-редактором mpcVisualEditor (info/save) для правки настроек со страницы.

2.5.31-rc

Лексиконы при правке из редактора, очистка атрибутов значений, фикс data-mpc-info.

  • LexiconSync: правка НЕ дефолтного языка теперь гарантирует ключ во ВСЕХ языках (включая дефолтный) — раньше дефолт оставался без ключа и рендерил сырой mpc_resource_*. Новый seedDefaultOriginal: при первой лексиконизации поля в не-дефолтном языке прежний литерал колонки/TV сохраняется в словарь дефолтного языка (иначе оригинал терялся). FieldWriter (rfield/tv) вызывает seed перед перезаписью колонки ключом.
  • LexiconManager::sanitizeValue: перед strip_tags срезаются style, on*-обработчики и javascript:/vbscript:-URL. strip_tags оставлял ВСЕ атрибуты на разрешённых тегах → вставка из IDE тащила style в лексикон. Теперь разметка в лексиконе — только теги форматирования, без инлайн-оформления.
  • InformationUpdater (data-mpc-info, грабер при --upd): значение элемента берётся через text(), а не nodeValue (у DiDom-элемента nodeValue по DOM-спеке всегда null → в системную/контекстную/ClientConfig-настройку писалась пустая строка).
  • FieldWriter: публичный copyTypeSectionToResource (выборочное копирование одной секции из типа — для сайдбара mpcVisualEditor).

2.5.30-rc

Фикс: смена картинки в простом image-TV из редактора ломала шаблон (Fenom).

  • FieldWriter::writeTv: для простого image-TV (modTemplateVar.type === 'image') запись-значение migx ([{MIGX_id,src,alt,title,width,height}]) сводится к URL (.src) перед записью. Раньше JSON-массив писался в TV как есть, каттер выводил его сырьём в src="{$resource.tvs.‹name›}", и фигурная скобка валила Fenom при запекании parsed/ («Unexpected token ':' near '{"MIGX_id":'»). migximage/image+ (хранят JSON по дизайну) не затронуты — только точный тип 'image'. Серверный барьер в пару к фронт-фиксу editors/image.js (mpcVisualEditor 1.0.3). Файлы: services/custom/Handlers/FieldWriter.php.
  • build.php: services/ пакуется поэлементно — из transport-пакета исключены composer.phar (~2.6 МБ), tests/, logs/, .phpunit.result.cache и dev-зависимости vendor (PHPUnit и транзитивные); vendor сводится к рантайму (didom + openspout + автозагрузчик). Пакет 1.7 МБ → ~930 КБ.
  • docs/readme.txt переписан на разрешённый набор HTML-тегов и актуализирован под код 2.5.x.

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)‹field›, значение — в словарь (без 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 исправлен pThumbmpcThumb (встроенная обёртка над 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.

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