1. Matthew Schinckel
  2. django-menus

Commits

Matthew Schinckel  committed 75a3b50

Make {% menu_item ... %} only show the menu item object if the
logged in user can retrieve the view.

  • Participants
  • Parent commits b4f141a
  • Branches default

Comments (0)

Files changed (2)

File menus/templates/menu/item.html

View file
  • Ignore whitespace
-<a href="{{ url }}" id="{{ element_id }}" class="{{ active }}">{{ text }}</a>
+<a href="{{ url }}" id="{{ element_id }}" class="{{ classes }}">{{ text }}</a>

File menus/templatetags/menu_item.py

View file
  • Ignore whitespace
+import types
+
 from django import template
 register = template.Library()
-from django.core.urlresolvers import reverse
+from django.core.urlresolvers import reverse, resolve
 
-def menu_item(context, url, text=None, classes=None):
-    if url.startswith("url:"):
-        url = reverse(url[4:], args=[])
-    if context.has_key('page_url'):
-        page_url = context['page_url']
-    elif context.has_key('flatpage'):
-        page_url = context['flatpage'].url
-    elif context.has_key('request'):
-        page_url = context['request'].path
-    else:
-        page_url = ""
+def get_callable_cells(function):
+    callables = []
+    if not hasattr(function, 'func_closure'):
+        if hasattr(function, 'view_func'):
+            return get_callable_cells(function.view_func)
+    if not function.func_closure:
+        return [function]
+    for closure in function.func_closure:
+        if hasattr(closure.cell_contents, '__call__'):
+            if closure.cell_contents.func_closure:
+                callables.extend(get_callable_cells(closure.cell_contents))
+            else:
+                callables.append(closure.cell_contents)
+    return callables
+
+def get_tests(function):
+    return [
+        x for x in get_callable_cells(function)[:-1]
+        if isinstance(x, types.LambdaType) or
+        x.func_code.co_varnames[0] in ["user", "u"]
+    ]
+
+class MenuItem(template.Node):
+    def __init__(self, template_file, url, text, classes=""):
+        super(MenuItem, self).__init__()
+        self.template_file = template_file or 'menu/item.html'
+        url = url.strip('\'"')
+        if url.startswith('url:'):
+            self.url = reverse(url[4:], args=[])
+        else:
+            self.url = url
+            
+        self.text = text.strip('\'"')
+        self.classes = set(classes.split() or [])
+        
+    def render(self, context):
+        if 'request' not in context:
+            raise template.TemplateSyntaxError("menu_item tag requires 'request' in context")
+
+        if 'page_url' in context:
+            page_url = context['page_url']
+        elif 'flatpage' in context:
+            page_url = context['flatpage'].url
+        else:
+            page_url = context['request'].path
+        
+        user = context['request'].user
+        view = resolve(self.url).func
+        can_view = all([test(user) for test in get_tests(view)])
+        
+        if not can_view:
+            return ''
+        
+        file_name = self.template_file
+        
+        if not getattr(self, 'nodelist', False):
+            from django.template.loader import get_template, select_template
+            if isinstance(file_name, template.Template):
+                t = file_name
+            elif not isinstance(file_name, basestring) and is_iterable(file_name):
+                t = select_template(file_name)
+            else:
+                t = get_template(file_name)
+            self.nodelist = t.nodelist
+        
+        if page_url.startswith(self.url):
+            self.classes.add("active")
+        
+        new_context = template.context.Context({
+            'url': self.url,
+            'text': self.text,
+            'classes': " ".join(self.classes)
+        })
+        return self.nodelist.render(new_context)
+        
     
-    return {
-        'url': url,
-        'active': "active" if page_url.startswith(url) else "",
-        'text': text or "",
-        'classes': classes or "",
-    }
+def tested_menu_item(parser, token):
+    error_message = "'menu_item' tag requires at least 2, at most 3 arguments"
+    
+    try:
+        parts = token.split_contents()
+    except ValueError:
+        raise template.TemplateSyntaxError(error_message)
+    
+    if not (3 <= len(parts) <= 4):
+        raise template.TemplateSyntaxError(error_message)
+    
+    return MenuItem('menu/item.html', *parts[1:])
 
-
-register.inclusion_tag('menu/item.html', takes_context=True)(menu_item)
-
+register.tag('menu_item', tested_menu_item)