diff --git a/wagtail/wagtailadmin/menu.py b/wagtail/wagtailadmin/menu.py
index 0ad4eb439..86a3c4705 100644
--- a/wagtail/wagtailadmin/menu.py
+++ b/wagtail/wagtailadmin/menu.py
@@ -11,6 +11,8 @@ except ImportError:
from django.utils.text import slugify
from django.utils.html import format_html
+from wagtail.wagtailcore import hooks
+
class MenuItem(object):
def __init__(self, label, url, name=None, classnames='', attrs=None, order=1000):
@@ -29,3 +31,12 @@ class MenuItem(object):
return format_html(
"""
""",
self.name, self.url, self.classnames, self.attr_string, self.label)
+
+
+_menu_items = None
+def get_menu_items():
+ global _menu_items
+ if _menu_items is None:
+ _menu_items = [fn() for fn in hooks.get_hooks('register_admin_menu_item')]
+
+ return _menu_items
diff --git a/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py b/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py
index 1cb2d5a96..b14eabf2d 100644
--- a/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py
+++ b/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py
@@ -2,14 +2,11 @@ from __future__ import unicode_literals
from django.conf import settings
from django import template
-from django.core import urlresolvers
-from django.utils.translation import ugettext_lazy as _
-
-from wagtail.wagtailadmin.menu import MenuItem
from wagtail.wagtailcore import hooks
from wagtail.wagtailcore.models import get_navigation_menu_items, UserPagePermissionsProxy, PageViewRestriction
from wagtail.wagtailcore.utils import camelcase_to_underscore
+from wagtail.wagtailadmin.menu import get_menu_items
register = template.Library()
@@ -31,13 +28,7 @@ def explorer_subnav(nodes):
@register.inclusion_tag('wagtailadmin/shared/main_nav.html', takes_context=True)
def main_nav(context):
- menu_items = [
- MenuItem(_('Explorer'), urlresolvers.reverse('wagtailadmin_explore_root'),
- classnames='icon icon-folder-open-inverse dl-trigger',
- attrs={'data-explorer-menu-url': urlresolvers.reverse('wagtailadmin_explorer_nav')},
- order=100),
- MenuItem(_('Search'), urlresolvers.reverse('wagtailadmin_pages_search'), classnames='icon icon-search', order=200),
- ]
+ menu_items = get_menu_items()[:] # need to clone with [:] because the return value of get_menu_items is global, and construct_main_menu hooks will mutate it
request = context['request']
diff --git a/wagtail/wagtailadmin/wagtail_hooks.py b/wagtail/wagtailadmin/wagtail_hooks.py
new file mode 100644
index 000000000..a529fb93c
--- /dev/null
+++ b/wagtail/wagtailadmin/wagtail_hooks.py
@@ -0,0 +1,20 @@
+from django.core import urlresolvers
+from django.utils.translation import ugettext_lazy as _
+
+from wagtail.wagtailcore import hooks
+from wagtail.wagtailadmin.menu import MenuItem
+
+
+@hooks.register('register_admin_menu_item')
+def register_explorer_menu_item():
+ return MenuItem(
+ _('Explorer'), urlresolvers.reverse('wagtailadmin_explore_root'),
+ classnames='icon icon-folder-open-inverse dl-trigger',
+ attrs={'data-explorer-menu-url': urlresolvers.reverse('wagtailadmin_explorer_nav')},
+ order=100)
+
+@hooks.register('register_admin_menu_item')
+def register_search_menu_item():
+ return MenuItem(
+ _('Search'), urlresolvers.reverse('wagtailadmin_pages_search'),
+ classnames='icon icon-search', order=200)