visualeditor 26.0.0 Заметное

Visualeditor 26.0.0: редактор HTML-страниц, который подключают своим кодом

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

С обновлениями пришёл новый модуль «Визуальный редактор» (visualeditor). Он открывает HTML-страницу сайта вместе с её стилями. Выбрали элемент, поправили текст, ссылку или картинку, и правка ложится точно в исходный файл, остальной код не переформатируется.

Модуль доступен и в «Управлении сайтом», и в коробочном Битрикс24, но сам ничего не включает. Это готовый безопасный редактор без хранилища и прав. Какие страницы открывать и кому их править, решает ваш код.

Новое

Установите модуль из списка обновлений

В списке обновлений модуль помечен как «Новый». Отметьте его и установите вместе с остальными. Установщик копирует JS в /bitrix/js/visualeditor/ и регистрирует модуль, таблиц и настроек нет.

После установки в админке не появится ни страницы, ни пункта меню, ни компонента. Штатные модули коробки редактор пока не используют: ни один не регистрирует для него адаптер.

SDK редактора уже внутри

В модуль целиком входит Visual Editor SDK 1.15.0. Он показывает список страниц сайта, открывает выбранную с ресурсами сайта и даёт кликнуть по элементу. Дальше правка текста, атрибутов и изображений, отмена правок, предпросмотр на весь экран. Для HTML-презентаций есть навигация по слайдам, для ИИ — кнопка «Спросить ИИ». Интерфейс переведён на десять языков, русский среди них.

Напишите адаптер к своим страницам

Где лежат документы и кто их правит, модуль не знает. Это знает адаптер — ваш класс с интерфейсом EditorAdapterInterface. В нём три метода, list(), read() и save(), и каждый возвращает Bitrix\Main\Result.

Пример: маркетологи правят промостраницы из папки /promo/, доступ только у администраторов.

        <?php

declare(strict_types=1);

namespace Local\Promo;

use Bitrix\Main\Engine\CurrentUser;
use Bitrix\Main\Error;
use Bitrix\Main\IO\Directory;
use Bitrix\Main\IO\File;
use Bitrix\Main\Result;
use Bitrix\VisualEditor\Public\Adapter\EditorAdapterInterface;

final class PromoPagesAdapter implements EditorAdapterInterface
{
    private const DIR = '/promo/';

    public function list(array $context): Result
    {
        if (!$this->canEdit()) {
            return $this->denied();
        }

        $paths = [];
        foreach ((new Directory($this->root()))->getChildren() as $item) {
            if ($item instanceof File && $item->getExtension() === 'html') {
                $paths[] = $item->getName();
            }
        }

        return (new Result())->setData(['paths' => $paths, 'truncated' => false]);
    }

    public function read(array $context, string $path): Result
    {
        $file = $this->file($path);

        return $file === null
            ? $this->denied()
            : (new Result())->setData(['content' => $file->getContents()]);
    }

    public function save(array $context, array $changeSet): Result
    {
        $file = $this->file($changeSet['path']);
        if ($file === null) {
            return $this->denied();
        }

        return $file->putContents($changeSet['content']) === false
            ? (new Result())->addError(new Error('Файл не записан', 'write-error'))
            : new Result();
    }

    private function file(string $path): ?File
    {
        if (!$this->canEdit() || preg_match('/^[a-z0-9-]+\.html$/D', $path) !== 1) {
            return null;
        }

        $file = new File($this->root() . $path);

        return $file->isExists() ? $file : null;
    }

    private function canEdit(): bool
    {
        return CurrentUser::get()->isAdmin();
    }

    private function root(): string
    {
        return $_SERVER['DOCUMENT_ROOT'] . self::DIR;
    }

    private function denied(): Result
    {
        return (new Result())->addError(new Error('Нет доступа', 'access-denied'));
    }
}

    

Пример проверен на «Управлении сайтом» с модулем 26.0.0: правка заголовка записалась в файл, путь с ../ отклонён, повторное сохранение со старым хешем вернуло stale-version.

Модуль проверяет ответы адаптера: list() обязан вернуть paths и truncated, read() — ключ content. В save() приходят готовые байты. Модуль сам перечитал файл через read(), применил правки и положил результат в $changeSet['content'], адаптеру остаётся записать.

$context и путь приходят из браузера как есть. Контроллер требует только авторизацию, POST и CSRF, так что любой авторизованный пользователь может вызвать visualeditor.editor.read с вашим ID адаптера. Права и белый список путей проверяйте в каждом методе, как в file() выше.

Зарегистрируйте адаптер и откройте редактор

Адаптер регистрируют в обработчике события visualeditor:onRegisterEditorAdapters. Рекомендуемый ID — <модуль>.<ресурс>. Повторная регистрация того же ID бросит исключение.

        use Bitrix\Main\Event;
use Bitrix\Main\EventManager;
use Local\Promo\PromoPagesAdapter;

EventManager::getInstance()->addEventHandler(
    'visualeditor',
    'onRegisterEditorAdapters',
    static function (Event $event): void {
        $event->getParameter('registry')->register('local.promo', new PromoPagesAdapter());
    },
);

    

Класс адаптера подключите автозагрузкой. На странице редактора, доступной только авторизованным, загрузите расширение и вызовите VisualEditor.mount():

        <?php \Bitrix\Main\UI\Extension::load('visualeditor.editor'); ?>
<div id="promo-editor" style="height: 80vh"></div>
<script>
    VisualEditor.mount(document.getElementById('promo-editor'), {
        adapterId: 'local.promo',
        context: {},
        editorOptions: { siteUrl: location.origin + '/promo/' },
    });
</script>

    

siteUrl обязателен: абсолютный адрес со слешем на конце. От него браузер грузит стили и картинки страницы. Без слеша относительные адреса будут считаться от папки уровнем выше.

Картинки по умолчанию попадают в HTML

Загруженная картинка встраивается прямо в разметку как data:-URL. Для пары иконок это терпимо, а фотографии раздуют страницу. Чтобы сохранять файлы, передайте в mount() параметр features: { uploadMode: 'file' } и реализуйте в адаптере EditorUploadInterface::upload().

Модуль примет только PNG, JPEG, GIF и WebP до 10 МиБ и сверит содержимое файла с MIME. Ответ адаптера тоже проверяется: src без // в начале и без управляющих символов, path относительный и без сегментов . и ...

Сервер не даст испортить разметку

Редактор отправляет на сервер только набор операций setInnerHTML и setAttr с 12-символьным хешем исходной версии. Если файл успели поменять, сервер вернёт stale-version и чужую правку не затрёт. Чтобы отключить проверку, адаптер реализует EditorCapabilitiesInterface и возвращает new EditorCapabilities(false). Там же задаётся лимит операций на одно сохранение: по умолчанию 10, допустимо от 1 до 500.

Сервер заменяет только точные куски исходной строки, форматирование остального HTML не меняется. В новую разметку пускают теги текста: абзацы, заголовки, списки, таблицы, ссылки, картинки. script, style, iframe, обработчики on*, srcdoc, PHP-, ASP- и SSI-вставки отклоняются. Старую нестандартную разметку можно оставить как есть, но подменить её новой запрещённой конструкцией нельзя.

Правки внутри <template> и пересекающиеся правки сервер пропускает, остальные изменения из набора сохраняет.

Старым сайтам на windows-1251 или koi8-r перекодировка не грозит: файл остаётся в своей кодировке. Если введённый символ в неё не помещается, сохранение отклоняется. Файлы в UTF-16 править нельзя.

BitrixGPT — только в коробочном Битрикс24

Расширение visualeditor.integration.bitrixgpt связывает кнопку «Спросить ИИ» с чатом BitrixGPT в правой панели. Оно открывает чат, кладёт запрос в черновик сообщения, перед отправкой сохраняет документ и публикует выделенные элементы в ui.page-context. Расширению нужны мессенджер и шаблон Битрикс24, поэтому работает оно только в коробочном Б24. В «Управлении сайтом» модулей im и intranet нет, там остаётся редактор без чата.

Пока идёт внешнее ИИ-задание, документ заблокирован. Цикл ведёт editor.ai: prepare(), finish() и cancel(). Метод resume() восстанавливает блокировку после перезагрузки страницы, если вы сохранили состояние задания. Поздний ответ уже не закроет новый цикл.

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

  • При замене картинки сервер обновляет и lazy-load атрибуты вроде data-src, и srcset у <source> внутри того же <picture>. Браузер не покажет старое изображение.
  • Ошибки чтения обёртка выводит над редактором в блоке с role="alert", а destroy() снимает её подписки.

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

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

Подписаться

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

superset 26.800.0 Заметное Свежее

Superset 26.800.0: поколения серверов и точные ошибки SQL

Superset 26.800.0 добавляет поколение BI-сервера и признак обязательного протокола в публичное состояние runtime. Ещё одно заметное изменение касается SQL Lab и виртуальных датасетов: ошибки пользовательского запроса теперь отделяются от недоступности BI-сервиса.

3 мин
socialnetwork 26.500.0 Безопасность Свежее

Socialnetwork 26.500.0: проверки прав в ленте и новый REST для комментариев

Socialnetwork 26.500.0 закрывает обходы прав в ленте, группах и чатах, добавляет REST-чтение комментариев и защищает лимит Scrum-проектов от параллельных запросов. Обновление также принудительно включает новый режим проектов и отключает компонент копирования группы.

4 мин
location 26.700.0 Заметное Свежее

Location 26.700.0: маршрут чек-инов сворачивается по геометрии карты

Карта чек-инов получила новые маркеры, кластеры и правила сворачивания маршрута. Обновление заметят пользователи мобильного учёта рабочего времени и разработчики, которые интегрируются с JS-расширениями `location.check-in` и `location.osm`.

4 мин
imconnector 26.250.0 Ломающее Свежее

Imconnector 26.250.0: Яндекс.Путешествия и привязка подключения

Imconnector 26.250.0 добавляет Яндекс.Путешествия в Контакт-центр и связывает каждое подключение с отдельным `binding_id`. Для порталов Азербайджана, Армении и Молдовы есть несовместимое изменение: MAX исчезает из штатного списка доступных каналов.

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

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

на связи

пишет…

Можно закрыть чат и перейти на другую страницу. Когда ответ будет готов, на кнопке Домового появится отметка.

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

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

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

Войти