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}}
+
+{{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"
)