-try:
- import mimeparse
-except:
- mimeparse = None
-
-
-ATOM = feedgenerator.Atom1Feed.mime_type
-RSS = feedgenerator.Rss201rev2Feed.mime_type
-FEEDS = SortedDict([
- (ATOM, feedgenerator.Atom1Feed),
- (RSS, feedgenerator.Rss201rev2Feed),
-])
-FEED_CHOICES = (
- (ATOM, "Atom"),
- (RSS, "RSS"),
-)
-
-
-class FeedView(MultiView):
- """
- The FeedView expects to handle a number of different feeds for the
- same object - i.e. patterns for a blog to handle all entries or
- just entries for a certain year/month/day.
-
- This class would subclass django.contrib.syndication.views.Feed, but
- that would make it callable, which causes problems.
- """
- feed_type = models.CharField(max_length=50, choices=FEED_CHOICES, default=ATOM)
- feed_suffix = models.CharField(max_length=255, blank=False, default="feed")
- feeds_enabled = models.BooleanField(default=True)
- feed_length = models.PositiveIntegerField(blank=True, null=True, default=15, help_text="The maximum number of items to return for this feed. All items will be returned if this field is blank.")
-
- item_title_template = models.ForeignKey(Template, blank=True, null=True, related_name="%(app_label)s_%(class)s_title_related")
- item_description_template = models.ForeignKey(Template, blank=True, null=True, related_name="%(app_label)s_%(class)s_description_related")
-
- item_context_var = 'items'
- object_attr = 'object'
-
- description = ""
-
- def feed_patterns(self, base, get_items_attr, page_attr, reverse_name):
- """
- Given the name to be used to reverse this view and the names of
- the attributes for the function that fetches the objects, returns
- patterns suitable for inclusion in urlpatterns.
- """
- urlpatterns = patterns('')
- if self.feeds_enabled:
- feed_reverse_name = "%s_feed" % reverse_name
- feed_view = http_not_acceptable(self.feed_view(get_items_attr, feed_reverse_name))
- feed_pattern = r'%s%s%s$' % (base, (base and base[-1] != "^") and "/" or "", self.feed_suffix)
- urlpatterns += patterns('',
- url(feed_pattern, feed_view, name=feed_reverse_name),
- )
- urlpatterns += patterns('',
- url(r"%s$" % base, self.page_view(get_items_attr, page_attr), name=reverse_name)
- )
- return urlpatterns
-
- def get_object(self, request, **kwargs):
- return getattr(self, self.object_attr)
-
- def feed_view(self, get_items_attr, reverse_name):
- """
- Returns a view function that renders a list of items as a feed.
- """
- get_items = callable(get_items_attr) and get_items_attr or getattr(self, get_items_attr)
-
- def inner(request, extra_context=None, *args, **kwargs):
- obj = self.get_object(request, *args, **kwargs)
- feed = self.get_feed(obj, request, reverse_name)
- items, xxx = get_items(request, extra_context=extra_context, *args, **kwargs)
- self.populate_feed(feed, items, request)
-
- response = HttpResponse(mimetype=feed.mime_type)
- feed.write(response, 'utf-8')
- return response
-
- return inner
-
- def page_view(self, get_items_attr, page_attr):
- """
- Returns a view function that renders a list of items as a page.
- """
- get_items = callable(get_items_attr) and get_items_attr or getattr(self, get_items_attr)
- page = isinstance(page_attr, Page) and page_attr or getattr(self, page_attr)
-
- def inner(request, extra_context=None, *args, **kwargs):
- items, extra_context = get_items(request, extra_context=extra_context, *args, **kwargs)
- items, item_context = self.process_page_items(request, items)
-
- context = self.get_context()
- context.update(extra_context or {})
- context.update(item_context or {})
-
- return page.render_to_response(request, extra_context=context)
- return inner
-
- def process_page_items(self, request, items):
- """
- Hook for handling any extra processing of items based on a
- request, such as pagination or searching. This method is
- expected to return a list of items and a dictionary to be
- added to the page context.
- """
- item_context = {
- self.item_context_var: items
- }
- return items, item_context
-
- def get_feed_type(self, request):
- feed_type = self.feed_type
- if feed_type not in FEEDS:
- feed_type = FEEDS.keys()[0]
- accept = request.META.get('HTTP_ACCEPT')
- if accept and feed_type not in accept and "*/*" not in accept and "%s/*" % feed_type.split("/")[0] not in accept:
- # Wups! They aren't accepting the chosen format. Is there another format we can use?
- if mimeparse:
- feed_type = mimeparse.best_match(FEEDS.keys(), accept)
- else:
- for feed_type in FEEDS.keys():
- if feed_type in accept or "%s/*" % feed_type.split("/")[0] in accept:
- break
- else:
- feed_type = None
- if not feed_type:
- raise HttpNotAcceptable
- return FEEDS[feed_type]
-
- def get_feed(self, obj, request, reverse_name):
- """
- Returns an unpopulated feedgenerator.DefaultFeed object for this object.
- """
- try:
- current_site = Site.objects.get_current()
- except Site.DoesNotExist:
- current_site = RequestSite(request)
-
- feed_type = self.get_feed_type(request)
- node = request.node
- link = node.get_absolute_url(with_domain=True, request=request, secure=request.is_secure())
-
- feed = 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 node.construct_url(node.subpath, with_domain=True, request=request, secure=request.is_secure()),
- request.is_secure()
- ),
- 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)
- )
- return feed
-
- def populate_feed(self, feed, items, request):
- if self.item_title_template:
- title_template = DjangoTemplate(self.item_title_template.code)
- else:
- title_template = None
- if self.item_description_template:
- description_template = DjangoTemplate(self.item_description_template.code)
- else:
- description_template = None
-
- node = request.node
- try:
- current_site = Site.objects.get_current()
- except Site.DoesNotExist:
- current_site = RequestSite(request)
-
- if self.feed_length is not None:
- items = items[:self.feed_length]
-
- for item in items:
- if title_template is not None:
- title = title_template.render(RequestContext(request, {'obj': item}))
- else:
- title = self.__get_dynamic_attr('item_title', item)
- if description_template is not None:
- description = description_template.render(RequestContext(request, {'obj': item}))
- else:
- description = self.__get_dynamic_attr('item_description', item)
-
- link = node.construct_url(self.reverse(obj=item), with_domain=True, request=request, secure=request.is_secure())
-
- enc = None
- enc_url = self.__get_dynamic_attr('item_enclosure_url', item)
- if enc_url:
- enc = feedgenerator.Enclosure(
- url = smart_unicode(add_domain(
- current_site.domain,
- enc_url,
- request.is_secure()
- )),
- 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)
- )
-
- 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 item_title(self, item):
- return escape(force_unicode(item))
-
- def item_description(self, item):
- return force_unicode(item)
-
- class Meta:
- abstract=True
-