Documentation
¶
Overview ¶
Package kinds is the site's content model as configuration: content kinds, entity kinds, tiers, restrictions and domain visibility policies. One Config drives storage, routes, media kinds, search and URLs (ContentKit #117).
Index ¶
- Constants
- Variables
- func ValidateAttributes(declared map[string]Attribute, values map[string]any) (map[string]any, error)
- func ValidateRequiredAttributes(declared map[string]Attribute, values map[string]any) error
- type AccessScope
- type Attribute
- type Config
- type ContentFilter
- type ContentFilters
- type Domain
- type Download
- type GroupKind
- type GroupRef
- type Kind
- type Locked
- type MembershipScope
- type Preview
- type TitlePolicy
- type UploadLimits
- type VersionRules
Constants ¶
const ( // DownloadPages is a zip of the version's pages (the media kind's // Publish.Download), the default of readers. DownloadPages = "pages" // DownloadOriginal is the version's video as uploaded (the upload its HLS // ladder encodes), the default of players. DownloadOriginal = "original" )
What a download serves.
const ( ShowDescription = "description" // the content's and versions' descriptions ShowCover = "cover" ShowGroups = "entities" ShowAttributes = "attributes" // the content's and versions' attributes ShowLength = "length" // versions' page counts, durations and segments ShowReactions = "reactions" // like and dislike counts; locked viewers may react ShowFavorites = "favorites" // favorite counts ShowComments = "comments" // comments and their counts; locked viewers may comment )
Properties a Locked view may show.
Variables ¶
var DefaultLocked = Locked{Show: []string{ShowDescription, ShowCover, ShowGroups, ShowAttributes, ShowLength}}
DefaultLocked shows a kind's metadata and cover: no preview, counts or comments.
var ErrInvalid = errors.New("kinds: invalid config")
ErrInvalid is a Config that cannot be served.
var Shown = []string{ShowDescription, ShowCover, ShowGroups, ShowAttributes, ShowLength, ShowReactions, ShowFavorites, ShowComments}
Shown are the properties a Locked view may show.
Functions ¶
func ValidateAttributes ¶
func ValidateAttributes(declared map[string]Attribute, values map[string]any) (map[string]any, error)
ValidateAttributes checks values against the declared attributes and returns them normalized: ints as int64, times as RFC 3339 UTC. A nil value drops the attribute; an undeclared one or a value of the wrong type is ErrInvalid.
func ValidateRequiredAttributes ¶ added in v0.112.0
ValidateRequiredAttributes checks a content's complete attributes after normalization. This is separate from ValidateAttributes because version attributes are overrides, and newly allocated drafts can be incomplete.
Types ¶
type AccessScope ¶ added in v0.112.0
type AccessScope string
AccessScope chooses where the editor presents the access rule. Both scopes retain the same permission checks and optional per-version overrides.
const ( AccessContent AccessScope = "content" AccessVersion AccessScope = "version" )
type Attribute ¶
type Attribute struct {
// Type is string, int, bool, enum or time.
Type string `json:"type" yaml:"type"`
Values []string `json:"values,omitempty" yaml:"values"`
Filterable bool `json:"filterable,omitempty" yaml:"filterable"`
Sortable bool `json:"sortable,omitempty" yaml:"sortable"`
// Required fields must be present on completed content metadata. Empty
// drafts may omit them while media uploads; versions inherit the parent.
Required bool `json:"required,omitempty" yaml:"required"`
}
Attribute is one declared per-kind field.
type Config ¶
type Config struct {
Languages []string `json:"languages" yaml:"languages"`
Tiers []string `json:"tiers,omitempty" yaml:"tiers"`
TokenClaims []string `json:"token_claims,omitempty" yaml:"token_claims"`
Restrictions []string `json:"restrictions,omitempty" yaml:"restrictions"`
ContentFilters ContentFilters `json:"content_filters,omitempty" yaml:"content_filters"`
Kinds map[string]Kind `json:"kinds" yaml:"kinds"`
GroupKinds map[string]GroupKind `json:"entity_kinds,omitempty" yaml:"group_kinds"`
}
Config is the application's content model.
func (Config) DomainFor ¶ added in v0.97.0
DomainFor returns an exact hostname match, ignoring case, port and the DNS trailing dot, or the domain named main. Domain order has no meaning. Host is the request host, not an authority selector; no configured policy returns false.
func (Config) Routes ¶
Routes maps every content kind and group kind with a route to it: the first path segment of its pages (contenturl.Routes).
func (*Config) UnmarshalJSON ¶ added in v0.103.2
func (*Config) UnmarshalYAML ¶ added in v0.97.0
UnmarshalYAML keeps the shared policy strict even inside permissively decoded app configuration. Retired per-content-kind domain blocks are rejected.
type ContentFilter ¶
type ContentFilter struct {
Group GroupRef `json:"-" yaml:",inline"`
Visible bool `json:"visible" yaml:"visible"`
UserToggle *bool `json:"user_toggle,omitempty" yaml:"user_toggle,omitempty"`
}
ContentFilter defines this domain's visibility for one entity. A nil UserToggle allows reader overrides. Internal and required entities are never reader-toggleable, irrespective of UserToggle.
func (ContentFilter) MarshalJSON ¶ added in v0.103.2
func (f ContentFilter) MarshalJSON() ([]byte, error)
func (ContentFilter) Toggleable ¶ added in v0.97.0
func (f ContentFilter) Toggleable() bool
Toggleable reports whether this rule permits reader overrides. The caller must additionally exclude internal and domain-required entities.
func (*ContentFilter) UnmarshalJSON ¶ added in v0.103.2
func (f *ContentFilter) UnmarshalJSON(data []byte) error
func (*ContentFilter) UnmarshalYAML ¶ added in v0.103.2
func (f *ContentFilter) UnmarshalYAML(data []byte) error
UnmarshalYAML keeps flat entity references strict even when decoded alone.
type ContentFilters ¶ added in v0.103.0
type ContentFilters struct {
AppliesTo []string `json:"applies_to" yaml:"applies_to"`
Domains []Domain `json:"domains" yaml:"domains"`
}
ContentFilters is one app's shared entity-filter policy. Filters apply to every selected content kind; required entities are independently scoped.
func DecodeContentFilters ¶ added in v0.103.0
func DecodeContentFilters(data []byte) (ContentFilters, error)
DecodeContentFilters reads an isolated YAML or JSON policy block. Hosts can pass their root config's content_filters section directly without copying the policy model or weakening its strict unknown-field validation.
func (ContentFilters) Clone ¶ added in v0.103.0
func (c ContentFilters) Clone() ContentFilters
Clone gives an embedded engine its own immutable policy snapshot, including the optional bool pointers and the per-kind required-entity slices.
func (*ContentFilters) UnmarshalJSON ¶ added in v0.103.2
func (c *ContentFilters) UnmarshalJSON(data []byte) error
func (*ContentFilters) UnmarshalYAML ¶ added in v0.103.0
func (c *ContentFilters) UnmarshalYAML(data []byte) error
UnmarshalYAML remains strict when an app uses a permissive outer decoder.
type Domain ¶ added in v0.97.0
type Domain struct {
Name string `json:"name" yaml:"name"`
Hosts []string `json:"hosts,omitempty" yaml:"hosts,omitempty"`
RequireEntities map[string][]GroupRef `json:"require_entities,omitempty" yaml:"require_entities,omitempty"`
Filters []ContentFilter `json:"filters,omitempty" yaml:"filters,omitempty"`
}
Domain is an entrypoint matched by request host. The domain named main is the fallback for unmatched hosts. Name is its stable branding/activity identity; entity display names belong to entity records, never policy config.
type Download ¶ added in v0.87.0
type Download struct {
// Off: the kind's versions have no download.
Off bool `json:"off,omitempty" yaml:"off"`
// Serve is DownloadPages, DownloadOriginal or a private MP4 preset of the
// kind's media ("mp4-1080"); empty is the presentation's default.
Serve string `json:"serve,omitempty" yaml:"serve"`
}
Download is what a kind's version download serves. Whether a viewer may download, and which pages are marked, is the host's DownloadPolicy.
type GroupKind ¶
type GroupKind struct {
Route string `json:"route,omitempty" yaml:"route"`
// Ordered members carry positions (series, playlists, rails).
Ordered bool `json:"ordered,omitempty" yaml:"ordered"`
// Parent is the group kind a group may sit under (series under series).
Parent string `json:"parent,omitempty" yaml:"parent"`
// ScopedBy makes slugs unique within a group of that kind (character
// within series).
ScopedBy string `json:"scoped_by,omitempty" yaml:"scoped_by"`
// Owner is "site" (staff-managed) or "user" (channels, playlists): a
// signed-in person creates a user-owned group and owns it.
Owner string `json:"owner,omitempty" yaml:"owner"`
// MaxPerOwner caps the active groups of the kind one person owns; 0 is
// no cap. Staff are not capped.
MaxPerOwner int `json:"max_per_owner,omitempty" yaml:"max_per_owner"`
// Avatar gives site-owned entities a square avatar beside their cover;
// user-owned kinds always have one.
Avatar bool `json:"avatar,omitempty" yaml:"avatar"`
// Uploads limit what editors other than staff upload to a user-owned
// group's media.
Uploads UploadLimits `json:"uploads,omitzero" yaml:"uploads"`
// Sellable groups carry offers; their key unlocks every member.
Sellable bool `json:"sellable,omitempty" yaml:"sellable"`
// AuthScope groups name an AuthKit group whose permissions govern their
// content (OnlyDemo channels).
AuthScope bool `json:"auth_scope,omitempty" yaml:"auth_scope"`
// Windows lets memberships carry active_from/active_until (rails).
Windows bool `json:"windows,omitempty" yaml:"windows"`
// MembershipScopes declares where this entity kind can be attached.
// Omitted keeps the legacy default: content, or version with VersionLevel.
MembershipScopes []MembershipScope `json:"membership_scopes,omitempty" yaml:"membership_scopes"`
// VersionLevel is the legacy version-only editor setting. Prefer
// MembershipScopes; it cannot be combined with that explicit setting.
VersionLevel bool `json:"version_level,omitempty" yaml:"version_level"`
// Creatable lets a content edit create an entity of the kind from a name no
// entity carries yet; never user-owned or authorization kinds.
Creatable bool `json:"creatable,omitempty" yaml:"creatable"`
// Roles are membership roles (publisher on an artist group).
Roles []string `json:"roles,omitempty" yaml:"roles"`
Attributes map[string]Attribute `json:"attributes,omitempty" yaml:"attributes"`
}
GroupKind is one kind of grouping: tag, artist, series, channel, rail, ...
func (GroupKind) AllowsMembership ¶ added in v0.112.0
func (k GroupKind) AllowsMembership(scope MembershipScope) bool
AllowsMembership reports the configured ownership scopes, including the legacy editor default when no explicit scopes were supplied.
type GroupRef ¶
type GroupRef struct {
Kind string `json:"kind" yaml:"kind"`
Slug string `json:"slug" yaml:"slug"`
}
GroupRef names an entity by kind and canonical slug.
type Kind ¶
type Kind struct {
// TitlePolicy defaults to localized text. OriginalAndVersion uses the
// selected edition title, then the shared original title, then legacy text.
TitlePolicy TitlePolicy `json:"title_policy,omitempty" yaml:"title_policy"`
// AccessLevels limits content and version rules. Empty permits every level.
// Omitting ppv and group_ppv also disables individual sales for this kind.
AccessLevels []string `json:"access_levels,omitempty" yaml:"access_levels"`
// AccessScope defaults to content; version presents a direct edition rule.
AccessScope AccessScope `json:"access_scope,omitempty" yaml:"access_scope"`
// CommentModes limits per-item comment settings. Empty permits every mode.
CommentModes []string `json:"comment_modes,omitempty" yaml:"comment_modes"`
// Route is the URL segment: /{lang}/{route}/{CODE}/{slug}.
Route string `json:"route" yaml:"route"`
// PathEntity names one of Groups whose slug prefixes the title in the
// decorative path, /{lang}/{route}/{CODE}/{entity}/{title}, for SEO.
PathEntity string `json:"path_entity,omitempty" yaml:"path_entity"`
// Presentation tells contentkit-ui how to show it: reader, player or post.
Presentation string `json:"presentation" yaml:"presentation"`
Versions VersionRules `json:"versions" yaml:"versions"`
// Segments enables ordered, individually released ranges (chapters).
Segments bool `json:"segments,omitempty" yaml:"segments"`
// Groups are the group kinds content of this kind may join.
Groups []string `json:"entities,omitempty" yaml:"groups"`
Attributes map[string]Attribute `json:"attributes,omitempty" yaml:"attributes"`
// Sort is the default list order.
Sort string `json:"sort,omitempty" yaml:"sort"`
// Locked is what a viewer who does not open its content sees of it;
// nil is DefaultLocked.
Locked *Locked `json:"locked,omitempty" yaml:"locked"`
// DraftTTL expires drafts that never went live this long after their
// creation: they are deleted, media included. Zero keeps them.
DraftTTL time.Duration `json:"draft_ttl,omitempty" yaml:"draft_ttl"`
// Download is what a version's download serves; nil serves its
// presentation's default (Download.Serves).
Download *Download `json:"download,omitempty" yaml:"download"`
}
func (Kind) AllowsAccess ¶ added in v0.105.0
AllowsAccess reports whether this kind accepts a rule's access level.
func (Kind) AllowsComments ¶ added in v0.105.0
AllowsComments reports whether this kind accepts a per-item comment mode.
func (Kind) LockedView ¶ added in v0.87.0
LockedView is k's Locked, else DefaultLocked.
type Locked ¶ added in v0.87.0
type Locked struct {
Show []string `json:"show" yaml:"show"`
// Preview is the free part when its access rule names none; nil is none.
Preview *Preview `json:"preview,omitempty" yaml:"preview"`
}
Locked is what a viewer who does not open content (or a version) still sees of it: the properties Show names and the free part Preview names. Its id, code, path, title, languages, traits, owner, creators and paywall always show; its body and private media never do.
type MembershipScope ¶ added in v0.112.0
type MembershipScope string
MembershipScope identifies which record owns an entity association.
const ( ContentScope MembershipScope = "content" VersionScope MembershipScope = "version" )
type Preview ¶ added in v0.86.0
type Preview struct {
Pages int `json:"pages,omitempty" yaml:"pages"`
Segments int `json:"segments,omitempty" yaml:"segments"`
}
Preview is the free part of locked content: its first Pages pages or first Segments segments (chapters).
type TitlePolicy ¶ added in v0.112.0
type TitlePolicy string
Kind is one content kind: a gallery, a movie, a creator post, a blog post. TitlePolicy chooses how a kind's display title is resolved.
const ( TitleLocalized TitlePolicy = "localized" TitleOriginalAndVersion TitlePolicy = "original_and_version" )
type UploadLimits ¶
type UploadLimits struct {
// FilesPerHour and BytesPerDay limit each uploader.
FilesPerHour int `json:"files_per_hour,omitempty" yaml:"files_per_hour"`
BytesPerDay int64 `json:"bytes_per_day,omitempty" yaml:"bytes_per_day"`
// QuotaBytes caps the group's stored originals.
QuotaBytes int64 `json:"quota_bytes,omitempty" yaml:"quota_bytes"`
}
UploadLimits bound a user-owned group's uploads; zero fields are off.
type VersionRules ¶
type VersionRules struct {
// Languages is "one" (a version serves one language) or "many".
Languages string `json:"languages" yaml:"languages"`
// Traits are the declared version traits (colored, uncensored, ...).
Traits []string `json:"traits,omitempty" yaml:"traits"`
// Hidden kinds have exactly one version that clients never see.
Hidden bool `json:"hidden,omitempty" yaml:"hidden"`
}
VersionRules shape a kind's versions.