docs/releases/6.1.md
May 1, 2024
---
local:
depth: 1
---
Continuing work on the Universal Listings project, this release rolls out universal listing styles for the following views:
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.
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.
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.
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.
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.
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.
wagtail.contrib.settings or ModelViewSetThis 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.
RelatedObjectsColumn to the table UI framework (Matt Westcott)WAGTAIL_DATE_FORMAT, WAGTAIL_DATETIME_FORMAT, WAGTAIL_TIME_FORMAT are correctly configured (Rohit Sharma, Coen van der Kamp)AbstractGroupApprovalTask to simplify customizing behavior of custom Task models (John-Scott Atlakson)djangorestframework to 3.15.1 (Sage Abdullah)IndexView.list_display (Abdelrahman Hamada)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 . Results are cached on request._wagtail_route_for_request (Gordon Pendleton)STORAGES alias name for WAGTAILIMAGES_RENDITION_STORAGE (Alec Baron)PASSWORD_REQUIRED_TEMPLATE setting to WAGTAIL_PASSWORD_REQUIRED_TEMPLATE with deprecation of previous naming (Saksham Misra, LB (Ben) Johnston)DOCUMENT_PASSWORD_REQUIRED_TEMPLATE setting to WAGTAILDOCS_PASSWORD_REQUIRED_TEMPLATE with deprecation of previous naming (Saksham Misra, LB (Ben) Johnston)get_parent (Nigel van Keulen)__str__ for MySQL search index (Jake Howard)date objects on human_readable_date template tag (Jhonatan Lopes)verbose_name in group edit view when listing custom permissions (Sage Abdullah, Neeraj Yetheendran, Omkar Jadhav)make livehtml (Sage Abdullah)LANGUAGE_CODE (Mark Niehues)UnsavedController checks for nested removal/additions of inputs so that the unsaved warning shows in more valid cases when editing a page (Karthik Ayangar)get_add_url() is always used to re-render the add button when the listing is refreshed in viewsets (Sage Abdullah)objects manager (Jhonatan Lopes)get_dummy_request's resulting host name when running tests with ALLOWED_HOSTS = ["*"] (David Buxton)timesince_last_update template tag (Matt Westcott)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)WAGTAIL_ALLOW_UNICODE_SLUGS setting when auto-generating slugs (LB (Ben) Johnston)convert_mariadb_uuids management command to assist with upgrading to Django 5.0+ on MariaDB (Matt Westcott)--purge-only in wagtail_update_image_renditions management command section (Pranith Beeram)6.3.0 with a fix for the missing favicon (Sage Abdullah)wagtail_update_image_renditions management command on the using images page (LB (Ben) Johnston)html.parser rather than html5lib (Jake Howard)html.parser & remove html5lib dependency (Jake Howard)Button that only renders links (a element) to Link and remove unused prop & behaviors that was non-compliant for aria role usage (Advik Kabra)wagtail.models.AbstractWorkflow model to support future customizations around workflows (Hossein)classnames template tag to handle nested lists of strings, use template tag for admin body element (LB (Ben) Johnston)UploadedDocument and UploadedImage into new UploadedFile model for easier shared code usage (Advik Kabra, Karl Hobley)window.chooserUrls globals, removing the need for inline scripts (Elhussein Almasri)w-init (InitController) to support a detail value to be dispatched on events (Chiemezuo Akujobi)page_breadcrumbs tag to use shared breadcrumbs.html template (Sage Abdullah)keyboard icon to admin icon set (Rohit Sharma)SwapController (LB (Ben) Johnston)w-block/BlockController) to instantiate StreamField blocks (Karthik Ayangar)w-kbd/KeyboardController) (Neeraj Yetheendran)xregexp (IE11 polyfill) along with window.XRegExp global util (LB (Ben) Johnston)urlify to use TypeScript, officially deprecate window.URLify global util (LB (Ben) Johnston)SubmissionsListView classAs 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:
ordering attribute has been renamed to default_ordering.register_user_listing_buttons hook signature changedThe 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_TEMPLATEThe 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_TEMPLATEThe 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.
html5lib dependencyWagtail 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.
user_listing_buttons template tagThe undocumented user_listing_buttons template tag has been deprecated and will be removed in a future release.
wagtailusers_groups:users URL patternThe 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.
window.chooserUrls within Draftail choosersThe 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.
anchorLinkChooserdocumentChooseremailLinkChooserembedsChooserexternalLinkChooserimageChooserpageChooserOverriding 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.
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.
# .../wagtail_hooks.py
@hooks.register("insert_editor_js")
def editor_js():
return format_html(
"""
<script>
window.chooserUrls.myCustomChooser = '{0}';
</script>
""",
reverse("myapp_chooser:choose"),
)
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=["..."],
),
)
chooserUrls valuesTo 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")
window.initBlockWidget to initialize a StreamField blockThe 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.
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>
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>
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 deprecatedThe 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 removedThe 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, '')