# Wagtail 6.0 release notes

_February 7, 2024_

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

## What's new

### Django 5.0 support

This release adds support for Django 5.0. The support has also been backported to Wagtail 5.2 LTS.

### New developer tutorial

A new [developer tutorial](../tutorial/index) series has been added to the documentation. This series builds upon the pre-existing [](/getting_started/tutorial), going through the creation and deployment of a portfolio website.

This tutorial series was created by Damilola Oladele as part of the Google Season of Docs program, with support from Meagen Voss, and Thibaud Colas. We also thank Storm Heg, Kalob Taulien, Kátia Nakamura, Mariusz Felisiak, and Rachel Smith for their support and feedback as part of the project.

### Universal listings

Following design improvements to the page listing view, Wagtail now provides a unified search and filtering interface for all listings. This will improve navigation capabilities, particularly for sites with a large number of pages or where content tends to use a flat structure.

In this release, the universal listing interface is available for Pages, Snippets, and Forms. For pages, the UI includes the following filters out of the box:

 * Page type
 * Date updated
 * Owner
 * Edited by
 * Site
 * Has child pages
 * Locale

This feature was developed by Ben Enright, Matt Westcott, Nick Lee, Thibaud Colas, and Sage Abdullah.

### Right-to-left language support

The admin interface now supports right-to-left languages, such as Persian, Arabic, and Hebrew. Though there are still some areas that need improvement, all admin views will now be displayed in the correct direction. Review our [UI guidelines](../contributing/ui_guidelines) for guidance on supporting right-to-left languages in admin interface customizations.

Thank you to Thibaud Colas, Badr Fourane, and Sage Abdullah for their work on this long-requested improvement.

### Accessibility checker in page editor

The [built-in accessibility checker](authoring_accessible_content) now displays as a side panel within page and snippet editors supporting preview. The new "Checks" side panel only shows accessibility-related issues for pages with the userbar enabled in this release, but will be updated to support [any content checks](https://github.com/wagtail/wagtail/discussions/11063) in the future.

This feature was implemented by Nick Lee, Thibaud Colas, and Sage Abdullah.

### Page types usage report

The new Page types report provides a breakdown of the number of pages for each type. It helps answer questions such as:

 * Which page types do we have on our CMS?
 * How many pages of that page type do we have?
 * When was a page of that type last edited? By whom? Which page was that?

This feature was developed by Jhonatan Lopes, as part of a sponsorship by the Mozilla Foundation.

### Accessibility improvements

This release comes with a high number of accessibility improvements across the admin interface.

 * Improve layout and accessibility of the image URL generator page, reduce reliance on JavaScript (Temidayo Azeez)
 * Remove overly verbose image captions in image listings for screen readers (Sage Abdullah)
 * Ensure screen readers and dictation tools can more easily navigate bulk actions in images, documents and page listings by streamlining labels and descriptions (Sage Abdullah)
 * Add optional caption field to `TypedTableBlock` (Tommaso Amici, Cynthia Kiser)
 * Switch the `TableBlock` header controls to a field that requires user input (Bhuvnesh Sharma, Aman Pandey, Cynthia Kiser)
 * Add support for `caption` on admin UI Table component (Aman Pandey)
 * Replace legacy dropdown component with new Tippy dropdown-button (Thibaud Colas)
 * Ensure the sidebar account toggle has no duplicate accessible labels (Nandini Arora)
 * Ensure that page listing re-ordering messages and accessible labels can be translated (Aman Pandey, LB (Ben) Johnston)
 * Resolve multiple issues with page listing re-ordering using keyboard and screen readers (Aman Pandey)
 * When using an empty table header (`th`) for visual spacing, ensure this is ignored by accessibility tooling (V Rohitansh)
 * Ensure that TableBlock cells are accessible when using keyboard control only (Elhussein Almasri)

### Other features

 * Added [`search_index` option to StreamField](streamfield_search) blocks to control whether the block is indexed for searching (Vedant Pandey)
 * Remember previous location on returning from page add/edit actions (Robert Rollins)
 * Update settings file in project settings to address Django 4.2 deprecations (Sage Abdullah)
 * Allow `UniqueConstraint` in place of `unique_together` for {class}`~wagtail.models.TranslatableMixin`'s system check (Temidayo Azeez, Sage Abdullah)
 * Make use of `IndexView.get_add_url()` in snippets index view template (Christer Jensen, Sage Abdullah)
 * Allow `Page.permissions_for_user()` to be overridden by specific page types (Sébastien Corbin)
 * Improve visual alignment of explore icon in Page listings for longer content (Krzysztof Jeziorny)
 * Add `extra_actions` blocks to Snippets and generic index templates (Bhuvnesh Sharma)
 * Add support for defining `panels` / `edit_handler` on `ModelViewSet` (Sage Abdullah)
 * Use a single instance of `PagePermissionPolicy` in `wagtail.permissions` module (Sage Abdullah)
 * Add max tag length validation for multiple uploads (documents/images) (Temidayo Azeez)
 * Ensure expanded side panel does not overlap form content for most viewports (Chiemezuo Akujobi)
 * Add ability to [modify the default ordering](page_model_ref) for the page explorer view (Shlomo Markowitz)
 * Remove support for Safari 14 (Thibaud Colas)
 * Add ability to click to copy the URL in the image URL generator page (Sai Srikar Dumpeti)
 * Show edit as a main action in generic history and usage views (Sage Abdullah)
 * Make styles for header buttons consistent (Sage Abdullah)
 * Improve styles of slim header's search and filters (Sage Abdullah)
 * Change page listing's add button to icon-only (Sage Abdullah)
 * Add sublabel to breadcrumbs, including history, usage, and inspect views (Sage Abdullah)
 * Standardise search form placeholder to 'Search…' (Sage Abdullah)
 * Use SlugInput on all SlugFields by default (LB (Ben) Johnston)
 * Show character counts on RichTextBlock with `max_length` (Elhussein Almasri)
 * Move locale selector in generic IndexView to a filter (Sage Abdullah)
 * Add ability to [customize a page's copy form](custom_page_copy_form) including an auto-incrementing slug example (Neeraj Yetheendran)
 * Add [`WAGTAILADMIN_LOGIN_URL` setting](wagtailadmin_login_url) to allow customizing the login URL (Neeraj Yetheendran)
 * Polish dark theme styles and update color tokens (Thibaud Colas, Rohit Sharma)
 * Keep database state of pages and snippets updated while in draft state (Stefan Hammer)
 * Add `DrilldownController` and `w-drilldown` component to support drilldown menus (Thibaud Colas)
 * Add API support for a [redirects (contrib)](redirects_api_endpoint) endpoint (Rohit Sharma, Jaap Roes, Andreas Donig)
 * Add the default ability for all `SnippetViewSet` & `ModelViewSet` to support [being copied](modelviewset_copy), this can be disabled by `copy_view_enabled = False` (Shlomo Markowitz)
 * Support dynamic Wagtail guide links in the admin that are based on the running version of Wagtail (Tidiane Dia)


### Bug fixes

 * Update system check for overwriting storage backends to recognize the `STORAGES` setting introduced in Django 4.2 (phijma-leukeleu)
 * Prevent password change form from raising a validation error when browser autocomplete fills in the "Old password" field (Chiemezuo Akujobi)
 * Ensure that the legacy dropdown options, when closed, do not get accidentally clicked by other interactions on wide viewports (CheesyPhoenix, Christer Jensen)
 * Add a fallback background for the editing preview iframe for sites without a background (Ian Price)
 * Preserve whitespace in rendered comments (Elhussein Almasri)
 * Remove search logging from project template so that new projects without the search promotions module will not error (Matt Westcott)
 * Ensure text only email notifications for updated comments do not escape HTML characters (Rohit Sharma)
 * Use the latest draft when copying an unpublished page for translation (Andrey Nehaychik)
 * Make Workflow and Aging Pages reports only available to users with page-related permissions (Rohit Sharma)
 * Make searching on specific fields work correctly on Elasticsearch when boost is in use (Matt Westcott)
 * Use a visible border and background color to highlight active formatting in the rich text toolbar (Cassidy Pittman)
 * Ensure image focal point box can be removed (Gunnar Scherf)
 * Ensure that Snippets search results correctly use the `index_results.html` or `index_results_template_name` override on initial load (Stefan Hammer)
 * Avoid error when attempting to moderate a page drafted by a now deleted user (Dan Braghis)
 * Do not show multiple error messages when editing a Site to use existing hostname and port (Rohit Sharma)
 * Avoid error when exporting Aging Pages report where a page has an empty `last_published_by_user` (Chiemezuo Akujobi)
 * Ensure Page querysets support using `alias` and `specific` (Tomasz Knapik)
 * Ensure workflow dashboard panels work when the page/snippet is missing (Sage Abdullah)
 * Ensure `ActionController` explicitly checks for elements that allow select functionality (Nandini Arora)
 * Prevent a ValueError with `FormSubmissionsPanel` on Django 5.0 when creating a new form page (Matt Westcott)
 * Avoid duplicate entries in "Recent edits" panel when copying pages (Matt Westcott)
 * Prevent TitleFieldPanel from raising an error when the slug field is missing or read-only (Rohit Sharma)
 * Ensure that the close button on the new dialog designs is visible in the non-message variant (Nandini Arora)
 * Avoid text overflow issues in comment replies and scroll position issues for long comments (Rohit Sharma)
 * Remove 'Page' from page types filter on aging pages report (Matt Westcott)
 * Prevent page types filter from showing other non-Page models that match by name (Matt Westcott)
 * Ensure `MultipleChooserPanel` modal works correctly when `USE_THOUSAND_SEPARATOR` is `True` for pages with ids over 1,000 (Sankalp, Rohit Sharma)
 * Ensure the panel anchor button sizes meet accessibility guidelines for minimum dimensions (Nandini Arora)
 * Raise a 404 for bulk actions for models which don't exist instead of throwing a 500 error (Alex Tomkins)
 * Raise a `SiteSetting.DoesNotExist` error when retrieving settings for an unrecognized site (Nick Smith)
 * Ensure that defaulted or unique values declared in `exclude_fields_in_copy` are correctly excluded in new copies, resolving to the default value (Elhussein Almasri)
 * Ensure that `default_ordering` set on IndexView is preserved if ModelViewSet does not specify an explicit ordering (Cynthia Kiser)
 * Resolve issue where clicking Publish for a Page that was in workflow in Safari would block publishing and not trigger the workflow confirmation modal (Alex Morega)
 * Fix pagination links on model history and usage views (Matt Westcott)
 * Fix crash when accessing workflow reports with a deleted snippet (Sage Abdullah)


### Documentation

 * Document, for contributors, the use of translate string literals passed as arguments to tags and filters using `_()` within templates (Chiemezuo Akujobi)
 * Document all features for the Documents app in one location, see [](../advanced_topics/documents/index) (Neeraj Yetheendran)
 * Add section to [testing docs](../advanced_topics/testing) about creating pages and working with page content (Mariana Bedran Lesche)
 * Add more nuance to the database recommendations in [](performance_overview) (Jadesola Kareem)
 * Add clarity that [`MultipleChooserPanel`](multiple_chooser_panel) may require a chooser viewset and how the functionality is expected to work (Andy Chosak)
 * Clarify where documentation build commands should be run (Nikhil S Kalburgi)
 * Add missing import to tutorial BlogPage example (Salvo Polizzi)
 * Update contributing guide documentation and GitHub templates to better support new contributors (Thibaud Colas)
 * Add more CSS authoring guidelines (Thibaud Colas)
 * Update MyST documentation parser library to 2.0.0 (Neeraj Yetheendran)
 * Add documentation writing guidelines for intersphinx / external links (LB (Ben) Johnston)
 * Add `Page` model reference `get_children` documentation (Salvo Polizzi)
 * Enforce CI build checks for documentation so that malformed links or missing images will not be allowed (Neeraj Yetheendran)
 * Update spelling on customizing admin template and page model section from British to American English (Victoria Poromon)
 * Add documentation for how to override the file locations for custom image models [](custom_image_model_upload_location) (Osaf AliSayed, Dharmik Gangani)
 * Update documentation theme (Sphinx Wagtail Theme) to 6.2.0, fixing the incorrect favicon (LB (Ben) Johnston, Sahil Jangra)
 * Refactor promotion banner without jQuery and use sameSite cookies when storing if cleared (LB (Ben) Johnston)
 * Use cross-reference for compatible Python versions in tutorial instead of the out of date listing (mirusu400)

### Maintenance

#### Generic class-based views adoption

As part of ongoing refactorings, we have migrated several views to use generic class-based views. This allows for easier extensibility and better code reuse.

 * Migrate the contrib styleguide index view to a class-based view (Chiemezuo Akujobi)
 * Migrate the contrib settings edit view to a class-based view (Chiemezuo Akujobi, Sage Abdullah)
 * Migrate account editing view to a class-based view (Kehinde Bobade)
 * Refactor page explorer index template to extend generic index template (Sage Abdullah)
 * Refactor snippets index view and template to make better use of generic IndexView (Sage Abdullah)
 * Refactor documents listing view to use generic IndexView (Sage Abdullah)
 * Refactor images listing view to use generic IndexView (Sage Abdullah)
 * Refactor form pages listing view to use generic IndexView (Sage Abdullah)
 * Reduce gap between snippets and generic views/templates (Sage Abdullah)

#### Other maintenance

 * Update BeautifulSoup upper bound to 4.12.x (scott-8)
 * Migrate initialization of classes (such as `body.ready`) from multiple JavaScript implementations to one Stimulus controller `w-init` (Chiemezuo Akujobi)
 * Adopt the usage of translate string literals using `arg=_('...')` in all `wagtailadmin` module templates (Chiemezuo Akujobi)
 * Update djhtml to 3.0.6 (Matt Westcott)
 * Remove django-pattern-library upper bound in testing dependencies (Sage Abdullah)
 * Split up functions in Elasticsearch backend for easier extensibility (Marcel Kornblum, Cameron Lamb, Sam Dudley)
 * Relax draftjs_exporter dependency to allow using version 5.x (Sylvain Fankhauser)
 * Refine styling of listings, account settings panels and the block chooser (Meli Imelda)
 * Remove icon font support (Matt Westcott)
 * Remove deprecated SVG icons (Matt Westcott)
 * Remove icon font styles (Thibaud Colas)
 * Upgrade frontend tooling to use Node 20 (LB (Ben) Johnston)
 * Upgrade `ruff` and replace `black` with `ruff format` (John-Scott Atlakson)
 * Update Willow upper bound to 2.x (Dan Braghis)
 * Removed support for Django < 4.2 (Dan Braghis)
 * Replace template components implementation with [standalone `laces` library](https://pypi.org/project/laces/) (Tibor Leupold)
 * Introduce an internal `{% formattedfield %}` tag to replace direct use of `wagtailadmin/shared/field.html` (Matt Westcott)
 * Update Telepath dependency to 0.3.1 (Matt Westcott)
 * Allow `ActionController` to have a `noop` method to more easily leverage standalone Stimulus action options (Nandini Arora)
 * Upgrade to latest TypeScript and Storybook (Thibaud Colas, Sage Abdullah)
 * Turn on `skipLibCheck` for TypeScript (LB (Ben) Johnston)
 * Support for the Stimulus `CloneController` to auto clear the added content after a set duration (LB (Ben) Johnston)
 * Update Stylelint, our linting configuration, Sass, and related code changes (LB (Ben) Johnston)
 * Simplify browserslist and browser support documentation for maintainers (Thibaud Colas)
 * Relax django-taggit dependency to allow 5.0 (Sylvain Fankhauser)
 * Fix various warnings when building docs (Cynthia Kiser)
 * Upgrade sphinxcontrib-spelling to 7.x for Python 3.12 compatibility (Matt Westcott)
 * Move logic for django-filters filtering into `BaseListingView` (Matt Westcott)
 * Remove or replace legacy CSS classes: visuallyhidden, visuallyvisible, divider-after, divider-before, inline, inline-block, block, u-hidden, clearfix, reordering, overflow (Thibaud Colas)
 * Prevent future issues with icon.html end-of-file newlines (Thibaud Colas)
 * Rewrite styles using legacy `c-`, `o-`, `u-`, `t-`, `is-` prefixes (Thibaud Colas)
 * Remove invalid CSS styles / Sass selector concatenation (Thibaud Colas)
 * Refactor listing views to share more queryset ordering logic (Matt Westcott)
 * Remove `initTooltips` in favor of Stimulus controller (LB (Ben) Johnston)
 * Enhance the Stimulus `InitController` to allow for custom event dispatching when ready (Aditya, LB (Ben) Johnston)
 * Remove inline script usage for comment initialization and adopt an event listener/dispatch approach for better CSP compliance (Aditya, LB (Ben) Johnston)
 * Migrate styleguide ad-hoc JavaScript to use styles only to avoid CSP issues (LB (Ben) Johnston)
 * Update Jest version - frontend tooling (Nandini Arora)
 * Remove non-functional and inaccessible auto-focus on first field in page create forms (LB (Ben) Johnston)
 * Migrate the unsaved form checks & confirmation trigger to Stimulus `UnsavedController` (Sai Srikar Dumpeti, LB (Ben) Johnston)
 * Migrate page listing menu re-ordering (drag & drop) from jQuery inline scripts to `OrderableController` with a more accessible solution (Aman Pandey, LB (Ben) Johnston)
 * Clean up scss variable usage, remove unused variables and mixins, adopt more core token variables (Jai Vignesh J, Nandini Arora, LB (Ben) Johnston)
 * Migrate Image URL generator views to class-based views (Rohit Sharma)
 * Use Django's `FileResponse` when serving files such as Images or Documents (Jake Howard)
 * Deprecated `WidgetWithScript` base widget class (LB (Ben) Johnston)
 * Remove support for Django 4.1 and below (Sage Abdullah)

## Upgrade considerations - removal of deprecated features from Wagtail 4.2 - 5.1

Features previously deprecated in Wagtail 4.2, 5.0 and 5.1 have been fully removed. For additional details on these changes, see:

 * [Wagtail 4.2 release notes](/releases/4.2)
 * [Wagtail 5.0 release notes](/releases/5.0)
 * [Wagtail 5.1 release notes](/releases/5.1)

The most significant changes are highlighted below.

### Removal of ModelAdmin app

The `wagtail.contrib.modeladmin` app has been removed. If you wish to continue using it, it is available as the external package [`wagtail-modeladmin`](https://github.com/wagtail-nest/wagtail-modeladmin).

### `Query` model moved to `wagtail.contrib.search_promotions`

The `Query` model (used to log search queries performed by users, to identify commonly searched terms) is no longer part of the `wagtail.search` module; it can now be found in the optional `wagtail.contrib.search_promotions` app. When updating code to import the model from the new location, ensure that you have added `wagtail.contrib.search_promotions` to your `INSTALLED_APPS` setting - failing to do this may result in a spurious migration being created within the core `wagtail` app.

### Support for Elasticsearch 5 and 6 dropped

The Elasticsearch 5 and 6 backends have been removed. If you are using one of these backends, you will need to upgrade to Elasticsearch 7 or 8 before upgrading to Wagtail 6.0.

### StreamField no longer requires `use_json_field=True`

The `use_json_field` argument to `StreamField` is no longer required, and can be removed. StreamField now consistently uses JSONField for its database representation, and Wagtail 5.0 required older TextField-based streams to be migrated. As such, `use_json_field` no longer has any effect.

### Other removals

 * The `WAGTAILADMIN_GLOBAL_PAGE_EDIT_LOCK` setting is no longer recognized and should be replaced with `WAGTAILADMIN_GLOBAL_EDIT_LOCK`.
 * The `wagtail.models.UserPagePermissionsProxy` class and `get_pages_with_direct_explore_permission`, `get_explorable_root_page` and `users_with_page_permission` functions have been removed; equivalent functionality exists in the `wagtail.permission_policies.pages.PagePermissionPolicy` class.
 * The `permission_type` field of the `GroupPagePermission` model has been removed; the `permission` field (a foreign key to Django's `Permission` model) should be used instead.
 * The legacy moderation system used before the introduction of workflows in Wagtail 2.10 has been removed. Any moderation requests still in the queue from before this time will be lost.
 * The Wagtail icon font has been removed; any direct usage of this needs to be converted to SVG icons.
 * Various unused icons deprecated in Wagtail 5.0 have been removed.
 * The `partial_match` argument on `SearchField` and on `search` methods has been removed. `AutocompleteField` and the `autocomplete` method should be used instead.
 * The `insert_editor_css` hook has been removed; the `insert_global_admin_css` hook should be used instead.
 * The `wagtail.contrib.frontend_cache` module now supports `azure-mgmt-cdn` version 10 and `azure-mgmt-frontdoor` version 1 as its minimum supported versions.
 * The `Task.page_locked_for_user` method has been removed; `Task.locked_for_user` should be used instead.
 * The `{% icon %}` template tag no longer accepts `class_name` as an argument; `classname` should be used instead.
 * The `wagtail.tests.utils` module has been removed and can now be found at `wagtail.test.utils`.
 * The template `wagtailadmin/shared/field_as_li.html` has been removed, and should be replaced with `wagtailadmin/shared/field.html` enclosed in an `<li>` tag.
 * The custom client-side events `wagtail:show` and `wagtail:hide` on showing and hiding dialogs have been removed; `w-dialog:show` and `w-dialog:hide` should be used instead.
 * The global Javascript definitions `headerSearch`, `initTagField`, `cancelSpinner` and `unicodeSlugsEnabled` have been removed; these should be replaced with Stimulus controllers.

## Upgrade considerations - changes affecting all projects

### Changes to UUID fields on MariaDB when upgrading to Django 5.0

Django 5.0 introduces support for MariaDB's native UUID type on MariaDB 10.7 and above. This breaks backwards compatibility with `CHAR`-based UUIDs created on earlier versions of Django and MariaDB, and so upgrading a site to Django 5.0+ and MariaDB 10.7+ is liable to result in errors such as `Data too long for column 'translation_key' at row 1` or `Data too long for column 'uuid' at row 1` when creating or editing pages. To fix this, it is necessary to run the [`convert_mariadb_uuids`](convert_mariadb_uuids) management command (available as of Wagtail 6.0.3) after upgrading:

```sh
./manage.py convert_mariadb_uuids
```

This will convert all existing UUID fields used by Wagtail to the new format. New sites created under Django 5.0+ and MariaDB 10.7+ are unaffected.

### `SnippetViewSet` & `ModelViewSet` copy view enabled by default

The newly introduced copy view will be enabled by default for all `ModelViewSet` and `SnippetViewSet` classes.

This can be disabled by setting `copy_view_enabled = False`, for example.

```python
class PersonViewSet(SnippetViewSet):
    model = Person
    #...
    copy_view_enabled = False

class PersonViewSet(ModelViewSet):
    model = Person
    #...
    copy_view_enabled = False
```

See [](modelviewset_copy) for additional details about this feature.

## Upgrade considerations - deprecation of old functionality

### Removed support for Django < 4.2

Django versions before 4.2 are no longer supported as of this release; please upgrade to Django 4.2 or above before upgrading Wagtail.

## Upgrade considerations - changes affecting Wagtail customizations

### `SlugInput` widget is now the default for `SlugField` fields

In Wagtail 5.0 a new `SlugInput` admin widget was added to support slug behavior in Page and Page copy forms. This widget was included by default if the `promote_panels` fields layout was customized, causing confusion.

As of this release, any forms that inherit from `WagtailAdminModelForm` (includes page and snippet model editing) will now use the `SlugInput` by default on all models with `SlugField` fields.

Previously, the widget had to be explicitly added.

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

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

Keeping the widget as above is fine, but will no longer be required. The JavaScript field behavior will be included by default.

```python
# ... imports

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

If you do not want this for some reason, you will now need to declare a different widget.

```python
from django.forms.widgets import TextInput
# ... other imports

class MyPage(Page):
    promote_panels = [
        FieldPanel("slug", widget=TextInput), # use a plain text field
        # ... other panels
    ]
```

### Changed handling of database updates of non-live `Page` objects or subclasses of `DraftStateMixin`

Before this release, the database record of a `Page` or any subclass of `DraftStateMixin` either contained the live data (if published), the state of the last published version (if unpublished), or the state of the first revision (if never published). Subsequent draft edits would create new `Revision` records, but the main database record would not be updated. As a result, the database record could lag substantially behind the current state of the object, causing unexpected behavior particularly when unique constraints are in use.

As of this release, the database record of a non-live object will be updated to reflect the draft state of the object. This is unlikely to have a visible effect on existing sites, since the admin backend works with the `Revision` records while the site front-end typically filters out non-live objects. However, any code that relies on the database record being untouched by draft edits (for example, using it to store a specific approved / archived state of the page) may need to be updated.

## Upgrade considerations - changes to undocumented internals

### `filter_queryset` and `get_filtered_queryset` methods no longer return filters

The undocumented internal methods `filter_queryset(queryset)` on `wagtail.admin.views.generic.IndexView`, and `get_filtered_queryset()` on `wagtail.admin.views.reports.ReportView`, now return just the filtered queryset; previously they returned a tuple of `(filters, queryset)`. The filterset instance is always available as the cached property `self.filters`.

### Deprecation of the undocumented `window.enableDirtyFormCheck` function

The admin frontend `window.enableDirtyFormCheck` will be removed in a future release and as of this release only supports the basic initialization.

The previous approach was to call a window global function as follows.

```javascript
window.enableDirtyFormCheck('.my-form', { alwaysDirty: true, confirmationMessage: 'You have unsaved changes'});
```

The new approach will be data attribute driven as follows.

```text
<form
  method="POST"
  data-controller="w-unsaved"
  data-action="w-unsaved#submit beforeunload@window->w-unsaved#confirm change->w-unsaved#check keyup->w-unsaved#check"
  data-w-unsaved-confirm-value="This page has unsaved changes." // equivalent to `confirmationMessage`.
  data-w-unsaved-force-value="true" // equivalent to `alwaysDirty`.
  data-w-unsaved-watch-value="edits comments" // can add 'comments' if comments is enabled, defaults to only 'edits'.
  >
  ... form contents
</form>
```

### `data-tippy-content` attribute support will be removed

The implementation of the JS tooltips have been fully migrated to the Stimulus `w-tooltip`/`TooltipController` implementation.

Dynamic support for any `data-tippy-content="..."` usage will be removed this release, for example, within chooser modals or dynamic html response data.

Some minimal backwards compatibility support for `data-tippy-content` will work until a future release, but only in the initial HTML response on a page.

#### Data attributes

These HTML data attributes were not documented, but if any custom code implemented custom tooltips, these will need to be changed.

| Old                                          | New                                                    | Notes                                |
| -------------------------------------------- | ------------------------------------------------------ | ------------------------------------ |
|                                              | `data-controller="w-tooltip"`                          | Required, new addition for any usage |
| `data-tippy-content="{% trans 'History' %}"` | `data-w-tooltip-content-value="{% trans 'History' %}"` | Required                             |
| `data-tippy-offset="[12, 24]"`               | `data-w-tooltip-offset-value="[12, 24]"`               | Optional, default is no offset       |
| `data-tippy-placement="top"`                 | `data-w-tooltip-placement-value="top"`                 | Optional, default is 'bottom'        |

### Deprecated `WidgetWithScript` base widget class

The undocumented `WidgetWithScript` class that used inline scripts to attach JavaScript to widgets will be removed in a future release.

This approach creates security risks and will not be compliant with CSP support. Instead, it's recommended that all similar requirements migrate to use the recommended Stimulus JS integration approach.

A full example of how to build this has been documented on [extending client-side behavior](extending_client_side_stimulus_widget), a basic example is below.

#### Old

```py
from django.forms import Media, widgets


class CustomRichTextArea(WidgetWithScript, widgets.Textarea):
    def render_js_init(self, id_, name, value):
        return f"window.customEditorInitScript({json.dumps(id_)});"

    @property
    def media(self):
        return Media(js=["vendor/custom-editor.js"])
```

#### New

```py
from django.forms import Media, widgets


class CustomRichTextArea(widgets.Textarea):
    def build_attrs(self, *args, **kwargs):
        attrs = super().build_attrs(*args, **kwargs)
        attrs['data-controller'] = 'custom-editor'

    @property
    def media(self):
        return Media(js=["vendor/custom-editor.js","js/custom-editor-controller.js"])
```

```javascript
// myapp/static/js/custom-editor-controller.js

class CustomEditorController extends window.StimulusModule.Controller {
    connect() {
        window.customEditorInitScript(this.element.id);
    }
}

window.wagtail.app.register('custom-editor', CustomEditorController);
```
