/*
-SGBlog -- Git-backed CGI/inetd blogging/phlogging engine
-Copyright (C) 2020 Sergey Matveev <stargrave@stargrave.org>
+SGBlog -- Git-backed CGI/UCSPI blogging/phlogging/gemlogging engine
+Copyright (C) 2020-2023 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
import (
"bufio"
- "encoding/json"
+ _ "embed"
"errors"
"fmt"
"io"
- "io/ioutil"
"log"
+ "net/url"
"os"
"strconv"
"strings"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
- "github.com/hjson/hjson-go"
+ "github.com/vorlif/spreak"
"go.stargrave.org/sgblog"
)
-const (
- TmplGopherMenu = `{{$CR := printf "\r"}}{{$CRLF := printf "\r\n" -}}
-{{- define "domainPort" }} {{.GopherDomain}} 70{{end}}{{$Cfg := .Cfg -}}
-i{{.Cfg.Title}} {{if .Topic}}(topic: {{.Topic}}) {{end}}({{.Offset}}-{{.OffsetNext}}) err{{template "domainPort" .Cfg}}{{$CRLF -}}
-{{- if .Cfg.AboutURL}}hAbout URL:{{.Cfg.AboutURL}}{{template "domainPort" .Cfg}}{{$CRLF}}{{end -}}
-{{- if .Offset}}1Prev {{if .Topic}}{{.Topic}}/{{end}}offset/{{.OffsetPrev}}{{template "domainPort" .Cfg}}{{$CRLF}}{{end -}}
-{{- if not .LogEnded}}1Next {{if .Topic}}{{.Topic}}/{{end}}offset/{{.OffsetNext}}{{template "domainPort" .Cfg}}{{$CRLF}}{{end -}}
-{{- $datePrev := "0001-01-01" -}}
-{{- range .Entries -}}
-{{- $dateCur := .Commit.Author.When.Format "2006-01-02" -}}
-{{- if ne $dateCur $datePrev}}{{$datePrev = $dateCur}}
-i{{$dateCur}} err{{template "domainPort" $Cfg}}{{$CR}}{{end}}
-0[{{.Commit.Author.When.Format "15:04"}}] {{.Title}} ({{.LinesNum}}L){{with .CommentsNum}} ({{.}}C){{end}}{{if .Topics}}{{range .Topics}} {{.}}{{end}}{{end}} /{{.Commit.Hash.String}}{{template "domainPort" $Cfg}}{{$CR}}{{end}}
-{{range .Topics}}
-1Topic: {{.}} {{.}}/offset/0{{template "domainPort" $Cfg}}{{$CR}}{{end}}
-iGenerated by: SGBlog {{.Version}} err{{template "domainPort" .Cfg}}{{$CR}}
-.{{$CRLF}}`
- TmplGopherEntry = `What: {{.Commit.Hash.String}}
-When: {{.When}}
-------------------------------------------------------------------------
-{{if .Topics}}Topics:{{range .Topics}} {{.}}{{end}}{{end}}
-------------------------------------------------------------------------
-{{.Commit.Message -}}
-{{- if .Note}}
-------------------------------------------------------------------------
-Note:
-{{.Note}}{{end -}}
-{{- if .Cfg.CommentsEmail}}
-------------------------------------------------------------------------
-leave comment: mailto:{{.Cfg.CommentsEmail}}?subject={{.Commit.Hash.String}}
-{{end}}{{range $idx, $comment := .Comments}}
-------------------------------------------------------------------------
-comment {{$idx}}:
-{{$comment}}
-{{end}}
-------------------------------------------------------------------------
-Generated by: SGBlog {{.Version}}
-`
+var (
+ //go:embed gopher-menu.tmpl
+ TmplGopherMenuRaw string
+ //go:embed gopher-entry.tmpl
+ TmplGopherEntryRaw string
+
+ TmplGopherMenu = template.Must(template.New("gopher-menu").Parse(TmplGopherMenuRaw))
+ TmplGopherEntry = template.Must(template.New("gopher-entry").Parse(TmplGopherEntryRaw))
)
type TableMenuEntry struct {
Topics []string
}
-func serveGopher() {
- cfgPath := os.Args[2]
- cfgRaw, err := ioutil.ReadFile(cfgPath)
- if err != nil {
- log.Fatalln(err)
- }
- var cfgGeneral map[string]interface{}
- if err = hjson.Unmarshal(cfgRaw, &cfgGeneral); err != nil {
- log.Fatalln(err)
- }
- cfgRaw, err = json.Marshal(cfgGeneral)
+func serveGopher(cfgPath string) {
+ cfg, err := readCfg(cfgPath)
if err != nil {
log.Fatalln(err)
}
- var cfg *Cfg
- if err = json.Unmarshal(cfgRaw, &cfg); err != nil {
- log.Fatalln(err)
- }
if cfg.GopherDomain == "" {
log.Fatalln("GopherDomain is not configured")
}
+ initLocalizer(cfg.Lang)
headHash, err := initRepo(cfg)
if err != nil {
selectorParts := strings.Split(selector, "/")
if strings.HasPrefix(selector, "URL:") {
selector = selector[len("URL:"):]
- fmt.Printf(`<html>
+ fmt.Printf(`<!DOCTYPE html>
+<html>
<head>
<meta http-equiv="Refresh" content="1; url=%s" />
<title>Redirect to non-gopher URL</title>
if err != nil {
log.Fatalln(err)
}
- tmpl := template.Must(template.New("entry").Parse(TmplGopherEntry))
- err = tmpl.Execute(os.Stdout, struct {
- Commit *object.Commit
- When string
- Cfg *Cfg
- Note string
- Comments []string
- Topics []string
- Version string
+ err = TmplGopherEntry.Execute(os.Stdout, struct {
+ T *spreak.Localizer
+ Commit *object.Commit
+ When string
+ Cfg *Cfg
+ Note string
+ Comments []string
+ Topics []string
+ Version string
+ TitleEscaped string
}{
+ T: localizer,
Commit: commit,
When: commit.Author.When.Format(sgblog.WhenFmt),
Cfg: cfg,
- Note: string(getNote(notesTree, commit.Hash)),
- Comments: parseComments(getNote(commentsTree, commit.Hash)),
- Topics: parseTopics(getNote(topicsTree, commit.Hash)),
+ Note: string(sgblog.GetNote(repo, notesTree, commit.Hash)),
+ Comments: sgblog.ParseComments(sgblog.GetNote(repo, commentsTree, commit.Hash)),
+ Topics: sgblog.ParseTopics(sgblog.GetNote(repo, topicsTree, commit.Hash)),
Version: sgblog.Version,
+ TitleEscaped: url.PathEscape(fmt.Sprintf(
+ "Re: %s (%s)", msgSplit(commit.Message)[0], commit.Hash,
+ )),
})
if err != nil {
log.Fatalln(err)
}
- } else if selectorParts[len(selectorParts)-2] == "offset" {
+ } else if len(selectorParts) > 1 &&
+ selectorParts[len(selectorParts)-2] == "offset" {
offset, err := strconv.Atoi(selectorParts[len(selectorParts)-1])
if err != nil {
log.Fatalln(err)
Commit: commit,
Title: lines[0],
LinesNum: len(lines) - 2,
- CommentsNum: len(parseComments(getNote(commentsTree, commit.Hash))),
- Topics: parseTopics(getNote(topicsTree, commit.Hash)),
+ CommentsNum: len(sgblog.ParseComments(sgblog.GetNote(repo, commentsTree, commit.Hash))),
+ Topics: sgblog.ParseTopics(sgblog.GetNote(repo, topicsTree, commit.Hash)),
})
}
- tmpl := template.Must(template.New("menu").Parse(TmplGopherMenu))
offsetPrev := offset - PageEntries
if offsetPrev < 0 {
offsetPrev = 0
}
- err = tmpl.Execute(os.Stdout, struct {
+ err = TmplGopherMenu.Execute(os.Stdout, struct {
+ T *spreak.Localizer
Cfg *Cfg
Topic string
Offset int
Topics []string
Version string
}{
+ T: localizer,
Cfg: cfg,
Topic: topic,
Offset: offset,