Меню
Меню — это меню WordPress: набор пунктов, поставленный в область темы. Областей четыре: header, footer_1, footer_2, footer_3. На многоязычном сайте в одной области на каждом языке стоит своё меню. Во фрейме меню выводит вставка onpress/menu (или onpress/footer-columns для подвала), а читается содержимое меню через системный источник menu.<область>.
Правило разделения простое: читать пункты — GET /data/menu.header; менять меню — ручки /menus. Полные параметры — в справочнике меню.
Какие меню есть
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/site/$SITE/menus" | jq '{theme, locations, default_lang, menus: [.menus[] | {id, name, slug, items, assigned}]}'
{
"theme": "onpress-v2",
"locations": ["header", "footer_1", "footer_2", "footer_3"],
"default_lang": "en",
"menus": [
{ "id": 83, "name": "header en", "slug": "header-en", "items": 3, "assigned": [ { "location": "header", "lang": "en" } ] },
{ "id": 107, "name": "header ru", "slug": "header-ru", "items": 3, "assigned": [ { "location": "header", "lang": "ru" } ] },
{ "id": 143, "name": "Документация", "slug": "footer-docs-en", "items": 5, "assigned": [ { "location": "footer_1", "lang": "en" } ] }
]
}
assigned — где меню стоит и на каком языке. Привязка к областям хранится отдельно для каждой темы; theme в ответе — чья карта прочитана (по умолчанию активной темы сайта). Параметр theme у ручек меню нужен, только если вы сознательно готовите меню под другую тему.
Адресация меню
{ref} в пути — id меню, его слаг, имя или область с тильдой: ~header — меню, которое сейчас стоит в шапке (с учётом lang, если он передан). Имя, общее для нескольких меню, — 409 ambiguous_menu.
curl -s -X PUT "$API/site/$SITE/menus/~header/items" … # меню шапки
curl -s -X PUT "$API/site/$SITE/menus/header-ru/items" … # меню со слагом header-ru
curl -s -X PUT "$API/site/$SITE/menus/107/items" … # меню с id 107
Создать меню и поставить в шапку одним запросом
curl -s -X POST "$API/site/$SITE/menus" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{
"name": "Шапка RU",
"slug": "header-ru",
"location": "header",
"lang": "ru",
"items": [
{ "page": "/docs/", "title": "Документация", "children": [
{ "page": "/docs/v2/ru/quickstart/" },
{ "page": "/docs/v2/ru/api/" }
]},
{ "page": "tarify" },
{ "url": "https://t.me/onpress", "title": "Telegram", "target": "_blank", "classes": "btn btn--primary" }
]
}' | jq '{menu, items: [.items[] | {at, title, url, valid}], invalid, rebaked: .rebaked.status}'
{
"menu": { "id": 171, "name": "Шапка RU", "slug": "header-ru", "items": 5 },
"items": [
{ "at": "0", "title": "Документация", "url": "/docs/", "valid": true },
{ "at": "0.0", "title": "Быстрый старт", "url": "/docs/v2/ru/quickstart/", "valid": true },
{ "at": "0.1", "title": "Справочник API", "url": "/docs/v2/ru/api/", "valid": true },
{ "at": "1", "title": "Тарифы", "url": "/tarify/", "valid": true },
{ "at": "2", "title": "Telegram", "url": "https://t.me/onpress", "valid": true }
],
"invalid": [],
"rebaked": "ok"
}
Форма пункта
| Поле | Смысл |
|---|---|
page | страница этого сайта: id, слаг или путь. Пункт ведёт на страницу и следует за её адресом при переносе |
url | любой адрес. Путь (/tarify/) дополняется адресом сайта и хранится полным — после смены домена такой пункт придётся переписать; для своих страниц используйте page |
title | подпись; у пункта-страницы без неё берётся заголовок страницы |
target | _blank, чтобы открывать в новой вкладке |
classes | CSS-классы пункта через пробел |
children | вложенные пункты той же формы |
Голое значение вместо объекта ("tarify", 6183) считается ссылкой на страницу. Пункт без page и url — ошибка.
Битые пункты
Пункт, который ведёт на удалённую, черновую или приватную страницу, WordPress посетителю не показывает, и меню из таких пунктов выглядит пустым без объяснений. Поэтому ссылки проверяются до записи:
- страницы нет вовсе —
409 menu_items_unresolvedпри записи дерева (в теле —errorsс координатой пунктаat) и404 page_not_foundпри добавлении одного пункта; ничего не записано; - страница есть, но не опубликована —
409 menu_items_invalidсо списком;"allow_invalid": trueзаписывает всё равно, а ответ помечает такие пунктыvalid: falseи причиной.
Источник menu.<область> при чтении отбрасывает битые пункты и сообщает их число в meta.invalid_items.
Поставить меню в область
curl -s -X PUT "$API/site/$SITE/menus/header-ru/location" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"location": "header", "lang": "ru"}' | jq .
С lang пишется языковая карта Polylang. Без lang или с основным языком — ещё и карта областей темы (её читает сайт без языков и запрос без языка). Каждая записанная карта перечитывается и сверяется; не совпало — 500 menu_assignment_not_stored, ничего не записано. Неизвестная область — 400 unknown_location со списком, неизвестный язык — 400 unknown_language, язык на сайте без Polylang — 409 polylang_inactive.
Снять меню с области отдельной ручкой нельзя (пустая location — 400 location_required): поставьте в область другое меню или удалите меню — удаление освобождает все его области.
Поменять пункты
Всё дерево целиком — основной путь; прежние пункты заменяются:
curl -s -X PUT "$API/site/$SITE/menus/~header/items" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"lang": "ru", "items": [{"page": "/docs/"}, {"page": "tarify"}]}' | jq '{total, invalid}'
То же делает запись в системный источник: PUT /data/menu.header с тем же телом — удобно, когда вы и так работаете с данными.
Один пункт в конец (или к родителю):
curl -s -X POST "$API/site/$SITE/menus/header-ru/items" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"item": {"page": "/blog/", "title": "Блог"}}' | jq '{item, total}'
Вложить пункт: {"item": {"page": "…", "parent": <id пункта>}}.
Правка пункта — заголовок, адрес, страница, цель, классы, родитель:
curl -s -X PATCH "$API/site/$SITE/menus/header-ru/items/6434" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"title": "OnPress.pro", "url": "https://onpress.pro/docs/"}' | jq .item
page превращает внешнюю ссылку в пункт-страницу, url — наоборот. Id пунктов — из ответов записи или из GET /data/menu.<область>.
Порядок пунктов одного уровня:
curl -s -X POST "$API/site/$SITE/menus/header-ru/reorder" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"order": [6436, 6433], "parent": 0}' | jq '{order, appended}'
Неназванные пункты уровня встают после названных в прежнем порядке (appended), соседние ветки не трогаются.
Удалить пункт — вместе с его веткой, иначе дети всплыли бы на верхний уровень:
curl -s -X DELETE "$API/site/$SITE/menus/header-ru/items/6434" -H "Authorization: Bearer $OP_TOKEN" | jq '{removed, total}'
Переименовать и удалить меню
curl -s -X PATCH "$API/site/$SITE/menus/header-ru" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"name": "Шапка (русская)"}' | jq .menu
curl -s -X DELETE "$API/site/$SITE/menus/header-ru" -H "Authorization: Bearer $OP_TOKEN" | jq '{deleted, removed_items, freed_locations}'
Удаление сносит меню со всеми пунктами и освобождает все его области в обеих картах.
После записи
Любая запись меню пересобирает фреймы, которые выводят меню, и сбрасывает кэш их страниц — ответ несёт rebaked. Меню, поправленное в wp-admin, платформа тоже замечает; если нет — POST /frames/rebake {"menus": true}.
Вывести меню во фрейме
<nav class="site-header__nav"><!-- onpress/menu {"place":"header","class":"site-header__menu","depth":1} /--></nav>
<footer class="site-footer"><!-- onpress/footer-columns {"places":"footer_1,footer_2"} /--></footer>
Нужна своя разметка — повторитель по источнику:
<!-- onpress/repeat {"source":"menu.header"} -->
<a class="topnav__link" data-op-bind='{"href":"item.url","text":"item.title"}'></a>
<!-- /onpress/repeat -->
Меню на странице берётся на её языке. Если область пуста на странице, а GET /menus показывает меню в ней, сравните theme в ответе с темой, которой выводится сайт.