web/lib/django/contrib/syndication/views.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
--- a/web/lib/django/contrib/syndication/views.py	Wed May 19 17:43:59 2010 +0200
+++ b/web/lib/django/contrib/syndication/views.py	Tue May 25 02:43:45 2010 +0200
@@ -1,9 +1,192 @@
-from django.contrib.syndication import feeds
+import datetime
+from django.conf import settings
+from django.contrib.sites.models import Site, RequestSite
+from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
 from django.http import HttpResponse, Http404
+from django.template import loader, Template, TemplateDoesNotExist, RequestContext
+from django.utils import feedgenerator, tzinfo
+from django.utils.encoding import force_unicode, iri_to_uri, smart_unicode
+from django.utils.html import escape
+
+def add_domain(domain, url):
+    if not (url.startswith('http://')
+            or url.startswith('https://')
+            or url.startswith('mailto:')):
+        # 'url' must already be ASCII and URL-quoted, so no need for encoding
+        # conversions here.
+        url = iri_to_uri(u'http://%s%s' % (domain, url))
+    return url
+
+class FeedDoesNotExist(ObjectDoesNotExist):
+    pass
+
+
+class Feed(object):
+    feed_type = feedgenerator.DefaultFeed
+    title_template = None
+    description_template = None
+
+    def __call__(self, request, *args, **kwargs):
+        try:
+            obj = self.get_object(request, *args, **kwargs)
+        except ObjectDoesNotExist:
+            raise Http404('Feed object does not exist.')
+        feedgen = self.get_feed(obj, request)
+        response = HttpResponse(mimetype=feedgen.mime_type)
+        feedgen.write(response, 'utf-8')
+        return response
+
+    def item_title(self, item):
+        # Titles should be double escaped by default (see #6533)
+        return escape(force_unicode(item))
+
+    def item_description(self, item):
+        return force_unicode(item)
+
+    def item_link(self, item):
+        try:
+            return item.get_absolute_url()
+        except AttributeError:
+            raise ImproperlyConfigured('Give your %s class a get_absolute_url() method, or define an item_link() method in your Feed class.' % item.__class__.__name__)
+
+    def __get_dynamic_attr(self, attname, obj, default=None):
+        try:
+            attr = getattr(self, attname)
+        except AttributeError:
+            return default
+        if callable(attr):
+            # Check func_code.co_argcount rather than try/excepting the
+            # function and catching the TypeError, because something inside
+            # the function may raise the TypeError. This technique is more
+            # accurate.
+            if hasattr(attr, 'func_code'):
+                argcount = attr.func_code.co_argcount
+            else:
+                argcount = attr.__call__.func_code.co_argcount
+            if argcount == 2: # one argument is 'self'
+                return attr(obj)
+            else:
+                return attr()
+        return attr
+
+    def feed_extra_kwargs(self, obj):
+        """
+        Returns an extra keyword arguments dictionary that is used when
+        initializing the feed generator.
+        """
+        return {}
+
+    def item_extra_kwargs(self, item):
+        """
+        Returns an extra keyword arguments dictionary that is used with
+        the `add_item` call of the feed generator.
+        """
+        return {}
+
+    def get_object(self, request, *args, **kwargs):
+        return None
+
+    def get_feed(self, obj, request):
+        """
+        Returns a feedgenerator.DefaultFeed object, fully populated, for
+        this feed. Raises FeedDoesNotExist for invalid parameters.
+        """
+        if Site._meta.installed:
+            current_site = Site.objects.get_current()
+        else:
+            current_site = RequestSite(request)
+
+        link = self.__get_dynamic_attr('link', obj)
+        link = add_domain(current_site.domain, link)
+
+        feed = self.feed_type(
+            title = self.__get_dynamic_attr('title', obj),
+            subtitle = self.__get_dynamic_attr('subtitle', obj),
+            link = link,
+            description = self.__get_dynamic_attr('description', obj),
+            language = settings.LANGUAGE_CODE.decode(),
+            feed_url = add_domain(current_site.domain,
+                    self.__get_dynamic_attr('feed_url', obj) or request.path),
+            author_name = self.__get_dynamic_attr('author_name', obj),
+            author_link = self.__get_dynamic_attr('author_link', obj),
+            author_email = self.__get_dynamic_attr('author_email', obj),
+            categories = self.__get_dynamic_attr('categories', obj),
+            feed_copyright = self.__get_dynamic_attr('feed_copyright', obj),
+            feed_guid = self.__get_dynamic_attr('feed_guid', obj),
+            ttl = self.__get_dynamic_attr('ttl', obj),
+            **self.feed_extra_kwargs(obj)
+        )
+
+        title_tmp = None
+        if self.title_template is not None:
+            try:
+                title_tmp = loader.get_template(self.title_template)
+            except TemplateDoesNotExist:
+                pass
+
+        description_tmp = None
+        if self.description_template is not None:
+            try:
+                description_tmp = loader.get_template(self.description_template)
+            except TemplateDoesNotExist:
+                pass
+
+        for item in self.__get_dynamic_attr('items', obj):
+            if title_tmp is not None:
+                title = title_tmp.render(RequestContext(request, {'obj': item, 'site': current_site}))
+            else:
+                title = self.__get_dynamic_attr('item_title', item)
+            if description_tmp is not None:
+                description = description_tmp.render(RequestContext(request, {'obj': item, 'site': current_site}))
+            else:
+                description = self.__get_dynamic_attr('item_description', item)
+            link = add_domain(current_site.domain, self.__get_dynamic_attr('item_link', item))
+            enc = None
+            enc_url = self.__get_dynamic_attr('item_enclosure_url', item)
+            if enc_url:
+                enc = feedgenerator.Enclosure(
+                    url = smart_unicode(enc_url),
+                    length = smart_unicode(self.__get_dynamic_attr('item_enclosure_length', item)),
+                    mime_type = smart_unicode(self.__get_dynamic_attr('item_enclosure_mime_type', item))
+                )
+            author_name = self.__get_dynamic_attr('item_author_name', item)
+            if author_name is not None:
+                author_email = self.__get_dynamic_attr('item_author_email', item)
+                author_link = self.__get_dynamic_attr('item_author_link', item)
+            else:
+                author_email = author_link = None
+
+            pubdate = self.__get_dynamic_attr('item_pubdate', item)
+            if pubdate and not pubdate.tzinfo:
+                ltz = tzinfo.LocalTimezone(pubdate)
+                pubdate = pubdate.replace(tzinfo=ltz)
+
+            feed.add_item(
+                title = title,
+                link = link,
+                description = description,
+                unique_id = self.__get_dynamic_attr('item_guid', item, link),
+                enclosure = enc,
+                pubdate = pubdate,
+                author_name = author_name,
+                author_email = author_email,
+                author_link = author_link,
+                categories = self.__get_dynamic_attr('item_categories', item),
+                item_copyright = self.__get_dynamic_attr('item_copyright', item),
+                **self.item_extra_kwargs(item)
+            )
+        return feed
+
 
 def feed(request, url, feed_dict=None):
+    """Provided for backwards compatibility."""
+    import warnings
+    warnings.warn('The syndication feed() view is deprecated. Please use the '
+                  'new class based view API.',
+                  category=PendingDeprecationWarning)
+
     if not feed_dict:
-        raise Http404, "No feeds are registered."
+        raise Http404("No feeds are registered.")
 
     try:
         slug, param = url.split('/', 1)
@@ -13,13 +196,14 @@
     try:
         f = feed_dict[slug]
     except KeyError:
-        raise Http404, "Slug %r isn't registered." % slug
+        raise Http404("Slug %r isn't registered." % slug)
 
     try:
         feedgen = f(slug, request).get_feed(param)
-    except feeds.FeedDoesNotExist:
-        raise Http404, "Invalid feed parameters. Slug %r is valid, but other parameters, or lack thereof, are not." % slug
+    except FeedDoesNotExist:
+        raise Http404("Invalid feed parameters. Slug %r is valid, but other parameters, or lack thereof, are not." % slug)
 
     response = HttpResponse(mimetype=feedgen.mime_type)
     feedgen.write(response, 'utf-8')
     return response
+