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 +- diff --git a/README.texi b/README.texi index aa5ddbf4bc49620986c78c65887acf1e31e56bce8f1ef0065a5aa0d40252cdb0..15eb2b06bcabd5acf964ba514843a6b64ea20cf08e24d72f19f5ff5a0ae1742a 100644 --- a/README.texi +++ b/README.texi @@ -30,6 +30,7 @@ @url{https://en.wikipedia.org/wiki/Inetd, inetd}/@url{http://cr.yp.to/ucspi-tcp.html, UCSPI-TCP} 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 @@ -114,6 +115,19 @@ @example $ 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 @@ # If that ref is set, then topics will be loaded from it 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 525138d58133059f0916d8882903789a7f9a2d9b010e9319a999ed7a6a505fc1..350651464b42a0aa1bddfeeb9cf67f57feea32044bd9fc1c2f0dbd10bfd42230 100644 --- a/cmd/sgblog/http-entry.tmpl +++ b/cmd/sgblog/http-entry.tmpl @@ -36,6 +36,10 @@ {{range .NoteLines}}{{. | lineURLize $.Cfg.URLPrefix}} {{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 9b1640ebaa90c9c7f4c64728c3ec2d51974c6df59d5c6b3e6099090684f95a3e..17da2643d46e001e624e36657ca91505f026291a8e3e59f9117eb23630e7eb52 100644 --- a/cmd/sgblog/http.go +++ b/cmd/sgblog/http.go @@ -484,13 +484,22 @@ for _, l := range lines[2:] { 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 @@ Lines []string NoteLines []string Comments []CommentEntry Topics []string + Imgs []Img }{ T: localizer, Version: sgblog.Version, @@ -756,6 +766,7 @@ Lines: lines[2:], 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 0000000000000000000000000000000000000000..78b93db9bdc7cb9d2de17edc852166cda59cd0d48a3233ca87e94ffbbb12a454 --- /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 a38070dcbf921f55515324a0c1c83c128fc4bcf86654cd6fe590bc2599a2be25..38f4ab78d474e5c2bc338859af32cbecd0b2f2fbad2b0ab1313332f2d7c5d7ef 100644 --- a/cmd/sgblog/main.go +++ b/cmd/sgblog/main.go @@ -81,6 +81,9 @@ TopicsNotesRef string 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 0000000000000000000000000000000000000000..e7c7c6efcc5e2c88211d21ec14e1002047eb4fb7c574457d6e8ca39d0512f11c --- /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 3d7367f5b2a0aef75ba97a03e1f6d926de08b03ab16b5f421f740d982eac14c3..0ba466da0458735cef9f3b8699db33c103a14d1445d1ef3bc1b7badc94a892c6 100644 --- a/common.go +++ b/common.go @@ -15,7 +15,7 @@ "go.cypherpunks.ru/recfile" ) const ( - Version = "0.27.3" + Version = "0.28.0" WhenFmt = "2006-01-02 15:04:05Z07:00" )