Permalink
Browse files

Use settings-based dashboard nav

Replace the import-time nav set-up with a settings-based one.  This is
more flexible ultimately although it doesn't allow extensions to easily
add their node automatically.  However this is probably a good thing
overall as it isn't easy to remove nodes in the previous impl.

Fixes 426
  • Loading branch information...
1 parent e9722a8 commit 3ec3307ab6f3385dcb1f5578b3c3bb3181a08c29 Sebastian Vetter committed with codeinthehole Dec 17, 2012
View
@@ -16,6 +16,7 @@ Customisation
recipes/how_to_override_a_core_class
recipes/how_to_customise_templates
recipes/how_to_disable_an_app
+ recipes/how_to_configure_the_dashboard_navigation
Catalogue
---------
@@ -0,0 +1,78 @@
+=========================================
+How to configure the dashboard navigation
+=========================================
+
+Oscar comes with a pre-configured dashboard navigation that gives you access
+to its individual pages. If you have your own dashboard app that you would like
+to show up in the dashboard navigation or want to arrange it differently,
+that's very easy. All you have to do is override the
+``OSCAR_DASHBOARD_NAVIGATION`` setting in you settings file.
+
+
+Add your own dashboard menu item
+--------------------------------
+
+Assuming that you just want to append a new menu item to the dashboard, all
+you have to do is open up your settings file and somewhere below the import
+of the Oscar default settings::
+
+ from oscar.defaults import *
+
+add your custom dashboard configuration. Let's assume you would like to add
+a new item "Store Manager" with a submenu item "Stores". The way you would
+do that is::
+
+ OSCAR_DASHBOARD_NAVIGATION += [
+ {
+ 'label': _('Store manager'),
+ 'children': [
+ {
+ 'label': _('Stores'),
+ 'url_name': 'your-reverse-url-lookup-name',
+ },
+ ]
+ },
+ ]
+
+That's it. You should now have *Store manager > Stores* in you dashboard
+menu.
+
+
+Add an icon to your dashboard menu
+----------------------------------
+
+Although you have your menu in the dashboard now, it doesn't look as
+nice as the other menu items that have icons displayed next to them. So
+you probably want to add an icon to your heading.
+
+Oscar uses `Font Awesome`_ for its icons which makes it very simple to
+an icon to your dashboard menu. All you need to do is find the right icon
+for your menu item. Check out `the icon list`_ to find one.
+
+Now that you have decided for an icon to use, all you need to do add the
+icon class for the icon to your menu heading::
+
+ OSCAR_DASHBOARD_NAVIGATION += [
+ {
+ 'label': _('Store manager'),
+ 'icon': 'icon-map-marker',
+ 'children': [
+ {
+ 'label': _('Stores'),
+ 'url_name': 'your-reverse-url-lookup-name',
+ },
+ ]
+ },
+ ]
+
+You are not resticted to use `Font Awesome`_ icons for you menu heading. Other
+web fonts will work as well as long as they support the same markup::
+
+ <i class="icon-map-marker"></i>
+
+The class is of the ``<i>`` is defined by the *icon* setting in the
+configuration of your dashboard navigation above.
+
+
+.. _`Font Awesome`: http://fortawesome.github.com/Font-Awesome/
+.. _`this icon list`: http://fortawesome.github.com/Font-Awesome/#all-icons
@@ -1,16 +1,8 @@
from django.conf.urls import patterns, url
-from django.utils.translation import ugettext_lazy as _
+from oscar.views.decorators import staff_member_required
from oscar.core.application import Application
from oscar.apps.dashboard.catalogue import views
-from oscar.apps.dashboard.nav import register, Node
-from oscar.views.decorators import staff_member_required
-
-node = Node(_('Catalogue'))
-node.add_child(Node(_('Products'), 'dashboard:catalogue-product-list'))
-node.add_child(Node(_('Categories'), 'dashboard:catalogue-category-list'))
-node.add_child(Node(_('Stock alerts'), 'dashboard:stock-alert-list'))
-register(node, 10)
class CatalogueApplication(Application):
@@ -1,14 +1,8 @@
from django.conf.urls import patterns, url
-from django.utils.translation import ugettext_lazy as _
from oscar.views.decorators import staff_member_required
from oscar.core.application import Application
from oscar.apps.dashboard.communications import views
-from oscar.apps.dashboard.nav import register, Node
-
-node = Node(_('Communications'))
-node.add_child(Node(_('Emails'), 'dashboard:comms-list'))
-register(node, 35)
class CommsDashboardApplication(Application):
@@ -1,12 +1,16 @@
from django.core.urlresolvers import reverse
+from django.core.exceptions import ImproperlyConfigured
+from django.conf import settings
_nodes = []
class Node(object):
- def __init__(self, label, url_name=None, url_args=None, url_kwargs=None, access_fn=None):
+ def __init__(self, label, url_name=None, url_args=None, url_kwargs=None,
+ access_fn=None, icon=None):
self.label = label
+ self.icon = icon
self.url_name = url_name
self.url_args = url_args
self.url_kwargs = url_kwargs
@@ -19,7 +23,8 @@ def is_heading(self):
@property
def url(self):
- return reverse(self.url_name, args=self.url_args, kwargs=self.url_kwargs)
+ return reverse(self.url_name, args=self.url_args,
+ kwargs=self.url_kwargs)
def add_child(self, node):
self.children.append(node)
@@ -41,20 +46,54 @@ def filter(self, user):
def has_children(self):
return len(self.children) > 0
+
def register(node, display_order=5):
# We use tuples so we can sort later on. The lower the display order, the
# closer to the left the node appears.
_nodes.append((display_order, node))
+
def flush():
+ global _nodes
_nodes = []
+
def get_nodes(user):
+ """
+ Return the visible navigation nodes for the passed user
+ """
+ if not _nodes:
+ dashboard_nav = settings.OSCAR_DASHBOARD_NAVIGATION
+ create_menu(dashboard_nav)
nodes = []
- for _, node in sorted(_nodes):
+ for __, node in sorted(_nodes):
filtered_node = node.filter(user)
if filtered_node:
nodes.append(node)
return nodes
+def create_menu(menu_items, parent=None):
+ """
+ Create the navigation nodes based on a passed list of dicts
+ """
+ for menu_dict in menu_items:
+ try:
+ label = menu_dict['label']
+ except KeyError:
+ raise ImproperlyConfigured(
+ "No label specified for menu item in dashboard")
+
+ children = menu_dict.get('children', [])
+ if children:
+ node = Node(label=label, icon=menu_dict.get('icon', None))
+ create_menu(children, parent=node)
+ else:
+ node = Node(label=label, icon=menu_dict.get('icon', None),
+ url_name=menu_dict.get('url_name', None),
+ url_kwargs=menu_dict.get('url_kwargs', None),
+ url_args=menu_dict.get('url_args', None))
+ if parent is None:
+ register(node, len(_nodes))
+ else:
+ parent.add_child(node)
@@ -1,13 +1,8 @@
from django.conf.urls import patterns, url
from oscar.views.decorators import staff_member_required
-from django.utils.translation import ugettext_lazy as _
from oscar.core.application import Application
from oscar.apps.dashboard.offers import views
-from oscar.apps.dashboard.nav import register, Node
-
-node = Node(_('Offers'), 'dashboard:offer-list')
-register(node, 50)
class OffersDashboardApplication(Application):
@@ -1,15 +1,8 @@
from django.conf.urls import patterns, url
-from oscar.views.decorators import staff_member_required
-from django.utils.translation import ugettext_lazy as _
+from oscar.views.decorators import staff_member_required
from oscar.core.application import Application
from oscar.apps.dashboard.orders import views
-from oscar.apps.dashboard.nav import register, Node
-
-node = Node(_('Orders'))
-node.add_child(Node(_('Orders'), 'dashboard:order-list'))
-node.add_child(Node(_('Statistics'), 'dashboard:order-stats'))
-register(node, 80)
class OrdersDashboardApplication(Application):
@@ -1,6 +1,6 @@
from django.conf.urls import patterns, url
-from oscar.views.decorators import staff_member_required
+from oscar.views.decorators import staff_member_required
from oscar.core.application import Application
from oscar.apps.dashboard.pages import views
@@ -1,17 +1,9 @@
from django.conf.urls import patterns, url
-from oscar.views.decorators import staff_member_required
-from django.utils.translation import ugettext_lazy as _
+from oscar.views.decorators import staff_member_required
from oscar.core.application import Application
from oscar.apps.dashboard.promotions import views
from oscar.apps.promotions.conf import PROMOTION_CLASSES
-from oscar.apps.dashboard.nav import register, Node
-
-node = Node(_('Content'))
-node.add_child(Node(_('Re-usable content blocks'), 'dashboard:promotion-list'))
-node.add_child(Node(_('Content blocks by page'), 'dashboard:promotion-list-by-page'))
-node.add_child(Node(_('Pages'), 'dashboard:page-list'))
-register(node, 20)
class PromotionsDashboardApplication(Application):
@@ -35,10 +27,10 @@ def get_urls(self):
url(r'^$', self.list_view.as_view(), name='promotion-list'),
url(r'^pages/$', self.page_list.as_view(), name='promotion-list-by-page'),
url(r'^pages/(?P<path>.+)/$', self.page_detail.as_view(), name='promotion-list-by-url'),
- url(r'^create/$',
- self.create_redirect_view.as_view(),
+ url(r'^create/$',
+ self.create_redirect_view.as_view(),
name='promotion-create-redirect'),
- url(r'^page-promotion/(?P<pk>\d+)/$',
+ url(r'^page-promotion/(?P<pk>\d+)/$',
self.delete_page_promotion_view.as_view(), name='pagepromotion-delete')
)
@@ -1,13 +1,8 @@
from django.conf.urls import patterns, url
-from oscar.views.decorators import staff_member_required
-from django.utils.translation import ugettext_lazy as _
+from oscar.views.decorators import staff_member_required
from oscar.core.application import Application
from oscar.apps.dashboard.ranges import views
-from oscar.apps.dashboard.nav import register, Node
-
-node = Node(_('Ranges'), 'dashboard:range-list')
-register(node, 70)
class RangeDashboardApplication(Application):
@@ -1,13 +1,8 @@
from django.conf.urls import patterns, url
-from oscar.views.decorators import staff_member_required
-from django.utils.translation import ugettext_lazy as _
+from oscar.views.decorators import staff_member_required
from oscar.core.application import Application
from oscar.apps.dashboard.reports import views
-from oscar.apps.dashboard.nav import register, Node
-
-node = Node(_('Reports'), 'dashboard:reports-index')
-register(node, 90)
class ReportsApplication(Application):
@@ -1,14 +1,9 @@
from django.conf.urls import patterns, url
-from oscar.views.decorators import staff_member_required
-from django.utils.translation import ugettext_lazy as _
+from oscar.views.decorators import staff_member_required
from oscar.core.application import Application
-from oscar.apps.dashboard.nav import register, Node
from oscar.apps.dashboard.reviews import views
-node = Node(_('Reviews'), 'dashboard:reviews-list')
-register(node, 35)
-
class ReviewsApplication(Application):
name = None
@@ -1,15 +1,8 @@
from django.conf.urls import patterns, url
-from oscar.views.decorators import staff_member_required
-from django.utils.translation import ugettext_lazy as _
from oscar.core.application import Application
+from oscar.views.decorators import staff_member_required
from oscar.apps.dashboard.users import views
-from oscar.apps.dashboard.nav import register, Node
-
-node = Node(_('Customers'))
-node.add_child(Node(_('Customers'), 'dashboard:users-index'))
-node.add_child(Node(_('Alerts'), 'dashboard:user-alert-list'))
-register(node, 30)
class UserManagementApplication(Application):
@@ -1,13 +1,8 @@
from django.conf.urls import patterns, url
-from oscar.views.decorators import staff_member_required
-from django.utils.translation import ugettext_lazy as _
+from oscar.views.decorators import staff_member_required
from oscar.core.application import Application
from oscar.apps.dashboard.vouchers import views
-from oscar.apps.dashboard.nav import register, Node
-
-node = Node(_('Vouchers'), 'dashboard:voucher-list')
-register(node, 60)
class VoucherDashboardApplication(Application):
@@ -22,10 +17,14 @@ class VoucherDashboardApplication(Application):
def get_urls(self):
urlpatterns = patterns('',
url(r'^$', self.list_view.as_view(), name='voucher-list'),
- url(r'^create/$', self.create_view.as_view(), name='voucher-create'),
- url(r'^update/(?P<pk>\d+)/$', self.update_view.as_view(), name='voucher-update'),
- url(r'^delete/(?P<pk>\d+)/$', self.delete_view.as_view(), name='voucher-delete'),
- url(r'^stats/(?P<pk>\d+)/$', self.stats_view.as_view(), name='voucher-stats'),
+ url(r'^create/$', self.create_view.as_view(),
+ name='voucher-create'),
+ url(r'^update/(?P<pk>\d+)/$', self.update_view.as_view(),
+ name='voucher-update'),
+ url(r'^delete/(?P<pk>\d+)/$', self.delete_view.as_view(),
+ name='voucher-delete'),
+ url(r'^stats/(?P<pk>\d+)/$', self.stats_view.as_view(),
+ name='voucher-stats'),
)
return self.post_process_urls(urlpatterns)
Oops, something went wrong.

0 comments on commit 3ec3307

Please sign in to comment.