Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
4.9 KiB
168 lines
4.9 KiB
package feeds |
|
|
|
// rss support |
|
// validation done according to spec here: |
|
// http://cyber.law.harvard.edu/rss/rss.html |
|
|
|
import ( |
|
"encoding/xml" |
|
"fmt" |
|
"time" |
|
) |
|
|
|
// private wrapper around the RssFeed which gives us the <rss>..</rss> xml |
|
type RssFeedXml struct { |
|
XMLName xml.Name `xml:"rss"` |
|
Version string `xml:"version,attr"` |
|
ContentNamespace string `xml:"xmlns:content,attr"` |
|
Channel *RssFeed |
|
} |
|
|
|
type RssContent struct { |
|
XMLName xml.Name `xml:"content:encoded"` |
|
Content string `xml:",cdata"` |
|
} |
|
|
|
type RssImage struct { |
|
XMLName xml.Name `xml:"image"` |
|
Url string `xml:"url"` |
|
Title string `xml:"title"` |
|
Link string `xml:"link"` |
|
Width int `xml:"width,omitempty"` |
|
Height int `xml:"height,omitempty"` |
|
} |
|
|
|
type RssTextInput struct { |
|
XMLName xml.Name `xml:"textInput"` |
|
Title string `xml:"title"` |
|
Description string `xml:"description"` |
|
Name string `xml:"name"` |
|
Link string `xml:"link"` |
|
} |
|
|
|
type RssFeed struct { |
|
XMLName xml.Name `xml:"channel"` |
|
Title string `xml:"title"` // required |
|
Link string `xml:"link"` // required |
|
Description string `xml:"description"` // required |
|
Language string `xml:"language,omitempty"` |
|
Copyright string `xml:"copyright,omitempty"` |
|
ManagingEditor string `xml:"managingEditor,omitempty"` // Author used |
|
WebMaster string `xml:"webMaster,omitempty"` |
|
PubDate string `xml:"pubDate,omitempty"` // created or updated |
|
LastBuildDate string `xml:"lastBuildDate,omitempty"` // updated used |
|
Category string `xml:"category,omitempty"` |
|
Generator string `xml:"generator,omitempty"` |
|
Docs string `xml:"docs,omitempty"` |
|
Cloud string `xml:"cloud,omitempty"` |
|
Ttl int `xml:"ttl,omitempty"` |
|
Rating string `xml:"rating,omitempty"` |
|
SkipHours string `xml:"skipHours,omitempty"` |
|
SkipDays string `xml:"skipDays,omitempty"` |
|
Image *RssImage |
|
TextInput *RssTextInput |
|
Items []*RssItem `xml:"item"` |
|
} |
|
|
|
type RssItem struct { |
|
XMLName xml.Name `xml:"item"` |
|
Title string `xml:"title"` // required |
|
Link string `xml:"link"` // required |
|
Description string `xml:"description"` // required |
|
Content *RssContent |
|
Author string `xml:"author,omitempty"` |
|
Category string `xml:"category,omitempty"` |
|
Comments string `xml:"comments,omitempty"` |
|
Enclosure *RssEnclosure |
|
Guid string `xml:"guid,omitempty"` // Id used |
|
PubDate string `xml:"pubDate,omitempty"` // created or updated |
|
Source string `xml:"source,omitempty"` |
|
} |
|
|
|
type RssEnclosure struct { |
|
//RSS 2.0 <enclosure url="http://example.com/file.mp3" length="123456789" type="audio/mpeg" /> |
|
XMLName xml.Name `xml:"enclosure"` |
|
Url string `xml:"url,attr"` |
|
Length string `xml:"length,attr"` |
|
Type string `xml:"type,attr"` |
|
} |
|
|
|
type Rss struct { |
|
*Feed |
|
} |
|
|
|
// create a new RssItem with a generic Item struct's data |
|
func newRssItem(i *Item) *RssItem { |
|
item := &RssItem{ |
|
Title: i.Title, |
|
Link: i.Link.Href, |
|
Description: i.Description, |
|
Guid: i.Id, |
|
PubDate: anyTimeFormat(time.RFC1123Z, i.Created, i.Updated), |
|
} |
|
if len(i.Content) > 0 { |
|
item.Content = &RssContent{Content: i.Content} |
|
} |
|
if i.Source != nil { |
|
item.Source = i.Source.Href |
|
} |
|
|
|
// Define a closure |
|
if i.Enclosure != nil && i.Enclosure.Type != "" && i.Enclosure.Length != "" { |
|
item.Enclosure = &RssEnclosure{Url: i.Enclosure.Url, Type: i.Enclosure.Type, Length: i.Enclosure.Length} |
|
} |
|
|
|
if i.Author != nil { |
|
item.Author = i.Author.Name |
|
} |
|
return item |
|
} |
|
|
|
// create a new RssFeed with a generic Feed struct's data |
|
func (r *Rss) RssFeed() *RssFeed { |
|
pub := anyTimeFormat(time.RFC1123Z, r.Created, r.Updated) |
|
build := anyTimeFormat(time.RFC1123Z, r.Updated) |
|
author := "" |
|
if r.Author != nil { |
|
author = r.Author.Email |
|
if len(r.Author.Name) > 0 { |
|
author = fmt.Sprintf("%s (%s)", r.Author.Email, r.Author.Name) |
|
} |
|
} |
|
|
|
var image *RssImage |
|
if r.Image != nil { |
|
image = &RssImage{Url: r.Image.Url, Title: r.Image.Title, Link: r.Image.Link, Width: r.Image.Width, Height: r.Image.Height} |
|
} |
|
|
|
channel := &RssFeed{ |
|
Title: r.Title, |
|
Link: r.Link.Href, |
|
Description: r.Description, |
|
ManagingEditor: author, |
|
PubDate: pub, |
|
LastBuildDate: build, |
|
Copyright: r.Copyright, |
|
Image: image, |
|
} |
|
for _, i := range r.Items { |
|
channel.Items = append(channel.Items, newRssItem(i)) |
|
} |
|
return channel |
|
} |
|
|
|
// FeedXml returns an XML-Ready object for an Rss object |
|
func (r *Rss) FeedXml() interface{} { |
|
// only generate version 2.0 feeds for now |
|
return r.RssFeed().FeedXml() |
|
|
|
} |
|
|
|
// FeedXml returns an XML-ready object for an RssFeed object |
|
func (r *RssFeed) FeedXml() interface{} { |
|
return &RssFeedXml{ |
|
Version: "2.0", |
|
Channel: r, |
|
ContentNamespace: "http://purl.org/rss/1.0/modules/content/", |
|
} |
|
}
|
|
|