Django

Code

Changeset 1194

Show
Ignore:
Timestamp:
11/11/05 21:44:53 (3 years ago)
Author:
adrian
Message:

Completely refactored legacy RSS framework to the new django.contrib.syndication package. Also added Atom support, changed the way feeds are registered and added documentation for the whole lot. This is backwards-incompatible, but the RSS framework had not yet been documented, so this should only affect tinkerers and WorldOnline. Fixes #329, #498, #502 and #554. Thanks for various patches/ideas to alastair, ismael, hugo, eric moritz and garthk

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/contrib/syndication/views.py

    r1131 r1194  
    1 from django.core import rs
     1from django.contrib.syndication import feed
    22from django.core.exceptions import Http404 
    33from django.utils.httpwrappers import HttpResponse 
    44 
    5 def feed(request, slug, param=None): 
     5def feed(request, url, feed_dict=None): 
     6    if not feed_dict: 
     7        raise Http404, "No feeds are registered." 
     8 
    69    try: 
    7         f = rss.get_registered_feed(slug).get_feed(param) 
    8     except (rss.FeedIsNotRegistered, rss.FeedDoesNotExist): 
    9         raise Http404 
     10        slug, param = url.split('/', 1) 
     11    except ValueError: 
     12        slug, param = url, '' 
     13 
     14    try: 
     15        f = feed_dict[slug] 
     16    except KeyError: 
     17        raise Http404, "Slug %r isn't registered." % slug 
     18 
     19    try: 
     20        feedgen = f(slug).get_feed(param) 
     21    except feeds.FeedDoesNotExist: 
     22        raise Http404, "Invalid feed parameters. Slug %r is valid, but other parameters, or lack thereof, are not." % slug 
     23 
    1024    response = HttpResponse(mimetype='application/xml') 
    11     f.write(response, 'utf-8') 
     25    feedgen.write(response, 'utf-8') 
    1226    return response 
  • django/trunk/django/utils/feedgenerator.py

    r641 r1194  
    2020 
    2121from django.utils.xmlutils import SimplerXMLGenerator 
     22import datetime, re, time 
     23import email.Utils 
     24from xml.dom import minidom 
     25from xml.parsers.expat import ExpatError 
     26 
     27def rfc2822_date(date): 
     28    return email.Utils.formatdate(time.mktime(date.timetuple())) 
     29 
     30def get_tag_uri(url, date): 
     31    "Creates a TagURI. See http://diveintomark.org/archives/2004/05/28/howto-atom-id" 
     32    tag = re.sub('^http://', '', url) 
     33    if date is not None: 
     34        tag = re.sub('/', ',%s:/' % date.strftime('%Y-%m-%d'), tag, 1) 
     35    tag = re.sub('#', '/', tag) 
     36    return 'tag:' + tag 
    2237 
    2338class SyndicationFeed: 
    2439    "Base class for all syndication feeds. Subclasses should provide write()" 
    25     def __init__(self, title, link, description, language=None): 
    26         self.feed_info = { 
     40    def __init__(self, title, link, description, language=None, author_email=None, 
     41            author_name=None, author_link=None, subtitle=None, categories=None): 
     42        self.feed = { 
    2743            'title': title, 
    2844            'link': link, 
    2945            'description': description, 
    3046            'language': language, 
     47            'author_email': author_email, 
     48            'author_name': author_name, 
     49            'author_link': author_link, 
     50            'subtitle': subtitle, 
     51            'categories': categories or (), 
    3152        } 
    3253        self.items = [] 
    3354 
    3455    def add_item(self, title, link, description, author_email=None, 
    35         author_name=None, pubdate=None, comments=None, unique_id=None, 
    36         enclosure=None, categories=None): 
     56        author_name=None, pubdate=None, comments=None, 
     57        unique_id=None, enclosure=None, categories=()): 
    3758        """ 
    3859        Adds an item to the feed. All args are expected to be Python Unicode 
     
    5071            'unique_id': unique_id, 
    5172            'enclosure': enclosure, 
    52             'categories': categories or []
     73            'categories': categories or ()
    5374        }) 
    5475 
     
    7192        self.write(s, encoding) 
    7293        return s.getvalue() 
     94 
     95    def latest_post_date(self): 
     96        """ 
     97        Returns the latest item's pubdate. If none of them have a pubdate, 
     98        this returns the current date/time. 
     99        """ 
     100        updates = [i['pubdate'] for i in self.items if i['pubdate'] is not None] 
     101        if len(updates) > 0: 
     102            updates.sort() 
     103            return updates[-1] 
     104        else: 
     105            return datetime.datetime.now() 
    73106 
    74107class Enclosure: 
     
    82115        handler = SimplerXMLGenerator(outfile, encoding) 
    83116        handler.startDocument() 
    84         self.writeRssElement(handler) 
    85         self.writeChannelElement(handler) 
    86         for item in self.items: 
    87             self.writeRssItem(handler, item) 
     117        handler.startElement(u"rss", {u"version": self._version}) 
     118        handler.startElement(u"channel", {}) 
     119        handler.addQuickElement(u"title", self.feed['title']) 
     120        handler.addQuickElement(u"link", self.feed['link']) 
     121        handler.addQuickElement(u"description", self.feed['description']) 
     122        if self.feed['language'] is not None: 
     123            handler.addQuickElement(u"language", self.feed['language']) 
     124        self.write_items(handler) 
    88125        self.endChannelElement(handler) 
    89         self.endRssElement(handler) 
    90  
    91     def writeRssElement(self, handler): 
    92         "Adds the <rss> element to handler, taking care of versioning, etc." 
    93         raise NotImplementedError 
    94  
    95     def endRssElement(self, handler): 
    96         "Ends the <rss> element." 
    97126        handler.endElement(u"rss") 
    98  
    99     def writeChannelElement(self, handler): 
    100         handler.startElement(u"channel", {}) 
    101         handler.addQuickElement(u"title", self.feed_info['title'], {}) 
    102         handler.addQuickElement(u"link", self.feed_info['link'], {}) 
    103         handler.addQuickElement(u"description", self.feed_info['description'], {}) 
    104         if self.feed_info['language'] is not None: 
    105             handler.addQuickElement(u"language", self.feed_info['language'], {}) 
    106127 
    107128    def endChannelElement(self, handler): 
     
    109130 
    110131class RssUserland091Feed(RssFeed): 
    111     def writeRssElement(self, handler): 
    112         handler.startElement(u"rss", {u"version": u"0.91"}) 
    113  
    114     def writeRssItem(self, handler, item): 
    115         handler.startElement(u"item", {}) 
    116         handler.addQuickElement(u"title", item['title'], {}) 
    117         handler.addQuickElement(u"link", item['link'], {}) 
    118         if item['description'] is not None: 
    119             handler.addQuickElement(u"description", item['description'], {}) 
    120         handler.endElement(u"item") 
     132    _version = u"0.91" 
     133    def write_items(self, handler): 
     134        for item in self.items: 
     135            handler.startElement(u"item", {}) 
     136            handler.addQuickElement(u"title", item['title']) 
     137            handler.addQuickElement(u"link", item['link']) 
     138            if item['description'] is not None: 
     139                handler.addQuickElement(u"description", item['description']) 
     140            handler.endElement(u"item") 
    121141 
    122142class Rss201rev2Feed(RssFeed): 
    123143    # Spec: http://blogs.law.harvard.edu/tech/rss 
    124     def writeRssElement(self, handler): 
    125         handler.startElement(u"rss", {u"version": u"2.0"}) 
    126  
    127     def writeRssItem(self, handler, item): 
    128         handler.startElement(u"item", {}) 
    129         handler.addQuickElement(u"title", item['title'], {}) 
    130         handler.addQuickElement(u"link", item['link'], {}) 
    131         if item['description'] is not None: 
    132             handler.addQuickElement(u"description", item['description'], {}) 
    133         if item['author_email'] is not None and item['author_name'] is not None: 
    134             handler.addQuickElement(u"author", u"%s (%s)" % \ 
    135                 (item['author_email'], item['author_name']), {}) 
    136         if item['pubdate'] is not None: 
    137             handler.addQuickElement(u"pubDate", item['pubdate'].strftime('%a, %d %b %Y %H:%M:%S %Z'), {}) 
    138         if item['comments'] is not None: 
    139             handler.addQuickElement(u"comments", item['comments'], {}) 
    140         if item['unique_id'] is not None: 
    141             handler.addQuickElement(u"guid", item['unique_id'], {}) 
    142         if item['enclosure'] is not None: 
    143             handler.addQuickElement(u"enclosure", '', 
    144                 {u"url": item['enclosure'].url, u"length": item['enclosure'].length, 
    145                     u"type": item['enclosure'].mime_type}) 
    146         for cat in item['categories']: 
    147             handler.addQuickElement(u"category", cat, {}) 
    148         handler.endElement(u"item") 
     144    _version = u"2.0" 
     145    def write_items(self, handler): 
     146        for item in self.items: 
     147            handler.startElement(u"item", {}) 
     148            handler.addQuickElement(u"title", item['title']) 
     149            handler.addQuickElement(u"link", item['link']) 
     150            if item['description'] is not None: 
     151                handler.addQuickElement(u"description", item['description']) 
     152 
     153            # Author information. 
     154            if item['author_email'] is not None and item['author_name'] is not None: 
     155                handler.addQuickElement(u"author", u"%s (%s)" % \ 
     156                    (item['author_email'], item['author_name'])) 
     157 
     158            if item['pubdate'] is not None: 
     159                handler.addQuickElement(u"pubDate", rfc2822_date(item['pubdate']).decode('ascii')) 
     160            if item['comments'] is not None: 
     161                handler.addQuickElement(u"comments", item['comments']) 
     162            if item['unique_id'] is not None: 
     163                handler.addQuickElement(u"guid", item['unique_id']) 
     164 
     165            # Enclosure. 
     166            if item['enclosure'] is not None: 
     167                handler.addQuickElement(u"enclosure", '', 
     168                    {u"url": item['enclosure'].url, u"length": item['enclosure'].length, 
     169                        u"type": item['enclosure'].mime_type}) 
     170 
     171            # Categories. 
     172            for cat in item['categories']: 
     173                handler.addQuickElement(u"category", cat) 
     174 
     175            handler.endElement(u"item") 
     176 
     177class Atom1Feed(SyndicationFeed): 
     178    # Spec: http://atompub.org/2005/07/11/draft-ietf-atompub-format-10.html 
     179    ns = u"http://www.w3.org/2005/Atom" 
     180    def write(self, outfile, encoding): 
     181        handler = SimplerXMLGenerator(outfile, encoding) 
     182        handler.startDocument() 
     183        if self.feed['language'] is not None: 
     184            handler.startElement(u"feed", {u"xmlns": self.ns, u"xml:lang": self.feed['language']}) 
     185        else: 
     186            handler.startElement(u"feed", {u"xmlns": self.ns}) 
     187        handler.addQuickElement(u"title", self.feed['title']) 
     188        handler.addQuickElement(u"link", "", {u"href": self.feed['link']}) 
     189        handler.addQuickElement(u"id", self.feed['link']) 
     190        handler.addQuickElement(u"updated", rfc2822_date(self.latest_post_date()).decode('ascii')) 
     191        if self.feed['author_name'] is not None: 
     192            handler.startElement(u"author", {}) 
     193            handler.addQuickElement(u"name", self.feed['author_name']) 
     194            if self.feed['author_email'] is not None: 
     195                handler.addQuickElement(u"email", self.feed['author_email']) 
     196            if self.feed['author_link'] is not None: 
     197                handler.addQuickElement(u"uri", self.feed['author_link']) 
     198            handler.endElement(u"author") 
     199        if self.feed['subtitle'] is not None: 
     200            handler.addQuickElement(u"subtitle", self.feed['subtitle']) 
     201        for cat in self.feed['categories']: 
     202            handler.addQuickElement(u"category", "", {u"term": cat}) 
     203        self.write_items(handler) 
     204        handler.endElement(u"feed") 
     205 
     206    def write_items(self, handler): 
     207        for item in self.items: 
     208            handler.startElement(u"entry", {}) 
     209            handler.addQuickElement(u"title", item['title']) 
     210            handler.addQuickElement(u"link", item['link']) 
     211            if item['pubdate'] is not None: 
     212                handler.addQuickElement(u"updated", rfc2822_date(item['pubdate']).decode('ascii')) 
     213 
     214            # Author information. 
     215            if item['author_name'] is not None: 
     216                handler.startElement(u"author", {}) 
     217                handler.addQuickElement(u"name", item['author_name']) 
     218                if item['author_email'] is not None: 
     219                    handler.addQuickElement(u"email", item['author_email']) 
     220                handler.endElement(u"author") 
     221 
     222            # Unique ID. 
     223            if item['unique_id'] is not None: 
     224                unique_id = item['unique_id'] 
     225            else: 
     226                unique_id = get_tag_uri(item['link'], item['pubdate']) 
     227            handler.addQuickElement(u"id", unique_id) 
     228 
     229            # Summary. 
     230            if item['description'] is not None: 
     231                handler.addQuickElement(u"summary", item['description'], {u"type": u"html"}) 
     232 
     233            # Enclosure. 
     234            if item['enclosure'] is not None: 
     235                handler.addQuickElement(u"link", '', 
     236                    {u"rel": u"enclosure", 
     237                     u"href": item['enclosure'].url, 
     238                     u"length": item['enclosure'].length, 
     239                     u"type": item['enclosure'].mime_type}) 
     240 
     241            # Categories: 
     242            for cat in item['categories']: 
     243                handler.addQuickElement(u"category", u"", {u"term": cat}) 
     244 
     245            handler.endElement(u"entry") 
    149246 
    150247# This isolates the decision of what the system default is, so calling code can 
    151 # do "feedgenerator.DefaultRssFeed" instead of "feedgenerator.Rss201rev2Feed". 
    152 DefaultRssFeed = Rss201rev2Feed 
     248# do "feedgenerator.DefaultFeed" instead of "feedgenerator.Rss201rev2Feed". 
     249DefaultFeed = Rss201rev2Feed