- phrasebook ships the backend::lang admin strings (pl, en) with CLDR plural maps, loads them as namespace backend, applies pact.HasLangOverrides trees (lang/<locale>/<namespace>/<group>.yaml) after every namespace, and fails activation when a backend key cannot convert to plural forms - Translator.Forms, Bundle, Resolved and Has serve keys as CLDR form maps - Public GET /lang returns every backend::lang key for the request locale over the fallback locale, Cache-Control no-cache - config_list, config_form and config_relation accept a strict messages block; omitted keys take framework defaults, schemas serve every message as CLDR forms, and activation fails on a missing phrase key - toolbar.buttons is an ordered [create, delete] list; the Winter string form, duplicates, unknown actions and delete without showCheckboxes fail at boot, and create is dropped when the controller has no form - Form schema serves the raw Winter redirects; scaffold emits the list syntax; form and relation schema routes are typed in the admin OpenAPI
307 lines
10 KiB
Go
307 lines
10 KiB
Go
package pact
|
|
|
|
import (
|
|
"context"
|
|
"io/fs"
|
|
"net/http"
|
|
|
|
"git.golem15.com/golem15/summercms/bonfire"
|
|
"github.com/go-gormigrate/gormigrate/v2"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// HasCommands is implemented by plugins that register console commands.
|
|
type HasCommands interface {
|
|
Commands() []bonfire.Command
|
|
}
|
|
|
|
// HasConfig is implemented by plugins that ship default YAML configuration.
|
|
// ConfigFS should contain files such as config/config.yaml, merged later at
|
|
// the plugin ID path.
|
|
type HasConfig interface {
|
|
ConfigFS() fs.FS
|
|
}
|
|
|
|
// HasMigrations is implemented by plugins that ship an ordered gormigrate
|
|
// set. The kernel runs each set in party.Activate order with a separate
|
|
// history table per plugin ID.
|
|
type HasMigrations interface {
|
|
Migrations() []*gormigrate.Migration
|
|
}
|
|
|
|
// Middleware is a named HTTP wrapper registered by a plugin.
|
|
type Middleware func(http.Handler) http.Handler
|
|
|
|
// HasMiddleware is implemented by plugins that register named middleware.
|
|
type HasMiddleware interface {
|
|
Middlewares() map[string]Middleware
|
|
}
|
|
|
|
// HasMiddlewareFactories is implemented by plugins that register
|
|
// parameterized middleware ("name:param"), resolved at wrap time.
|
|
type HasMiddlewareFactories interface {
|
|
MiddlewareFactories() map[string]func(param string) Middleware
|
|
}
|
|
|
|
// HasHouseMiddleware is implemented by plugins that register middleware
|
|
// tagged as house-envelope/error handling -- refused inside a raw group
|
|
// (D-16). This is the ONLY way a plugin declares a house-tagged name:
|
|
// plugins never call a Router.Register* method directly (there is no such
|
|
// call site anywhere in this codebase -- RegisterMiddleware/
|
|
// RegisterMiddlewareFactory/RegisterHouseMiddleware are all called
|
|
// exclusively from surf.Assemble/BuildRouter's plugin loop, the same way
|
|
// HasMiddleware's Middlewares() map is today). A name present in both
|
|
// Middlewares() and HouseMiddlewares() (from the same or a different
|
|
// plugin) fails boot with the existing duplicate-name error, since both are
|
|
// registered into the same underlying name table.
|
|
type HasHouseMiddleware interface {
|
|
HouseMiddlewares() map[string]Middleware
|
|
}
|
|
|
|
// Router is the Laravel-like group builder implemented by surf.
|
|
type Router interface {
|
|
Group(prefix string, middleware []string, fn func(Router))
|
|
GroupRaw(prefix string, middleware []string, fn func(Router))
|
|
Get(path string, handler http.HandlerFunc, middleware ...string)
|
|
Post(path string, handler http.HandlerFunc, middleware ...string)
|
|
Put(path string, handler http.HandlerFunc, middleware ...string)
|
|
Patch(path string, handler http.HandlerFunc, middleware ...string)
|
|
Delete(path string, handler http.HandlerFunc, middleware ...string)
|
|
Where(param, pattern string)
|
|
WhereIn(param string, values ...string)
|
|
}
|
|
|
|
// HasRoutes is implemented by plugins that declare HTTP routes.
|
|
type HasRoutes interface {
|
|
Routes(Router) error
|
|
}
|
|
|
|
// HasModels is implemented by plugins that expose GORM models.
|
|
type HasModels interface {
|
|
Models() []any
|
|
}
|
|
|
|
// JobArgs is the typed payload a job worker receives. Kind identifies the
|
|
// job so a worker can reject unexpected argument types.
|
|
type JobArgs interface {
|
|
Kind() string
|
|
}
|
|
|
|
// Job is a unit of background work. Phase 11 adapts this onto River; the
|
|
// interface itself must not import River.
|
|
type Job interface {
|
|
Work(ctx context.Context, args JobArgs) error
|
|
}
|
|
|
|
// HasJobs is implemented by plugins that register background jobs.
|
|
type HasJobs interface {
|
|
Jobs() []Job
|
|
}
|
|
|
|
// AdminController is the compile-time admin controller contract. Phase 9
|
|
// grows the schema pipeline; ID, model name and YAML config directory are
|
|
// enough for generated stubs to compile.
|
|
type AdminController interface {
|
|
ID() string
|
|
ModelName() string
|
|
ConfigDir() string
|
|
}
|
|
|
|
// HasAdminControllers is implemented by plugins that register admin controllers.
|
|
type HasAdminControllers interface {
|
|
AdminControllers() []AdminController
|
|
}
|
|
|
|
// AdminAssets is the plugin-owned embedded tree of Winter admin YAML.
|
|
// Paths are relative to the plugin root (controllers/..., models/...).
|
|
type AdminAssets interface {
|
|
AdminFS() fs.FS
|
|
}
|
|
|
|
// AdminPermissioned is the D-03 permission list enforced before schema or SQL.
|
|
type AdminPermissioned interface {
|
|
RequiredPermissions() []string
|
|
}
|
|
|
|
// AdminRecordSource supplies the GORM model the generic admin handlers query.
|
|
// NewRecord returns a pointer to the model struct.
|
|
type AdminRecordSource interface {
|
|
NewRecord() any
|
|
}
|
|
|
|
// Permission is one registerPermissions() entry.
|
|
type Permission struct {
|
|
Code string
|
|
Tab string
|
|
Label string
|
|
Roles []string
|
|
}
|
|
|
|
// HasPermissions is implemented by plugins that declare backend permissions.
|
|
type HasPermissions interface {
|
|
Permissions() []Permission
|
|
}
|
|
|
|
// NavigationItem is one registerNavigation() entry. Controller is the admin
|
|
// controller ID; the SPA derives its route from that ID.
|
|
type NavigationItem struct {
|
|
Code string
|
|
Label string
|
|
Icon string
|
|
Permissions []string
|
|
Order int
|
|
Controller string
|
|
SideMenu []NavigationItem
|
|
}
|
|
|
|
// HasNavigation is implemented by plugins that declare backend navigation.
|
|
type HasNavigation interface {
|
|
Navigation() []NavigationItem
|
|
}
|
|
|
|
// SettingsItem is one registerSettings() entry.
|
|
type SettingsItem struct {
|
|
Code string
|
|
Label string
|
|
Description string
|
|
Category string
|
|
Icon string
|
|
Model string
|
|
Order int
|
|
Keywords []string
|
|
Permissions []string
|
|
Form string `json:"-"`
|
|
NewModel func() any `json:"-"`
|
|
}
|
|
|
|
// HasSettings is implemented by plugins that declare settings screens.
|
|
type HasSettings interface {
|
|
Settings() []SettingsItem
|
|
}
|
|
|
|
// Option is one dropdown choice. Label may be a phrase key until request time.
|
|
type Option struct {
|
|
Value string `json:"value"`
|
|
Label string `json:"label"`
|
|
}
|
|
|
|
// DropdownOptionsProvider serves method-backed dropdown options.
|
|
type DropdownOptionsProvider interface {
|
|
DropdownOptions(field string) []Option
|
|
}
|
|
|
|
// ListExtendQuery optionally narrows the admin list query.
|
|
type ListExtendQuery interface {
|
|
ListExtendQuery(ctx context.Context, db *gorm.DB) *gorm.DB
|
|
}
|
|
|
|
// ListRelationColumnMapper maps a source-schema relation column onto the
|
|
// physical column exposed by the related Go model. It supports legacy admin
|
|
// schemas whose public field names no longer match the database schema.
|
|
type ListRelationColumnMapper interface {
|
|
ListRelationColumn(relation, column string) (string, bool)
|
|
}
|
|
|
|
// FormExtendQuery optionally narrows admin form record lookup.
|
|
type FormExtendQuery interface {
|
|
FormExtendQuery(ctx context.Context, db *gorm.DB) *gorm.DB
|
|
}
|
|
|
|
// FormBeforeCreate optionally rejects or stamps a record before insert.
|
|
type FormBeforeCreate interface {
|
|
FormBeforeCreate(ctx context.Context, model any) error
|
|
}
|
|
|
|
// FormBeforeUpdate optionally rejects or stamps a record before update.
|
|
type FormBeforeUpdate interface {
|
|
FormBeforeUpdate(ctx context.Context, model any) error
|
|
}
|
|
|
|
// FormAfterCreate optionally runs after insert, inside the same transaction.
|
|
type FormAfterCreate interface {
|
|
FormAfterCreate(ctx context.Context, model any) error
|
|
}
|
|
|
|
// FormAfterUpdate optionally runs after update, inside the same transaction.
|
|
type FormAfterUpdate interface {
|
|
FormAfterUpdate(ctx context.Context, model any) error
|
|
}
|
|
|
|
// FormBeforeDelete optionally rejects a record before delete.
|
|
type FormBeforeDelete interface {
|
|
FormBeforeDelete(ctx context.Context, model any) error
|
|
}
|
|
|
|
// FormAfterDelete optionally runs after delete, inside the same transaction.
|
|
type FormAfterDelete interface {
|
|
FormAfterDelete(ctx context.Context, model any) error
|
|
}
|
|
|
|
// RelationExtendManageQuery optionally narrows relation-manager candidates.
|
|
type RelationExtendManageQuery interface {
|
|
RelationExtendManageQuery(ctx context.Context, relation string, db *gorm.DB) *gorm.DB
|
|
}
|
|
|
|
// RelationExtendOptionsQuery optionally narrows the rows a form relation
|
|
// field offers (D-17). The same scoped query revalidates submitted ids on
|
|
// save, so a row it does not return cannot be attached (D-18).
|
|
type RelationExtendOptionsQuery interface {
|
|
RelationExtendOptionsQuery(ctx context.Context, field string, db *gorm.DB) *gorm.DB
|
|
}
|
|
|
|
// RelationBeforeLink optionally stamps pivot columns before a link insert.
|
|
type RelationBeforeLink interface {
|
|
RelationBeforeLink(ctx context.Context, relation string, parent, related any, pivot map[string]any) error
|
|
}
|
|
|
|
// FilterScope is the model capability a config_filter scope name may call.
|
|
// FilterScopes is the exact, case-sensitive set of names; request text never
|
|
// selects a method outside that set.
|
|
type FilterScope interface {
|
|
FilterScopes() []string
|
|
FilterScope(name string, db *gorm.DB, value any) *gorm.DB
|
|
}
|
|
|
|
// HasLang is implemented by plugins that ship embedded translation YAML
|
|
// under lang/<locale>/<group>.yaml.
|
|
type HasLang interface {
|
|
LangFS() fs.FS
|
|
}
|
|
|
|
// HasLangOverrides is implemented by plugins (usually the app plugin) that
|
|
// replace or add translations of any loaded namespace, including the
|
|
// framework's backend::lang admin strings, and may add locales (D-20).
|
|
// Layout: lang/<locale>/<namespace>/<group>.yaml, e.g.
|
|
// lang/pl/backend/lang.yaml.
|
|
type HasLangOverrides interface {
|
|
LangOverridesFS() fs.FS
|
|
}
|
|
|
|
// HasMailTemplates is implemented by plugins that ship Winter-shaped mail
|
|
// templates and layout aliases. MailTemplatesFS contains views/mail assets;
|
|
// MailTemplates lists dotted template names; MailLayouts maps a short layout
|
|
// name to the full dotted layout name.
|
|
type HasMailTemplates interface {
|
|
MailTemplatesFS() fs.FS
|
|
MailTemplates() []string
|
|
MailLayouts() map[string]string
|
|
}
|
|
|
|
// OptionalMessage is a service an optional plugin may publish so other
|
|
// plugins can integrate without importing that plugin's package.
|
|
type OptionalMessage interface {
|
|
Message() string
|
|
}
|
|
|
|
// Future capability families are type-asserted when their first consumer
|
|
// packages exist:
|
|
//
|
|
// HasListeners
|
|
// HasSchedule
|
|
//
|
|
// The kernel type-asserts HasConfig (party, before Register), HasCommands
|
|
// (generated app main, after Boot), HasMigrations (lagoon migrate), and
|
|
// HasMiddleware/HasMiddlewareFactories/HasHouseMiddleware/HasRoutes
|
|
// (surf assemble). surf.BucketProvider is type-asserted in Assemble/
|
|
// BuildRouter (not a pact interface: pact cannot import surf without a cycle).
|