atom.go (4815B)
1 package feeds 2 3 import ( 4 "encoding/xml" 5 "fmt" 6 "net/url" 7 "time" 8 ) 9 10 // Generates Atom feed as XML 11 12 const ns = "http://www.w3.org/2005/Atom" 13 14 type AtomPerson struct { 15 Name string `xml:"name,omitempty"` 16 Uri string `xml:"uri,omitempty"` 17 Email string `xml:"email,omitempty"` 18 } 19 20 type AtomSummary struct { 21 XMLName xml.Name `xml:"summary"` 22 Content string `xml:",chardata"` 23 Type string `xml:"type,attr"` 24 } 25 26 type AtomContent struct { 27 XMLName xml.Name `xml:"content"` 28 Content string `xml:",chardata"` 29 Type string `xml:"type,attr"` 30 } 31 32 type AtomAuthor struct { 33 XMLName xml.Name `xml:"author"` 34 AtomPerson 35 } 36 37 type AtomContributor struct { 38 XMLName xml.Name `xml:"contributor"` 39 AtomPerson 40 } 41 42 type AtomEntry struct { 43 XMLName xml.Name `xml:"entry"` 44 Xmlns string `xml:"xmlns,attr,omitempty"` 45 Title string `xml:"title"` // required 46 Updated string `xml:"updated"` // required 47 Id string `xml:"id"` // required 48 Category string `xml:"category,omitempty"` 49 Content *AtomContent 50 Rights string `xml:"rights,omitempty"` 51 Source string `xml:"source,omitempty"` 52 Published string `xml:"published,omitempty"` 53 Contributor *AtomContributor 54 Links []AtomLink // required if no child 'content' elements 55 Summary *AtomSummary // required if content has src or content is base64 56 Author *AtomAuthor // required if feed lacks an author 57 } 58 59 // Multiple links with different rel can coexist 60 type AtomLink struct { 61 //Atom 1.0 <link rel="enclosure" type="audio/mpeg" title="MP3" href="http://www.example.org/myaudiofile.mp3" length="1234" /> 62 XMLName xml.Name `xml:"link"` 63 Href string `xml:"href,attr"` 64 Rel string `xml:"rel,attr,omitempty"` 65 Type string `xml:"type,attr,omitempty"` 66 Length string `xml:"length,attr,omitempty"` 67 } 68 69 type AtomFeed struct { 70 XMLName xml.Name `xml:"feed"` 71 Xmlns string `xml:"xmlns,attr"` 72 Title string `xml:"title"` // required 73 Id string `xml:"id"` // required 74 Updated string `xml:"updated"` // required 75 Category string `xml:"category,omitempty"` 76 Icon string `xml:"icon,omitempty"` 77 Logo string `xml:"logo,omitempty"` 78 Rights string `xml:"rights,omitempty"` // copyright used 79 Subtitle string `xml:"subtitle,omitempty"` 80 Link *AtomLink 81 Author *AtomAuthor `xml:"author,omitempty"` 82 Contributor *AtomContributor 83 Entries []*AtomEntry `xml:"entry"` 84 } 85 86 type Atom struct { 87 *Feed 88 } 89 90 func newAtomEntry(i *Item) *AtomEntry { 91 id := i.Id 92 // assume the description is html 93 s := &AtomSummary{Content: i.Description, Type: "html"} 94 95 if len(id) == 0 { 96 // if there's no id set, try to create one, either from data or just a uuid 97 if len(i.Link.Href) > 0 && (!i.Created.IsZero() || !i.Updated.IsZero()) { 98 dateStr := anyTimeFormat("2006-01-02", i.Updated, i.Created) 99 host, path := i.Link.Href, "/invalid.html" 100 if url, err := url.Parse(i.Link.Href); err == nil { 101 host, path = url.Host, url.Path 102 } 103 id = fmt.Sprintf("tag:%s,%s:%s", host, dateStr, path) 104 } else { 105 id = "urn:uuid:" + NewUUID().String() 106 } 107 } 108 var name, email string 109 if i.Author != nil { 110 name, email = i.Author.Name, i.Author.Email 111 } 112 113 link_rel := i.Link.Rel 114 if link_rel == "" { 115 link_rel = "alternate" 116 } 117 x := &AtomEntry{ 118 Title: i.Title, 119 Links: []AtomLink{{Href: i.Link.Href, Rel: link_rel, Type: i.Link.Type}}, 120 Id: id, 121 Updated: anyTimeFormat(time.RFC3339, i.Updated, i.Created), 122 Summary: s, 123 } 124 125 // if there's a content, assume it's html 126 if len(i.Content) > 0 { 127 x.Content = &AtomContent{Content: i.Content, Type: "html"} 128 } 129 130 if i.Enclosure != nil && link_rel != "enclosure" { 131 x.Links = append(x.Links, AtomLink{Href: i.Enclosure.Url, Rel: "enclosure", Type: i.Enclosure.Type, Length: i.Enclosure.Length}) 132 } 133 134 if len(name) > 0 || len(email) > 0 { 135 x.Author = &AtomAuthor{AtomPerson: AtomPerson{Name: name, Email: email}} 136 } 137 return x 138 } 139 140 // create a new AtomFeed with a generic Feed struct's data 141 func (a *Atom) AtomFeed() *AtomFeed { 142 updated := anyTimeFormat(time.RFC3339, a.Updated, a.Created) 143 feed := &AtomFeed{ 144 Xmlns: ns, 145 Title: a.Title, 146 Link: &AtomLink{Href: a.Link.Href, Rel: a.Link.Rel}, 147 Subtitle: a.Description, 148 Id: a.Link.Href, 149 Updated: updated, 150 Rights: a.Copyright, 151 } 152 if a.Author != nil { 153 feed.Author = &AtomAuthor{AtomPerson: AtomPerson{Name: a.Author.Name, Email: a.Author.Email}} 154 } 155 for _, e := range a.Items { 156 feed.Entries = append(feed.Entries, newAtomEntry(e)) 157 } 158 return feed 159 } 160 161 // FeedXml returns an XML-Ready object for an Atom object 162 func (a *Atom) FeedXml() interface{} { 163 return a.AtomFeed() 164 } 165 166 // FeedXml returns an XML-ready object for an AtomFeed object 167 func (a *AtomFeed) FeedXml() interface{} { 168 return a 169 }