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 _
10 register = template.Library()
13 class LazyNavigationRecurser(object):
14 def __init__(self, template_nodes, items, context, request):
15 self.template_nodes = template_nodes
17 self.context = context
18 self.request = request
22 context = self.context
23 request = self.request
28 if 'navloop' in context:
29 parentloop = context['navloop']
34 depth = items[0].get_level()
35 len_items = len(items)
37 loop_dict = context['navloop'] = {
38 'parentloop': parentloop,
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)
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)
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)
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']()
67 # Then render the nodelist bit by bit.
68 for node in self.template_nodes:
69 bits.append(node.render(context))
71 return mark_safe(''.join(bits))
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
80 def render(self, context):
82 request = context['request']
86 instance = self.instance_var.resolve(context)
87 key = self.key_var.resolve(context)
89 # Fall back on old behavior if the key doesn't seem to be a variable.
91 token = self.key_var.token
92 if token[0] not in ["'", '"'] and '.' not in token:
95 return settings.TEMPLATE_STRING_IF_INVALID
98 items = instance.navigation[key]
100 return settings.TEMPLATE_STRING_IF_INVALID
102 return LazyNavigationRecurser(self.template_nodes, items, context, request)()
106 def recursenavigation(parser, token):
108 The recursenavigation templatetag takes two arguments:
109 - the node for which the navigation should be found
110 - the navigation's key.
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:
115 ============================== ================================================
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 ============================== ================================================
134 {% recursenavigation node main %}
135 <li{% if navloop.active %} class='active'{% endif %}>
136 {{ navloop.item.text }}
137 {% if item.get_children %}
143 {% endrecursenavigation %}
146 bits = token.contents.split()
148 raise template.TemplateSyntaxError(_('%s tag requires two arguments: a node and a navigation section name') % bits[0])
150 instance_var = parser.compile_filter(bits[1])
151 key_var = parser.compile_filter(bits[2])
153 template_nodes = parser.parse(('endrecursenavigation',))
154 token = parser.delete_first_token()
155 return RecurseNavigationNode(template_nodes, instance_var, key_var)
159 def has_navigation(node, key=None):
161 nav = node.navigation
163 if key in nav and bool(node.navigation[key]):
165 elif key not in node.navigation:
167 return bool(node.navigation)
173 def navigation_host(node, key):
175 return Navigation.objects.filter(node__in=node.get_ancestors(include_self=True), key=key).order_by('-node__level')[0].node