23 KiB
Wagtail 6.1 release notes
May 1, 2024
---
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 .
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 . 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 and private collections (documents) 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, with format-ico
:
<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
module or 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.
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 (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 customTask
models (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, andPage.find_for_request()
to find the page given a request object and a URL path, see . Results are cached onrequest._wagtail_route_for_request
(Gordon Pendleton) - Optimise 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 forWAGTAILIMAGES_RENDITION_STORAGE
(Alec Baron) - Update
PASSWORD_REQUIRED_TEMPLATE
setting toWAGTAIL_PASSWORD_REQUIRED_TEMPLATE
with deprecation of previous naming (Saksham Misra, LB (Ben) Johnston) - Update
DOCUMENT_PASSWORD_REQUIRED_TEMPLATE
setting toWAGTAILDOCS_PASSWORD_REQUIRED_TEMPLATE
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 onhuman_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 withALLOWED_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 forglobal
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
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 (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
inwagtail_update_image_renditions
management command section (Pranith Beeram) - Update 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 and example settings (Matt Westcott, with thanks to Georgios Roumeliotis of TwelveSec for the original report)
- Add clarity to how custom StreamField validation works (Tibor Leupold)
- Add additional reference to the
wagtail_update_image_renditions
management command on the using images 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 section (Drikus Roor)
- Update list of third-party tutorials (LB (Ben) Johnston)
- Update 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 thanhtml5lib
(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
& removehtml5lib
dependency (Jake Howard) - Adjust Eslint rules for TypeScript files (Karthik Ayangar)
- Rename the React
Button
that only renders links (a element) toLink
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 adminbody
element (LB (Ben) Johnston) - Merge
UploadedDocument
andUploadedImage
into newUploadedFile
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 adetail
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 sharedbreadcrumbs.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 instantiateStreamField
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 withwindow.XRegExp
global util (LB (Ben) Johnston) - Refactor the Django port of
urlify
to use TypeScript, officially deprecatewindow.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 todefault_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
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.
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.
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 and 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:
reverse('wagtailusers_groups:users', args=[group.id])
should be updated to:
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
# .../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.
# .../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 to be registered as a replacement feature for Draftail customizations as per the documentation.
# .../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:
<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:
<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 ) 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
.
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. Alternatively, the slugify
or parameterize
(a Django URLify port) 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.
// 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, '')