The purpose of this document is to explain the use of the bus of message on the client side.
As the interface is divided in several components and plugins a bus has been written to allow them to communicate.
For example, it allows windows (tools) to communicate which each other.
The search tool can say in the bus : The content X had been selected. As a consequence, the history tool can say : I am out of date ; while the edit button will ask the server for a refresh (to know if user can edit X).
The big advantage of the bus of message, is that tools do not know each other ; and at any time a new tool can be added (opened) : it will access to all information and will be updated by other tools.
Here is a sample of code to say something to the bus
var msgBuilder = org.ametys.messagebus.bus.MessageBuilder.getInstance(); var targets = []; for (var i = 0; i < records.length; i++) { targets.push(msgBuilder.createTarget("content", {'id': records[i].data.id})); } var message = msgBuilder.createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED, null, targets); org.ametys.messagebus.MessageBus.getInstance().fireMessages([message]);
This sample iterate on a record array to say something like : The content X, Y and Z are selected.
A message has a type and mutiple targets.
The type declares the kind of message : selection, focus...
The targets declares what is impacted by the message. A target can be a content, a page, a tool...
See the API of org.ametys.messagebus.message.Message for more information.
To create a message, you must use MessageBuilder#createMessage with the following parameters:
The target must be created using a factory.
Creating a target is more complicated that it could appears at first and moreover it could change from one application to another.
The point is that a target has subtargets, that reflects the "involved" target of the message.
Imagine a tool that only know about "contents" such as the history view. If you say "I have selected the Page A", nothing will happen, whereas if you say "I have selected the Page A (so the content X)" : the history view will work fine.
The third parameter of MessageBuilder#createMessage is an array of targets involved, but each target can have subtargets and so on.
The value for a message of several pages selection can be the following :
The MessageBuilder allows you to create a target (it will call the right factory depending on the target type)
See the API of org.ametys.messagebus.bus.MessageBuilder for more information.
Once created you can simply send the message using the MessageBus.
See the API of org.ametys.messagebus.MessageBus for more information.
To receive messages from the bus you have to register a callback.
Be careful, many components are automatically registered on the bus. Check that before manually register or you will receive every messages twice and that will slow down the application.
To register, use MessageBus#register. Do not forget to unregister when you are closed or destroy.
org.ametys.messagebus.MessageBus.getInstance().register(this);
When registered you have to implement the MessageListener interface. You will have to write a onMessage handler.
Be careful, there are MANY messages on the bus so you code have to be very optimized.
Here is a sample of a handler to a selection event on contents:
onMessage = function (messages) { // SELECTION for (var i = messages.length - 1; i >= 0; i--) { if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED) { var targets = messages[i].getTargets(); var allTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(targets, function(target) {return target.getType() == 'content'}); // YOUR CODE HERE return; } } }
As you can see, you may receive many events at once.
In that case we read on in the reverse order because only the last selection do interest us (if there are many at once).
Here is a sample of behavior you may implement in that method:
When search tool receives the focus, it will send into the bus its current selection (the one that was selected before it lost focus): Content X, Y, Z are selected. So other tools can synchronize.
When search tool is closed, it will send a null selection event.
When content tool receives the focus, it must of course send "the content X is selected"
Be careful, when the focus is lost (blur event) do not send a null selection: read tools like history tool would be cleared as soon as you click on it.
As targets is an array, and could have subtargets, that have subtargets and so on... finding a target can be complex. Use the MessageTargetHelper to explore the targets.
DO NOT send a message in the onMessage method. Use external mecanism, such as the outofdate/refresh for the tools
See the API of org.ametys.messagebus.MessageBus ; org.ametys.messagebus.MessageListener and org.ametys.messagebus.message.MessageTargetHelper for more information.
You understand now how to be aware of the current selection... only when you receive the event.
If you want to know what is the selection in other situations (you are not registered, you have just registered because your tool is just opened or you don't want to remember the last selection in your tool...).
You then can use org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets()
Be careful, DO NOT CALL getCurrentSelectionTargets in your onMessage method (except if the message has nothing to deal with selection) because sometimes you will have the previous selection, sometimes the new one.
Most of the tools send a null selection message to the bus, even if they have nothing to deal with.
If not you will meet strange behaviors: buttons that are still available, view not synchronized.
To do so, add the 'focus' listener on the panel of your tool and here is the code of the listener:
var message = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED, null, []); org.ametys.messagebus.MessageBus.getInstance().fireMessages([message]);
A very few number of tools may not send this event: these are the 'read' tools such as Details or History.
Note also that you should send a null selection to the bus on the "close" handler of your tool
To use a message factory, please read the "creation" part of this document.
To create a message factory, you have to declare it in a plugin
<extension id="org.ametys.cms.userinterface.BasicMessageTargetFactory" point="org.ametys.cms.workspace.uitool.MessageTargetFactoriesManager" class="org.ametys.cms.workspace.impl.StaticContextualClientSideElement"> <action class="org.ametys.messagebus.bus.impl.BasicMessageTargetFactory"> <param name="type">*</param> </action> </extension>
The JS class used have to implement the org.ametys.messagebus.bus.MessageTargetFactory interface. This API is very simple: there is a single method that ask for the creation of a type of event.
The type parameter is mandatory, it will be used to know which factory can handle which kind of targets.
The '*' type is for the factory that handle all unknown types.
For a simple application, this BasicMessageTargetFactory can be enough.
Here is a sample of this basic factory:
org.ametys.messagebus.bus.impl.BasicMessageTargetFactory.prototype.createTarget = function (type, parameters) { return new org.ametys.messagebus.message.MessageTarget(type, parameters, []); }
Here is a sample of a "page" message target factory
var target = new org.ametys.messagebus.message.MessageTarget(type, parameters, []); var response = Tools.postFromUrl(getPluginDirectUrl("web") + "/repository/page-info", "id=" + pageId); if (response != null) { var contents = response.selectNodes("/page/contents/content"); for (var i = 0; i < contents.length; i++) { var contentName = contents[i].selectSingleNode("name")[Tools.xmlTextContent]; var subTarget = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget("content", {'id': contentName}); target.getSubTargets().push(subTarget); } } return target;