- pact.FormVirtualFields lists form fields that are not model columns: never bound, filled or projected; their values reach the Form hooks through cabana.VirtualFieldsFromContext when the field's context allows the operation - type: password is a masked field that must be listed as virtual - pact.FormRules supplies the rule set per operation and replaces the model's Rules() for admin saves; a rule on a virtual field sees the submitted value - preset on a text field follows another text field on the create form - SPA: PasswordField, preset handling in FormView, empty password left out of an update - README, docs, OpenAPI document, TS types and dist updated
634 lines
24 KiB
Go
634 lines
24 KiB
Go
package pact
|
|
|
|
import (
|
|
"context"
|
|
"io/fs"
|
|
"net/http"
|
|
"time"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/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
|
|
}
|
|
|
|
type cadenceKind uint8
|
|
|
|
const (
|
|
cadenceNone cadenceKind = iota
|
|
cadenceDaily
|
|
cadenceEvery
|
|
)
|
|
|
|
// Cadence is how often a scheduled command runs. Build one with Daily,
|
|
// DailyAt or Every; the zero value is no cadence and is rejected by the
|
|
// scheduler.
|
|
type Cadence struct {
|
|
kind cadenceKind
|
|
hour int
|
|
minute int
|
|
every time.Duration
|
|
}
|
|
|
|
// Daily runs once a day at 00:00 in the app timezone (Laravel ->daily()).
|
|
func Daily() Cadence { return DailyAt(0, 0) }
|
|
|
|
// DailyAt runs once a day at hour:minute in the app timezone.
|
|
func DailyAt(hour, minute int) Cadence {
|
|
return Cadence{kind: cadenceDaily, hour: hour, minute: minute}
|
|
}
|
|
|
|
// Every runs at every multiple of d since local midnight in the app
|
|
// timezone. The scheduler requires d to be at least one second and to divide
|
|
// 24h evenly.
|
|
func Every(d time.Duration) Cadence {
|
|
return Cadence{kind: cadenceEvery, every: d}
|
|
}
|
|
|
|
// IsZero reports whether c is the zero Cadence.
|
|
func (c Cadence) IsZero() bool { return c.kind == cadenceNone }
|
|
|
|
// Interval is the period of c: 24h for a daily cadence, d for Every(d) and
|
|
// zero for the zero Cadence.
|
|
func (c Cadence) Interval() time.Duration {
|
|
switch c.kind {
|
|
case cadenceDaily:
|
|
return 24 * time.Hour
|
|
case cadenceEvery:
|
|
return c.every
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// At returns the wall-clock time of a daily cadence; ok is false for Every
|
|
// and for the zero Cadence.
|
|
func (c Cadence) At() (hour, minute int, ok bool) {
|
|
if c.kind != cadenceDaily {
|
|
return 0, 0, false
|
|
}
|
|
return c.hour, c.minute, true
|
|
}
|
|
|
|
// ScheduledCommand is one recurring run of a registered console command.
|
|
// Command is the command name (namespace:verb) and Args its arguments.
|
|
type ScheduledCommand struct {
|
|
Command string
|
|
Args []string
|
|
Cadence Cadence
|
|
}
|
|
|
|
// HasSchedule is implemented by plugins that run console commands on a
|
|
// schedule. Only these compiled entries are ever executed by the scheduler.
|
|
type HasSchedule interface {
|
|
Schedule() []ScheduledCommand
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// AdminClientAssets is Winter's addJs/addCss for one admin controller. The
|
|
// paths are relative to the owning plugin's AdminFS and must live under
|
|
// assets/ (for example assets/js/lookup.js). The admin SPA loads them when the
|
|
// controller's list or form opens; files are always served from the embedded
|
|
// tree, never from disk. It is separate from AdminAssets, which is the YAML
|
|
// tree itself.
|
|
type AdminClientAssets interface {
|
|
AdminJS() []string
|
|
AdminCSS() []string
|
|
}
|
|
|
|
// AdminAction is one controller action that a list toolbar button or a form
|
|
// widget runs. The admin framework owns the HTTP route, the CSRF check,
|
|
// authentication and record scoping; Run only carries the business logic.
|
|
// Name is an identifier unique within the controller; create and delete are
|
|
// reserved for the built-in toolbar actions. Label is a phrase key or literal
|
|
// text used as the button caption. Permissions are checked in addition to the
|
|
// controller's RequiredPermissions.
|
|
type AdminAction struct {
|
|
Name string
|
|
Label string
|
|
Permissions []string
|
|
Run func(ctx context.Context, in AdminActionInput) (AdminActionResult, error) `json:"-"`
|
|
}
|
|
|
|
// AdminActionInput is what the framework hands an AdminAction. Field is the
|
|
// widget field name and is empty for a toolbar action. RecordID is nil on the
|
|
// create form and always nil for a toolbar action: toolbar actions carry no
|
|
// record ids, so an id list can never become an unscoped lookup. Record is the
|
|
// record the framework loaded through the controller's FormExtendQuery scope,
|
|
// nil when RecordID is nil. Values is the widget's snapshot of its fill
|
|
// fields, already reduced to the field's declared fill keys and to scalars.
|
|
type AdminActionInput struct {
|
|
Field string
|
|
RecordID *uint64
|
|
Record any
|
|
Values map[string]any
|
|
}
|
|
|
|
// AdminActionResult is an action's answer. Message is a phrase key or text,
|
|
// localized by the framework and shown as a toast. Fill is the widget
|
|
// write-back; keys outside the field's declared fill keys and non-scalar
|
|
// values are dropped before the response is written.
|
|
type AdminActionResult struct {
|
|
Message string
|
|
Fill map[string]any
|
|
}
|
|
|
|
// HasAdminActions is implemented by an admin controller that registers named
|
|
// actions for its toolbar.buttons list and its `type: widget` form fields.
|
|
type HasAdminActions interface {
|
|
AdminActions() []AdminAction
|
|
}
|
|
|
|
// AdminBulkAction is one controller action an administrator runs on the rows
|
|
// selected in a list (config_list.yaml bulkActions). The admin framework owns
|
|
// the HTTP route, the CSRF check, authentication, the resolution of the posted
|
|
// ids and the transaction; Run only carries the business logic and reads the
|
|
// write transaction with cabana.TxFromContext. Name is an identifier unique
|
|
// among the controller's bulk actions; create and delete are reserved, and a
|
|
// record action may reuse a bulk action's name. Label and Confirm are phrase
|
|
// keys or literal text: Label is the menu item, Confirm the confirmation
|
|
// question, and an empty Confirm means the framework's default confirm text.
|
|
// Permissions are checked in addition to the controller's
|
|
// RequiredPermissions.
|
|
type AdminBulkAction struct {
|
|
Name string
|
|
Label string
|
|
Confirm string
|
|
Permissions []string
|
|
Run func(ctx context.Context, in AdminBulkActionInput) (AdminBulkActionResult, error) `json:"-"`
|
|
}
|
|
|
|
// AdminBulkActionInput is what the framework hands an AdminBulkAction.
|
|
// Records are pointers to the controller's model, loaded and row-locked
|
|
// through the controller's ListExtendQuery scope and ordered by primary key.
|
|
// A bulk action never receives ids, so an id list can never become an
|
|
// unscoped lookup.
|
|
type AdminBulkActionInput struct {
|
|
Records []any
|
|
}
|
|
|
|
// AdminBulkActionResult is a bulk action's answer. Message is a phrase key or
|
|
// text, localized by the framework and shown as a toast; when it is empty the
|
|
// admin shows its default text with Affected. Affected is the number of
|
|
// records the action changed, which may be lower than the selection when the
|
|
// action skips rows.
|
|
type AdminBulkActionResult struct {
|
|
Message string
|
|
Affected int
|
|
}
|
|
|
|
// HasAdminBulkActions is implemented by an admin controller that registers
|
|
// named bulk actions for its config_list.yaml bulkActions list.
|
|
type HasAdminBulkActions interface {
|
|
AdminBulkActions() []AdminBulkAction
|
|
}
|
|
|
|
// AdminRecordAction is one controller action an administrator runs on a
|
|
// single record (config_form.yaml recordActions). The admin framework owns the
|
|
// HTTP route, the CSRF check, authentication, the record lookup and the
|
|
// transaction; Run only carries the business logic and reads the write
|
|
// transaction with cabana.TxFromContext. Name is an identifier unique among
|
|
// the controller's record actions; create and delete are reserved, and a bulk
|
|
// action may use the same name. Label and Confirm are phrase keys or literal
|
|
// text; an empty Confirm means the framework's default confirm text.
|
|
// Permissions are checked in addition to the controller's
|
|
// RequiredPermissions. Applies reports whether the action applies to the
|
|
// record in its current state: a nil Applies means the action always applies.
|
|
// Applies must be a pure read, because it also runs when a record is shown, to
|
|
// decide which actions the admin is offered.
|
|
type AdminRecordAction struct {
|
|
Name string
|
|
Label string
|
|
Confirm string
|
|
Permissions []string
|
|
Applies func(ctx context.Context, record any) (bool, error) `json:"-"`
|
|
Run func(ctx context.Context, in AdminRecordActionInput) (AdminRecordActionResult, error) `json:"-"`
|
|
}
|
|
|
|
// AdminRecordActionInput is what the framework hands an AdminRecordAction.
|
|
// Record is the record the framework loaded through the controller's
|
|
// FormExtendQuery scope with a row lock, inside the action's transaction;
|
|
// RecordID is its primary key.
|
|
type AdminRecordActionInput struct {
|
|
RecordID uint64
|
|
Record any
|
|
}
|
|
|
|
// AdminRecordActionResult is a record action's answer. Message is a phrase key
|
|
// or text, localized by the framework and shown as a toast; when it is empty
|
|
// the admin shows its default text.
|
|
type AdminRecordActionResult struct {
|
|
Message string
|
|
}
|
|
|
|
// HasAdminRecordActions is implemented by an admin controller that registers
|
|
// named record actions for its config_form.yaml recordActions list.
|
|
type HasAdminRecordActions interface {
|
|
AdminRecordActions() []AdminRecordAction
|
|
}
|
|
|
|
// AdminPartialData supplies the view model a controller partial template
|
|
// renders (config_list.yaml headerPartial, fields.yaml `type: partial`). name
|
|
// is the partial name; record is the scoped record for a form partial on an
|
|
// existing record, else nil. The result must be a curated view model built
|
|
// for the template, never a GORM model: the framework refuses a view model
|
|
// that carries the controller's model or any other GORM model, even nested
|
|
// in a field, a collection or an interface value.
|
|
type AdminPartialData interface {
|
|
PartialData(ctx context.Context, name string, record any) (any, error)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// RowState is one state an admin list row may carry. The set is fixed; the
|
|
// admin shows each state as a text badge and a row text style.
|
|
type RowState string
|
|
|
|
// The row states the admin understands.
|
|
const (
|
|
// RowStateDeleted marks a soft-deleted record.
|
|
RowStateDeleted RowState = "deleted"
|
|
// RowStateNegative marks a record in a blocked or otherwise negative state.
|
|
RowStateNegative RowState = "negative"
|
|
// RowStateDisabled marks an inactive record.
|
|
RowStateDisabled RowState = "disabled"
|
|
)
|
|
|
|
// ListRowStates optionally marks the rows of an admin list with states. The
|
|
// framework calls it once per list page with the page's records, in page
|
|
// order, and the list's database handle: a list does not run in a
|
|
// transaction, so cabana.TxFromContext reports none here. The result is
|
|
// index-aligned with records; a row may carry several states and a nil entry
|
|
// means none. Values outside the three RowState constants are dropped.
|
|
type ListRowStates interface {
|
|
ListRowStates(ctx context.Context, db *gorm.DB, records []any) ([][]RowState, error)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// FormVirtualFields optionally names fields of the controller's form that are
|
|
// not model columns for the purpose of the form, such as a password and its
|
|
// confirmation or a "send an invitation" checkbox. The admin framework never
|
|
// binds such a field to a column, never fills it into the model and never
|
|
// returns it in a record response. The values an administrator submits for
|
|
// them reach the Form hooks (FormBeforeCreate, FormAfterCreate,
|
|
// FormBeforeUpdate, FormAfterUpdate) through cabana.VirtualFieldsFromContext,
|
|
// and only for the fields whose `context` allows the operation. Every name
|
|
// must be a field of the form's fields.yaml of type password, text, textarea,
|
|
// number, checkbox, switch or dropdown; a `type: password` field must be
|
|
// listed here.
|
|
type FormVirtualFields interface {
|
|
FormVirtualFields() []string
|
|
}
|
|
|
|
// FormRules optionally supplies the validation rules of an admin save. op is
|
|
// "create" or "update". When a controller implements it, the returned set
|
|
// replaces the model's Rules() for saves through the admin form; the form's
|
|
// `required` flags are still merged in. Rule strings use the tokens
|
|
// lagoon.Validate supports. A rule may name a field listed by
|
|
// FormVirtualFields: it is then checked against the submitted value, never
|
|
// against a model column of the same name.
|
|
type FormRules interface {
|
|
FormRules(ctx context.Context, op string) map[string]string
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// The six relation child hooks below are optional admin-controller
|
|
// capabilities for a relation manager that creates, updates or deletes
|
|
// related records. Before hooks run inside the child write's transaction
|
|
// before the row is written; After hooks run after the write and before the
|
|
// commit. parent is the loaded parent record, or a fresh record with a zero
|
|
// key while the parent is not saved yet (deferral). child is the related
|
|
// record being written. An error rolls the write back and the admin API
|
|
// answers its opaque lifecycle error.
|
|
|
|
// RelationBeforeCreate optionally runs before a related record is created
|
|
// through a relation manager.
|
|
type RelationBeforeCreate interface {
|
|
RelationBeforeCreate(ctx context.Context, relation string, parent, child any) error
|
|
}
|
|
|
|
// RelationAfterCreate optionally runs after a related record is created
|
|
// through a relation manager, before the commit.
|
|
type RelationAfterCreate interface {
|
|
RelationAfterCreate(ctx context.Context, relation string, parent, child any) error
|
|
}
|
|
|
|
// RelationBeforeUpdate optionally runs before a related record is updated
|
|
// through a relation manager.
|
|
type RelationBeforeUpdate interface {
|
|
RelationBeforeUpdate(ctx context.Context, relation string, parent, child any) error
|
|
}
|
|
|
|
// RelationAfterUpdate optionally runs after a related record is updated
|
|
// through a relation manager, before the commit.
|
|
type RelationAfterUpdate interface {
|
|
RelationAfterUpdate(ctx context.Context, relation string, parent, child any) error
|
|
}
|
|
|
|
// RelationBeforeDelete optionally runs before a related record is deleted
|
|
// through a relation manager.
|
|
type RelationBeforeDelete interface {
|
|
RelationBeforeDelete(ctx context.Context, relation string, parent, child any) error
|
|
}
|
|
|
|
// RelationAfterDelete optionally runs after a related record is deleted
|
|
// through a relation manager, before the commit.
|
|
type RelationAfterDelete interface {
|
|
RelationAfterDelete(ctx context.Context, relation string, parent, child 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
|
|
}
|
|
|
|
// FilterOptions serves the choices of a model-backed config_filter scope
|
|
// (D-27). It is implemented by the same model as FilterScope and receives the
|
|
// filter's scope method name; labels may be phrase keys.
|
|
type FilterOptions interface {
|
|
FilterOptions(scope string) []Option
|
|
}
|
|
|
|
// 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
|
|
//
|
|
// The kernel type-asserts HasConfig (party, before Register), HasCommands
|
|
// (generated app main, after Boot), HasMigrations (lagoon migrate),
|
|
// HasJobs and HasSchedule (conga workers), 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).
|