# Wagtail 6.1 release notes

_May 1, 2024_

```{contents}
---
local:
depth: 1
---
```

## What's new

### Universal listings continued

Continuing work on the Universal Listings project, this release rolls out universal listing styles for the following views:

 * Image listings
 * Document listings
 * Site and locale listings
 * Page and snippet history views
 * Form builder submissions
 * Collections listings
 * Groups
 * Users
 * Workflow and task views
 * Search promotions index views
 * Redirects index

Universal listing components like header buttons have also been tweaked to improve usability, and the `PageListingViewSet` now includes `ChooseParentView` to allow creating pages from custom page listings.

Thank you to everyone who worked on these features: Ben Enright, Sage Abdullah, Rohit Sharma, Storm Heg, Temidayo Azeez, and Abdelrahman Hamada.

### Information-dense admin interface

Wagtail now provides a way for CMS users to control the information density of the admin interface, via their user profile preferences.
The new setting allows switching between the "default" density and a new "snug" mode, which reduces the spacing and size of UI elements.

It’s also possible for site implementers to customize this for the needs of their project – see [](custom_ui_information_density).

This feature was developed by Ben Enright and Thibaud Colas.

### Custom per-page-type listings

A new viewset class `PageListingViewSet` has been introduced, allowing developers to create custom page listings for specific page types similar to those previously provided by the Modeladmin package - see [](custom_page_listings). This feature was developed by Matt Westcott.

### Keyboard shortcuts overview

A new dialog is available from the help menu, providing an overview of keyboard shortcuts available in the Wagtail admin. This feature was developed by Karthik Ayangar and Rohit Sharma.

### Better guidance for password-protected content

Wagtail now includes extra guidance in its [private pages](private_pages) and [private collections (documents)](private_collections) forms, to warn users about the pitfalls of the "shared password" option.
For projects with higher security requirements, it's now possible to disable the shared password option entirely.
Thank you to Rohit Sharma, Salvo Polizzi, and Jake Howard for implementing those changes.

### Favicon images generation

For sites managing favicons via the CMS, Wagtail now supports [`.ico` favicon generation](favicon_generation), with `format-ico`:

```html+django
<link rel="icon" href="{% image favicon_image format-ico %}" />
```

This feature was developed by Jake Howard.

### CVE-2024-32882: Permission check bypass when editing a model with per-field restrictions through `wagtail.contrib.settings` or `ModelViewSet`

This release addresses a permission vulnerability in the Wagtail admin interface. If a model has been made available for editing through the [`wagtail.contrib.settings`](/reference/contrib/settings) module or [ModelViewSet](modelviewset), and the permission argument on FieldPanel has been used to further restrict access to one or more fields of the model, a user with edit permission over the model but not the specific field can craft an HTTP POST request that bypasses the permission check on the individual field, allowing them to update its value.

The vulnerability is not exploitable by an ordinary site visitor without access to the Wagtail admin, or by a user who has not been granted edit access to the model in question. The editing interfaces for pages and snippets are also unaffected.

Many thanks to Ben Morse and Joshua Munn for reporting this issue, and Jake Howard and Sage Abdullah for the fix. For further details, please see [the CVE-2024-32882 security advisory](https://github.com/wagtail/wagtail/security/advisories/GHSA-w2v8-php4-p8hc).

### Other features

 * Add `RelatedObjectsColumn` to the table UI framework (Matt Westcott)
 * Reduce memory usage when rebuilding search indexes (Jake Howard)
 * Add system checks to ensure that `WAGTAIL_DATE_FORMAT`, `WAGTAIL_DATETIME_FORMAT`, `WAGTAIL_TIME_FORMAT` are [correctly configured](wagtail_date_time_formats) (Rohit Sharma, Coen van der Kamp)
 * Allow custom permissions with the same prefix as built-in permissions (Sage Abdullah)
 * Allow displaying permissions linked to the Admin model's content type (Sage Abdullah)
 * Add support for Draftail's JavaScript to use chooserUrls provided by entity options & for the Draftail widget to encode lazy URLs/ translations (Elhussein Almasri)
 * Added `AbstractGroupApprovalTask` to simplify [customizing behavior of custom `Task` models](../extending/custom_tasks) (John-Scott Atlakson)
 * Add ability to bulk toggle permissions in the user group editing view, including shift+click for multiple selections (LB (Ben) Johnston, Kalob Taulien)
 * Update the minimum version of `djangorestframework` to 3.15.1 (Sage Abdullah)
 * Add support for related fields in generic `IndexView.list_display` (Abdelrahman Hamada)
 * Improve page fetching logic and cache route results per request. You can now use `Page.route_for_request()` to find the page route, 
   and `Page.find_for_request()` to find the page given a request object and a URL path, see [](page_model_ref). Results are cached on `request._wagtail_route_for_request` (Gordon Pendleton)
 * Optimize rewriting of links / embeds in rich text using bulk database lookups (Andy Chosak)
 * Add normalization mechanism to StreamField so that assignments and defaults can be passed in a wider range of data types (Joshua Munn, Matt Westcott)
 * Allow specifying a `STORAGES` alias name for [`WAGTAILIMAGES_RENDITION_STORAGE`](wagtailimages_rendition_storage) (Alec Baron)
 * Update `PASSWORD_REQUIRED_TEMPLATE` setting to [`WAGTAIL_PASSWORD_REQUIRED_TEMPLATE`](frontend_authentication) with deprecation of previous naming (Saksham Misra, LB (Ben) Johnston)
 * Update `DOCUMENT_PASSWORD_REQUIRED_TEMPLATE` setting to [`WAGTAILDOCS_PASSWORD_REQUIRED_TEMPLATE`](frontend_authentication) with deprecation of previous naming (Saksham Misra, LB (Ben) Johnston)
 * When editing settings (contrib) use the same icon in the editing view that was declared when registering the setting (Vince Salvino, Rohit Sharma)
 * Populate django-treebeard cache during page routing to improve performance of `get_parent` (Nigel van Keulen)
 * Add additional field types to Elasticsearch mapping (scott-8)


### Bug fixes

 * Fix typo in `__str__` for MySQL search index (Jake Howard)
 * Ensure that unit tests correctly check for migrations in all core Wagtail apps (Matt Westcott)
 * Correctly handle `date` objects on `human_readable_date` template tag (Jhonatan Lopes)
 * Ensure re-ordering buttons work correctly when using a nested InlinePanel (Adrien Hamraoui)
 * Consistently remove model's `verbose_name` in group edit view when listing custom permissions (Sage Abdullah, Neeraj Yetheendran, Omkar Jadhav)
 * Resolve issue local development of docs when running `make livehtml` (Sage Abdullah)
 * Resolve issue with unwanted padding in chooser modal listings (Sage Abdullah)
 * Ensure form builder emails that have date or datetime fields correctly localize dates based on the configured `LANGUAGE_CODE` (Mark Niehues)
 * Ensure the Stimulus `UnsavedController` checks for nested removal/additions of inputs so that the unsaved warning shows in more valid cases when editing a page (Karthik Ayangar)
 * Ensure `get_add_url()` is always used to re-render the add button when the listing is refreshed in viewsets (Sage Abdullah)
 * Ensure dropdown content cannot get higher than the viewport and add scrolling within content if needed (Chiemezuo Akujobi)
 * Prevent snippets model index view from crashing when a model does not have an `objects` manager (Jhonatan Lopes)
 * Fix `get_dummy_request`'s resulting host name when running tests with `ALLOWED_HOSTS = ["*"]` (David Buxton)
 * Fix timezone handling in the `timesince_last_update` template tag (Matt Westcott)
 * Fix Postgres phrase search to respect the language set in settings (Ihar Marhitych)
 * Retain query parameters when switching between locales in the page chooser (Abdelrahman Hamada, Sage Abdullah)
 * Add `w-kbd-scope-value` with support for `global` so that specific keyboard shortcuts (e.g. ctrl+s/cmd+s) trigger consistently even when focused on fields (Neeraj Yetheendran)
 * Improve exception handling when generating image renditions concurrently (Andy Babic)
 * Respect `WAGTAIL_ALLOW_UNICODE_SLUGS` setting when auto-generating slugs (LB (Ben) Johnston)
 * Use correct URL when redirecting back to page search results after an AJAX search (Sage Abdullah)
 * Reinstate missing static files in style guide (Sage Abdullah)
 * Provide [`convert_mariadb_uuids`](convert_mariadb_uuids) management command to assist with upgrading to Django 5.0+ on MariaDB (Matt Westcott)


### Documentation

 * Add contributing development documentation on how to work with a [fork of Wagtail](developing_using_a_fork) (Nix Asteri, Dan Braghis)
 * Make sure the settings panel is listed in tabbed interface examples (Tibor Leupold)
 * Update content and page names to their US spelling instead of UK spelling (Victoria Poromon)
 * Update broken and incorrect links throughout the documentation (EK303)
 * Fix formatting of `--purge-only` in [`wagtail_update_image_renditions`](wagtail_update_image_renditions) management command section (Pranith Beeram)
 * Update [template components](creating_template_components) documentation to better explain the usage of the Laces library (Tibor Leupold)
 * Update Sphinx theme to `6.3.0` with a fix for the missing favicon (Sage Abdullah)
 * Document risk of XSS attacks on document upload in [](wagtaildocs_serve_method) and example settings (Matt Westcott, with thanks to Georgios Roumeliotis of TwelveSec for the original report)
 * Add clarity to how custom [StreamField validation](streamfield_validation) works (Tibor Leupold)
 * Add additional reference to the [`wagtail_update_image_renditions`](wagtail_update_image_renditions) management command on the [using images](regenerate_image_renditions) page (LB (Ben) Johnston)
 * Correct information about line endings in Window development docs (Sage Abdullah)
 * Improve code snippets for ["Create a footer for all pages"](../tutorial/create_footer_for_all_pages) tutorial section (Drikus Roor)
 * Update list of third-party tutorials (LB (Ben) Johnston)
 * Update [Integrating into Django](../getting_started/integrating_into_django) documentation to emphasise creating page models (Matt Westcott)


### Maintenance

 * Move RichText HTML whitelist parser to use the faster, built in `html.parser` rather than `html5lib` (Jake Howard)
 * Remove duplicate 'path' in default_exclude_fields_in_copy (Ramchandra Shahi Thakuri)
 * Update unit tests to always use the faster, built in `html.parser` & remove `html5lib` dependency (Jake Howard)
 * Adjust Eslint rules for TypeScript files (Karthik Ayangar)
 * Rename the React `Button` that only renders links (a element) to `Link` and remove unused prop & behaviors that was non-compliant for aria role usage (Advik Kabra)
 * Set up an `wagtail.models.AbstractWorkflow` model to support future customizations around workflows (Hossein)
 * Improve `classnames` template tag to handle nested lists of strings, use template tag for admin `body` element (LB (Ben) Johnston)
 * Merge `UploadedDocument` and `UploadedImage` into new `UploadedFile` model for easier shared code usage (Advik Kabra, Karl Hobley)
 * Optimize queries in dashboard panels (Sage Abdullah)
 * Optimize queries in group create/edit view (Sage Abdullah)
 * Move modal-workflow.js script usage to base admin template instead of ad-hoc imports (Elhussein Almasri)
 * Update all Draftail chooserUrls to be passed in via the Entity options instead of using `window.chooserUrls` globals, removing the need for inline scripts (Elhussein Almasri)
 * Enhance `w-init` (InitController) to support a `detail` value to be dispatched on events (Chiemezuo Akujobi)
 * Remove usage of inline scripts and instead use event dispatching to instantiate standalone Draftail editor instances (Chiemezuo Akujobi)
 * Refactor `page_breadcrumbs` tag to use shared `breadcrumbs.html` template (Sage Abdullah)
 * Add `keyboard` icon to admin icon set (Rohit Sharma)
 * Remove dead code in the minimap when elements are not found (LB (Ben) Johnston)
 * Ensure untrusted data sources are logged correctly in the Stimulus `SwapController` (LB (Ben) Johnston)
 * Update Wagtail logo in admin sidebar & favicon plus documentation to the latest version (Osaf AliSayed, Albina Starykova, LB (Ben) Johnston)
 * Remove usage of inline scripts and instead use a new Stimulus controller (`w-block`/`BlockController`) to instantiate `StreamField` blocks (Karthik Ayangar)
 * Update NPM Babel, TypeScript and Webpack packages (Neeraj Yetheendran)
 * Replace ad-hoc JavaScript and vendor Mousetrap usage to a new Stimulus controller (`w-kbd`/`KeyboardController`) (Neeraj Yetheendran)
 * Update django-filter to 24.x (Sebastian Muthwill)
 * Remove jQuery usage in telepath widget classes (Matt Westcott)
 * Remove `xregexp` (IE11 polyfill) along with `window.XRegExp` global util (LB (Ben) Johnston)
 * Refactor the Django port of `urlify` to use TypeScript, officially deprecate `window.URLify` global util (LB (Ben) Johnston)


## Upgrade considerations - changes affecting Wagtail customizations

### Changes to `SubmissionsListView` class

As part of the Universal Listings project, the `SubmissionsListView` for listing form submissions in the `wagtail.contrib.forms` app has been refactored to become a subclass of `wagtail.admin.views.generic.base.BaseListingView`. As a result, the class has undergone a number of changes, including the following:

- The filtering mechanism has been reimplemented to use django-filter.
- The `ordering` attribute has been renamed to `default_ordering`.
- The template used to render the view has been significantly refactored to use the new universal listings UI.


### `register_user_listing_buttons` hook signature changed

The function signature for the [`register_user_listing_buttons`](register_user_listing_buttons) hook was updated to accept a `request_user` argument instead of `context`. If you use this hook, make sure to update your function signature to match the new one. The old signature with `context` will continue to work for now, but the context only contains the request object. Support for the old signature will be removed in a future release.

### `PASSWORD_REQUIRED_TEMPLATE` has changed to `WAGTAIL_PASSWORD_REQUIRED_TEMPLATE`

The setting `PASSWORD_REQUIRED_TEMPLATE` has been deprecated, it will continue to work until a future release but the new name for this same setting will be `WAGTAIL_PASSWORD_REQUIRED_TEMPLATE` to align with other settings naming conventions.

See [](frontend_authentication).

### `DOCUMENT_PASSWORD_REQUIRED_TEMPLATE` has changed to `WAGTAILDOCS_PASSWORD_REQUIRED_TEMPLATE`

The setting `DOCUMENT_PASSWORD_REQUIRED_TEMPLATE` has been deprecated, it will continue to work until a future release but the new name for this same setting will be `WAGTAILDOCS_PASSWORD_REQUIRED_TEMPLATE` to align with other settings naming conventions.

See [](frontend_authentication).

## Upgrade considerations - changes to undocumented internals

### Removal of `html5lib` dependency

Wagtail now uses `html.parser` for its rich text processing, and no longer depends on `html5lib`. If your project relies on `html5lib`, update rich text code to use Wagtail’s documented rich text APIs like [rewrite handlers](rich_text_rewrite_handlers) and [format converters](rich_text_format_converters). Or update project dependencies to include `html5lib`.

### Deprecation of `user_listing_buttons` template tag

The undocumented `user_listing_buttons` template tag has been deprecated and will be removed in a future release.

### Deprecation of `wagtailusers_groups:users` URL pattern

The undocumented `wagtailusers_groups:users` URL pattern has been deprecated and will be removed in a future release. If you are using `reverse` with this URL pattern name, you should update your code to use the `wagtailusers_users:index` URL pattern name and the group ID as the `group` query parameter. For example:

```python
reverse('wagtailusers_groups:users', args=[group.id])
```

should be updated to:

```python
reverse('wagtailusers_users:index') + f'?group={group.id}'
```

The corresponding `wagtailusers_groups:users_results` URL pattern has been removed as part of this change.

A redirect from the old URL pattern to the new one has been added to ensure that existing URLs continue to work. This redirect will be removed in a future release.

### Deprecation of `window.chooserUrls` within Draftail choosers

The undocumented usage of the JavaScript `window.chooserUrls` within Draftail choosers will be removed in a future release.

The following `chooserUrl` object values will be impacted.

-   `anchorLinkChooser`
-   `documentChooser`
-   `emailLinkChooser`
-   `embedsChooser`
-   `externalLinkChooser`
-   `imageChooser`
-   `pageChooser`

Overriding these inner values on the global `window.chooserUrls` object will still override their usage in the Draftail choosers for now but this capability will be removed in a future release.

#### Example

It's recommended that usage of this global is removed in any customizations or third party packages and instead a custom Draftail Entity be used instead. See example below.

##### Old

```python
# .../wagtail_hooks.py

@hooks.register("insert_editor_js")
def editor_js():
    return format_html(
        """
        <script>
            window.chooserUrls.myCustomChooser = '{0}';
        </script>
        """,
        reverse("myapp_chooser:choose"),
    )
```

##### New

Remove the `insert_editor_js` hook usage and instead pass the data needed via the Entity's data.

```python
# .../wagtail_hooks.py

from django.urls import reverse_lazy

@hooks.register("register_rich_text_features")
def register_my_custom_feature(features):
    # features.register_link_type...

    features.register_editor_plugin(
        "draftail",
        "custom-link",
        draftail_features.EntityFeature(
            {
                "type": "CUSTOM_ITEM",
                "icon": "doc-full-inverse",
                "description": gettext_lazy("Item"),
                "chooserUrls": {
                    # Important: `reverse_lazy` must be used unless the URL path is hard-coded
                    "myChooser": reverse_lazy("myapp_chooser:choose")
                },
            },
            js=["..."],
        ),
    )

```

#### Overriding existing `chooserUrls` values

To override existing chooser Entities' `chooserUrls` values, here is an example of an unsupported monkey patch can achieve a similar goal.

However, it's recommended that a [custom Entity be created](creating_new_draftail_editor_entities) to be registered as a replacement feature for Draftail customizations as per the documentation.

```py
# .../wagtail_hooks.py
from django.urls import reverse_lazy

from wagtail import hooks

@hooks.register("register_rich_text_features")
def override_embed_feature_url(features):
    features.plugins_by_editor["draftail"]["embed"].data["chooserUrls"]["embedsChooser"] = reverse_lazy("my_embeds:chooser")
```

### Deprecation of `window.initBlockWidget` to initialize a StreamField block

The undocumented global function `window.initBlockWidget` has now been deprecated and will be removed in a future release.

Any complex customizations that have re-implemented parts of this functionality will need to be modified to adopt the new approach that uses Stimulus and avoids inline scripts.

The usage of this new approach is still unofficial and could change in the future, it's recommended that the documented `BlockWidget` and `StreamField` approaches be used instead.

However, for comparison, here is the old and new approach below.

### Old

Assuming we are using Django's `format_html` to prepare the HTML output with `JSON.dumps` strings for the block data values.

The old approach would call the `window.initBlockWidget` global function with an inline script as follows:

```html
<div
    id="{id}"
    data-block="{block_json}"
    data-value="{value_json}"
    data-error="{error_json}"
></div>
<script>
    initBlockWidget('{id}');
</script>
```

### New

In the new approach, we no longer need to attach an inline script but instead use the Stimulus data attributes to attach the behavior with the `w-block` identifier as follows:

```html
<div
    id="{id}"
    data-block
    data-controller="w-block"
    data-w-block-data-value="{block_json}"
    data-w-block-arguments-value="[{value_json},{error_json}]"
></div>
```

### Removal of jQuery from base client-side Widget and BoundWidget classes

The JavaScript base classes `Widget` and `BoundWidget` that provide client-side access to form widgets (see [](streamfield_widget_api)) no longer use jQuery. The `input` property of `BoundWidget` (previously a jQuery collection) is now a native DOM element, and the `element` argument passed to the `BoundWidget` constructor (previously a jQuery collection) is now passed as a native DOM element if the HTML representation consists of a single element, and an iterable of elements (`NodeList` or array) otherwise. User code that extends these classes should be updated accordingly.

### `window.URLify` deprecated

The undocumented client-side global util `window.URLify` is now deprecated and will be removed in a future release.

If this was required for slug field behavior, it's recommended that the `SlugInput` widget be used instead. This will automatically convert values entered into a suitable slug in the browser while respecting the global configuration `WAGTAIL_ALLOW_UNICODE_SLUGS`.

```python
from wagtail.admin.widgets.slug import SlugInput
# ... other imports

class MyPage(Page):
    promote_panels = [
        FieldPanel("slug", widget=SlugInput),
        # ... other panels
    ]
```

If you require this for custom JavaScript functionality, it's recommended you either include your own implementation from the original [Django URLify source](https://raw.githubusercontent.com/wagtail/wagtail/stable/6.0.x/wagtail/admin/static_src/wagtailadmin/js/vendor/urlify.js). Alternatively, the [`slugify`](https://www.npmjs.com/package/slugify) or [`parameterize` (a Django URLify port)](https://www.npmjs.com/package/parameterize) NPM packages might be suitable.

### `window.XRegExp` polyfill removed

The legacy `window.XRegExp` global polyfill util has been removed and will throw an error if called.

Instead, any usage of this should be updated to the well supported [browser native Regex implementation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions).

```javascript
// old
const newStr = XRegExp.replace(originalStr, XRegExp('[ab+c]', 'g'), '')

// new (with RegExp)
const newStr = originalStr.replace(new RegExp('[ab+c]', 'g'), '')
// OR
const newStr = originalStr.replace(/[ab+c]/g, '')
```
