diff --git a/kallithea/lib/feeds.py b/kallithea/lib/feeds.py
--- a/kallithea/lib/feeds.py
+++ b/kallithea/lib/feeds.py
@@ -22,38 +22,131 @@ Shared code for providing RSS and ATOM f
import datetime
import re
-from webhelpers import feedgenerator
+import mako.template
language = 'en-us'
ttl = "5"
+
+# From ``django.utils.feedgenerator`` via webhelpers.feedgenerator
+def rfc2822_date(date):
+ # We do this ourselves to be timezone aware, email.Utils is not tz aware.
+ if getattr(date, "tzinfo", False):
+ time_str = date.strftime('%a, %d %b %Y %H:%M:%S ')
+ offset = date.tzinfo.utcoffset(date)
+ timezone = (offset.days * 24 * 60) + (offset.seconds / 60)
+ hour, minute = divmod(timezone, 60)
+ return time_str + "%+03d%02d" % (hour, minute)
+ else:
+ return date.strftime('%a, %d %b %Y %H:%M:%S -0000')
+
+# From ``django.utils.feedgenerator`` via webhelpers.feedgenerator
+def rfc3339_date(date):
+ if getattr(date, "tzinfo", False):
+ time_str = date.strftime('%Y-%m-%dT%H:%M:%S')
+ offset = date.tzinfo.utcoffset(date)
+ timezone = (offset.days * 24 * 60) + (offset.seconds / 60)
+ hour, minute = divmod(timezone, 60)
+ return time_str + "%+03d:%02d" % (hour, minute)
+ else:
+ return date.strftime('%Y-%m-%dT%H:%M:%SZ')
+
+# From ``django.utils.feedgenerator`` via webhelpers.feedgenerator
+def get_tag_uri(url, date):
+ "Creates a TagURI. See http://diveintomark.org/archives/2004/05/28/howto-atom-id"
+ tag = re.sub('^http://', '', url)
+ if date is not None:
+ tag = re.sub('/', ',%s:/' % date.strftime('%Y-%m-%d'), tag, 1)
+ tag = re.sub('#', '/', tag)
+ return u'tag:' + tag
+
+
+class Attributes(object):
+ """Simple namespace for attribute dict access in mako and elsewhere"""
+ def __init__(self, a_dict):
+ self.__dict__ = a_dict
+
+
class _Feeder(object):
content_type = None
- feed_factory = None # a webhelpers.feedgenerator
+ template = None # subclass must provide a mako.template.Template
@classmethod
def render(cls, header, entries):
- feed = cls.feed_factory(
+ try:
+ latest_pubdate = max(
+ pubdate for pubdate in (e.get('pubdate') for e in entries)
+ if pubdate
+ )
+ except ValueError: # max() arg is an empty sequence ... or worse
+ latest_pubdate = datetime.datetime.now()
+
+ return cls.template.render(
language=language,
ttl=ttl, # rss only
+ latest_pubdate=latest_pubdate,
+ rfc2822_date=rfc2822_date, # for RSS
+ rfc3339_date=rfc3339_date, # for Atom
+ get_tag_uri=get_tag_uri,
+ entries=[Attributes(e) for e in entries],
**header
)
- for e in entries:
- feed.add_item(**e)
- return feed.writeString('utf-8')
class AtomFeed(_Feeder):
content_type = 'application/atom+xml'
- feed_factory = feedgenerator.Atom1Feed
+ template = mako.template.Template('''\
+
+
+ ${title}
+
+ ${link}
+ ${rfc3339_date(latest_pubdate)}
+ % for entry in entries:
+
+ ${entry.title}
+
+ ${rfc3339_date(entry.pubdate)}
+ ${rfc3339_date(entry.pubdate)}
+
+ ${entry.author_name}
+ ${entry.author_email}
+
+ ${get_tag_uri(entry.link, entry.pubdate)}
+ ${entry.description}
+
+ % endfor
+
+''', default_filters=['x'], output_encoding='utf-8', encoding_errors='replace')
class RssFeed(_Feeder):
content_type = 'application/rss+xml'
- feed_factory = feedgenerator.Rss201rev2Feed
+ template = mako.template.Template('''\
+
+
+
+ ${title}
+ ${link}
+ ${description}
+ ${language}
+ ${rfc2822_date(latest_pubdate)}
+ ${ttl}
+ % for entry in entries:
+ -
+ ${entry.title}
+ ${entry.link}
+ ${entry.description}
+ ${entry.author_email} (${entry.author_name})
+ ${rfc2822_date(entry.pubdate)}
+
+ % endfor
+
+
+''', default_filters=['x'], output_encoding='utf-8', encoding_errors='replace')