diff --git a/docs/reference/hooks.rst b/docs/reference/hooks.rst
index 92491948bd..758f203519 100644
--- a/docs/reference/hooks.rst
+++ b/docs/reference/hooks.rst
@@ -493,6 +493,46 @@ Hooks for customising the way users are directed through the process of creating
Uses the same behaviour as ``before_create_page``.
+.. _register_page_action_menu_item:
+
+``register_page_action_menu_item``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Add an item to the popup menu of actions on the page creation and edit views. The callable passed to this hook must return an instance of ``wagtail.admin.views.pages.ActionMenuItem``. The following attributes and methods are available to be overridden on subclasses of ``ActionMenuItem``:
+
+ :order: an integer (default 100) which determines the item's position in the menu. Can also be passed as a keyword argument to the object constructor
+ :label: the displayed text of the menu item
+ :get_url: a method which returns a URL for the menu item to link to; by default, returns ``None`` which causes the menu item to behave as a form submit button instead
+ :name: value of the ``name`` attribute of the submit button, if no URL is specified
+ :is_shown: a method which returns a boolean indicating whether the menu item should be shown; by default, true except when editing a locked page
+ :template: path to a template to render to produce the menu item HTML
+ :get_context: a method that returns a context dictionary to pass to the template
+ :render_html: a method that returns the menu item HTML; by default, renders ``template`` with the context returned from ``get_context``
+
+ The ``get_url``, ``is_shown``, ``get_context`` and ``render_html`` methods all accept a request object and a context dictionary containing the following fields:
+
+ :view: name of the current view: ``'create'``, ``'edit'`` or ``'revisions_revert'``
+ :page: For ``view`` = ``'edit'`` or ``'revisions_revert'``, the page being edited
+ :parent_page: For ``view`` = ``'create'``, the parent page of the page being created
+ :user_page_permissions: a ``UserPagePermissionsProxy`` object for the current user, to test permissions against
+
+ .. code-block:: python
+
+ from wagtail.core import hooks
+ from wagtail.admin.views.pages import ActionMenuItem
+
+ class GuacamoleMenuItem(ActionMenuItem):
+ label = "Guacamole"
+
+ def get_url(self, request, context):
+ return "https://www.youtube.com/watch?v=dNJdJIwCF_Y"
+
+
+ @hooks.register('register_page_action_menu_item')
+ def register_guacamole_menu_item():
+ return GuacamoleMenuItem(order=10)
+
+
.. _construct_wagtail_userbar:
``construct_wagtail_userbar``
diff --git a/wagtail/admin/tests/test_pages_views.py b/wagtail/admin/tests/test_pages_views.py
index 673c45fe48..f3521848bf 100644
--- a/wagtail/admin/tests/test_pages_views.py
+++ b/wagtail/admin/tests/test_pages_views.py
@@ -663,6 +663,8 @@ class TestPageCreation(TestCase, WagtailTestUtils):
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Content')
self.assertContains(response, 'Promote')
+ # test register_page_action_menu_item hook
+ self.assertContains(response, '')
def test_create_multipart(self):
"""
@@ -1274,6 +1276,9 @@ class TestPageEdit(TestCase, WagtailTestUtils):
self.assertContains(response, '')
self.assertContains(response, 'Add speakers')
+ # test register_page_action_menu_item hook
+ self.assertContains(response, '')
+
def test_edit_draft_page_with_no_revisions(self):
# Tests that the edit page loads
response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.unpublished_page.id, )))
diff --git a/wagtail/admin/views/pages.py b/wagtail/admin/views/pages.py
index 42bdb41c93..994c096687 100644
--- a/wagtail/admin/views/pages.py
+++ b/wagtail/admin/views/pages.py
@@ -133,12 +133,23 @@ class DeleteMenuItem(ActionMenuItem):
return reverse('wagtailadmin_pages:delete', args=(context['page'].id,))
-ACTION_MENU_ITEMS = [
- UnpublishMenuItem(order=10),
- DeleteMenuItem(order=20),
- PublishMenuItem(order=30),
- SubmitForModerationMenuItem(order=40),
-]
+ACTION_MENU_ITEMS = None
+
+
+def _get_action_menu_items():
+ global ACTION_MENU_ITEMS
+
+ if ACTION_MENU_ITEMS is None:
+ ACTION_MENU_ITEMS = [
+ UnpublishMenuItem(order=10),
+ DeleteMenuItem(order=20),
+ PublishMenuItem(order=30),
+ SubmitForModerationMenuItem(order=40),
+ ]
+ for hook in hooks.get_hooks('register_page_action_menu_item'):
+ ACTION_MENU_ITEMS.append(hook())
+
+ return ACTION_MENU_ITEMS
def get_valid_next_url_from_request(request):
@@ -416,7 +427,7 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_
'page_class': page_class,
'parent_page': parent_page,
'edit_handler': edit_handler,
- 'action_menu_items': ACTION_MENU_ITEMS,
+ 'action_menu_items': _get_action_menu_items(),
'preview_modes': page.preview_modes,
'form': form,
'next': next_url,
@@ -646,7 +657,7 @@ def edit(request, page_id):
'content_type': content_type,
'edit_handler': edit_handler,
'errors_debug': errors_debug,
- 'action_menu_items': ACTION_MENU_ITEMS,
+ 'action_menu_items': _get_action_menu_items(),
'preview_modes': page.preview_modes,
'form': form,
'next': next_url,
@@ -1257,7 +1268,7 @@ def revisions_revert(request, page_id, revision_id):
'content_type': content_type,
'edit_handler': edit_handler,
'errors_debug': None,
- 'action_menu_items': ACTION_MENU_ITEMS,
+ 'action_menu_items': _get_action_menu_items(),
'preview_modes': page.preview_modes,
'form': form, # Used in unit tests
})
diff --git a/wagtail/tests/testapp/wagtail_hooks.py b/wagtail/tests/testapp/wagtail_hooks.py
index 6b819637da..65e35bbefd 100644
--- a/wagtail/tests/testapp/wagtail_hooks.py
+++ b/wagtail/tests/testapp/wagtail_hooks.py
@@ -6,6 +6,7 @@ import wagtail.admin.rich_text.editors.draftail.features as draftail_features
from wagtail.admin.menu import MenuItem
from wagtail.admin.rich_text import HalloPlugin
from wagtail.admin.search import SearchArea
+from wagtail.admin.views.pages import ActionMenuItem
from wagtail.core import hooks
@@ -104,3 +105,13 @@ def register_blockquote_feature(features):
css={'all': ['testapp/css/draftail-blockquote.css']},
)
)
+
+
+class PanicMenuItem(ActionMenuItem):
+ label = "Panic!"
+ name = 'action-panic'
+
+
+@hooks.register('register_page_action_menu_item')
+def register_panic_menu_item():
+ return PanicMenuItem()