1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
package handlers
import (
"bytes"
"database/sql"
"encoding/xml"
"log"
"net/http"
"strconv"
"strings"
"time"
"golang.org/x/net/html"
"paterissa.net/mblog/internal/models"
)
type Item struct {
XMLName xml.Name `xml:"item"`
Title string `xml:"title"`
Link string `xml:"link"`
Guid string `xml:"guid"`
Description string `xml:"description"`
PubDate string `xml:"pubDate"`
Content string `xml:"content:encoded"`
}
type Channel struct {
XMLName xml.Name `xml:"channel"`
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
Items []Item `xml:"item"`
}
type rssExportContext struct {
err *log.Logger
db *sql.DB
serv string
}
func (ctx *rssExportContext) feed(w http.ResponseWriter, r *http.Request) {
feed := &Channel{
Title: "Paterissa",
Link: "https://" + ctx.serv,
Description: "Blog feed",
}
rows, err := ctx.db.Query("SELECT p.id, u.name, p.time, p.brief, p.content FROM posts p INNER JOIN logins u ON p.user_id = u.id ORDER BY p.id DESC LIMIT 20;")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal Error"))
ctx.err.Printf("Could not load posts from DB: %v\n", err)
return
}
defer rows.Close()
for rows.Next() {
var p models.Post
if err = rows.Scan(&p.Id, &p.Name, &p.Time, &p.Brief, &p.Content); err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal Error"))
ctx.err.Printf("Could not load posts from DB: %v\n", err)
return
}
p.FormattedTime = p.Time.Format(time.RFC1123Z)
title := ""
z := html.NewTokenizer(strings.NewReader(string(p.Brief)))
for {
tt := z.Next()
if tt == html.ErrorToken {
break
} else if tt == html.StartTagToken {
tag := z.Token()
if tag.Data == "h1" {
if tt = z.Next(); tt == html.TextToken {
title = z.Token().Data
}
}
}
}
feed.Items = append(feed.Items, Item{
Title: title,
Link: "https://" + ctx.serv + "/post?id=" + strconv.Itoa(p.Id),
Guid: "https://" + ctx.serv + "/post?id=" + strconv.Itoa(p.Id),
Description: "<![CDATA[" + string(p.Brief) + "]]>",
PubDate: p.FormattedTime,
Content: "<![CDATA[" + string(p.Content) + "]]>",
})
}
out, err := xml.MarshalIndent(feed, "", " ")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal Error"))
ctx.err.Printf("Could not create RSS feed: %v\n", err)
return
}
// Really stupid workaround.
// Basically, encoders will escape HTML automatically.
// This can be configured on the JSON encoder.
// It cannot on the XML.
// God only knows why.
out = []byte(html.UnescapeString(string(out)))
w.Write([]byte(xml.Header + `<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">` + string(out) + `</rss>`))
}
|