Split shipherd NodeNavigationInline into inlines for hosted/targeting inlines. Added...
[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
8
9 register = template.Library()
10
11
12 class RecurseNavigationNode(RecurseTreeNode):
13         def __init__(self, template_nodes, instance_var):
14                 self.template_nodes = template_nodes
15                 self.instance_var = instance_var
16         
17         def _render_node(self, context, node, request):
18                 bits = []
19                 context.push()
20                 for child in node.get_children():
21                         context['navigation'] = child
22                         bits.append(self._render_node(context, child, request))
23                 context['navigation'] = node
24                 context['children'] = mark_safe(u''.join(bits))
25                 context['active'] = node.is_active(request)
26                 rendered = self.template_nodes.render(context)
27                 context.pop()
28                 return rendered
29         
30         def render(self, context):
31                 try:
32                         request = context['request']
33                 except KeyError:
34                         return ''
35                 
36                 instance = self.instance_var.resolve(context)
37                 
38                 if isinstance(instance, Node):
39                         qs = Navigation.objects.closest_navigation(instance)
40                 elif hasattr(instance, '__iter__'):
41                         # Is this the right way to check?
42                         qs = instance
43                 else:
44                         return settings.TEMPLATE_STRING_IF_INVALID
45                 
46                 roots = cache_tree_children(qs)
47                 bits = [self._render_node(context, node, request) for node in roots]
48                 return ''.join(bits)
49
50
51 @register.tag
52 def recursenavigation(parser, token):
53         """
54         Based on django-mptt's recursetree templatetag. In addition to {{ navigation }} and {{ children }},
55         sets {{ active }} in the context.
56         
57         Note that the tag takes one variable, which is a Node instance.
58         
59         Usage:
60                 <ul>
61                         {% recursenavigation node %}
62                                 <li{% if active %} class='active'{% endif %}>
63                                         {{ navigation.text }}
64                                         {% if not navigation.is_leaf_node %}
65                                                 <ul>
66                                                         {{ children }}
67                                                 </ul>
68                                         {% endif %}
69                                 </li>
70                         {% endrecursenavigation %}
71                 </ul>
72         """
73         bits = token.contents.split()
74         if len(bits) != 2:
75                 raise template.TemplateSyntaxError(_('%s tag requires an instance') % bits[0])
76         
77         instance_var = parser.compile_filter(bits[1])
78         
79         template_nodes = parser.parse(('endrecursenavigation',))
80         parser.delete_first_token()
81         
82         return RecurseNavigationNode(template_nodes, instance_var)
83
84
85 @register.filter
86 def has_navigation(node):
87         return bool(Navigation.objects.closest_navigation(node).count())
88
89
90 @register.filter
91 def navigation_host(node):
92         try:
93                 return Navigation.objects.closest_navigation(node)[0].hosting_node
94         except:
95                 return node
96
97
98 @register.filter
99 def targeting_navigation(node):
100         return Navigation.objects.closest_navigation(node).filter(target_node=node).order_by('level', 'lft')