Merge remote branch 'melinath/master'
[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 mptt.templatetags.mptt_tags import RecurseTreeNode, cache_tree_children
7 from django.utils.translation import ugettext as _
8
9
10 register = template.Library()
11
12
13 class RecurseNavigationNode(RecurseTreeNode):
14         def __init__(self, template_nodes, instance_var, key):
15                 self.template_nodes = template_nodes
16                 self.instance_var = instance_var
17                 self.key = key
18         
19         def _render_node(self, context, item, request):
20                 bits = []
21                 context.push()
22                 
23                 # loosely based on django.template.defaulttags.ForNode.render
24                 children = item.get_children()
25                 parentloop = context['navloop']
26                 loop_dict = context['navloop'] = {'parentloop':parentloop}
27                 len_items = len(children)
28                 for i, child in enumerate(children):
29                         context['item'] = child
30                         loop_dict['counter0'] = i
31                         loop_dict['counter'] = i + 1
32                         loop_dict['revcounter'] = len_items - i
33                         loop_dict['revcounter0'] = len_items - i - 1
34                         loop_dict['first'] = (i == 0)
35                         loop_dict['last'] = (i == len_items - 1)
36                         bits.append(self._render_node(context, child, request))
37                 context['navloop'] = context['navloop']['parentloop']
38                 context['item'] = item
39                 context['children'] = mark_safe(u''.join(bits))
40                 context['active'] = item.is_active(request)
41                 context['active_descendants'] = item.has_active_descendants(request)
42                 rendered = self.template_nodes.render(context)
43                 context.pop()
44                 return rendered
45         
46         def render(self, context):
47                 try:
48                         request = context['request']
49                 except KeyError:
50                         return ''
51                 
52                 instance = self.instance_var.resolve(context)
53                 
54                 try:
55                         items = instance.navigation[self.key]
56                 except:
57                         return settings.TEMPLATE_STRING_IF_INVALID
58                 
59                 bits = []
60                 
61                 # loosely based on django.template.defaulttags.ForNode.render
62                 # This is a repetition of the stuff that happens above. We should eliminate that somehow.
63                 loop_dict = context['navloop'] = {'parentloop':{}}
64                 len_items = len(items)
65                 for i, item in enumerate(items):
66                         loop_dict['counter0'] = i
67                         loop_dict['counter'] = i + 1
68                         loop_dict['revcounter'] = len_items - i
69                         loop_dict['revcounter0'] = len_items - i - 1
70                         loop_dict['first'] = (i == 0)
71                         loop_dict['last'] = (i == len_items - 1)
72                         bits.append(self._render_node(context, item, request))
73                 
74                 return ''.join(bits)
75
76
77 @register.tag
78 def recursenavigation(parser, token):
79         """
80         Based on django-mptt's recursetree templatetag. In addition to {{ item }} and {{ children }},
81         sets {{ active }}, {{ active_descendants }}, {{ navloop.counter }}, {{ navloop.counter0 }},
82         {{ navloop.revcounter }}, {{ navloop.revcounter0 }}, {{ navloop.first }}, {{ navloop.last }},
83         and {{ navloop.parentloop }} in the context.
84         
85         Note that the tag takes two variables: a Node instance and the key of the navigation to
86         be recursed.
87         
88         Usage:
89                 <ul>
90                         {% recursenavigation node main %}
91                                 <li{% if active %} class='active'{% endif %}>
92                                         {{ item.text }}
93                                         {% if children %}
94                                                 <ul>
95                                                         {{ children }}
96                                                 </ul>
97                                         {% endif %}
98                                 </li>
99                         {% endrecursenavigation %}
100                 </ul>
101         """
102         bits = token.contents.split()
103         if len(bits) != 3:
104                 raise template.TemplateSyntaxError(_('%s tag requires two arguments: a node and a navigation section name') % bits[0])
105         
106         instance_var = parser.compile_filter(bits[1])
107         key = bits[2]
108         
109         template_nodes = parser.parse(('endrecursenavigation',))
110         parser.delete_first_token()
111         
112         return RecurseNavigationNode(template_nodes, instance_var, key)
113
114
115 @register.filter
116 def has_navigation(node, key=None):
117         try:
118                 nav = node.navigation
119                 if key is not None:
120                         if key in nav and bool(node.navigation[key]):
121                                 return True
122                         elif key not in node.navigation:
123                                 return False
124                 return bool(node.navigation)
125         except:
126                 return False
127
128
129 @register.filter
130 def navigation_host(node, key):
131         try:
132                 return Navigation.objects.filter(node__in=node.get_ancestors(include_self=True), key=key).order_by('-node__level')[0].node
133         except:
134                 if settings.TEMPLATE_DEBUG:
135                         raise
136                 return node