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

lists

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: 21 Imported by: 0

README

Lists

Short ordered lists such as an FAQ, services, features or steps: entries with a title, Markdown text, an optional image and an optional link.

One instance is one list, so a site with an FAQ and a list of services adds two instances. Entries have no pages of their own: a list is shown whole, on the page it is placed on or anywhere else through a template function.

Add it to the site

app.Use(
    lists.Module("faq", "FAQ"),
    lists.Module("services", "Services"),
)

The key (faq) is the list's name in URLs and templates and must be unique on the site. The title (FAQ) is what editors see in the admin navigation and the page editor.

Place it on a page

In the admin, open a page and choose the list under Shows on this page. With the page at /faq:

Address Template Shows
/faq faq/list.gohtml the published entries, in the order set in the admin

Other addresses below the page get the site's 404 page. Templates are looked up by instance key first, then by module type, so lists/list.gohtml serves every list that has no design of its own; .Key tells the lists apart inside a shared template. Generate a starting point with every field of the data:

task templates -- faq

A minimal FAQ:

{{/* @dot github.com/donseba/go-webthings/modules/lists.ListView */}}
<h1>{{.Title}}</h1>

{{range .Entries}}
    <article id="{{.ID}}">
        {{with .ImageURL}}<img src="{{image . "120x120 crop"}}" width="120" height="120" alt="">{{end}}
        <h2>{{.Title}}</h2>
        {{.Body}}
        {{with .Link}}<a href="{{.}}">Read more</a>{{end}}
    </article>
{{end}}

Title is the published title of the page the list is placed on. Body is the rendered Markdown, with media from the library already resolved. Link is a template.URL, so tel: links work in href. ImageURL is the image as pages show it, at most 1600 pixels wide, with ImageSrcset, ImageWidth and ImageHeight for an img tag of that size and ImageOriginalURL for the uploaded file; given ImageURL, image makes the square copy above from the uploaded image (see Media and images). Keep id="{{.ID}}" on each entry, as the generated starting point does: site search results link to #<id> on the list's page, and the ID never changes.

An entry appears in each language it has a title for.

Show a list anywhere

The entries template function returns the published entries of a list in the current language, in their order. The list does not need to be placed on a page for this.

<section>
    <h2>Frequently asked</h2>
    {{range entries "faq"}}
        <h3>{{.Title}}</h3>
        {{.Body}}
    {{end}}
    <a href="{{moduleURL "faq"}}">All questions</a>
</section>

Content API

While the site's content API is on (Config.API), /api/v1/<key>/entries serves the published entries as JSON, in the language of ?locale=, or the site's default language without it. With the list faq:

Endpoint Route name Answers with
/api/v1/faq/entries api.faq.entries The published entries, in the editors' order, as a list
/api/v1/faq/entries/{id} api.faq.entry One entry

Entries have no page of their own, so the list gives each entry whole: its id, title and body_html, its image as an image object, null without one, its link, url, api_url and updated_at. link is the entry's link as a visitor follows it from the list's page: a path of the site made absolute, an anchor on the list's page, and other addresses, such as mailto: links, as they are; null without a link. url is the entry on the list's page, such as https://example.com/en/faq#<id>, and null while the list is on no page.

/entries/{id} sends a Last-Modified: the latest change of the entry, of the page the instance is on and of the entry's media, its image and those in its text. An entry with an image that was deleted gets none, and so does every entry while the instance is on no page; the lists get none either; see Caching.

Editing

Each list has its own section in the admin (/admin/faq; /admin is the default admin path) with its entries.

  • An entry has an optional image from the media library and an optional link, shared by all languages, and per language a title and Markdown text.
  • A link is a path on the site (/contact), an anchor (#opening-hours), or a web, mailto: or tel: address.
  • Entries have no drafts or revisions: saving changes the entry at once, also when it is published. New entries start unpublished at the end of the list. Publish and Unpublish show or hide an entry; publishing needs a title in the site's default language.
  • The move buttons on the overview set the order of the entries on the site.
  • Move to trash moves an entry to the trash, where it can be restored or deleted for good.

Editors' rights are set per list in the groups' permission matrix.

Published entries appear in the site search by title and text once the list is placed on a page.

Links to addresses on the site in the text and links of published entries count for Links to a page: the editor of a page lists the entries that link to it, and Pages › Broken links lists their links that lead nowhere.

From Go

lists.Of(app, "faq") returns the list once the app has started:

faq := lists.Of(app, "faq")

entries, err := faq.Entries(ctx, "en")

Store() gives write access for imports and seed data, through the same rules the admin uses:

store := faq.Store()

entry, err := store.Save(ctx, content.Draft[lists.Fields, lists.Text]{
    Fields: lists.Fields{
        Link: "/contact",
    },
    Translations: []content.Translation[lists.Text]{
        {
            Locale: "en",
            Text: lists.Text{
                Title: "Do you deliver?",
                Body:  "Within the city, yes. [Ask us](/contact) about other places.",
            },
        },
    },
})
if err != nil {
    return err
}

err = store.Publish(ctx, entry.ID, "", "en")
  • People suits a team, with a page and contact details per person.
  • Testimonials keeps quotes from customers.
  • Gallery shows albums of images.

Documentation

Overview

Package lists keeps short ordered lists such as FAQs, services, features or steps. One module instance is one list (for team members with photos, roles and pages of their own, use the people module):

app.Use(lists.Module("faq", "FAQ"), lists.Module("services", "Services"))

Each entry has a title and a Markdown body per language, and optionally an image and a link. Entries are published one by one and keep the order set in the admin (the move buttons on the list's overview).

When the list is mounted on a page, the website renders lists/list.gohtml with a ListView. To show a list elsewhere, for example the FAQ on the home page, use the entries template function ({{range entries "faq"}}) or Of(app, "faq").Entries(ctx, locale) from Go; both return the same entries.

Every Entry has an ID. The site search links to an entry on the list's page at #<ID>, so give each entry id="{{.ID}}" as the generated starting point does.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Module

func Module(key, title string) webthings.Module

Module adds a list. key is used in URLs of the admin and must be unique on the site; title is shown in the admin and the page editor.

Types

type APIEntry

type APIEntry struct {
	ID       string     `json:"id"`
	Title    string     `json:"title"`
	BodyHTML string     `json:"body_html"`
	Image    *api.Image `json:"image"`
	// Link is the entry's link as a visitor follows it from the list's
	// page: a path of the site made absolute, an anchor on the list's
	// page, other addresses (https:, mailto:, tel:) as they are; null
	// without a link.
	Link *string `json:"link"`
	// URL is the entry on the list's page (#<id>), null while the list is
	// on no page; APIURL the entry in the API.
	URL       *string   `json:"url"`
	APIURL    string    `json:"api_url"`
	UpdatedAt time.Time `json:"updated_at"`
}

APIEntry is a published entry in the content API (see package api), at /api/v1/<key>/entries/{id} and in the list of /api/v1/<key>/entries. Entries have no page of their own: the list's page shows them whole, so the list has their text too.

type Entry

type Entry struct {
	ID    string
	Title string
	Body  template.HTML
	// ImageURL is the entry's image as pages show it, at most media.ImageWidth
	// wide (see frontend.Frontend.Image); ImageSrcset lists smaller
	// copies for a srcset attribute, ImageWidth and ImageHeight are the
	// size of ImageURL and ImageOriginalURL is the uploaded file, for
	// downloads. All are empty when the entry has no image.
	ImageURL         string
	ImageSrcset      string
	ImageOriginalURL string
	ImageWidth       int
	ImageHeight      int
	// Link is empty when not set. It is a template.URL because the store only
	// accepts paths, anchors and http(s), mailto: and tel: addresses, so
	// templates can use it in href without html/template replacing tel:
	// links with #ZgotmplZ.
	Link template.URL
}

Entry is a published entry as templates see it.

type Fields

type Fields struct {
	Image string `db:"image_id,null"`
	Link  string `db:"link_url"`
}

Fields are the language-independent parts of an entry.

type Item

type Item = content.Item[Fields, Text]

Item is a list entry with its translations, as the store keeps it.

type ListView

type ListView struct {
	// Key is the list's instance key, for example "faq", so one template can
	// show several lists differently.
	Key string
	// Title is the title of the page the list is mounted on.
	Title   string
	Entries []Entry
}

ListView is the data of lists/list.gohtml.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is one list, for the website's own code.

func Of

func Of(app *webthings.App, key string) *Service

Of returns the list with key from a started app, or nil.

func (*Service) Entries

func (s *Service) Entries(ctx context.Context, locale string) ([]Entry, error)

Entries returns the published entries in locale, in their order.

func (*Service) Store

func (s *Service) Store() content.Store[Fields, Text]

Store gives write access to this instance's entries, for imports and seeds. Admin edits go through the same store.

type Text

type Text struct {
	Title string `db:"title"`
	Body  string `db:"body"`
}

Text is an entry in one language. Body is Markdown.

Jump to

Keyboard shortcuts

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