feat(cabana): read-only Plugins screen with installed plugins
- GET /plugins (backend.view_plugins, superusers always pass) lists the plugins from party.Installed, or party.Activated IDs for older builds, with the built framework version and whether the registry is configured - Settings -> System -> Plugins entry with the new SettingsEntry.path, added only for principals allowed to open it - PluginsView at /system/plugins with an Installed tab (reka-ui Tabs); settings panel, rail, breadcrumbs and page title know the route - OpenAPI and SPA types regenerated; contract, token-class and conformance inventories list the route; en/pl strings; docs
This commit is contained in:
@@ -41,6 +41,10 @@ The button at the top of the plugin rail switches the theme: each click moves fr
|
||||
|
||||
When the server offers more than one admin language (`meta.locales` of `GET .../lang`), the user menu lists them under Language, each named in its own language. Choosing one saves it as the administrator's `locale` preference, caches it in `localStorage` (`summer-admin.locale`) and reloads the page, so the strings, navigation, settings, schemas and validation messages all come back in that language, in any browser and whatever `Accept-Language` says. A form with unsaved changes asks before the reload.
|
||||
|
||||
## Plugins screen
|
||||
|
||||
Settings → System → Plugins opens `<prefix>/system/plugins`, a read-only screen for administrators holding `backend.view_plugins` (`cabana.PermissionViewPlugins`; superusers always pass). Its Installed tab lists the plugins the binary was built with, from `GET <prefix>/api/v1/plugins`: the name and description from each plugin's `plugin.yaml` (only the ID for a plugin without one), the version and where the plugin came from (Go module, git submodule or the application itself), with the framework version the application was built with above the tabs. The settings entry carries a `path` instead of a controller, so the settings index, the settings panel and the breadcrumbs treat the screen like any other System entry. The SPA never calls the plugin registry itself; see [Publishing plugins](../plugins/marketplace.md#admin-plugins-screen).
|
||||
|
||||
## New versions
|
||||
|
||||
A tab opened before a deploy would otherwise keep running the old admin against the new server, because the session refreshes silently. The SPA reads the build it booted with from the `summer-admin-build` meta of the served `index.html` and asks `<prefix>/build.json` again when the window gets focus, when the tab becomes visible and every 5 minutes while it is visible, at most once every 30 seconds. When the server answers a different build, a banner at the bottom right says that a new version is available, with Reload and Close buttons. The SPA never reloads by itself: Reload goes through the usual page reload, so a form with unsaved changes still asks first, and Close hides the banner until yet another build is deployed. When the served index carries no build, as under the Vite dev server, the check is off.
|
||||
|
||||
@@ -68,7 +68,7 @@ func (p *BlogPlugin) Navigation() []pact.NavigationItem {
|
||||
|
||||
A controller's `pact.AdminPermissioned.RequiredPermissions` are checked before any schema is served or query runs, and navigation and settings entries are filtered by the permissions they name, so an administrator sees only what they may open: a main menu item the administrator may not open is dropped even when one of its side-menu entries would pass, and an allowed item that links to a controller the administrator cannot open links to its first openable side-menu entry instead. A plugin can also hide an entry per request with `pact.NavigationItem.Visible` (for a screen whose feature is turned off in its settings); the same drop and relink rules apply. `cabana.Allows` is the check and follows Winter's `hasAnyAccess` for JWT sessions: superusers pass, an administrator needs any one of the listed codes, and an empty requirement list allows any signed-in administrator. Wildcards match on both sides: a grant ending in `.*` covers every code with that prefix, and a required code such as `acme.blog.*` is met by any grant under `acme.blog.`. When the request carries a personal API token with a non-full scope, the scope is checked first (see [API tokens](#api-tokens)). The last lines of the activation example on [Admin controllers](admin-controllers.md) show it.
|
||||
|
||||
The admin registers two permissions of its own on the System tab of the permission editor: `backend.manage_users` (`cabana.PermissionManageUsers`) opens the Administrators screen and `backend.manage_roles` (`cabana.PermissionManageRoles`) the Roles screen. Neither is granted to any role in code, so after an upgrade only superusers reach those screens until an administrator grants a code to a role or to an administrator.
|
||||
The admin registers three permissions of its own on the System tab of the permission editor: `backend.manage_users` (`cabana.PermissionManageUsers`) opens the Administrators screen, `backend.manage_roles` (`cabana.PermissionManageRoles`) the Roles screen and `backend.view_plugins` (`cabana.PermissionViewPlugins`) the read-only Plugins screen (see [Admin SPA](admin-spa.md#plugins-screen)). None is granted to any role in code, so after an upgrade only superusers reach those screens until an administrator grants a code to a role or to an administrator.
|
||||
|
||||
An administrator's grants are the role's `permissions` merged with the administrator's own `backend_users.permissions`, the way WinterCMS merges them: the administrator's value for a code replaces the role's, and only `1` grants. A `-1` (or `0`) on the administrator therefore removes a permission the role grants, so rows copied from a WinterCMS database keep their denies. As in WinterCMS the merge compares codes exactly, so denying `acme.blog.access_posts` does not take it back from a role that grants `acme.blog.*`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user