Removed backwards-compatibility for database template loader. Added VERSION information.
[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                         # Then render the nodelist bit by bit.
64                         for node in self.template_nodes:
65                                 bits.append(node.render(context))
66                 context.pop()
67                 return mark_safe(''.join(bits))
68
69
70 class RecurseNavigationNode(template.Node):
71         def __init__(self, template_nodes, instance_var, key):
72                 self.template_nodes = template_nodes
73                 self.instance_var = instance_var
74                 self.key = key
75         
76         def render(self, context):
77                 try:
78                         request = context['request']
79                 except KeyError:
80                         return ''
81                 
82                 instance = self.instance_var.resolve(context)
83                 
84                 try:
85                         items = instance.navigation[self.key]
86                 except:
87                         return settings.TEMPLATE_STRING_IF_INVALID
88                 
89                 return LazyNavigationRecurser(self.template_nodes, items, context, request)()
90
91
92 @register.tag
93 def recursenavigation(parser, token):
94         """
95         The recursenavigation templatetag takes two arguments:
96         - the node for which the navigation should be found
97         - the navigation's key.
98         
99         It will then recursively loop over each item in the navigation and render the template
100         chunk within the block. recursenavigation sets the following variables in the context:
101         
102                 ==============================  ================================================
103                 Variable                        Description
104                 ==============================  ================================================
105                 ``navloop.depth``               The current depth of the loop (1 is the top level)
106                 ``navloop.depth0``              The current depth of the loop (0 is the top level)
107                 ``navloop.counter``             The current iteration of the current level(1-indexed)
108                 ``navloop.counter0``            The current iteration of the current level(0-indexed)
109                 ``navloop.first``               True if this is the first time through the current level
110                 ``navloop.last``                True if this is the last time through the current level
111                 ``navloop.parentloop``          This is the loop one level "above" the current one
112                 ==============================  ================================================
113                 ``item``                        The current item in the loop (a NavigationItem instance)
114                 ``children``                    If accessed, performs the next level of recursion.
115                 ``navloop.active``              True if the item is active for this request
116                 ``navloop.active_descendants``  True if the item has active descendants for this request
117                 ==============================  ================================================
118         
119         Example:
120                 <ul>
121                         {% recursenavigation node main %}
122                                 <li{% if navloop.active %} class='active'{% endif %}>
123                                         {{ navloop.item.text }}
124                                         {% if item.get_children %}
125                                                 <ul>
126                                                         {{ children }}
127                                                 </ul>
128                                         {% endif %}
129                                 </li>
130                         {% endrecursenavigation %}
131                 </ul>
132         """
133         bits = token.contents.split()
134         if len(bits) != 3:
135                 raise template.TemplateSyntaxError(_('%s tag requires two arguments: a node and a navigation section name') % bits[0])
136         
137         instance_var = parser.compile_filter(bits[1])
138         key = bits[2]
139         
140         template_nodes = parser.parse(('recurse', 'endrecursenavigation',))
141         
142         token = parser.next_token()
143         if token.contents == 'recurse':
144                 template_nodes.append(RecurseNavigationMarker())
145                 template_nodes.extend(parser.parse(('endrecursenavigation')))
146                 parser.delete_first_token()
147         
148         return RecurseNavigationNode(template_nodes, instance_var, key)
149
150
151 @register.filter
152 def has_navigation(node, key=None):
153         try:
154                 nav = node.navigation
155                 if key is not None:
156                         if key in nav and bool(node.navigation[key]):
157                                 return True
158                         elif key not in node.navigation:
159                                 return False
160                 return bool(node.navigation)
161         except:
162                 return False
163
164
165 @register.filter
166 def navigation_host(node, key):
167         try:
168                 return Navigation.objects.filter(node__in=node.get_ancestors(include_self=True), key=key).order_by('-node__level')[0].node
169         except:
170                 return node