feat(11.1-04): add the Backend section, the remaining Services pages and the concept map links
- docs/backend: admin controllers, forms, lists and filters, relation manager, users and permissions, settings, partials and widgets, admin SPA - docs/services: storage, outbound HTTP, realtime, Web Push, search, parity testing and the Frontend and AJAX (not provided) page - Examples for cabana (with testdata/docs YAML), fetchguard, lighthouse and its centrifugo driver, flare, beachcomber and typesense, tide; lighthouse and beachcomber TestDocs* regions run on their Postgres harnesses - concept map rows link their guide pages and the not-provided rows the Frontend and AJAX page; index lists Backend, Database and Services - TestDocsRequiredPages asserts the D-08 section order
This commit is contained in:
63
docs/backend/partials-and-widgets.md
Normal file
63
docs/backend/partials-and-widgets.md
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: Partials and widgets
|
||||
description: Extend admin screens with server-rendered partials, plugin JavaScript and CSS, form widgets backed by server actions, and custom toolbar buttons.
|
||||
section: backend
|
||||
order: 70
|
||||
---
|
||||
# Partials and widgets
|
||||
|
||||
WinterCMS controllers extend their screens with partials, `addJs` and `addCss`, custom form widgets and toolbar buttons that call AJAX handlers. The SummerCMS admin is a single-page app, so [cabana](../../modules/cabana/README.md) keeps these extension points in a form the SPA can render safely: partials arrive as an allowlisted node tree, plugin scripts are declared files, and every button or widget runs a server action through a cabana-owned route.
|
||||
|
||||
## Server-rendered partials
|
||||
|
||||
Two places accept a partial:
|
||||
|
||||
- `headerPartial: <name>` in `config_list.yaml`, a strip above the list;
|
||||
- a `type: partial` field with `path: <name>` in `fields.yaml`.
|
||||
|
||||
Both render `{ConfigDir}/_<name>.htm` with Go's `html/template`. WinterCMS `$/` and `~/` partial paths are not supported. The template's data is `.Data`, the value the controller's `pact.AdminPartialData` returns for that partial name; for a form partial on an existing record, cabana passes the record it loaded through the controller's `pact.FormExtendQuery` scope. `trans "<key>"` translates a phrase key in the request locale.
|
||||
|
||||
A statistics strip above a list, using the SPA's partial style classes:
|
||||
|
||||
```html src=modules/cabana/testdata/extension/controllers/gadgets/_stats.htm
|
||||
<dl class="summer-stats">
|
||||
{{- range .Data.Items -}}
|
||||
<div class="summer-stat"><dt class="summer-stat__label">{{ trans .Label }}</dt><dd class="summer-stat__value">{{ .Count }}</dd></div>
|
||||
{{- end -}}
|
||||
</dl>
|
||||
```
|
||||
|
||||
The view model must be a struct built for the template. cabana refuses a view model that holds the controller's model or any other GORM model, anywhere inside it, and refuses pre-escaped `html/template` content types, so every record value stays escaped.
|
||||
|
||||
The rendered HTML is parsed and walked through an allowlist before it reaches the SPA: script, style, iframe, form and similar elements are removed with their content, unknown elements are unwrapped, `id`, `style` and event handler attributes are dropped, and links and images must be same-origin paths. Output is capped at 64 KiB, 2000 nodes and a depth of 32. The cabana README lists the allowed elements, attributes and style classes.
|
||||
|
||||
## Plugin JavaScript and CSS
|
||||
|
||||
A controller that implements `pact.AdminClientAssets` names `.js`, `.mjs` and `.css` files under its plugin's `assets/` directory, the Go form of `addJs` and `addCss`. They are read from the embedded tree at start-up (a missing file stops it) and served from `<prefix>/assets/{vendor}/{plugin}/...` with a content hash in the URL, the admin Content-Security-Policy (`script-src 'self'`) and `nosniff`. Only declared files are reachable; the YAML and templates never are.
|
||||
|
||||
Plugin CSS may use only the SPA's public CSS variables (`--c-bg`, `--c-surface`, `--c-text`, `--c-primary` and the others the cabana README lists), which switch with dark mode. Do not hardcode colours and do not rely on the SPA's utility classes.
|
||||
|
||||
## Form widgets
|
||||
|
||||
A `type: widget` field puts a plugin custom element in the form and connects it to a server action:
|
||||
|
||||
```yaml
|
||||
lookup:
|
||||
label: acme.blog::lang.posts.lookup
|
||||
type: widget
|
||||
widget: acme-blog-lookup
|
||||
action: lookup
|
||||
fill: [title, slug]
|
||||
```
|
||||
|
||||
- `widget` is the custom element's tag, which must start with the plugin's `{vendor}-{plugin}-` prefix. A plugin script declared through `pact.AdminClientAssets` defines the element.
|
||||
- `action` names an action the controller registers through `pact.HasAdminActions`.
|
||||
- `fill` lists the writable scalar fields of the same form the action may write back.
|
||||
|
||||
The SPA posts the widget's values to `.../widgets/{field}`. cabana checks the CSRF header, the controller's and the action's permissions and the record scope, then calls the action's `Run` with a `pact.AdminActionInput`. The answer's `pact.AdminActionResult` carries a message and the fill values; keys outside `fill` and values that are not scalars are dropped before the response is written. An action may return a `cabana.ValidationError` to answer 422 on a field.
|
||||
|
||||
## Toolbar actions
|
||||
|
||||
Names in `toolbar.buttons` of `config_list.yaml`, other than the built-in `create` and `delete`, are actions the controller registers through `pact.HasAdminActions`. Each needs a label. A toolbar action runs with an empty body and no record IDs, so it can never become an unscoped lookup of IDs the client chose. The list schema lists only the actions the administrator may run.
|
||||
|
||||
An unknown action name, a widget tag outside the plugin's prefix or a fill key that is not a writable scalar field stops the start-up.
|
||||
Reference in New Issue
Block a user