Files
sm-sitemap-plugin/contract/contract.go
Jakub Zych 9e5c393709 feat: the listing names the content types switched off
contract.ListingSettings.TypesOff lists the signpost keys of the content
types switched off in the sitemap settings, so another plugin (the SEO
tab's sitemap status) can tell an address left out with ReasonType
without reading the sitemap's tables. Additive.
2026-11-04 13:35:17 +01:00

154 lines
5.6 KiB
Go

// Package contract is the published seam of the golem15.sitemap plugin:
// the Filter another plugin implements to keep addresses out of the
// sitemaps or move them to another file group, and the Listing other
// plugins read to show the sitemap's addresses (an HTML sitemap, IndexNow).
//
// It is a package of its own so a plugin can import it without importing
// the sitemap plugin's root package, whose init registers the plugin. The
// sitemap plugin depends on no implementation: it looks a Filter up with
//
// f, ok := app.Lookup[contract.Filter]()
//
// and includes every published address when none is published. A plugin
// publishes one in its Register:
//
// app.Publish[contract.Filter](myFilter{})
package contract
import (
"context"
"time"
)
// The reasons a Decision may give for leaving an address out. The sitemap
// counts excluded addresses by reason; another reason is counted under its
// own name.
const (
// ReasonNoindex is an address whose page is set to noindex.
ReasonNoindex = "noindex"
// ReasonCanonical is an address whose canonical URL points elsewhere.
ReasonCanonical = "canonical"
// ReasonRedirect is an address that redirects.
ReasonRedirect = "redirect"
// ReasonType is an address of a content type switched off in the
// sitemap settings; the sitemap sets it itself, a Filter need not.
ReasonType = "type"
)
// Candidate is one address the sitemap would list: TypeKey is the content
// type key (a signpost menu item type, or "extra" for an additional address
// of the settings), RecordID the record's ID within it (for an additional
// address the hex SHA-256 of its path), Locale the locale ("" when the
// address has none) and URL its public path or absolute URL.
type Candidate struct {
TypeKey string
RecordID string
Locale string
URL string
}
// Decision is a Filter's answer for one Candidate. Exclude leaves the
// address out of the sitemap files, counted under Reason. Group, when not
// empty, puts the address in another file group: the files are then named
// sitemap-<group>-<n>.xml, so a Group must use lowercase letters, digits and
// hyphens (another value is ignored). LastMod, when set, is the first
// lastmod of an address the sitemap has not listed before; it never moves
// the lastmod of a listed address.
type Decision struct {
Exclude bool
Reason string
Group string
LastMod *time.Time
}
// Group names a file group a Filter's decisions use: Key is the group as
// in Decision.Group, Titles its heading per locale for an HTML sitemap.
type Group struct {
Key string
Titles map[string]string
}
// Filter decides which addresses the sitemap lists. Decide receives one
// batch (a page of records of one content type, or the additional
// addresses) and answers one Decision per Candidate in the same order; an
// answer of another length or an error fails the rebuild, which keeps the
// files it served before. Groups lists the groups the Filter's decisions
// may name, with their titles.
type Filter interface {
Decide(ctx context.Context, batch []Candidate) ([]Decision, error)
Groups(ctx context.Context) ([]Group, error)
}
// Listing is what the sitemap publishes for other plugins: its settings and
// the listed addresses of each group. The sitemap plugin publishes it with
// app.Publish[contract.Listing] in its Register.
type Listing interface {
Settings(ctx context.Context) (ListingSettings, error)
Entries(ctx context.Context, q EntriesQuery) (EntriesPage, error)
}
// ListingSettings are the sitemap's settings as other plugins need them.
// SiteURL is the absolute site address every address starts with ("" when
// none is configured), IndexURL the absolute address of the sitemap index,
// DefaultLocale the site's default locale ("" when not set) and Groups the
// file groups with listed addresses. HTMLEnabled, HTMLPaths (locale to
// path) and HTMLPageSize are the HTML sitemap's settings (the sitemap
// serves its data at /_sitemap/api/v1/html); KeyFile is the IndexNow key
// file name (<key>.txt, served at /_sitemap/indexnow/<key>.txt for the
// site root), "" while IndexNow is off. TypesOff lists the content types
// (signpost keys) switched off in the sitemap settings, whose addresses
// are left out with ReasonType.
type ListingSettings struct {
SiteURL string
IndexURL string
DefaultLocale string
Groups []GroupInfo
HTMLEnabled bool
HTMLPaths map[string]string
HTMLPageSize int
KeyFile string
TypesOff []string
}
// GroupInfo is one file group: Key is the group (as Entries takes it), Slug
// the name part of its files (sitemap-<slug>-<n>.xml; today the same as
// Key), Titles its heading per locale and Count its listed addresses.
type GroupInfo struct {
Key string
Slug string
Titles map[string]string
Count int
}
// EntriesQuery asks for one page of a group's listed addresses. Locale,
// when set, keeps the addresses of that locale and those without one.
// After is the Next of the previous page ("" for the first) and Limit the
// page size (at most 1000; 0 means 100).
type EntriesQuery struct {
Group string
Locale string
After string
Limit int
}
// Entry is one listed address. URL is absolute (on ListingSettings.SiteURL)
// and LastMod its last real change, nil when unknown.
type Entry struct {
Group string
TypeKey string
RecordID string
Locale string
URL string
Title string
LastMod *time.Time
}
// EntriesPage is one page of Entries in a stable order: Next is the After
// of the following page ("" on the last page) and Total the number of
// addresses the query matches across all pages.
type EntriesPage struct {
Entries []Entry
Next string
Total int
}