Initial node_url_refactor commit. Revises node_url templatetag to accept args/kwargs...
[philo.git] / templatetags / nodes.py
1 from django import template
2 from django.conf import settings
3 from django.contrib.sites.models import Site
4 from django.core.urlresolvers import reverse, NoReverseMatch
5 from django.template.defaulttags import kwarg_re
6 from philo.exceptions import ViewCanNotProvideSubpath
7
8
9 register = template.Library()
10
11
12 class NodeURLNode(template.Node):
13         def __init__(self, node, as_var, with_obj=None, view_name=None, args=None, kwargs=None):
14                 self.as_var = as_var
15                 self.view_name = view_name
16                 
17                 # Because the following variables have already been compiled as filters if they exist, they don't need to be re-scanned as template variables.
18                 self.node = node
19                 self.with_obj = with_obj
20                 self.args = args
21                 self.kwargs = kwargs
22         
23         def render(self, context):
24                 if self.node:
25                         node = self.node.resolve(context)
26                 else:
27                         node = context['node']
28                 
29                 if self.with_obj is not None:
30                         try:
31                                 view_name, args, kwargs = node.view.get_reverse_params(self.with_obj.resolve(context))
32                         except ViewCanNotProvideSubpath:
33                                 return settings.TEMPLATE_STRING_IF_INVALID
34                 elif self.view_name is not None:
35                         view_name = self.view_name
36                         args = [arg.resolve(context) for arg in self.args]
37                         kwargs = dict([(smart_str(k, 'ascii'), v.resolve(context)) for k, v in self.kwargs.items()])
38                 else:
39                         return node.get_absolute_url()
40                 
41                 if not node.view.accepts_subpath:
42                         return settings.TEMPLATE_STRING_IF_INVALID
43                 
44                 url = ''
45                 try:
46                         subpath = reverse(view_name, urlconf=node.view, args=args, kwargs=kwargs)
47                 except NoReverseMatch:
48                         if self.as_var is None:
49                                 raise
50                 else:
51                         if subpath[0] == '/':
52                                 subpath = subpath[1:]
53                         
54                         url = node.get_absolute_url() + subpath
55                 
56                 if self.as_var:
57                         context[self.as_var] = url
58                         return ''
59                 else:
60                         return url
61
62
63 @register.tag(name='node_url')
64 def do_node_url(parser, token):
65         """
66         {% node_url [for <node>] [as <var] %}
67         {% node_url with <obj> [for <node>] [as <var>] %}
68         {% node_url <view_name> [<arg1> [<arg2> ...] ] [for <node>] [as <var>] %}
69         {% node_url <view_name> [<key1>=<value1> [<key2>=<value2> ...] ] [for <node>] [as <var>]%}
70         """
71         params = token.split_contents()
72         tag = params[0]
73         as_var = None
74         with_obj = None
75         node = None
76         params = params[1:]
77         
78         if len(params) >= 2 and params[-2] == 'as':
79                 as_var = params[-1]
80                 params = params[:-2]
81         
82         if len(params) >= 2 and params[-2] == 'for':
83                 node = parser.compile_filter(params[-1])
84                 params = params[:-2]
85         
86         if len(params) >= 2 and params[-2] == 'with':
87                 with_obj = parser.compile_filter(params[-1])
88                 params = params[:-2]
89         
90         if with_obj is not None:
91                 if params:
92                         raise template.TemplateSyntaxError('`%s` template tag accepts no arguments or keyword arguments if with <obj> is specified.' % tag)
93                 return NodeURLNode(with_obj=with_obj, node=node, as_var=as_var)
94         
95         if params:
96                 args = []
97                 kwargs = {}
98                 view_name = params.pop(0)
99                 for param in params:
100                         match = kwarg_re.match(param)
101                         if not match:
102                                 raise TemplateSyntaxError("Malformed arguments to `%s` tag" % tag)
103                         name, value = match.groups()
104                         if name:
105                                 kwargs[name] = parser.compile_filter(value)
106                         else:
107                                 args.append(parser.compile_filter(value))
108                 return NodeURLNode(view_name=view_name, args=args, kwargs=kwargs, node=node, as_var=as_var)
109         
110         return NodeURLNode(node=node, as_var=as_var)