Prevent structure changes

Sometimes you want users to only be able to edit specific parts of the document. For instance when you have documents with tables which some users can add or modify, but other user are only allowed to change the text content of those tables. Changing the XML Schema is not feasible as you would have to have a separate schema for each table configuration.

In this case you can use the Xopus API to prevent all changes to those particular elements without the need to alter the XML Schema. You can put the following code in your /config/config.js file or include it using a javascript reference.

// These elements and their element children can't be changed.
var immutableNames = { table: 1, tbody: 1, tr: 1 };

// These elements can't be changed, but children can be moved, deleted and added.
var immutableContainerNames = { td: 1, th: 1 };

Editor.addEventListener("load", loadHandler); 

function loadHandler(evt) {  
  evt.document.addEventListener("XopusBeforeSubtreeModified", immutable);
  evt.document.addEventListener("XopusBeforeNodeSplit", immutableContainer);
  evt.document.addEventListener("XopusBeforeNodeMerged", immutableContainer);
}

function immutable(evt) {  
  if (evt.target.getLocalName() in immutableNames)
    evt.cancelEvent = true;
}

function immutableContainer(evt) {
  if (evt.target.getLocalName() in immutableContainerNames)
    evt.cancelEvent = true;
}

All element names in immutableNames are completely immutable because the XopusBeforeSubtreeModified event is fired for changes that involve the event target and changes to the childNodes collection of the event target (like appendChild, insertBefore and removeChild).

All elements names in immutableContainerNames just can't be split or merged. We need to listen to these events because for those operations the target is the split or merged node, so our test in the immutable function will not hit in those cases. The cases where immutableContainer elements are removed are covered by the immutable function.

Preventing changes to structures that are not built up of mutiple levels of elements is left as exercise for the reader.

This API code will prevent structure changes, but it will not hide the blocked actions from the user interface. To make the interface easier to understand for the user, you can hide certain elements from the user interface by giving them the hidden-from-ui role. This will hide elements from the insert, delete and move menus, but not from the status bar.

You can add the following elements to your roleMapping element in your configuration:

<x:element name="table" role="hidden-from-ui" />
<x:element name="tbody" role="hidden-from-ui" />
<x:element name="tr"    role="hidden-from-ui" />
<x:element name="th"    role="hidden-from-ui" />
<x:element name="td"    role="hidden-from-ui" />