mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 11:22:27 -05:00 
			
		
		
		
	* start adding rss functionality * add gorilla/feeds dependency * first bash at building rss feed still needs work, this is an interim commit * tidy up a bit * add publicOnly option to GetAccountLastPosted * implement rss endpoint * fix test * add initial user docs for rss * update rss logo * docs update * add rssFeed to frontend * feed -> feed.rss * enableRSS * increase rss logo size a lil bit * add rss toggle * move emojify to text package * fiddle with rss feed formatting * add Text field to test statuses * move status to rss item to typeconverter * update bun schema for enablerss * simplify 304 checking * assume account not rss * update tests * update swagger docs * allow more characters in title, trim nicer * update last posted to be more consistent
		
			
				
	
	
		
			168 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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/",
 | |
| 	}
 | |
| }
 |