documentgenerator 26.200.0 Ломающее

Documentgenerator 26.200.0: плейсхолдеры в атрибутах DOCX больше не вычищаются, у DocxXml новые сигнатуры

3 мин чтения Устаревших API: 2
Узнавайте о ломающих изменениях Битрикса раньше, чем они доедут до прода: свежие разборы — в канале @bxmax_updates

Ломающее обновление

Удалены или изменены публичные API — прикладной код может перестать работать.

Генератор документов 26.200.0 заполняет DOCX обходом DOM вместо регулярки по всему XML, и плейсхолдеры в атрибутах разметки, кроме descr и name у картинок, теперь доходят до готового документа как текст. У Bitrix\DocumentGenerator\Body\DocxXml удалён protected-метод и сменились сигнатуры ещё двух, а установщик модуля переехал с install.sql на миграции main. В коробочный Битрикс24 версия вышла 2 сентября 2026 года, так указано в официальном канале «Битрикс24 changelog».

Проверьте готовые документы по своим DOCX-шаблонам

До 26.200.0 DocxXml::replacePlaceholders() прогонял preg_replace_callback по всему XML документа, а новая версия ищет плейсхолдеры только в текстовых узлах //w:t и в атрибутах descr и name у //w:drawing//wp:docPr. Плейсхолдер в descr или name заменяется пустой строкой.

Раньше clearPlaceholdersInAttributes() вырезал из XML плейсхолдеры, которых нет ни в w:t, ни среди картинок. В 26.200.0 тело метода сведено к return;, а сам он помечен @deprecated Attribute cleanup is not part of the supported DOCX placeholder surface. Вызов из normalizeContent() остался, замены у метода нет. Такие плейсхолдеры остаются в готовом документе как текст и уезжают пользователю вместе с файлом.

DocxXml::getPlaceholders() отдаёт только плейсхолдеры картинок и текстовых узлов, без остальных совпадений регулярки по XML.

Сгенерируйте по документу из каждого своего DOCX-шаблона и посмотрите, что осталось в фигурных скобках, например так: unzip -p contract.docx 'word/*.xml' | grep -o '{[^{}]*}'. Первыми проверяйте шаблоны, где плейсхолдеры стоят вне обычного текста.

Обновите наследников DocxXml

Если в /local/ есть класс, унаследованный от Bitrix\DocumentGenerator\Body\DocxXml, проверьте в нём три метода.

replaceRealByTemporaryPlaceholers(array $placeholdersMatches, DocxXml $document) удалён, опечатку в имени учитывайте при поиске. Вызов через parent:: упадёт с fatal error, а ваше переопределение станет мёртвым кодом, потому что модуль этот метод больше не вызывает. Вместо него есть replaceWhitelistedPlaceholdersByTemporaryPlaceholders(DocxXml $document, bool $includeImageAttributes = true). Готовые совпадения регулярки он не принимает и ищет плейсхолдеры в DOM сам.

processContentWithTemporaryPlaceholders() потерял четвёртый параметр bool $isPurgeEmptyValue = true. Лишний позиционный аргумент PHP отбросит без ошибки, так что сломается это тихо. Режим остался один. Фигурные скобки, которые не являются временными плейсхолдерами, остаются как есть, а временный плейсхолдер без ключа в $values заменяется пустой строкой.

getPlaceholders(): array теперь объявлен в самом DocxXml. У родителя Xml::getPlaceholders() типа возврата нет, поэтому переопределение без : array в наследнике DocxXml до 26.200.0 работало, а на новой версии даёт fatal error о несовместимой сигнатуре. Хватит дописать тип:

        <?php declare(strict_types=1);

namespace Vendor\Docs\Body;

use Bitrix\DocumentGenerator\Body\DocxXml;

final class ContractDocxXml extends DocxXml
{
    public function getPlaceholders(): array
    {
        $placeholders = parent::getPlaceholders();
        // ваша доработка списка

        return $placeholders;
    }
}

    

Для наследников в DocxXml появились protected-методы новой схемы: getTextPlaceholderNames(), getPlaceholderNamesFromXml(string $content), replaceNodeValuePlaceholdersByTemporaryPlaceholders() и getTemporaryPlaceholder(). К ним добавились константы TEMPORARY_PLACEHOLDER_CONTEXT_TEXT и TEMPORARY_PLACEHOLDER_CONTEXT_DOCPR_ATTRIBUTE.

Проверьте поля, исправленные вручную

В Bitrix\DocumentGenerator\Document сравнение ручного значения с вычисленным вынесли в private isOverrideKept(), его используют getValue() и getExternalValues(true). Скаляры и Value-объекты сравниваются как строки с normalizeValue($computedRaw, true). В docblock объяснено, что так Money, Date и похожие значения перестанут всегда считаться изменёнными.

Ручную правку поля, у которого вычисленное значение является массивом, getValue() раньше не применял никогда, потому что условие требовало !is_array($this->values[$name]). Теперь применяет, если она отличается от вычисленного значения (сравнение через !=). Поле-массив с ручной правкой после обновления начнёт отдавать эту правку. Ещё setFields() стал пропускать элементы, которые не являются массивами.

Уберите из своих скриптов install.sql модуля

Файлы install/db/mysql/install.sql, uninstall.sql и их pgsql-копии удалены. documentgenerator::InstallDB() и UnInstallDB() вызывают $this->installMigrations() и $this->uninstallMigrations($dropTables), это protected-методы CModule из main. В main 26.250.100 их ещё нет, в main 26.650.0 уже есть. Скрипты, которые читали install.sql модуля напрямую, перестанут работать.

Схема не менялась. Те же 15 таблиц, от b_documentgenerator_template до b_documentgenerator_actualize_queue, описаны в install/migrations/tables.php через CreateTableBuilder с прежними полями и индексами. В migration_config.json задан defaultTableName: b_documentgenerator_template и соответствие install/components → components, install/js → js.

Обработчики событий и агенты переехали из install/index.php в install/migrations/events.php и agents.php. Обработчиков три: main:onNumberGeneratorsClassesCollect через registerCompatible, rest:OnRestServiceBuildDescription и pull:OnGetDependentModule с sort 800. При удалении модуля в режиме ModuleUninstall вызов register превращается в unregister.

Агентов тоже три, все непериодические: Driver::installDefaultRoles() с интервалом 150, Service\ActualizeQueue::process(5) и Driver::installDefaultTemplatesForCurrentRegion() с интервалом 300. Первый запуск раньше задавался явно (+150 с, без задержки и +300 с). Теперь задержку выбирает Migration\Agent::doAdd() из main: 60 с при установке модуля, 15 минут при установке дистрибутива, 10 минут в облаке.

Мелочи и находки

  • Временный плейсхолдер раньше выглядел как {SystemHtmlValues<N>}, где N бралось из random_int(200, 10000). Новый выглядит как {SystemDocxValue<hex>} с bin2hex(random_bytes(16)) и проверкой, что такого плейсхолдера ещё нет ни в карте, ни в тексте документа.
  • Старый код склеивал XPath-запрос с текстом плейсхолдера ($matches[0]). Новый делает один запрос //w:t[text()[contains(.,"{")]] и разбирает значения узлов регуляркой.
  • В размножаемых блоках после каждого processMultiplyingBlock() добавлен saveContent(), а имена полей внутри блока берутся из DOM через getPlaceholderNamesFromXml() с кешем по контенту. Для пустой картинки (empty или ' ') узлы w:drawing удаляются из блока, раньше на место плейсхолдера картинки подставлялся маркер {__SystemEmptyImage}.
  • Docx::setNodes() существовал и раньше, а сохранять изменения начал только сейчас. Он нормализует внутренний документ, кладёт его контент обратно в zip и обновляет $this->content. Его вызывает анонимайзер модуля disk. Метод save() в disk/lib/integration/anonymizer/docxprovider.php сначала зовёт setNodes(), а потом process(). Внутри цикла по внутренним документам после addContentToZip() добавлен ещё $this->zip->close(), хотя addContentToZip() сама делает close() и open(). Повторного open() до следующей итерации нет.
  • Имена агентов в install/migrations/agents.php начинаются с \. Migration\Agent::doAdd() в режиме isDevMode() отвергает такие имена исключением 201 «Agent name should not start with backslash», но CModule::installMigrations() dev-режим не включает. Если переводите свой модуль на миграции, пишите имена агентов без ведущего слеша.
  • Controller\Template::add() пропускает список доступа через private filterAccessCodes(), который отбрасывает нестроковые и пустые коды и убирает дубли. Если после фильтра ничего не осталось, старые права шаблона не удаляются. Раньше непустой массив мусора приводил к deleteByTemplateIds() и попыткам add().
  • Компонент documentgenerator.templates передаёт фильтр региона в URL слайдера как ['REGION' => [$region['CODE']]] вместо ключа 'REGION[]'.

Устаревшие и удалённые API в этой версии

Символ Статус Чем заменять
Bitrix\DocumentGenerator\Body\DocxXml::clearPlaceholdersInAttributes() Deprecated —
Bitrix\DocumentGenerator\Body\DocxXml::replaceRealByTemporaryPlaceholers() Удалено Bitrix\DocumentGenerator\Body\DocxXml::replaceWhitelistedPlaceholdersByTemporaryPlaceholders()

Новая версия вышла — разбор уже в Telegram

В канале @bxmax_updates публикуем каждый свежий разбор: что сломается, что устарело и что появилось в API. Узнаете до того, как обновите прод.

Подписаться

Читайте дальше

documentgenerator 26.300.0 Безопасность Свежее

Documentgenerator 26.300.0: file.delete требует права на шаблоны и закрыт для REST

До 26.300.0 любой авторизованный сотрудник портала мог удалить файл генератора документов по ID через `documentgenerator.api.file.delete`, в том числе PDF и превью документов. Теперь экшен требует права на изменение шаблонов и принимается только из AJAX, `FileTable::delete()` не удаляет превью и PDF…

6 мин
documentgenerator 26.200.100 Ломающее Свежее

Documentgenerator 26.200.100: рисунки без файла больше не остаются в размноженных строках DOCX

Documentgenerator 26.200.100 перестал оставлять в разметке DOCX рисунки из размноженных строк, у которых файл картинки не существует или не читается. Если вы наследуете `Docx` и переопределяете protected `replaceImages()`, смените `: void` на `: array` и возвращайте ID связей на удаление, иначе полу…

1 мин
timeman 26.350.0 Заметное Свежее

Timeman 26.350.0: план дня пишется в нужную запись, блоки БП возвращают Saved

В timeman 26.350.0 для коробочного Битрикс24 блоки «Сохранить план на день» и «Сохранить отчёт сотрудника за день» из нового конструктора бизнес-процессов научились писать в заданную запись рабочего дня, оставлять Markdown как есть и сообщать, сохранилось ли (у отчёта новые настройки в конструкторе,…

2 мин
rpa 26.300.0 Безопасность Свежее

Rpa 26.300.0: права на перемещение элементов наконец проверяются

До RPA 26.300.0 `UserPermissions::canMoveItem()` сразу отвечал `true`, и любой сотрудник, кроме экстранет-пользователей, мог перетащить любой элемент на любую стадию, а права стадии на перемещение не учитывались. Теперь права проверяются, а тем, кто может только смотреть, `rpa.item.get` и `rpa.item….

3 мин
Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.
AI Домовой

AI Домовой История

на связи

пишет…
Нет истории чатов
AI Домовой

Нужна авторизация

Войдите, чтобы задавать вопросы AI Домовому.

Войти