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 }