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 # Then render the nodelist bit by bit.
64 for node in self.template_nodes:
65 bits.append(node.render(context))
67 return mark_safe(''.join(bits))
70 class RecurseNavigationNode(template.Node):
71 def __init__(self, template_nodes, instance_var, key_var):
72 self.template_nodes = template_nodes
73 self.instance_var = instance_var
74 self.key_var = key_var
76 def render(self, context):
78 request = context['request']
82 instance = self.instance_var.resolve(context)
83 key = self.key_var.resolve(context)
85 # Fall back on old behavior if the key doesn't seem to be a variable.
87 token = self.key_var.token
88 if token[0] not in ["'", '"'] and '.' not in token:
91 return settings.TEMPLATE_STRING_IF_INVALID
94 items = instance.navigation[key]
96 return settings.TEMPLATE_STRING_IF_INVALID
98 return LazyNavigationRecurser(self.template_nodes, items, context, request)()
102 def recursenavigation(parser, token):
104 The :ttag:`recursenavigation` templatetag takes two arguments:
106 * the :class:`.Node` for which the :class:`.Navigation` should be found
107 * the :class:`.Navigation`'s :attr:`~.Navigation.key`.
109 It will then recursively loop over each :class:`.NavigationItem` in the :class:`.Navigation` and render the template
110 chunk within the block. :ttag:`recursenavigation` sets the following variables in the context:
112 ============================== ================================================
114 ============================== ================================================
115 ``navloop.depth`` The current depth of the loop (1 is the top level)
116 ``navloop.depth0`` The current depth of the loop (0 is the top level)
117 ``navloop.counter`` The current iteration of the current level(1-indexed)
118 ``navloop.counter0`` The current iteration of the current level(0-indexed)
119 ``navloop.first`` True if this is the first time through the current level
120 ``navloop.last`` True if this is the last time through the current level
121 ``navloop.parentloop`` This is the loop one level "above" the current one
123 ``item`` The current item in the loop (a :class:`.NavigationItem` instance)
124 ``children`` If accessed, performs the next level of recursion.
125 ``navloop.active`` True if the item is active for this request
126 ``navloop.active_descendants`` True if the item has active descendants for this request
127 ============================== ================================================
132 {% recursenavigation node "main" %}
133 <li{% if navloop.active %} class='active'{% endif %}>
134 <a href="{{ item.get_target_url }}">{{ item.text }}</a>
135 {% if item.get_children %}
141 {% endrecursenavigation %}
144 .. note:: {% recursenavigation %} requires that the current :class:`HttpRequest` be present in the context as ``request``. The simplest way to do this is with the `request context processor`_. Simply make sure that ``django.core.context_processors.request`` is included in your :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting.
146 .. _request context processor: https://docs.djangoproject.com/en/dev/ref/templates/api/#django-core-context-processors-request
149 bits = token.contents.split()
151 raise template.TemplateSyntaxError(_('%s tag requires two arguments: a node and a navigation section name') % bits[0])
153 instance_var = parser.compile_filter(bits[1])
154 key_var = parser.compile_filter(bits[2])
156 template_nodes = parser.parse(('endrecursenavigation',))
157 token = parser.delete_first_token()
158 return RecurseNavigationNode(template_nodes, instance_var, key_var)
162 def has_navigation(node, key=None):
163 """Returns ``True`` if the node has a :class:`.Navigation` with the given key and ``False`` otherwise. If ``key`` is ``None``, returns whether the node has any :class:`.Navigation`\ s at all."""
165 return bool(node.navigation[key])
171 def navigation_host(node, key):
172 """Returns the :class:`.Node` which hosts the :class:`.Navigation` which ``node`` has inherited for ``key``. Returns ``node`` if any exceptions are encountered."""
174 return node.navigation[key].node