Based on the discussion in wagtail/wagtail#7636, we would like to have an automatic page locking mechanism to support autosave in the future. This automatic locking will be separate from (thus can co-exist with) the existing page locking feature, as it will have more advanced logic tightly coupled to the client-side interaction.
For clarity, we'll refer to the two locking mechanisms as follows:
- "auto lock" - the new automatic locking mechanism that prevents two tabs from editing the same page at the same time
- "manual lock" - the existing locking mechanism that allows a user to prevent other users from editing the page
In general, an auto lock should be automatically acquired when a user visits the page edit view, and released when the user leaves.
An auto lock requires the user to have edit permissions on the page. This means that an auto lock can only be acquired if the manual lock (if any) does not lock the page for the user. In other words, an auto lock can only be acquired if any of these is true:
- there is no manual lock on the page
- the user is part of a group that has a permission for the page's manual lock
- the user is a superuser
To ensure that the user who acquires an auto lock has the latest version of the page in the editor, acquiring an auto lock must happen when the edit view is initially loaded. (An exception to this will be explained later.)
As a user can open multiple editor tabs of the same page at the same time, the lock must only be acquired via the first tab. All the other tabs must be locked to prevent discrepancies in the editor. Thus, a "user" in the case of auto locks is more like a "session".
If a session is not actively editing the page for a prolonged period of time (e.g. forget to close the tab, leave the editor open for hours, or lose the internet connection), the auto lock should be released and thus acquirable by other sessions. This ensures the page is not stuck in the auto lock limbo in such situations.
When an auto lock is released, all other sessions who are viewing the editor while the page was still locked will receive a notification that says the page is now available for editing. This notification should tell them to reload if they wish to edit, making it up for grabs. Only the first session that reloads the page and successfully acquires the lock will be able to edit the page.
If an inactive session comes back active and the auto lock wasn’t acquired by any other session during the inactivity, the lock can be re-acquired immediately. We can be sure this session still has the latest version, since we know that no other session has changed the page since this session was last on it.
Meanwhile, if another session acquired an auto lock after the first session's auto lock lapsed, the first session should be forced to reload the page and re-do the edits.
- Add new database fields to store the locking information
auto_lock_browser_id
UUIDField
auto_lock_expires_at
DateTimeField
- Put in a mixin, e.g.
AutoLockMixin
?
- When user visits edit view
- Acquire the lock if it isn't currently locked by anyone else and the user is allowed to edit the page
- While they edit
- Ping back to the server periodically as long as the user is doing something. This will extend the auto lock expiry
- If they lose connection and the pings don't get through, the lock may lapse and someone else might acquire the lock
- If they come back, we can check if someone else has edited the page in the meantime (by checking if
auto_lock_browser_id
is the same) and re-acquire the lock if not- If the
auto_lock_browser_id
had changed, they will be forced to reload the page (show the notification)
- If the
- If an admin requests to unlock the page
- Change
auto_lock_browser_id
- When the existing user's browser sends ping back to the server, they will be notified that they can no longer edit the page and need to reload
- Generally, this would only be done if they are AFK so wouldn't normally reload their browser, allowing a new user to edit the page
- Change
- When they leave the page
- Send a request to the server to remove the auto lock (by setting
auto_lock_browser_id
/auto_lock_expires_at
to empty values) using abeforeunload
handler
- Send a request to the server to remove the auto lock (by setting
- When they submit the page
- Make sure the auto lock fields are cleared. (Might not be necessary if the
beforeunload
handler also fires? But might be better to clear it anyway just in case the handler didn't get through.)
- Make sure the auto lock fields are cleared. (Might not be necessary if the
(Thanks Karl!)