#coding:utf-8 import re from django import template from django.conf import settings from django.contrib.syndication.views import Feed from django.core.urlresolvers import reverse, resolve, NoReverseMatch from django.template import Node from django.template import TemplateSyntaxError from django.utils.encoding import smart_str from django.utils.html import escape as html_escape from django.utils.safestring import mark_safe register = template.Library() kwarg_re = re.compile(r"(?:(\w+)=)?(.+)") class FeedInfoNode(Node): def __init__(self, view_name, args, kwargs, asvar): self.view_name = view_name self.args = args self.kwargs = kwargs self.asvar = asvar def render(self, context): args = [arg.resolve(context) for arg in self.args] kwargs = dict([(smart_str(k,'ascii'), v.resolve(context)) for k, v in self.kwargs.items()]) # Try to look up the URL twice: once given the view name, and again # relative to what we guess is the "main" app. If they both fail, # re-raise the NoReverseMatch unless we're using the # {% feed_info ... as var %} construct in which cause return nothing. url = '' try: url = reverse(self.view_name, args=args, kwargs=kwargs, current_app=context.current_app) except NoReverseMatch, e: if settings.SETTINGS_MODULE: project_name = settings.SETTINGS_MODULE.split('.')[0] try: url = reverse(project_name + '.' + self.view_name, args=args, kwargs=kwargs, current_app=context.current_app) except NoReverseMatch: if self.asvar is None: # Re-raise the original exception, not the one with # the path relative to the project. This makes a # better error message. raise e else: if self.asvar is None: raise e if url: if 'request' in context: request = context['request'] else: request = None feed_instance, feed_args, feed_kwargs = resolve(url) if not isinstance(feed_instance, Feed): raise NoReverseMatch, \ 'feed_info can only reverse class-based feeds' feed_obj = feed_instance.get_object(request, *feed_args, **feed_kwargs) feed_data = { 'url': url, 'obj': feed_instance, 'args': feed_args, 'kwargs': feed_kwargs, #'title': html_escape(feed_instance.__get_dynamic_attr('title', obj)), 'title': html_escape( feed_instance._Feed__get_dynamic_attr('title', feed_obj) ), 'type': feed_instance.feed_type.mime_type, } if self.asvar: if url: context[self.asvar] = feed_data else: context[self.asvar] = {} return '' else: return mark_safe( '' \ % feed_data ) def feed_info(parser, token): """ Returns an mapping containing populated info about the reversed feed Works exactly as the url tag, but the mapping is not returned, instead a variable is always set in the context. This is a way to define links that aren't tied to a particular URL configuration:: {% feed_info path.to.some_feed_view_class arg1 arg2 as feed_info_var %} or {% feed_info path.to.some_feed_view_class name1=value1 name2=value2 as feed_info_var %} """ bits = token.split_contents() if len(bits) < 2: raise TemplateSyntaxError("'%s' takes at least one argument" " (path to a feed view)" % bits[0]) viewname = bits[1] args = [] kwargs = {} asvar = None bits = bits[2:] if len(bits) >= 2 and bits[-2] == 'as': asvar = bits[-1] bits = bits[:-2] # Backwards compatibility: check for the old comma separated format # {% url urlname arg1,arg2 %} # Initial check - that the first space separated bit has a comma in it if bits and ',' in bits[0]: check_old_format = True # In order to *really* be old format, there must be a comma # in *every* space separated bit, except the last. for bit in bits[1:-1]: if ',' not in bit: # No comma in this bit. Either the comma we found # in bit 1 was a false positive (e.g., comma in a string), # or there is a syntax problem with missing commas check_old_format = False break else: # No comma found - must be new format. check_old_format = False if check_old_format: # Confirm that this is old format by trying to parse the first # argument. An exception will be raised if the comma is # unexpected (i.e. outside of a static string). match = kwarg_re.match(bits[0]) if match: value = match.groups()[1] try: parser.compile_filter(value) except TemplateSyntaxError: bits = ''.join(bits).split(',') # Now all the bits are parsed into new format, # process them as template vars if len(bits): for bit in bits: match = kwarg_re.match(bit) if not match: raise TemplateSyntaxError(u"Malformed arguments to url tag") name, value = match.groups() if name: kwargs[name] = parser.compile_filter(value) else: args.append(parser.compile_filter(value)) return FeedInfoNode(viewname, args, kwargs, asvar) feed_info = register.tag(feed_info)