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{} }, }} }