Files
summercms/modules/cabana/example_action_extension_test.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

62 lines
2.2 KiB
Go

package cabana_test
import (
"context"
"errors"
"testing"
"git.golem15.com/golem15/summercms/modules/cabana"
"git.golem15.com/golem15/summercms/modules/pact"
)
// ProfileLink is a row of the acme.profiles plugin that links a frontend
// profile to a backend administrator.
type ProfileLink struct {
ID uint `gorm:"column:id;primaryKey"`
BackendUserID uint `gorm:"column:backend_user_id"`
}
func (ProfileLink) TableName() string { return "acme_profiles_links" }
// ProfilesPlugin adds a record action to the admin's Administrators screen,
// a controller it does not own.
type ProfilesPlugin struct{}
var _ pact.HasAdminActionExtensions = ProfilesPlugin{}
// AdminActionExtensions adds an action to the Administrators screen.
func (ProfilesPlugin) AdminActionExtensions() []pact.AdminActionExtension {
return []pact.AdminActionExtension{{
Controller: cabana.AdministratorsControllerID,
RecordActions: []pact.AdminRecordAction{{
Name: "acme_unlink_profile",
Label: "acme.profiles::lang.unlink",
Confirm: "acme.profiles::lang.unlink_confirm",
Permissions: []string{"acme.profiles.manage_links"},
Run: func(ctx context.Context, in pact.AdminRecordActionInput) (pact.AdminRecordActionResult, error) {
tx, ok := cabana.TxFromContext(ctx) // the action's row-locked transaction
if !ok {
return pact.AdminRecordActionResult{}, errors.New("acme.profiles: no transaction")
}
err := tx.Where("backend_user_id = ?", in.RecordID).Delete(&ProfileLink{}).Error
return pact.AdminRecordActionResult{Message: "acme.profiles::lang.unlinked"}, err
},
}},
}}
}
func TestExampleActionExtension(t *testing.T) {
extensions := ProfilesPlugin{}.AdminActionExtensions()
if len(extensions) != 1 || extensions[0].Controller != "summercms.cabana.users" || len(extensions[0].RecordActions) != 1 {
t.Fatalf("extensions = %+v", extensions)
}
action := extensions[0].RecordActions[0]
if action.Name != "acme_unlink_profile" || action.Run == nil {
t.Fatalf("action = %+v", action)
}
// Outside the record action route there is no transaction.
if _, err := action.Run(context.Background(), pact.AdminRecordActionInput{RecordID: 1}); err == nil {
t.Fatal("Run without a transaction succeeded")
}
}