import (
"bufio"
- "bytes"
"encoding/json"
"errors"
"fmt"
"os"
"strconv"
"strings"
- "time"
+ "text/template"
"github.com/hjson/hjson-go"
"go.stargrave.org/sgblog"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/object"
)
-const CRLF = "\r\n"
-
-var DashLine = strings.Repeat("-", 72)
+const (
+ TmplGopherMenu = `{{$Cfg := .Cfg}}{{$CR := .CR}}i{{.Cfg.Title}} ({{.Offset}}-{{.OffsetNext}}) err {{.Cfg.GopherDomain}} 70{{.CR}}
+{{if .Cfg.AboutURL}}hAbout URL:{{.Cfg.AboutURL}} {{.Cfg.GopherDomain}} 70{{.CR}}{{end}}
+{{if .Offset}}1Prev offset/{{.OffsetPrev}} {{.Cfg.GopherDomain}} 70{{.CR}}{{end}}
+{{if not .LogEnded}}1Next offset/{{.OffsetNext}} {{.Cfg.GopherDomain}} 70{{.CR}}{{end -}}
+{{- $yearPrev := 0}}{{$monthPrev := 0}}{{$dayPrev := 0 -}}
+{{- range .Entries }}{{$yearCur := .Commit.Author.When.Year}}{{$monthCur := .Commit.Author.When.Month}}{{$dayCur := .Commit.Author.When.Day -}}
+{{- if or (ne $dayCur $dayPrev) (ne $monthCur $monthPrev) (ne $yearCur $yearPrev)}}{{$dayPrev = $dayCur}}{{$monthPrev = $monthCur}}{{$yearPrev = $yearCur}}
+i{{$yearCur | printf "%04d"}}-{{$monthCur | printf "%02d"}}-{{$dayCur | printf "%02d"}} err {{$Cfg.GopherDomain}} 70{{$CR}}{{end}}
+0[{{.Commit.Author.When.Hour | printf "%02d"}}:{{.Commit.Author.When.Minute | printf "%02d"}}] {{.Title}} ({{.LinesNum}}L){{if .CommentsNum}} ({{.CommentsNum}}C){{end}} /{{.Commit.Hash.String}} {{$Cfg.GopherDomain}} 70{{$CR}}{{end}}
+iGenerated by: SGBlog {{.Version}} err {{.Cfg.GopherDomain}} 70{{.CR}}
+.{{.CR}}
+`
+ TmplGopherEntry = `What: {{.Commit.Hash.String}}
+When: {{.When}}
+------------------------------------------------------------------------
+{{.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}}
+`
+)
-func makeI(cfg *Cfg, value string) string {
- return strings.Join([]string{"i" + value, "err", cfg.GopherDomain, "70", CRLF}, "\t")
+type TableMenuEntry struct {
+ Commit *object.Commit
+ Title string
+ LinesNum int
+ CommentsNum int
}
func serveGopher() {
if err != nil {
log.Fatalln(err)
}
- commitN := 0
for i := 0; i < offset; i++ {
if _, err = repoLog.Next(); err != nil {
break
}
- commitN++
}
-
logEnded := false
- var menu bytes.Buffer
- var yearPrev int
- var monthPrev time.Month
- var dayPrev int
+ entries := make([]TableMenuEntry, 0, PageEntries)
for i := 0; i < PageEntries; i++ {
commit, err := repoLog.Next()
if err != nil {
logEnded = true
break
}
- yearCur, monthCur, dayCur := commit.Author.When.Date()
- if dayCur != dayPrev || monthCur != monthPrev || yearCur != yearPrev {
- menu.WriteString(makeI(cfg, fmt.Sprintf(
- "%04d-%02d-%02d", yearCur, monthCur, dayCur,
- )))
- yearPrev, monthPrev, dayPrev = yearCur, monthCur, dayCur
- }
- commitN++
lines := msgSplit(commit.Message)
- var commentsValue string
- if l := len(parseComments(getNote(commentsTree, commit.Hash))); l > 0 {
- commentsValue = fmt.Sprintf(" (%dC)", l)
- }
- menu.WriteString(fmt.Sprintf(
- "0[%02d:%02d] %s (%dL)%s\t/%s\t%s\t%d%s",
- commit.Author.When.Hour(),
- commit.Author.When.Minute(),
- lines[0],
- len(lines)-2,
- commentsValue,
- commit.Hash.String(),
- cfg.GopherDomain, 70, CRLF,
- ))
- }
-
- fmt.Print(makeI(cfg, fmt.Sprintf("%s (%d-%d)", cfg.Title, offset, offset+PageEntries)))
- if cfg.AboutURL != "" {
- fmt.Printf(
- "hAbout\tURL:%s\t%s\t%d%s",
- cfg.AboutURL,
- cfg.GopherDomain, 70, CRLF,
- )
+ entries = append(entries, TableMenuEntry{
+ Commit: commit,
+ Title: lines[0],
+ LinesNum: len(lines) - 2,
+ CommentsNum: len(parseComments(getNote(commentsTree, commit.Hash))),
+ })
}
- if offset > 0 {
- offsetPrev := offset - PageEntries
- if offsetPrev < 0 {
- offsetPrev = 0
- }
- fmt.Printf(
- "1Prev\toffset/%d\t%s\t%d%s",
- offsetPrev,
- cfg.GopherDomain, 70, CRLF,
- )
- }
- if !logEnded {
- fmt.Printf(
- "1Next\toffset/%d\t%s\t%d%s",
- offset+PageEntries,
- cfg.GopherDomain, 70, CRLF,
- )
+ tmpl := template.Must(template.New("menu").Parse(TmplGopherMenu))
+ err = tmpl.Execute(os.Stdout, struct {
+ Cfg *Cfg
+ Offset int
+ OffsetPrev int
+ OffsetNext int
+ LogEnded bool
+ Entries []TableMenuEntry
+ Version string
+ CR string
+ }{
+ Cfg: cfg,
+ Offset: offset,
+ OffsetPrev: offset - PageEntries,
+ OffsetNext: offset + PageEntries,
+ LogEnded: logEnded,
+ Entries: entries,
+ Version: sgblog.Version,
+ CR: "\r",
+ })
+ if err != nil {
+ log.Fatalln(err)
}
- fmt.Print(menu.String())
- fmt.Printf(makeI(cfg, "Generated by: SGBlog "+sgblog.Version))
- fmt.Print("." + CRLF)
} else if strings.HasPrefix(selector, "URL:") {
selector = selector[len("URL:"):]
fmt.Printf(`<html>
if err != nil {
log.Fatalln(err)
}
- fmt.Printf(
- "What: %s\nWhen: %s\n%s\n%s",
- commit.Hash.String(),
- commit.Author.When.Format(sgblog.WhenFmt),
- DashLine,
- commit.Message,
- )
- notesRaw := getNote(notesTree, commit.Hash)
- if len(notesRaw) > 0 {
- fmt.Printf("%s\nNote:\n%s\n", DashLine, string(notesRaw))
- }
- if cfg.CommentsEmail != "" {
- fmt.Printf(
- "%s\nleave comment: mailto:%s?subject=%s\n",
- DashLine,
- cfg.CommentsEmail,
- commit.Hash.String(),
- )
- }
- for i, comment := range parseComments(getNote(commentsTree, commit.Hash)) {
- fmt.Printf("%s\ncomment %d:\n%s\n", DashLine, i, comment)
+ 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
+ Version string
+ }{
+ Commit: commit,
+ When: commit.Author.When.Format(sgblog.WhenFmt),
+ Cfg: cfg,
+ Note: string(getNote(notesTree, commit.Hash)),
+ Comments: parseComments(getNote(commentsTree, commit.Hash)),
+ Version: sgblog.Version,
+ })
+ if err != nil {
+ log.Fatalln(err)
}
- fmt.Printf("%s\nGenerated by: SGBlog %s\n", DashLine, sgblog.Version)
} else {
log.Fatalln(errors.New("unknown selector"))
}
"os"
"strconv"
"strings"
- "time"
+ "text/template"
"github.com/hjson/hjson-go"
"go.stargrave.org/sgblog"
const (
AtomPostsFeed = "feed.atom"
AtomCommentsFeed = "comments.atom"
+ TmplHTMLIndex = `<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta name="generator" content="SGBlog {{.Version}}">
+ <title>{{.Cfg.Title}} ({{.Offset}}-{{.OffsetNext}})</title>
+ {{if .Cfg.CSS}}<link rel="stylesheet" type="text/css" href="{{.Cfg.CSS}}">{{end}}
+ {{if .Cfg.Webmaster}}<link rev="made" href="mailto:{{.Cfg.Webmaster}}">{{end -}}
+ {{- range .Cfg.GitURLs}}
+ <link rel="vcs-git" href="{{.}}" title="Git repository">{{end}}
+ <link rel="top" href="{{.Cfg.URLPrefix}}/" title="top">
+ <link rel="alternate" title="Posts feed" href="{{.Cfg.AtomBaseURL}}{{.Cfg.URLPrefix}}/{{.AtomPostsFeed}}" type="application/atom+xml">
+ {{if .CommentsEnabled}}<link rel="alternate" title="Comments feed" href="{{.Cfg.AtomBaseURL}}{{.Cfg.URLPrefix}}/{{.AtomCommentsFeed}}" type="application/atom+xml">{{end}}
+ {{if .Offset}}<link rel="prev" href="{{.Cfg.URLPrefix}}/{{if .OffsetPrev}}?offset={{.OffsetPrev}}{{end}}" title="prev">{{end}}
+ {{if not .LogEnded}}<link rel="next" href="{{.Cfg.URLPrefix}}/?offset={{.OffsetNext}}" title="next">{{end}}
+</head>
+<body>
+{{if .Cfg.AboutURL}}[<a href="{{.Cfg.AboutURL}}">about</a>]{{end}}
+{{block "links" .}}
+{{if .Offset}}[<a href="{{.Cfg.URLPrefix}}/{{if .OffsetPrev}}?offset={{.OffsetPrev}}{{end}}">prev</a>]{{end}}
+{{if not .LogEnded}}[<a href="{{.Cfg.URLPrefix}}/?offset={{.OffsetNext}}">next</a>]{{end}}
+{{end}}
+{{- $Cfg := .Cfg -}}
+{{- $yearPrev := 0 -}}
+{{- $monthPrev := 0 -}}
+{{- $dayPrev := 0}}
+<table border=1>
+<tr>
+ <th>N</th><th>When</th><th>Title</th>
+ <th size="5%"><a title="Lines">L</a></th>
+ <th size="5%"><a title="Comments">C</a></th>
+ <th>Linked to</th>
+</tr>{{range .Entries -}}
+{{- $yearCur := .Commit.Author.When.Year -}}
+{{- $monthCur := .Commit.Author.When.Month -}}
+{{- $dayCur := .Commit.Author.When.Day -}}
+{{- if or (ne $dayCur $dayPrev) (ne $monthCur $monthPrev) (ne $yearCur $yearPrev) -}}
+<tr><td colspan=6><center><tt>{{$yearCur | printf "%04d"}}-{{$monthCur | printf "%02d"}}-{{$dayCur | printf "%02d"}}</tt></center></td></tr>
+{{- $dayPrev = $dayCur}}{{$monthPrev = $monthCur}}{{$yearPrev = $yearCur -}}
+{{- end -}}
+<tr>
+ <td>{{.Num}}</td>
+ <td><tt>{{.Commit.Author.When.Hour | printf "%02d" -}}:{{- .Commit.Author.When.Minute | printf "%02d"}}</tt></td>
+ <td><a href="{{$Cfg.URLPrefix}}/{{.Commit.Hash.String}}">{{.Title}}</a></td>
+ <td>{{.LinesNum}}</td>
+ <td>{{if .CommentsNum}}{{.CommentsNum}}{{else}} {{end}}</td>
+ <td>{{range .DomainURLs}} {{.}} {{end}}</td>
+</tr>
+{{- end}}</table>
+{{template "links" .}}
+</body>
+</html>
+`
+ TmplHTMLEntry = `{{$Cfg := .Cfg}}<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta name="generator" content="SGBlog {{.Version}}">
+ <title>{{.Title}} ({{.When}})</title>
+ {{if .Cfg.CSS}}<link rel="stylesheet" type="text/css" href="{{.Cfg.CSS}}">{{end}}
+ {{if .Cfg.Webmaster}}<link rev="made" href="mailto:{{.Cfg.Webmaster}}">{{end -}}
+ {{- range .Cfg.GitURLs}}
+ <link rel="vcs-git" href="{{.}}" title="Git repository">{{end}}
+ <link rel="top" href="{{.Cfg.URLPrefix}}/" title="top">
+ <link rel="alternate" title="Comments feed" href="{{.AtomCommentsURL}}" type="application/atom+xml">
+ {{if .Parent}}<link rel="prev" href="{{.Cfg.URLPrefix}}/{{.Parent}}" title="prev">{{end}}
+</head>
+<body>
+{{if .Cfg.AboutURL}}[<a href="{{.Cfg.AboutURL}}">about</a>]{{end}}
+[<a href="{{.Cfg.URLPrefix}}/">index</a>]
+{{if .Parent}}[<a href="{{.Cfg.URLPrefix}}/{{.Parent}}">prev</a>]{{end}}
+[<tt><a title="When">{{.When}}</a></tt>]
+[<tt><a title="What">{{.Commit.Hash.String}}</a></tt>]
+
+<hr/>
+<h2>{{.Title}}</h2>
+<pre>
+{{range .Lines}}{{. | lineURLize $Cfg.URLPrefix}}
+{{end}}</pre>
+<hr/>
+
+{{if .NoteLines}}Note:<pre>
+{{range .NoteLines}}{{. | lineURLize $Cfg.URLPrefix}}
+{{end}}</pre>
+<hr/>{{end}}
+
+{{if .Cfg.CommentsEmail}}[<a href="mailto:{{.Cfg.CommentsEmail}}?subject={{.Commit.Hash.String}}">leave comment</a>]{{end}}
+
+<dl>{{range $idx, $comment := .Comments}}
+<dt><a name="comment{{$idx}}"><a href="#comment{{$idx}}">comment {{$idx}}</a>:</dt>
+<dd><pre>
+{{range $comment.HeaderLines}}{{.}}
+{{end}}{{range $comment.BodyLines}}{{. | lineURLize $Cfg.URLPrefix}}
+{{end}}</pre></dd>
+{{end}}</dl>
+
+</body>
+</html>
+`
)
var (
- defaultLinks = []string{}
-
renderableSchemes = map[string]struct{}{
"ftp": struct{}{},
"gopher": struct{}{},
)
type TableEntry struct {
- commit *object.Commit
- commentsRaw []byte
+ Commit *object.Commit
+ CommentsRaw []byte
+ Num int
+ Title string
+ LinesNum int
+ CommentsNum int
+ DomainURLs []string
+}
+
+type CommentEntry struct {
+ HeaderLines []string
+ BodyLines []string
}
func makeA(href, text string) string {
cols[i] = makeA(col, col)
continue
}
- cols[i] = sha1DigestRe.ReplaceAllString(col, makeA(
- urlPrefix+"/$1", "$1",
- ))
+ cols[i] = sha1DigestRe.ReplaceAllString(col, makeA(urlPrefix+"/$1", "$1"))
}
return strings.Join(cols, " ")
}
+func lineURLizeInTemplate(urlPrefix, line interface{}) string {
+ return lineURLize(urlPrefix.(string), line.(string))
+}
+
func startHeader(etag hash.Hash, gziped bool) string {
lines := []string{
"Content-Type: text/html; charset=UTF-8",
return strings.Join(lines, "\n")
}
-func startHTML(title string, additional []string) string {
- return fmt.Sprintf(`<html>
-<head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <meta name="generator" content="SGBlog %s">
- <title>%s</title>
- %s
-</head>
-<body>
-`,
- sgblog.Version, title,
- strings.Join(append(defaultLinks, additional...), "\n "),
- )
-}
-
func makeErr(err error) {
fmt.Print("Content-Type: text/plain; charset=UTF-8\n\n")
fmt.Println(err)
if err != nil {
panic(err)
}
- etagHash.Write([]byte("SGBLOG"))
- etagHash.Write([]byte(sgblog.Version))
- etagHash.Write([]byte(cfg.GitPath))
- etagHash.Write([]byte(cfg.Branch))
- etagHash.Write([]byte(cfg.Title))
- etagHash.Write([]byte(cfg.URLPrefix))
- etagHash.Write([]byte(cfg.AtomBaseURL))
- etagHash.Write([]byte(cfg.AtomId))
- etagHash.Write([]byte(cfg.AtomAuthor))
-
- etagHashForWeb := [][]byte{}
- if cfg.CSS != "" {
- defaultLinks = append(defaultLinks, `<link rel="stylesheet" type="text/css" href="`+cfg.CSS+`">`)
- etagHashForWeb = append(etagHashForWeb, []byte(cfg.CSS))
- }
- if cfg.Webmaster != "" {
- defaultLinks = append(defaultLinks, `<link rev="made" href="mailto:`+cfg.Webmaster+`">`)
- etagHashForWeb = append(etagHashForWeb, []byte(cfg.Webmaster))
+ for _, s := range []string{
+ "SGBLOG",
+ sgblog.Version,
+ cfg.GitPath,
+ cfg.Branch,
+ cfg.Title,
+ cfg.URLPrefix,
+ cfg.AtomBaseURL,
+ cfg.AtomId,
+ cfg.AtomAuthor,
+ } {
+ if _, err = etagHash.Write([]byte(s)); err != nil {
+ panic(err)
+ }
}
- if cfg.AboutURL != "" {
- etagHashForWeb = append(etagHashForWeb, []byte(cfg.AboutURL))
+ etagHashForWeb := []string{
+ cfg.CSS,
+ cfg.Webmaster,
+ cfg.AboutURL,
+ cfg.CommentsNotesRef,
+ cfg.CommentsEmail,
}
for _, gitURL := range cfg.GitURLs {
- defaultLinks = append(defaultLinks, `<link rel="vcs-git" href="`+gitURL+`" title="Git repository">`)
- etagHashForWeb = append(etagHashForWeb, []byte(gitURL))
- }
- if cfg.CommentsNotesRef != "" {
- etagHashForWeb = append(etagHashForWeb, []byte(cfg.CommentsNotesRef))
- }
- if cfg.CommentsEmail != "" {
- etagHashForWeb = append(etagHashForWeb, []byte(cfg.CommentsEmail))
+ etagHashForWeb = append(etagHashForWeb, gitURL)
}
- defaultLinks = append(defaultLinks, `<link rel="top" href="`+cfg.URLPrefix+`/" title="top">`)
- atomPostsURL := cfg.AtomBaseURL + cfg.URLPrefix + "/" + AtomPostsFeed
- atomCommentsURL := cfg.AtomBaseURL + cfg.URLPrefix + "/" + AtomCommentsFeed
-
headHash, err := initRepo(cfg)
if err != nil {
makeErr(err)
entries := make([]TableEntry, 0, PageEntries)
logEnded := false
for _, data := range etagHashForWeb {
- etagHash.Write(data)
+ etagHash.Write([]byte(data))
}
etagHash.Write([]byte("INDEX"))
for i := 0; i < PageEntries; i++ {
etagHash.Write(commit.Hash[:])
commentsRaw := getNote(commentsTree, commit.Hash)
etagHash.Write(commentsRaw)
- entries = append(entries, TableEntry{commit, commentsRaw})
+ entries = append(entries, TableEntry{
+ Commit: commit,
+ CommentsRaw: commentsRaw,
+ })
}
checkETag(etagHash)
- var table bytes.Buffer
- table.WriteString(
- "<table border=1>\n" +
- "<th>N</th>" +
- "<th>When</th>" +
- "<th>Title</th>" +
- `<th size="5%"><a title="Lines">L</a></th>` +
- `<th size="5%"><a title="Comments">C</a></th>` +
- "<th>Linked to</th></tr>\n")
- var yearPrev int
- var monthPrev time.Month
- var dayPrev int
- for _, entry := range entries {
- yearCur, monthCur, dayCur := entry.commit.Author.When.Date()
- if dayCur != dayPrev || monthCur != monthPrev || yearCur != yearPrev {
- table.WriteString(fmt.Sprintf(
- "<tr><td colspan=6><center><tt>%04d-%02d-%02d</tt></center></td></tr>\n",
- yearCur, monthCur, dayCur,
- ))
- yearPrev, monthPrev, dayPrev = yearCur, monthCur, dayCur
- }
+ for i, entry := range entries {
commitN++
- lines := msgSplit(entry.commit.Message)
- domains := []string{}
+ entry.Num = commitN
+ lines := msgSplit(entry.Commit.Message)
+ entry.Title = lines[0]
+ entry.LinesNum = len(lines) - 2
for _, line := range lines[2:] {
- if u := urlParse(line); u == nil {
+ u := urlParse(line)
+ if u == nil {
break
- } else {
- domains = append(domains, makeA(line, u.Host))
}
+ entry.DomainURLs = append(entry.DomainURLs, makeA(line, u.Host))
}
- var commentsValue string
- if l := len(parseComments(entry.commentsRaw)); l > 0 {
- commentsValue = strconv.Itoa(l)
- } else {
- commentsValue = " "
- }
- table.WriteString(fmt.Sprintf(
- "<tr><td>%d</td><td><tt>%02d:%02d</tt></td>"+
- "<td>%s</td>"+
- "<td>%d</td><td>%s</td>"+
- "<td>%s</td></tr>\n",
- commitN,
- entry.commit.Author.When.Hour(),
- entry.commit.Author.When.Minute(),
- makeA(cfg.URLPrefix+"/"+entry.commit.Hash.String(), lines[0]),
- len(lines)-2,
- commentsValue,
- strings.Join(domains, " "),
- ))
+ entry.CommentsNum = len(parseComments(entry.CommentsRaw))
+ entries[i] = entry
}
- table.WriteString("</table>")
-
- var href string
- links := []string{`<link rel="alternate" title="Posts feed" href="` + atomPostsURL + `" type="application/atom+xml">`}
- var refs bytes.Buffer
- if commentsTree != nil {
- links = append(links, `<link rel="alternate" title="Comments feed" href="`+atomCommentsURL+`" type="application/atom+xml">`)
- }
- if offset > 0 {
- if offsetPrev := offset - PageEntries; offsetPrev > 0 {
- href = cfg.URLPrefix + "/?offset=" + strconv.Itoa(offsetPrev)
- } else {
- href = cfg.URLPrefix + "/"
- }
- links = append(links, `<link rel="prev" href="`+href+`" title="prev">`)
- refs.WriteString("\n" + makeA(href, "[prev]"))
- }
- if !logEnded {
- href = cfg.URLPrefix + "/?offset=" + strconv.Itoa(offset+PageEntries)
- links = append(links, `<link rel="next" href="`+href+`" title="next">`)
- refs.WriteString("\n" + makeA(href, "[next]"))
- }
-
+ tmpl := template.Must(template.New("index").Parse(TmplHTMLIndex))
os.Stdout.Write([]byte(startHeader(etagHash, gzipWriter != nil)))
- out.Write([]byte(startHTML(
- fmt.Sprintf("%s (%d-%d)", cfg.Title, offset, offset+PageEntries),
- links,
- )))
- if cfg.AboutURL != "" {
- out.Write([]byte(fmt.Sprintf("[%s]", makeA(cfg.AboutURL, "about"))))
+ err = tmpl.Execute(out, struct {
+ Version string
+ Cfg *Cfg
+ CommentsEnabled bool
+ AtomPostsFeed string
+ AtomCommentsFeed string
+ Offset int
+ OffsetPrev int
+ OffsetNext int
+ LogEnded bool
+ Entries []TableEntry
+ }{
+ Version: sgblog.Version,
+ Cfg: cfg,
+ CommentsEnabled: commentsTree != nil,
+ AtomPostsFeed: AtomPostsFeed,
+ AtomCommentsFeed: AtomCommentsFeed,
+ Offset: offset,
+ OffsetPrev: offset - PageEntries,
+ OffsetNext: offset + PageEntries,
+ LogEnded: logEnded,
+ Entries: entries,
+ })
+ if err != nil {
+ makeErr(err)
}
- out.Write(refs.Bytes())
- out.Write(table.Bytes())
- out.Write(refs.Bytes())
- out.Write([]byte("\n"))
} else if pathInfo == "/"+AtomPostsFeed {
commit, err := repo.CommitObject(*headHash)
if err != nil {
Title: cfg.Title,
ID: cfg.AtomId,
Updated: atom.Time(commit.Author.When),
- Link: []atom.Link{{Rel: "self", Href: atomPostsURL}},
- Author: &atom.Person{Name: cfg.AtomAuthor},
+ Link: []atom.Link{{
+ Rel: "self",
+ Href: cfg.AtomBaseURL + cfg.URLPrefix + "/" + AtomPostsFeed,
+ }},
+ Author: &atom.Person{Name: cfg.AtomAuthor},
}
repoLog, err := repo.Log(&git.LogOptions{From: *headHash})
if err != nil {
Title: cfg.Title + " comments",
ID: "urn:uuid:" + bytes2uuid(idHasher.Sum(nil)),
Updated: atom.Time(commit.Author.When),
- Link: []atom.Link{{Rel: "self", Href: atomCommentsURL}},
- Author: &atom.Person{Name: cfg.AtomAuthor},
+ Link: []atom.Link{{
+ Rel: "self",
+ Href: cfg.AtomBaseURL + cfg.URLPrefix + "/" + AtomCommentsFeed,
+ }},
+ Author: &atom.Person{Name: cfg.AtomAuthor},
}
repoLog, err := repo.Log(&git.LogOptions{From: commentsRef.Hash()})
if err != nil {
idHasher.Write(commit.Hash[:])
idHasher.Write([]byte(commentN))
feed.Entry = append(feed.Entry, &atom.Entry{
- Title: strings.Join([]string{
- "Comment ", commentN,
- " for \"", msgSplit(commit.Message)[0],
- "\" by ", from,
- }, ""),
+ Title: fmt.Sprintf(
+ "Comment %s for \"%s\" by %s",
+ commentN, msgSplit(commit.Message)[0], from,
+ ),
Author: &atom.Person{Name: from},
ID: "urn:uuid:" + bytes2uuid(idHasher.Sum(nil)),
Link: []atom.Link{{
makeErr(err)
}
for _, data := range etagHashForWeb {
- etagHash.Write(data)
+ etagHash.Write([]byte(data))
}
etagHash.Write([]byte("ENTRY"))
etagHash.Write(commit.Hash[:])
- atomCommentsURL = strings.Join([]string{
+ atomCommentsURL := strings.Join([]string{
cfg.AtomBaseURL, cfg.URLPrefix, "/",
commit.Hash.String(), "/", AtomCommentsFeed,
}, "")
idHasher.Write(commit.Hash[:])
idHasher.Write([]byte(comment.n))
feed.Entry = append(feed.Entry, &atom.Entry{
- Title: strings.Join([]string{
- "Comment", comment.n,
- "by", comment.from,
- }, " "),
+ Title: fmt.Sprintf("Comment %s by %s", comment.n, comment.from),
Author: &atom.Person{Name: comment.from},
ID: "urn:uuid:" + bytes2uuid(idHasher.Sum(nil)),
Link: []atom.Link{{
notesRaw := getNote(notesTree, commit.Hash)
etagHash.Write(notesRaw)
checkETag(etagHash)
+
lines := msgSplit(commit.Message)
title := lines[0]
when := commit.Author.When.Format(sgblog.WhenFmt)
- os.Stdout.Write([]byte(startHeader(etagHash, gzipWriter != nil)))
- links := []string{`<link rel="alternate" title="Comments feed" href="` + atomCommentsURL + `" type="application/atom+xml">`}
var parent string
if len(commit.ParentHashes) > 0 {
parent = commit.ParentHashes[0].String()
- links = append(links, `<link rel="prev" href="`+cfg.URLPrefix+"/"+parent+`" title="prev">`)
- }
- out.Write([]byte(startHTML(fmt.Sprintf("%s (%s)", title, when), links)))
- if cfg.AboutURL != "" {
- out.Write([]byte(fmt.Sprintf("[%s]\n", makeA(cfg.AboutURL, "about"))))
- }
- out.Write([]byte(fmt.Sprintf("[%s]\n", makeA(cfg.URLPrefix+"/", "index"))))
- if parent != "" {
- out.Write([]byte(fmt.Sprintf("[%s]\n", makeA(cfg.URLPrefix+"/"+parent, "prev"))))
}
- out.Write([]byte(fmt.Sprintf(
- "[<tt><a title=\"When\">%s</a></tt>]\n"+
- "[<tt><a title=\"What\">%s</a></tt>]\n"+
- "<hr/>\n<h2>%s</h2>\n<pre>\n",
- when, commit.Hash.String(), title,
- )))
- for _, line := range lines[2:] {
- out.Write([]byte(lineURLize(cfg.URLPrefix, line) + "\n"))
+ commentsParsed := parseComments(commentsRaw)
+ comments := make([]CommentEntry, 0, len(commentsParsed))
+ for _, comment := range commentsParsed {
+ lines := strings.Split(comment, "\n")
+ comments = append(comments, CommentEntry{lines[:3], lines[3:]})
}
- out.Write([]byte("</pre>\n<hr/>\n"))
+ var notesLines []string
if len(notesRaw) > 0 {
- out.Write([]byte("Note:<pre>\n"))
- for _, line := range strings.Split(string(notesRaw), "\n") {
- out.Write([]byte(lineURLize(cfg.URLPrefix, line) + "\n"))
- }
- out.Write([]byte("</pre>\n<hr/>\n"))
- }
- if cfg.CommentsEmail != "" {
- out.Write([]byte("[" + makeA(
- "mailto:"+cfg.CommentsEmail+"?subject="+commit.Hash.String(),
- "leave comment",
- ) + "]\n"))
+ notesLines = strings.Split(string(notesRaw), "\n")
}
- out.Write([]byte("<dl>\n"))
- for i, comment := range parseComments(commentsRaw) {
- out.Write([]byte(fmt.Sprintf(
- "<dt><a name=\"comment%d\"><a href=\"#comment%d\">comment %d</a>:"+
- "</dt>\n<dd><pre>\n",
- i, i, i,
- )))
- lines = strings.Split(comment, "\n")
- for _, line := range lines[:3] {
- out.Write([]byte(line + "\n"))
- }
- for _, line := range lines[3:] {
- out.Write([]byte(lineURLize(cfg.URLPrefix, line) + "\n"))
- }
- out.Write([]byte("</pre></dd>\n"))
+
+ tmpl := template.New("entry")
+ tmpl = tmpl.Funcs(template.FuncMap{"lineURLize": lineURLizeInTemplate})
+ tmpl = template.Must(tmpl.Parse(TmplHTMLEntry))
+ os.Stdout.Write([]byte(startHeader(etagHash, gzipWriter != nil)))
+ err = tmpl.Execute(out, struct {
+ Version string
+ Cfg *Cfg
+ Title string
+ When string
+ AtomCommentsURL string
+ Parent string
+ Commit *object.Commit
+ Lines []string
+ NoteLines []string
+ Comments []CommentEntry
+ }{
+ Version: sgblog.Version,
+ Cfg: cfg,
+ Title: title,
+ When: when,
+ AtomCommentsURL: atomCommentsURL,
+ Parent: parent,
+ Commit: commit,
+ Lines: lines[2:],
+ NoteLines: notesLines,
+ Comments: comments,
+ })
+ if err != nil {
+ makeErr(err)
}
- out.Write([]byte("</dl>\n"))
} else {
makeErr(errors.New("unknown URL action"))
}