package pact import ( "context" "time" ) // ContentDescriptor describes the records behind a menu item type, for // consumers that work on every content type at once: sitemaps, search // indexers, audit tools, metadata editors. A type sets it in // MenuItemType.Content; a type without one takes no part and behaves as // before. // // Controller is the ID of the admin controller whose form edits the // records, "" when none does. Model is the records' morph type, the value // RecordSaved and RecordDeleted carry (lagoon.MorphType), so an event maps to // its content type with one lookup; it is required. Describe is required and // describes one record by id. Records pages through the records and Match // maps a public path to a record; both are optional. // // The functions read through cabana.TxFromContext(ctx) when ctx carries a // write transaction, so code running inside a save (a form extension's Save, // for example) sees the values the save has just written. // // Kind and Tools are optional facts about the type as a whole: what kind of // thing a record is (one of the ContentKind constants, "" when the type does // not say) and which MCP tools change its records, keyed by operation (the // ContentTool constants), so a consumer can point at a fixing tool without // importing the content plugin. type ContentDescriptor struct { Controller string Model string Records func(ctx context.Context, in ContentList) (ContentPage, error) `json:"-"` Describe func(ctx context.Context, in ContentDescribe) (*ContentRecord, error) `json:"-"` Match func(ctx context.Context, in ContentMatch) (*ContentMatchResult, error) `json:"-"` // Kind is what a record of the type is, one of the ContentKind constants; // "" when the type does not say. Kind string // Tools maps an operation (a ContentTool constant) to the full name of // the MCP tool that performs it on a record, such as "acme_update_page"; // nil when the type names none. Tools map[string]string } // The kinds a ContentDescriptor.Kind names. const ( // ContentKindArticle is a dated piece with authors, such as a post. ContentKindArticle = "article" // ContentKindCollection is a record that lists other records, such as a // category or a tag. ContentKindCollection = "collection" // ContentKindProfile is a record about a person or an organisation. ContentKindProfile = "profile" // ContentKindPage is a standalone page. ContentKindPage = "page" ) // The operations a ContentDescriptor.Tools map is keyed by. const ( // ContentToolEdit names the tool that edits a record's fields and body. ContentToolEdit = "edit" // ContentToolHeadings names the tool that changes the headings of a // record's body. ContentToolHeadings = "headings" // ContentToolImageAlt names the tool that sets the alternative text of // the images in a record's body. ContentToolImageAlt = "image_alt" ) // ContentList is what ContentDescriptor.Records receives. Pattern is the URL // pattern already layered by the registry (the host's override, else the // type's default). After is the ID of the last record of the previous page, // "" for the first page; Limit is the page size the caller wants (the type // may cap it). UnprefixedLocale names the locale whose URLs carry no locale // prefix; "" leaves the choice to the type. type ContentList struct { Pattern string After string Limit int UnprefixedLocale string } // ContentPage is one page of records in a stable order. Next is the After of // the following page, "" on the last page. type ContentPage struct { Records []ContentRecord Next string } // ContentDescribe is what ContentDescriptor.Describe receives: the record's // ID, the layered URL pattern and the unprefixed locale, as in ContentList. type ContentDescribe struct { ID string Pattern string UnprefixedLocale string } // ContentRecord describes one record. ID is its primary key as text, // Published whether visitors can see it, LastMod its last change (nil when // unknown). Locales lists the record per locale in the site's locale order. // The remaining fields are optional; a nil slice and an empty one mean the // same: the type has nothing to say. type ContentRecord struct { ID string Published bool LastMod *time.Time Locales []ContentLocale // PublishedAt is the record's first publication, nil when unknown or // never published. PublishedAt *time.Time // Parents lists the records this one sits under, in order of preference: // the first is the default primary parent. Parents []ContentRef // Authors lists the people who wrote the record, in byline order. Authors []ContentPerson // TopHeading is the highest heading level the record's body may use: 2 // when the page renders the title as its only level-1 heading, 0 when the // type sets no rule. TopHeading int } // ContentLocale is a record in one locale. URL is its public path, "" when // the locale or the host has no public page for it. Title, Description and // Image (a URL) are the record's own defaults for page metadata; Words is the // body's word count, nil when the type has no body. Images, Videos and // Headings are optional; a nil slice and an empty one mean the same. type ContentLocale struct { Locale string URL string Title string Description string Image string Words *int // Images lists the record's images in this locale (the featured image // first, then the body's in document order). Images []ContentImage // Videos lists the videos the body embeds, in document order. Videos []ContentVideo // Headings is the body's heading outline in document order. Headings []ContentHeading } // ContentRef points at another record: Type is a menu item type key and ID // the record's ID within it. type ContentRef struct { Type string ID string } // ContentPerson is a person named on a record. Ref points at the person's // own record; its Type is "" when the person has none. type ContentPerson struct { Name string Ref ContentRef } // ContentImage is an image a record shows. URL is its address and Alt its // alternative text; Decorative marks an image that carries no content, whose // empty Alt is intended. Width and Height are in pixels, 0 when unknown. type ContentImage struct { URL string Alt string Decorative bool Width int Height int } // ContentVideo is a video a record embeds: Provider names the host (such as // "youtube") and ID the video there. type ContentVideo struct { Provider string ID string } // ContentHeading is one heading of a record's body: Level 1 to 6 and its // text. type ContentHeading struct { Level int Text string } // ContentMatch is what ContentDescriptor.Match receives: a public path (no // query, no host), the layered URL pattern and the unprefixed locale. type ContentMatch struct { Path string Pattern string UnprefixedLocale string } // ContentMatchResult names the record a path belongs to and the locale the // path addresses. Exact is false when the path names the record but a // segment does not match it (another category, a stale locale prefix, a // slug of another locale): the caller may then redirect to the record's URL. // Match answers nil when the path names no record visitors can see. type ContentMatchResult struct { ID string Locale string Exact bool }