- admin-controllers: placement, values, Load/Save, permissions, widgets, partials and boot errors, with the ExtrasPlugin snippet - Example_formExtension compiles an owner form with the extension - coming-from-wintercms maps extendFormFields, addFields, addTabFields and removeField to the form extension - cabana README feature bullet and Activate row
148 lines
4.9 KiB
Go
148 lines
4.9 KiB
Go
package cabana
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io/fs"
|
|
"strconv"
|
|
"strings"
|
|
"testing/fstest"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
"git.golem15.com/golem15/summercms/modules/party"
|
|
)
|
|
|
|
// ExtraValue is a row of the acme.extras plugin's own table: the subtitle
|
|
// and the featured flag it keeps for a post of the acme.blog plugin.
|
|
type ExtraValue struct {
|
|
PostID uint `gorm:"column:post_id;primaryKey;autoIncrement:false"`
|
|
Subtitle string `gorm:"column:subtitle"`
|
|
Featured bool `gorm:"column:featured"`
|
|
}
|
|
|
|
func (ExtraValue) TableName() string { return "acme_extras_values" }
|
|
|
|
// extrasFields is the extension's fields document: fields.yaml keys plus
|
|
// before:/after: anchors, and a tabs: list for the new tab.
|
|
const extrasFields = `tabs:
|
|
- name: acme.extras::lang.tab.publishing
|
|
after: acme.blog::lang.tab.content
|
|
fields:
|
|
subtitle:
|
|
label: acme.extras::lang.subtitle
|
|
type: text
|
|
span: left
|
|
after: title
|
|
featured:
|
|
label: acme.extras::lang.featured
|
|
type: switch
|
|
tab: acme.extras::lang.tab.publishing
|
|
`
|
|
|
|
// ExtrasPlugin adds a subtitle after the title and a Publishing tab to the
|
|
// acme.blog posts form, which it does not own, and hides the owner's summary
|
|
// field. It stores its values in its own table.
|
|
type ExtrasPlugin struct{}
|
|
|
|
func (ExtrasPlugin) ID() string { return "acme.extras" }
|
|
func (ExtrasPlugin) Requires() []string { return []string{"acme.blog"} }
|
|
func (ExtrasPlugin) Register(*backpack.App) error { return nil }
|
|
func (ExtrasPlugin) Boot(*backpack.App) error { return nil }
|
|
func (ExtrasPlugin) AdminFS() fs.FS {
|
|
return fstest.MapFS{"extensions/posts.yaml": &fstest.MapFile{Data: []byte(extrasFields)}}
|
|
}
|
|
|
|
var _ pact.HasAdminActionExtensions = ExtrasPlugin{}
|
|
|
|
// AdminActionExtensions adds fields and a tab to the acme.blog posts form.
|
|
func (ExtrasPlugin) AdminActionExtensions() []pact.AdminActionExtension {
|
|
return []pact.AdminActionExtension{{
|
|
Controller: "acme.blog.posts",
|
|
Form: &pact.AdminFormExtension{
|
|
Fields: "extensions/posts.yaml", // in this plugin's AdminFS
|
|
Hide: []string{"summary"},
|
|
Permissions: []string{"acme.extras.edit"},
|
|
Load: func(ctx context.Context, in pact.AdminFormLoad) (map[string]any, error) {
|
|
var row ExtraValue
|
|
err := in.DB.Where("post_id = ?", in.ID).Limit(1).Find(&row).Error
|
|
return map[string]any{"subtitle": row.Subtitle, "featured": row.Featured}, err
|
|
},
|
|
Save: func(ctx context.Context, in pact.AdminFormSave) (pact.AdminFormSaveResult, error) {
|
|
subtitle, _ := in.Values["subtitle"].(string)
|
|
if len(subtitle) > 120 {
|
|
return pact.AdminFormSaveResult{}, pact.AdminFormErrors{"subtitle": {"acme.extras::lang.subtitle_too_long"}}
|
|
}
|
|
id, err := strconv.ParseUint(in.ID, 10, 64)
|
|
if err != nil {
|
|
return pact.AdminFormSaveResult{}, err
|
|
}
|
|
featured, _ := in.Values["featured"].(bool)
|
|
row := ExtraValue{PostID: uint(id), Subtitle: subtitle, Featured: featured}
|
|
// in.DB is the record's save transaction: an error rolls back
|
|
// the owner's row too.
|
|
return pact.AdminFormSaveResult{}, in.DB.Save(&row).Error
|
|
},
|
|
},
|
|
}}
|
|
}
|
|
|
|
// exampleBlog is the owner: acme.blog with a posts form of its own.
|
|
type exampleBlog struct{}
|
|
|
|
func (exampleBlog) ID() string { return "acme.blog" }
|
|
func (exampleBlog) Requires() []string { return nil }
|
|
func (exampleBlog) Register(*backpack.App) error { return nil }
|
|
func (exampleBlog) Boot(*backpack.App) error { return nil }
|
|
func (exampleBlog) AdminControllers() []pact.AdminController {
|
|
return []pact.AdminController{feCtl{id: "acme.blog.posts"}}
|
|
}
|
|
func (exampleBlog) AdminFS() fs.FS {
|
|
return feOwnerPlugin{id: "acme.blog", fields: `fields:
|
|
title:
|
|
label: acme.blog::lang.title
|
|
type: text
|
|
span: left
|
|
summary:
|
|
label: acme.blog::lang.summary
|
|
type: text
|
|
body:
|
|
label: acme.blog::lang.body
|
|
type: text
|
|
tab: acme.blog::lang.tab.content
|
|
meta:
|
|
label: acme.blog::lang.meta
|
|
type: text
|
|
tab: acme.blog::lang.tab.meta
|
|
`}.AdminFS()
|
|
}
|
|
|
|
// Example_formExtension compiles the owner's form with the extension, as
|
|
// the admin does at boot, and prints each tab with its fields.
|
|
func Example_formExtension() {
|
|
plugins := []party.Plugin{exampleBlog{}, ExtrasPlugin{}}
|
|
reg, err := feCompile(plugins...)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
cc, _ := reg.Get("acme.blog.posts")
|
|
var tabs []string
|
|
fields := map[string][]string{}
|
|
for _, field := range cc.Form.Fields {
|
|
tab := displayTab(field.Tab)
|
|
if _, seen := fields[tab]; !seen {
|
|
tabs = append(tabs, tab)
|
|
}
|
|
fields[tab] = append(fields[tab], field.Name)
|
|
}
|
|
for _, tab := range tabs {
|
|
fmt.Printf("%s: %s\n", tab, strings.Join(fields[tab], ", "))
|
|
}
|
|
// Output:
|
|
// default: title, subtitle
|
|
// acme.blog::lang.tab.content: body
|
|
// acme.extras::lang.tab.publishing: featured
|
|
// acme.blog::lang.tab.meta: meta
|
|
}
|