17 |
17 |
18 For definitions of the different versions of RSS, see: |
18 For definitions of the different versions of RSS, see: |
19 http://diveintomark.org/archives/2004/02/04/incompatible-rss |
19 http://diveintomark.org/archives/2004/02/04/incompatible-rss |
20 """ |
20 """ |
21 |
21 |
22 import re |
|
23 import datetime |
22 import datetime |
|
23 import urlparse |
24 from django.utils.xmlutils import SimplerXMLGenerator |
24 from django.utils.xmlutils import SimplerXMLGenerator |
25 from django.utils.encoding import force_unicode, iri_to_uri |
25 from django.utils.encoding import force_unicode, iri_to_uri |
26 |
26 |
27 def rfc2822_date(date): |
27 def rfc2822_date(date): |
28 # We do this ourselves to be timezone aware, email.Utils is not tz aware. |
28 # We do this ourselves to be timezone aware, email.Utils is not tz aware. |
44 return time_str + "%+03d:%02d" % (hour, minute) |
44 return time_str + "%+03d:%02d" % (hour, minute) |
45 else: |
45 else: |
46 return date.strftime('%Y-%m-%dT%H:%M:%SZ') |
46 return date.strftime('%Y-%m-%dT%H:%M:%SZ') |
47 |
47 |
48 def get_tag_uri(url, date): |
48 def get_tag_uri(url, date): |
49 "Creates a TagURI. See http://diveintomark.org/archives/2004/05/28/howto-atom-id" |
49 """ |
50 tag = re.sub('^http://', '', url) |
50 Creates a TagURI. |
|
51 |
|
52 See http://diveintomark.org/archives/2004/05/28/howto-atom-id |
|
53 """ |
|
54 url_split = urlparse.urlparse(url) |
|
55 |
|
56 # Python 2.4 didn't have named attributes on split results or the hostname. |
|
57 hostname = getattr(url_split, 'hostname', url_split[1].split(':')[0]) |
|
58 path = url_split[2] |
|
59 fragment = url_split[5] |
|
60 |
|
61 d = '' |
51 if date is not None: |
62 if date is not None: |
52 tag = re.sub('/', ',%s:/' % date.strftime('%Y-%m-%d'), tag, 1) |
63 d = ',%s' % date.strftime('%Y-%m-%d') |
53 tag = re.sub('#', '/', tag) |
64 return u'tag:%s%s:%s/%s' % (hostname, d, path, fragment) |
54 return u'tag:' + tag |
|
55 |
65 |
56 class SyndicationFeed(object): |
66 class SyndicationFeed(object): |
57 "Base class for all syndication feeds. Subclasses should provide write()" |
67 "Base class for all syndication feeds. Subclasses should provide write()" |
58 def __init__(self, title, link, description, language=None, author_email=None, |
68 def __init__(self, title, link, description, language=None, author_email=None, |
59 author_name=None, author_link=None, subtitle=None, categories=None, |
69 author_name=None, author_link=None, subtitle=None, categories=None, |
60 feed_url=None, feed_copyright=None, feed_guid=None, ttl=None, **kwargs): |
70 feed_url=None, feed_copyright=None, feed_guid=None, ttl=None, **kwargs): |
61 to_unicode = lambda s: force_unicode(s, strings_only=True) |
71 to_unicode = lambda s: force_unicode(s, strings_only=True) |
62 if categories: |
72 if categories: |
63 categories = [force_unicode(c) for c in categories] |
73 categories = [force_unicode(c) for c in categories] |
|
74 if ttl is not None: |
|
75 # Force ints to unicode |
|
76 ttl = force_unicode(ttl) |
64 self.feed = { |
77 self.feed = { |
65 'title': to_unicode(title), |
78 'title': to_unicode(title), |
66 'link': iri_to_uri(link), |
79 'link': iri_to_uri(link), |
67 'description': to_unicode(description), |
80 'description': to_unicode(description), |
68 'language': to_unicode(language), |
81 'language': to_unicode(language), |
89 enclosure, which is an instance of the Enclosure class. |
102 enclosure, which is an instance of the Enclosure class. |
90 """ |
103 """ |
91 to_unicode = lambda s: force_unicode(s, strings_only=True) |
104 to_unicode = lambda s: force_unicode(s, strings_only=True) |
92 if categories: |
105 if categories: |
93 categories = [to_unicode(c) for c in categories] |
106 categories = [to_unicode(c) for c in categories] |
|
107 if ttl is not None: |
|
108 # Force ints to unicode |
|
109 ttl = force_unicode(ttl) |
94 item = { |
110 item = { |
95 'title': to_unicode(title), |
111 'title': to_unicode(title), |
96 'link': iri_to_uri(link), |
112 'link': iri_to_uri(link), |
97 'description': to_unicode(description), |
113 'description': to_unicode(description), |
98 'author_email': to_unicode(author_email), |
114 'author_email': to_unicode(author_email), |
184 self.write_items(handler) |
200 self.write_items(handler) |
185 self.endChannelElement(handler) |
201 self.endChannelElement(handler) |
186 handler.endElement(u"rss") |
202 handler.endElement(u"rss") |
187 |
203 |
188 def rss_attributes(self): |
204 def rss_attributes(self): |
189 return {u"version": self._version} |
205 return {u"version": self._version, |
|
206 u"xmlns:atom": u"http://www.w3.org/2005/Atom"} |
190 |
207 |
191 def write_items(self, handler): |
208 def write_items(self, handler): |
192 for item in self.items: |
209 for item in self.items: |
193 handler.startElement(u'item', self.item_attributes(item)) |
210 handler.startElement(u'item', self.item_attributes(item)) |
194 self.add_item_elements(handler, item) |
211 self.add_item_elements(handler, item) |
196 |
213 |
197 def add_root_elements(self, handler): |
214 def add_root_elements(self, handler): |
198 handler.addQuickElement(u"title", self.feed['title']) |
215 handler.addQuickElement(u"title", self.feed['title']) |
199 handler.addQuickElement(u"link", self.feed['link']) |
216 handler.addQuickElement(u"link", self.feed['link']) |
200 handler.addQuickElement(u"description", self.feed['description']) |
217 handler.addQuickElement(u"description", self.feed['description']) |
|
218 handler.addQuickElement(u"atom:link", None, {u"rel": u"self", u"href": self.feed['feed_url']}) |
201 if self.feed['language'] is not None: |
219 if self.feed['language'] is not None: |
202 handler.addQuickElement(u"language", self.feed['language']) |
220 handler.addQuickElement(u"language", self.feed['language']) |
203 for cat in self.feed['categories']: |
221 for cat in self.feed['categories']: |
204 handler.addQuickElement(u"category", cat) |
222 handler.addQuickElement(u"category", cat) |
205 if self.feed['feed_copyright'] is not None: |
223 if self.feed['feed_copyright'] is not None: |
233 handler.addQuickElement(u"author", "%s (%s)" % \ |
251 handler.addQuickElement(u"author", "%s (%s)" % \ |
234 (item['author_email'], item['author_name'])) |
252 (item['author_email'], item['author_name'])) |
235 elif item["author_email"]: |
253 elif item["author_email"]: |
236 handler.addQuickElement(u"author", item["author_email"]) |
254 handler.addQuickElement(u"author", item["author_email"]) |
237 elif item["author_name"]: |
255 elif item["author_name"]: |
238 handler.addQuickElement(u"dc:creator", item["author_name"], {"xmlns:dc": u"http://purl.org/dc/elements/1.1/"}) |
256 handler.addQuickElement(u"dc:creator", item["author_name"], {u"xmlns:dc": u"http://purl.org/dc/elements/1.1/"}) |
239 |
257 |
240 if item['pubdate'] is not None: |
258 if item['pubdate'] is not None: |
241 handler.addQuickElement(u"pubDate", rfc2822_date(item['pubdate']).decode('utf-8')) |
259 handler.addQuickElement(u"pubDate", rfc2822_date(item['pubdate']).decode('utf-8')) |
242 if item['comments'] is not None: |
260 if item['comments'] is not None: |
243 handler.addQuickElement(u"comments", item['comments']) |
261 handler.addQuickElement(u"comments", item['comments']) |