.. _admin_hooks:
Hooks
=====
On loading, Wagtail will search for any app with the file ``wagtail_hooks.py`` and execute the contents. This provides a way to register your own functions to execute at certain points in Wagtail's execution, such as when a ``Page`` object is saved or when the main menu is constructed.
Registering functions with a Wagtail hook is done through the ``@hooks.register`` decorator:
.. code-block:: python
from wagtail.wagtailcore import hooks
@hooks.register('name_of_hook')
def my_hook_function(arg1, arg2...)
# your code here
Alternatively, ``hooks.register`` can be called as an ordinary function, passing in the name of the hook and a handler function defined elsewhere:
.. code-block:: python
hooks.register('name_of_hook', my_hook_function)
The available hooks are listed below.
.. contents::
:local:
:depth: 1
Admin modules
-------------
Hooks for building new areas of the admin interface (alongside pages, images, documents and so on).
.. _construct_homepage_panels:
``construct_homepage_panels``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Add or remove panels from the Wagtail admin homepage. The callable passed into this hook should take a ``request`` object and a list of ``panels``, objects which have a ``render()`` method returning a string. The objects also have an ``order`` property, an integer used for ordering the panels. The default panels use integers between ``100`` and ``300``.
.. code-block:: python
from django.utils.safestring import mark_safe
from wagtail.wagtailcore import hooks
class WelcomePanel(object):
order = 50
def render(self):
return mark_safe("""
No, but seriously -- welcome to the admin homepage.
`` element to the whitelist, and allow the ``target`` attribute on ```` elements: .. code-block:: python from wagtail.wagtailcore import hooks from wagtail.wagtailcore.whitelist import attribute_rule, check_url, allow_without_attributes @hooks.register('construct_whitelister_element_rules') def whitelister_element_rules(): return { 'blockquote': allow_without_attributes, 'a': attribute_rule({'href': check_url, 'target': True}), } .. _insert_editor_css: ``insert_editor_css`` ~~~~~~~~~~~~~~~~~~~~~ Add additional CSS files or snippets to the page editor. .. code-block:: python from django.contrib.staticfiles.templatetags.staticfiles import static from django.utils.html import format_html from wagtail.wagtailcore import hooks @hooks.register('insert_editor_css') def editor_css(): return format_html( '', static('demo/css/vendor/font-awesome/css/font-awesome.min.css') ) .. _insert_global_admin_css: ``insert_global_admin_css`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Add additional CSS files or snippets to all admin pages. .. code-block:: python from django.utils.html import format_html from django.contrib.staticfiles.templatetags.staticfiles import static from wagtail.wagtailcore import hooks @hooks.register('insert_global_admin_css') def global_admin_css(): return format_html('', static('my/wagtail/theme.css')) .. _insert_editor_js: ``insert_editor_js`` ~~~~~~~~~~~~~~~~~~~~ Add additional JavaScript files or code snippets to the page editor. .. code-block:: python from django.utils.html import format_html, format_html_join from django.conf import settings from wagtail.wagtailcore import hooks @hooks.register('insert_editor_js') def editor_js(): js_files = [ 'demo/js/hallo-plugins/hallo-demo-plugin.js', ] js_includes = format_html_join('\n', '', ((settings.STATIC_URL, filename) for filename in js_files) ) return js_includes + format_html( """ """ ) .. _insert_global_admin_js: ``insert_global_admin_js`` ~~~~~~~~~~~~~~~~~~~~~~~~~~ Add additional JavaScript files or code snippets to all admin pages. .. code-block:: python from django.utils.html import format_html from wagtail.wagtailcore import hooks @hooks.register('insert_global_admin_js') def global_admin_js(): return format_html( '', ) Editor workflow --------------- Hooks for customising the way users are directed through the process of creating page content. .. _after_create_page: ``after_create_page`` ~~~~~~~~~~~~~~~~~~~~~ Do something with a ``Page`` object after it has been saved to the database (as a published page or a revision). The callable passed to this hook should take a ``request`` object and a ``page`` object. The function does not have to return anything, but if an object with a ``status_code`` property is returned, Wagtail will use it as a response object. By default, Wagtail will instead redirect to the Explorer page for the new page's parent. .. code-block:: python from django.http import HttpResponse from wagtail.wagtailcore import hooks @hooks.register('after_create_page') def do_after_page_create(request, page): return HttpResponse("Congrats on making content!", content_type="text/plain") .. _before_create_page: ``before_create_page`` ~~~~~~~~~~~~~~~~~~~~~~ Called at the beginning of the "create page" view passing in the request, the parent page and page model class. The function does not have to return anything, but if an object with a ``status_code`` property is returned, Wagtail will use it as a response object and skip the rest of the view. Unlike, ``after_create_page``, this is run both for both ``GET`` and ``POST`` requests. This can be used to completely override the editor on a per-view basis: .. code-block:: python from wagtail.wagtailcore import hooks from .models import AwesomePage from .admin_views import edit_awesome_page @hooks.register('before_create_page') def before_create_page(request, parent_page, page_class): # Use a custom create view for the AwesomePage model if page_class == AwesomePage: return create_awesome_page(request, parent_page) .. _after_delete_page: ``after_delete_page`` ~~~~~~~~~~~~~~~~~~~~~ Do something after a ``Page`` object is deleted. Uses the same behavior as ``after_create_page``. .. _before_delete_page: ``before_delete_page`` ~~~~~~~~~~~~~~~~~~~~~~ Called at the beginning of the "delete page" view passing in the request and the page object. Uses the same behavior as ``before_create_page``. .. _after_edit_page: ``after_edit_page`` ~~~~~~~~~~~~~~~~~~~ Do something with a ``Page`` object after it has been updated. Uses the same behavior as ``after_create_page``. .. _before_edit_page: ``before_edit_page`` ~~~~~~~~~~~~~~~~~~~~~ Called at the beginning of the "edit page" view passing in the request and the page object. Uses the same behavior as ``before_create_page``. .. _construct_wagtail_userbar: ``construct_wagtail_userbar`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionchanged:: 1.0 The hook was renamed from ``construct_wagtail_edit_bird`` Add or remove items from the wagtail userbar. Add, edit, and moderation tools are provided by default. The callable passed into the hook must take the ``request`` object and a list of menu objects, ``items``. The menu item objects must have a ``render`` method which can take a ``request`` object and return the HTML string representing the menu item. See the userbar templates and menu item classes for more information. .. code-block:: python from wagtail.wagtailcore import hooks class UserbarPuppyLinkItem(object): def render(self, request): return 'Puppies! ' @hooks.register('construct_wagtail_userbar') def add_puppy_link_item(request, items): return items.append( UserbarPuppyLinkItem() ) Page explorer ------------- .. _construct_explorer_page_queryset: ``construct_explorer_page_queryset`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Called when rendering the page explorer view, to allow the page listing queryset to be customised. The callable passed into the hook will receive the parent page object, the current page queryset, and the request object, and must return a Page queryset (either the original one, or a new one). .. code-block:: python from wagtail.wagtailcore import hooks @hooks.register('construct_explorer_page_queryset') def show_my_profile_only(parent_page, pages, request): # If we're in the 'user-profiles' section, only show the user's own profile if parent_page.slug == 'user-profiles': pages = pages.filter(owner=request.user) return pages .. _register_page_listing_buttons: ``register_page_listing_buttons`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Add buttons to the actions list for a page in the page explorer. This is useful when adding custom actions to the listing, such as translations or a complex workflow. This example will add a simple button to the listing: .. code-block:: python from wagtail.wagtailadmin import widgets as wagtailadmin_widgets @hooks.register('register_page_listing_buttons') def page_listing_buttons(page, page_perms, is_parent=False): yield wagtailadmin_widgets.PageListingButton( 'A page listing button', '/goes/to/a/url/', priority=10 ) The ``priority`` argument controls the order the buttons are displayed in. Buttons are ordered from low to high priority, so a button with ``priority=10`` will be displayed before a button with ``priority=20``. Buttons with dropdown lists ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The admin widgets also provide ``ButtonWithDropdownFromHook``, which allows you to define a custom hook for generating a dropdown menu that gets attached to your button. Creating a button with a dropdown menu involves two steps. Firstly, you add your button to the ``register_page_listing_buttons`` hook, just like the example above. Secondly, you register a new hook that yields the contents of the dropdown menu. This example shows how Wagtail's default admin dropdown is implemented. You can also see how to register buttons conditionally, in this case by evaluating the ``page_perms``: .. code-block:: python @hooks.register('register_page_listing_buttons') def page_custom_listing_buttons(page, page_perms, is_parent=False): yield wagtailadmin_widgets.ButtonWithDropdownFromHook( 'More actions', hook_name='my_button_dropdown_hook', page=page, page_perms=page_perms, is_parent=is_parent, priority=50 ) @hooks.register('my_button_dropdown_hook') def page_custom_listing_more_buttons(page, page_perms, is_parent=False): if page_perms.can_move(): yield Button('Move', reverse('wagtailadmin_pages:move', args=[page.id]), priority=10) if page_perms.can_delete(): yield Button('Delete', reverse('wagtailadmin_pages:delete', args=[page.id]), priority=30) if page_perms.can_unpublish(): yield Button('Unpublish', reverse('wagtailadmin_pages:unpublish', args=[page.id]), priority=40) The template for the dropdown button can be customised by overriding ``wagtailadmin/pages/listing/_button_with_dropdown.html``. The JavaScript that runs the dropdowns makes use of custom data attributes, so you should leave ``data-dropdown`` and ``data-dropdown-toggle`` in the markup if you customise it. Page serving ------------ .. _before_serve_page: ``before_serve_page`` ~~~~~~~~~~~~~~~~~~~~~ Called when Wagtail is about to serve a page. The callable passed into the hook will receive the page object, the request object, and the ``args`` and ``kwargs`` that will be passed to the page's ``serve()`` method. If the callable returns an ``HttpResponse``, that response will be returned immediately to the user, and Wagtail will not proceed to call ``serve()`` on the page. .. code-block:: python from wagtail.wagtailcore import hooks @hooks.register('before_serve_page') def block_googlebot(page, request, serve_args, serve_kwargs): if request.META.get('HTTP_USER_AGENT') == 'GoogleBot': return HttpResponse("bad googlebot no cookie
")