af7f05f722dfbe62a1ffc605aad63d6aa61b1a9b
[philo.git] / contrib / shipherd / templatetags / shipherd.py
1 from django import template
2 from django.conf import settings
3 from django.utils.safestring import mark_safe
4 from philo.contrib.shipherd.models import Navigation
5 from philo.models import Node
6 from django.utils.translation import ugettext as _
7
8
9 register = template.Library()
10
11
12 class RecurseNavigationMarker(object):
13         pass
14
15
16 class RecurseNavigationNode(template.Node):
17         def __init__(self, template_nodes, instance_var, key):
18                 self.template_nodes = template_nodes
19                 self.instance_var = instance_var
20                 self.key = key
21         
22         def _render_items(self, items, context, request):
23                 if not items:
24                         return ''
25                 
26                 if 'navloop' in context:
27                         parentloop = context['navloop']
28                 else:
29                         parentloop = {}
30                 context.push()
31                 
32                 depth = items[0].get_level()
33                 len_items = len(items)
34                 
35                 loop_dict = context['navloop'] = {
36                         'parentloop': parentloop,
37                         'depth': depth + 1,
38                         'depth0': depth
39                 }
40                 
41                 bits = []
42                 
43                 for i, item in enumerate(items):
44                         # First set context variables.
45                         loop_dict['counter0'] = i
46                         loop_dict['counter'] = i + 1
47                         loop_dict['revcounter'] = len_items - i
48                         loop_dict['revcounter0'] = len_items - i - 1
49                         loop_dict['first'] = (i == 0)
50                         loop_dict['last'] = (i == len_items - 1)
51                         
52                         # Set on loop_dict and context for backwards-compatibility.
53                         # Eventually only allow access through the loop_dict.
54                         loop_dict['item'] = context['item'] = item
55                         loop_dict['active'] = context['active'] = item.is_active(request)
56                         loop_dict['active_descendants'] = context['active_descendants'] = item.has_active_descendants(request)
57                         
58                         # Then render the nodelist bit by bit.
59                         for node in self.template_nodes:
60                                 if isinstance(node, RecurseNavigationMarker):
61                                         # Then recurse!
62                                         children = items.get_children()
63                                         bits.append(self._render_items(children, context, request))
64                                 elif isinstance(node, template.VariableNode) and node.filter_expression.var.lookups == (u'children',):
65                                         # Then recurse! This is here for backwards-compatibility only.
66                                         children = items.get_children()
67                                         bits.append(self._render_items(children, context, request))
68                                 else:
69                                         bits.append(node.render(context))
70                 context.pop()
71                 return ''.join(bits)
72         
73         def render(self, context):
74                 try:
75                         request = context['request']
76                 except KeyError:
77                         return ''
78                 
79                 instance = self.instance_var.resolve(context)
80                 
81                 try:
82                         items = instance.navigation[self.key]
83                 except:
84                         return settings.TEMPLATE_STRING_IF_INVALID
85                 
86                 return self._render_items(items, context, request)
87
88
89 @register.tag
90 def recursenavigation(parser, token):
91         """
92         Based on django-mptt's recursetree templatetag. In addition to {{ item }} and {{ children }},
93         sets {{ active }}, {{ active_descendants }}, {{ navloop.counter }}, {{ navloop.counter0 }},
94         {{ navloop.revcounter }}, {{ navloop.revcounter0 }}, {{ navloop.first }}, {{ navloop.last }},
95         and {{ navloop.parentloop }} in the context.
96         
97         Note that the tag takes two variables: a Node instance and the key of the navigation to
98         be recursed.
99         
100         Usage:
101                 <ul>
102                         {% recursenavigation node main %}
103                                 <li{% if active %} class='active'{% endif %}>
104                                         {{ item.text }}
105                                         {% if item.get_children %}
106                                                 <ul>
107                                                         {% recurse %}
108                                                 </ul>
109                                         {% endif %}
110                                 </li>
111                         {% endrecursenavigation %}
112                 </ul>
113         """
114         bits = token.contents.split()
115         if len(bits) != 3:
116                 raise template.TemplateSyntaxError(_('%s tag requires two arguments: a node and a navigation section name') % bits[0])
117         
118         instance_var = parser.compile_filter(bits[1])
119         key = bits[2]
120         
121         template_nodes = parser.parse(('recurse', 'endrecursenavigation',))
122         
123         token = parser.next_token()
124         if token.contents == 'recurse':
125                 template_nodes.append(RecurseNavigationMarker())
126                 template_nodes.extend(parser.parse(('endrecursenavigation')))
127                 parser.delete_first_token()
128         
129         return RecurseNavigationNode(template_nodes, instance_var, key)
130
131
132 @register.filter
133 def has_navigation(node, key=None):
134         try:
135                 nav = node.navigation
136                 if key is not None:
137                         if key in nav and bool(node.navigation[key]):
138                                 return True
139                         elif key not in node.navigation:
140                                 return False
141                 return bool(node.navigation)
142         except:
143                 return False
144
145
146 @register.filter
147 def navigation_host(node, key):
148         try:
149                 return Navigation.objects.filter(node__in=node.get_ancestors(include_self=True), key=key).order_by('-node__level')[0].node
150         except:
151                 if settings.TEMPLATE_DEBUG:
152                         raise
153                 return node