Use xml marshaling to create the rss feed
This commit is contained in:
parent
04c452853a
commit
0637cb7602
7 changed files with 159 additions and 82 deletions
158
lib/rss.go
Normal file
158
lib/rss.go
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
package trantor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gitlab.com/trantor/trantor/lib/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rss struct {
|
||||||
|
XMLName xml.Name `xml:"rss"`
|
||||||
|
Version string `xml:"version,attr"`
|
||||||
|
Channel channel `xml:"channel"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type channel struct {
|
||||||
|
Title string `xml:"title"`
|
||||||
|
Description string `xml:"description"`
|
||||||
|
Link string `xml:"link"`
|
||||||
|
ManagingEditor string `xml:"managingEditor,omitempty"`
|
||||||
|
Item []item `xml:"item"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type item struct {
|
||||||
|
Title string `xml:"title"`
|
||||||
|
Description string `xml:"description"`
|
||||||
|
Link string `xml:"link"`
|
||||||
|
GUID *guid `xml:"guid,omitempty"`
|
||||||
|
Enclosure *enclosure `xml:"enclosure,omitempty"`
|
||||||
|
Category []string `xml:"category"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type guid struct {
|
||||||
|
IsPermaLink bool `xml:"isPermaLink,attr"`
|
||||||
|
Chardata string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type enclosure struct {
|
||||||
|
URL string `xml:"url,attr"`
|
||||||
|
Length int `xml:"length,attr"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadRss(w http.ResponseWriter, tmpl string, data interface{}) error {
|
||||||
|
var err error
|
||||||
|
var res rss
|
||||||
|
switch tmpl {
|
||||||
|
case "news":
|
||||||
|
res, err = newsRss(data)
|
||||||
|
case "search":
|
||||||
|
res, err = searchRss(data)
|
||||||
|
case "list":
|
||||||
|
res, err = listRss(data)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Write([]byte(xml.Header))
|
||||||
|
enc := xml.NewEncoder(w)
|
||||||
|
enc.Indent("", " ")
|
||||||
|
return enc.Encode(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newsRss(data interface{}) (rss, error) {
|
||||||
|
news, ok := data.(newsData)
|
||||||
|
if !ok {
|
||||||
|
return rss{}, errors.New("Data is not valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
feed := rss{
|
||||||
|
Version: "2.0",
|
||||||
|
Channel: channel{
|
||||||
|
Title: news.S.Title,
|
||||||
|
Description: "News of the library",
|
||||||
|
Link: news.S.BaseURL + "/news/",
|
||||||
|
Item: make([]item, len(news.News)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, n := range news.News {
|
||||||
|
feed.Channel.Item[i] = item{
|
||||||
|
Title: n.Date,
|
||||||
|
Description: n.Text,
|
||||||
|
Link: news.S.BaseURL + "/news/",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return feed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchRss(data interface{}) (rss, error) {
|
||||||
|
search, ok := data.(searchData)
|
||||||
|
if !ok {
|
||||||
|
return rss{}, errors.New("Data is not valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
description := "Last books added"
|
||||||
|
link := search.S.BaseURL
|
||||||
|
if search.S.Search != "" {
|
||||||
|
description = "Book search: " + search.S.Search
|
||||||
|
link += "/search/?q=" + search.S.Search
|
||||||
|
}
|
||||||
|
|
||||||
|
feed := rss{
|
||||||
|
Version: "2.0",
|
||||||
|
Channel: channel{
|
||||||
|
Title: search.S.Title,
|
||||||
|
Description: description,
|
||||||
|
Link: link,
|
||||||
|
Item: bookListRss(search.Books, search.S.BaseURL),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return feed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func listRss(data interface{}) (rss, error) {
|
||||||
|
lb, ok := data.(listData)
|
||||||
|
if !ok {
|
||||||
|
return rss{}, errors.New("Data is not valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
feed := rss{
|
||||||
|
Version: "2.0",
|
||||||
|
Channel: channel{
|
||||||
|
Title: lb.S.Title,
|
||||||
|
Description: strings.Join(lb.List.Description, "\n"),
|
||||||
|
Link: lb.S.BaseURL + "/list/" + lb.List.ListID,
|
||||||
|
ManagingEditor: lb.List.User.Username,
|
||||||
|
Item: bookListRss(lb.List.Books, lb.S.BaseURL),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return feed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bookListRss(books []database.Book, baseURL string) []item {
|
||||||
|
items := make([]item, len(books))
|
||||||
|
for i, b := range books {
|
||||||
|
items[i] = item{
|
||||||
|
Title: b.Title,
|
||||||
|
Description: b.Description,
|
||||||
|
Link: baseURL + "/download/" + b.ID,
|
||||||
|
Category: b.Tags,
|
||||||
|
Enclosure: &enclosure{
|
||||||
|
URL: baseURL + "/download/" + b.ID + "/" + b.Title + ".epub",
|
||||||
|
Length: b.FileSize,
|
||||||
|
Type: "application/epub+zip",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if b.Isbn != "" {
|
||||||
|
items[i].GUID = &guid{
|
||||||
|
IsPermaLink: false,
|
||||||
|
Chardata: "ISBN: " + b.Isbn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
|
@ -35,7 +35,7 @@ func (h handler) load(tmpl string, data interface{}) {
|
||||||
fmt := h.r.FormValue("fmt")
|
fmt := h.r.FormValue("fmt")
|
||||||
switch fmt {
|
switch fmt {
|
||||||
case "rss":
|
case "rss":
|
||||||
err = h.template.rss.ExecuteTemplate(h.w, tmpl+".rss", data)
|
err = loadRss(h.w, tmpl, data)
|
||||||
case "opds":
|
case "opds":
|
||||||
err = h.template.opds.ExecuteTemplate(h.w, tmpl+".opds", data)
|
err = h.template.opds.ExecuteTemplate(h.w, tmpl+".opds", data)
|
||||||
case "json":
|
case "json":
|
||||||
|
|
|
@ -55,7 +55,6 @@ func GetStatus(h handler) Status {
|
||||||
|
|
||||||
type Template struct {
|
type Template struct {
|
||||||
html *html_tmpl.Template
|
html *html_tmpl.Template
|
||||||
rss *txt_tmpl.Template
|
|
||||||
opds *txt_tmpl.Template
|
opds *txt_tmpl.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,18 +70,6 @@ func InitTemplate(assetsPath string) *Template {
|
||||||
log.Critical("Error loading html templates: ", err)
|
log.Critical("Error loading html templates: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.rss, err = txt_tmpl.New("rss").Funcs(txt_tmpl.FuncMap{
|
|
||||||
"book_list": func(books, baseURL interface{}) (map[string]interface{}, error) {
|
|
||||||
data := make(map[string]interface{}, 2)
|
|
||||||
data["Books"] = books
|
|
||||||
data["BaseURL"] = baseURL
|
|
||||||
return data, nil
|
|
||||||
},
|
|
||||||
}).ParseGlob(path.Join(templatePath, "*.rss"))
|
|
||||||
if err != nil {
|
|
||||||
log.Critical("Error loading rss templates: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.opds, err = txt_tmpl.ParseGlob(path.Join(templatePath, "*.opds"))
|
t.opds, err = txt_tmpl.ParseGlob(path.Join(templatePath, "*.opds"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Critical("Error loading opds templates: ", err)
|
log.Critical("Error loading opds templates: ", err)
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
{{$baseURL := .BaseURL}}
|
|
||||||
{{range .Books}}
|
|
||||||
<item>
|
|
||||||
<title>{{.Title}} - {{index .Authors 0}}</title>
|
|
||||||
<description>{{.Description}}</description>
|
|
||||||
<link>{{$baseURL}}/book/{{.ID}}</link>
|
|
||||||
{{if .Isbn}}
|
|
||||||
<guid isPermaLink="false">ISBN: {{.Isbn}}</guid>
|
|
||||||
{{end}}
|
|
||||||
<enclosure url="{{$baseURL}}/download/{{.ID}}/{{.Title}}.epub" length="{{.FileSize}}" type="application/epub+zip" />
|
|
||||||
{{range .Authors}}
|
|
||||||
{{if .}}
|
|
||||||
<category>{{.}}</category>
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
</item>
|
|
||||||
{{end}}
|
|
|
@ -1,14 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<rss version="2.0">
|
|
||||||
<channel>
|
|
||||||
<title>{{.S.Title}}</title>
|
|
||||||
<description>{{range .List.Description}}
|
|
||||||
{{.}}
|
|
||||||
{{end}}</description>
|
|
||||||
<link>{{.S.BaseURL}}/list/{{.List.ListID}}</link>
|
|
||||||
<managingEditor>{{.List.User.Username}}</managingEditor>
|
|
||||||
|
|
||||||
{{template "book_list.rss" book_list .List.Books .S.BaseURL}}
|
|
||||||
|
|
||||||
</channel>
|
|
||||||
</rss>
|
|
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<rss version="2.0">
|
|
||||||
<channel>
|
|
||||||
{{with .S}}
|
|
||||||
<title>{{.Title}}</title>
|
|
||||||
<description>News of the library</description>
|
|
||||||
<link>{{.BaseURL}}/news/</link>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{$baseURL := .S.BaseURL}}
|
|
||||||
{{range .News}}
|
|
||||||
<item>
|
|
||||||
<title>{{.Date}}</title>
|
|
||||||
<description>{{.Text}}</description>
|
|
||||||
<link>{{$baseURL}}/news/</link>
|
|
||||||
</item>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
</channel>
|
|
||||||
</rss>
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<rss version="2.0">
|
|
||||||
<channel>
|
|
||||||
{{with .S}}
|
|
||||||
<title>{{.Title}}</title>
|
|
||||||
{{if .Search}}
|
|
||||||
<description>Book search: {{.Search}}</description>
|
|
||||||
{{else}}
|
|
||||||
<description>Last books added</description>
|
|
||||||
{{end}}
|
|
||||||
<link>{{.BaseURL}}</link>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{template "book_list.rss" book_list .Books .S.BaseURL}}
|
|
||||||
|
|
||||||
</channel>
|
|
||||||
</rss>
|
|
Reference in a new issue