- docs/backend: admin controllers, forms, lists and filters, relation manager, users and permissions, settings, partials and widgets, admin SPA - docs/services: storage, outbound HTTP, realtime, Web Push, search, parity testing and the Frontend and AJAX (not provided) page - Examples for cabana (with testdata/docs YAML), fetchguard, lighthouse and its centrifugo driver, flare, beachcomber and typesense, tide; lighthouse and beachcomber TestDocs* regions run on their Postgres harnesses - concept map rows link their guide pages and the not-provided rows the Frontend and AJAX page; index lists Backend, Database and Services - TestDocsRequiredPages asserts the D-08 section order
134 lines
4.7 KiB
Go
134 lines
4.7 KiB
Go
package cabana_test
|
|
|
|
import (
|
|
"io/fs"
|
|
"os"
|
|
"time"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
)
|
|
|
|
// Post is the model behind the acme.blog posts controller.
|
|
type Post struct {
|
|
ID uint `gorm:"column:id;primaryKey"`
|
|
Title string `gorm:"column:title"`
|
|
Slug string `gorm:"column:slug"`
|
|
Status string `gorm:"column:status"`
|
|
Published bool `gorm:"column:published"`
|
|
PublishedAt *time.Time `gorm:"column:published_at"`
|
|
Content string `gorm:"column:content"`
|
|
}
|
|
|
|
func (Post) TableName() string { return "acme_blog_posts" }
|
|
|
|
// Fillable lists the columns the admin form may write.
|
|
func (Post) Fillable() []string {
|
|
return []string{"title", "slug", "status", "published", "content"}
|
|
}
|
|
|
|
// PostsController is the admin controller for posts: the Go form of a
|
|
// WinterCMS controller with the List and Form behaviours.
|
|
type PostsController struct{}
|
|
|
|
var (
|
|
_ pact.AdminController = PostsController{}
|
|
_ pact.AdminRecordSource = PostsController{}
|
|
_ pact.AdminPermissioned = PostsController{}
|
|
_ pact.HasAdminControllers = (*BlogPlugin)(nil)
|
|
)
|
|
|
|
// ID is the controller ID; its admin API path is /acme/blog/posts.
|
|
func (PostsController) ID() string { return "acme.blog.posts" }
|
|
|
|
// ModelName must equal modelClass in the YAML.
|
|
func (PostsController) ModelName() string { return "Post" }
|
|
|
|
// ConfigDir holds config_list.yaml, config_form.yaml and config_filter.yaml.
|
|
func (PostsController) ConfigDir() string { return "controllers/posts" }
|
|
|
|
// NewRecord returns the model the generic admin handlers query.
|
|
func (PostsController) NewRecord() any { return &Post{} }
|
|
|
|
// RequiredPermissions are checked before any schema or query.
|
|
func (PostsController) RequiredPermissions() []string {
|
|
return []string{"acme.blog.access_posts"}
|
|
}
|
|
|
|
// BlogSettings is the singleton row behind the plugin's settings page.
|
|
type BlogSettings struct {
|
|
ID uint `gorm:"column:id;primaryKey"`
|
|
PostsPerPage int `gorm:"column:posts_per_page"`
|
|
CommentsEnabled bool `gorm:"column:comments_enabled"`
|
|
}
|
|
|
|
func (BlogSettings) TableName() string { return "acme_blog_settings" }
|
|
|
|
func (BlogSettings) Fillable() []string { return []string{"posts_per_page", "comments_enabled"} }
|
|
|
|
// Rules validates a settings save, as a WinterCMS settings model's $rules.
|
|
func (BlogSettings) Rules() map[string]string {
|
|
return map[string]string{"posts_per_page": "required|integer|between:1,100"}
|
|
}
|
|
|
|
// BlogPlugin is the acme.blog plugin; only its backend surface is shown.
|
|
type BlogPlugin struct{}
|
|
|
|
var (
|
|
_ pact.AdminAssets = (*BlogPlugin)(nil)
|
|
_ pact.HasPermissions = (*BlogPlugin)(nil)
|
|
_ pact.HasNavigation = (*BlogPlugin)(nil)
|
|
_ pact.HasSettings = (*BlogPlugin)(nil)
|
|
)
|
|
|
|
func (p *BlogPlugin) ID() string { return "acme.blog" }
|
|
func (p *BlogPlugin) Requires() []string { return nil }
|
|
func (p *BlogPlugin) Register(app *backpack.App) error { return nil }
|
|
func (p *BlogPlugin) Boot(app *backpack.App) error { return nil }
|
|
|
|
// AdminControllers registers the plugin's admin controllers.
|
|
func (p *BlogPlugin) AdminControllers() []pact.AdminController {
|
|
return []pact.AdminController{PostsController{}}
|
|
}
|
|
|
|
// AdminFS is the plugin's embedded controllers/ and models/ tree. A real
|
|
// plugin returns an embed.FS; the example reads the same files from testdata.
|
|
func (p *BlogPlugin) AdminFS() fs.FS { return os.DirFS("testdata/docs") }
|
|
|
|
// Permissions replaces registerPermissions().
|
|
func (p *BlogPlugin) Permissions() []pact.Permission {
|
|
return []pact.Permission{
|
|
{Code: "acme.blog.access_posts", Tab: "acme.blog::lang.plugin.name", Label: "acme.blog::lang.permissions.posts"},
|
|
{Code: "acme.blog.access_settings", Tab: "acme.blog::lang.plugin.name", Label: "acme.blog::lang.permissions.settings"},
|
|
}
|
|
}
|
|
|
|
// Navigation replaces registerNavigation().
|
|
func (p *BlogPlugin) Navigation() []pact.NavigationItem {
|
|
return []pact.NavigationItem{{
|
|
Code: "blog",
|
|
Label: "acme.blog::lang.plugin.name",
|
|
Icon: "icon-pencil",
|
|
Permissions: []string{"acme.blog.access_posts"},
|
|
Controller: "acme.blog.posts",
|
|
SideMenu: []pact.NavigationItem{
|
|
{Code: "posts", Label: "acme.blog::lang.posts.title", Controller: "acme.blog.posts"},
|
|
},
|
|
}}
|
|
}
|
|
|
|
// Settings replaces registerSettings().
|
|
func (p *BlogPlugin) Settings() []pact.SettingsItem {
|
|
return []pact.SettingsItem{{
|
|
Code: "blog",
|
|
Label: "acme.blog::lang.settings.label",
|
|
Description: "acme.blog::lang.settings.description",
|
|
Category: "acme.blog::lang.plugin.name",
|
|
Icon: "icon-pencil",
|
|
Model: "BlogSettings",
|
|
Permissions: []string{"acme.blog.access_settings"},
|
|
Form: "models/settings/fields.yaml",
|
|
NewModel: func() any { return &BlogSettings{} },
|
|
}}
|
|
}
|