Hi everyone,
I would like to introduce a JavaScript helper for handling the pre-edit checks (checking if the page is locked, if it is part of an extension, if the page would be broken by editing due to missing script/… rights, etc.). These pre-edit checks can either produce errors (that prevent editing) or warnings (that require an explicit user confirmation).
We already have a similar implementation for in-place editing, but it is tied to this specific edit mode and not re-usable. Further, the server-side endpoint is a wiki page (XWiki.InplaceEditing) which is far from ideal for re-usability. The idea is to extract what we currently have for in-place editing and make it reusable (also for Cristal).
For this reason, I propose introducing a REST API that handles the server-side part of the pre-edit checks and optional document locking.
Before discussing the actual API design, I would like to discuss a few design considerations:
Locking Behavior
Locking should be optional, as we already don’t check if the page is locked when using the realtime editor. Further, when editing only, say, a boolean property, it makes little sense to use locking – but we should still execute the remaining checks. The decision if the page shall be locked currently depends on the editor (like “inline”, or “wysiwyg”). However, this concept of “editor” makes little sense in the context of the REST API, as it is not tied to a specific frontend implementation and the server cannot know about every possible editor implementation.
For this reason, I think the client should be responsible for deciding if locking is required or not.
A question here is: should we still check for locks when not asking for locking?
I don’t think so – if the client does not ask for locking, we should not check for locks either. To implement this, I suggest augmenting the edit confirmation checker Java API to allow passing a list of checks to skip. The REST API wouldn’t expose this directly, but it would use this feature to skip the lock check when not asking for locking.
Forcing Checks
At the moment, whenever the user confirms, all checks are “forced”, which means that an identifier is stored in the session to avoid showing the same check again. I think this might not always be desirable, as it means that if a user skips the warning and then wants to go back to see it again, they will not see it. I think it would be better to add an explicit “Remember confirmation” option to the API so the UI is free to add an option for users to choose whether they want to remember the confirmation or not. I think we didn’t do this back when we introduced the confirmations because we feared that further requests during the editing process would show the same confirmation again. But it could be a possibility in a new UI like Cristal where we could more clearly separate the confirmation from the actual editing process.
Further, I noticed that there is a possible gap in the current implementation that if the document is changed while the user is confirming, the warnings the user sees might not be the same as the ones they confirm. For this reason, I suggest introducing a confirmation token that is basically a hash of the shown confirmation messages. This token would be transmitted together with the warnings to the client and would need to be provided back to the server when the user confirms. The server would then re-compute the token based on the current version of the document and compare it with the one provided by the client. If they match, the confirmation is valid and the confirmations can be forced.
The lock would, of course, still always be forced if the client asks for locking and provides a valid confirmation token.
Output Format of Warning Messages
Internally, the edit confirmation checker API returns Block elements, but they frequently already
just contain HTML content, including complex UI elements like expandable sections for required rights. For this reason, we have no real choice but to return HTML content for the error and warning messages. I would still propose returning them as individual messages in the response so the client can decide how to display them, in particular, if there are several warnings/errors.
Proposed REST endpoints
The same API exists for the default version of a page and for translations, similar to other page endpoints in the XWiki REST API.
-
Default translation:
POST /wikis/{wikiName}/spaces/{spaceName: .+}/pages/{pageName}/editconfirmation -
Explicit translation:
POST /wikis/{wikiName}/spaces/{spaceName: .+}/pages/{pageName}/translations/{language}/editconfirmation
Request
All fields are optional unless stated otherwise.
-
lock:booleantrue: attempt to lock for editing (only done if there are no warnings/errors or the user has confirmed the warnings)false: unlock (no checks are run)- omitted: only run pre-edit checks
-
confirmationToken:StringAn opaque token returned by a previous response when confirmation is required. When present, it indicates that the user has confirmed and that the request may proceed (including lock override, if needed).
-
rememberConfirmation:booleanOnly meaningful when
confirmationTokenis provided. Whentrue, the server will persist the last confirmed warnings in the session (so that the exact same warnings are not shown again for this document in the same session).
Response
warnings:List<String>- HTML content for each warning message, if anyerrors:List<String>- HTML content for each error message, if anyerrorMessage:String- an optional additional error message in case the requested action cannot be performed, e.g., because the page has been modified in the meantime and the confirmation token is no longer valid. This error message isn’t related to the pre-edit checks and doesn’t require confirmation.confirmationToken:String- returned when confirmation is required (and can be used in the next request)
Status codes
-
200 OKRequest processed successfully and no (further) confirmation is required.
-
409 ConflictConfirmation is required (warnings and/or errors exist) or the provided
confirmationTokenis not valid anymore. In both cases, the response body contains the confirmation details to display. -
401 UnauthorizedAuthentication is required or the current user lacks view/edit rights for the page.
Processing rules
- If
lockis omitted, run pre-edit checks and return them if confirmation is required. - If
lock = false, unlock and return200. - If
lock = true:- If no
confirmationTokenis provided:- Run pre-edit checks.
- If warnings/errors exist, return
409withconfirmationRequired=trueand aconfirmationToken. - If no warnings/errors exist, lock and return
200.
- If
confirmationTokenis provided:- Re-run the pre-edit checks.
- If the token is still valid or no warnings/errors exist:
- Lock the document. If it was locked by another user, override the lock.
- If
rememberConfirmation=true, persist the confirmed warnings in the session. - Return
200.
- Otherwise, return
409with a fresh confirmation payload.
- If no
Example flow
1) Attempt to lock before editing
Request:
POST /wikis/xwiki/spaces/Main/pages/WebHome/editconfirmation
Content-Type: application/json
{ "lock": true }
Response (confirmation required):
409 Conflict
Content-Type: application/json
{
"warnings": ["<div>... rendered HTML warnings (including lock warning if any) ...</div>"],
"errors": [],
"errorMessage": null,
"confirmationToken": "<opaque>"
}
2) User confirms
Request (confirm, override lock if needed, and remember the decision):
POST /wikis/xwiki/spaces/Main/pages/WebHome/editconfirmation
Content-Type: application/json
{
"lock": true,
"confirmationToken": "<opaque>",
"rememberConfirmation": true
}
Response:
200 OK
Content-Type: application/json
{
"warnings": [],
"errors": [],
"errorMessage": null,
"confirmationToken": "<opaque>"
}
3) Unlock after editing
POST /wikis/xwiki/spaces/Main/pages/WebHome/editconfirmation
Content-Type: application/json
{ "lock": false }
200 OK
Content-Type: application/json
{
"warnings": [],
"errors": [],
"errorMessage": null,
"confirmationToken": null
}
Choices Regarding the API Design
Separate Locking and Pre-Edit Checks
From an API design, it seems pretty weird to have a single endpoint that handles both locking and pre-edit checks. However, it also seems not so nice to clients if these two operations are split into two different endpoints as they would always need to be used together. If locking was a separate endpoint, we would also either still need to include the lock warning in the pre-edit checks (which seems weird) or clients would basically need to re-implement the lock warning separately, which goes against the idea of the generic edit confirmation checkers.
More Information in the Response
We could also consider adding more information to the response, such as the actual lock status of the document, the user who currently has the lock, etc. We could also include metadata for every check. At the moment, while we have the information about the locks, it feels weird to include this information in the pre-edit checks response. For the other checks, we currently don’t have any metadata, and it’s also not clear to me how clients would use this information.
Non-HTML Response Format
At the moment, the response contains only HTML content which makes them hard or even impossible to use in non-web contexts. It would require a significant effort to provide non-HTML representations of the warnings and errors, but it could be interesting for clients that aren’t web applications.
Using HTTP Headers for Confirmation Token
Instead of including the confirmation token in the response body, we could use the ETag HTTP header to transmit the confirmation token and then use an If-Match header to transmit the token back to the server when confirming. The response code should then be 412 Precondition Failed if the token is not valid anymore.
It is not fully clear to me if this would be a better API design, in particular as we’re not using this in other places.
Use DELETE for Unlocking
Instead of using a POST request with lock=false, we could use a DELETE request to unlock the document. This would be more consistent with the HTTP method semantics.
Further Confirmation Tokens
We could consider using edit confirmation tokens more broadly, like during document saving to check if there have been new warnings/errors since the last confirmation. We would need to make sure that the token is still valid after confirming the warnings.
I’m looking forward to your feedback on this proposal and any suggestions for improvements.