Интеграция сайта
На стороне сайта нужно реализовать несколько понятных точек расширения: открыть редактор, принять callback, сохранить файлы, сохранить HTML/JSON, подключить viewer и CSS шрифтов.
Обязательные части
| Часть | Обязательность | Назначение |
|---|---|---|
| Открытие редактора | Да | В админке сайта вызвать SDK/API и открыть URL редактора для material_id. |
| Callback endpoint | Да | Принять article_saved и site_fonts_updated от Verstka. |
| Проверка подписи | Да | Проверить X-Verstka-Signature по формуле material_id:content_url. |
| Скачивание ZIP | Да | Скачать content_url с api_key, material_id и подписью. |
| Сохранение медиа | Да | Сохранить vms_media/* в публичное хранилище сайта. |
| Сохранение HTML/JSON | Да | Записать vms_html и vms_json в модель статьи или ревизию. |
| Вывод статьи | Да | Отдать сохраненный HTML на странице материала. |
| Подключение viewer script | Да | Инициализировать Verstka viewer для интерактивности статьи. |
| Сохранение шрифтов | Да, если используются site fonts | Сохранить vms_fonts/*, vms_fonts.css, vms_fonts.json. |
| Подключение CSS шрифтов | Да, если используются site fonts | Добавить <link rel="stylesheet"> для сохраненного CSS. |
Методы сайта
Названия могут быть любыми. Важно, чтобы роли были закрыты.
| Метод | Обязательность | Что делает |
|---|---|---|
getEditorUrl(materialId, vmsJson, metadata) | Да | Вызывает session/open, передает сохраненный vms_json при повторном редактировании. |
loadArticleVmsJson(materialId) | Да для редактирования | Возвращает последнее сохраненное состояние редактора из CMS. |
handleVerstkaCallback(payload, signature) | Да | Общий обработчик article_saved и site_fonts_updated. |
saveMedia(filename, tempPath, materialId, metadata) | Да | Кладет медиа в публичное хранилище и возвращает URL. |
saveFontFile(filename, tempPath, materialId, metadata) | Да для шрифтов | Кладет .woff/.woff2 в публичное хранилище и возвращает URL. |
saveFontsManifest(filename, tempPath, materialId, metadata) | Да для шрифтов | Сохраняет vms_fonts.css и vms_fonts.json. |
onContentPreSave(ctx) | Опционально | Проверяет права и бизнес-ограничения до скачивания ZIP; обычно опирается на timeLimitedAuthToken из metadata. |
onContentFinalize(ctx) | Да | Сохраняет vms_html, vms_json, metadata, ревизию, индексы. |
onFontsPreSave(ctx) | Опционально | Проверяет, можно ли менять site fonts; обычно опирается на timeLimitedAuthToken из metadata. |
onFontsFinalize(ctx) | Опционально, но полезно | Сохраняет URL CSS/JSON шрифтов в настройках сайта. |
renderArticle(materialId) | Да | Вставляет сохраненный vms_html в публичную страницу. |
enqueueVerstkaViewer() | Да | Подключает frontend runtime и вызывает initArticle/initArticles. |
enqueueVerstkaFontsCss() | Да для шрифтов | Подключает CSS с @font-face. |
Metadata сайта
metadata — JSON object, который сайт передает при открытии редактора. Verstka сохраняет его в сессии, возвращает в callbacks и кладет в metadata_json.json внутри ZIP.
metadata не имеет жесткой схемы: сайт может передавать любые кастомные JSON-ключи, которые нужны для маршрутизации, прав доступа, аудита, хранения файлов или собственной бизнес-логики. Значения должны быть JSON-сериализуемыми: строки, числа, boolean, массивы и вложенные объекты.
Обычно туда кладут:
| Ключ | Назначение |
|---|---|
anySiteAdditionalKey | пример произвольного ключа сайта; значение — например anySiteAdditionalValue. |
timeLimitedAuthToken | ограниченный по времени токен авторизации, который сайт проверяет в pre-save hooks. |
customContainers | массив определений Custom Containers, если сайт использует свои контейнеры или виджеты. |
Допустимы и другие кастомные ключи — например workflowState, brandId, sectionId, campaignId, reviewRequired. Полное описание — в разделе metadata документации API.
Служебные ключи:
| Ключ | Кто задает | Что важно |
|---|---|---|
version | SDK / WordPress plugin | Метка клиента интеграции: Python SDK передает python_<sdk-version>, PHP SDK — php_<sdk-version>, WordPress plugin — wp_<plugin-version>. |
version_id | Verstka backend | ID версии, созданной при последнем сохранении статьи. |
version_cdate | Verstka backend | Дата создания последней версии. |
user_email | Verstka backend / WordPress plugin | Email редактора в callback. Verstka обновляет значение при сохранении. |
user_ip | Verstka backend / WordPress plugin | IP редактора в callback. Verstka обновляет значение при сохранении. |
webhook_auth_user, webhook_auth_password | SDK / WordPress plugin / сайт | Служебные ключи авторизации исходящего callback: Basic Auth (оба ключа) или Bearer token (только webhook_auth_user). Не используйте эти имена для бизнес-данных сайта. |
Не используйте metadata как произвольное хранилище секретов, токенов доступа, приватных ключей и персональных данных, которые не нужны callback-обработчику. Эти данные сохраняются в сессии и попадают в metadata_json.json внутри ZIP.
При интеграции без SDK передайте эти ключи в metadata при session/open: Basic Auth — оба ключа; Bearer — только webhook_auth_user с токеном. Verstka добавит Authorization к исходящим callback article_saved и site_fonts_updated, а также к GET clientUrl при session restore.
Создание и редактирование статьи
Для новой статьи сайт может передать только material_id и metadata. Если vms_json не передан, Verstka откроет дефолтный шаблон.
Для повторного редактирования сайт должен передать последний сохраненный vms_json, который вернулся из callback:
editor_url = client.get_editor_url(
material_id=str(article.id),
vms_json=article.vms_json,
metadata={
"anySiteAdditionalKey": "anySiteAdditionalValue",
"timeLimitedAuthToken": "cms-scope-token",
"customContainers": [],
# опционально: callback authorization (см. Callback Authorization)
# "webhook_auth_user": "callback-user",
# "webhook_auth_password": "callback-password",
},
)Практический порядок:
- CMS показывает кнопку "Редактировать в Verstka".
- Backend CMS строит editor URL через SDK.
- UI открывает URL в новой вкладке.
- При сохранении CMS получает
article_saved. - CMS сохраняет новую ревизию
vms_html/vms_json. - Публичная страница начинает отдавать новый HTML.
Обработка медиа
В HTML и JSON, которые приходят из ZIP, временные URL выглядят как dummy-<filename>. Сайт должен:
- сохранить каждый файл из
vms_media/*; - получить публичный URL;
- заменить
dummy-<filename>вvms_html; - записать URL в
vms_json.assets[filename].clientUrl; - вернуть обновленный
vms_jsonвdata.vms_json.
SDK делает эти шаги автоматически, если реализован StorageAdapter.
Управление шрифтами
Site fonts - это настройки уровня сайта, а не конкретной статьи. При изменении шрифтов редактор отправляет site_fonts_updated.
Сайт должен сохранить:
| Файл | Куда использовать |
|---|---|
vms_fonts/* | Публичные файлы шрифтов. |
vms_fonts.css | CSS, который нужно подключать на страницах со статьями Verstka. |
vms_fonts.json | Манифест для отладки или собственной админки. |
После сохранения CSS желательно записать его URL в настройки сайта:
verstka_fonts_css_url = https://cdn.example.com/verstka/fonts/vms_fonts.cssИ подключать на публичных страницах:
<link
rel="stylesheet"
href="https://cdn.example.com/verstka/fonts/vms_fonts.css"
/>Подключение frontend viewer
Сохраненный vms_html уже содержит разметку статьи и JSON payload. Для полной работы интерактивных элементов нужно инициализировать viewer runtime.
Самый прямой вариант:
<script type="module">
import('https://r2.verstka.org/viewer-latest.js')
.then(({ Verstka }) => {
Verstka.initArticles(document)
})
.catch((error) => {
console.error('Failed to initialize Verstka articles', error)
})
</script>Если сайт использует npm-сборку, можно подключить wrapper:
npm install verstka-viewerimport { initArticles } from 'verstka-viewer'
await initArticles(document)Настройка GA4/Яндекс Метрики, приоритет ID и analytics: false описаны в разделе Клиентская аналитика.
Для stage/debug окружений wrapper поддерживает dev, debug и явный viewerUrl.
Что хранить в CMS
Минимальная модель статьи:
| Поле | Назначение |
|---|---|
material_id | ID статьи в CMS. |
vms_html | Готовый HTML для публичной страницы. |
vms_json | Состояние редактора для повторного открытия. |
metadata | Данные последнего save: кастомные ключи сайта, version_id, version_cdate, user_email, user_ip. |
is_vms | Флаг, что статья отрисовывается через Verstka. |
Опционально:
- таблица ревизий для истории сохранений;
- URL
vms_fonts.cssв настройках сайта; - audit log callback-ов;
- lock/permission policy через
onPreSave(проверкаtimeLimitedAuthTokenиз metadata).
Custom Containers
Если статья использует Custom Containers, сайт может:
- заменить HTML-комментарии
<!-- ReplaceMarker: value -->своими виджетами; - прочитать
data-vrstk-custom-*атрибуты; - инициализировать свои frontend-виджеты после
Verstka.initArticles.
Подробнее: Custom Containers.
Stick to Edges
Горизонтальные привязки stickToEdges.left/right ориентируются на viewport документа, содержащего статью. В iframe это ширина самого iframe. Отступы заданы в CSS px и не умножаются на масштаб фрейма. Виджет может выходить за границы контейнера статьи.
SSR выдаёт авторскую геометрию и содержимое. После монтирования viewer измеряет положение фрейма относительно viewport и уточняет layout привязанных объектов; при первом отображении возможен сдвиг. Измерение обновляется при смещении контейнера или изменении окна и не меняет сохранённый EditorContent.
Интеграция не должна обрезать выходящие за статью элементы, если они должны быть видны. Проверьте overflow: hidden, overflow: clip, clip-path и paint containment у внешних предков. Viewer не меняет стили host-страницы и не переносит виджеты за пределы её DOM.
Вертикальные привязки top/bottom ориентируются на высоту самой статьи, включая нижний зазор, и прокручиваются вместе с ней. Объект с bottom и все его потомки исключаются из расчёта автоматической высоты статьи, чтобы избежать циклической зависимости. Растяжение по обеим осям пересчитывает геометрию потомков группы. В EditorContent v5 свойство stickToEdges необязательно; отдельные поля left, right, top, bottom также можно опускать. Отсутствующий или равный null край выключен. Старые статьи без свойства сохраняют прежнюю геометрию; добавление привязок не требует миграции или повышения версии.