Moved templatetag docs into the respective modules and fleshed them out. Centralized...
[philo.git] / philo / templatetags / nodes.py
1 """
2 The node template tags are automatically included as builtins if :mod:`philo` is an installed app.
3
4 .. templatetag:: node_url
5
6 node_url
7 --------
8
9 The :ttag:`node_url` tag allows access to :meth:`.View.reverse` from a template for a :class:`.Node`. By default, the :class:`.Node` that is used for the call is pulled from the context variable ``node``; however, this can be overridden with the ``[for <node>]`` option.
10
11 Usage::
12
13         {% node_url [for <node>] [as <var>] %}
14         {% node_url with <obj> [for <node>] [as <var>] %}
15         {% node_url <view_name> [<arg1> [<arg2> ...] ] [for <node>] [as <var>] %}
16         {% node_url <view_name> [<key1>=<value1> [<key2>=<value2> ...] ] [for <node>] [as <var>] %}
17
18 """
19
20 from django import template
21 from django.conf import settings
22 from django.contrib.sites.models import Site
23 from django.core.urlresolvers import reverse, NoReverseMatch
24 from django.template.defaulttags import kwarg_re
25 from django.utils.encoding import smart_str
26
27 from philo.exceptions import ViewCanNotProvideSubpath
28
29
30 register = template.Library()
31
32
33 class NodeURLNode(template.Node):
34         def __init__(self, node, as_var, with_obj=None, view_name=None, args=None, kwargs=None):
35                 self.as_var = as_var
36                 self.view_name = view_name
37                 
38                 # Because the following variables have already been compiled as filters if they exist, they don't need to be re-scanned as template variables.
39                 self.node = node
40                 self.with_obj = with_obj
41                 self.args = args
42                 self.kwargs = kwargs
43         
44         def render(self, context):
45                 if self.node:
46                         node = self.node.resolve(context)
47                 else:
48                         node = context.get('node', None)
49                 
50                 if not node:
51                         return settings.TEMPLATE_STRING_IF_INVALID
52                 
53                 if self.with_obj is None and self.view_name is None:
54                         url = node.get_absolute_url()
55                 else:
56                         if not node.view.accepts_subpath:
57                                 return settings.TEMPLATE_STRING_IF_INVALID
58                         
59                         if self.with_obj is not None:
60                                 try:
61                                         view_name, args, kwargs = node.view.get_reverse_params(self.with_obj.resolve(context))
62                                 except ViewCanNotProvideSubpath:
63                                         return settings.TEMPLATE_STRING_IF_INVALID
64                         else: # self.view_name is not None
65                                 view_name = self.view_name
66                                 args = [arg.resolve(context) for arg in self.args]
67                                 kwargs = dict([(smart_str(k, 'ascii'), v.resolve(context)) for k, v in self.kwargs.items()])
68                         
69                         url = ''
70                         try:
71                                 subpath = reverse(view_name, urlconf=node.view, args=args, kwargs=kwargs)
72                         except NoReverseMatch:
73                                 if self.as_var is None:
74                                         if settings.TEMPLATE_DEBUG:
75                                                 raise
76                                         return settings.TEMPLATE_STRING_IF_INVALID
77                         else:
78                                 url = node.construct_url(subpath)
79                 
80                 if self.as_var:
81                         context[self.as_var] = url
82                         return ''
83                 else:
84                         return url
85
86
87 @register.tag(name='node_url')
88 def do_node_url(parser, token):
89         """
90         {% node_url [for <node>] [as <var>] %}
91         {% node_url with <obj> [for <node>] [as <var>] %}
92         {% node_url <view_name> [<arg1> [<arg2> ...] ] [for <node>] [as <var>] %}
93         {% node_url <view_name> [<key1>=<value1> [<key2>=<value2> ...] ] [for <node>] [as <var>] %}
94         
95         """
96         params = token.split_contents()
97         tag = params[0]
98         as_var = None
99         with_obj = None
100         node = None
101         params = params[1:]
102         
103         if len(params) >= 2 and params[-2] == 'as':
104                 as_var = params[-1]
105                 params = params[:-2]
106         
107         if len(params) >= 2 and params[-2] == 'for':
108                 node = parser.compile_filter(params[-1])
109                 params = params[:-2]
110         
111         if len(params) >= 2 and params[-2] == 'with':
112                 with_obj = parser.compile_filter(params[-1])
113                 params = params[:-2]
114         
115         if with_obj is not None:
116                 if params:
117                         raise template.TemplateSyntaxError('`%s` template tag accepts no arguments or keyword arguments if with <obj> is specified.' % tag)
118                 return NodeURLNode(with_obj=with_obj, node=node, as_var=as_var)
119         
120         if params:
121                 args = []
122                 kwargs = {}
123                 view_name = params.pop(0)
124                 for param in params:
125                         match = kwarg_re.match(param)
126                         if not match:
127                                 raise TemplateSyntaxError("Malformed arguments to `%s` tag" % tag)
128                         name, value = match.groups()
129                         if name:
130                                 kwargs[name] = parser.compile_filter(value)
131                         else:
132                                 args.append(parser.compile_filter(value))
133                 return NodeURLNode(view_name=view_name, args=args, kwargs=kwargs, node=node, as_var=as_var)
134         
135         return NodeURLNode(node=node, as_var=as_var)