Removed outdated RecurseNavigationMarker code and added filtered interpretation of...
[philo.git] / contrib / shipherd / templatetags / shipherd.py
1 from django import template, VERSION as django_version
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.safestring import mark_safe
7 from django.utils.translation import ugettext as _
8
9
10 register = template.Library()
11
12
13 class LazyNavigationRecurser(object):
14         def __init__(self, template_nodes, items, context, request):
15                 self.template_nodes = template_nodes
16                 self.items = items
17                 self.context = context
18                 self.request = request
19         
20         def __call__(self):
21                 items = self.items
22                 context = self.context
23                 request = self.request
24                 
25                 if not items:
26                         return ''
27                 
28                 if 'navloop' in context:
29                         parentloop = context['navloop']
30                 else:
31                         parentloop = {}
32                 context.push()
33                 
34                 depth = items[0].get_level()
35                 len_items = len(items)
36                 
37                 loop_dict = context['navloop'] = {
38                         'parentloop': parentloop,
39                         'depth': depth + 1,
40                         'depth0': depth
41                 }
42                 
43                 bits = []
44                 
45                 for i, item in enumerate(items):
46                         # First set context variables.
47                         loop_dict['counter0'] = i
48                         loop_dict['counter'] = i + 1
49                         loop_dict['revcounter'] = len_items - i
50                         loop_dict['revcounter0'] = len_items - i - 1
51                         loop_dict['first'] = (i == 0)
52                         loop_dict['last'] = (i == len_items - 1)
53                         
54                         # Set on loop_dict and context for backwards-compatibility.
55                         # Eventually only allow access through the loop_dict.
56                         loop_dict['active'] = context['active'] = item.is_active(request)
57                         loop_dict['active_descendants'] = context['active_descendants'] = item.has_active_descendants(request)
58                         
59                         # Set these directly in the context for easy access.
60                         context['item'] = item
61                         context['children'] = self.__class__(self.template_nodes, item.get_children(), context, request)
62                         
63                         # Django 1.2.X compatibility - a lazy recurser will not be called if accessed as a template variable.
64                         if django_version < (1,3):
65                                 context['children'] = context['children']()
66                         
67                         # Then render the nodelist bit by bit.
68                         for node in self.template_nodes:
69                                 bits.append(node.render(context))
70                 context.pop()
71                 return mark_safe(''.join(bits))
72
73
74 class RecurseNavigationNode(template.Node):
75         def __init__(self, template_nodes, instance_var, key_var):
76                 self.template_nodes = template_nodes
77                 self.instance_var = instance_var
78                 self.key_var = key_var
79         
80         def render(self, context):
81                 try:
82                         request = context['request']
83                 except KeyError:
84                         return ''
85                 
86                 instance = self.instance_var.resolve(context)
87                 key = self.key_var.resolve(context)
88                 
89                 # Fall back on old behavior if the key doesn't seem to be a variable.
90                 if not key:
91                         token = self.key_var.token
92                         if token[0] not in ["'", '"'] and '.' not in token:
93                                 key = token
94                         else:
95                                 return settings.TEMPLATE_STRING_IF_INVALID
96                 
97                 try:
98                         items = instance.navigation[key]
99                 except:
100                         return settings.TEMPLATE_STRING_IF_INVALID
101                 
102                 return LazyNavigationRecurser(self.template_nodes, items, context, request)()
103
104
105 @register.tag
106 def recursenavigation(parser, token):
107         """
108         The recursenavigation templatetag takes two arguments:
109         - the node for which the navigation should be found
110         - the navigation's key.
111         
112         It will then recursively loop over each item in the navigation and render the template
113         chunk within the block. recursenavigation sets the following variables in the context:
114         
115                 ==============================  ================================================
116                 Variable                        Description
117                 ==============================  ================================================
118                 ``navloop.depth``               The current depth of the loop (1 is the top level)
119                 ``navloop.depth0``              The current depth of the loop (0 is the top level)
120                 ``navloop.counter``             The current iteration of the current level(1-indexed)
121                 ``navloop.counter0``            The current iteration of the current level(0-indexed)
122                 ``navloop.first``               True if this is the first time through the current level
123                 ``navloop.last``                True if this is the last time through the current level
124                 ``navloop.parentloop``          This is the loop one level "above" the current one
125                 ==============================  ================================================
126                 ``item``                        The current item in the loop (a NavigationItem instance)
127                 ``children``                    If accessed, performs the next level of recursion.
128                 ``navloop.active``              True if the item is active for this request
129                 ``navloop.active_descendants``  True if the item has active descendants for this request
130                 ==============================  ================================================
131         
132         Example:
133                 <ul>
134                         {% recursenavigation node main %}
135                                 <li{% if navloop.active %} class='active'{% endif %}>
136                                         {{ navloop.item.text }}
137                                         {% if item.get_children %}
138                                                 <ul>
139                                                         {{ children }}
140                                                 </ul>
141                                         {% endif %}
142                                 </li>
143                         {% endrecursenavigation %}
144                 </ul>
145         """
146         bits = token.contents.split()
147         if len(bits) != 3:
148                 raise template.TemplateSyntaxError(_('%s tag requires two arguments: a node and a navigation section name') % bits[0])
149         
150         instance_var = parser.compile_filter(bits[1])
151         key_var = parser.compile_filter(bits[2])
152         
153         template_nodes = parser.parse(('endrecursenavigation',))
154         token = parser.delete_first_token()
155         return RecurseNavigationNode(template_nodes, instance_var, key_var)
156
157
158 @register.filter
159 def has_navigation(node, key=None):
160         try:
161                 nav = node.navigation
162                 if key is not None:
163                         if key in nav and bool(node.navigation[key]):
164                                 return True
165                         elif key not in node.navigation:
166                                 return False
167                 return bool(node.navigation)
168         except:
169                 return False
170
171
172 @register.filter
173 def navigation_host(node, key):
174         try:
175                 return Navigation.objects.filter(node__in=node.get_ancestors(include_self=True), key=key).order_by('-node__level')[0].node
176         except:
177                 return node