David Jean Louis avatar David Jean Louis committed 5eef866

better API for menus and dashboards

Comments (0)

Files changed (17)

admin_tools/dashboard/models.py

 from admin_tools.utils import AppListElementMixin
 
 
-class Dashboard(list):
+class Dashboard(object):
     """
     Base class for dashboards.
     The Dashboard class is a simple python list that has three additional
         from admin_tools.dashboard.models import *
 
         class MyDashboard(Dashboard):
-            def render(self, request):
+            def __init__(self, **kwargs):
                 # we want a 3 columns layout
                 self.columns = 3
 
                 # append an app list module for "Applications"
-                self.append(AppListDashboardModule(
+                self.children.append(AppListDashboardModule(
                     title=_('Applications'),
                     exclude_list=('django.contrib',),
                 ))
         
                 # append an app list module for "Administration"
-                self.append(AppListDashboardModule(
+                self.children.append(AppListDashboardModule(
                     title=_('Administration'),
                     include_list=('django.contrib',),
                 ))
         
                 # append a recent actions module
-                self.append(RecentActionsDashboardModule(
+                self.children.append(RecentActionsDashboardModule(
                     enabled=False,
                     title=_('Recent Actions'),
                     limit=5
         css = ()
         js  = ()
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, **kwargs):
         """
         Dashboard constructor.
         """
-        super(Dashboard, self).__init__()
         self.title = kwargs.get('title', _('Dashboard'))
         self.template = kwargs.get('template', 'dashboard/dashboard.html')
         self.columns = kwargs.get('columns', 2)
+        self.children = kwargs.get('children', [])
 
-    def render(self, request):
+    def init_with_context(self, context):
         """
-        The ``Dashboard.render()`` method is called just before the display
-        with a ``django.http.HttpRequest`` as unique argument.
-        Override this method to build your dashboard if you need to access to
-        the request instance.
+        Sometimes you may need to access context or request variables to build
+        your dashboard, this is what the ``init_with_context()`` method is for.
+        This method is called just before the display with a 
+        ``django.template.RequestContext`` as unique argument, so you can 
+        access to all context variables and to the ``django.http.HttpRequest``.
         """
         pass
 
         from admin_tools.dashboard.models import *
 
         class MyAppIndexDashboard(AppIndexDashboard):
-            def render(self, request):
+            def __init__(self, **kwargs):
+                AppIndexDashboard.__init__(self, **kwargs)
                 # we don't want a title, it's redundant
                 self.title = ''
 
                 # append a model list module that lists all models 
                 # for the app
-                self.append(ModelListDashboardModule(
+                self.children.append(ModelListDashboardModule(
                     title=self.app_title,
                     include_list=self.models,
                 ))
         
                 # append a recent actions module for the current app
-                self.append(RecentActionsDashboardModule(
+                self.children.append(RecentActionsDashboardModule(
                     title=_('Recent Actions'),
                     include_list=self.models,
                     limit=5
 
     .. image:: images/dashboard_app_index_example.png
     """
-    def __init__(self, app_title, models, *args, **kwargs):
-        super(AppIndexDashboard, self).__init__(*args, **kwargs)
+    def __init__(self, app_title, models, **kwargs):
+        super(AppIndexDashboard, self).__init__(**kwargs)
         self.app_title = app_title
         self.models = models
     
         The template to use to render the module.
         Default value: 'dashboard/module.html'.
     """
-    def __init__(self, *args, **kwargs):
+    def __init__(self, **kwargs):
         self.enabled = kwargs.get('enabled', True)
         self.draggable = kwargs.get('draggable', True)
         self.collapsible = kwargs.get('collapsible', True)
         self.pre_content = kwargs.get('pre_content')
         self.post_content = kwargs.get('post_content')
         self.template = kwargs.get('template', 'dashboard/module.html')
-        self.entries = kwargs.get('entries', [])
+        self.children = kwargs.get('children', [])
 
-    def render(self, request):
+    def init_with_context(self, context):
+        """
+        Like for the ``Dashboard`` class, dashboard modules have a 
+        ``init_with_context`` method that is called with a 
+        ``django.template.RequestContext`` instance as unique argument.
+
+        This gives you enough flexibility to build complex modules, for 
+        example, let's build a "history" dashboard module, that will list the 
+        last ten visited pages::
+
+            class HistoryDashboardModule(LinkListDashboardModule):
+                def init_with_context(self, context):
+                    self.title = 'History'
+                    request = context['request']
+                    # we use sessions to store the visited pages stack
+                    history = request.session.get('history', [])
+                    for item in history:
+                        self.children.append(item)
+                    # add the current page to the history
+                    history.insert(0, {
+                        'title': context['title'],
+                        'url': request.META['PATH_INFO']
+                    })
+                    if len(history) > 10:
+                        history = history[:10]
+                    request.session['history'] = history
+
+        Here's a screenshot of our history item:
+
+        .. image:: images/history_dashboard_module.png
+        """
         pass
 
     def is_empty(self):
         >>> mod.pre_content = None
         >>> mod.is_empty()
         True
-        >>> mod.entries.append('foo')
+        >>> mod.children.append('foo')
         >>> mod.is_empty()
         False
-        >>> mod.entries = []
+        >>> mod.children = []
         >>> mod.is_empty()
         True
         """
         return self.pre_content is None and \
                self.post_content is None and \
-               len(self.entries) == 0
+               len(self.children) == 0
 
     def render_css_classes(self):
         """
         The layout of the list, possible values are ``stacked`` and ``inline``.
         The default value is ``stacked``.
 
-    Link list modules entries are simple python dictionaries that can have the
+    Link list modules children are simple python dictionaries that can have the
     following keys:
 
     ``title``
     Here's a small example of building a link list module::
         
         from admin_tools.dashboard.models import *
-        
-        mydashboard = Dashboard()
-        mydashboard.append(LinkListDashboardModule(
-            layout='inline',
-            entries=(
-                {
-                    'title': 'Python website',
-                    'url': 'http://www.python.org',
-                    'external': True,
-                    'title': 'Python programming language rocks !',
-                },
-                {
-                    'title': 'Django website',
-                    'url': 'http://www.djangoproject.com',
-                    'external': True
-                },
-                {
-                    'title': 'Some internal link',
-                    'url': '/some/internal/link/',
-                    'external': False
-                },
-            )
-        ))
+
+        class MyDashboard(Dashboard):
+            def __init__(self, **kwargs): 
+                Dashboard.__init__(self, **kwargs)
+
+                self.children.append(LinkListDashboardModule(
+                    layout='inline',
+                    children=(
+                        {
+                            'title': 'Python website',
+                            'url': 'http://www.python.org',
+                            'external': True,
+                            'title': 'Python programming language rocks !',
+                        },
+                        {
+                            'title': 'Django website',
+                            'url': 'http://www.djangoproject.com',
+                            'external': True
+                        },
+                        {
+                            'title': 'Some internal link',
+                            'url': '/some/internal/link/',
+                            'external': False
+                        },
+                    )
+                ))
 
     The screenshot of what this code produces:
 
     .. image:: images/linklist_dashboard_module.png
     """
 
-    def __init__(self, *args, **kwargs):
-        super(LinkListDashboardModule, self).__init__(*args, **kwargs)
+    def __init__(self, **kwargs):
+        super(LinkListDashboardModule, self).__init__(**kwargs)
         self.title = kwargs.get('title', _('Links'))
         self.template = kwargs.get('template',
                                    'dashboard/modules/link_list.html')
     Here's a small example of building an app list module::
  
         from admin_tools.dashboard.models import *
-     
-        mydashboard = Dashboard()
 
-        # will only list the django.contrib apps
-        mydashboard.append(AppListDashboardModule(
-            title='Administration',
-            include_list=('django.contrib',)
-        )) 
-        # will list all apps except the django.contrib ones
-        mydashboard.append(AppListDashboardModule(
-            title='Applications',
-            exclude_list=('django.contrib',)
-        )) 
+        class MyDashboard(Dashboard):
+            def __init__(self, **kwargs): 
+                Dashboard.__init__(self, **kwargs)
+
+                # will only list the django.contrib apps
+                self.children.append(AppListDashboardModule(
+                    title='Administration',
+                    include_list=('django.contrib',)
+                ))
+                # will list all apps except the django.contrib ones
+                self.children.append(AppListDashboardModule(
+                    title='Applications',
+                    exclude_list=('django.contrib',)
+                ))
 
     The screenshot of what this code produces:
 
         the django.contrib.auth.Group model line will not be displayed.
     """
 
-    def __init__(self, *args, **kwargs):
-        super(AppListDashboardModule, self).__init__(*args, **kwargs)
+    def __init__(self, **kwargs):
+        super(AppListDashboardModule, self).__init__(**kwargs)
         self.title = kwargs.get('title', _('Applications'))
         self.template = kwargs.get('template',
                                    'dashboard/modules/app_list.html')
         self.include_list = kwargs.get('include_list', [])
         self.exclude_list = kwargs.get('exclude_list', [])
 
-    def render(self, request):
+    def init_with_context(self, context):
+        request = context['request']
         apps = {}
         for model, model_admin in admin.site._registry.items():
             perms = self._check_perms(request, model, model_admin)
         for app in apps_sorted:
             # sort model list alphabetically
             apps[app]['models'].sort(lambda x, y: cmp(x['title'], y['title']))
-            self.entries.append(apps[app])
+            self.children.append(apps[app])
 
 
 class ModelListDashboardModule(DashboardModule, AppListElementMixin):
     Here's a small example of building a model list module::
         
         from admin_tools.dashboard.models import *
+
+        class MyDashboard(Dashboard):
+            def __init__(self, **kwargs): 
+                Dashboard.__init__(self, **kwargs)
         
-        mydashboard = Dashboard()
-        # will only list the django.contrib.auth models
-        mydashboard.append(ModelListDashboardModule(
-            title='Authentication',
-            include_list=('django.contrib.auth',)
-        )) 
+                # will only list the django.contrib.auth models
+                self.children.append(ModelListDashboardModule(
+                    title='Authentication',
+                    include_list=('django.contrib.auth',)
+                ))
 
     The screenshot of what this code produces:
 
         the django.contrib.auth.Group model line will not be displayed.
     """
 
-    def __init__(self, *args, **kwargs):
-        super(ModelListDashboardModule, self).__init__(*args, **kwargs)
+    def __init__(self, **kwargs):
+        super(ModelListDashboardModule, self).__init__(**kwargs)
         self.title = kwargs.get('title', '')
         self.template = kwargs.get('template',
                                    'dashboard/modules/model_list.html')
         self.include_list = kwargs.get('include_list', [])
         self.exclude_list = kwargs.get('exclude_list', [])
 
-    def render(self, request):
+    def init_with_context(self, context):
+        request = context['request']
         for model, model_admin in admin.site._registry.items():
             perms = self._check_perms(request, model, model_admin)
             if not perms:
                 model_dict['change_url'] = self._get_admin_change_url(model)
             if perms['add']:
                 model_dict['add_url'] = self._get_admin_add_url(model)
-            self.entries.append(model_dict)
+            self.children.append(model_dict)
 
         # sort model list alphabetically
-        self.entries.sort(lambda x, y: cmp(x['title'], y['title']))
+        self.children.sort(lambda x, y: cmp(x['title'], y['title']))
 
 
 class RecentActionsDashboardModule(DashboardModule):
         displayed.
 
     ``limit``
-        The maximum number of entries to display. Default value: 10.
+        The maximum number of children to display. Default value: 10.
 
     Here's a small example of building a recent actions module::
         
         from admin_tools.dashboard.models import *
         
-        mydashboard = Dashboard()
-        # will only list the django.contrib apps
-        mydashboard.append(RecentActionsDashboardModule(
-            title='Django CMS recent actions',
-            include_list=('cms.page', 'cms.cmsplugin',)
-        ))
+        class MyDashboard(Dashboard):
+            def __init__(self, **kwargs): 
+                Dashboard.__init__(self, **kwargs)
+
+                # will only list the django.contrib apps
+                self.children.append(RecentActionsDashboardModule(
+                    title='Django CMS recent actions',
+                    include_list=('cms.page', 'cms.cmsplugin',)
+                ))
 
     The screenshot of what this code produces:
 
     .. image:: images/recentactions_dashboard_module.png
     """
 
-    def __init__(self, *args, **kwargs):
-        super(RecentActionsDashboardModule, self).__init__(*args, **kwargs)
+    def __init__(self, **kwargs):
+        super(RecentActionsDashboardModule, self).__init__(**kwargs)
         self.title = kwargs.get('title', _('Recent Actions'))
         self.template = kwargs.get('template',
                                    'dashboard/modules/recent_actions.html')
         self.exclude_list = kwargs.get('exclude_list', [])
         self.limit = kwargs.get('limit', 10)
 
-    def render(self, request):
+    def init_with_context(self, context):
         from django.db.models import Q
         from django.contrib.admin.models import LogEntry
 
+        request = context['request']
+
         def get_qset(list):
             qset = None
             for contenttype in list:
         if self.exclude_list:
             qs = qs.exclude(get_qset(self.exclude_list))
 
-        self.entries = qs.select_related('content_type', 'user')[:self.limit]
-        if not len(self.entries):
+        self.children = qs.select_related('content_type', 'user')[:self.limit]
+        if not len(self.children):
             self.pre_content = _('No recent actions.')
 
 
         The URL of the feed.
 
     ``limit``
-        The maximum number of feed entries to display. Default value: None, 
-        which means that all entries are displayed.
+        The maximum number of feed children to display. Default value: None, 
+        which means that all children are displayed.
 
     Here's a small example of building a recent actions module::
         
         from admin_tools.dashboard.models import *
         
-        mydashboard = Dashboard()
-        # will only list the django.contrib apps
-        mydashboard.append(FeedDashboardModule(
-            title=_('Latest Django News'),
-            feed_url='http://www.djangoproject.com/rss/weblog/',
-            limit=5
-        ))
+        class MyDashboard(Dashboard):
+            def __init__(self, **kwargs): 
+                Dashboard.__init__(self, **kwargs)
+ 
+                # will only list the django.contrib apps
+                self.children.append(FeedDashboardModule(
+                    title=_('Latest Django News'),
+                    feed_url='http://www.djangoproject.com/rss/weblog/',
+                    limit=5
+                ))
 
     The screenshot of what this code produces:
 
     .. image:: images/feed_dashboard_module.png
     """
-    def __init__(self, *args, **kwargs):
-        super(FeedDashboardModule, self).__init__(*args, **kwargs)
+    def __init__(self, **kwargs):
+        super(FeedDashboardModule, self).__init__(**kwargs)
         self.title = kwargs.get('title', _('RSS Feed'))
         self.template = kwargs.get('template', 'dashboard/modules/feed.html')
         self.feed_url = kwargs.get('feed_url')
         self.limit = kwargs.get('limit')
 
-    def render(self, request):
+    def init_with_context(self, context):
         import datetime
         if self.feed_url is None:
             raise ValueError('You must provide a valid feed URL')
             except:
                 # no date for certain feeds
                 pass
-            self.entries.append(entry)
+            self.children.append(entry)
 
 
 class DefaultIndexDashboard(Dashboard):
     And then set the ``ADMIN_TOOLS_INDEX_DASHBOARD`` settings variable to 
     point to your custom index dashboard class.
     """ 
-    def __init__(self, *args, **kwargs):
-        super(DefaultIndexDashboard, self).__init__(*args, **kwargs)
+    def __init__(self, **kwargs):
+        Dashboard.__init__(self, **kwargs)
 
         # append a link list module for "quick links"
-        self.append(LinkListDashboardModule(
+        self.children.append(LinkListDashboardModule(
             title=_('Quick links'),
             layout='inline',
             draggable=False,
             deletable=False,
             collapsible=False,
-            entries=[
+            children=[
                 {
                     'title': _('Return to site'),
                     'url': '/',
         ))
 
         # append an app list module for "Applications"
-        self.append(AppListDashboardModule(
+        self.children.append(AppListDashboardModule(
             title=_('Applications'),
             exclude_list=('django.contrib',),
         ))
 
         # append an app list module for "Administration"
-        self.append(AppListDashboardModule(
+        self.children.append(AppListDashboardModule(
             title=_('Administration'),
             include_list=('django.contrib',),
         ))
 
         # append a recent actions module
-        self.append(RecentActionsDashboardModule(
+        self.children.append(RecentActionsDashboardModule(
             enabled=False,
             title=_('Recent Actions'),
             limit=5
         ))
 
         # append a feed module
-        self.append(FeedDashboardModule(
+        self.children.append(FeedDashboardModule(
             enabled=False,
             title=_('Latest Django News'),
             feed_url='http://www.djangoproject.com/rss/weblog/',
         ))
 
         # append another link list module for "support". 
-        self.append(LinkListDashboardModule(
+        self.children.append(LinkListDashboardModule(
             title=_('Support'),
-            entries=[
+            children=[
                 {
                     'title': _('Django documentation'),
                     'url': 'http://docs.djangoproject.com/',
     And then set the ``ADMIN_TOOLS_APP_INDEX_DASHBOARD`` settings variable to 
     point to your custom app index dashboard class.
     """
-    def render(self, request):
+    def __init__(self, **kwargs):
+        AppIndexDashboard.__init__(self, **kwargs)
 
         # we disable title because its redundant with the model list module
         self.title = ''
 
         # append a model list module
-        self.append(ModelListDashboardModule(
+        self.children.append(ModelListDashboardModule(
             title=self.app_title,
             include_list=self.models,
         ))
 
         # append a recent actions module
-        self.append(RecentActionsDashboardModule(
+        self.children.append(RecentActionsDashboardModule(
             title=_('Recent Actions'),
             include_list=self.get_app_content_types(),
             limit=5

admin_tools/dashboard/templates/dashboard/dashboard.html

     {% if has_disabled_modules %}
     <ul>
         {% spaceless %}
-        {% for module in dashboard %}
+        {% for module in dashboard.children %}
         {% if not module.enabled %}
         <li><a href="#" rel="module_{{ forloop.counter }}" class="addlink add-dashboard-module">{{ module.title }}</a></li>
         {% endif %}
     {% endif %}
 </div>
 <div id="{{ dashboard.get_id }}" class="dashboard-container">
-    {% for module in dashboard %}
+    {% for module in dashboard.children %}
 {% render_dashboard_module module forloop.counter %}{% endfor %}
 </div>

admin_tools/dashboard/templates/dashboard/dashboard.txt

 from django.core.urlresolvers import reverse
 from admin_tools.dashboard.models import *
 
-# create your custom modules here if you want, for example:
-#
-# class CustomDashboardModule(DashboardModule):
-#     pass
-#
 
-
-# to activate your index dashboard you must add the following to your 
-# project's settings.py file:
+# to activate your index dashboard add the following to your settings.py:
 #
 # ADMIN_TOOLS_INDEX_DASHBOARD = '{{ project }}.{{ file }}.CustomIndexDashboard'
+
 class CustomIndexDashboard(Dashboard):
     """
-    Document your custom dashboard.
+    Custom index dashboard for {{ project }}.
     """ 
-    def render(self, request):
+    def __init__(self, **kwargs):
+        Dashboard.__init__(self, **kwargs)
+
         # append a link list module for "quick links"
-        self.append(LinkListDashboardModule(
+        self.children.append(LinkListDashboardModule(
             title=_('Quick links'),
             layout='inline',
             draggable=False,
             deletable=False,
             collapsible=False,
-            entries=[
+            children=[
                 {
                     'title': _('Return to site'),
                     'url': '/',
         ))
 
         # append an app list module for "Applications"
-        self.append(AppListDashboardModule(
+        self.children.append(AppListDashboardModule(
             title=_('Applications'),
             exclude_list=('django.contrib',),
         ))
 
         # append an app list module for "Administration"
-        self.append(AppListDashboardModule(
+        self.children.append(AppListDashboardModule(
             title=_('Administration'),
             include_list=('django.contrib',),
         ))
 
         # append a recent actions module
-        self.append(RecentActionsDashboardModule(
+        self.children.append(RecentActionsDashboardModule(
             title=_('Recent Actions'),
             limit=5
         ))
 
         # append a feed module
-        self.append(FeedDashboardModule(
+        self.children.append(FeedDashboardModule(
             title=_('Latest Django News'),
             feed_url='http://www.djangoproject.com/rss/weblog/',
             limit=5
         ))
 
         # append another link list module for "support". 
-        self.append(LinkListDashboardModule(
+        self.children.append(LinkListDashboardModule(
             title=_('Support'),
-            entries=[
+            children=[
                 {
                     'title': _('Django documentation'),
                     'url': 'http://docs.djangoproject.com/',
             ]
         ))
 
+    def init_with_context(self, context):
+        """
+        Use this method if you need to access the request context.
+        """
+        pass
 
-# to activate your app index dashboard you must add the following to your 
-# project's settings.py file:
+
+# to activate your app index dashboard add the following to your settings.py:
 #
 # ADMIN_TOOLS_APP_INDEX_DASHBOARD = '{{ project }}.{{ file }}.CustomAppIndexDashboard'
+
 class CustomAppIndexDashboard(AppIndexDashboard):
     """
-    Document your custom app index dashboard.
+    Custom app index dashboard for {{ project }}.
     """ 
-    def render(self, request):
+    def __init__(self, **kwargs):
+        AppIndexDashboard.__init__(self, **kwargs)
+
         # we disable title because its redundant with the model list module
         self.title = ''
 
         # append a model list module
-        self.append(ModelListDashboardModule(
+        self.children.append(ModelListDashboardModule(
             title=self.app_title,
             include_list=self.models,
         ))
 
         # append a recent actions module
-        self.append(RecentActionsDashboardModule(
+        self.children.append(RecentActionsDashboardModule(
             title=_('Recent Actions'),
             include_list=self.get_app_content_types(),
         ))
+
+    def init_with_context(self, context):
+        """
+        Use this method if you need to access the request context.
+        """
+        pass

admin_tools/dashboard/templates/dashboard/module.html

             <p>{{ module.pre_content }}</p>
             {% endif %}
             {% block module_content %}
-            {% for entry in module.entries %}
-            {{ entry }}
+            {% for child in module.children %}
+            {{ child }}
             {% endfor %}
             {% endblock %}
             {% if module.post_content %}

admin_tools/dashboard/templates/dashboard/modules/app_list.html

 {% extends "dashboard/module.html" %}
 {% load i18n %}
 {% block module_content %}
-        {% for entry in module.entries %}
-        <h3><a href="{{ entry.url }}">{{ entry.title }}</a></h3>
+        {% for child in module.children %}
+        <h3><a href="{{ child.url }}">{{ child.title }}</a></h3>
         <ul>
-            {% for model in entry.models %}
+            {% for model in child.models %}
             {% spaceless %}
             <li>
                 {% if model.change_url %}<a href="{{ model.change_url }}">{{ model.title }}</a>{% else %}{{ model.title }}{% endif %}

admin_tools/dashboard/templates/dashboard/modules/feed.html

 {% block module_content %}
 <ul>
     {% spaceless %}
-    {% for entry in module.entries %}
+    {% for child in module.children %}
     <li class="{% cycle 'odd' 'even' %}">
-        {% if entry.date %}<span class="float-right">{{ entry.date|date }}&nbsp;</span>{% endif %}
-        <a class="external-link" href="{{ entry.url }}">{{ entry.title }}</a>
+        {% if child.date %}<span class="float-right">{{ child.date|date }}&nbsp;</span>{% endif %}
+        <a class="external-link" href="{{ child.url }}">{{ child.title }}</a>
     </li>
     {% endfor %}
     {% endspaceless %}

admin_tools/dashboard/templates/dashboard/modules/model_list.html

 {% load i18n %}
 {% block module_content %}
         <ul>
-            {% for entry in module.entries %}
+            {% for child in module.children %}
             {% spaceless %}
             <li>
-                {% if entry.change_url %}<a href="{{ entry.change_url }}">{{ entry.title }}</a>{% else %}{{ entry.title }}{% endif %}
-                {% if entry.add_url or entry.change_url %}
+                {% if child.change_url %}<a href="{{ child.change_url }}">{{ child.title }}</a>{% else %}{{ child.title }}{% endif %}
+                {% if child.add_url or child.change_url %}
                 <ul>
-                    {% if entry.add_url %}<li><a class="addlink" href="{{ entry.add_url }}"><span class="icon">{% trans "Add" %}</span></a></li>{% endif %}
-                    {% if entry.change_url %}<li><a class="changelink" href="{{ entry.change_url }}"><span class="icon">{% trans "Change" %}</span></a></li>{% endif %}
+                    {% if child.add_url %}<li><a class="addlink" href="{{ child.add_url }}"><span class="icon">{% trans "Add" %}</span></a></li>{% endif %}
+                    {% if child.change_url %}<li><a class="changelink" href="{{ child.change_url }}"><span class="icon">{% trans "Change" %}</span></a></li>{% endif %}
                 </ul>
                 {% endif %}
             </li>

admin_tools/dashboard/templates/dashboard/modules/recent_actions.html

 {% block module_content %}
 <ul>
     {% spaceless %}
-    {% for entry in module.entries %}
+    {% for child in module.children %}
     <li class="{% cycle 'odd' 'even' %}">
-        <span class="float-right">{{ entry.action_time|date }}</span>
-        {% if entry.is_deletion %}
-        <span class="deletelink">{% if entry.content_type %}{% filter capfirst %}{% trans entry.content_type.name %}{% endfilter %}&nbsp;{% endif %}{{ entry.object_repr }}</span>
+        <span class="float-right">{{ child.action_time|date }}</span>
+        {% if child.is_deletion %}
+        <span class="deletelink">{% if child.content_type %}{% filter capfirst %}{% trans child.content_type.name %}{% endfilter %}&nbsp;{% endif %}{{ child.object_repr }}</span>
         {% else %}
-        <a href="{{ entry.get_admin_url }}" class="{% if entry.is_addition %} addlink{% endif %}{% if entry.is_change %} changelink{% endif %}">{% if entry.content_type %}{% filter capfirst %}{% trans entry.content_type.name %}{% endfilter %}&nbsp;{% endif %}{{ entry.object_repr }}</a>
+        <a href="{{ child.get_admin_url }}" class="{% if child.is_addition %} addlink{% endif %}{% if child.is_change %} changelink{% endif %}">{% if child.content_type %}{% filter capfirst %}{% trans child.content_type.name %}{% endfilter %}&nbsp;{% endif %}{{ child.object_repr }}</a>
         {% endif %}
     </li>
     {% endfor %}

admin_tools/dashboard/templatetags/dashboard_tags.py

 """
 Dashboard template tags, the following dashboard tags are available:
-* ``{% render_dashboard %}``
-* ``{% render_dashboard_module %}``
+ * ``{% render_dashboard %}``
+ * ``{% render_dashboard_module %}``
+ * ``{% render_dashboard_css %}``
 
-To load the dashboard tags just do: ``{% load dashboard_tags %}``.
+To load the dashboard tags: ``{% load dashboard_tags %}``.
 """
 
 import math
 from admin_tools.dashboard.utils import get_dashboard_from_context
 
 register = template.Library()
+tag_func = register.inclusion_tag('dashboard/dummy.html', takes_context=True)
 
 
 def render_dashboard(context, dashboard=None):
     """
     if not dashboard:
         dashboard = get_dashboard_from_context(context)
-    dashboard.render(context['request'])
+    dashboard.init_with_context(context)
     context.update({
         'template': dashboard.template,
         'dashboard': dashboard,
-        'split_at': math.ceil(float(len(dashboard))/float(dashboard.columns)),
+        'split_at': math.ceil(float(len(dashboard.children))/float(dashboard.columns)),
         'media_url': settings.MEDIA_URL.rstrip('/'),
-        'has_disabled_modules': len([m for m in dashboard if not m.enabled]) > 0,
+        'has_disabled_modules': len([m for m in dashboard.children \
+                                if not m.enabled]) > 0,
     })
     return context
-render_dashboard = register.inclusion_tag(
-    'dashboard/dummy.html',
-    takes_context=True
-)(render_dashboard)
+render_dashboard = tag_func(render_dashboard)
 
 
 def render_dashboard_module(context, module, index=None):
     ``DashboardModule`` instance as first parameter and an integer ``index`` as
     second parameter, that is the index of the module in the dashboard.
     """
-    module.render(context['request'])
+    module.init_with_context(context)
     context.update({
         'template': module.template,
         'module': module,
         'index': index,
     })
     return context
-render_dashboard_module = register.inclusion_tag(
-    'dashboard/dummy.html',
-    takes_context=True
-)(render_dashboard_module)
+render_dashboard_module = tag_func(render_dashboard_module)
 
 
 def render_dashboard_css(context, dashboard=None):
         dashboard = get_dashboard_from_context(context)
 
     context.update({
+        'template' : 'dashboard/css.html',
         'css_files': dashboard.Media.css,
         'media_url': settings.MEDIA_URL.rstrip('/'),
     })
     return context
-render_dashboard_css = register.inclusion_tag(
-    'dashboard/css.html',
-    takes_context=True
-)(render_dashboard_css)
+render_dashboard_css = tag_func(render_dashboard_css)

admin_tools/dashboard/utils.py

 
 
 def get_dashboard_from_context(context):
-    try:
-        request = context['request']
-    except KeyError:
-        request = HttpRequest()
+    """
+    Return the dashboard instance given the context.
+    """
+    request = context['request']
     if request.META.get('REQUEST_URI') == reverse('admin:index'):
-        return get_index_dashboard(request)
+        return get_index_dashboard()
+    # this is a mess, needs cleanup !
     app = context['app_list'][0]
     models = []
     app_label = None
                 if m['name'] == capfirst(model._meta.verbose_name_plural):
                     mod = '%s.%s' % (model.__module__, model.__name__)
                     models.append(mod)
-    return get_app_index_dashboard(request, app_label, app_title, models)
+    return get_app_index_dashboard(app_label, app_title, models)
 
 
-def get_index_dashboard(request):
+def get_index_dashboard():
     """
     Returns the admin dashboard defined by the user or the default one.
     """
     return getattr(mod, inst)()
 
 
-def get_app_index_dashboard(request, app_label=None, app_title='',
-                            model_list=[]):
+def get_app_index_dashboard(app_label=None, app_title='', model_list=[]):
     """
     Returns the admin dashboard defined by the user or the default one.
     """

admin_tools/menu/models.py

 from admin_tools.utils import AppListElementMixin
 
 
-class Menu(list):
+class Menu(object):
     """
-    Base class for menus.
-    The Menu class is a simple python list that has an extra property:
+    This is the base class for creating custom navigation menus.
+    A menu can have the following properties:
     
     ``template``
-        The template to use to render the menu.
+        A string representing the path to template to use to render the menu.
+        As for any other template, the path must be relative to one of the 
+        directories of your ``TEMPLATE_DIRS`` setting.
         Default value: "menu/menu.html".
+    
+    ``children``
+        A list of children menu items. All children items mus be instances of
+        the ``MenuItem`` class.
 
     If you want to customize the look of your menu and it's menu items, you
     can declare css stylesheets and/or javascript files to include when 
                 css = ('/media/css/mymenu.css',)
                 js = ('/media/js/mymenu.js',)
 
-    Here's an example of a custom menu::
+    Here's a concrete example of a custom menu::
 
+        from django.core.urlresolvers import reverse
         from admin_tools.menu.models import *
 
         class MyMenu(Menu):
-            def render(self, request):
-                self.append(MenuItem(title='Home', url=reverse('admin:index')))
-                self.append(AppListMenuItem(title='Applications'))
-                item = MenuItem('Multi level menu item')
-                item.append(MenuItem('Child 1'))
-                item.append(MenuItem('Child 2'))
-                self.append(item)
+            def __init__(self, **kwargs):
+                super(MyMenu, self).__init__(**kwargs)
+                self.children.append(
+                    MenuItem(title='Home', url=reverse('admin:index'))
+                )
+                self.children.append(
+                    AppListMenuItem(title='Applications')
+                )
+                self.children.append(
+                    MenuItem(
+                        title='Multi level menu item',
+                        children=[
+                            MenuItem('Child 1'),
+                            MenuItem('Child 2'),
+                        ]
+                    ),
+                )
 
     Below is a screenshot of the resulting menu:
 
         css = ()
         js  = ()
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, **kwargs):
         """
-        Manu constructor, keyword argument:
-        * ``template``: the path to the menu template (optional)
+        Menu constructor.
         """
-        super(Menu, self).__init__()
         self.template = kwargs.get('template', 'menu/menu.html')
+        self.children = kwargs.get('children', [])
 
-    def render(self, request):
+    def init_with_context(self, context):
         """
-        The ``Menu.render()`` method is called just before the display with a 
-        ``django.http.HttpRequest`` as unique argument.
-        Override this method to build your menu if you need to access to the
-        request instance.
+        Sometimes you may need to access context or request variables to build
+        your menu, this is what the ``init_with_context()`` method is for.
+        This method is called just before the display with a 
+        ``django.template.RequestContext`` as unique argument, so you can 
+        access to all context variables and to the ``django.http.HttpRequest``.
         """
         pass
 
 
-class MenuItem(list):
+class MenuItem(object):
     """
-    Base class for menu items.
-    A menu item is a simple python list that has some additional properties:
+    This is the base class for custom menu items.
+    A menu item can have the following properties:
 
     ``title``
         String that contains the menu item title, make sure you use the
 
     ``css_classes``
         A list of css classes to be added to the menu item ``li`` class 
-        attribute. Default value: None.
+        attribute. Default value: [].
 
     ``accesskey``
         The menu item accesskey. Default value: None.
         The template to use to render the menu item.
         Default value: 'menu/item.html'.
 
-    Menu items can be nested so for example you can do the following::
-
-        from admin_tools.menu.models import *
-
-        mymenu = Menu()
-        item = MenuItem(title='Foo')
-        item.append(MenuItem(title='Bar'))
-        mymenu.append(item)
+    ``children``
+        A list of children menu items. All children items must be instances of
+        the ``MenuItem`` class.
     """
 
-    def __init__(self, *args, **kwargs):
-        super(MenuItem, self).__init__()
+    def __init__(self, **kwargs):
+        """
+        ``MenuItem`` constructor.
+        """
         self.title = kwargs.get('title', 'Untitled menu item')
         self.url = kwargs.get('url', '#')
         self.css_classes = kwargs.get('css_classes', [])
         self.accesskey = kwargs.get('accesskey')
         self.description = kwargs.get('description')
         self.template = kwargs.get('template', 'menu/item.html')
+        self.children = kwargs.get('children', [])
 
-    def render(self, request):
+    def init_with_context(self, context):
         """
-        The ``MenuItem.render()`` is called just before the display with a 
-        ``django.http.HttpRequest`` as unique argument.
-        You can use it to build your item when you need to access the request
-        instance, for example::
+        Like for menus, menu items have a ``init_with_context`` method that is
+        called with a ``django.template.RequestContext`` instance as unique 
+        argument.
+        This gives you enough flexibility to build complex items, for example,
+        let's build a "history" menu item, that will list the last ten visited
+        pages::
+            
+            from admin_tools.menu.models import *
 
-            from admin_tools.menu.models import *
-        
-            class MyMenuItem(MenuItem):
-                def render(self, request):
-                    if request.user.username == 'foo':
-                        self.title = 'Foo'
-                    else:
-                        self.title = 'Bar'
+            class HistoryMenuItem(MenuItem):
+                def init_with_context(self, context):
+                    self.title = 'History'
+                    request = context['request']
+                    # we use sessions to store the visited pages stack
+                    history = request.session.get('history', [])
+                    for item in history:
+                        self.children.append(MenuItem(
+                            title=item['title'],
+                            url=item['url']
+                        ))
+                    # add the current page to the history
+                    history.insert(0, {
+                        'title': context['title'],
+                        'url': request.META['PATH_INFO']
+                    })
+                    if len(history) > 10:
+                        history = history[:10]
+                    request.session['history'] = history
 
-            mymenu = Menu()
-            mymenu.append(MyMenuItem())
+        Here's a screenshot of our history item:
+
+        .. image:: images/history_menu_item.png
         """
         pass
 
 class AppListMenuItem(MenuItem, AppListElementMixin):
     """
     A menu item that lists installed apps an their models.
-    As well as the ``MenuItem`` properties, the ``AppListMenuItem`` has two
-    extra properties:
+    In addition to the parent ``MenuItem`` properties, the ``AppListMenuItem``
+    has two extra properties:
 
     ``exclude_list``
         A list of apps to exclude, if an app name (e.g. "django.contrib.auth"
     Here's a small example of building an app list menu item::
  
         from admin_tools.menu.models import *
-     
-        mymenu = Menu()
-
-        # will list all apps except the django.contrib ones
-        mymenu.append(AppListMenuItem(
-            title='Applications',
-            exclude_list=('django.contrib',)
-        ))   
+         
+        class MyMenu(Menu):
+            def __init__(self, **kwargs):
+                super(MyMenu, self).__init__(**kwargs)
+                self.children.append(AppListMenuItem(
+                    title='Applications',
+                    exclude_list=('django.contrib',)
+                )
 
     The screenshot of what this code produces:
 
 
     .. note::
 
-        Note that this module takes into account user permissions, for 
-        example, if a user has no rights to change or add a ``Group``, then
-        the django.contrib.auth.Group model child item will not be displayed.
+        Note that this module takes into account user permissions, as a
+        consequence, if a user has no rights to change or add a ``Group`` for
+        example, the ``django.contrib.auth.Group model`` child item won't be 
+        displayed in the menu item.
     """
 
-    def __init__(self, *args, **kwargs):
-        super(AppListMenuItem, self).__init__(*args, **kwargs)
+    def __init__(self, **kwargs):
+        """
+        ``AppListMenuItem`` constructor.
+        """
+        super(AppListMenuItem, self).__init__(**kwargs)
         self.include_list = kwargs.get('include_list', [])
         self.exclude_list = kwargs.get('exclude_list', [])
 
-    def render(self, request):
+    def init_with_context(self, context):
+        """
+        Please refer to the ``MenuItem::init_with_context()`` documentation.
+        """
+        request = context['request']
         apps = {}
         for model, model_admin in admin.site._registry.items():
             perms = self._check_perms(request, model, model_admin)
             # sort model list alphabetically
             apps[app]['models'].sort(lambda x, y: cmp(x['title'], y['title']))
             for model_dict in apps[app]['models']:
-                item.append(MenuItem(**model_dict))
-            self.append(item)
+                item.children.append(MenuItem(**model_dict))
+            self.children.append(item)
 
 
 class DefaultMenu(Menu):
     """
-    The default menu displayed by default by django-admin-tools.
+    The default menu displayed by django-admin-tools.
     To change the default menu you'll have to type the following from the
     commandline in your project root directory::
 
     And then set the ``ADMIN_TOOLS_MENU`` settings variable to point to your
     custom menu class.
     """
-    def render(self, request):
-        self.append(MenuItem(
+    def __init__(self, **kwargs):
+        super(DefaultMenu, self).__init__(**kwargs)
+        self.children.append(MenuItem(
             title=_('Dashboard'),
             url=reverse('admin:index')
         ))
-        self.append(AppListMenuItem(
+        self.children.append(AppListMenuItem(
             title=_('Applications'),
-            exclude_list=('django.contrib',),
+            exclude_list=('django.contrib',)
         ))
-        self.append(AppListMenuItem(
+        self.children.append(AppListMenuItem(
             title=_('Administration'),
-            include_list=('django.contrib',),
+            include_list=('django.contrib',)
         ))

admin_tools/menu/templates/menu/item.html

 {% load menu_tags %}
 {% spaceless %}
 <li class="menu-item{% ifequal index 1 %} first{% endifequal %}{% if item.css_classes %} {{ item.css_classes|join:' ' }}{% endif %}">
-    <a href="{{ item.url }}"{% if item.description %} title="{{ item.description }}"{% endif %}{% if item.accesskey %} accesskey="{{ item.accesskey }}"{% endif %}>{% if item %}<span class="icon"></span>{% endif %}{{ item.title }}</a>
-    {% if item %}
+    <a href="{{ item.url }}"{% if item.description %} title="{{ item.description }}"{% endif %}{% if item.accesskey %} accesskey="{{ item.accesskey }}"{% endif %}>{% if item.children %}<span class="icon"></span>{% endif %}{{ item.title }}</a>
+    {% if item.children %}
     <ul>
-        {% for child_item in item %}
+        {% for child_item in item.children %}
         {% render_menu_item child_item %}
         {% endfor %}
     </ul>

admin_tools/menu/templates/menu/menu.html

 {% load menu_tags %}
-{% if menu %}
+{% if menu.children %}
 <!--[if IE 6]>
 <script type="text/javascript">
 var hover_ie6 = function() {
 <![endif]-->
 
 <ul id="navigation-menu">
-    {% for item in menu %}{% render_menu_item item forloop.counter %}{% endfor %}
+    {% for item in menu.children %}{% render_menu_item item forloop.counter %}{% endfor %}
 </ul>
 {% endif %}

admin_tools/menu/templates/menu/menu.txt

 from django.utils.translation import ugettext_lazy as _
 from admin_tools.menu.models import *
 
-# create your custom menu items here if you want:
-#
-# class CustomMenuItem(MenuItem):
-#     pass
-#
-
-
-# to activate your custom menu you must add the following to your 
-# project's settings.py file:
+# to activate your custom menu add the following to your settings.py:
 #
 # ADMIN_TOOLS_MENU = '{{ project }}.{{ file }}.CustomMenu'
+
 class CustomMenu(Menu):
     """
-    Returns the admin menu defined by the user or the default one.
+    Custom Menu for {{ project }} admin site.
     """
-    def render(self, request):
-        self.append(MenuItem(
+    def __init__(self, **kwargs):
+        Menu.__init__(self, **kwargs)
+        self.children.append(MenuItem(
             title=_('Dashboard'),
             url=reverse('admin:index')
         ))
-        self.append(AppListMenuItem(
+        self.children.append(AppListMenuItem(
             title=_('Applications'),
-            exclude_list=('django.contrib',),
+            exclude_list=('django.contrib',)
         ))
-        self.append(AppListMenuItem(
+        self.children.append(AppListMenuItem(
             title=_('Administration'),
-            include_list=('django.contrib',),
+            include_list=('django.contrib',)
         ))
+
+    def init_with_context(self, context):
+        """
+        Use this method if you need to access the request context.
+        """
+        pass

admin_tools/menu/templatetags/menu_tags.py

 """
 Menu template tags, the following menu tags are available:
-* ``{% render_menu %}``
-* ``{% render_menu_item %}``
 
-To load the menu tags just do: ``{% load menu_tags %}``.
+ * ``{% render_menu %}``
+ * ``{% render_menu_item %}``
+ * ``{% render_menu_css %}``
+
+To load the menu tags in your templates: ``{% load menu_tags %}``.
 """
 
 from django import template
 from admin_tools.menu.utils import get_admin_menu
 
 register = template.Library()
+tag_func = register.inclusion_tag('menu/dummy.html', takes_context=True)
 
 def render_menu(context, menu=None):
     """
     ``get_admin_menu`` function.
     """
     if menu is None:
-        menu = get_admin_menu(context['request'])
+        menu = get_admin_menu()
 
-    menu.render(context['request'])
+    menu.init_with_context(context)
+
     context.update({
         'template': menu.template,
         'menu': menu,
         'media_url': settings.MEDIA_URL.rstrip('/'),
     })
     return context
-render_menu = register.inclusion_tag(
-    'menu/dummy.html',
-    takes_context=True
-)(render_menu)
+render_menu = tag_func(render_menu)
 
 
 def render_menu_item(context, item, index=None):
     Template tag that renders a given menu item, it takes a ``MenuItem``
     instance as unique parameter.
     """
-    item.render(context['request'])
+    item.init_with_context(context)
+
     context.update({
         'template': item.template,
         'item': item,
         'index': index,
     })
     return context
-render_menu_item = register.inclusion_tag(
-    'menu/dummy.html',
-    takes_context=True
-)(render_menu_item)
+render_menu_item = tag_func(render_menu_item)
 
 
 def render_menu_css(context, menu=None):
     """
-    Template tag that renders the menu css files.
+    Template tag that renders the menu css files,, it takes an optional 
+    ``Menu`` instance as unique argument, if not given, the menu will be
+    retrieved with the ``get_admin_menu`` function.
     """
     if menu is None:
-        menu = get_admin_menu(context['request'])
+        menu = get_admin_menu()
 
     context.update({
+        'template': 'menu/css.html',
         'css_files': menu.Media.css,
         'media_url': settings.MEDIA_URL.rstrip('/'),
     })
     return context
-render_menu_css = register.inclusion_tag(
-    'menu/css.html',
-    takes_context=True
-)(render_menu_css)
+render_menu_css = tag_func(render_menu_css)

admin_tools/menu/utils.py

 
 from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import reverse
-from django.utils.translation import ugettext_lazy as _
 from django.utils.importlib import import_module
-from admin_tools.menu.models import Menu, MenuItem, AppListMenuItem
 
 
-def get_admin_menu(request):
+def get_admin_menu():
     """
     Returns the admin menu defined by the user or the default one.
     """
     try:
         mod, inst = menu_cls.rsplit('.', 1)
         mod = import_module(mod)
-    except:
+    except Exception, exc:
         raise ImproperlyConfigured((
             'The class pointed by your ADMIN_TOOLS_MENU setting variable '
-            'cannot be imported'
+            'cannot be imported: %s' % exc.message
         ))
     return getattr(mod, inst)()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.