diff --git a/config.toml b/config.toml index 7fd55af..9aeb85d 100644 --- a/config.toml +++ b/config.toml @@ -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" diff --git a/dir.go b/dir.go index e0a2505..24f63bb 100644 --- a/dir.go +++ b/dir.go @@ -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) diff --git a/docs/kiln.1.scd b/docs/kiln.1.scd index dbb4c8a..1923995 100644 --- a/docs/kiln.1.scd +++ b/docs/kiln.1.scd @@ -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 diff --git a/main.go b/main.go index b081099..30e5c26 100644 --- a/main.go +++ b/main.go @@ -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 } diff --git a/site.go b/site.go index 925ece7..189a9f7 100644 --- a/site.go +++ b/site.go @@ -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 } diff --git a/templates/_default/atom.xml b/templates/_default/atom.xml index 89cdd2a..553da4b 100644 --- a/templates/_default/atom.xml +++ b/templates/_default/atom.xml @@ -4,7 +4,7 @@