feat(19.6.1-01): Administrators screen and cross-controller record actions

- pact.HasAdminActionExtensions / AdminActionExtension: plugins add record
  actions to any admin controller by ID, merged at boot before permission
  validation (unknown target, no preview, duplicate name stop the boot)
- internal summercms.cabana pseudo-plugin, appended only when some plugin
  registers admin controllers: Administrators list and preview, settings
  link under System, backend.manage_users and backend.manage_roles with no
  role grants
- backend::lang user, user.role and permissions copy (en, pl)
- smoke tests: compile-time seam and settings, Postgres list/preview/action
This commit is contained in:
Jakub Zych
2026-10-22 10:59:23 +02:00
parent 88ec4df2b3
commit 7340241f22
19 changed files with 1239 additions and 5 deletions

View File

@@ -71,6 +71,10 @@ func (s *service) apiBase() string {
// Activate compiles admin controllers and, when any exist, requires
// admin.jwt.secret. No controllers means no admin routes and no secret check.
// When some plugin registers admin controllers, the admin's own
// Administrators and Roles screens (an internal pseudo-plugin with ID
// summercms.cabana) are compiled with them, and the record actions plugins
// add to any controller through pact.HasAdminActionExtensions are merged in.
func Activate(app *backpack.App, plugins []party.Plugin) (*Routes, error) {
items, err := collectControllers(plugins)
if err != nil {
@@ -79,6 +83,12 @@ func Activate(app *backpack.App, plugins []party.Plugin) (*Routes, error) {
if len(items) == 0 {
return nil, nil
}
// The admin is active: its own screens join the plugins for every later
// step. plugins is the caller's slice and is never modified.
plugins, screens := withBackendScreens(app, plugins)
if items, err = collectControllers(plugins); err != nil {
return nil, err
}
if err := checkReservedSegments(items); err != nil {
return nil, err
}
@@ -122,9 +132,15 @@ func Activate(app *backpack.App, plugins []party.Plugin) (*Routes, error) {
if err := checkDeferredModels(reg, plugins, gdb); err != nil {
return nil, err
}
// Contributed record actions are merged before compileContributions,
// which validates every record action's permissions.
if err := compileActionExtensions(reg, plugins); err != nil {
return nil, err
}
if err := compileContributions(reg, plugins); err != nil {
return nil, err
}
screens.reg = reg
if err := compileTokenScopes(reg, plugins); err != nil {
return nil, err
}