Integrating Concurrent Editing
Oxygen XML Web Author provides the ability for teams to edit and review content concurrently. A user can share their editing session by using the Share Session toolbar button and then sending the retrieved URL to other collaborators and then all of them can edit and review the same document simultaneously.
Enabling the Share Session Action
To enable the Share Session action, the following conditions must be met:
- The Concurrent Editing Plugin must be enabled in the Administration Page (it is bundled with the Oxygen XML Web Author installers so it is enabled by default).
- The
sharedSessionCompatible
loading option is set totrue
by the CMS connector plugin.
For the feature to work correctly, when a concurrent session is active, the CMS connector plugin should ensure that:
- Any save/commit action is disabled for session guests.
- Any check-in/check-out actions are disabled for session guests.
- The document is editable for guests even though they do not have a lock for it.
var mgr = editor.getEditingSupport().getConcurrentEditingManager(); if (mgr && !mgr.isCreator()) { ... }
- Concurrent editing does not work if Oxygen XML Web Author is deployed on multiple servers behind a load balancer.
-
Oxygen XML Web Author uses Web Sockets to propagate changes in real time between collaborators. If you are using a reverse proxy, some additional configuration may be required to enable Web Socket connections to the
./ws
endpoint of the application.For example, NGINX requires the following configuration for the /oxygen-xml-web-author/ws path:location /oxygen-xml-web-author/ws { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_pass "http://web-author:8080/oxygen-xml-web-author/ws"; }
As another example, when using Apache HTTP server as a reverse proxy, a system administrator needs to enable the following modules:
-
rewrite
-
proxy_http
-
proxy_wstunnel
Also, a configuration similar to the one below should be added:ProxyPass /oxygen-xml-web-author http://<internal-host>:8080/oxygen-xml-web-author ProxyPassReverse /oxygen-xml-web-author http://<internal-host>:8080/oxygen-xml-web-author RewriteEngine on RewriteCond %{HTTP:Upgrade} websocket [NC] RewriteCond %{HTTP:Connection} upgrade [NC] RewriteRule ^/oxygen-xml-web-author/?(.*) "ws://<internal-host>:8080/oxygen-xml-web-author/$1" [P,L]
-
- Some customizations can cause problems if used in situations with
high activity or a large number of users simultaneously. These possible limitations
include:
- The
onChange
property for the combo box form control is not supported in concurrent editing sessions. - Inline CSS actions (for example, when used in an
oxy_button
form control). - Editable combo boxes on the floating toolbar.
- Quick fixes that come from XML Schema or DTD.
- The
Disabling the Share Session Action
To remove the Share Session action from the toolbar, simply disable the Concurrent Editing Plugin in the Administration Page.
CMS-owned Concurrent Editing Sessions
Aside from using the Share Session action, a more integrated concurrent editing approach is to have a concurrent editing room owned by the CMS.
- Ensuring that aside from the Web Author users, no other users are modifying the file. The CMS can use a "service" account to hold the "lock" on the edited file, or to do a check-out.
- Creating the concurrent editing room.
- Enabling users to join the room.
- Specifying the Save Strategy.
- Closing the room.
Creating a Concurrent Editing Room
editor.getEditingSupport().getConcurrentEditingManager().createRoom().then(roomId =>
window.open(window.location.href + '&roomId=' + roomId))
The ID of the room can then be shared with other users. To join an existing room, the
roomId
URL parameter can be used.
By using the Java API, a room can be created like this:
String roomId = RoomsManager.INSTANCE.createRoomFromDocument(AuthorDocumentModel)
This code can be used during the editor loading on the following callbacks:
ro.sync.ecss.extensions.api.webapp.access.WebappEditingSessionLifecycleListener.editingSessionAboutToBeStarted()
. In this case, the room ID has to be added to thesessionAttributes
map using thero.sync.ecss.extensions.api.webapp.ce.Room.ROOM_ID_ATTRIBUTE
key.ro.sync.ecss.extensions.api.webapp.access.WebappEditingSessionLifecycleListener.editingSessionStarted()
. In this case, the room ID has to be added as an attribute to the editing context of the current document usingro.sync.ecss.extensions.api.webapp.ce.Room.ROOM_ID_ATTRIBUTE
as the name. The editing context can be obtained by calling:authorDocumentModel.getAuthorAccess().getEditorAccess().getEditingContext()
.
Saving a Concurrent Editing Session
When saving a concurrently edited document, the application uses a save
strategy that can be specified when creating the room by calling
ro.sync.ecss.extensions.api.webapp.ce.RoomFactory.createRoom(AuthorDocumentModel,
SaveStrategy)
.
The save strategy specifies what gets written to the CMS. Either the changed document is
saved for each peer individually or all changes since the last save are grouped and
written to the CMS only once. Additionally, it provides the URLConnection
for the OutputStream
where the changes from an author or from a list of
author are written.
ro.sync.ecss.extensions.api.webapp.ce.GroupChangesForSinglePeerStrategy
- It facilitates tracking precise authorship of changes, as each written revision contains changes by only one author.Note: TheURLConnection openConnection(URL documentUrl, PeerContext author)
method has only one peer involved, being both the author and the committer.ro.sync.ecss.extensions.api.webapp.ce.GroupChangesForMultiplePeersStrategy
- It facilitates a minimum number of writes to the file server per save.Note: TheURLConnection openConnection(URL documentUrl, PeerContext committer, List<PeerContext> authors)
method has a committer and a list of authors, whose changes are grouped together.
Enabling Users to Join a Room
To enable users to join a room, there are several options:
- Add
roomId=<the-id>
as a URL parameter. - Add
roomId=<the-id>
as a loading option in a JavaScript plugin. - Set
roomId=<the-id>
as a session attribute in one of the following Java listeners:ro.sync.ecss.extensions.api.webapp.access.WebappEditingSessionLifecycleListener.editingSessionAboutToBeStarted()
ro.sync.ecss.extensions.api.webapp.access.WebappEditingSessionLifecycleListener.editingSessionStarted()
Closing the Room
A room created this way is not closed when the last user leaves. It should be closed
using Room.close()
when all the users have left and all the changes are
saved.