Hello,
The "sync.actions.InsertXref" action was designed to work for DITA documents. I registered an issue to make add similar functionality for DocBook and I will update this thread when it will be available in a released version of Web Author.
Let us know if you want to test beta version of the DocBook framework that includes this functionality, please write us an email on
support@oxygenxml.com .
Meanwhile, you can implement your own action in your DocBook framework extension. To do this you need to:
1. Create a custom action that prompts the user to choose the destination file and the ID of the target element. The code for this action needs to be added in the "web" folder of you framework in a new JS file. You can find below a minimal working example:
Code: Select all
var DocBookCrossRefXrefAction = function (editor) {
sync.actions.AbstractAction.call(this);
this.editor = editor;
this.xrefTargetsDialog = null;
};
DocBookCrossRefXrefAction.prototype = Object.create(sync.actions.AbstractAction.prototype);
DocBookCrossRefXrefAction.prototype.constructor = DocBookCrossRefXrefAction;
DocBookCrossRefXrefAction.prototype.getDisplayName = function () {
return 'Insert cross reference (xref)';
};
DocBookCrossRefXrefAction.prototype.actionPerformed = function (callback) {
// This callback might have to be called at a later point, so save it until then.
this.callback = callback || function () {};
var chooser = workspace.getUrlChooser();
var context = new sync.api.UrlChooser.Context(sync.api.UrlChooser.Type.EXTERNAL_REF);
chooser.chooseUrl(context, this.showXrefTargetChooser.bind(this), sync.api.UrlChooser.Purpose.CHOOSE);
};
DocBookCrossRefXrefAction.prototype.showXrefTargetChooser = function (url) {
if (url) {
// Create a dialog to display all targets, so the user can select one to insert cross reference.
this.xrefTargetsDialog = this.createXrefTargetsDialog();
this.populateXrefTargetsDialog(url);
this.xrefTargetsDialog.onSelect(function (key, e) {
if (key === 'ok') {
e.preventDefault();
this.xrefTargetChosen();
} else {
this.callback();
}
}.bind(this));
this.xrefTargetsDialog.show();
} else {
this.callback();
}
};
DocBookCrossRefXrefAction.prototype.createXrefTargetsDialog = function () {
var dialog = workspace.createDialog();
dialog.setTitle('Choose cross reference');
dialog.setButtonConfiguration(sync.api.Dialog.ButtonConfiguration.OK_CANCEL);
dialog.setPreferredSize(700, 500);
dialog.setResizable(true);
return dialog;
};
DocBookCrossRefXrefAction.prototype.populateXrefTargetsDialog = function (url) {
this.editor.getActionsManager()
.invokeOperation('ro.sync.servlet.operation.FindXrefTargetsOperation', {url: url})
.then(function(str) { return JSON.parse(str);})
.then(this.xrefTargetsReceived.bind(this))
};
DocBookCrossRefXrefAction.prototype.xrefTargetsReceived = function (targets) {
var container = this.xrefTargetsDialog.getElement();
if (!targets || !targets.length) {
container.textContent = 'No cross reference targets found in the chosen file';
} else {
for (var i = 0; i < targets.length; i++) {
var radio = document.createElement('input');
radio.name = 'docbook-ref-table-radio';
radio.type = 'radio';
radio.dataset.id = targets[i].id;
var label = document.createElement('label');
label.style = 'display: block';
label.appendChild(radio);
label.appendChild(document.createTextNode(targets[i].nodeName + ' (#' + targets[i].id + ') - ' + targets[i].content.substring(0, 50)));
container.appendChild(label);
}
}
};
DocBookCrossRefXrefAction.prototype.xrefTargetChosen = function() {
var targetRadio = this.xrefTargetsDialog.getElement().querySelector('input[name="docbook-ref-table-radio"]:checked');
if (targetRadio) {
this.editor.getActionsManager().invokeOperation('InsertFragmentOperation',
{fragment: '<xref linkend="' + targetRadio.dataset.id + '"/>'})
.then(this.xrefInserted.bind(this));
}
};
DocBookCrossRefXrefAction.prototype.xrefInserted = function () {
this.xrefTargetsDialog && this.xrefTargetsDialog.dispose();
this.callback();
};
DocBookCrossRefXrefAction.prototype.dispose = function() {
this.xrefTargetsDialog && this.xrefTargetsDialog.dispose();
};
goog.events.listen(workspace, sync.api.Workspace.EventType.EDITOR_LOADED, function(e) {
var editor = e.editor;
goog.events.listen(editor, sync.api.Editor.EventTypes.ACTIONS_LOADED, function(e) {
editor.getActionsManager().registerAction('insert.cross.reference.xref', new DocBookCrossRefXrefAction(editor));
});
});
2. This action uses a server-side AuthorOperationWithResult to collect xref targets from the chosen file. You can find below the code of this Java operation:
Code: Select all
package ro.sync.servlet.operation;
import java.net.URL;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import org.codehaus.jackson.map.ObjectMapper;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import ro.sync.basic.util.URLUtil;
import ro.sync.ecss.extensions.api.ArgumentsMap;
import ro.sync.ecss.extensions.api.AuthorOperationException;
import ro.sync.ecss.extensions.api.webapp.AuthorDocumentModel;
import ro.sync.ecss.extensions.api.webapp.AuthorOperationWithResult;
import ro.sync.ecss.extensions.api.webapp.WebappRestSafe;
import ro.sync.xml.parser.ParserCreator;
@WebappRestSafe
public class FindXrefTargetsOperation extends AuthorOperationWithResult {
@Override
public String doOperation(AuthorDocumentModel model, ArgumentsMap args) throws AuthorOperationException {
String urlString = (String) args.getArgumentValue("url");
try {
URL url = URLUtil.addAuthenticationInfo(model.getAuthorAccess().getEditorAccess().getEditorLocation(), new URL(urlString));
InputSource is = new InputSource(url.toString());
DocumentBuilder docBuilder = ParserCreator.newSchemaAwareDocumentBuilder();
Document document = docBuilder.parse(is);
ArrayList<XrefTarget> xrefTargets = new ArrayList<>();
gatherXrefTargets(document, xrefTargets);
return new ObjectMapper().writeValueAsString(xrefTargets);
} catch (Exception e) {
throw new AuthorOperationException(e.getMessage(), e);
}
}
public static class XrefTarget {
public final String id;
public final String nodeName;
public final String content;
public XrefTarget(Element elem) {
this.id = getIDValue(elem);
this.nodeName = elem.getTagName();
this.content = elem.getTextContent();
}
}
private static void gatherXrefTargets(Node node, ArrayList<XrefTarget> xrefTargets) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element elem = (Element) node;
if (getIDValue(elem) != null) {
xrefTargets.add(new XrefTarget(elem));
}
}
NodeList childNodes = node.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
gatherXrefTargets(childNodes.item(i), xrefTargets);
}
}
public static String getIDValue(Element elem) {
NamedNodeMap attributes = elem.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Attr attribute = (Attr)attributes.item(i);
// There are some ID attributes defined as such in the schema file.
if (attribute.isId() || "xml:id".equals(attribute.getName()) || "id".equals(attribute.getName())) {
return attribute.getValue();
}
}
return null;
}
}
You need to compile this Java class and add it as a JAR file in the classpath of your framework. More details here:
https://www.oxygenxml.com/doc/versions/ ... h-tab.html .
Best,
Cristian