diff --git a/docs/reference/pages/model_reference.md b/docs/reference/pages/model_reference.md index 2ba41560ca..af1870db7a 100644 --- a/docs/reference/pages/model_reference.md +++ b/docs/reference/pages/model_reference.md @@ -610,6 +610,50 @@ The model is added to allow snippets to have changes that are not immediately re .. automethod:: with_content_json ``` +## `LockableMixin` + +`LockableMixin` is an abstract model that can be added to any non-page Django model to allow its instances to be locked. +Pages already include this mixin, so there is no need to add it. + +```{versionadded} 4.2 +The model is added to allow snippets to be locked. See [](wagtailsnippets_locking_snippets) for more details. +``` + +### Database fields + +```{eval-rst} +.. class:: LockableMixin + + .. attribute:: locked + + (boolean) + + A boolean that is set to ``True`` if the object is locked. + + .. attribute:: locked_at + + (date/time) + + The date/time when the object was locked. + + .. attribute:: locked_by + + (foreign key to user model) + + The user who locked the object. +``` + +### Methods and properties + +```{eval-rst} +.. class:: LockableMixin + :noindex: + + .. automethod:: get_lock + + .. automethod:: with_content_json +``` + (revision_model_ref)= ## `Revision` diff --git a/docs/releases/4.2.md b/docs/releases/4.2.md index d2d782ea8a..fbd4e72446 100644 --- a/docs/releases/4.2.md +++ b/docs/releases/4.2.md @@ -15,6 +15,12 @@ depth: 1 Wagtail now provides a set of utilities for creating data migrations on StreamField data. For more information, see [StreamField data migrations](streamfield_data_migrations). This feature was developed by Sandil Ranasinghe, initially as the [wagtail-streamfield-migration-toolkit](https://github.com/wagtail/wagtail-streamfield-migration-toolkit) add-on package, as part of the Google Summer of Code 2022 initiative, with support from Jacob Topp-Mugglestone, Joshua Munn and Karl Hobley. +### Locking for snippets + +Snippets can now be locked by users to prevent other users from editing, through the use of the `LockableMixin`. For more details, see [](wagtailsnippets_locking_snippets). + +This feature was developed by Sage Abdullah. + ### Other features * Switch to using [Willow](https://github.com/wagtail/Willow/) instead of Pillow for images (Darrel O'Pry) diff --git a/docs/topics/snippets.md b/docs/topics/snippets.md index ab1a3b82fb..5286909940 100644 --- a/docs/topics/snippets.md +++ b/docs/topics/snippets.md @@ -416,6 +416,44 @@ Publishing a snippet instance requires `publish` permission on the snippet model Wagtail does not yet have a mechanism to prevent editors from including unpublished ("draft") snippets in pages. When including a `DraftStateMixin`-enabled snippet in pages, make sure that you add necessary checks to handle how a draft snippet should be rendered (e.g. by checking its `live` field). We are planning to improve this in the future. ``` +(wagtailsnippets_locking_snippets)= + +## Locking snippets + +```{versionadded} 4.2 +The `LockableMixin` class was introduced. +``` + +If a snippet model inherits from {class}`~wagtail.models.LockableMixin`, Wagtail will automatically add the ability to lock instances of the model. When editing, Wagtail will show the locking information in the "Status" side panel, and a button to lock/unlock the instance if the user has the permission to do so. + +If the model is also configured to have scheduled publishing (as shown in [](wagtailsnippets_saving_draft_changes_of_snippets) above), Wagtail will lock any instances that are scheduled for publishing. + +Similar to pages, users who locked a snippet can still edit it, unless [`WAGTAILADMIN_GLOBAL_EDIT_LOCK`](wagtailadmin_global_edit_lock) is set to `True`. + +For example, instances of the `Advert` snippet could be locked by defining it as follows: + +```python +# ... + +from wagtail.models import LockableMixin + +# ... + +@register_snippet +class Advert(LockableMixin, models.Model): + url = models.URLField(null=True, blank=True) + text = models.CharField(max_length=255) + + panels = [ + FieldPanel('url'), + FieldPanel('text'), + ] +``` + +The `LockableMixin` includes additional fields that need to be added to your database table. Make sure to run the `makemigrations` and `migrate` management commands after making the above changes to apply the changes to your database. + +Locking and unlocking a snippet instance requires `lock` and `unlock` permissions on the snippet model, respectively. For models with `LockableMixin` applied, Wagtail automatically creates the corresponding `lock` and `unlock` permissions and display them in the 'Groups' area of the Wagtail admin interface. For more details on how to configure the permission, see [](permissions). + ## Tagging snippets Adding tags to snippets is very similar to adding tags to pages. The only difference is that {class}`taggit.manager.TaggableManager` should be used in the place of {class}`~modelcluster.contrib.taggit.ClusterTaggableManager`. diff --git a/wagtail/models/__init__.py b/wagtail/models/__init__.py index adda3e3c06..49d8c1ab65 100644 --- a/wagtail/models/__init__.py +++ b/wagtail/models/__init__.py @@ -803,13 +803,8 @@ class LockableMixin(models.Model): def with_content_json(self, content): """ - Returns a new version of the object with field values updated to reflect changes - in the provided ``content`` (which usually comes from a previously-saved revision). - - Certain field values are preserved in order to prevent errors if the returned - object is saved, such as ``id``. The following field values are also preserved, - as they are considered to be meaningful to the object as a whole, rather than - to a specific revision: + Similar to :meth:`RevisionMixin.with_content_json`, + but with the following fields also preserved: * ``locked`` * ``locked_at`` @@ -827,7 +822,7 @@ class LockableMixin(models.Model): def get_lock(self): """ - Returns a sub-class of BaseLock if the instance is locked, otherwise None + Returns a sub-class of ``BaseLock`` if the instance is locked, otherwise ``None``. """ if isinstance(self, DraftStateMixin) and self.scheduled_revision: return ScheduledForPublishLock(self)