Commits

David Jean Louis  committed a642340

greatly improved documentation, better customization API

  • Participants
  • Parent commits b8e6a3a

Comments (0)

Files changed (29)

File admin_tools/dashboard/__init__.py

+"""
+Dashboard registry.
+"""
+
+from admin_tools.dashboard.models import Dashboard
+
+class Registry(object):
+    """
+    Registry for application dashboards.
+    """
+    registry = {}
+
+    def register(cls, klass, app_name):
+        if not issubclass(klass, Dashboard):
+            raise ValueError('%s is not an instance of Dashboard' % klass)
+        if app_name in cls.registry:
+            raise ValueError('A dashboard has already been registered for '
+                             'the application "%s"', app_name)
+        cls.registry[app_name] = klass
+    register = classmethod(register)
+
+
+def register(cls, *args, **kwargs):
+    """
+    Register a custom dashboard into the global registry.
+    """
+    Registry.register(cls, *args, **kwargs)
+
+
+def autodiscover(blacklist=[]):
+    """
+    Automagically discover custom dashboards and menus for installed apps.
+    Optionally you can pass a ``blacklist`` of apps that you don't want to 
+    provide their own app index dashboard.
+    """
+    import imp
+    from django.conf import settings
+    from django.utils.importlib import import_module
+
+    blacklist.append('admin_tools.dashboard')
+    blacklist.append('admin_tools.menu')
+    blacklist.append('admin_tools.theming')
+
+    for app in settings.INSTALLED_APPS:
+        # skip blacklisted apps
+        if app in blacklist:
+            continue
+
+        # try to import the app
+        try:
+            app_path = import_module(app).__path__
+        except AttributeError:
+            continue
+
+        # try to find a app.dashboard module
+        try:
+            imp.find_module('dashboard', app_path)
+        except ImportError:
+            continue
+
+        # looks like we found it so import it !
+        import_module('%s.dashboard' % app)

File admin_tools/dashboard/default_dashboard.py

-"""
-django-admin-tools default dashboards.
-"""
-
-from django.core.urlresolvers import reverse
-from django.utils.translation import ugettext_lazy as _
-from admin_tools.dashboard.models import *
-
-
-class DefaultIndexDashboard(Dashboard):
-    """
-    Default admin index dashboard.
-    """ 
-    def __init__(self, *args, **kwargs):
-        super(DefaultIndexDashboard, self).__init__(*args, **kwargs)
-
-        # append a link list module for "quick links"
-        self.append(LinkListDashboardModule(
-            title=_('Quick links'),
-            layout='inline',
-            draggable=False,
-            deletable=False,
-            collapsible=False,
-            entries=[
-                {
-                    'title': _('Return to site'),
-                    'url': '/',
-                },
-                {
-                    'title': _('Change password'),
-                    'url': reverse('admin:password_change'),
-                },
-                {
-                    'title': _('Log out'),
-                    'url': reverse('admin:logout')
-                },
-            ]
-        ))
-
-        # append an app list module for "Applications"
-        self.append(AppListDashboardModule(
-            title=_('Applications'),
-            exclude_list=('django.contrib',),
-        ))
-
-        # append an app list module for "Administration"
-        self.append(AppListDashboardModule(
-            title=_('Administration'),
-            include_list=('django.contrib',),
-        ))
-
-        # append a recent actions module
-        self.append(RecentActionsDashboardModule(
-            enabled=False,
-            title=_('Recent Actions'),
-            limit=5
-        ))
-
-        # append a feed module
-        self.append(FeedDashboardModule(
-            enabled=False,
-            title=_('Latest Django News'),
-            feed_url='http://www.djangoproject.com/rss/weblog/',
-            limit=5
-        ))
-
-        # append another link list module for "support". 
-        self.append(LinkListDashboardModule(
-            title=_('Support'),
-            entries=[
-                {
-                    'title': _('Django documentation'),
-                    'url': 'http://docs.djangoproject.com/',
-                    'external': True,
-                },
-                {
-                    'title': _('Django "django-users" mailing list'),
-                    'url': 'http://groups.google.com/group/django-users',
-                    'external': True,
-                },
-                {
-                    'title': _('Django irc channel'),
-                    'url': 'irc://irc.freenode.net/django',
-                    'external': True,
-                },
-            ]
-        ))
-
-
-class DefaultAppIndexDashboard(AppIndexDashboard):
-    """
-    Default admin app index dashboard.
-    """
-    def __init__(self, *args, **kwargs):
-        super(DefaultAppIndexDashboard, self).__init__(*args, **kwargs)
-
-        # we disable title because its redundant with the model list module
-        self.title = ''
-
-        # append a model list module
-        self.append(ModelListDashboardModule(
-            title=self.app_title,
-            include_list=self.models,
-        ))
-
-        # append a recent actions module
-        self.append(RecentActionsDashboardModule(
-            title=_('Recent Actions'),
-            include_list=self.models,
-            limit=5
-        ))

File admin_tools/dashboard/models.py

 class Dashboard(list):
     """
     Base class for dashboards.
-    The Dashboard class is a simple python list that takes three optional 
-    keywords arguments ``title``, ``template`` and ``columns``.
+    The Dashboard class is a simple python list that has three additional
+    properties:
 
-    >>> d = Dashboard(template='foo.html', columns=3)
-    >>> d.template
-    'foo.html'
-    >>> d.columns
-    3
-    >>> d.append(DashboardModule())
-    >>> d.append(DashboardModule())
-    >>> len(d)
-    2
-    >>> d.pop().__class__.__name__
-    'DashboardModule'
-    >>> len(d)
-    1
+    ``title``
+        The dashboard title, by default, it is displayed above the dashboard
+        in a ``h2`` tag. Default value: 'Dashboard'.
+
+    ``template``
+        The template to use to render the dashboard.
+        Default value: 'dashboard/dashboard.html'
+
+    ``columns``
+        An integer that represents the number of columns for the dashboard.
+        Default value: 2.
+
+    If you want to customize the look of your dashboard and it's modules, you
+    can declare css stylesheets and/or javascript files to include when 
+    rendering the dashboard, for example::
+
+        from admin_tools.dashboard.models import *
+
+        class MyDashboard(Dashboard):
+            class Media:
+                css = {'screen': '/media/css/mydashboard.css'}
+                js = ('/media/js/mydashboard.js',)
+
+    Here's an example of a custom dashboard::
+
+        from django.core.urlresolvers import reverse
+        from django.utils.translation import ugettext_lazy as _
+        from admin_tools.dashboard.models import *
+
+        class MyDashboard(Dashboard):
+            def render(self, request):
+                # we want a 3 columns layout
+                self.columns = 3
+
+                # append an app list module for "Applications"
+                self.append(AppListDashboardModule(
+                    title=_('Applications'),
+                    exclude_list=('django.contrib',),
+                ))
+        
+                # append an app list module for "Administration"
+                self.append(AppListDashboardModule(
+                    title=_('Administration'),
+                    include_list=('django.contrib',),
+                ))
+        
+                # append a recent actions module
+                self.append(RecentActionsDashboardModule(
+                    enabled=False,
+                    title=_('Recent Actions'),
+                    limit=5
+                ))
+
+    Below is a screenshot of the resulting dashboard:
+
+    .. image:: images/dashboard_example.png
     """
     class Media:
         css = {
 
     def __init__(self, *args, **kwargs):
         """
-        Dashboard constructor, keyword argument:
-        
-        ``title``
-            the title to display for your dashboard.
-            Default value: 'Dashboard'.
-        
-        ``template``
-            the path to the dashboard template.
-            Default value: 'dashboard/dashboard.html'.
-
-        ``columns``
-            The number of columns for the dashboard. Default value: 2.
+        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)
 
+    def render(self, request):
+        """
+        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.
+        """
+        pass
+
 
 class AppIndexDashboard(Dashboard):
     """
-    Class that represents an app index dashboard, it is very similar to the 
-    standard dashboard except that its constructors receives two arguments:
+    Class that represents an app index dashboard, app index dashboards are 
+    displayed in the applications index page.
+    ``AppIndexDashboard`` is very similar to the ``Dashboard`` class except
+    that its constructor receives two extra arguments:
 
     ``app_title``
         The title of the application
 
     If you want to provide custom app index dashboard, be sure to inherit from
     this class instead of the ``Dashboard`` class.
+
+    Here's an example of a custom app index dashboard::
+
+        from django.core.urlresolvers import reverse
+        from django.utils.translation import ugettext_lazy as _
+        from admin_tools.dashboard.models import *
+
+        class MyAppIndexDashboard(AppIndexDashboard):
+            def render(self, request):
+                # 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(
+                    title=self.app_title,
+                    include_list=self.models,
+                ))
+        
+                # append a recent actions module for the current app
+                self.append(RecentActionsDashboardModule(
+                    title=_('Recent Actions'),
+                    include_list=self.models,
+                    limit=5
+                ))
+
+    Below is a screenshot of the resulting dashboard:
+
+    .. image:: images/dashboard_app_index_example.png
     """
     def __init__(self, app_title, models, *args, **kwargs):
         super(AppIndexDashboard, self).__init__(*args, **kwargs)
         self.app_title = app_title
         self.models = models
 
+
 class DashboardModule(object):
     """
     Base class for all dashboard modules.
+    Dashboard modules have the following properties:
+
+    ``enabled``
+        Boolean that determines whether the module should be enabled in 
+        the dashboard by default or not. Default value: ``True``.
+
+    ``draggable``
+        Boolean that determines whether the module can be draggable or not.
+        Draggable modules can be re-arranged by users. Default value: ``True``.
+
+    ``collapsible``
+        Boolean that determines whether the module is collapsible, this 
+        allows users to show/hide module content. Default: ``True``.
+
+    ``deletable``
+        Boolean that determines whether the module can be removed from the 
+        dashboard by users or not. Default: ``True``.
+
+    ``title``
+        String that contains the module title, make sure you use the django
+        gettext functions if your application is multilingual. 
+        Default value: ''.
+
+    ``title_url``
+        String that contains the module title URL. If given the module 
+        title will be a link to this URL. Default value: ``None``.
+
+    ``css_classes``
+        A list of css classes to be added to the module ``div`` class 
+        attribute. Default value: ``None``.
+
+    ``pre_content``
+        Text or HTML content to display above the module content.
+        Default value: ``None``.
+
+    ``content``
+        The module text or HTML content. Default value: ``None``.
+
+    ``post_content``
+        Text or HTML content to display under the module content.
+        Default value: ``None``.
+
+    ``template``
+        The template to use to render the module.
+        Default value: 'dashboard/module.html'.
     """
     def __init__(self, *args, **kwargs):
-        """
-        Dashboard module constructor, keywords arguments (all are optional):
-
-        ``enabled``
-            Boolean that determines whether the module should be enabled in 
-            the dashboard by default or not. Default value: True.
-
-        ``draggable``
-            Boolean that determines whether the module can be draggable or not.
-            Draggable modules can be re-arranged by users. Default value: True.
-
-        ``collapsible``
-            Boolean that determines whether the module is collapsible, this 
-            allows users to show/hide module content. Default: True.
-
-        ``deletable``
-            Boolean that determines whether the module can be removed from the 
-            dashboard by users or not. Default: True.
-
-        ``title``
-            String that contains the module title, make sure you use the django
-            gettext functions if your application is multilingual. 
-            Default value: ''.
-
-        ``title_url``
-            String that contains the module title URL. If given the module 
-            title will be a link to this URL. Default value: None.
-
-        ``css_classes``
-            A list of css classes to be added to the module ``div`` class 
-            attribute. Default value: None.
-
-        ``pre_content``
-            Text or HTML content to display above the module content.
-            Default value: None.
-
-        ``content``
-            The module text or HTML content. Default value: None.
-
-        ``post_content``
-            Text or HTML content to display under the module content.
-            Default value: None.
-
-        ``template``
-            The template to use to render the module.
-            Default value: 'dashboard/module.html'.
-        """
         self.enabled = kwargs.get('enabled', True)
         self.draggable = kwargs.get('draggable', True)
         self.collapsible = kwargs.get('collapsible', True)
         return ' '.join(ret)
 
 
-class TextDashboardModule(DashboardModule):
-    """
-    Dashboard module that displays a list of links.
-    """
-
-    def __init__(self, *args, **kwargs):
-        super(TextDashboardModule, self).__init__(*args, **kwargs)
-        self.entries.append(kwargs.get('text', ''))
-
-
 class LinkListDashboardModule(DashboardModule):
     """
-    Dashboard module that displays a list of links.
+    A module that displays a list of links.
+    As well as the ``DashboardModule`` properties, the
+    ``LinkListDashboardModule`` takes an extra keyword argument:
+
+    ``layout``
+        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
+    following keys:
+
+    ``title``
+        The link title.
+
+    ``url``
+        The link URL.
+
+    ``external``
+        Boolean that indicates whether the link is an external one or not.
+
+    ``description``
+        A string describing the link, it will be the ``title`` attribute of
+        the html ``a`` tag.
+
+    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
+                },
+            )
+        ))
+
+    The screenshot of what this code produces:
+
+    .. image:: images/linklist_dashboard_module.png
     """
 
     def __init__(self, *args, **kwargs):
 
 class AppListDashboardModule(DashboardModule, AppListElementMixin):
     """
-    Class that represents a dashboard module that lists installed apps.
+    Module that lists installed apps and their models.
+    As well as the ``DashboardModule`` properties, the
+    ``AppListDashboardModule`` has two extra properties:
+
+    ``exclude_list``
+        A list of apps to exclude, if an app name (e.g. "django.contrib.auth"
+        starts with an element of this list (e.g. "django.contrib") it won't
+        appear in the dashboard module.
+
+    ``include_list``
+        A list of apps to include, only apps whose name (e.g. 
+        "django.contrib.auth") starts with one of the strings (e.g. 
+        "django.contrib") in the list will appear in the dashboard module.
+
+    If no include/exclude list is provided, **all apps** are shown.
+
+    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',)
+        )) 
+
+    The screenshot of what this code produces:
+
+    .. image:: images/applist_dashboard_module.png
+
+    .. 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 line will not be displayed.
     """
 
     def __init__(self, *args, **kwargs):
         super(AppListDashboardModule, self).__init__(*args, **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', [])
-        self.template = kwargs.get('template',
-                                   'dashboard/modules/app_list.html')
 
     def render(self, request):
         apps = {}
         for model, model_admin in admin.site._registry.items():
             perms = self._check_perms(request, model, model_admin)
-            if not perms:
+            if not perms or ('add' not in perms and 'change' not in perms):
                 continue
             app_label = model._meta.app_label
             if app_label not in apps:
 
 
 class ModelListDashboardModule(DashboardModule, AppListElementMixin):
+    """
+    Module that lists a set of models.
+    As well as the ``DashboardModule`` properties, the
+    ``ModelListDashboardModule`` takes two extra keyword arguments:
+
+    ``include_list``
+        A list of models to include, only models whose name (e.g. 
+        "blog.comments.Comment") starts with one of the strings (e.g. "blog") 
+        in the include list will appear in the dashboard module.
+
+    ``exclude_list``
+        A list of models to exclude, if a model name (e.g. 
+        "blog.comments.Comment" starts with an element of this list (e.g. 
+        "blog.comments") it won't appear in the dashboard module.
+
+    Here's a small example of building a model list module::
+        
+        from admin_tools.dashboard.models import *
+        
+        mydashboard = Dashboard()
+        # will only list the django.contrib.auth models
+        mydashboard.append(ModelListDashboardModule(
+            title='Authentication',
+            include_list=('django.contrib.auth',)
+        )) 
+
+    The screenshot of what this code produces:
+
+    .. image:: images/recentactions_dashboard_module.png
+
+    .. 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 line will not be displayed.
+    """
 
     def __init__(self, *args, **kwargs):
         super(ModelListDashboardModule, self).__init__(*args, **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', [])
-        self.template = kwargs.get('template',
-                                   'dashboard/modules/model_list.html')
 
     def render(self, request):
         for model, model_admin in admin.site._registry.items():
 class RecentActionsDashboardModule(DashboardModule):
     """
     Module that lists the recent actions for the current user.
+    As well as the ``DashboardModule`` properties, the
+    ``RecentActionsDashboardModule`` takes three extra keyword arguments:
+
+    ``include_list``
+        A list of models to include, only actions for models whose name (e.g. 
+        "blog.comments.Comment") starts with one of the strings (e.g. "blog") 
+        in the include list will appear in the dashboard module.
+
+    ``exclude_list``
+        A list of models to exclude, if a model name (e.g. 
+        "blog.comments.Comment" starts with an element of this list (e.g. 
+        "blog.comments") it's recent actions won't appear in the dashboard
+        module.
+
+    ``limit``
+        The maximum number of entries 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',)
+        ))
+
+    The screenshot of what this code produces:
+
+    .. image:: images/recentactions_dashboard_module.png
     """
 
     def __init__(self, *args, **kwargs):
         super(RecentActionsDashboardModule, self).__init__(*args, **kwargs)
         self.title = kwargs.get('title', _('Recent Actions'))
+        self.template = kwargs.get('template',
+                                   'dashboard/modules/recent_actions.html')
         self.include_list = kwargs.get('include_list', [])
         self.exclude_list = kwargs.get('exclude_list', [])
-        self.limit = kwargs.get('limit', [])
-        self.template = kwargs.get('template',
-                                   'dashboard/modules/recent_actions.html')
+        self.limit = kwargs.get('limit', 10)
 
     def render(self, request):
         from django.contrib.admin.models import LogEntry
 class FeedDashboardModule(DashboardModule):
     """
     Class that represents a feed dashboard module.
+
+    .. important::
+
+        This class uses the 
+        `Universal Feed Parser module <http://www.feedparser.org/>`_ to parse 
+        the feeds, so you'll need to install it, all feeds supported by 
+        FeedParser are thus supported by the FeedDashboardModule.
+
+    As well as the ``DashboardModule`` properties, the ``FeedDashboardModule``
+    takes two extra keyword arguments:
+
+    ``feed_url``
+        The URL of the feed.
+
+    ``limit``
+        The maximum number of feed entries to display. Default value: None, 
+        which means that all entries 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
+        ))
+
+    The screenshot of what this code produces:
+
+    .. image:: images/feed_dashboard_module.png
     """
     def __init__(self, *args, **kwargs):
         super(FeedDashboardModule, self).__init__(*args, **kwargs)
                 # no date for certain feeds
                 pass
             self.entries.append(entry)
+
+
+class DefaultIndexDashboard(Dashboard):
+    """
+    The default dashboard displayed on the admin index page.
+    To change the default dashboard you'll have to type the following from the
+    commandline in your project root directory::
+
+        python manage.py customdashboard
+
+    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)
+
+        # append a link list module for "quick links"
+        self.append(LinkListDashboardModule(
+            title=_('Quick links'),
+            layout='inline',
+            draggable=False,
+            deletable=False,
+            collapsible=False,
+            entries=[
+                {
+                    'title': _('Return to site'),
+                    'url': '/',
+                },
+                {
+                    'title': _('Change password'),
+                    'url': reverse('admin:password_change'),
+                },
+                {
+                    'title': _('Log out'),
+                    'url': reverse('admin:logout')
+                },
+            ]
+        ))
+
+        # append an app list module for "Applications"
+        self.append(AppListDashboardModule(
+            title=_('Applications'),
+            exclude_list=('django.contrib',),
+        ))
+
+        # append an app list module for "Administration"
+        self.append(AppListDashboardModule(
+            title=_('Administration'),
+            include_list=('django.contrib',),
+        ))
+
+        # append a recent actions module
+        self.append(RecentActionsDashboardModule(
+            enabled=False,
+            title=_('Recent Actions'),
+            limit=5
+        ))
+
+        # append a feed module
+        self.append(FeedDashboardModule(
+            enabled=False,
+            title=_('Latest Django News'),
+            feed_url='http://www.djangoproject.com/rss/weblog/',
+            limit=5
+        ))
+
+        # append another link list module for "support". 
+        self.append(LinkListDashboardModule(
+            title=_('Support'),
+            entries=[
+                {
+                    'title': _('Django documentation'),
+                    'url': 'http://docs.djangoproject.com/',
+                    'external': True,
+                },
+                {
+                    'title': _('Django "django-users" mailing list'),
+                    'url': 'http://groups.google.com/group/django-users',
+                    'external': True,
+                },
+                {
+                    'title': _('Django irc channel'),
+                    'url': 'irc://irc.freenode.net/django',
+                    'external': True,
+                },
+            ]
+        ))
+
+
+class DefaultAppIndexDashboard(AppIndexDashboard):
+    """
+    The default dashboard displayed on the applications index page.
+    To change the default dashboard you'll have to type the following from the
+    commandline in your project root directory::
+
+        python manage.py customdashboard
+
+    And then set the ``ADMIN_TOOLS_APP_INDEX_DASHBOARD`` settings variable to 
+    point to your custom app index dashboard class.
+    """
+    def render(self, request):
+
+        # we disable title because its redundant with the model list module
+        self.title = ''
+
+        # append a model list module
+        self.append(ModelListDashboardModule(
+            title=self.app_title,
+            include_list=self.models,
+        ))
+
+        # append a recent actions module
+        self.append(RecentActionsDashboardModule(
+            title=_('Recent Actions'),
+            include_list=self.models,
+            limit=5
+        ))

File admin_tools/dashboard/templates/dashboard/dashboard.txt

     """
     Document your custom dashboard.
     """ 
-    def __init__(self, *args, **kwargs):
-        super(CustomIndexDashboard, self).__init__(*args, **kwargs)
+    def render(self, request):
+        # append a link list module for "quick links"
+        self.append(LinkListDashboardModule(
+            title=_('Quick links'),
+            layout='inline',
+            draggable=False,
+            deletable=False,
+            collapsible=False,
+            entries=[
+                {
+                    'title': _('Return to site'),
+                    'url': '/',
+                },
+                {
+                    'title': _('Change password'),
+                    'url': reverse('admin:password_change'),
+                },
+                {
+                    'title': _('Log out'),
+                    'url': reverse('admin:logout')
+                },
+            ]
+        ))
 
-        # append your modules here, example:
+        # append an app list module for "Applications"
         self.append(AppListDashboardModule(
-            title=_('Modules'),
+            title=_('Applications'),
+            exclude_list=('django.contrib',),
         ))
+
+        # append an app list module for "Administration"
+        self.append(AppListDashboardModule(
+            title=_('Administration'),
+            include_list=('django.contrib',),
+        ))
+
+        # append a recent actions module
         self.append(RecentActionsDashboardModule(
+            enabled=False,
             title=_('Recent Actions'),
             limit=5
         ))
 
+        # append a feed module
+        self.append(FeedDashboardModule(
+            enabled=False,
+            title=_('Latest Django News'),
+            feed_url='http://www.djangoproject.com/rss/weblog/',
+            limit=5
+        ))
+
+        # append another link list module for "support". 
+        self.append(LinkListDashboardModule(
+            title=_('Support'),
+            entries=[
+                {
+                    'title': _('Django documentation'),
+                    'url': 'http://docs.djangoproject.com/',
+                    'external': True,
+                },
+                {
+                    'title': _('Django "django-users" mailing list'),
+                    'url': 'http://groups.google.com/group/django-users',
+                    'external': True,
+                },
+                {
+                    'title': _('Django irc channel'),
+                    'url': 'irc://irc.freenode.net/django',
+                    'external': True,
+                },
+            ]
+        ))
+
 
 # to activate your app index dashboard you must add the following to your 
 # project's settings.py file:
     """
     Document your custom app index dashboard.
     """ 
-    def __init__(self, app_title, models, *args, **kwargs):
-        super(CustomAppIndexDashboard, self).__init__(*args, **kwargs)
+    def render(self, request):
+        # we disable title because its redundant with the model list module
+        self.title = ''
 
-        # append your modules here, example:
-        self.append(AppListDashboardModule(
-            title=_('Modules'),
-            include_list=models,
+        # append a model list module
+        self.append(ModelListDashboardModule(
+            title=self.app_title,
+            include_list=self.models,
         ))
+
+        # append a recent actions module
         self.append(RecentActionsDashboardModule(
             title=_('Recent Actions'),
-            limit=5,
-            include_list=models,
+            include_list=self.models,
+            limit=5
         ))

File admin_tools/dashboard/templatetags/dashboard_tags.py

     """
     if not dashboard:
         dashboard = get_dashboard_from_context(context)
+    dashboard.render(context['request'])
     context.update({
         'template': dashboard.template,
         'dashboard': dashboard,

File admin_tools/dashboard/utils.py

 from django.http import HttpRequest
 from django.utils.importlib import import_module
 from django.utils.text import capfirst
-from django.utils.translation import ugettext_lazy as _
+from admin_tools.dashboard import Registry
 from admin_tools.dashboard.models import *
-from admin_tools.dashboard.default_dashboard import *
 
 
 def get_dashboard_from_context(context):
     try:
         app = context['app_list'][0]
         models = []
-        app_label = app['name']
+        app_label = None
+        app_title = app['name']
         for model, model_admin in admin.site._registry.items():
             if app['name'] == model._meta.app_label.title():
-                app_label = model._meta.app_label
+                split = model.__module__.find(model._meta.app_label)
+                app_label = model.__module__[0:split] + model._meta.app_label
+                app_title = model._meta.app_label.title
                 for m in app['models']:
                     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, models)
+        return get_app_index_dashboard(request, app_label, app_title, models)
     except KeyError:
         return get_app_index_dashboard(request, '', [])
 
     """
     Returns the admin dashboard defined by the user or the default one.
     """
-    dashboard_cls = getattr(settings, 'ADMIN_TOOLS_INDEX_DASHBOARD', False)
-    if dashboard_cls:
-        try:
-            mod, inst = dashboard_cls.rsplit('.', 1)
-            mod = import_module(mod)
-            return getattr(mod, inst)()
-        except:
-            raise ImproperlyConfigured((
-                'The class pointed by your ADMIN_TOOLS_INDEX_DASHBOARD '
-                'setting variable cannot be imported'
-            ))
-    return DefaultIndexDashboard()
+    dashboard_cls = getattr(
+        settings,
+        'ADMIN_TOOLS_INDEX_DASHBOARD',
+        'admin_tools.dashboard.models.DefaultIndexDashboard'
+    )
+    try:
+        mod, inst = dashboard_cls.rsplit('.', 1)
+        mod = import_module(mod)
+    except:
+        raise ImproperlyConfigured((
+            'The class pointed by your ADMIN_TOOLS_INDEX_DASHBOARD '
+            'setting variable cannot be imported'
+        ))
+    return getattr(mod, inst)()
 
 
-def get_app_index_dashboard(request, app_label='', model_list=[]):
+def get_app_index_dashboard(request, app_label=None, app_title='',
+                            model_list=[]):
     """
     Returns the admin dashboard defined by the user or the default one.
     """
-    app_title = app_label.title()
 
-    # try to discover corresponding app dashboard module
-    mod_name = getattr(settings, 'ADMIN_TOOLS_APP_INDEX_DASHBOARD_MODULE', 'dashboard')
-    mod_class = getattr(settings, 'ADMIN_TOOLS_APP_INDEX_DASHBOARD_CLASS', '%sDashboard' % capfirst(app_label))
+    # if an app has registered its own dashboard, use it
+    if app_label is not None and app_label in Registry.registry:
+        return Registry.registry[app_label](app_title, model_list)
+
+    # try to discover a general app_index dashboard (with fallback to the 
+    # default dashboard)
+    dashboard_cls = getattr(
+        settings,
+        'ADMIN_TOOLS_APP_INDEX_DASHBOARD',
+        'admin_tools.dashboard.models.DefaultAppIndexDashboard'
+    )
     try:
-        mod = import_module('%s.%s' % (app_label, mod_name))
-        return getattr(mod, mod_class)(app_title, model_list)
+        mod, inst = dashboard_cls.rsplit('.', 1)
+        mod = import_module(mod)
     except:
-        pass
-
-    # try to discover a general app_index dashboard
-    dashboard_cls = getattr(settings, 'ADMIN_TOOLS_APP_INDEX_DASHBOARD', False)
-    if dashboard_cls:
-        try:
-            mod, inst = dashboard_cls.rsplit('.', 1)
-            mod = import_module(mod)
-            return getattr(mod, inst)(app_title, model_list)
-        except:
-            raise ImproperlyConfigured((
-                'The class pointed by your ADMIN_TOOLS_APP_INDEX_DASHBOARD '
-                'setting variable cannot be imported'
-            ))
-
-    # fallback to default dashboard
-    return DefaultAppIndexDashboard(app_title, model_list)
+        raise ImproperlyConfigured((
+            'The class pointed by your ADMIN_TOOLS_APP_INDEX_DASHBOARD '
+            'setting variable cannot be imported'
+        ))
+    return getattr(mod, inst)(app_title, model_list)

File admin_tools/menu/models.py

 from django.contrib import admin
 from django.core.urlresolvers import reverse
 from django.utils.text import capfirst
+from django.utils.translation import ugettext_lazy as _
 from admin_tools.utils import AppListElementMixin
 
 
 class Menu(list):
     """
     Base class for menus.
-    The Menu class is a simple python list that takes an optional keyword 
-    argument ``template``.
+    The Menu class is a simple python list that has an extra property:
+    
+    ``template``
+        The template to use to render the menu.
+        Default value: "menu/menu.html".
 
-    >>> m = Menu(template='foo.html')
-    >>> m.template
-    'foo.html'
-    >>> m.append(MenuItem())
-    >>> m.append(MenuItem())
-    >>> len(m)
-    2
-    >>> m.pop().__class__.__name__
-    'MenuItem'
-    >>> len(m)
-    1
+    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 
+    rendering the menu, for example::
+
+        from admin_tools.menu.models import *
+
+        class MyMenu(Menu):
+            class Media:
+                css = {'screen': '/media/css/mymenu.css'}
+                js = ('/media/js/mymenu.js',)
+
+    Here's an example of a custom menu::
+
+        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)
+
+    Below is a screenshot of the resulting menu:
+
+    .. image:: images/menu_example.png
     """
 
     class Media:
         super(Menu, self).__init__()
         self.template = kwargs.get('template', 'menu/menu.html')
 
-    def is_empty(self):
+    def render(self, request):
         """
-        Return True if the menu is empty and false otherwise.
+        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.
         """
-        return len([i for i in self]) == 0
+        pass
 
 
 class MenuItem(list):
     """
     Base class for menu items.
-    A menu item is a simple python list that takes some optional keywords
-    arguments. Menu items can be nested.
+    A menu item is a simple python list that has some additional properties:
+
+    ``title``
+        String that contains the menu item title, make sure you use the
+        django gettext functions if your application is multilingual. 
+        Default value: 'Untitled menu item'.
+
+    ``url``
+        String that contains the menu item URL.
+        Default value: '#'.
+
+    ``css_classes``
+        A list of css classes to be added to the menu item ``li`` class 
+        attribute. Default value: None.
+
+    ``accesskey``
+        The menu item accesskey. Default value: None.
+
+    ``description``
+        An optional string that will be used as the ``title`` attribute of 
+        the menu-item ``a`` tag. Default value: None.
+
+    ``template``
+        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)
     """
 
     def __init__(self, *args, **kwargs):
-        """
-        MenuItem module constructor, keywords arguments (all are optional):
-
-        ``title``
-            String that contains the menu item title, make sure you use the
-            django gettext functions if your application is multilingual. 
-            Default value: 'Untitled menu item'.
-
-        ``url``
-            String that contains the menu item URL.
-            Default value: '#'.
-
-        ``css_classes``
-            A list of css classes to be added to the menu item ``li`` class 
-            attribute. Default value: None.
-
-        ``accesskey``
-            The menu item accesskey. Default value: None.
-
-        ``description``
-            An optional string that will be used as the ``title`` attribute of 
-            the menu-item ``a`` tag. Default value: None.
-
-        ``template``
-            The template to use to render the menu item.
-            Default value: 'menu/item.html'.
-        """
         super(MenuItem, self).__init__()
         self.title = kwargs.get('title', 'Untitled menu item')
         self.url = kwargs.get('url', '#')
         self.template = kwargs.get('template', 'menu/item.html')
 
     def render(self, request):
+        """
+        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::
+
+            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'
+
+            mymenu = Menu()
+            mymenu.append(MyMenuItem())
+        """
         pass
 
 
 class AppListMenuItem(MenuItem, AppListElementMixin):
     """
-    Class that represents a menu item that lists installed apps.
+    A menu item that lists installed apps an their models.
+    As well as the ``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"
+        starts with an element of this list (e.g. "django.contrib") it won't
+        appear in the menu item.
+
+    ``include_list``
+        A list of apps to include, only apps whose name (e.g. 
+        "django.contrib.auth") starts with one of the strings (e.g. 
+        "django.contrib") in the list will appear in the menu item.
+
+    If no include/exclude list is provided, **all apps** are shown.
+
+    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',)
+        ))   
+
+    The screenshot of what this code produces:
+
+    .. image:: images/applist_menu_item.png
+
+    .. 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.
     """
 
     def __init__(self, *args, **kwargs):
             for model_dict in apps[app]['models']:
                 item.append(MenuItem(**model_dict))
             self.append(item)
+
+
+class DefaultMenu(Menu):
+    """
+    The default menu displayed by default by django-admin-tools.
+    To change the default menu you'll have to type the following from the
+    commandline in your project root directory::
+
+        python manage.py custommenu
+
+    And then set the ``ADMIN_TOOLS_MENU`` settings variable to point to your
+    custom menu class.
+    """
+    def render(self, request):
+        self.append(MenuItem(
+            title=_('Dashboard'),
+            url=reverse('admin:index')
+        ))
+        self.append(AppListMenuItem(
+            title=_('Applications'),
+            exclude_list=('django.contrib',),
+        ))
+        self.append(AppListMenuItem(
+            title=_('Administration'),
+            include_list=('django.contrib',),
+        ))

File admin_tools/menu/templates/menu/menu.html

 window.onload = hover_ie6;
 </script>
 <![endif]-->
-{% if not menu.is_empty %}
+{% if menu %}
 <ul id="navigation-menu">
     {% for item in menu %}{% render_menu_item item forloop.counter %}{% endfor %}
 </div>

File admin_tools/menu/templates/menu/menu.txt

     """
     Returns the admin menu defined by the user or the default one.
     """
-    def __init__(self, *args, **kwargs):
-        super(CustomMenu, self).__init__(*args, **kwargs)
-
-        # append your menu items here, for example:
+    def render(self, request):
         self.append(MenuItem(
-            title=_('Home'),
+            title=_('Dashboard'),
             url=reverse('admin:index')
         ))
-        self.append(MenuItem(
-            title=_('Some link'),
-            url='http://example.com'
+        self.append(AppListMenuItem(
+            title=_('Applications'),
+            exclude_list=('django.contrib',),
         ))
+        self.append(AppListMenuItem(
+            title=_('Administration'),
+            include_list=('django.contrib',),
+        ))

File admin_tools/menu/templatetags/menu_tags.py

     """
     if menu is None:
         menu = get_admin_menu(context['request'])
+    menu.render(context['request'])
     context.update({
         'template': menu.template,
         'menu': menu,

File admin_tools/menu/utils.py

     """
     Returns the admin menu defined by the user or the default one.
     """
-    menu_cls = getattr(settings, 'ADMIN_TOOLS_MENU', False)
-    if menu_cls:
-        try:
-            mod, inst = menu_cls.rsplit('.', 1)
-            mod = import_module(mod)
-            return getattr(mod, inst)()
-        except:
-            raise ImproperlyConfigured((
-                'The class pointed by your ADMIN_TOOLS_MENU setting variable '
-                'cannot be imported'
-            ))
-
-    admin_menu = Menu()
-    admin_menu.append(MenuItem(
-        title=_('Dashboard'),
-        url=reverse('admin:index')
-    ))
-    admin_menu.append(AppListMenuItem(
-        title=_('Applications'),
-        exclude_list=('django.contrib',),
-    ))
-    admin_menu.append(AppListMenuItem(
-        title=_('Administration'),
-        include_list=('django.contrib',),
-    ))
-    return admin_menu
+    menu_cls = getattr(
+        settings,
+        'ADMIN_TOOLS_MENU',
+        'admin_tools.menu.models.DefaultMenu'
+    )
+    try:
+        mod, inst = menu_cls.rsplit('.', 1)
+        mod = import_module(mod)
+    except:
+        raise ImproperlyConfigured((
+            'The class pointed by your ADMIN_TOOLS_MENU setting variable '
+            'cannot be imported'
+        ))
+    return getattr(mod, inst)()

File docs/conf.py

 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.append(os.path.abspath('.'))
+sys.path.append(os.path.abspath('..'))
+# required for autodoc
+os.environ['DJANGO_SETTINGS_MODULE'] = 'django.conf.global_settings'
 
 # -- General configuration -----------------------------------------------------
 

File docs/configuration.rst

+.. _configuration:
+
+Configuring django-admin-tools
+==============================
+
+Basic configuration
+-------------------
+
+Once installed, you can add django-admin-tools to any Django-based
+project you're developing.
+
+django-admin-tools is composed of several modules:
+
+* admin_tools.theming: an app that makes it easy to customize the look 
+  and feel of the admin interface;
+
+* admin_tools.menu: a customizable navigation menu that sits on top of 
+  every django administration index page;
+
+* admin_tools.dashboard: a customizable dashboard that replaces the django 
+  administration index page.
+
+Required settings
+~~~~~~~~~~~~~~~~~
+
+First make sure you have the following template context processors 
+installed::
+
+    TEMPLATE_CONTEXT_PROCESSORS = (
+        'django.core.context_processors.auth',
+        'django.core.context_processors.request',
+    )
+
+Then, add the django-admin-tools modules to the ``INSTALLED_APPS`` like 
+this::
+
+    INSTALLED_APPS = (
+        'admin_tools.theming',
+        'admin_tools.menu',
+        'admin_tools.dashboard',
+        'django.contrib.auth',
+        'django.contrib.sites',
+        'django.contrib.admin'
+        # ...other installed applications...
+    )
+
+.. note::
+    it is very important that you put the admin_tools modules **before** 
+    the ``django.contrib.admin module``, because django-admin-tools
+    overrides the default django admin templates, and this will not work 
+    otherwise.
+
+django-admin-tools is modular, so if you want to disable a particular 
+module, just remove or comment it in your ``INSTALLED_APPS``. 
+For example, if you just want to use the dashboard::
+
+    INSTALLED_APPS = (
+        'admin_tools.dashboard',
+        'django.contrib.auth',
+        'django.contrib.sites',
+        'django.contrib.admin'
+        # ...other installed applications...
+    )
+
+
+Setting up the django-admin-tools media files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To do this you have two options:
+
+* create a symbolic link to the django-admin-tools media files to your 
+  ``MEDIA_ROOT`` directory, for example::
+
+      ln -s /usr/local/lib/python2.6/dist-packages/admin_tools/media/admin_tools /path/to/yourproject/media/
+
+* copy the django-admin-tools media files to your ``MEDIA_ROOT`` directory, 
+  for example::
+  
+      cp -r /usr/local/lib/python2.6/dist-packages/admin_tools/media/admin_tools /path/to/yourproject/media/
+
+
+Available settings variables
+----------------------------
+
+``ADMIN_TOOLS_MENU``
+    The path to your custom menu class, for example 
+    "yourproject.menu.CustomMenu".
+
+``ADMIN_TOOLS_INDEX_DASHBOARD``
+    The path to your custom index dashboard, for example 
+    "yourproject.dashboard.CustomIndexDashboard".
+
+``ADMIN_TOOLS_APP_INDEX_DASHBOARD``
+    The path to your custom app index dashboard, for example 
+    "yourproject.dashboard.CustomAppIndexDashboard".
+
+``ADMIN_TOOLS_THEMING_CSS``
+    The path to your theming css stylesheet, relative to your MEDIA_URL,
+    for example::
+
+        ADMIN_TOOLS_THEMING_CSS = 'css/theming.css'
+

File docs/customization.rst

 Introduction
 ------------
 
-todo: write docs for "Customizing introduction"
+django-admin-tools is very easy to customize, you can override the
+admin menu, the index dashboard and the app index dashboard.
+
+For this django-admin-tools provides two management commands:
+ * ``custommenu``
+ * ``customdashboard``
 
 
 Customizing the navigation menu
 -------------------------------
 
-todo: write docs for "Customizing the navigation menu"
+To customize the admin menu, the first step is to do the following::
+    
+    python manage.py custommenu
+
+This will create a file named ``menu.py`` in your project directory.
+If for some reason you want another file name, you can do::
+
+    python manage.py custommenu somefile.py
+
+The created file contains a class that is a copy of the default menu,
+it is named ``CustomMenu``, you can rename it if you want but if you do
+so, make sure you put the correct class name in your ADMIN_TOOLS_MENU
+settings variable.
+
+.. note::
+    You could have done the above by hand, without using the 
+    ``custommenu`` management command, but it's simpler with it.
+
+
+Now you need to tell django-admin-tools to use your custom menu instead 
+of the default one, open your settings.py file and add the following::
+
+    ADMIN_TOOLS_MENU = 'yourproject.menu.CustomMenu'
+
+Obviously, you need to change "yourproject" to the real project name, 
+if you have chosen a different file name or if you renamed the menu 
+class, you'll also need to change the above string to reflect your 
+modifications.
+
+At this point the menu displayed in the admin is your custom menu, now 
+you can read :ref:`the menu and menu items API documentation <menu>` 
+to learn how to create your custom menu.
+
 
 Customizing the dashboards
 --------------------------
 
-todo: write docs for "Customizing the dashboards"
+To customize the index and app index dashboards, the first step is to do
+the following::
+    
+    python manage.py customdashboard
 
+This will create a file named ``dashboard.py`` in your project directory.
+If for some reason you want another file name, you can do::
 
-Customizing the look and feel
------------------------------
+    python manage.py customdashboard somefile.py
 
-todo: write docs for "Customizing the look and feel"
+The created file contains two classes:
+ * The ``CustomIndexDashboard`` class that corresponds to the admin 
+   index page dashboard;
+ * The ``CustomAppIndexDashboard`` class that corresponds to the 
+   index page of each installed application.
+
+You can rename theses classes  if you want but if you do so, make sure 
+adjust the ``ADMIN_TOOLS_INDEX_DASHBOARD`` and 
+``ADMIN_TOOLS_APP_INDEX_DASHBOARD`` settings variables to match your
+class names.
+
+.. note::
+    You could have done the above by hand, without using the 
+    ``customdashboard`` management command, but it's simpler with it.
+
+
+Now you need to tell django-admin-tools to use your custom dashboard(s).
+Open your settings.py file and add the following::
+
+    ADMIN_TOOLS_INDEX_DASHBOARD = 'yourproject.dashboard.CustomIndexDashboard'
+    ADMIN_TOOLS_APP_INDEX_DASHBOARD = 'yourproject.dashboard.CustomAppIndexDashboard'
+
+If you only want a custom index dashboard, you would just need the first
+line. Obviously, you need to change "yourproject" to the real project name, 
+if you have chosen a different file name or if you renamed the dashboard
+classes, you'll also need to change the above string to reflect your 
+modifications.
+
+At this point the dashboards displayed in the index and the app index 
+should be your custom dashboards, now you can read 
+:ref:`the dashboard and dashboard modules API documentation <dashboard>` 
+to learn how to create your custom dashboard.
+
+
+Customizing the theme
+---------------------
+
+.. warning::
+    The theming support is still very basic and I'm still not sure it's
+    a good idea, so do not rely to much on it for the moment.
+
+This is very simple, just configure the ``ADMIN_TOOLS_THEMING_CSS`` to
+point to your custom css file, for example::
+
+    ADMIN_TOOLS_THEMING_CSS = 'css/theming.css'
+
+A good start is to copy the 
+``admin_tools/media/admin_tools/css/theming.css`` to your custom file and
+to modify it to suits your needs.

File docs/dashboard.rst

+.. _dashboard:
+
+The django-admin-tools dashboard and dashboard modules API
+==========================================================
+
+This section describe the API of the django-admin-tools dashboard and 
+dashboard modules.
+Make sure you read this before creating your custom dashboard and 
+custom modules.
+
+The ``Dashboard`` class
+-----------------------
+
+.. autoclass:: admin_tools.dashboard.models.Dashboard
+    :members:
+
+The ``AppIndexDashboard`` class
+-------------------------------
+
+.. autoclass:: admin_tools.dashboard.models.AppIndexDashboard
+    :members:
+
+The ``DashboardModule`` class
+-----------------------------
+
+.. autoclass:: admin_tools.dashboard.models.DashboardModule
+    :members:
+
+The ``LinkListDashboardModule`` class
+-------------------------------------
+
+.. autoclass:: admin_tools.dashboard.models.LinkListDashboardModule
+    :members:
+
+The ``AppListDashboardModule`` class
+------------------------------------
+
+.. autoclass:: admin_tools.dashboard.models.AppListDashboardModule
+    :members:
+
+The ``RecentActionsDashboardModule`` class
+------------------------------------------
+
+.. autoclass:: admin_tools.dashboard.models.RecentActionsDashboardModule
+    :members:
+
+The ``FeedDashboardModule`` class
+---------------------------------
+
+.. autoclass:: admin_tools.dashboard.models.FeedDashboardModule
+    :members:

File docs/images/applist_dashboard_module.png

Added
New image

File docs/images/applist_menu_item.png

Added
New image

File docs/images/dashboard_app_index_example.png

Added
New image

File docs/images/dashboard_example.png

Added
New image

File docs/images/feed_dashboard_module.png

Added
New image

File docs/images/menu_example.png

Added
New image

File docs/images/modellist_dashboard_module.png

Added
New image

File docs/images/recentactions_dashboard_module.png

Added
New image

File docs/index.rst

 
 This documentation covers the latest release of django-admin-tools, a
 collection of extensions and tools for the 
-`Django <http://www.djangoproject.com>`_ administration interface.
+`Django <http://www.djangoproject.com>`_ administration interface, 
+django-admin-tools includes:
+
+ * a full featured and customizable dashboard (for the admin index page
+   and the admin applications index pages),
+ * a customizable menu bar,
+ * tools to make admin theming easier.
+
+It was originally developed for django-cms, and then extracted to this 
+pluggable app.
 
 To get up and running quickly, consult the :ref:`quick-start guide
 <quickstart>`, which describes all the necessary steps to install
 
    quickstart
    installation
+   configuration
    customization
+   menu
+   dashboard
+   integration

File docs/integration.rst

+.. _integration:
+
+Integration with third party applications
+=========================================
+
+todo: write doc for "Integration with third party applications" section.

File docs/menu.rst

+.. _menu:
+
+The django-admin-tools menu and menu items API
+==============================================
+
+This section describe the API of the django-admin-tools menu and menu items.
+Make sure you read this before creating your custom menu.
+
+The ``Menu`` class
+------------------
+
+.. autoclass:: admin_tools.menu.models.Menu
+    :members:
+
+The ``MenuItem`` class
+----------------------
+
+.. autoclass:: admin_tools.menu.models.MenuItem
+    :members:
+
+The ``AppListMenuItem`` class
+-----------------------------
+
+.. autoclass:: admin_tools.menu.models.AppListMenuItem
+    :members:

File docs/quickstart.rst

 Basic configuration
 -------------------
 
-Once installed, you can add django-admin-tools to any Django-based
-project you're developing.
-
-django-admin-tools is composed of several modules:
-
-* admin_tools.theming: an app that makes it easy to customize the look 
-  and feel of the admin interface;
-
-* admin_tools.menu: a customizable navigation menu that sits on top of 
-  every django administration index page;
-
-* admin_tools.dashboard: a customizable dashboard that replaces the django 
-  administration index page.
+For a more detailed guide on how to configure django-admin-tools, please
+consult :ref:`the configuration section <configuration>`.
 
 Required settings
 ~~~~~~~~~~~~~~~~~
         # ...other installed applications...
     )
 
-.. note::
+.. important::
     it is very important that you put the admin_tools modules **before** 
     the ``django.contrib.admin module``, because django-admin-tools
     overrides the default django admin templates, and this will not work 
 
 django-admin-tools is modular, so if you want to disable a particular 
 module, just remove or comment it in your ``INSTALLED_APPS``. 
-For example, if you just want to use the dashboard::
-
-    INSTALLED_APPS = (
-        'admin_tools.dashboard',
-        'django.contrib.auth',
-        'django.contrib.sites',
-        'django.contrib.admin'
-        # ...other installed applications...
-    )
-
 
 Setting up the django-admin-tools media files
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~