From 392291e2888eab29f07cff1cfae4e6d69aef3ccc Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Fri, 28 Jul 2023 21:26:56 +0300 Subject: [PATCH] Images support --- README.texi | 18 +++++++++++++ cmd/sgblog/http-entry.tmpl | 4 +++ cmd/sgblog/http.go | 23 +++++++++++----- cmd/sgblog/img.go | 55 ++++++++++++++++++++++++++++++++++++++ cmd/sgblog/main.go | 3 +++ comment.rec | 4 +++ common.go | 2 +- 7 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 cmd/sgblog/img.go create mode 100644 comment.rec diff --git a/README.texi b/README.texi index 18b8335..ffe2704 100644 --- a/README.texi +++ b/README.texi @@ -30,6 +30,7 @@ Its main competitive features: interface for working as @url{https://en.wikipedia.org/wiki/Gopher_(protocol), Gopher} server @item Topics (tags/categories) support +@item Images linking support @item Supports on the fly generation of @url{https://en.wikipedia.org/wiki/Atom_(feed), Atom} feeds for posts, comments and per-post comments @@ -115,6 +116,19 @@ To reset incorrectly added topic: $ git update-ref refs/notes/topics refs/notes/topics^ @end example +@node Images +@unnumbered Images + +You can link any number of image files with your post. You need to put +them in a directory (@code{ImgPath} configuration file's option) which +path is equal to your post's hash, with two subdirectory levels. For +example directory for post @code{3e12180dd2b6fb3b44c77c365d355d5a3796a43f} +will be @code{ImgPath/3e/12/180dd2b6fb3b44c77c365d355d5a3796a43f}. +Filenames are lexically sorted. Filename without an extension will be +image's alternative text. Currently only @file{.jxl} and @file{.webp} +are recognized. @code{ImgDomain} configuration file's option will be +used to construct URL to @code{//ImgDomain/3e/12/180dd2b6fb3b44c77c365d355d5a3796a43f}. + @node Installation @unnumbered Installation @@ -258,6 +272,10 @@ optional fields: TopicsNotesRef: refs/notes/topics # Optional file for topics state caching TopicsCachePath: /path/to/sgblog-topics-cache.gob + + # If set, then images are searched in XX/YY/ZZZ...ZZZ directory + ImgPath: /path/to/directory/with/images + ImgDomain: img.blog.example.com @} @end example diff --git a/cmd/sgblog/http-entry.tmpl b/cmd/sgblog/http-entry.tmpl index 240617a..efff427 100644 --- a/cmd/sgblog/http-entry.tmpl +++ b/cmd/sgblog/http-entry.tmpl @@ -36,6 +36,10 @@ {{end}}
{{end}} +
    {{range $idx, $img := .Imgs}} +
  1. {{$img.Alt}}
  2. +{{end}}
+ {{if .Cfg.CommentsEmail}}[{{$.T.Get "leave a comment"}}]{{end}}
{{range $idx, $comment := .Comments}} diff --git a/cmd/sgblog/http.go b/cmd/sgblog/http.go index fd5f7b4..50bb1a8 100644 --- a/cmd/sgblog/http.go +++ b/cmd/sgblog/http.go @@ -484,13 +484,22 @@ func serveHTTP() { htmlized = append(htmlized, lineURLize(cfg.AtomBaseURL+cfg.URLPrefix, l)) } htmlized = append(htmlized, "") + links := []atom.Link{{ + Rel: "alternate", + Href: cfg.AtomBaseURL + cfg.URLPrefix + "/" + commit.Hash.String(), + }} + for _, img := range listImgs(cfg, commit.Hash) { + links = append(links, atom.Link{ + Rel: "enclosure", + Href: "http://" + cfg.ImgDomain + "/" + img.Path, + Type: img.Typ, + Length: uint(img.Size), + }) + } feed.Entry = append(feed.Entry, &atom.Entry{ - Title: lines[0], - ID: "urn:uuid:" + bytes2uuid(commit.Hash[:]), - Link: []atom.Link{{ - Rel: "alternate", - Href: cfg.AtomBaseURL + cfg.URLPrefix + "/" + commit.Hash.String(), - }}, + Title: lines[0], + ID: "urn:uuid:" + bytes2uuid(commit.Hash[:]), + Link: links, Published: atom.Time(commit.Author.When), Updated: atom.Time(commit.Author.When), Summary: &atom.Text{Type: "text", Body: lines[0]}, @@ -742,6 +751,7 @@ func serveHTTP() { NoteLines []string Comments []CommentEntry Topics []string + Imgs []Img }{ T: localizer, Version: sgblog.Version, @@ -756,6 +766,7 @@ func serveHTTP() { NoteLines: notesLines, Comments: comments, Topics: sgblog.ParseTopics(topicsRaw), + Imgs: listImgs(cfg, commit.Hash), }) if err != nil { makeErr(err, http.StatusInternalServerError) diff --git a/cmd/sgblog/img.go b/cmd/sgblog/img.go new file mode 100644 index 0000000..7f97d98 --- /dev/null +++ b/cmd/sgblog/img.go @@ -0,0 +1,55 @@ +package main + +import ( + "log" + "os" + "path" + "strings" + + "github.com/go-git/go-git/v5/plumbing" +) + +type Img struct { + Path string + Alt string + Size int64 + Typ string +} + +var ImgTypes = map[string]string{ + ".jxl": "image/jxl", + ".webp": "image/webp", +} + +func listImgs(cfg *Cfg, what plumbing.Hash) (out []Img) { + if cfg.ImgPath == "" { + return nil + } + w := what.String() + sub := path.Join(w[:2], w[2:4], w[4:]) + ents, err := os.ReadDir(path.Join(cfg.ImgPath, sub)) + if err != nil { + return nil + } + out = make([]Img, 0, len(ents)) + for _, ent := range ents { + name := ent.Name() + ext := path.Ext(name) + info, err := ent.Info() + if err != nil { + log.Println("imgs: Info():", err) + continue + } + typ := ImgTypes[ext] + if typ == "" { + typ = "application/octet-stream" + } + out = append(out, Img{ + Path: path.Join(sub, ent.Name()), + Alt: strings.TrimSuffix(name, ext), + Size: info.Size(), + Typ: typ, + }) + } + return +} diff --git a/cmd/sgblog/main.go b/cmd/sgblog/main.go index 6cd1efd..a761302 100644 --- a/cmd/sgblog/main.go +++ b/cmd/sgblog/main.go @@ -81,6 +81,9 @@ type Cfg struct { TopicsCachePath string GopherDomain string + + ImgPath string + ImgDomain string } func msgSplit(msg string) []string { diff --git a/comment.rec b/comment.rec new file mode 100644 index 0000000..b5337a0 --- /dev/null +++ b/comment.rec @@ -0,0 +1,4 @@ +%rec: Comment +%doc: SGBlog's comment +%mandatory: From Date Body +%type: Date date diff --git a/common.go b/common.go index 9c4843b..e3c092d 100644 --- a/common.go +++ b/common.go @@ -15,7 +15,7 @@ import ( ) const ( - Version = "0.27.3" + Version = "0.28.0" WhenFmt = "2006-01-02 15:04:05Z07:00" ) -- 2.44.0