gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }