web/lib/django/contrib/syndication/feeds.py
changeset 0 0d40e90630ef
child 29 cc9b7e14412b
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 from datetime import datetime, timedelta
       
     2 
       
     3 from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
       
     4 from django.template import loader, Template, TemplateDoesNotExist
       
     5 from django.contrib.sites.models import Site, RequestSite
       
     6 from django.utils import feedgenerator
       
     7 from django.utils.tzinfo import FixedOffset
       
     8 from django.utils.encoding import smart_unicode, iri_to_uri
       
     9 from django.conf import settings         
       
    10 from django.template import RequestContext
       
    11 
       
    12 def add_domain(domain, url):
       
    13     if not (url.startswith('http://') or url.startswith('https://')):
       
    14         # 'url' must already be ASCII and URL-quoted, so no need for encoding
       
    15         # conversions here.
       
    16         url = iri_to_uri(u'http://%s%s' % (domain, url))
       
    17     return url
       
    18 
       
    19 class FeedDoesNotExist(ObjectDoesNotExist):
       
    20     pass
       
    21 
       
    22 class Feed(object):
       
    23     item_pubdate = None
       
    24     item_enclosure_url = None
       
    25     feed_type = feedgenerator.DefaultFeed
       
    26     feed_url = None
       
    27     title_template = None
       
    28     description_template = None
       
    29 
       
    30     def __init__(self, slug, request):
       
    31         self.slug = slug
       
    32         self.request = request
       
    33         self.feed_url = self.feed_url or request.path
       
    34         self.title_template_name = self.title_template or ('feeds/%s_title.html' % slug)
       
    35         self.description_template_name = self.description_template or ('feeds/%s_description.html' % slug)
       
    36 
       
    37     def item_link(self, item):
       
    38         try:
       
    39             return item.get_absolute_url()
       
    40         except AttributeError:
       
    41             raise ImproperlyConfigured, "Give your %s class a get_absolute_url() method, or define an item_link() method in your Feed class." % item.__class__.__name__
       
    42 
       
    43     def __get_dynamic_attr(self, attname, obj, default=None):
       
    44         try:
       
    45             attr = getattr(self, attname)
       
    46         except AttributeError:
       
    47             return default
       
    48         if callable(attr):
       
    49             # Check func_code.co_argcount rather than try/excepting the
       
    50             # function and catching the TypeError, because something inside
       
    51             # the function may raise the TypeError. This technique is more
       
    52             # accurate.
       
    53             if hasattr(attr, 'func_code'):
       
    54                 argcount = attr.func_code.co_argcount
       
    55             else:
       
    56                 argcount = attr.__call__.func_code.co_argcount
       
    57             if argcount == 2: # one argument is 'self'
       
    58                 return attr(obj)
       
    59             else:
       
    60                 return attr()
       
    61         return attr
       
    62 
       
    63     def feed_extra_kwargs(self, obj):
       
    64         """
       
    65         Returns an extra keyword arguments dictionary that is used when
       
    66         initializing the feed generator.
       
    67         """
       
    68         return {}
       
    69 
       
    70     def item_extra_kwargs(self, item):
       
    71         """
       
    72         Returns an extra keyword arguments dictionary that is used with
       
    73         the `add_item` call of the feed generator.
       
    74         """
       
    75         return {}
       
    76 
       
    77     def get_object(self, bits):
       
    78         return None
       
    79 
       
    80     def get_feed(self, url=None):
       
    81         """
       
    82         Returns a feedgenerator.DefaultFeed object, fully populated, for
       
    83         this feed. Raises FeedDoesNotExist for invalid parameters.
       
    84         """
       
    85         if url:
       
    86             bits = url.split('/')
       
    87         else:
       
    88             bits = []
       
    89 
       
    90         try:
       
    91             obj = self.get_object(bits)
       
    92         except ObjectDoesNotExist:
       
    93             raise FeedDoesNotExist
       
    94 
       
    95         if Site._meta.installed:
       
    96             current_site = Site.objects.get_current()
       
    97         else:
       
    98             current_site = RequestSite(self.request)
       
    99         
       
   100         link = self.__get_dynamic_attr('link', obj)
       
   101         link = add_domain(current_site.domain, link)
       
   102 
       
   103         feed = self.feed_type(
       
   104             title = self.__get_dynamic_attr('title', obj),
       
   105             subtitle = self.__get_dynamic_attr('subtitle', obj),
       
   106             link = link,
       
   107             description = self.__get_dynamic_attr('description', obj),
       
   108             language = settings.LANGUAGE_CODE.decode(),
       
   109             feed_url = add_domain(current_site.domain,
       
   110                                   self.__get_dynamic_attr('feed_url', obj)),
       
   111             author_name = self.__get_dynamic_attr('author_name', obj),
       
   112             author_link = self.__get_dynamic_attr('author_link', obj),
       
   113             author_email = self.__get_dynamic_attr('author_email', obj),
       
   114             categories = self.__get_dynamic_attr('categories', obj),
       
   115             feed_copyright = self.__get_dynamic_attr('feed_copyright', obj),
       
   116             feed_guid = self.__get_dynamic_attr('feed_guid', obj),
       
   117             ttl = self.__get_dynamic_attr('ttl', obj),
       
   118             **self.feed_extra_kwargs(obj)
       
   119         )
       
   120 
       
   121         try:
       
   122             title_tmp = loader.get_template(self.title_template_name)
       
   123         except TemplateDoesNotExist:
       
   124             title_tmp = Template('{{ obj }}')
       
   125         try:
       
   126             description_tmp = loader.get_template(self.description_template_name)
       
   127         except TemplateDoesNotExist:
       
   128             description_tmp = Template('{{ obj }}')
       
   129 
       
   130         for item in self.__get_dynamic_attr('items', obj):
       
   131             link = add_domain(current_site.domain, self.__get_dynamic_attr('item_link', item))
       
   132             enc = None
       
   133             enc_url = self.__get_dynamic_attr('item_enclosure_url', item)
       
   134             if enc_url:
       
   135                 enc = feedgenerator.Enclosure(
       
   136                     url = smart_unicode(enc_url),
       
   137                     length = smart_unicode(self.__get_dynamic_attr('item_enclosure_length', item)),
       
   138                     mime_type = smart_unicode(self.__get_dynamic_attr('item_enclosure_mime_type', item))
       
   139                 )
       
   140             author_name = self.__get_dynamic_attr('item_author_name', item)
       
   141             if author_name is not None:
       
   142                 author_email = self.__get_dynamic_attr('item_author_email', item)
       
   143                 author_link = self.__get_dynamic_attr('item_author_link', item)
       
   144             else:
       
   145                 author_email = author_link = None
       
   146 
       
   147             pubdate = self.__get_dynamic_attr('item_pubdate', item)
       
   148             if pubdate and not pubdate.tzinfo:
       
   149                 now = datetime.now()
       
   150                 utcnow = datetime.utcnow()
       
   151 
       
   152                 # Must always subtract smaller time from larger time here.
       
   153                 if utcnow > now:
       
   154                     sign = -1
       
   155                     tzDifference = (utcnow - now)
       
   156                 else:
       
   157                     sign = 1
       
   158                     tzDifference = (now - utcnow)
       
   159 
       
   160                 # Round the timezone offset to the nearest half hour.
       
   161                 tzOffsetMinutes = sign * ((tzDifference.seconds / 60 + 15) / 30) * 30
       
   162                 tzOffset = timedelta(minutes=tzOffsetMinutes)
       
   163                 pubdate = pubdate.replace(tzinfo=FixedOffset(tzOffset))
       
   164 
       
   165             feed.add_item(
       
   166                 title = title_tmp.render(RequestContext(self.request, {'obj': item, 'site': current_site})),
       
   167                 link = link,
       
   168                 description = description_tmp.render(RequestContext(self.request, {'obj': item, 'site': current_site})),
       
   169                 unique_id = self.__get_dynamic_attr('item_guid', item, link),
       
   170                 enclosure = enc,
       
   171                 pubdate = pubdate,
       
   172                 author_name = author_name,
       
   173                 author_email = author_email,
       
   174                 author_link = author_link,
       
   175                 categories = self.__get_dynamic_attr('item_categories', item),
       
   176                 item_copyright = self.__get_dynamic_attr('item_copyright', item),
       
   177                 **self.item_extra_kwargs(item)
       
   178             )
       
   179         return feed