Path in the Gestor modulos/menus/
The menus module builds navigation menus (header, footer, sidebar, breadcrumb, dropdown, mobile) without writing PHP. The operator builds a tree of items in the admin panel, picks a visual template and drops the menu into any layout or page with a widget marker. The final HTML is assembled on the server on every request, always with the current canonical page links.
How to use it
-
Create the menu at Menus → Add (
/menus/adicionar/). Give it a name and pick a template; the available templates are thetemplatesrecords withtarget = 'menus'. -
Build the tree in the editor (
/menus/editar/?id=...): add items, drag to reorder and nest, and search pages by name. The preview tab calls thewidget-previewAJAX route, which uses the same renderer as the website. -
Embed it in a layout or page with the widget wrapper.
grupo_slugis the menuid:
<!-- widgets#menus->render({"grupo_slug": "main-menu"}) < -->
<nav>…static mockup, only for whoever edits the layout…</nav>
<!-- widgets#menus->render({"grupo_slug": "main-menu"}) > -->Item types
type |
Behavior |
|---|---|
pagina |
Points to a page by page_id (slug). URL and label come from the database in the current language; a label set on the item wins. |
link-custom |
Free label and URL; supports target (_self/_blank). |
link-action |
Same as link-custom, meant for actions (logout, modals) via css_classes. |
cabecalho |
Grouping header with children; falls back to # without a URL. |
publicador |
Expands a publisher's publications at runtime (publisher_id), limited by count (default 5) and sorted by order_by (date_desc, date_asc, title_asc, title_desc). Replaces manual children. |
separador |
Visual divider with an optional label. |
An old tree saved as a list of slugs (the initial format) still works: each string becomes a pagina item.
Per-visitor visibility
fields_schema.availability decides whether the menu is the same for everyone (todos) or depends on the visitor (condicional). In conditional mode, the conditions are evaluated in order, and the first match wins:
| Condition | Matches when |
|---|---|
publico |
the visitor is not logged in |
logado |
there is an authenticated user with a valid token and permission |
perfil_usuario |
the logged-in user belongs to one of the profile_ids (plain id or its sha256 hash) |
Each condition has a slug, and its items live in fields_schema.menus[<slug>]. If none matches, menus.visible_to_all is used. The template may carry different HTML per condition using <!-- menu-conditional-<slug> < --> blocks; otherwise it uses <!-- menu-visible < --> or the whole template.
Template contract
A menu template is HTML with model blocks that the renderer repeats:
| Block | Use |
|---|---|
<!-- item < --> … <!-- item > --> |
Item without children |
<!-- item-parent < --> … <!-- item-parent > --> |
Item with children; children go into [[item#children]], recursively |
<!-- item-separator < --> … <!-- item-separator > --> |
Separator |
<!-- no-item < --> … <!-- no-item > --> |
Shown when the menu has no items |
Per-item variables: [[item#label]], [[item#url]], [[item#target]], [[item#slug]], [[item#css_classes]] and [[item#children]]. The database form with at signs (@[[item#url]]@) is also accepted, and the at signs are consumed by the replacement.
Rules worth knowing:
-
Without
item-parent, the tree is flattened: children are rendered at the same level with theitemblock. No item is lost. -
Without
item-separator, separators are drawn with an emptyitemblock. -
The rendered HTML replaces the first
item(oritem-parent) block; the other model blocks are removed. -
The menu's CSS, compiled CSS and
html_extra_headare injected into<head>only once, even when the menu repeats on the page (gestor_pagina_recursos_incluir()).
Templates shipped with the core: menus-horizontal-navbar, menus-vertical-sidebar, menus-footer-colunas, menus-dropdown, menus-breadcrumb and menus-mobile-hamburguer.
Browser behavior
Whenever the widget appears on a page, menus.widget.js is included automatically. Using event delegation, it:
-
toggles the
.menu-mobile-listlist when.menu-mobile-btnis clicked (updatingaria-expanded); -
opens and closes
.group-hover-sub-menuon hover, as a fallback for when Tailwind group variants do not reach the final CSS.
Data
The menus table: id (slug), name, language, fields_schema (JSON), html, css, css_compiled, html_extra_head, status, versao, plugin, user_modified and system_updated, plus the dates.
{
"template_id": "menus-vertical-sidebar",
"availability": "todos",
"conditions": [],
"menus": {
"visible_to_all": [
{ "type": "pagina", "page_id": "comece-a-construir", "label": "", "children": [] },
{ "type": "cabecalho", "label": "Reference", "children": [
{ "type": "pagina", "page_id": "docs-reference-libraries-modelo", "children": [] }
]}
]
}
}Because it is website configuration, a menu can be versioned as a project resource: declare the menus table with sync_resources in the project's resources/project_tables_config.json. Records live in resources/<lang>/menus.json and the HTML in resources/<lang>/menus/<id>/<id>.html. This is the format c2f docs:build generates for the documentation sidebar.
See also
-
modelo.php library: the string functions the Gestor's templates are built on.
Tailwind panel controls
Checkboxes and radio buttons have clickable labels; editor validation shows inline messages. The public widget still follows its template and parameters.
Shared conventions: Administrative interface.