Публикация и обновление
Публикация отправляет собранную страницу во внешнюю систему — витрину. Основной
провайдер — 4CMS (fourcms), он создаёт там статью из HTML, CSS и JS вашей
страницы. Есть также провайдер ftp, выкладывающий самодостаточный HTML-файл.
Что именно создаётся на витрине, зависит от вида документа: страница становится статьёй, а шаблон и макет — отдельной сущностью «шаблон документа».
Что нужно до первой публикации
Заголовок раздела «Что нужно до первой публикации»- Страница привязана к сайту. Без сайта публикация вернёт 400 «Page has no associated site».
- У сайта настроен провайдер. Если провайдер не привязан, ответ 400 прямо
подскажет, что делать:
PUT /api/v1/sites/{id}/provider/fourcms. Для 4CMS в настройках обязателенapi_token, для FTP —host,username,password. - Известны ID рубрик 4CMS, к которым будет привязана статья. PageCraft
их не придумывает; в интерфейсе рубрики показываются деревом
(
GET /api/sites/{id}/rubrics), в API их берут из 4partners.io. Шаблону рубрики не нужны — их связь ведёт 4CMS, и диалог публикации их у шаблона не спрашивает: для документа, чейkindнеarticle, в нём остаётся только имя.
Publish или publish-update
Заголовок раздела «Publish или publish-update»Это два разных эндпоинта, и каждый работает только в своём состоянии:
| Состояние страницы | Что вызывать |
|---|---|
external_id пустой (никогда не публиковалась) |
POST /api/v1/pages/{id}/publish |
external_id заполнен |
POST /api/v1/pages/{id}/publish-update |
Перепутали — придёт 400 с прямым указанием: «Page already published. Use publish-update instead.» или «Page not published yet. Use publish first.»
Смысл разделения простой: первая публикация создаёт статью на витрине и запоминает её идентификатор, повторная — обновляет ту же самую статью по этому идентификатору. Поэтому повторная публикация не плодит дубли.
Как узнать текущее состояние
Заголовок раздела «Как узнать текущее состояние»GET /api/v1/pages/42/publication{ "is_published": true, "external_id": 12345, "external_slug": "about-the-company", "published_at": "2026-06-08T12:30:00+00:00", "updated_at": "2026-06-08T12:31:10+00:00"}Эндпоинт отвечает из локальной базы и не ходит к провайдеру — он быстрый
и безопасен для опроса перед каждой публикацией. Тот же объект возвращают
publish и publish-update.
Первая публикация
Заголовок раздела «Первая публикация»POST /api/v1/pages/42/publish
{ "rubric_ids": [42, 51], "meta_title": "О компании | Мой сайт", "meta_description": "Команда, ценности, история.", "slug": "about"}rubric_idsобязателен для страницы и должен содержать хотя бы один положительный ID. Без него провайдер отвечает «rubric_ids is required».- Остальные поля необязательны:
name,meta_title,meta_description,meta_keywords,meta_robots,slug,preview_text. Непереданное поле берётся со страницы — из её названия и SEO-полей. Пустые значения провайдеру не отправляются вовсе. - Статья создаётся сразу активной.
После успеха PageCraft сохраняет external_id и external_slug, полученные
от витрины, и проставляет published_at.
Публикация шаблона и макета
Заголовок раздела «Публикация шаблона и макета»У документа с kind ≠ article тело публикации не нужно вовсе:
POST /api/v1/pages/42/publish
{}rubric_ids, slug и meta_* шаблону не передаются: адрес, SEO и привязку
шаблона к рубрикам ведёт 4CMS. На витрине создаётся шаблон документа, его
идентификатор сохраняется в external_id; external_slug у шаблона всегда null —
своего адреса у него нет. Повторная публикация — тот же publish-update, тоже
без тела.
Провайдер ftp шаблоны публиковать не умеет: попытка вернёт 400 «провайдер
не умеет публиковать шаблоны».
Повторная публикация
Заголовок раздела «Повторная публикация»POST /api/v1/pages/42/publish-updateТело опционально. Если не передать ничего, на витрине обновятся только
HTML, CSS и JS блоков, а название, рубрики и SEO-поля останутся прежними.
Переданные поля заменят соответствующие значения в 4CMS; rubric_ids здесь
не обязателен — без него привязка к рубрикам не меняется.
Так выглядит обычный цикл работы: правите блоки на странице → publish-update →
проверяете результат на витрине.
Что именно уезжает
Заголовок раздела «Что именно уезжает»- Без макета — HTML блоков, CSS блоков и JS блоков тремя отдельными полями.
- С макетом — один готовый HTML-документ: содержимое подставлено в шаблон макета, CSS и JS уже внутри.
- Блоки со слотами данных (
bindingSlots) уезжают шаблоном внутри<template>, а их значения — отдельным полемcontext, которое отрисовывает рантайм витрины. Провайдер FTP такого рантайма не имеет и рендерит эти блоки как обычные.
Подробности сборки — в разделе Сборка страницы из блоков.
Живой URL
Заголовок раздела «Живой URL»GET /api/pages/{id}/live-urlАдрес не хранится готовым, а собирается заново: провайдер спрашивает у витрины
текущий slug статьи и складывает его с доменом сайта. Заодно PageCraft
синхронизирует external_slug, если адрес поменяли на стороне 4CMS.
Ответы: 400 — страница не опубликована; 502 — провайдер не смог разрешить URL (нет токена, статья не найдена, не определился домен).
Коды ошибок публикации
Заголовок раздела «Коды ошибок публикации»| Код | Когда |
|---|---|
| 400 | не то состояние публикации, нет сайта, не привязан или неизвестен провайдер, невалидное тело, провайдер не поддерживает шаблоны |
| 401 | нет API-ключа или сессии |
| 404 | страница не найдена или принадлежит другому пользователю |
| 502 | ошибка внешнего провайдера; тело содержит его сообщение, например 4CMS API error: invalid token |
502 — единственный код, за которым стоит чужая система: повтор запроса имеет смысл, изменение данных страницы обычно нет.
Источники
Заголовок раздела «Источники»backend/src/Service/Page/PublishPageService.php,PublishUpdatePageService.php,PageLiveUrlService.phpbackend/src/Publishing/Provider/FourCmsProvider.php,FtpProvider.phpbackend/src/Publishing/BlockAssembler.php,LayoutRenderer.phpbackend/src/Controller/V1/Page/PublishPagePublicAction.php,PublishUpdatePagePublicAction.php,GetPagePublicationStatusAction.phpbackend/src/Controller/Page/PageLiveUrlAction.php