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, 'Speaker lineup') 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()