TeamRoom Plugin Integrations
This topic explains how to integrate TeamRoom in Agility.
Concept
The Digital.ai Agility TeamRoom™ feature allows users to use Agility without all of the noise. Most people do not use every feature every day, but they do tend to use a handful of the same features every day. TeamRooms tie simplified navigation with strong data filters to help people focus on what matters most.
However, as many features Agility has, every company uses other tools to get their work done. Using the REST API you can build integrations to help synchronize those together. One thing that was lacking though was a visual experience inside of the app.
TeamRoom Plugins are a way for developers to create visual elements and integrations that 'live' inside of a TeamRoom. Plugins communicate with the TeamRoom to produce things like Panels/Toggles or Radiators and can also receive messages like when a user changed the active iteration or selects an avatar to filter on.
Plugins are written in Javascript and can be hosted on your servers. But some plugins are generic enough and may be useful for other companies; in that case they can be shared and when installing the plugin they can be configured for your specific configuration.
We hope that you will find writing a plugin easy and intuitive and will build some great things to share with everyone in the Agility community!
Architecture
TeamRoom is built mostly on the client with Javascript and Backbone. Plugins are also written in Javascript but Backbone is not required. Plugins are actually hidden iframes that are injected into the TeamRoom. These hidden iframes are the entry point and communication hub of your plugin.
Plugins communicate with a TeamRoom through a message based protocol. Messages are serialized to JSON strings and sent through a secure boundary. TeamRooms receive the message, process it and send a callback message to the plugin. This asynchronous programming style is common in Javascript and allows us to create a strong boundary between a TeamRoom and its plugins.
Security
TeamRoom Plugins must be a hosted webpage. They are not run on the VersionOne server and are not even run inside of the TeamRoom window. Each iframe creates a new window that is completely isolated from its parent running a completely separate Javascript runtime. That means a plugin cannot 'reach out' into a TeamRoom and have a field day. In addition, since the plugin is hosted somewhere other than the V1 server it has to play by the very strict CORS rules set in place by browsers and cannot run arbitrary code under the user's identity.
So how then do messages go back and forth? The answer is in a browser API called postMessage that allows two windows to send string-only events to each other. That means that you cannot send arbitrary Javascript and further means that unsupported messages are ignored.
The advantage of this architecture and security model is that supported messages can and will be executed inside of the browser window as the user that is currently running. So queries and updates will have better security inside of the app than a generic 'robot' account that is common with integrations.
Quick Example
We ship a very basic plugin with Agility called Web Panel. This plugin allows the TeamRoom user to configure a new Toggle (name) and a corresponding Panel (url). That information is sent to the plugin upon start up and it in-turn sends a message to create a Panel with that information. Let's walk through that code:
Host
Every plugin must be hosted in a webpage. That page references a copy of the TeamRoomMessenger
as well as whatever plugin code you write. It can be inline with the page, or a seperate file.
<!DOCTYPE html>
<html><body>
<script type="text/javascript" src="../../dist/team-room-messenger.js"></script>
<script type="text/javascript" src="plugin.js"></script>
</body></html>
Initialization
Once your scripts are loaded the plugin must send an initialization command to the TeamRoom. This signals that you are ready to receieve and send messages. To get things started, we recommend using an immediately invoked function expression as good practice.
(function () {
... below ...
})();
The first thing to do is create an instance of a TeamRoomMessenger
. The constructor can take an optional options
object to control the behavior of the messenger, one you may be interested in turning on right away is the debug option.
var messenger = new TeamRoomMessenger({ debug:false });
When you are ready, send the init
message. It is not necessary to supply a callback function when sending a message, but the init
message in particular is useful to supply one as no messages will be processed until after you have received the callback.
messenger.send('init', onInit);
Similar to node, an error object is supplied as the first parameter to any callback. It is highly recommended that you check this for a false value before continuing with your plugin messages.
function onInit(err, args) {
if (err) throw err;
Sending a message
Assuming there is no error, the second argument is the args to the callback. This is an arbitrarily structure object depending on which message the callback is for. In this case we have registered the plugin to accept two configuration parameters Name
and Url
(more on this later).
var panelArgs = { name : args.Name || 'Plugin' , url : args.url };
Coincidentally the add-panel
message accepts two parameters as well, and we've copied the values from how the end user has configured this plugin. The values of these parameters do not have to come from configuration; you can hard-code them or pull from a different source all together.
messenger.send('add-panel', panelArgs);
}
For the sake of simplicity we did not supply a callback argument as the third parameter. You probably should though so you can check the error argument to ensure you have successfully created a panel.
Receiving a message
Those are the basics on sending a message and receiving a callback. You can also register to receive messages originating from the TeamRoom with messenger.receive(message_name, callback)
. The callback looks just the same as it does when you send a message.
Registration
Currently anyone can register a plugin for use by any TeamRoom. Registering a plugin defines what it is and what configuration it needs to be fully functioning. It does not put it inside of any TeamRooms, it simply makes it available for TeamRoom configurators to install them into particular rooms.
Plugin registration happens through a minimalistic UI located in your app http://<host>/versionone/teamroomplugins.mvc/register
. When registering you will need to provide:
Name - the name of the plugin as users see it
Description - a short message about what it does
URL - where the plugin webpage is hosted
Image - optional image to uniquely identify your plugin
Additionally you may ask the configurator to supply your plugin with the data that it needs. Plugins can have as many parameters as needed and each configuration parameter must be defined:
Id - the name of the variable to be sent to the plugin
Name - the name that the user will see when installing
Description - a short message about valid values or instructions
Validation Regular Expression - basic javascript validation to help prevent mistakes
Once a plugin has been defined, updates to its configuration are allowed by implicitly by registering a 'new' plugin but keeping the same hosted url. This will not force updates to existing installations of your plugin, so please be concious about changes.
Code
TeamRoomMessenger
is the main interface you will use to communicate with a TeamRoom. It uses a ReceiveMessenger
to listen for messages from the other window and SendMessenger
to post messages to the other window. It also uses an additional SendMessenger
to send callback messages that are bound to the original message.
Examples
The most basic example. Start here.
Two panels with different droppable asset types.
API Messages
Interacting with the TeamRoomMessenger will require you to use callback functions. All callback functions have this structure.
function callbackFn( 'err', { 'arg' : 'value' } ) { }
Sendable Messages
Send these messages from your plugin, always be sure to check the err
in the callback to ensure success.
messenger.send( 'name', { 'arg' : 'value' }, callbackFn ) {} );
Initialize
Indicates to the TeamRoom that your plugin page / script is ready to communicate. Send this message after document load, before you send any other messages. Messages sent before
init
will cause a callback with anerr
.Name
init
Errors
Args
Callback Args
name : string
The name that the user has given this instance of the plugin.
< id > : string configurable
Any registered parameters will be added to the args object with the values that the user has configured this instance of the plugin with. The parameter Ids supplied during registration are used as the property names in this object.
Add Panel
Adds a panel and a toggle to the TeamRoom. The panel displays an iframe that can be pointed to any url. After the panel is added the callback will be invoked with a unique id identify it; you may add as many panels as you'd like. Each panel may have it's own TeamRoomMessenger to communicate with the room, but it is not a requirement. If you do want to communicate from the panel, be sure to send a
panel-init
message.Name
add-panel
Errors
'not initialzied'
The plugin did not send an
init
message before sending this message.Args
url : required string
The url that the iframe will display.
name : required string
The name of toggle button that controls visibility of the panel.
droppableAssetTypes : optional string array
An array of VersionOne asset types that are allowed to be dropped on the panel. An empty array or null/undefined specifies assets cannot be dropped on the panel.
Callback Args
panelId : string
A unique generated ID that you can reference later. This is especially useful if you add multiple panels from one plugin.
Panel Initialize
If you prefer to communicate directly from a panel that was added with
add-panel
rather than from your main plugin, you must send this message before sending others.Name
panel-init
Errors
Args
Callback Args
Receivable Messages
Signing up to be notified when messages come from the TeamRoom has a similar structure to sending a message. The difference is that you supply a handler function instead of a callback function. A handler function looks similar to a callback function except that a third parameter is supplied: a callback function. Invoke the callback function when a response is required.
messenger.receive( 'name', function handler( err, { 'arg' : 'value' }, callbackFn ) {} );
Panel Shown
When the user has toggled a panel to be visible that was added with
add-panel
.Name
panel-shown
Args
panelId : required string
The unique generated ID that was supplied as a callback argument to the add-panel message.
Callback Args
Panel Hidden
When the user has toggled a panel to be invisible that was added with
add-panel
.Name
panel-hidden
Args
panelId : required string
The unique generated ID that was supplied as a callback argument to the add-panel message.
Callback Args
Panel Drop
When the user dropped an asset onto a panel that was added with
add-panel
. Only asset types that were specified withdroppableAssetTypes
in theadd-panel
args will be able to be dropped.Name
panel-drop
Args
panelId : required string
The unique generated ID that was supplied as a callback argument to the add-panel message.
asset : required object
oid : required string
assetType : required string
isClosed : required bool
Callback Args