Files
summercms/modules/cabana/backend_users_admin.go
Jakub Zych 7340241f22 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
2026-10-22 10:59:23 +02:00

71 lines
2.4 KiB
Go

package cabana
import (
"context"
"errors"
"strings"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/pact"
"gorm.io/gorm"
)
// usersAdmin is the Administrators screen (summercms.cabana.users), the
// WinterCMS Backend\Controllers\Users controller over backend_users. Every
// route needs backend.manage_users. screens gives access to the activating
// app and the compiled registry at request time.
type usersAdmin struct {
screens *backendScreens
}
var (
_ pact.AdminController = usersAdmin{}
_ pact.AdminPermissioned = usersAdmin{}
_ pact.AdminRecordSource = usersAdmin{}
_ pact.FormRecordTitle = usersAdmin{}
_ FieldRelationProvider = usersAdmin{}
)
func (usersAdmin) ID() string { return AdministratorsControllerID }
func (usersAdmin) ModelName() string { return BackendUserMorphType }
func (usersAdmin) ConfigDir() string { return "controllers/users" }
func (usersAdmin) RequiredPermissions() []string { return []string{PermissionManageUsers} }
func (usersAdmin) NewRecord() any { return &BackendUser{} }
// application is the activating app, or nil.
func (c usersAdmin) application() *backpack.App { return c.screens.application() }
// registry is the compiled registry, or nil before compilation finished.
func (c usersAdmin) registry() *Registry { return c.screens.registry() }
// FormRecordTitle names an administrator "First Last" when either name is
// set, otherwise by the login.
func (usersAdmin) FormRecordTitle(_ context.Context, _ *gorm.DB, record any) (string, error) {
user, ok := record.(*BackendUser)
if !ok || user == nil {
return "", errors.New("cabana: administrators: record is not a backend user")
}
if name := strings.TrimSpace(strings.TrimSpace(user.FirstName) + " " + strings.TrimSpace(user.LastName)); name != "" {
return name, nil
}
return user.Login, nil
}
// AdminFieldRelations binds the role picker. role_id is a protected column,
// so the contract opts in to writing it through this one field; the
// submitted id is still checked against the options and the role locks.
func (usersAdmin) AdminFieldRelations() []FieldRelationContract {
return []FieldRelationContract{{
Field: "role",
Kind: "belongsTo",
NewRelated: func() any { return &BackendUserRole{} },
ForeignKey: "role_id",
LabelColumn: "name",
WritableForeignKey: true,
}}
}