Skip to content

Commit 3989cde

Browse files
committed
Fixes issue gorillaGH-25 for RSS feeds.
Adds the ability to add custom fields with a namespace to the RSS feed.
1 parent ffafbe1 commit 3989cde

File tree

3 files changed

+152
-18
lines changed

3 files changed

+152
-18
lines changed

feed.go

+53
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ type Item struct {
3535
Created time.Time
3636
Enclosure *Enclosure
3737
Content string
38+
Extension []interface{}
3839
}
3940

4041
type Feed struct {
42+
Attrs []xml.Attr
4143
Title string
4244
Link *Link
4345
Description string
@@ -56,6 +58,57 @@ func (f *Feed) Add(item *Item) {
5658
f.Items = append(f.Items, item)
5759
}
5860

61+
func (f *Feed) AddAttribute(name, nsURI string) {
62+
f.Attrs = append(f.Attrs, xml.Attr{
63+
Name: xml.Name{Local: name},
64+
Value: nsURI,
65+
})
66+
}
67+
68+
func (i *Item) AddExtension(extend interface{}) {
69+
i.Extension = append(i.Extension, extend)
70+
}
71+
72+
func (i *Item) AddExtensionString(name string, nsURI string, value string) {
73+
i.Extension = append(i.Extension, struct {
74+
XMLName xml.Name
75+
Text string `xml:",chardata"`
76+
}{
77+
XMLName: xml.Name{Local: name, Space: nsURI},
78+
Text: value,
79+
})
80+
}
81+
82+
func (i *Item) AddExtensionInt(name string, nsURI string, value int) {
83+
i.Extension = append(i.Extension, struct {
84+
XMLName xml.Name
85+
Number int `xml:",chardata"`
86+
}{
87+
XMLName: xml.Name{Local: name, Space: nsURI},
88+
Number: value,
89+
})
90+
}
91+
92+
func (i *Item) AddExtensionUint(name string, nsURI string, value uint) {
93+
i.Extension = append(i.Extension, struct {
94+
XMLName xml.Name
95+
Number uint `xml:",chardata"`
96+
}{
97+
XMLName: xml.Name{Local: name, Space: nsURI},
98+
Number: value,
99+
})
100+
}
101+
102+
func (i *Item) AddExtensionFloat64(name string, nsURI string, value float64) {
103+
i.Extension = append(i.Extension, struct {
104+
XMLName xml.Name
105+
Number float64 `xml:",chardata"`
106+
}{
107+
XMLName: xml.Name{Local: name, Space: nsURI},
108+
Number: value,
109+
})
110+
}
111+
59112
// returns the first non-zero time formatted as a string or ""
60113
func anyTimeFormat(format string, times ...time.Time) string {
61114
for _, t := range times {

rss.go

+22-18
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,25 @@ type RssTextInput struct {
4141
}
4242

4343
type RssFeed struct {
44-
XMLName xml.Name `xml:"channel"`
45-
Title string `xml:"title"` // required
46-
Link string `xml:"link"` // required
47-
Description string `xml:"description"` // required
48-
Language string `xml:"language,omitempty"`
49-
Copyright string `xml:"copyright,omitempty"`
50-
ManagingEditor string `xml:"managingEditor,omitempty"` // Author used
51-
WebMaster string `xml:"webMaster,omitempty"`
52-
PubDate string `xml:"pubDate,omitempty"` // created or updated
53-
LastBuildDate string `xml:"lastBuildDate,omitempty"` // updated used
54-
Category string `xml:"category,omitempty"`
55-
Generator string `xml:"generator,omitempty"`
56-
Docs string `xml:"docs,omitempty"`
57-
Cloud string `xml:"cloud,omitempty"`
58-
Ttl int `xml:"ttl,omitempty"`
59-
Rating string `xml:"rating,omitempty"`
60-
SkipHours string `xml:"skipHours,omitempty"`
61-
SkipDays string `xml:"skipDays,omitempty"`
44+
XMLName xml.Name `xml:"channel"`
45+
Attrs []xml.Attr `xml:",attr"`
46+
Title string `xml:"title"` // required
47+
Link string `xml:"link"` // required
48+
Description string `xml:"description"` // required
49+
Language string `xml:"language,omitempty"`
50+
Copyright string `xml:"copyright,omitempty"`
51+
ManagingEditor string `xml:"managingEditor,omitempty"` // Author used
52+
WebMaster string `xml:"webMaster,omitempty"`
53+
PubDate string `xml:"pubDate,omitempty"` // created or updated
54+
LastBuildDate string `xml:"lastBuildDate,omitempty"` // updated used
55+
Category string `xml:"category,omitempty"`
56+
Generator string `xml:"generator,omitempty"`
57+
Docs string `xml:"docs,omitempty"`
58+
Cloud string `xml:"cloud,omitempty"`
59+
Ttl int `xml:"ttl,omitempty"`
60+
Rating string `xml:"rating,omitempty"`
61+
SkipHours string `xml:"skipHours,omitempty"`
62+
SkipDays string `xml:"skipDays,omitempty"`
6263
Image *RssImage
6364
TextInput *RssTextInput
6465
Items []*RssItem
@@ -77,6 +78,7 @@ type RssItem struct {
7778
Guid string `xml:"guid,omitempty"` // Id used
7879
PubDate string `xml:"pubDate,omitempty"` // created or updated
7980
Source string `xml:"source,omitempty"`
81+
Extensions interface{}
8082
}
8183

8284
type RssEnclosure struct {
@@ -99,6 +101,7 @@ func newRssItem(i *Item) *RssItem {
99101
Description: i.Description,
100102
Guid: i.Id,
101103
PubDate: anyTimeFormat(time.RFC1123Z, i.Created, i.Updated),
104+
Extensions: i.Extension,
102105
}
103106
if len(i.Content) > 0 {
104107
item.Content = &RssContent{Content: i.Content}
@@ -136,6 +139,7 @@ func (r *Rss) RssFeed() *RssFeed {
136139
}
137140

138141
channel := &RssFeed{
142+
Attrs: r.Attrs,
139143
Title: r.Title,
140144
Link: r.Link.Href,
141145
Description: r.Description,

rss_test.go

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package feeds
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
"time"
7+
)
8+
9+
func TestRssFeedExtensions(t *testing.T) {
10+
var rssOutput = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
11+
<channel test="https://example.com/test/uri">
12+
<title>Example</title>
13+
<link>https://rss.example.com</link>
14+
<description>Example Example</description>
15+
<copyright>copyright © Example</copyright>
16+
<managingEditor>Example (Example)</managingEditor>
17+
<pubDate>Wed, 16 Jan 2013 21:52:35 -0500</pubDate>
18+
<item>
19+
<title>Example example</title>
20+
<link>https://example.com/link</link>
21+
<description>example</description>
22+
<content:encoded><![CDATA[Example example example, example example...]]></content:encoded>
23+
<author>Jason Moiron</author>
24+
<pubDate>Wed, 16 Jan 2013 21:52:35 -0500</pubDate>
25+
<test:version>v1</test:version>
26+
<test:example>1234567890</test:example>
27+
</item>
28+
</channel>
29+
</rss>`
30+
31+
now, err := time.Parse(time.RFC3339, "2013-01-16T21:52:35-05:00")
32+
if err != nil {
33+
t.Error(err)
34+
}
35+
tz := time.FixedZone("EST", -5*60*60)
36+
now = now.In(tz)
37+
38+
feed := &Feed{
39+
Title: "Example",
40+
Link: &Link{Href: "https://rss.example.com"},
41+
Description: "Example Example",
42+
Author: &Author{Name: "Example", Email: "Example"},
43+
Created: now,
44+
Copyright: "copyright © Example",
45+
}
46+
47+
feed.Items = []*Item{
48+
{
49+
Title: "Example example",
50+
Link: &Link{Href: "https://example.com/link"},
51+
Description: "example",
52+
Author: &Author{Name: "Jason Moiron", Email: "[email protected]"},
53+
Created: now,
54+
Content: "Example example example, example example...",
55+
},
56+
}
57+
58+
feed.AddAttribute("test", "https://example.com/test/uri")
59+
feed.Items[0].AddExtensionString("test:version", "", "v1")
60+
feed.Items[0].AddExtensionInt("test:example", "", 1234567890)
61+
62+
rss, err := feed.ToRss()
63+
if err != nil {
64+
t.Errorf("unexpected error encoding RSS: %v", err)
65+
}
66+
if rss != rssOutput {
67+
t.Errorf("Rss not what was expected. Got:\n%s\n\nExpected:\n%s\n", rss, rssOutput)
68+
}
69+
70+
var buf = new(bytes.Buffer)
71+
if err := feed.WriteRss(buf); err != nil {
72+
t.Errorf("unexpected error writing RSS: %v", err)
73+
}
74+
if got := buf.String(); got != rssOutput {
75+
t.Errorf("Rss not what was expected. Got:\n%s\n\nExpected:\n%s\n", got, rssOutput)
76+
}
77+
}

0 commit comments

Comments
 (0)