Documentation
¶
Overview ¶
Package frontend serves the public website. WebThings resolves the URL (language prefix, page tree, module mounts) and builds one View per request; the website's templates only render it.
The layout gets the whole View: site, menu, languages and title. A content template gets only its own typed data (View.Content), so it stays small and its go-clue @dot contract can be checked:
layout.gohtml frontend.View, with {{content}} for the page
page.gohtml frontend.Page, a content page
<key>/<name>.gohtml a module screen, for example news/post.gohtml
404.gohtml frontend.NotFound (optional)
search.gohtml frontend.SearchView, at /<locale>/search (optional)
Module screens are looked up by the instance key first ("news/post"), then by the module type ("publishing/post"), so instances of one module can share templates or each have their own. Modules declare their templates (see Template); App.WriteTemplates writes starting points for them.
Modules also offer components, parts of a page any template includes with a function such as {{commentThread "remarks" .ID}}; a theme replaces one with <key>/<name>.gohtml or <type>/<name>.gohtml, core's with site/<name>.gohtml, and otherwise the module's default renders (see Component).
The templates of a site are grouped in themes, one folder each (see Themes); the paths above are relative to the theme.
Every template gets the page language from the {{locale}} function and the localizer the translation functions take from {{localizer}}, as in {{tl localizer "Read more"}}. Any other template engine can be used by implementing Renderer, which receives the whole View.
Requests from HTMX receive only the content, without the layout, with the theme's regions out of band (see Templates). One that targets a fragment of the theme gets only that fragment. One that targets another element gets the content, with HX-Retarget and HX-Reswap headers so HTMX swaps the content element; with hx-select (HX-Request-Type "full") the whole page comes back. Of the theme's menu region, a navigation gets only the items whose state changes from the page the visitor comes from (HX-Current-URL), when the items carry their ElementID (see MenuItem). The theme's head region, regions/head.gohtml, replaces the page's tags in <head>, such as the canonical, alternate and feed links, with those of the page arrived at (see Templates).
The layout and content templates also have go-partial's helpers. Of those, url, request, urlIs, urlStarts and urlContains see the page's whole path, for example /en/news/hello on a news post; content is the content template in the layout. slot and hasSlot place the theme's regions in the layout, and oob and oobAttr tell a region it goes out of band. The others, partial, runtime, ctx, basePath, joinPath and urlPath, belong to go-partial's own page structure; basePath is "". Template functions of the site and of modules cannot take any of these names, nor locale, localizer, requiredSlot and deferredSlot: Start refuses them (see render.Reserved).
Index ¶
- Constants
- Variables
- func AbsoluteURL(base string, r *http.Request, path string) string
- func Actions(key string, next http.Handler, limits ActionLimits) http.Handler
- func Back(w http.ResponseWriter, r *http.Request, key, result string)
- func BackPath(r *http.Request) string
- func BackWith(w http.ResponseWriter, r *http.Request, key, result string, extra url.Values)
- func BuiltInCalendar(locale string) bool
- func CalendarTexts() []extract.Message
- func Changed(r *http.Request) bool
- func Civil(t time.Time) time.Time
- func ClientIP(r *http.Request) string
- func Component(ctx context.Context, key, name string, data any) (template.HTML, error)
- func CoreAssets(file string) http.Handler
- func CoreTexts() []string
- func FocalPointsStylesheet() fs.FS
- func FormatDate(locale string, t time.Time, style string) string
- func FormatMoney(locale string, amount int64, code string) string
- func Funcs() template.FuncMap
- func HeadScaffold() string
- func IsSpam(r *http.Request) bool
- func LayoutScaffold() string
- func Limits(key string, limits ActionLimits) func(http.Handler) http.Handler
- func Locale(ctx context.Context) string
- func MailScaffold(t Template) string
- func NothingChanged(r *http.Request)
- func PreviewRevision(r *http.Request, adminURL string) string
- func Request(ctx context.Context) *http.Request
- func Result(r *http.Request, key string, allowed ...string) string
- func Robots(baseURL, sitemapPath string) http.Handler
- func Scaffold(t Template) string
- func Schema() fs.FS
- func Secure(r *http.Request) bool
- func Service(ctx context.Context, key string) any
- func ServiceOf[T any](ctx context.Context, key string) (T, bool)
- func StyleScaffold() string
- func Summary(query Query, length int, texts ...string) string
- func TemplateFuncs(ctx context.Context) template.FuncMap
- func Throttle(limit int, window time.Duration, next http.Handler, options ...ThrottleOption) http.Handler
- func TrackChanges(r *http.Request) *http.Request
- func Translations(dir string) (*translator.Translator, error)
- func VisitorForm(ctx context.Context, model any, action, key string) form.RenderModel
- func WithActionModule(ctx context.Context, key string) context.Context
- func WithTheme(ctx context.Context, theme string) context.Context
- type ActionLimits
- type Calendars
- type Content
- type EditLink
- type Feed
- type FeedFunc
- type FeedLink
- type Format
- func (f Format) Date(t time.Time, style string) string
- func (f Format) DateRange(start, end time.Time, style string) string
- func (f Format) Money(amount int64, code string) string
- func (f Format) Time(t time.Time) string
- func (f Format) TimeRange(start, end time.Time) string
- func (f Format) Weekday(day time.Weekday, style string) string
- type Frontend
- func (f *Frontend) APILink(r *http.Request) string
- func (f *Frontend) AbsoluteMarkdown(r *http.Request, source string) template.HTML
- func (f *Frontend) AltText(ctx context.Context, id, locale string) string
- func (f *Frontend) Assets() http.Handler
- func (f *Frontend) BackLocale(r *http.Request) string
- func (f *Frontend) BindForm(r *http.Request, input any) (form.FieldErrors, error)
- func (f *Frontend) CheckTemplates() ([]TemplateProblem, error)
- func (f *Frontend) CleanupForms(ctx context.Context) error
- func (f *Frontend) Editors(next http.Handler) http.Handler
- func (f *Frontend) Execute(ctx context.Context, name, locale string, data any) (string, error)
- func (f *Frontend) Fail(w http.ResponseWriter, r *http.Request, err error)
- func (f *Frontend) FeedLinks(r *http.Request, title string) []FeedLink
- func (f *Frontend) FeedURL(r *http.Request, format string, name ...string) string
- func (f *Frontend) Format(ctx context.Context, locale string) Format
- func (f *Frontend) Handler(modules map[string]http.Handler) http.Handler
- func (f *Frontend) Has(theme, name string) bool
- func (f *Frontend) Image(ctx context.Context, id string) media.Image
- func (f *Frontend) KeepForm(w http.ResponseWriter, r *http.Request, key string, model any, ...)
- func (f *Frontend) Mail(ctx context.Context, locale, key, name string, data any) (mail.Message, error)
- func (f *Frontend) MailImage(ctx context.Context, id string) media.Image
- func (f *Frontend) Markdown(ctx context.Context, source string) template.HTML
- func (f *Frontend) MarkdownOptions(ctx context.Context, locale, base string, mail bool) markdown.Options
- func (f *Frontend) MediaURL(ctx context.Context, id string) string
- func (f *Frontend) ModulePreviewURL(ctx context.Context, key, locale, relative, revisionID string) string
- func (f *Frontend) NotFound(w http.ResponseWriter, r *http.Request)
- func (f *Frontend) PagePreviewURL(ctx context.Context, page pages.Page, locale string) string
- func (f *Frontend) Render(w http.ResponseWriter, r *http.Request, content Content)
- func (f *Frontend) Route(name string, args ...any) string
- func (f *Frontend) Router() *router.Router
- func (f *Frontend) Search(ctx context.Context, locale, query string, limit int) ([]SearchResult, error)
- func (f *Frontend) SendMail(ctx context.Context, sender mail.Sender, letter Letter) error
- func (f *Frontend) ServeFeeds(key string, routes *router.Router, feeds []Feed) error
- func (f *Frontend) SetBaseURL(base string)
- func (f *Frontend) SetComponents(components map[string][]Template)
- func (f *Frontend) SetDatabase(database *db.DB)
- func (f *Frontend) SetDiagnostics(diagnostics render.Diagnostics)
- func (f *Frontend) SetEditors(identify func(r *http.Request) (SignedIn, bool))
- func (f *Frontend) SetFormTheme(name string)
- func (f *Frontend) SetMail(templates map[string][]Template)
- func (f *Frontend) SetModules(types map[string]string, services map[string]any)
- func (f *Frontend) SetReload(reload bool)
- func (f *Frontend) SetRoutes(routes *router.Router)
- func (f *Frontend) SetSearch(searchers map[string]SearchFunc)
- func (f *Frontend) SetSearchThrottle(limit int, window time.Duration)
- func (f *Frontend) SetStructuredData(funcs []StructuredDataFunc)
- func (f *Frontend) SetTranslations(tr *translator.Translator)
- func (f *Frontend) Sitemap(baseURL string, modules map[string]SitemapFunc, version func() uint64) http.Handler
- func (f *Frontend) SocialImageURL(ctx context.Context, ref string) string
- func (f *Frontend) TagURI(r *http.Request, date time.Time, specific string) string
- func (f *Frontend) Template(theme, key, name string) string
- func (f *Frontend) ThemeCSP(ctx context.Context) map[string][]string
- func (f *Frontend) ThemeScreenshot(theme Theme) string
- func (f *Frontend) ThemeStyles() http.Handler
- func (f *Frontend) Themes() *Themes
- func (f *Frontend) ValidateSchema(r *http.Request, schema form.Schema, values url.Values) form.FieldErrors
- type Label
- type Language
- type Letter
- type MenuItem
- type Mount
- type NotFound
- type Page
- type Profile
- type Query
- type Renderer
- type RevisionPreview
- type SearchFunc
- type SearchResult
- type SearchView
- type ShareBar
- type ShareLink
- type SignedIn
- type Site
- type SitemapFunc
- type StructuredDataFunc
- type Template
- type TemplateProblem
- type Theme
- type ThemeSetting
- type ThemeView
- type Themes
- type Thing
- type ThrottleOption
- type View
- type When
Constants ¶
const ( // DefaultSearchLimit is the number of results the search screen shows, // and what Frontend.Search returns for a limit of zero or less. DefaultSearchLimit = 20 // MaxSearchLimit is the most results one search returns, whatever a // template asks for, so a search stays cheap. MaxSearchLimit = 100 )
Search limits: results per search and the length of a query.
const CalendarReference = "core:calendar"
CalendarReference is the reference of CalendarTexts in the site's texts (see App.TranslationCatalog).
const DefaultSearchThrottle = 30
DefaultSearchThrottle is how many searches one visitor may run per minute on the search screen (see SetSearchThrottle).
const DefaultTheme = "default"
DefaultTheme is the theme other themes fall back to.
const FormTheme = "webthings"
FormTheme is the go-form theme visitor forms render with unless the site sets another: go-form's markup with the classes of webthings.css.
const Honeypot = "website"
Honeypot is the name of the hidden field that people leave empty and many bots fill in: go-form's spam trap (form.Info.Honeypot).
const KeptFormCookie = "wt_form_"
KeptFormCookie goes before the form's key in the name of the cookie that brings a visitor's kept post to its page (see KeepForm): a request with such a cookie may get a page of its own, which caches must not keep for others.
const OrganizationID = "/#organization"
OrganizationID is the "@id" of the site's Organization item, for items that name it, such as the author or publisher of an article: Thing{"@id": frontend.OrganizationID}. Like other addresses, it is made absolute.
const PreviewParam = "revision"
PreviewParam is the query parameter that names the revision to preview.
const ResultThrottled = "throttled"
ResultThrottled is the result Actions returns a visitor with who posted more often than its limits allow: Back(w, r, key, ResultThrottled).
const SearchPath = "/search"
SearchPath is the path of the search screen below the language prefix.
Variables ¶
var ErrNoTemplate = errors.New("template_not_found")
ErrNoTemplate is returned when neither the theme nor the module has a template.
var Floating = time.FixedZone("floating", 0)
Floating is the location of civil times: calendar dates and wall-clock times that are not an instant in the site's timezone. It has no offset, like UTC, but Format never moves a time in Floating to another zone, so a date stored as midnight UTC stays on its day west of UTC.
Functions ¶
func AbsoluteURL ¶
AbsoluteURL makes path absolute with base, or with the address of r when base is empty; for links in e-mail and share links. The address of r is its own TLS state and Host header; X-Forwarded-Proto and X-Forwarded-Host, or else Forwarded, count only from trusted proxies (webthings.Config.TrustedProxies), as go-router's middleware.Origin reads them. A Host header is what the client sent, so sites set base in production.
func Actions ¶
Actions wraps next, a handler of a module's visitor actions (webthings.Env.Actions, at /_/<key>/...), with limits, always in the same order: the body is limited first, so that even the answer to a throttled visitor, which reads the form to find the way back, reads no more than Body bytes; then each visitor gets Posts posts per Window. key is the module instance, which names the result in the page address.
func Back ¶
func Back(w http.ResponseWriter, r *http.Request, key, result string)
Back redirects to the page the form was on, adding result to its query, for example Back(w, r, "comment", "sent") returns to /en/news/hello?comment=sent. A #fragment of the page is kept. Only paths on this site are followed. Without a back field, as when a post over the body limit could not be read, the visitor returns to the Referer when that is an address on this site, and to the home page otherwise.
func BackPath ¶
BackPath returns the path of the page a visitor's form was on (the back field of VisitorForm, or the Referer when the form has none, see Back), without query or fragment, or "" when it is not a path on this site.
func BackWith ¶
BackWith is Back with more query values, for example a code the page shows: BackWith(w, r, "signup", "registered", url.Values{"code": {code}}).
func BuiltInCalendar ¶
BuiltInCalendar reports whether the names of months and days and the date patterns of locale, such as "nl", "en_US" or "nl-BE", are built in: a catalogue need not translate them (see CalendarTexts), as what it does not translate shows the built-in ones.
func CalendarTexts ¶
CalendarTexts are the names and patterns Format looks up in the translations, with their contexts: the English names of the months ("month", "month-short") and days ("day", "day-short"), AM and PM ("time"), and the styles of the date patterns ("date": "short", "medium", "long", "full" and "time", translated as a pattern such as "dd.MM.y"). A translation replaces the built-in name or pattern; a language with built-in ones (BuiltInCalendar) needs none. Extraction adds them to the site's texts (see App.TranslationCatalog).
func Changed ¶
Changed reports whether the post r may have changed what the site's pages show: true unless NothingChanged was called for it.
func Civil ¶
Civil returns the wall clock of t (its date and time of day in t's own location) in Floating, so Format shows it unchanged. Modules return dates without a zone (stored as midnight UTC) and times in a zone chosen on purpose, such as an event in "UTC", through Civil. A zero time stays zero. Store a civil time with t.UTC(), which keeps the same date and time.
func ClientIP ¶
ClientIP is the visitor's IP address: the address the request came from, or, when that is a trusted proxy (webthings.Config.TrustedProxies), the address the proxy reports in X-Forwarded-For, X-Real-IP or Forwarded. Without trusted proxies those headers are ignored, since anyone can send them. App finds it with go-router's RealIP middleware; this is middleware.ClientIP.
func Component ¶
Component renders the component name of the module instance key with data, for the template functions of modules, for example Component(ctx, "remarks", "thread", thread). Key "site" is a component of core. The theme's <key>/<name>.gohtml or <type>/<name>.gohtml is used when it has one, otherwise the module's default.
func CoreAssets ¶
CoreAssets serves a file core offers themes at the route of the same name: webthings.css, the optional stylesheet of the default components, at /_/webthings.css; focal-points.css, the classes of focalClass, at /_/focal-points.css; and partial-errors.css, go-partial's stylesheet of the box that takes the place of a part of a page that failed (in development, or without an error fragment template), at /_/partial-errors.css. The box has no style attributes, so a Content-Security-Policy does not block it, and is plain text without this stylesheet. The response has a strong ETag and, at the file's current version, may be cached for a year.
func CoreTexts ¶
func CoreTexts() []string
CoreTexts are the texts core translates in Go with the site's translations rather than in a template, so extraction finds them only through this list: the title of the 404 page, the names of social networks, the labels of share links and profiles (site.NetworkName), the title of feeds with the site's name ("%[1]s from %[2]s", see Feed), the message of a theme's 500.gohtml (go-partial's ext/errors SafeMessage, its .Message), which arrives translated in the page's language, and the status texts of the error page's heading (its .Title; see errorStatuses).
The names of months and days and the date patterns of Format, which Format looks up with a context, are CalendarTexts.
func FocalPointsStylesheet ¶
FocalPointsStylesheet returns a file system that holds focal-points.css, the classes of focalClass (media.FocalPoint.PositionClass), for other parts of the site that serve it with their own assets, such as the admin's crop previews.
func FormatDate ¶
FormatDate formats t in locale with a style (short, medium, long, full) or a pattern, in t's own timezone.
func FormatMoney ¶
FormatMoney formats an amount in minor units (cents) of an ISO 4217 currency, for example FormatMoney("nl", 4500, "EUR") is "€ 45,00".
func Funcs ¶
Funcs are the core template functions. Module functions come with their modules; all of them can be used in every template.
go-clue:funcmap
func HeadScaffold ¶
func HeadScaffold() string
HeadScaffold is a starting point for regions/head.gohtml, the tags of the page in <head>, which the layout places with {{slot "head"}}: core's tags (webthings/head-tags.gohtml, see Templates) and room for the theme's own. Without the file, core's tags alone are the region.
func IsSpam ¶
IsSpam reports whether the honeypot field was filled in (form.IsSpam). Answer such requests as if they succeeded, but store nothing: when it reports true, it tells the app that the post changed nothing (NothingChanged).
func LayoutScaffold ¶
func LayoutScaffold() string
LayoutScaffold is a starting point for layout.gohtml.
func Limits ¶
Limits is Actions as middleware, for the routes of a module's action router (webthings.Env.Actions): all of them with Use, or one with RouteRegistration.Use.
func MailScaffold ¶
MailScaffold is a mail template's Example, or a starting point with every field of its data in the text.
func NothingChanged ¶
NothingChanged tells the app that the visitor's post r changed nothing the site's pages show, such as a vote on a closed poll: the output cache keeps its pages (see TrackChanges). Call it only for an answer that stored nothing a page shows.
func PreviewRevision ¶
PreviewRevision returns the revision a request asks to preview (see PreviewParam) when it comes from a signed-in editor who may read the admin section of adminURL, such as the section's index (see webthings.Env.AdminURL); "" otherwise. Module handlers look the revision up for the content at the address (see content.Revisions.PreviewBySlug) and pass what they show to RevisionOf.
func Request ¶
Request is the request of the page being rendered, for template functions that read its query, for example ?comment=sent after a visitor's post.
func Result ¶
Result returns the result of a visitor action that the page address carries as ?<key>=<result> (see Back), when it is one of allowed, and "" otherwise, so a page never shows a made-up result from the address bar.
func Robots ¶
Robots serves /robots.txt, which names the sitemap at sitemapPath, such as the path of the app's route named sitemap. It does not name the admin, whose path a site may choose to keep to itself: admin pages answer with X-Robots-Tag: noindex, which keeps them out of search results also when other sites link to them, as a Disallow line cannot.
func Scaffold ¶
Scaffold is a starting point for a template: its go-clue contract and every field of its data, in plain HTML to build the design around.
func Schema ¶
Schema holds this package's schema files per dialect (see db.ApplySchema): the table of kept posts.
func Secure ¶
Secure reports whether the request arrived over HTTPS: its own TLS state, or what a trusted proxy reports with X-Forwarded-Proto or Forwarded. A cookie set for the visitor can then be marked Secure; the headers alone are not believed, since anyone can send them. It is go-router's middleware.Scheme.
func Service ¶
Service returns the API of a module instance while a page renders, for template functions, for example Service(ctx, "news").(*publishing.Service).
func ServiceOf ¶
ServiceOf returns the API of the module instance key while a page renders, as Service does, when it is a T, such as ServiceOf[*publishing.Service](ctx, "news"); a module's template functions use it. An instance key the site has no T of is a mistake in a template that would show nothing: it is logged at WARN once, with the keys of the instances there are, such as `posts: no publishing instance "nieuws" (known: blog, news)`, and in development (see SetDiagnostics) the render fails with it: ServiceOf panics with the error, which the template turns into the error of the function's call. Outside a render it reports false.
func StyleScaffold ¶
func StyleScaffold() string
StyleScaffold is a starting point for assets/site.css, the stylesheet of the theme that LayoutScaffold links with {{styles}}.
func Summary ¶
Summary is a snippet (see Query.Snippet) of the first text that matches the query, or of the first text that is not empty.
func TemplateFuncs ¶
TemplateFuncs are every template function with ctx bound, as templates call them: the core functions, the modules' and the site's own, and localizer and locale for the language. ctx is the context of the request a Renderer renders, or of Frontend.Execute; elsewhere it gives nil. A Renderer for another template engine uses it to offer the functions; see Renderer.
func Throttle ¶
func Throttle(limit int, window time.Duration, next http.Handler, options ...ThrottleOption) http.Handler
Throttle allows each visitor (by ClientIP) at most limit posts per window through next; others get 429, or the OnLimit handler, and change nothing (NothingChanged). Requests that only read (go-router's middleware.SafeMethod: GET, HEAD, OPTIONS and TRACE) are not counted, and a limit or window of zero or less does not throttle. Posts are counted in memory, per process: a site on several servers allows limit posts per server, and a restart forgets them. It is go-router's RateLimit.
func TrackChanges ¶
TrackChanges returns r with a record of whether the post changes something, which NothingChanged clears and Changed reads. The app tracks the visitors' posts to the module actions and the pages.
func Translations ¶
func Translations(dir string) (*translator.Translator, error)
Translations loads the site's own UI strings from <dir>/<locale>.po (for example translations/nl.po), for webthings.Config.Translations: templates use them as {{tl localizer "Read more"}}, with plurals {{tn localizer "%d comment" "%d comments" .Count .Count}} and contexts ctl and ctn (go-translator's functions), and core translates its own texts (see CoreTexts), go-form's labels and messages and the names of months and days with them. Text without a translation is shown as written (go-translator's ShowOriginal: for tn and ctn the singular when n is 1, the plural otherwise), so a newly enabled language works before its catalogue exists. A locale without a catalogue of its own uses the one of its language (go-translator's language fallback): nl_BE finds nl.po, and nl finds nl_NL.po. go-form's own messages, such as its validation errors, that a catalogue does not translate show in go-form's catalogue of the page's language.
func VisitorForm ¶
VisitorForm is model, a struct with go-form's tags, as a visitor's form for templates, which render it with go-form's functions:
{{form_open .Form}}
{{form_field .Form "name" nil}}
<button type="submit">{{tl localizer "Send"}}</button>
{{form_close}}
It posts to action, with the spam trap (Honeypot) and, as its back field, the page being served or rendered (see Back): the element with the id key on that page, so a theme puts the form and its result in an element with that id. key, such as the module instance's key, also goes before the ids of the form's fields (form.Info.IDPrefix), so that the forms of several instances on one page keep ids of their own: the name field of the instance "guestbook" has the id guestbook-name. When the visitor's last post of this form, to action, had mistakes (KeepForm), the form shows that post again: the visitor's values and the messages at their fields. Of several such forms on the page, such as a comment form per post, only the one whose hidden fields hold the posted values does. A form in a fragment that loads after the page ({{deferredSlot}}) does not: the request for the page takes the kept post before the fragment's own request comes, so place such a form with {{slot}} or {{requiredSlot}}.
func WithActionModule ¶
WithActionModule is ctx of a request for a visitor action of the module instance key, below /_/<key>: the app sets it for every action, and Fail logs a failing action with that module, as it logs a module's page with the module on the page (Mount.Module).
Types ¶
type ActionLimits ¶
type ActionLimits struct {
// Body is the largest request body in bytes. It can only lower the
// limit: the site's limit of module actions
// (webthings.DefaultActionBodyLimit, or the module's
// webthings.Parts.ActionBodyLimit) applies before Actions, so a larger
// Body has no effect; raise Parts.ActionBodyLimit instead. Zero keeps
// the site's limit.
Body int64
// Posts is how many posts one visitor may send per Window (see
// Throttle); zero does not throttle.
Posts int
Window time.Duration
// Throttled answers a visitor over the limit. By default it returns the
// visitor to the form's page with ResultThrottled, as Back does.
Throttled http.Handler
}
ActionLimits are the limits of a module's visitor actions (see Actions).
type Calendars ¶
type Calendars struct {
// contains filtered or unexported fields
}
Calendars keeps the names and patterns of months and days that Formats look up in translations, per language (see Format.Calendars). Its zero value is ready to use; Forget drops what it keeps, for translations that changed.
type Content ¶
type Content struct {
// Template is the module's template name, for example "post".
Template string
// Module is the instance key. Render uses the module of the current page
// when empty.
Module string
// Title and Description are the page's title and description, for the
// layout's <title> and meta tags.
Title string
Description string
// Image is the image shown when the page is shared (View.Image).
Image string
// Data is what the template gets as the content, such as a post.
Data any
// Alternates maps locales to this content's path below the mount, for the
// language switcher, for example {"en": "/hello", "nl": "/hallo"}. Nil
// links to the mounted page in each language.
Alternates map[string]string
// EditURL is the admin screen of the content, for the edit link signed-in
// editors see (View.Editor), built from a route name (see
// webthings.Env.AdminURL), such as the editor of a news item. Empty links
// to the module's admin section, its route "index".
EditURL string
// Status is the response status; zero is 200 OK.
Status int
// Revision is set when the content is a revision an editor previews
// (see PreviewRevision and RevisionOf); Render marks the page as a
// preview.
Revision *RevisionPreview
// StructuredData are the content's schema.org items, such as a post's
// Article, which {{structuredData}} writes with the site's own (see
// Thing); the first is the page's main entity.
StructuredData []Thing
}
Content is what a module handler renders.
type EditLink ¶
EditLink is View.Editor: the signed-in editor and the admin screen of the current page or module item.
type Feed ¶
type Feed struct {
// Name names the feed's routes on the module's router: Name, a dot
// and the format, such as feed.atom and feed.rss for the default name
// "feed".
Name string
// Path is where the feed is below the module's page, without the
// extension, as the patterns of the module's routes: "/feed", the
// default, serves <page>/feed.atom and <page>/feed.rss, and
// "/category/{slug}/feed" a feed per category. Its last segment is not
// a wildcard.
Path string
// Page names the module's route whose pages link to the feed in their
// head ({{feedLinks}}), such as "category", the feed's wildcards
// filled in with those of the page. Empty links the feed on every page
// of the module; its Path then has no wildcards.
Page string
// Limit is how many of the newest entries the feed lists at most, such
// as a module's option for its own feeds, up to site.MaxFeedSize. Zero,
// the default, is the site's setting (site.Settings.FeedLimit).
Limit int
// Build returns the feed for a request of it.
Build FeedFunc
}
Feed is a feed a module offers below the page it is on (webthings.Parts.Feeds), in Atom and RSS (see package feed). The app serves it on the module's router (Router) at Path below the page, with the extension of each format, as routes named Name and the format:
webthings.Parts{
Public: routes,
Feeds: []frontend.Feed{
// <page>/feed.atom and <page>/feed.rss, routes feed.atom and feed.rss
{Build: s.feed},
// <page>/category/<slug>/feed.atom, linked on the category's page
{Name: "category-feed", Path: "/category/{slug}/feed", Page: "category", Build: s.categoryFeed},
},
}
The addresses of a feed come from its route names, as those of the module's other routes: routes.For(r).RoutePath("feed.atom") in a handler. The layout links to the feeds of the page it shows with {{feedLinks}}. A feed is a page of the site for the output cache, which keeps it for visitors until what visitors see changes, and for the caching of pages (ETag and the site's Cache-Control, see site.Settings.PageCache). Both answer a reader's If-None-Match, and an If-Modified-Since without it, with 304 Not Modified while the feed is unchanged. Its Last-Modified never goes back, also when the newest entry is taken off the site: it is when the app first served the feed as it is now, or its Updated time when that is later. Each process of the app keeps these times itself, so after a restart, or from another process, a reader that goes by the date gets the whole feed once more. It is not in sitemap.xml.
type FeedFunc ¶
FeedFunc returns a module's feed (Feed.Build) for a request of it, which the module's router serves: routes.For(r) builds addresses on the page, r.PathValue gives the wildcards of the feed's Path and MountFrom(r.Context()) the page and its language. limit is how many of the newest entries the feed lists, the feed's Limit or else the site setting (site.Settings.FeedLimit); the app leaves out entries beyond it.
The app fills in what the feed leaves empty: Title is the page's title, Link the module's page, ID the address of the feed's Atom document, Language the page's language, Author the site's name and Subtitle its tagline; Updated, without entries, is when the page was published. It sets Self to the address of the format served, makes links and image addresses that are paths on the site absolute, and gives an image without a type the one of its extension. Readers see the Title with the site's name, "News from Atelier Noord", translated with the site's translations. Entry content is HTML with absolute addresses, such as AbsoluteMarkdown gives. An error that wraps db.ErrNotFound, such as for a category that does not exist, answers with the site's 404 page; other errors are logged and answered with its error page (see Fail).
type FeedLink ¶
FeedLink is a link to a feed of the page shown, as {{feedLinks}} writes it in the page's head: the feed's media type, its title as readers see it and its absolute address.
type Format ¶
type Format struct {
// Locale is a language code such as "nl", "en", "en-US" or "de_DE".
Locale string
// Zone is where times in UTC (or the server's local zone) are shown; nil
// shows them as they are. Times in Floating (see Civil) and in any other
// zone are always shown as they are.
Zone *time.Location
// Lookup returns the translation of a msgid in a context (msgctxt) in
// Locale, and whether there is one, such as a translator's LookupC; it
// supplies the names and patterns of months and days, which replace
// the built-in ones (see CalendarTexts). Nil uses the built-in ones,
// English for a language without.
Lookup func(context, msgid string) (string, bool)
// Calendars, when set, keeps the names and patterns Lookup gives per
// language, so they are looked up once rather than for every date.
// Formats that share it must have a Lookup with the same translations.
Calendars *Calendars
}
Format formats dates, times and amounts of money in one language. Module code that writes e-mail uses it; Frontend.Format gives one with the site's timezone and translations, and FormatDate and FormatMoney are shortcuts.
func (Format) Date ¶
Date formats the day of t with a style (short, medium, long or full; "" is long) or a pattern. A zero time gives "".
func (Format) DateRange ¶
DateRange formats the days from start to end, leaving out what they share in the built-in languages, for example "25–27 October 2026". An end on the same day as start, or a zero end, gives one date.
func (Format) Money ¶
Money formats an amount in minor units (cents) of an ISO 4217 currency with the language's separators and symbol position: "€45.00" in English, "€ 45,00" in Dutch, "45,00 €" in German.
type Frontend ¶
type Frontend struct {
// contains filtered or unexported fields
}
Frontend builds Views and renders them. Module handlers receive it to render their screens with the site's menu, languages and layout.
func New ¶
func New( settings *site.Service, tree *pages.Service, library *media.Service, renderer Renderer, funcs ...template.FuncMap, ) *Frontend
New creates a frontend with one renderer, for example another template engine. Themed gives every theme its own templates. funcs are the modules' and site's template functions: the renderer itself does not get them (see Renderer and TemplateFuncs), but components, mail and Execute use them, and the site's tl among them translates labels such as those of share links.
func Themed ¶
func Themed( settings *site.Service, tree *pages.Service, library *media.Service, themes *Themes, funcs ...template.FuncMap, ) *Frontend
Themed creates a frontend that renders each theme with Go templates from its files (see Templates and Themes).
func (*Frontend) APILink ¶
APILink is the absolute address of the page r shows in the content API (package api), for a <link rel="alternate" type="application/json">: /api/v1/pages/<id>?locale=<locale> for the page itself, and /api/v1/pages/resolve?path=<address> for an address below a module's page, such as a post's. It is "" while the API is off, and for a request on no page, such as an error page.
func (*Frontend) AbsoluteMarkdown ¶
AbsoluteMarkdown renders editor content for readers away from the site, such as entries of a feed: as Markdown does, in the language of the request's page, but with every address of the site absolute (the site's BaseURL, or the address of r) and images as e-mail shows them (MailImage), a copy every reader can show, with its size. Relative links lead from the address of r, such as the feed's, as a reader resolves them in a document without a base (see markdown.Options.Base).
func (*Frontend) AltText ¶
AltText is the alt text editors wrote in the media library for the image asset id in locale, or "", such as for an image in e-mail.
func (*Frontend) Assets ¶
Assets serves the themes' assets/ folders at the route "theme", /theme/{file...}: /theme/<theme>/<file> from that theme and /theme/<file> from the active one. Themes find files they lack in the default theme. Every response has a strong ETag; a request for a file's current version (see asset) may be cached for a year, any other one revalidates.
func (*Frontend) BackLocale ¶
BackLocale returns the language of the page a visitor's form was on: the first segment of its path (see BackPath) when that is an enabled language, and the site's default language otherwise. It returns "" when the languages cannot be read.
func (*Frontend) BindForm ¶
BindForm maps a visitor's post onto input, a pointer to a struct with go-form's tags, and validates it, as a module action does before it stores anything:
var in PostInput
errs, err := env.Frontend.BindForm(r, &in)
if err != nil || len(errs) > 0 {
env.Frontend.KeepForm(w, r, key, in, errs)
frontend.Back(w, r, key, "invalid")
return
}
It is go-form's Bind: text is trimmed (MapOptions.TrimSpace), and its line breaks are read as "\n", as a browser counts them for maxlength, rather than the "\r\n" it sends (MapOptions.NormalizeNewlines). A value that cannot be read as its field's type, such as "abc" for a number, comes back as an error with the rule "invalid" in place of what validation says about the field; the others carry the rule they broke (form.FieldValidationError.Rule), for a result code per rule. Limits of an input that implements form.Overrider apply. The messages are in the language of the page the form was on (BackLocale), translated with the site's translations, for the form to show at its fields (KeepForm). err is only set when the post cannot be mapped at all. Date-times of datetime-local inputs are read in the site's timezone, in which visitor forms show them (MapOptions.Location), as the admin does.
func (*Frontend) CheckTemplates ¶
func (f *Frontend) CheckTemplates() ([]TemplateProblem, error)
CheckTemplates parses every template of every theme, *.gohtml in any folder, with the functions and render stages the theme's pages render with, and reports every problem a render would fail with: a template that does not parse, a function the site does not have, such as one of a module that is not in app.Use, and a {{template}} that names a template no file of the theme defines (go-partial's Partial.CheckTemplates). A file of the default theme that other themes use is reported once, with all of them. A site with a Renderer of its own has nothing to check. The error is that of reading the files.
func (*Frontend) CleanupForms ¶
CleanupForms removes the kept posts (KeepForm) whose five minutes have passed without a view of their page. Keeping a post does this too; App also runs it with its hourly cleanup, so posts nobody came back for do not stay when no new ones come.
func (*Frontend) Editors ¶
Editors wraps the public site: it recognises signed-in editors, so Editor and View.Editor work below it, and keeps their pages out of caches.
func (*Frontend) Execute ¶
Execute renders a template of the active theme on its own, without the layout, for example Execute(ctx, "partials/card", "nl", data), through go-partial as a part of the theme's pages. Templates in partials/ can be used, and every template function works in locale.
func (*Frontend) Fail ¶
Fail answers a public request that failed with err, for module handlers and visitor actions as for core's own pages: content that does not exist (db.ErrNotFound, which content and pages return) gets the site's 404 page; any other error is logged with the module and path and answered with status 500 by the theme's error page, 500.gohtml, with its message in the page's language (see Templates), or go-partial's plain error markup for a theme without one (go-partial's ext/errors Respond). The cause ends up in the log rather than in the visitor's browser. An HTMX request, such as a form post, gets the page too, which the interactions client's reloadOnError shows as the whole page.
func (*Frontend) FeedLinks ¶
FeedLinks are the links to the feeds of the page r shows, both formats of each, Atom first: the module's feeds linked on every page of it, and those linked on the page's route (Feed.Page). title is the page's title, the title of the links to the latter; the others have the title of the module's page. As for {{feedLinks}}, there are none on an error page, such as a category that does not exist, whose feed does not exist either: the request a Renderer gets knows the page's status.
func (*Frontend) FeedURL ¶
FeedURL is the address of the feed of the page r shows in format, "atom" or "rss", such as "/en/news/feed.atom", for a visible link to it: the feed linked on the page's route (Feed.Page), such as a category's on the category's page, or else the one linked on every page of the module; the first the module declared of either. A name picks the feed with that Name instead. It is "" for a page without such a feed, an unknown format and, as for FeedLinks, an error page.
func (*Frontend) Format ¶
Format returns the formatting of locale with the site's timezone and the site's translations, as the template functions use it.
func (*Frontend) Handler ¶
Handler serves the public site: "/" redirects to the default language, "/<locale>/..." resolves through the page tree. Paths at and below a module page go to that module's handler (see Router).
func (*Frontend) Image ¶
Image is how a page shows an image asset by default: a copy at most media.ImageWidth wide in media.DefaultVariantFormat, with a srcset of smaller ones, its size and the original for downloads (see media.Service.Image). Module views give their images with it, as ImageURL, ImageSrcset, ImageWidth, ImageHeight and ImageOriginalURL fields (<X>URL and so on for an image named otherwise, such as Photo); an empty id gives an empty Image.
func (*Frontend) KeepForm ¶
func (f *Frontend) KeepForm(w http.ResponseWriter, r *http.Request, key string, model any, errs form.FieldErrors)
KeepForm keeps a visitor's failed post of the form key for the page the form was on (see BackPath), for VisitorForm to show it again there: the values r posted of model's fields (r.PostForm), without passwords, files and the spam trap (Honeypot), and errs at their fields, such as those BindForm returned. model is the input struct, a go-form Schema, or the form VisitorForm made; key and the address r posted to must be those VisitorForm made the form with. Call it before the redirect, Back or BackWith. Nothing is kept when the form was on no page of this site; problems are logged, and the visitor then returns with the result alone. A failed post changed nothing: KeepForm tells the app so (NothingChanged).
One visitor (by ClientIP) has at most ten posts kept at a time, and none for more than five minutes; the actions' throttle (Limits) bounds how fast they come.
func (*Frontend) Mail ¶
func (f *Frontend) Mail(ctx context.Context, locale, key, name string, data any) (mail.Message, error)
Mail writes a module's e-mail from its template name in locale, for example Mail(ctx, "nl", "agenda", "confirmation", data): the subject, text and optional HTML of the message. The caller sets the recipients.
Every part renders through go-partial as a part of the theme's pages, with its functions, render stages and diagnostics: the subject and text as plain text (go-partial's RenderText), the HTML part as HTML.
func (*Frontend) MailImage ¶
MailImage is how e-mail shows an image asset: a copy at most media.MailImageWidth wide, never enlarged, as a JPEG, or as a PNG when the upload is a PNG with transparency (media.MailFormat); a GIF keeps its own address. Its URL is a path on the site when the library serves the file: mail needs it made absolute. An empty id gives an empty Image.
func (*Frontend) Markdown ¶
Markdown renders editor content with media resolved, in the language of the page being rendered or of the request's page (see MountFrom). JPEG and PNG images are shown as resized copies, as Image gives them; an image without a description in the Markdown gets its alt text from the media library.
func (*Frontend) MarkdownOptions ¶
func (f *Frontend) MarkdownOptions(ctx context.Context, locale, base string, mail bool) markdown.Options
MarkdownOptions are the options to render editor content with (markdown.Render, markdown.PlainMedia) as the site does: links to media give the file, and an image without a description in the Markdown gets its alt text in locale from the media library. base is markdown.Options.Base, such as "https://example.com/en/" for readers away from the site, or "" for the site's own pages. Images are shown as a page shows them (Image), a resized copy with a srcset; with mail, as e-mail shows them (MailImage), one copy every reader can show, such as for a feed or a mailing.
func (*Frontend) MediaURL ¶
MediaURL is the public URL of a media asset, or "". Images on pages use Image instead, which does not send the uploaded original.
func (*Frontend) ModulePreviewURL ¶
func (f *Frontend) ModulePreviewURL(ctx context.Context, key, locale, relative, revisionID string) string
ModulePreviewURL is the address at which an editor previews a revision of module content, at relative below the page of the module instance key, for example "/hello"; "" while the instance is on no page in locale.
func (*Frontend) NotFound ¶
func (f *Frontend) NotFound(w http.ResponseWriter, r *http.Request)
NotFound renders 404.gohtml when the site has one, with the title "Not found" in the page's language (the site's translations, see Translations); otherwise a plain 404.
func (*Frontend) PagePreviewURL ¶
PagePreviewURL is the address at which an editor previews a page revision in locale (see PreviewParam); "" when it has none, because its parent page is not published.
func (*Frontend) Route ¶
Route is the address of a named route of the app's root router, with its parameters as name and value pairs, as go-router's RoutePathArgs fills them in: Route("theme", "file", "default/site.css"), or a module's visitor action, whose names have the instance key in front, such as Route("downloads.download", "id", id). It is "" for an unknown route or a missing parameter, which is logged, and before the app has built its routes; they exist by the time a module's Setup runs.
func (*Frontend) Router ¶
Router returns a go-router for a module's public handler (Parts.Public), one per module instance. The site resolves the page a module is on and serves the router at the page's address for that request (go-router's ServeAt), so routes are relative to the page, whichever address the page has: "/{$}" is the page itself, "/{slug}" an item below it. Handlers see the request's whole path.
routes := env.Frontend.Router()
routes.Get("/{$}", list)
routes.Get("/{slug}", show).As("show")
Every request without a route, any path or method, gets NotFound: the site's 404 page rather than a plain "404 page not found" or "405 method not allowed". That answer is go-router's NotFound and MethodNotAllowed, not an interception of statuses, so a route that answers 404 itself, such as a lookup of a code that does not exist, keeps its own page. Modules therefore register no "/" route of their own. Route names are the module's own; in a handler, routes.For(r) builds their addresses at the page the request is on, such as routes.For(r).RoutePathArgs("show", "slug", slug) for /en/news/hello. Other routers report them with the instance key in front, such as news.show.
func (*Frontend) Search ¶
func (f *Frontend) Search(ctx context.Context, locale, query string, limit int) ([]SearchResult, error)
Search finds published pages and module content in locale that contain every word of query, best matches first. A module that fails is logged and left out, so one module cannot break the search.
func (*Frontend) SendMail ¶
SendMail writes letter with its mail template and sends it with sender.
It is meant for mail a visitor's post or an editor's action causes, after the post itself was stored: the mail is sent without the request's cancellation, so it still goes out when the visitor leaves the page (the sender's own timeout bounds it), and a failure is logged with the module and the mail's name. The error is returned for callers that tell the visitor; most ignore it.
func (*Frontend) ServeFeeds ¶
ServeFeeds registers a module's feeds (webthings.Parts.Feeds) on its router, the module's Public router of Router, before the app serves. It returns an error for a feed it cannot serve: one without Build, a Path that does not start with a slash or ends in a wildcard, a Page the router has no route for, a feed with wildcards linked on every page, a Limit below zero or above site.MaxFeedSize, or routes whose path or name the router has already.
func (*Frontend) SetBaseURL ¶
SetBaseURL sets the site's public address, for example "https://example.com", for absolute links such as share links; empty uses the address of each request.
func (*Frontend) SetComponents ¶
SetComponents tells the frontend the components of the module instances, by key; their Example is used when a theme has none.
func (*Frontend) SetDatabase ¶
SetDatabase gives the frontend the site's database, which keeps visitors' posts with mistakes (KeepForm) for their pages; Schema has its table. App calls it at Start. Without it no post is kept.
func (*Frontend) SetDiagnostics ¶
func (f *Frontend) SetDiagnostics(diagnostics render.Diagnostics)
SetDiagnostics sets what the themes' template renderers report, for example render metrics, and whether they show errors in detail (development); see render.Diagnostics. A Renderer of the site's own is left alone. Call it before serving.
func (*Frontend) SetEditors ¶
SetEditors sets how the frontend recognises a signed-in editor from a request; App connects it to the admin's sessions.
func (*Frontend) SetFormTheme ¶
SetFormTheme names the go-form theme the site's visitor forms render with (form_field and the other form functions), such as one the site registered with go-form's formtemplates.RegisterClassTheme with the classes of its CSS; empty is FormTheme. Call it before serving.
func (*Frontend) SetMail ¶
SetMail tells the frontend the mail templates of the module instances, by key; their Example (or a generated text) is used when a theme has none.
func (*Frontend) SetModules ¶
SetModules tells the frontend the type and API of every module instance: types let "news/post" fall back to "publishing/post", services are what template functions use (see Service).
func (*Frontend) SetReload ¶
SetReload makes the frontend read the themes' templates again for every render when reload is true, so edits to template files show without a restart; for development (webthings.Config.ReloadTemplates). Otherwise every template, the layout, content templates, partials and components, is parsed once. Call it before serving.
func (*Frontend) SetRoutes ¶
SetRoutes gives the frontend the app's root router, whose named routes Route builds addresses of. webthings.App sets it before it serves.
func (*Frontend) SetSearch ¶
func (f *Frontend) SetSearch(searchers map[string]SearchFunc)
SetSearch gives the frontend the modules' search functions by instance key.
func (*Frontend) SetSearchThrottle ¶
SetSearchThrottle allows each visitor (by ClientIP) at most limit searches per window on the search screen; more get the search page with SearchView.Throttled and status 429 Too Many Requests. A search reads all published content, so a visitor sending queries in a loop would keep the site busy. Visiting the screen without a query is not counted, nor is the {{search}} template function, whose queries the site's templates choose. Searches are counted in memory, per process. Zero or less turns the limit off; the default is DefaultSearchThrottle searches per minute.
func (*Frontend) SetStructuredData ¶
func (f *Frontend) SetStructuredData(funcs []StructuredDataFunc)
SetStructuredData sets the functions that add to the structured data of every page, in the order of the modules (see StructuredDataFunc).
func (*Frontend) SetTranslations ¶
func (f *Frontend) SetTranslations(tr *translator.Translator)
SetTranslations sets the site's translations (see Translations): the themes' templates, components and mail get its tl, tn, ctl and ctn, and core translates with it its own texts (CoreTexts), go-form's texts of visitor forms and the names of months and days (see Format). Without, every text shows as written. Call it before serving.
func (*Frontend) Sitemap ¶
func (f *Frontend) Sitemap(baseURL string, modules map[string]SitemapFunc, version func() uint64) http.Handler
Sitemap serves /sitemap.xml: every published page in every language, with language alternates, plus the paths modules report below their pages. A module that fails is logged and left out, so one module cannot break the sitemap. It lists at most 50,000 URLs, the most a sitemap file may hold.
version is the version of what the sitemap lists, such as the app's output cache version, which moves on with every change in this process of the published pages and the modules' content. The sitemap is built again as soon as it moved on, so a page published in this process is in the next sitemap, also one the output cache keeps for the new version. Otherwise it is built at most once every ten seconds. It lists the published routes as the page service keeps them in memory (see pages.Service.SetCacheTTL, webthings.Config.CacheTTL), so changes made by other processes show up within that cache time plus ten seconds. A nil version only counts the time.
func (*Frontend) SocialImageURL ¶
SocialImageURL is the image shown when a page is shared, as View.Image has it: a 1200 × 630 JPEG of the image ref, an asset ID or a media URL, cut around its focal point (see Site.SocialImageURL). Modules give it as the image of their structured data, such as an article's. Other files and addresses keep their own; "" gives "".
func (*Frontend) TagURI ¶
TagURI is an ID for a feed or entry that stays the same when its address changes (see feed.TagURI): a tag: URI on the site's host, that of BaseURL or else of r, without its port. date is a day the site had the host, such as when the item was created, and specific tells the item apart from every other the site named that day, such as "news/<post ID>".
func (*Frontend) Template ¶
Template returns the template for a module screen in a theme: <key>/<name> when the theme has it, otherwise <type>/<name>. A theme's own templates come before those it has from the default theme (see Themes), both names (go-partial's FirstExistingIn).
func (*Frontend) ThemeCSP ¶
ThemeCSP are the Content-Security-Policy sources of the theme a request renders in (see Theme.CSP): the active theme's, or the previewed one's (see WithTheme); nil when the theme has none or the settings cannot be read. The map is the theme's own, to read and never to change.
func (*Frontend) ThemeScreenshot ¶
ThemeScreenshot is the address of the picture of theme its theme.json names (Theme.Screenshot), versioned like {{asset}}, for pages outside the theme, such as the admin's theme cards; "" without one.
func (*Frontend) ThemeStyles ¶
ThemeStyles serves the values stylesheets of the themes at the route "theme.values", /theme/<theme>/values.css, from the current settings (see themeStyle); a theme without settings with "css" has none, and a theme's own assets/values.css is not served there. The response has a strong ETag; a request for the current version may be cached for a year, any other one revalidates.
func (*Frontend) ValidateSchema ¶
func (f *Frontend) ValidateSchema(r *http.Request, schema form.Schema, values url.Values) form.FieldErrors
ValidateSchema checks the values of a visitor's form defined at runtime, a go-form Schema such as a form an editor put together, as BindForm checks a struct: go-form's errors, with their messages translated with the site's translations into the language of the page being served (Mount.Locale), for the page to show at their fields with form_field or form_render.
type Label ¶
Label is a text with optional translations, written in theme.json as a string or as an object of language codes.
func (*Label) UnmarshalJSON ¶
UnmarshalJSON reads a plain string as the text for every language (key ""), and an object as a text per language code.
type Language ¶
type Language struct {
// Locale is the language code, such as "nl".
Locale string
// URL is the address of the page in this language.
URL string
// Active is set for the language being shown.
Active bool
// Default is set for the site's default language. When the page has
// more than one language, themes also give its address as the
// x-default alternate, for visitors whose language the site does not
// have, as the sitemap does:
//
// {{if .Default}}<link rel="alternate" hreflang="x-default" href="{{absURL .URL}}">{{end}}
Default bool
}
Language is one language of the site in a language switch: the current page in that language, or the home page when there is no current page. Languages the page is not translated into are left out.
type Letter ¶
type Letter struct {
// Locale is the language the mail is written in, such as the visitor's
// language, or the admin language for a notification to staff.
Locale string
// Key is the module instance, Name the mail template of its Module.Mail.
Key string
Name string
// Data is what the template gets as its dot.
Data any
// To are the recipients' addresses.
To []string
// ReplyTo is the address answers go to, such as the visitor who wrote a
// message; empty for the sender's own address.
ReplyTo string
}
Letter is a module e-mail to send with SendMail: the mail template Name of the module instance Key (see Mail), written in Locale with Data.
type MenuItem ¶
type MenuItem struct {
// ID is the page's ID, Path its path without language prefix, for
// example "/about/team", so templates can tell pages apart.
ID string
Path string
// ElementID is the HTML id of the item's element, "menu-" and the
// page's ID, as in <a id="{{.ElementID}}" href="{{.URL}}">. When the
// items of the theme's menu region, regions/menu.gohtml, carry it, an
// HTMX navigation sends only the items whose Active changes, rather
// than the whole menu; the region must then show nothing else that
// changes from page to page. Without it, the whole menu goes, as it
// does with every other request. Pages added, moved or renamed show in
// the menu with the next whole page.
ElementID string
Title string
// URL is the page's address in the page's language.
URL string
// Active is set for the page being shown and the pages above it, so a
// menu can mark where the visitor is.
Active bool
// Children are the published pages below this one.
Children []MenuItem
}
MenuItem is one published page in the site's menu, with the pages below it, in the order editors gave them. The menu is made once for every version of the published pages and shared by the requests that show it: a Renderer reads it and never changes it.
type Mount ¶
type Mount struct {
// Locale is the language of the request, such as "en".
Locale string
// Path is the page path without language prefix, for example "/blog".
Path string
// URL is the full path of the request, for example "/en/blog/hello".
URL string
// PageID is the ID of the page the request is on or below.
PageID string
// Module is the key of the module instance placed on that page; empty
// for content pages.
Module string
// Title is the published title of the page in Locale. A newer draft of
// the page never shows here, so module screens can use it as their title.
Title string
// contains filtered or unexported fields
}
Mount is where the current request sits in the page tree.
type Page ¶
type Page struct {
ID string
// ParentID is the parent page's ID, empty for the home page.
ParentID string
// Slug is the page's own part of its path in this language, Path the
// whole path without language prefix, for example "team" and
// "/about/team"; "/" for the home page. In a preview Path and URL are
// where the previewed revision shows: below the parent's published
// path, with the revision's slug (see pages.Service.PreviewPath), also
// for a page that was never published.
Slug string
Path string
Title string
Description string
Body template.HTML
URL string
// IsHome is set for the home page, which sites often design differently.
IsHome bool
}
Page is a published content page.
type Profile ¶
type Profile struct {
// Network is one of site.Networks, for example "instagram", for an icon.
Network string
// Label is the network's name in the page's language.
Label string
URL string
}
Profile is one of the site's social media profiles as templates see it.
type Query ¶
type Query struct {
// contains filtered or unexported fields
}
Query is a parsed search: its words, folded for comparison.
func ParseQuery ¶
ParseQuery splits a search into words. Case and accents are folded away, so matching ignores them.
type Renderer ¶
type Renderer interface {
Render(w http.ResponseWriter, r *http.Request, name string, view View) error
Has(name string) bool
}
Renderer turns a View into HTML. name is the template name without extension, for example "page" or "news/post". Has reports whether a template exists.
A Renderer other than the built-in Templates (webthings.Config.Renderer) renders the layout around the content itself: View.Content is the content template's data, the rest of View the layout's. It answers "page", "404" (optional, see Has), "search" (optional) and module screens such as "news/post", and HTMX requests as it sees fit. Render's request carries the page being rendered, so TemplateFuncs(r.Context()) gives every template function (core, modules' and site's, with localizer and locale of the page) ready to call; modules' functions such as {{posts "news" 3}} and components such as {{commentThread "remarks" .ID}} need them. When Render returns an error it must not have written anything, so the frontend can answer with its error page (see Frontend.Fail) and log it; the one exception is an error that wraps render.ErrShown, returned after the renderer answered with an error page itself, such as a theme's 500.gohtml.
func Missing ¶
func Missing() Renderer
Missing is the renderer of a site without templates; it explains what to add.
func Templates ¶
Templates renders with Go templates from files: layout.gohtml, with the View, wraps <name>.gohtml, with View.Content. Requests from HTMX receive only the content. The localizer in templates is the page language, for use with go-translator's `tl`.
Templates in partials/ can be used in every template, for example {{template "partials/card.gohtml" .}}. Functions whose first parameter is a context.Context get the request's context; Locale and Service work there.
For HTMX, a theme has two more folders. A template in regions/, such as regions/menu.gohtml, is a part of the layout that the layout places with {{slot "menu"}}: it gets the View, and goes along out of band with every HTMX request for the content, so a menu or a language switch follows the page. Of regions/menu.gohtml, whose items carry their ElementID, a navigation sends only the items that change (see MenuItem).
regions/head.gohtml is the region of the page's tags in <head>: its <title>, the canonical and alternate links, {{feedLinks}}, the description, the tags for sharing and {{structuredData}}, the page's JSON-LD (see Thing). The layout places it in <head> with {{slot "head"}}; it needs no ID and no {{oobAttr}}. A navigation replaces the tags it wrote on the page left with those of the page arrived at, so none stays behind and none is doubled: on a whole page the tags sit between two empty <template> markers, and with HTMX they are removed and added with two out-of-band swaps (go-partial's exp/head). The <title> goes on its own, which HTMX sets as the document's title.
A theme without regions/head.gohtml, also in the default theme, whose layout places {{slot "head"}}, gets core's head region: the title (on the home page the site's SEO title when there is one), the description (the page's, the SEO description or the tagline), robots for a preview, the canonical and alternate links, og:title, og:description, og:type, og:url, og:site_name, og:locale, og:image, twitter:card, the icons, {{feedLinks}}, {{apiLink}} and {{structuredData}}; a page with status 400 or above has no canonical, alternate or og:url. Those tags are webthings/head-tags.gohtml, which a theme's own regions/head.gohtml can call and add to:
{{template "webthings/head-tags.gohtml" .}}
<meta name="theme-color" content="#336699">
A layout without {{slot "head"}} writes its tags itself and gets no head region from core.
A template in fragments/, such as fragments/comments.gohtml, is a part of a page that HTMX can ask for on its own: a template places it with {{slot "comments"}}, or {{requiredSlot "comments"}}, which fails when the theme has no such fragment, and a request whose target is #comments gets only that fragment; {{deferredSlot "comments"}} places a placeholder that loads it after the page, {{deferredSlot "comments" "reveal"}} when it scrolls into view. A fragment gets View.Content, like the content template; its outer element has the fragment's name as ID, a region's also {{oobAttr}}. A fragment of search.gohtml, such as the results a live search asks for while the visitor types, comes with the regions out of band, as the content does: the search's address changes with every query, and the <title> of regions/head.gohtml and the language links follow it.
A theme with a 500.gohtml shows it when a template fails, outside development: on its own, without the layout, with go-partial's ext/errors Data as its dot and the View for @model (see render.Diagnostics.ErrorPage). Its .Message is a safe sentence, already translated in the page's language with the site's translations (see CoreTexts), so {{.Message}} shows it.
Every template is parsed once and kept; see Frontend.SetReload for development.
type RevisionPreview ¶
type RevisionPreview struct {
// Number is the revision's number in the item's history.
Number int
// Published is set when the previewed revision is the published one;
// usually it is not.
Published bool
// PublishedURL is the same address without the preview.
PublishedURL string
}
RevisionPreview is View.Revision: the revision an editor previews.
func RevisionOf ¶
func RevisionOf[F, T any](r *http.Request, item content.Item[F, T], previewID string) *RevisionPreview
RevisionOf is Content.Revision for an item a module shows: set when item holds the revision the request previews (see PreviewRevision), nil otherwise.
type SearchFunc ¶
SearchFunc searches a module instance's published content in locale for at most limit results. Modules parse query with ParseQuery and score and summarise with its methods, so all results rank alike.
type SearchResult ¶
type SearchResult struct {
Title string
// Summary is plain text around the first match.
Summary string
URL string
// Module is the module instance key, empty for pages.
Module string
// Type is the module type, for example "publishing", or "page".
Type string
// Score orders the results, highest first; see Query.Score.
Score int
}
SearchResult is one hit of the site search.
func Rank ¶
func Rank(results []SearchResult, limit int) []SearchResult
Rank sorts results best first (by score, then title) and keeps at most limit of them.
type SearchView ¶
type SearchView struct {
Query string
Results []SearchResult
// URL is the search screen in this language, for the form's action.
URL string
// Throttled is set when the visitor searched too often in a short time
// (see Frontend.SetSearchThrottle): the search did not run, Results is
// empty and the response has status 429, so the template shows a
// message instead, also where a live search swaps the results.
Throttled bool
}
SearchView is the data of search.gohtml.
type ShareBar ¶
type ShareBar struct {
// URL is the absolute address of the shared page, Title its title.
}
ShareBar is the data of the site/share component.
type ShareLink ¶
type ShareLink struct {
Network string
// Label is the network's name; the share function translates it.
}
ShareLink is a link that shares a page on one network.
func ShareLinks ¶
ShareLinks returns the share links for an absolute page address and its title, in a fixed order, with English labels.
type SignedIn ¶
type SignedIn struct {
UserID string
Name string
// CanEdit reports whether the editor may change what an admin URL shows,
// such as the editor of a page; CanView whether they may see it, which
// allows previews (see PreviewParam).
CanEdit func(adminURL string) bool
CanView func(adminURL string) bool
// AdminURL is the address of a named admin route, such as
// "admin.pages.edit" with "id", as admin.Admin.TryURL builds it: links
// into the admin never assume its path. It returns an error for an
// unknown route or parameter.
AdminURL router.PathFunc
}
SignedIn is an editor, signed in to the admin, viewing the public site.
type Site ¶
type Site struct {
Name string
Tagline string
SEOTitle string
SEODescription string
Footer template.HTML
// LogoURL is the logo, an image of the media library that the editors
// chose in the settings, at most 600 pixels wide, never enlarged, in
// the format it was uploaded in (media.OriginalFormat): a PNG stays a
// lossless PNG and a JPEG a JPEG. A PNG no wider, a GIF and an SVG
// keep their own address. A theme's own logo is one of its assets
// ({{asset "logo.svg"}}), not LogoURL.
LogoURL string
// FaviconURL is the favicon as a 32 × 32 PNG, for <link rel="icon">;
// AppleTouchIconURL is the same image as a 180 × 180 PNG, for <link
// rel="apple-touch-icon">. Both are cut square around the image's focal
// point.
FaviconURL string
AppleTouchIconURL string
// SocialImageURL is the image shown when a page is shared on social
// media and has no image of its own (Site settings), for og:image: a
// 1200 × 630 JPEG cut around the image's focal point, or smaller with
// those proportions when the image is.
SocialImageURL string
Organisation string
Email string
Phone string
Address string
HomeURL string
// Profiles are the site's social media profiles, in the order of
// site.Networks, labelled in the page's language.
Profiles []Profile
}
Site is the website identity in the current language.
type SitemapFunc ¶
SitemapFunc lists a module's public paths in a locale, relative to the page the module is mounted on, for example "/hello-world".
type StructuredDataFunc ¶
StructuredDataFunc adds to the structured data of a page, or changes it, for a module that adds to the pages of others, such as a registrations module that gives an event its offers. items are the page's items from its module (View.StructuredData, see Content.StructuredData) and those earlier functions returned; it returns them with its own, and gets the page shown, View.Content and View.Module telling what it is.
type Template ¶
type Template struct {
// Name is the file name without extension, for example "post".
Name string
// Data is a value of the template's data type, for example Post{}.
Data any
// Example is a hand-written starting point. Empty generates one from Data.
Example string
}
Template is a template a module renders, declared so sites can be checked and starting points can be generated.
func CoreComponents ¶
func CoreComponents() []Template
CoreComponents are the components of core, rendered by its template functions and replaced by a theme with site/<name>.gohtml.
func SearchTemplate ¶
func SearchTemplate() Template
SearchTemplate declares search.gohtml, for checks and starting points.
type TemplateProblem ¶
type TemplateProblem struct {
// Themes are the themes that render the file, such as "default" and
// "dark" for a file of the default theme that dark does not replace.
Themes []string
// Path is the file in the site's templates, in the folder of the theme
// it comes from, such as "default/layout.gohtml"; the file alone for a
// single theme at the root of the templates.
Path string
// Line and Column are where the problem is in the file, counted from 1;
// Column is 0 when the parser does not say.
Line int
Column int
// Func is the function the template calls that the site does not
// have, such as "newsletterForm"; empty for other problems.
Func string
// Template is the template a {{template}} names that no file defines;
// empty for other problems.
Template string
// Suggestion is the function or template whose name is closest to Func
// or Template; empty when none is close.
Suggestion string
// Message says what is wrong, without the position, such as
// `function "newsletterForm" not defined`.
Message string
// EveryPage reports whether every page of the Themes fails with it: a
// template that does not parse or calls a function the site does not
// have, in layout.gohtml, regions/ or partials/, which every page
// parses.
EveryPage bool
}
TemplateProblem is a problem of a theme's template that CheckTemplates finds: one its renders fail with.
func (TemplateProblem) Error ¶
func (p TemplateProblem) Error() string
Error is the position and the message, with the suggestion when there is one.
func (TemplateProblem) Position ¶
func (p TemplateProblem) Position() string
Position is the problem's file, line and, when known, column, such as "default/layout.gohtml:69:9".
type Theme ¶
type Theme struct {
// Name is the folder name.
Name string
Title string
Description string
Settings []ThemeSetting
// CSP are the sources, by directive, that the site's
// Content-Security-Policy allows for pages in the theme, besides the
// site's own, such as a font host: theme.json's "csp", as
// {"font-src": ["https://fonts.example.com"]}, checked with
// site.CheckCSPSources.
CSP map[string][]string
// Screenshot is a picture of the theme that the admin shows with it,
// theme.json's "screenshot": the path of an image in the theme's
// assets/ folder, such as "screenshot.png"; empty without one. See
// checkScreenshot.
Screenshot string
// Files are the theme's templates on top of the default theme's.
Files fs.FS
// contains filtered or unexported fields
}
Theme is one design.
type ThemeSetting ¶
type ThemeSetting struct {
Name string `json:"name"`
// Label is shown in the admin, one text or one per admin language.
Label Label `json:"label"`
// Type is color, text, select or number.
Type string `json:"type"`
Options []string `json:"options"`
// Default is the value without one saved; for a number, the number
// alone, such as "38".
Default string `json:"default"`
// Min and Max bound a number, both included; a number needs both.
Min *float64 `json:"min"`
Max *float64 `json:"max"`
// Step is the difference between a number's values, counted from Min,
// such as 0.5; zero allows any number between Min and Max.
Step float64 `json:"step"`
// Unit is the CSS unit of a number, one of cssUnits, such as "rem",
// "px" or "%", which the values stylesheet writes after it; empty for
// a number without one, such as a line height.
Unit string `json:"unit"`
// CSS is the custom property, such as "--accent", that the theme's
// values stylesheet sets to the value of a color, select or number
// setting (see Themes); a select's options must then be plain CSS
// values (see cssOption).
CSS string `json:"css"`
}
ThemeSetting is a value editors choose for a theme.
type ThemeView ¶
type ThemeView struct {
Name string
// Values are the theme's settings, with defaults for unset ones.
Values map[string]string
}
ThemeView is the active theme as templates see it.
type Themes ¶
type Themes struct {
// contains filtered or unexported fields
}
Themes are the designs of a site, one folder each in the templates:
templates/default/layout.gohtml templates/default/page.gohtml templates/default/assets/site.css served at /theme/site.css templates/default/theme.json optional title and settings templates/dark/layout.gohtml only what differs from default templates/dark/assets/site.css
A theme finds the templates and assets it does not have in "default", so a variant can be just a stylesheet. A site with layout.gohtml directly in its templates folder has one theme, "default", made of the whole folder.
theme.json names the theme and declares settings that editors change in the admin, such as colours; templates read them as {{.Theme.Values.accent}} in the layout or {{theme "accent"}} anywhere:
{
"title": "Atelier",
"description": "Warm colours, serif headings",
"settings": [
{"name": "accent", "label": "Accent colour", "type": "color", "default": "#c2410c", "css": "--accent"},
{"name": "headings", "label": "Headings", "type": "select", "options": ["serif", "sans"], "default": "serif"},
{"name": "measure", "label": "Text width", "type": "number", "min": 30, "max": 48, "step": 1, "unit": "rem", "default": "38", "css": "--measure"},
{"name": "notice", "label": {"en": "Notice", "nl": "Mededeling"}, "type": "text"}
]
}
A number setting has a min and a max, and optionally a step (from min) and a unit; its value is the number alone, such as "38", and a template adds the unit itself. A colour, select or number setting with "css" becomes a custom property of the theme's values stylesheet, /theme/<theme>/values.css, which {{themeStyle}} links: :root { --accent: #c2410c; --measure: 38rem; }, a number with its unit. It needs no style attribute, so it works under a Content-Security-Policy without 'unsafe-inline' (see Frontend.ThemeStyles). A text setting never goes into CSS.
func LoadThemes ¶
LoadThemes reads the themes in files. Every folder with a layout.gohtml or a theme.json is a theme; layout.gohtml at the root makes the root the only theme. Files without any themes give an empty list.
type Thing ¶
Thing is one schema.org item of a page's structured data, such as an Article: its "@type" and properties, as encoding/json writes them: text, numbers, booleans, time.Time, nested Things and lists. Empty values, such as "", nil, a zero time.Time and empty lists, are left out, so a module can name every property without checking for content first. Addresses in the properties that hold them (see urlProperties) may be paths, such as a post's URL or an image of the media library: they are made absolute. Dates are best given as text, in the content's own timezone, such as "2026-10-07" or "2026-10-07T19:30:00+02:00".
func ItemList ¶
ItemList is the structured data of a page that lists items with pages of their own, such as a news list or an agenda (Content.StructuredData): an ItemList named name of their addresses, in the order the page shows them. Items without an address are left out; without any, ItemList is nil, which adds nothing.
type ThrottleOption ¶
type ThrottleOption func(*throttle)
ThrottleOption changes how Throttle answers.
func OnLimit ¶
func OnLimit(handler http.Handler) ThrottleOption
OnLimit answers requests over the limit with handler instead of a bare 429, for example to return to the form: OnLimit(http.HandlerFunc(func(w, r) { frontend.Back(w, r, "comment", "throttled") })).
type View ¶
type View struct {
Site Site
Locale string
// Path is the current URL path, including the language prefix. Links
// here, in Menu and Languages are paths; {{absURL .Path}} makes one
// absolute, for example for a canonical link or hreflang.
Path string
Title string
Description string
// Image is an optional absolute or site-relative image URL for sharing;
// Site.SocialImageURL is the site's default. An image from the media
// library is given as Site.SocialImageURL is.
Image string
// Menu is the published page tree in this language, shared with
// other requests: read it, never change it (see MenuItem).
Menu []MenuItem
// Languages links to this same content in the other languages.
Languages []Language
// Page is the current page; for module screens the page they are mounted on.
Page *Page
// Module is the key of the module instance on module screens, for example
// "faq", so one template can serve several instances differently.
Module string
// Content is the data of the content template: a Page, a NotFound or the
// module's view.
Content any
// StructuredData are the schema.org items of a module's content
// (Content.StructuredData), which {{structuredData}} writes with those
// of the site and the page (see Thing).
StructuredData []Thing
// Preview is true when an editor previews a revision instead of the
// published content (see PreviewParam); Revision says which, for a
// banner such as "Preview of revision 4 (not published)". Layouts
// should also add <meta name="robots" content="noindex"> then.
Preview bool
Revision *RevisionPreview
// Theme is the active theme and the values editors chose for it.
Theme ThemeView
// Editor is set when a signed-in editor who may edit the current page or
// module item views it; nil for visitors. See Editors.
Editor *EditLink
// Status is the response's status, such as 404 for 404.gohtml; zero
// is 200 OK. Templates gives it to the response; for another
// Renderer, the frontend sets it on the response writer Render gets.
Status int
// contains filtered or unexported fields
}
View is everything a public template receives.
type When ¶
When is when something takes place, such as an event, for templates to show in one line: Start and End in the zone they are shown in, such as an event's own timezone, or as civil times (see Civil). End is zero when it has no end; for something that takes AllDay it is the last day. The events module's Event and the registrations module's Subject give theirs with a When method, so one partial shows both:
{{/* @dot github.com/donseba/go-webthings/frontend.When */}}
{{if .AllDay}}{{dateRange .Start .End "full"}}
{{else if .SameDay}}{{date .Start "full"}} · {{timeRange .Start .End}}
{{else}}{{date .Start "full"}}, {{time .Start}} – {{date .End "full"}}, {{time .End}}{{end}}
Source Files
¶
- actions.go
- apilink.go
- assets.go
- changes.go
- check.go
- component.go
- context.go
- editor.go
- feeds.go
- format.go
- forms.go
- frontend.go
- handler.go
- head.go
- keptform.go
- mail.go
- markdowncache.go
- menu.go
- preview.go
- routes.go
- scaffold.go
- screenshot.go
- search.go
- searchcache.go
- sendmail.go
- share.go
- sitemap.go
- structured.go
- templates.go
- themes.go
- themestyle.go
- translations.go
- view.go
- visitors.go
- when.go