Help us shape the future of Go! Take the 2026 Go Developer Survey

editor

package
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Overview

Package editor is the ready-made admin for content stores (see the content package): an overview with language tabs, a go-form editor per language, publication and, for revisioned content, history, restore and preview. A module supplies a form struct and two mapping functions; extra screens can be added with Config.Routes.

Index

Constants

View Source
const (

	// PublishLinked asks before a revision is published that moves a page
	// or an item away from the address that published content links to.
	PublishLinked = "Published items link to the old address, and their links will lead nowhere. Publish anyway?"
)

Questions before an item that published content links to leaves its address.

Variables

View Source
var ErrPermanent = errors.New("permanent_item")

ErrPermanent refuses a change that would take an item that is permanent by its nature (Config.Permanent) off the website. Config.Issue can name it, as the page tree does for the home page; the default message is general.

Functions

func MediaChoices

func MediaChoices(ctx context.Context, library *media.Service, images bool, selected string) (form.SortedSelect[string], error)

MediaChoices is a dropdown of media files by name. Its field offers none with a placeholder, such as placeholder:"—". With images set, only images are offered.

func New

func New[F, T, I any](config Config[F, T, I]) admin.Section

New returns the admin section for a content type.

func RedirectTargets

func RedirectTargets(table *pages.Table, locale, leaving, selected string) form.SortedSelect[string]

RedirectTargets lists the published pages of locale that addresses can redirect to, in tree order and indented by depth, with selected chosen: all of them, or without the page leaving and the pages below it.

func RedirectedNotice

func RedirectedNotice(r *http.Request, a *admin.Admin, done string, count int, err error) flash.Message

RedirectedNotice is the toast after a change that redirected count old addresses: done, a translation key such as "Published.", and how many redirect now. A failed redirect, err, makes it a warning.

func Redirector

func Redirector(r *http.Request, a *admin.Admin, check *links.Check) links.Redirector

Redirector returns the module that redirects old addresses (see links.Check.Redirects) when the user of r may add redirects to it; nil otherwise, also for a nil check.

func Slug

func Slug(typed, title string) string

Slug is the web address an editor typed into an item's slug field, or, when the field is left empty, one made from title (content.Slug), for an editor's Apply:

text.Slug = editor.Slug(in.Slug, in.Title)

A title without letters content.Slug can use, such as one in another script, gives "".

func Status

func Status[F, T any](item content.Item[F, T], revisioned bool) string

Status names the publication state of an item for overviews, as a translation key: "Scheduled", "In review", "Draft", "Changes not published" or "Published". "In review" and "Changes not published" apply to revisioned content only; all content can be "Scheduled", "Draft" or "Published".

Types

type Config

type Config[F, T, I any] struct {
	// ID, Title, Path and Icon describe the admin section; see admin.Section.
	// Path is below the admin, such as "/news"; empty is "/" followed by ID.
	ID    string
	Title string
	Path  string
	Icon  string
	// NewLabel is the "new item" button text, for example "New post".
	NewLabel string
	// Links are extra buttons on the overview, for example to categories.
	Links []Link
	// Permission is the section whose rights apply, when shared with another
	// section (see admin.Section.Permission). Empty means ID.
	Permission string
	// Single allows one item, for modules where an instance is one thing (a
	// form). "New" disappears once it exists.
	Single bool

	// Store keeps the items, bound to the instance (see
	// content.Revisions.In and content.Collection.In).
	Store content.Store[F, T]
	// Locales gives the languages the editor offers tabs for.
	Locales locales.Provider

	// Form fills the input for one language of an item; item is empty for new content.
	Form func(ctx context.Context, item content.Item[F, T], locale string) (I, error)
	// Apply writes a validated submission into the draft for one language.
	Apply func(ctx context.Context, input I, draft *content.Draft[F, T], locale string) error
	// Label names an item in lists, from its text in one language.
	Label func(text T) string
	// ItemLabel names an item from the whole item, for content named by its
	// Fields (a person's name, a social network). It is used instead of Label
	// when set.
	ItemLabel func(item content.Item[F, T], locale string) string
	// Notes are shown muted after the labels in the overview, such as a
	// download count or "Expired", by item ID; a missing or empty note shows
	// nothing. It is called once per overview with all listed items, so a
	// module can read what it needs in one query. Notes are shown as written:
	// translate fixed texts with admin.T and admin.TN, which use ctx.
	Notes func(ctx context.Context, items []content.Item[F, T], locale string) map[string]string

	// PreviewURL is the address at which an editor previews a revision on
	// the website, in locale: its public address with
	// frontend.PreviewParam (see frontend.PagePreviewURL and
	// ModulePreviewURL); empty when it has none yet, for example while the
	// module is on no page. Nil offers no preview.
	PreviewURL func(ctx context.Context, item content.Item[F, T], locale string) string
	// Names maps user IDs to names for the revision history.
	Names func(ctx context.Context) (map[string]string, error)
	// Issue turns module errors of saving, publishing and deleting into
	// messages for the editor; empty means unknown. A refused delete because
	// the item is still referenced (a foreign key) defaults to "This item is
	// still in use.".
	Issue func(err error) string
	// Badge is a count next to the section in the navigation; see
	// admin.Section.Badge.
	Badge func(ctx context.Context) int
	// Stylesheet names a CSS file in Files that the admin links; see
	// admin.Section.Stylesheet.
	Stylesheet string

	// Files can override the editor templates and add module templates.
	Files fs.FS
	// Overview replaces the default overview, for content with a structure
	// of its own such as the page tree. Nil uses the default list.
	Overview func(w http.ResponseWriter, r *http.Request, editor Editor[F, T, I])
	// Permanent reports items that must stay on the website by their
	// nature, such as the home page: their editor offers no unpublish, no
	// planned unpublication and no delete, refuses them when they are
	// posted anyway, and offers no way to remove the mark. Administrators
	// can mark other items permanent, and remove that mark again, in the
	// item's editor when the admin keeps marks (see admin.Permanence).
	Permanent func(item content.Item[F, T]) bool
	// Routes adds module screens to the section.
	Routes func(routes *admin.Routes, editor Editor[F, T, I])
	// Edit adds to the edit screen of an item that exists, once the editor
	// has built it: it may add Notices, such as the pages that link to the
	// item, and change or replace the forms of view, such as with a
	// question before the item is unpublished (admin.Admin.Confirm) or a
	// publish form that posts to a route of its own (Config.Routes). Nil
	// adds nothing.
	Edit func(r *http.Request, editor Editor[F, T, I], item content.Item[F, T], locale string, view *EditView) error
	// Saved follows up the save of an item that existed, with the item
	// before and after: it may act on the change, such as redirecting the
	// item's old address (Linked.Saved), and returns the toast to show,
	// nil for the editor's own. Nil does nothing.
	Saved func(r *http.Request, editor Editor[F, T, I], before, after content.Item[F, T], locale string) *flash.Message
	// Trash adds the module's own deleted records to the section's trash,
	// such as a form's submissions, next to the deleted items of Store.
	Trash admin.Trash
}

Config describes one editable content type. I is a go-form input struct for one language of an item.

type Conflict

type Conflict struct {
	// Text says who saved the item and when; it is shown as written.
	Text string
	// URL is the editor of the saved version, which the notice opens in a
	// new tab to compare.
	URL string
	// Form is the id of the edit form; Name and Value are those of the
	// "Save anyway" button that submits it. Value is the saved version,
	// which the form may then save over.
	Form  string
	Name  string
	Value string
}

Conflict is the notice of a save that was refused because someone saved the item after its form was opened (see EditView.Conflict).

type EditView

type EditView struct {
	// Title is the item's label, or for a new item the translated "new"
	// label of the section; it is shown as written.
	Title   string
	BackURL string
	// BackLabel names the overview BackURL leads to, the section's title,
	// as a translation key.
	BackLabel string
	Tabs      []admin.LocaleTab
	Panel     *admin.RevisionPanel
	Publish   *form.RenderModel
	Unpublish *form.RenderModel
	Form      form.RenderModel
	Errors    form.FieldErrors
	// Conflict is the notice of a save that was refused because someone
	// saved the item after its form was opened; Form then shows what the
	// editor typed. Nil otherwise.
	Conflict *Conflict
	// ReadOnly is set for users who may view the item but not edit it: Form
	// is then read-only (disabled, without its button).
	ReadOnly bool
	Delete   *form.RenderModel
	// Permanent items stay on the website: they cannot be unpublished,
	// scheduled to be unpublished or deleted. PermanentFixed items are
	// permanent by their nature (see Config.Permanent); the mark of others
	// was set by an administrator.
	Permanent      bool
	PermanentFixed bool
	// Permanence sets or removes the permanent mark, for administrators
	// when the admin keeps marks; nil otherwise.
	Permanence *form.RenderModel
	// Notices are warnings shown above the form (see Config.Edit).
	Notices []Notice
}

EditView is the data of the edit screen of one item in one language: the form, its buttons and, for revisioned content, the revision panel.

type Editor

type Editor[F, T, I any] struct {
	// contains filtered or unexported fields
}

Editor is the editor of one content type inside the admin, handed to Config.Routes so a module's own screens can link to its items and check its permissions.

func (Editor[F, T, I]) Admin

func (e Editor[F, T, I]) Admin() *admin.Admin

Admin returns the admin the editor belongs to, for module screens.

func (Editor[F, T, I]) Issue

func (e Editor[F, T, I]) Issue(r *http.Request, err error) string

Issue is the message for editors of an error of saving, publishing or deleting an item, translated: Config.Issue's, or the editor's own for the errors of the content package; empty for an unexpected error. Module screens (Config.Routes) explain refusals with it.

func (Editor[F, T, I]) Locale

func (e Editor[F, T, I]) Locale(r *http.Request) (string, locales.Config, error)

Locale returns the content language of the request (?locale=), defaulting to the site's default language, and the language configuration.

func (Editor[F, T, I]) Permission

func (e Editor[F, T, I]) Permission(action string) admin.Permission

Permission returns the permission for an action in the section whose rights apply to the editor.

func (Editor[F, T, I]) RefusePermanent

func (e Editor[F, T, I]) RefusePermanent(w http.ResponseWriter, r *http.Request) bool

RefusePermanent shows the editor with the reason when the item in the URL is permanent, for a change that would take it off the website, and reports whether it did. Module screens (Config.Routes) that take an item off the website call it first, as the editor's own do.

type IndexView

type IndexView struct {
	Title    string
	NewURL   string
	NewLabel string
	Links    []Link
	// TrashURL shows the section's deleted items in the admin's trash.
	TrashURL   string
	Revisioned bool
	Tabs       []admin.LocaleTab
	Rows       []Row
	// Pager pages through long lists.
	Pager admin.Pager
}

IndexView is the data of the overview of a content type: one row per item, in one language.

type Link struct {
	Label string
	URL   string
	Route string
}

Link is a button on the overview: to URL, or to the admin route named Route, such as admin.RouteName("news-categories", "index"), whose address is built when the overview is shown. Route is used instead of URL when set; an unknown route fails the overview.

type Linked

type Linked[F, T, I any] struct {
	// Check is the site's link check (webthings.Env.Links); nil adds
	// nothing.
	Check *links.Check
	// Address is the address of item, which may be any revision of it, on
	// the site in locale, such as "/en/news/hello": where visitors find it
	// once it is published. Empty when it has none there, such as an item
	// of an instance on no page. It is asked only for languages in which
	// the item is complete (content.Spec.Available).
	Address func(ctx context.Context, item content.Item[F, T], locale string) (string, error)
	// Below, if set, are the addresses below the item's address in locale
	// that leave the website with it, such as the entries of a directory's
	// category, which go offline when the category is unpublished but keep
	// working at their old addresses when its slug changes. The editor then
	// counts the links below the item's address apart from those to the
	// item itself, and unpublishing it with redirects redirects these
	// addresses too, to the same page. Without it, links below the item's
	// address count as links to the item. It is asked only for languages in
	// which the item is complete.
	Below func(ctx context.Context, item content.Item[F, T], locale string) ([]string, error)
}

Linked adds the links to an item to the editor of a module whose items have an address of their own, such as posts or events. Its methods are the editor's hooks:

linked := editor.Linked[PostFields, PostText, postInput]{Check: env.Links(), Address: service.address}

editor.New(editor.Config[PostFields, PostText, postInput]{
	// ...
	Edit:   linked.Edit,
	Routes: linked.Routes,
	Saved:  linked.Saved,
})

A module with hooks of its own calls these from them.

func (Linked[F, T, I]) Edit

func (l Linked[F, T, I]) Edit(r *http.Request, e Editor[F, T, I], item content.Item[F, T], locale string, view *EditView) error

Edit adds the notices about links to the editor of a published item (Config.Edit).

func (Linked[F, T, I]) Routes

func (l Linked[F, T, I]) Routes(routes *admin.Routes, e Editor[F, T, I])

Routes adds the editor's routes that redirect: "unpublish-redirect", and "revision-publish-moved" for content with revisions (Config.Routes).

func (Linked[F, T, I]) Saved

func (l Linked[F, T, I]) Saved(r *http.Request, e Editor[F, T, I], before, after content.Item[F, T], _ string) *flash.Message

Saved redirects the old addresses of a published item without revisions to its new ones when saving changed them, such as with a new slug, and the user may add redirects (Config.Saved). Content with revisions changes its address when a revision is published instead (Routes).

type MovedInput

type MovedInput struct {
	Redirect bool `` /* 155-byte string literal not displayed */
}

MovedInput publishes a revision that changes the address of a page or an item, redirecting the old addresses to the new ones when Redirect is set.

type Notice

type Notice struct {
	// Text is shown as written: translate it first (admin.T, admin.TN).
	Text string
	// Items are listed below the text, such as the pages that link to the
	// item.
	Items []NoticeItem
	// Forms act on the notice, such as one that creates a redirect.
	Forms []form.RenderModel
}

Notice is a warning on the edit screen (see Config.Edit): a text, the content it concerns and forms that act on it.

type NoticeItem

type NoticeItem struct {
	Label string
	URL   string
	Note  string
}

NoticeItem is an entry in a Notice's list, linked to URL when set. Label and Note, which is shown muted after it, are shown as written.

func LinkItem

func LinkItem(r *http.Request, a *admin.Admin, check *links.Check, link links.Link) NoticeItem

LinkItem names the content a link is in, linked to its editor, with a note of its kind, a page or the title of its module, and its language, such as "News · en".

func LinkItems

func LinkItems(r *http.Request, a *admin.Admin, check *links.Check, found []links.Link) []NoticeItem

LinkItems lists the content that the links found are in, once each, as notice items: linked to its editor, with its kind and language (see LinkItem).

type RedirectInput

type RedirectInput struct {
	Target form.SortedSelect[string] `form:"dropdown" label:"Redirect to" required:"true" placeholder:"—"`
}

RedirectInput chooses the page that the addresses a page or an item leaves redirect to.

type Row

type Row struct {
	Title string
	// Note is shown muted after the title; see Config.Notes.
	Note    string
	URL     string
	Status  string
	Missing bool
	// Moves are the item's up and down arrows, for ordered stores; see
	// admin.Moves.
	Moves []admin.Move
}

Row is one item in the overview, with its publication state (see Status).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL