Added is_active method to Navigation class. Replaced the get_navigation and is_active...
authorStephen Burrows <stephen.r.burrows@gmail.com>
Mon, 3 Jan 2011 22:18:35 +0000 (17:18 -0500)
committerStephen Burrows <stephen.r.burrows@gmail.com>
Tue, 4 Jan 2011 14:48:05 +0000 (09:48 -0500)
contrib/navigation/models.py
contrib/navigation/templatetags/navigation.py

index b37f614..91b2147 100644 (file)
@@ -158,6 +158,33 @@ class Navigation(TreeEntity):
                        return self.url_or_subpath
        target_url = property(get_target_url)
        
+       def is_active(self, request):
+               # First check if this particular navigation is active. It is considered active if:
+               # - the requested node is this instance's target node and its subpath matches the requested path.
+               # - the requested node is a descendant of this instance's target node and this instance's target
+               #   node is not the hosting node of this navigation structure.
+               # - this instance has no target node and the url matches either the request path or the full url.
+               # - any of this instance's children are active.
+               node = request.node
+               
+               if self.target_node == node:
+                       if self.target_url == request.path:
+                               return True
+               elif self.target_node is None:
+                       if self.url_or_subpath == request.path or self.url_or_subpath == "http%s://%s%s" % (request.is_secure() and 's' or '', request.get_host(), request.path):
+                               return True
+               elif self.target_node.is_ancestor_of(node) and self.target_node != self.hosting_node:
+                       return True
+               
+               # Always fall back to whether the node has active children.
+               return self.has_active_children(request)
+       
+       def has_active_children(self, request):
+               for child in self.get_children():
+                       if child.is_active(request):
+                               return True
+               return False
+       
        def _has_changed(self):
                if model_to_dict(self) == self._initial_data:
                        return False
index 11fdb44..4510f15 100644 (file)
@@ -1,29 +1,72 @@
 from django import template
 from django.conf import settings
+from django.utils.safestring import mark_safe
 from philo.contrib.navigation.models import Navigation
+from mptt.templatetags.mptt_tags import RecurseTreeNode, cache_tree_children
 
 
 register = template.Library()
 
 
-@register.filter
-def get_navigation(node):
-       return Navigation.objects.closest_navigation(node)
+class RecurseNavigationNode(RecurseTreeNode):
+       def __init__(self, template_nodes, instance_var):
+               self.template_nodes = template_nodes
+               self.instance_var = instance_var
+       
+       def _render_node(self, context, node, request):
+               bits = []
+               context.push()
+               for child in node.get_children():
+                       context['node'] = child
+                       bits.append(self._render_node(context, child, request))
+               context['node'] = node
+               context['children'] = mark_safe(u''.join(bits))
+               context['active'] = node.is_active(request)
+               rendered = self.template_nodes.render(context)
+               context.pop()
+               return rendered
+       
+       def render(self, context):
+               try:
+                       request = context['request']
+               except KeyError:
+                       return ''
+               
+               instance = self.instance_var.resolve(context)
+               roots = cache_tree_children(Navigation.objects.closest_navigation(instance))
+               bits = [self._render_node(context, node, request) for node in roots]
+               return ''.join(bits)
 
-@register.filter
-def is_active(navigation, request):
+
+@register.tag
+def recursenavigation(parser, token):
        """
-       Returns true if the navigation is considered `active`.
+       Based on django-mptt's recursetree templatetag. In addition to {{ node }} and {{ children }},
+       sets {{ active }} in the context.
+       
+       Note that the tag takes one variable, navigation, which is a Navigation instance.
        
-       But what does "active" mean? Should this be defined on the model instead, perhaps?
+       Usage:
+               <ul>
+                       {% recursenavigation navigation %}
+                               <li{% if active %} class='active'{% endif %}>
+                                       {{ node.name }}
+                                       {% if not node.is_leaf_node %}
+                                               <ul>
+                                                       {{ children }}
+                                               </ul>
+                                       {% endif %}
+                               </li>
+                       {% endrecursenavigation %}
+               </ul>
        """
-       try:
-               if navigation.target_node == request.node:
-                       if request.path == navigation.target_url:
-                               return True
-                       return False
-               if navigation.target_url in request.path:
-                       return True
-       except:
-               pass
-       return False
\ No newline at end of file
+       bits = token.contents.split()
+       if len(bits) != 2:
+               raise template.TemplateSyntaxError(_('%s tag requires an instance') % bits[0])
+       
+       instance_var = template.Variable(bits[1])
+       
+       template_nodes = parser.parse(('endrecursenavigation',))
+       parser.delete_first_token()
+       
+       return RecurseNavigationNode(template_nodes, instance_var)
\ No newline at end of file