tasks: add tasks.feeds support

Every task can now have a [[tasks.feeds]] entry to generate one or more feeds.
It must have the following arguments:

[[tasks.feeds]]
input_dir = ""
title = "Example feed"
template = "atom.xml"
output = "atom.xml"
This commit is contained in:
oliverpool 2021-09-02 11:59:08 +02:00 committed by adnano
parent d5f85931ea
commit 2ef8f77d24
6 changed files with 126 additions and 18 deletions

View file

@ -1,9 +1,6 @@
title = "Example website"
urls = ["gemini://example.com"]
[feeds]
"/" = "Example feed"
[permalinks]
"/" = "/{{ .Date.Format `2006/01/02` }}/{{ path.Base .Permalink }}/"
@ -13,3 +10,9 @@ output = ".gmi"
template = ".gmi"
static_dir = "static"
output_dir = "public"
[[tasks.feeds]]
input_dir = ""
title = "Example feed"
template = "atom.xml"
output = "atom.xml"

71
dir.go
View file

@ -18,11 +18,13 @@ import (
// Dir represents a directory.
type Dir struct {
Permalink string
Pages []*Page
Dirs []*Dir
index *Page // The index page.
feed []byte // Atom feed.
Permalink string
Pages []*Page
Dirs []*Dir
index *Page // The index page.
feed []byte // Atom feed (deprecated)
extraFiles map[string][]byte // Atom/RSS feeds
path string // relative to the content dir
}
// Page represents a page.
@ -57,7 +59,7 @@ func (d *Dir) _read(srcDir, path string, task *Task, cfg *Site) error {
continue
}
// Gather directory data
dir := &Dir{Permalink: "/" + path + "/"}
dir := &Dir{Permalink: "/" + path + "/", path: path}
if err := dir._read(srcDir, path, task, cfg); err != nil {
return err
}
@ -151,6 +153,21 @@ func (d *Dir) _read(srcDir, path string, task *Task, cfg *Site) error {
// process processes the directory's contents.
func (d *Dir) process(cfg *Site, task *Task) error {
// build feeds
// this must happen before the page processing
// to have an unprocessed Page.Content
// 192: d.Pages[i].Content = b.String()
for _, feed := range task.Feeds {
if d.path != feed.InputDir {
continue
}
b, err := d.buildFeed(cfg, feed)
if err != nil {
return err
}
d.addExtraFile(feed.Output, b)
}
if task.TemplateExt != "" {
// Create index
if d.index != nil {
@ -214,6 +231,39 @@ func (d *Dir) process(cfg *Site, task *Task) error {
return nil
}
// buildFeed build the feed of the directory
func (d Dir) buildFeed(cfg *Site, feed Feed) ([]byte, error) {
tmpl, ok := cfg.templates.FindTemplate(d.Permalink, feed.Template)
if !ok {
return nil, fmt.Errorf("missing feed template %q to generate %q", feed.Template, feed.Title)
}
var b bytes.Buffer
data := struct {
Title string // Feed title.
Permalink string // Feed permalink.
Updated time.Time // Last updated time.
Pages []*Page // Feed pages.
}{
Title: feed.Title,
Permalink: d.Permalink,
Updated: time.Now(),
Pages: d.Pages,
}
if err := tmpl.Execute(&b, data); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func (d *Dir) addExtraFile(name string, content []byte) {
if d.extraFiles == nil {
d.extraFiles = map[string][]byte{name: content}
} else {
d.extraFiles[name] = content
}
}
// write writes the directory's contents to the provided destination path.
func (d *Dir) write(dstDir string, task *Task) error {
dirPath := pathpkg.Join(dstDir, d.Permalink)
@ -255,6 +305,15 @@ func (d *Dir) write(dstDir string, task *Task) error {
}
}
// Write feeds
for name, content := range d.extraFiles {
dstPath := pathpkg.Join(dstDir, name)
os.MkdirAll(dirPath, 0755)
if err := os.WriteFile(dstPath, content, 0644); err != nil {
return err
}
}
// Write subdirectories
for _, dir := range d.Dirs {
dir.write(dstDir, task)

View file

@ -187,20 +187,31 @@ site URLs may be specified for sites that are available at multiple locations.
## FEEDS
Feeds can be specified in the [feeds] table of the configuration file. Keys
denote the path to the feed directory and values denote the title of the feed.
Feeds are written to the output directory plus the feed directory plus
"atom.xml".
Feeds can be specified in [[tasks.feeds]] for every task needing them.
Example feed configuration:
```
# This will generate a feed which will be written to public/blog/atom.xml
[feeds]
"/blog/" = "My blog"
[[tasks.feeds]]
input_dir = "blog"
title = "My Blog"
template = "atom.xml"
output = "blog/atom.xml"
```
*input_dir*
the content folder for which the feed will be generated
*title*
the title of the feed, accessible via {{ .Title }} in the template
*template*
the template to use for the feed
*output*
the absolute path for the rendered feed
## PERMALINKS
Permalinks can be used to rewrite page paths. Permalinks are specified in the

View file

@ -71,7 +71,7 @@ func (site *Site) run() error {
func (s *Site) runTask(task *Task) error {
// Read content
s.root = &Dir{Permalink: "/"}
s.root = &Dir{Permalink: "/", path: ""}
if err := s.root.read("content", task, s); err != nil {
return err
}

35
site.go
View file

@ -2,7 +2,10 @@ package main
import (
"fmt"
"log"
"os"
"path"
"strings"
"text/template"
"github.com/pelletier/go-toml"
@ -31,6 +34,14 @@ type Task struct {
StaticDir string `toml:"static_dir"` // static file directory
OutputDir string `toml:"output_dir"` // output directory
UglyURLs bool `toml:"ugly_urls"` // whether to use ugly URLs
Feeds []Feed `toml:"feeds"`
}
type Feed struct {
InputDir string `toml:"input_dir"`
Title string `toml:"title"`
Template string `toml:"template"`
Output string `toml:"output"`
}
func (t *Task) Match(ext string) bool {
@ -80,6 +91,30 @@ func LoadSite(config string) (*Site, error) {
return nil, err
}
// deprecate [feeds]
if len(site.Feeds) > 0 {
log.Println("The [feeds] configuration is deprecated")
for _, task := range site.Tasks {
if len(task.Feeds) > 0 {
log.Println("and can't be used along [[tasks.feeds]]")
return nil, fmt.Errorf("please remove (or rename) the [feeds] category")
}
}
log.Println("Please replace it with [[tasks.feeds]] in every task needing feeds:")
for permalink, title := range site.Feeds {
dir := strings.Trim(permalink, "/")
output := path.Join(dir, "atom.xml")
fmt.Fprintf(log.Writer(), `[[tasks.feeds]]
input_dir = %q
title = %q
template = "atom.xml"
output = %q
`, dir, title, output)
}
log.Println("You will also need to change .Entries to .Pages in \"atom.xml\"")
}
return site, nil
}

View file

@ -4,7 +4,7 @@
<title>{{ .Title }}</title>
<updated>{{ .Updated.Format "2006-01-02T15:04:05Z07:00" }}</updated>
<link href="{{ index site.URLs 0 | safeURL }}{{ .Permalink }}" rel="alternate"/>
{{ range .Entries }}<entry>
{{ range .Pages }}<entry>
<id>{{ index site.URLs 0 }}{{ .Permalink }}</id>
<title>{{ .Title }}</title>
<updated>{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}</updated>