feat(quick-261016-s8x): pact.NavigationItem.Visible hides navigation entries at request time
- pact.NavigationItem gains Visible func(ctx) bool (json:"-"); nil keeps the entry always listed - cabana.Registry.Metadata runs it after the permission check, drops a main item (with its side menu) or a side item that answers false, and relinks a parent whose controller is a hidden child's to its first visible openable child - TestNavigationHidesInvisibleItemsAndRepointsTarget covers the rules - pact and cabana READMEs, users-and-permissions and admin-controllers docs describe the hook
This commit is contained in:
@@ -24,7 +24,7 @@ Use cases:
|
||||
|
||||
- Plugin capability interfaces: `pact.HasRoutes`, `pact.HasConfig`, `pact.HasMigrations`, `pact.HasCommands`, `pact.HasModels`, `pact.HasJobs`, `pact.HasSchedule`, `pact.HasLang`, `pact.HasLangOverrides` and `pact.HasMailTemplates`.
|
||||
- HTTP contracts: the `pact.Router` group builder (implemented by surf), the `pact.Middleware` type, and named, parameterized (`name:param`) and house-envelope middleware through `pact.HasMiddleware`, `pact.HasMiddlewareFactories` and `pact.HasHouseMiddleware`.
|
||||
- Backend registration data: `pact.Permission`, `pact.NavigationItem` and `pact.SettingsItem`, exposed through `pact.HasPermissions`, `pact.HasNavigation` and `pact.HasSettings`. A settings entry is either a singleton form or a link that opens an admin controller from the Settings page.
|
||||
- Backend registration data: `pact.Permission`, `pact.NavigationItem` and `pact.SettingsItem`, exposed through `pact.HasPermissions`, `pact.HasNavigation` and `pact.HasSettings`. A settings entry is either a singleton form or a link that opens an admin controller from the Settings page. A `pact.NavigationItem.Visible` func hides a navigation entry at request time (nil = always listed).
|
||||
- Personal API token scopes: `pact.TokenScope` and `pact.HasTokenScopes`, so a plugin declares named scopes (permissions and HTTP methods) that administrators can assign when creating a personal API token.
|
||||
- Admin controller contracts: `pact.AdminController`, `pact.HasAdminControllers`, `pact.AdminAssets` (embedded Winter-shaped admin YAML), `pact.AdminPermissioned` and `pact.AdminRecordSource`.
|
||||
- Admin extension contracts, so a plugin extends the compiled admin SPA without a Node build: `pact.AdminClientAssets` (per-controller JS and CSS from the plugin's embedded `assets/` tree, Winter's `addJs`/`addCss`), `pact.HasAdminActions` with `pact.AdminAction`, `pact.AdminActionInput` and `pact.AdminActionResult` (named toolbar and widget actions whose routes, CSRF check, permissions and record scoping the framework owns), `pact.HasAdminBulkActions` with `pact.AdminBulkAction`, `pact.AdminBulkActionInput` and `pact.AdminBulkActionResult` (named actions on the rows selected in a list, which receive records the framework loaded through the list scope, never ids; a `Detached` action runs outside the framework transaction), `pact.HasAdminRecordActions` with `pact.AdminRecordAction`, `pact.AdminRecordActionInput` and `pact.AdminRecordActionResult` (named actions on one record, each with an `Applies` rule for the record's state), and `pact.AdminPartialData` (the curated view model a partial template renders).
|
||||
@@ -119,7 +119,7 @@ func (p *Plugin) Schedule() []pact.ScheduledCommand {
|
||||
| `pact.HasPermissions` | Declares backend permissions. |
|
||||
| `pact.TokenScope` | One personal API token scope: name, label, description, permission patterns and HTTP methods. |
|
||||
| `pact.HasTokenScopes` | Declares personal API token scopes compiled at admin boot. |
|
||||
| `pact.NavigationItem` | One backend navigation entry, with an optional side menu. |
|
||||
| `pact.NavigationItem` | One backend navigation entry, with an optional side menu and an optional `Visible` func the admin runs on every navigation request. |
|
||||
| `pact.SettingsItem` | One settings screen entry: a singleton settings form (`Model`, `Form`, `NewModel`), or a link to an admin controller when `Controller` names its ID, as a WinterCMS `'url' => Backend::url(...)` entry. |
|
||||
| `pact.AdminController` | Admin controller identity: ID, model name and YAML config directory. |
|
||||
| `pact.AdminAssets` | Embedded tree of the plugin's admin YAML. |
|
||||
|
||||
@@ -537,7 +537,10 @@ type HasTokenScopes interface {
|
||||
}
|
||||
|
||||
// NavigationItem is one registerNavigation() entry. Controller is the admin
|
||||
// controller ID; the SPA derives its route from that ID.
|
||||
// controller ID; the SPA derives its route from that ID. Every field but
|
||||
// Visible is static and compiled once at boot; Visible is the one hook the
|
||||
// admin runs per navigation request, so a plugin can hide a screen whose
|
||||
// feature is turned off in its settings without a restart.
|
||||
type NavigationItem struct {
|
||||
Code string
|
||||
Label string
|
||||
@@ -546,6 +549,14 @@ type NavigationItem struct {
|
||||
Order int
|
||||
Controller string
|
||||
SideMenu []NavigationItem
|
||||
// Visible decides, per request, whether the entry is listed. Nil means
|
||||
// always listed. When set, the admin (cabana) calls it on every
|
||||
// navigation request with the request context, after the permission
|
||||
// check has passed, and leaves the entry out when it answers false; a
|
||||
// main item hidden this way takes its whole side menu with it. The func
|
||||
// must be cheap, must not panic, and should answer true when it cannot
|
||||
// decide (no database yet, a load error).
|
||||
Visible func(ctx context.Context) bool `json:"-"`
|
||||
}
|
||||
|
||||
// HasNavigation is implemented by plugins that declare backend navigation.
|
||||
|
||||
Reference in New Issue
Block a user