This release contains significant UI changes that affect all of Wagtail's admin, largely driven by the implementation of the new Page Editor. These include:
Rich text blocks within StreamField now provide the ability to split a block at the cursor position, allowing new blocks to be inserted in between. This feature was developed by Jacob Topp-Mugglestone and sponsored by The Motley Fool.
The panel types `StreamFieldPanel`, `RichTextFieldPanel`, `ImageChooserPanel`, `DocumentChooserPanel` and `SnippetChooserPanel` have been phased out, and can now be replaced with `FieldPanel`. Additionally, `PageChooserPanel` is only required when passing a `page_type` or `can_choose_root`, and can otherwise be replaced with `FieldPanel`. In all cases, `FieldPanel` will now automatically select the most appropriate form element. This feature was developed by Matt Westcott.
[`FieldPanel`](wagtail.admin.panels.FieldPanel) now accepts a `permission` keyword argument to specify that the field should only be available to users with a given permission level. This feature was developed by Matt Westcott and sponsored by Google as part of Wagtail's page editor redevelopment.
With every Wagtail Page you are able to add a helpful description text, similar to a `help_text` model attribute. By adding `page_description` to your Page model you'll be adding a short description that can be seen in different places within Wagtail:
Trying to upload an image that's a duplicate of one already in the image library will now lead to a confirmation step. This feature was developed by Tidiane Dia and sponsored by The Motley Fool.
When using a queryset to render a list of items with images, you can now make use of Django's built-in ``prefetch_related()`` queryset method to prefetch the renditions needed for rendering with a single extra query. For long lists of items, or where multiple renditions are used for each item, this can provide a significant boost to performance. This feature was developed by Andy Babic.
* Major updates to frontend tooling; move Node tooling from Gulp to Webpack, upgrade to Node v16 and npm v8, eslint v8, stylelint v14 and others (Thibaud Colas)
* Rename the setting `BASE_URL` (undocumented) to [`WAGTAILADMIN_BASE_URL`](wagtailadmin_base_url) and add to documentation, `BASE_URL` will be removed in a future release (Sandil Ranasinghe)
* Add the ability for choices to be separated by new lines instead of just commas within the form builder, commas will still be supported if used (Abdulmajeed Isa)
* Add useful help text to Tag fields to advise what content is allowed inside tags, including when `TAG_SPACES_ALLOWED` is `True` or `False` (Abdulmajeed Isa)
* When Documents (e.g. PDFs) have been configured to be served inline via `WAGTAILDOCS_CONTENT_TYPES`&`WAGTAILDOCS_INLINE_CONTENT_TYPES` ensure that the filename is correctly set in the `Content-Disposition` header so that saving the files will use the correct filename (John-Scott Atlakson)
Various modules of Wagtail have been reorganised, and imports should be updated as follows:
* All modules under `wagtail.core` can now be found under `wagtail` - for example, `from wagtail.core.models import Page` should be changed to `from wagtail.models import Page`
* The `wagtail.tests` module is renamed to `wagtail.test`
*`wagtail.admin.edit_handlers` is renamed to `wagtail.admin.panels`
*`wagtail.contrib.forms.edit_handlers` is renamed to `wagtail.contrib.forms.panels`
These changes can be applied automatically to your project codebase by running the following commands from the project root:
```console
wagtail updatemodulepaths --list # list the files to be changed without updating them
wagtail updatemodulepaths --diff # show the changes to be made, without updating files
wagtail updatemodulepaths # actually update the files
Within panel definitions on models, `StreamFieldPanel`, `RichTextFieldPanel`, `ImageChooserPanel`, `DocumentChooserPanel` and `SnippetChooserPanel` should now be replaced with `FieldPanel`. Additionally, `PageChooserPanel` can be replaced with `FieldPanel` if it does not use the `page_type` or `can_choose_root` arguments.
References to `BASE_URL` in your settings should be updated to [`WAGTAILADMIN_BASE_URL`](wagtailadmin_base_url). This setting was not previously documented, but was part of the default project template when starting a project with the `wagtail start` command, and specifies the full base URL for the Wagtail admin, for use primarily in email notifications.
All uses of `StreamField` should be updated to include the argument `use_json_field=True`. After adding this, make sure to generate and run migrations. This converts the field to use `JSONField` as its internal type instead of `TextField`, which will allow you to use `JSONField` lookups and transforms on the field. This change is necessary to ensure that the database migration is applied; a future release will drop support for `TextField`-based StreamFields.
IE11 support was officially dropped in Wagtail 2.15, and as of this release there will no longer be a warning shown to users of this browser. Wagtail is fully compatible with Microsoft Edge, Microsoft’s replacement for Internet Explorer. You may consider using its [IE mode](https://docs.microsoft.com/en-us/deployedge/edge-ie-mode) to keep access to IE11-only sites, while other sites and apps like Wagtail can leverage modern browser capabilities.
Hallo was deprecated in [Wagtail v2.0 (February 2018)](https://docs.wagtail.org/en/stable/releases/2.0.html#new-rich-text-editor) and has had only a minimal level of support since then. If you still require Hallo for your Wagtail installation, you will need to install the [Wagtail Hallo editor](https://github.com/wagtail/wagtail-hallo) legacy package. We encourage all users of the Hallo editor to take steps to migrate to the new Draftail editor as this external package is unlikely to have ongoing maintenance. `window.registerHalloPlugin` will no longer be created on the page editor load, unless the legacy package is installed.
### Removal of legacy `clean_name` on `AbstractFormField`
If you are upgrading a pre-2.10 project that uses the [Wagtail form builder](form_builder), and has existing form submission data that needs to be preserved, you must first upgrade to a version between 2.10 and 2.16, and run migrations and start the application server, before upgrading to 3.0. This ensures that the `clean_name` field introduced in Wagtail 2.10 is populated. The mechanism for doing this (which had a dependency on the [Unidecode](https://pypi.org/project/Unidecode/) package) has been dropped in Wagtail 3.0. Any new form fields created under Wagtail 2.10 or above use the [AnyAscii](https://pypi.org/project/anyascii/) library instead.
### Removed support for Jinja2 2.x
Jinja2 2.x is no longer supported as of this release; if you are using Jinja2 templating on your project, please upgrade to Jinja2 3.0 or above.
Various changes have been made to the internal API for defining panel types, previously known as edit handlers. As noted above, the module `wagtail.admin.edit_handlers` has been renamed to `wagtail.admin.panels`, and `wagtail.contrib.forms.edit_handlers` is renamed to `wagtail.contrib.forms.panels`.
Additionally, the base `wagtail.admin.edit_handlers.EditHandler` class has been renamed to `wagtail.admin.panels.Panel`, and `wagtail.admin.edit_handlers.BaseCompositeEditHandler` has been renamed to `wagtail.admin.panels.PanelGroup`.
Template paths have also been renamed accordingly - templates previously within `wagtailadmin/edit_handlers/` are now located under `wagtailadmin/panels/`, and `wagtailforms/edit_handlers/form_responses_panel.html` is now at `wagtailforms/panels/form_responses_panel.html`.
Where possible, third-party packages that implement their own field panel types should be updated to allow using a plain `FieldPanel` instead, in line with Wagtail dropping its own special-purpose field panel types such as `StreamFieldPanel` and `ImageChooserPanel`. The steps for doing this will depend on the package's functionality, but in general:
* If the panel sets a custom template, your code should instead define [a `Widget` class](https://docs.djangoproject.com/en/stable/ref/forms/widgets/) that produces your desired HTML rendering.
* If the panel provides a `widget_overrides` method, your code should instead call [`register_form_field_override`](/extending/forms) so that the desired widget is always selected for the relevant model field type.
* If the panel provides a `get_comparison_class` method, your code should instead call `wagtail.admin.compare.register_comparison_class` to register the comparison class against the relevant model field type.
Within the `Panel` class, the methods `widget_overrides`, `required_fields` and `required_formsets` have been deprecated in favour of a new `get_form_options` method that returns a dict of configuration options to be passed on to the generated form class:
* Panels that define `required_fields` should instead return this value as a `fields` item in the dict returned from `get_form_options`
* Panels that define `required_formsets` should instead return this value as a `formsets` item in the dict returned from `get_form_options`
* Panels that define `widget_overrides` should instead return this value as a `widgets` item in the dict returned from `get_form_options`
The methods `on_request_bound`, `on_instance_bound` and `on_form_bound` are no longer used. In previous versions, over the course of serving a request an edit handler would have the attributes `request`, `model`, `instance` and `form` attached to it, with the corresponding `on_*_bound` method being called at that point. In the new implementation, only the `model` attribute and `on_model_bound` method are still available. This means it is no longer possible to vary or patch the form class in response to per-request information such as the user object. For permission checks, you should use the new `permission` option on `FieldPanel`; for other per-request customisations to the form object, use [a custom form class](custom_edit_handler_forms) with an overridden `__init__` method. (The current user object is available from the form as `self.for_user`.)
Binding to a request, instance and form object is now handled by a new class `Panel.BoundPanel`. Any initialisation logic previously performed in `on_request_bound`, `on_instance_bound` or `on_form_bound` can instead be moved to the constructor method of a subclass of `BoundPanel`:
```python
class CustomPanel(Panel):
class BoundPanel(Panel.CustomPanel):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# The attributes self.panel, self.request, self.instance and self.form
The template context for panels derived from `BaseChooserPanel` has changed. `BaseChooserPanel` is deprecated and now functionally identical to `FieldPanel`; as a result, the context variable `is_chosen`, and the variable name given by the panel's `object_type_name` property, are no longer available on the template. The only available variables are now `field` and `show_add_comment_button`. If your template depends on these additional variables, you will need to pass them explicitly by overriding the `render_as_field` method.
* When overriding the `get_form_class` method of a ModelAdmin `CreateView` or `EditView` to pass a custom form class, that form class must now inherit from `wagtail.admin.forms.models.WagtailAdminModelForm`. Passing a plain Django ModelForm subclass is no longer valid.
* The `ModelAdmin.get_form_fields_exclude` method is no longer passed a `request` argument. Subclasses that override this method should remove this from the method signature. If the request object is being used to vary the set of fields based on the user's permission, this can be replaced with the new `permission` option on `FieldPanel`.
* The `ModelAdmin.get_edit_handler` method is no longer passed a `request` or `instance` argument. Subclasses that override this method should remove this from the method signature.
The `content_json` field in the `PageRevision` model has been renamed to `content`, and this field now internally uses `JSONField` instead of `TextField`. If you have a large number of `PageRevision` objects, running the migrations might take a while.
The `data_json` field in the `BaseLogEntry` model (and its subclasses `PageLogEntry` and `ModelLogEntry`) has been renamed to `data`, and this field now internally uses `JSONField` instead of `TextField`. If you have a large number of objects for these models, running the migrations might take a while.
### Replaced `form_data` `TextField` with `JSONField` in `AbstractFormSubmission`
The `form_data` field in the `AbstractFormSubmission` model (and its subclasses `FormSubmission`) has been converted to `JSONField` instead of `TextField`. If you have customisations that programmatically add form submissions you will need to ensure that the `form_data` that is output is no longer a JSON string but instead a serialisable Python object. When interacting with the `form_data` you will now receive a Python object and not a string.
The `size` argument of the undocumented `wagtail.utils.sendfile_streaming_backend.was_modified_since` function has been removed. This argument was used to add a `length` parameter to the HTTP header; however, this was never part of the HTTP/1.0 and HTTP/1.1 specifications see [RFC7232](https://httpwg.org/specs/rfc7232.html#header.if-modified-since) and existed only as a an unofficial implementation in IE browsers.