Files
summercms/modules/cabana/backend_screens.go
Jakub Zych 653307de0c feat(19.6.1-01): Roles screen
- Settings -> System -> Roles (summercms.cabana.roles, backend.manage_roles,
  order 110): name, code slugged from the name, description, checkbox
  permission editor with the editor's ceiling
- system roles (is_system or code-declared grants) keep their code and
  cannot be deleted; a role any admin holds (deleted ones included) cannot
  be deleted, the refusal names the role and the count
- role codes unique among roles
2026-10-22 11:18:30 +02:00

140 lines
5.0 KiB
Go

package cabana
import (
"embed"
"io/fs"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/pact"
"git.golem15.com/golem15/summercms/modules/party"
)
// The admin's own screens for backend administrators and roles (Settings →
// System), registered by cabana itself through an internal pseudo-plugin.
const (
// PermissionManageUsers opens the Administrators screen. No role is
// granted it in code: until an administrator grants it, only superusers
// reach the screen.
PermissionManageUsers = "backend.manage_users"
// PermissionManageRoles opens the Roles screen. No role is granted it in
// code either.
PermissionManageRoles = "backend.manage_roles"
// AdministratorsControllerID is the admin controller ID of the
// Administrators screen, the target other plugins name to add record
// actions to it (pact.HasAdminActionExtensions).
AdministratorsControllerID = "summercms.cabana.users"
// RolesControllerID is the admin controller ID of the Roles screen.
RolesControllerID = "summercms.cabana.roles"
)
// backendScreensPluginID is the pseudo-plugin's ID. It is also the
// migration history ID of the backend tables (lagoon), which is untouched:
// the pseudo-plugin declares no migrations.
const backendScreensPluginID = "summercms.cabana"
// backendScreensFS holds the screens' admin YAML and partials. all: keeps
// the partials, whose names start with an underscore.
//
//go:embed all:backendscreens
var backendScreensFS embed.FS
// backendScreens is the internal pseudo-plugin that registers the
// Administrators and Roles screens, their two permissions and their settings
// entries. Activate appends it to the plugins only when some plugin
// registers admin controllers, so an application without an admin still
// boots without one. app is the activating application and reg the compiled
// registry, set once compilation is done; the controllers read both per
// request.
type backendScreens struct {
app *backpack.App
reg *Registry
}
var (
_ party.Plugin = (*backendScreens)(nil)
_ pact.AdminAssets = (*backendScreens)(nil)
_ pact.HasAdminControllers = (*backendScreens)(nil)
_ pact.HasPermissions = (*backendScreens)(nil)
_ pact.HasSettings = (*backendScreens)(nil)
)
func (*backendScreens) ID() string { return backendScreensPluginID }
func (*backendScreens) Requires() []string { return nil }
func (*backendScreens) Register(*backpack.App) error { return nil }
func (*backendScreens) Boot(*backpack.App) error { return nil }
// AdminFS serves the embedded tree rooted at backendscreens/, so its paths
// start at controllers/ and models/.
func (*backendScreens) AdminFS() fs.FS {
sub, err := fs.Sub(backendScreensFS, "backendscreens")
if err != nil {
return nil
}
return sub
}
func (s *backendScreens) AdminControllers() []pact.AdminController {
return []pact.AdminController{usersAdmin{screens: s}, rolesAdmin{screens: s}}
}
// Permissions declares the two screen permissions with no role grants
// (D-04): an upgrade gives no existing role access to the screens.
func (*backendScreens) Permissions() []pact.Permission {
return []pact.Permission{
{Code: PermissionManageUsers, Tab: "backend::lang.permissions.tab_system", Label: "backend::lang.permissions.manage_users"},
{Code: PermissionManageRoles, Tab: "backend::lang.permissions.tab_system", Label: "backend::lang.permissions.manage_roles"},
}
}
// Settings links the screens from Settings → System; there is no navigation
// entry.
func (*backendScreens) Settings() []pact.SettingsItem {
return []pact.SettingsItem{
{
Code: "administrators",
Label: "backend::lang.user.menu_label",
Description: "backend::lang.user.menu_description",
Category: "backend::lang.settings.category_system",
Icon: "users",
Order: 100,
Permissions: []string{PermissionManageUsers},
Controller: AdministratorsControllerID,
},
{
Code: "roles",
Label: "backend::lang.user.role.menu_label",
Description: "backend::lang.user.role.menu_description",
Category: "backend::lang.settings.category_system",
Icon: "shield-check",
Order: 110,
Permissions: []string{PermissionManageRoles},
Controller: RolesControllerID,
},
}
}
// application is the activating app, or nil.
func (s *backendScreens) application() *backpack.App {
if s == nil {
return nil
}
return s.app
}
// registry is the compiled registry, or nil before compilation finished.
func (s *backendScreens) registry() *Registry {
if s == nil {
return nil
}
return s.reg
}
// withBackendScreens returns a new plugin slice with the pseudo-plugin
// appended, and the pseudo-plugin. plugins itself is never modified.
func withBackendScreens(app *backpack.App, plugins []party.Plugin) ([]party.Plugin, *backendScreens) {
screens := &backendScreens{app: app}
out := make([]party.Plugin, 0, len(plugins)+1)
out = append(out, plugins...)
return append(out, screens), screens
}